From 1c78bdb9ba18e76553283dbf9bb3a2b34e9d70eb Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Tue, 20 Oct 2026 21:06:12 -0700 Subject: [PATCH] build(deps): upgrade @corbits/* packages to published npm releases (CL-10973) --- apps/hub/README.md | 8 +- apps/hub/package.json | 22 +-- apps/hub/src/corbits-mount.ts | 191 +++++++++++---------- apps/hub/src/migrate.ts | 65 +++---- apps/hub/src/server.ts | 5 +- apps/hub/src/workflow-run-authenticator.ts | 2 +- apps/web/package.json | 4 +- bun.lock | 85 ++++----- e2e/browser/zero-to-one.test.ts | 2 +- e2e/hub/composition.test.ts | 135 ++++++++++++++- e2e/hub/mailbox-persist.test.ts | 58 +++---- e2e/hub/mailbox-send-terminal.test.ts | 51 +++--- package.json | 15 +- packages/worker/package.json | 6 +- scripts/db-setup.ts | 9 +- 15 files changed, 376 insertions(+), 282 deletions(-) diff --git a/apps/hub/README.md b/apps/hub/README.md index 60bb3375d..c61eb38a1 100644 --- a/apps/hub/README.md +++ b/apps/hub/README.md @@ -13,15 +13,15 @@ one origin. Apps stay generic — every product rule belongs in a package (see migrations and each mounted Corbits library's idempotent migration under a Postgres advisory lock. - `src/corbits-mount.ts` mounts the Corbits mount block: - - `@corbits/artifacts` (`mountArtifacts` and `mountWorkflowArtifacts`) + - `@corbits/artifacts` (`createArtifactRoutes` and `createWorkflowArtifactRoutes`) - Live workflow-run SSE stream routes (`src/run-stream.ts`) - `@corbits/agent-token` (`mountAgentTokens` and `createAgentTokenVerifier`) - - `@corbits/mailbox` (`mountMailbox` with `src/mailbox-persist.ts` and `src/mailbox-send.ts`) + - `@corbits/mailbox` (`createMailboxRoutes` with `src/mailbox-persist.ts` and `src/mailbox-send.ts`) - `@corbits/cron` (`createCronRoutes` and `createCronTicker`) - - `@corbits/memory` (`createMemory` and `mountWorkflowMemory`) + - `@corbits/memory` (`createMemory`, `createMemoryRoutes`, and `mountWorkflowMemory`) - `@corbits/oauth-core/hub` (`mountOAuthCallback`, `mountOAuthLogin`, and `createOAuthTokenRefresher` for Codex and xAI) - `@corbits/mcp/hub` (`mountMcpDiscovery`) - - `@corbits/webhooks` (`installWebhooks`) + - `@corbits/webhooks` (`createHookRoutes`) - Serves no static files. In dev, `vite dev` (`apps/web`) serves the SPA and proxies `/api` to the hub; in e2e, `e2e/lib/browser.ts` serves the built `apps/web/dist` in front of the hub on one origin. diff --git a/apps/hub/package.json b/apps/hub/package.json index 87407d865..ae80d755c 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -12,18 +12,18 @@ "test": "bun test" }, "dependencies": { - "@corbits/agent-token": "github:corbitsdev/corbits-agent-token#ed286f4592190f911de480ef5330ce03d8605f00", - "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", - "@corbits/codex-provider": "0.1.2", - "@corbits/credential-http": "^0.1.0", - "@corbits/cron": "0.2.1", + "@corbits/agent-token": "0.2.2", + "@corbits/artifacts": "0.3.0", + "@corbits/codex-provider": "0.1.3", + "@corbits/credential-http": "^0.1.2", + "@corbits/cron": "0.2.2", "@corbits/error-sink": "workspace:*", - "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", - "@corbits/mcp": "^0.3.0", - "@corbits/memory": "github:corbitsdev/corbits-memory#8be608becc749fa0795a899826277823dca6db3e", - "@corbits/oauth-core": "^0.3.0", - "@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d", - "@corbits/xai-provider": "0.1.2", + "@corbits/mailbox": "0.2.1", + "@corbits/mcp": "^0.3.1", + "@corbits/memory": "0.2.1", + "@corbits/oauth-core": "^0.3.2", + "@corbits/webhooks": "0.3.2", + "@corbits/xai-provider": "0.1.3", "@intx/authz": "workspace:*", "@intx/crypto": "0.5.0", "@intx/db": "workspace:*", diff --git a/apps/hub/src/corbits-mount.ts b/apps/hub/src/corbits-mount.ts index 68b7c563a..5ca112c34 100644 --- a/apps/hub/src/corbits-mount.ts +++ b/apps/hub/src/corbits-mount.ts @@ -1,9 +1,8 @@ import { createAgentTokenVerifier, mountAgentTokens } from "@corbits/agent-token"; import { + createArtifactRoutes, + createWorkflowArtifactRoutes, InlineContentStore, - mountArtifacts, - mountWorkflowArtifacts, - type WorkflowArtifactEnv, } from "@corbits/artifacts"; import { CODEX_PROVIDER, @@ -15,13 +14,13 @@ import { createCronRoutes, createCronTicker, createRunTriggerCronDeliver } from import { reportError } from "@corbits/error-sink"; import { createInMemoryMailboxEventBus, - createMailboxDb, createMailboxPersist, - mountMailbox, + createMailboxRoutes, } from "@corbits/mailbox"; import { mountMcpDiscovery } from "@corbits/mcp/hub"; import { createMemory, + createMemoryRoutes, loadMemoryConfig, mountWorkflowMemory, type WorkflowMemoryEnv, @@ -34,10 +33,10 @@ import { type OAuthLoginProviders, } from "@corbits/oauth-core/hub"; import { + createHookRoutes, createRunTriggerDeliverer, createTenantSystemSender, - installWebhooks, - type HookMailRouter, + type HookRouter, } from "@corbits/webhooks"; import { XAI_PROVIDER, @@ -79,7 +78,6 @@ const TENANT_PREFIX = "/api/tenants/:tenantId"; export type MountCorbitsDeps = { readonly app: Hono; readonly db: ReturnType["db"]; - readonly databaseUrl: string; readonly port: number; readonly grantStore: ReturnType; readonly principalKeyStore: PrincipalKeyStore; @@ -92,26 +90,24 @@ export type MountedCorbits = { stop(): void; }; -export async function mountCorbits({ +export function mountCorbits({ app, db, - databaseUrl, port, grantStore, principalKeyStore, credentialCipher, sidecarRouter, lookups, -}: MountCorbitsDeps): Promise { +}: MountCorbitsDeps): MountedCorbits { const log = getLogger(["hub"]); const requireGrant = createRequireGrant({ grantStore, conditionRegistry: grantConditionRegistry, }); - const { db: mailboxDb } = createMailboxDb(databaseUrl); const mailboxBus = createInMemoryMailboxEventBus(); - const persistMail = createMailboxPersist(mailboxDb, { + const persistMail = createMailboxPersist(db, { upstream: async () => [], authorizeSender: createHubMailboxAuthorizeSender(db), bus: mailboxBus, @@ -120,15 +116,14 @@ export async function mountCorbits({ // mutating it in place ensures agent outbound mail also lands in principal inboxes. lookups.persistMail = persistMail; - { - const artifactsApi = new Hono(); - mountArtifacts(artifactsApi, { + app.route( + TENANT_PREFIX, + createArtifactRoutes({ db, contentStore: InlineContentStore, requireGrant, - }); - app.route(TENANT_PREFIX, artifactsApi); - } + }), + ); app.route( TENANT_PREFIX, @@ -144,7 +139,6 @@ export async function mountCorbits({ mountAgentTokens(agentTokensApp, { db, requireGrant: requireGrant("credential:*", "create"), - resolveTenantId: (ctx) => (ctx as { get(key: "tenant"): { id: string } }).get("tenant").id, // Scoped to the `workflow` source asset so the workbench can mint the // token before the deploy that creates the run. resolveDefinition: async (tenantId, definitionId) => { @@ -161,6 +155,9 @@ export async function mountCorbits({ app.route(TENANT_PREFIX, agentTokensApp); } + // The run routes sit at the hub root outside `/api/tenants/:tenantId`, so + // `verify` seeds `tenant` on the context from the run named by + // `x-workflow-run-address` before `createAgentTokenVerifier` reads it. const verifyAgentToken = createAgentTokenVerifier({ db }); const resolveAgentRun = async (runAddress: string) => { if (runAddress === "") return null; @@ -174,82 +171,83 @@ export async function mountCorbits({ runId: run.id, }; }; + const agentTokenAuth = { + verify: async (ctx: unknown) => { + const c = ctx as { + req: { header(name: string): string | undefined }; + set(key: "tenant", value: { id: string }): void; + }; + const runScope = await resolveAgentRun(c.req.header("x-workflow-run-address") ?? ""); + if (runScope === null) return undefined; + c.set("tenant", { id: runScope.tenantId }); + return verifyAgentToken(c as Parameters[0]); + }, + resolveRun: resolveAgentRun, + }; { - const workflowArtifactsApi = new Hono(); const workflowRunAuthenticator = createWorkflowRunAuthenticator({ db }); - mountWorkflowArtifacts(workflowArtifactsApi, { - db, - contentStore: InlineContentStore, - resolveRunScope: (token, runAddress) => workflowRunAuthenticator.resolve(token, runAddress), - agentToken: { - verify: (ctx) => verifyAgentToken(ctx), - resolveRun: resolveAgentRun, - }, - }); - app.route("/api/workflow-artifacts", workflowArtifactsApi); + app.route( + "/api/workflow-artifacts", + createWorkflowArtifactRoutes({ + db, + contentStore: InlineContentStore, + resolveRunScope: (token, runAddress) => workflowRunAuthenticator.resolve(token, runAddress), + agentToken: agentTokenAuth, + }), + ); } { const mailboxApp = new Hono(); mailboxApp.use("/me/inbox/send", captureMailboxRequest()); - mountMailbox(mailboxApp, { - db: mailboxDb, - bus: mailboxBus, - resolvePrincipal: (ctx) => { - const c = ctx as { get(key: "tenant" | "principal"): { id: string } }; - return { - tenantId: c.get("tenant").id, - principalId: c.get("principal").id, - }; - }, - senderAddressFor: async (principal) => { - const [tenantRow] = await db - .select({ domain: tenantTable.domain }) - .from(tenantTable) - .where(eq(tenantTable.id, principal.tenantId)) - .limit(1); - if (tenantRow === undefined) { - throw new Error(`no tenant "${principal.tenantId}" to address a mailbox sender from`); - } - const [principalRow] = await db - .select({ refId: principalTable.refId }) - .from(principalTable) - .where(eq(principalTable.id, principal.principalId)) - .limit(1); - if (principalRow === undefined) { - throw new Error(`no principal "${principal.principalId}" to address a mailbox sender as`); - } - // Small models lowercase addresses before replying, so stamp lowercase canonically. - return `${principalRow.refId}@${tenantRow.domain}`.toLowerCase(); - }, - deliver: createMailboxDeliver({ app, persistMail }), - }); + mailboxApp.route( + "/", + createMailboxRoutes({ + db, + bus: mailboxBus, + requireGrant, + senderAddressFor: async (principal) => { + const [tenantRow] = await db + .select({ domain: tenantTable.domain }) + .from(tenantTable) + .where(eq(tenantTable.id, principal.tenantId)) + .limit(1); + if (tenantRow === undefined) { + throw new Error(`no tenant "${principal.tenantId}" to address a mailbox sender from`); + } + const [principalRow] = await db + .select({ refId: principalTable.refId }) + .from(principalTable) + .where(eq(principalTable.id, principal.principalId)) + .limit(1); + if (principalRow === undefined) { + throw new Error( + `no principal "${principal.principalId}" to address a mailbox sender as`, + ); + } + // Small models lowercase addresses before replying, so stamp lowercase canonically. + return `${principalRow.refId}@${tenantRow.domain}`.toLowerCase(); + }, + deliver: createMailboxDeliver({ app, persistMail }), + }), + ); app.route(`${TENANT_PREFIX}/mailbox`, mailboxApp); } - const systemTriggerMailRouter: HookMailRouter = { - routeMail: (address, rawMessage, authenticatedSender, messageId) => { + const systemTriggerMailRouter: HookRouter = { + routeMail: async (address, rawMessage, authenticatedSender, messageId, runGrants) => { if (!sidecarRouter.getRoutableAddresses().includes(address)) { return false; } - void sidecarRouter - .routeMail(address, rawMessage, authenticatedSender, messageId) - .catch((error: unknown) => { - reportError(error, { - operation: "hub.systemTriggerMailRouter.routeMail", - extra: { address, authenticatedSender }, - }); - }); - return true; - }, - sendRunGrants: (address, runId, stepGrants, senderIdentities) => - sidecarRouter.sendRunGrants( + return sidecarRouter.routeMail( address, - runId, - stepGrants as Parameters[2], - senderIdentities as Parameters[3], - ), + rawMessage, + authenticatedSender, + messageId, + runGrants, + ); + }, }; app.route( @@ -319,22 +317,23 @@ export async function mountCorbits({ cronTicker.start(); { - const memoryApp = new Hono(); const memory = createMemory({ - app: memoryApp, config: loadMemoryConfig(), grantStore, conditionRegistry: grantConditionRegistry, }); - app.route("/", memoryApp); + app.route( + `${TENANT_PREFIX}/memory`, + createMemoryRoutes({ + memory, + requireGrant, + }), + ); const workflowMemoryApi = new Hono(); mountWorkflowMemory(workflowMemoryApi, { memory, - agentToken: { - verify: (ctx) => verifyAgentToken(ctx), - resolveRun: resolveAgentRun, - }, + agentToken: agentTokenAuth, }); app.route("/api/workflow-memory", workflowMemoryApi); } @@ -431,13 +430,15 @@ export async function mountCorbits({ app.route(TENANT_PREFIX, mcpApi); } - await installWebhooks({ - app, - db, - credentialCipher, - principalKeyStore, - router: systemTriggerMailRouter, - }); + app.route( + "/api/hooks", + createHookRoutes({ + db, + credentialCipher, + principalKeyStore, + router: systemTriggerMailRouter, + }), + ); return { stop() { diff --git a/apps/hub/src/migrate.ts b/apps/hub/src/migrate.ts index 973198702..959f5f0f8 100644 --- a/apps/hub/src/migrate.ts +++ b/apps/hub/src/migrate.ts @@ -5,35 +5,30 @@ // idempotent migration right after, exactly the way upstream // Interchange applies its schema — no separate migration-runner // service, no ledger table, just the hub doing the work itself. -import { runMigrations } from "@intx/db"; +import { runMigrations, type createDB, type DBConfig } from "@intx/db"; import { sql } from "drizzle-orm"; import postgres from "postgres"; -import { applyAgentTokenMigrations } from "@corbits/agent-token/migrations"; +import { runAgentTokenMigrations } from "@corbits/agent-token/migrations"; import { runCronMigrations } from "@corbits/cron/migrations"; -import { createMailboxDb, runMailboxMigrations } from "@corbits/mailbox"; +import { runMailboxMigrations } from "@corbits/mailbox"; import { runArtifactMigrations } from "@corbits/artifacts"; -import { runMemoryMigrations } from "@corbits/memory"; +import { runMemoryMigrations } from "@corbits/memory/migrations"; +import { runWebhookMigrations } from "@corbits/webhooks/migrations"; -export interface HubMigrateConfig { - host: string; - port: number; - user: string; - password: string; - database: string; +export type HubMigrateConfig = DBConfig & { schema?: string; -} +}; + +type HubDatabase = ReturnType["db"]; -async function platformSchemaPresent( - db: Parameters[0], - schema: string, -): Promise { +async function platformSchemaPresent(db: HubDatabase, schema: string): Promise { const rows = await db.execute<{ present: string | null }>( sql`select to_regclass(${`"${schema}"."user"`}) as present`, ); return rows[0]?.present != null; } -export function databaseUrlFrom(config: HubMigrateConfig): string { +function databaseUrlFrom(config: HubMigrateConfig): string { return ( `postgres://${encodeURIComponent(config.user)}:${encodeURIComponent(config.password)}` + `@${config.host}:${String(config.port)}/${config.database}` @@ -43,16 +38,9 @@ export function databaseUrlFrom(config: HubMigrateConfig): string { /** * Apply the platform schema plus every mounted Corbits package's own * migration, in the order the hub mounts them: cron, agent tokens, mailbox, - * artifacts, memory. `@corbits/webhooks` owns no table of its own, so it has no - * migration step. `db` is the same drizzle handle the rest - * of the hub uses, so `runArtifactMigrations` (which takes a drizzle - * db, not a URL) shares the one connection pool rather than opening - * its own. + * artifacts, memory, webhooks. */ -export async function migrateHub( - config: HubMigrateConfig, - db: Parameters[0], -): Promise { +export async function migrateHub(config: HubMigrateConfig, db: HubDatabase): Promise { // Concurrent boots serialize on a session-level advisory lock held by a // dedicated connection. No transaction stays open, so a single-backend // database like PGlite still serves the migrations' other connections. @@ -71,12 +59,13 @@ export async function migrateHub( const MIGRATE_LOCK_KEY = 7_460_391_205; -async function applyMigrations( - config: HubMigrateConfig, - db: Parameters[0], -): Promise { - const databaseUrl = databaseUrlFrom(config); +async function applyMigrations(config: HubMigrateConfig, db: HubDatabase): Promise { const schema = config.schema ?? "public"; + const ftsLanguageRaw = process.env["FTS_LANGUAGE"]; + const ftsLanguage = + ftsLanguageRaw !== undefined && ftsLanguageRaw.trim() !== "" + ? ftsLanguageRaw.trim() + : "english"; // Upstream's platform SQL is one-shot (no ledger, no IF NOT EXISTS), // so it runs only on a database that has no platform tables yet. @@ -84,15 +73,9 @@ async function applyMigrations( await runMigrations(config, { schema }); } await runCronMigrations(config, { schema }); - await applyAgentTokenMigrations(databaseUrl, { tenantSchema: schema }); - - const mailboxDb = createMailboxDb(databaseUrl); - try { - await runMailboxMigrations(mailboxDb.db); - } finally { - await mailboxDb.close(); - } - - await runArtifactMigrations(db); - await runMemoryMigrations(databaseUrl); + await runAgentTokenMigrations(config, { schema }); + await runMailboxMigrations(config, { schema }); + await runArtifactMigrations(config, { schema }); + await runMemoryMigrations(config, { schema, ftsLanguage }); + await runWebhookMigrations(config, { schema }); } diff --git a/apps/hub/src/server.ts b/apps/hub/src/server.ts index 704d58da5..b74bf6cf4 100644 --- a/apps/hub/src/server.ts +++ b/apps/hub/src/server.ts @@ -56,7 +56,7 @@ import { import { MAX_SIDECAR_FRAME_BYTES } from "@intx/types/sidecar"; import { upgradeWebSocket, websocket } from "hono/bun"; import { mountCorbits } from "./corbits-mount"; -import { databaseUrlFrom, migrateHub } from "./migrate"; +import { migrateHub } from "./migrate"; import { createProcessSidecarProvisioner, readProcessProvisionerConfig, @@ -518,10 +518,9 @@ export async function createHubServer({ }), }); - const corbits = await mountCorbits({ + const corbits = mountCorbits({ app, db, - databaseUrl: databaseUrlFrom(dbConfig), port, grantStore, principalKeyStore, diff --git a/apps/hub/src/workflow-run-authenticator.ts b/apps/hub/src/workflow-run-authenticator.ts index 7caf0a0cf..6e6a581b8 100644 --- a/apps/hub/src/workflow-run-authenticator.ts +++ b/apps/hub/src/workflow-run-authenticator.ts @@ -5,7 +5,7 @@ import { workflowRun } from "@intx/db/schema"; // The one concrete `WorkflowRunAuthenticator` every workflow-run-authenticated // Corbits surface takes structurally (`@corbits/artifacts`' -// `mountWorkflowArtifacts`): a sidecar bearer token + run address resolve to +// `createWorkflowArtifactRoutes`): a sidecar bearer token + run address resolve to // the tenant/principal/run it names. The token must belong to a live // allocation, and the run must be that allocation's anchor run or one of its // children, in the allocation's tenant. diff --git a/apps/web/package.json b/apps/web/package.json index e829420c5..329478221 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -19,8 +19,8 @@ "dependencies": { "@corbits/credential-mcp": "^0.1.0", "@corbits/error-sink": "workspace:*", - "@corbits/mcp": "^0.3.0", - "@corbits/react-ui": "0.3.0", + "@corbits/mcp": "^0.3.1", + "@corbits/react-ui": "0.3.1", "@corbits/worker": "workspace:*", "@intx/mime": "0.5.0", "@intx/types": "workspace:*", diff --git a/bun.lock b/bun.lock index 5d5aebaea..dda60f282 100644 --- a/bun.lock +++ b/bun.lock @@ -9,11 +9,12 @@ }, "devDependencies": { "@copilotkit/aimock": "^1.43.0", - "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", - "@corbits/codex-provider": "0.1.2", - "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", - "@corbits/ollama-adapter": "github:corbitsdev/corbits-ollama-adapter#0387eb17bee5ac87530e9c48bb3bffd9a6998ded", - "@corbits/xai-provider": "0.1.2", + "@corbits/agent-token": "0.2.2", + "@corbits/artifacts": "0.3.0", + "@corbits/codex-provider": "0.1.3", + "@corbits/mailbox": "0.2.1", + "@corbits/ollama-adapter": "0.2.1", + "@corbits/xai-provider": "0.1.3", "@intx/agent": "workspace:*", "@intx/crypto": "workspace:*", "@intx/db": "workspace:*", @@ -40,18 +41,18 @@ "name": "@workbench/hub", "version": "0.0.1", "dependencies": { - "@corbits/agent-token": "github:corbitsdev/corbits-agent-token#ed286f4592190f911de480ef5330ce03d8605f00", - "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", - "@corbits/codex-provider": "0.1.2", - "@corbits/credential-http": "^0.1.0", - "@corbits/cron": "0.2.1", + "@corbits/agent-token": "0.2.2", + "@corbits/artifacts": "0.3.0", + "@corbits/codex-provider": "0.1.3", + "@corbits/credential-http": "^0.1.2", + "@corbits/cron": "0.2.2", "@corbits/error-sink": "workspace:*", - "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", - "@corbits/mcp": "^0.3.0", - "@corbits/memory": "github:corbitsdev/corbits-memory#8be608becc749fa0795a899826277823dca6db3e", - "@corbits/oauth-core": "^0.3.0", - "@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d", - "@corbits/xai-provider": "0.1.2", + "@corbits/mailbox": "0.2.1", + "@corbits/mcp": "^0.3.1", + "@corbits/memory": "0.2.1", + "@corbits/oauth-core": "^0.3.2", + "@corbits/webhooks": "0.3.2", + "@corbits/xai-provider": "0.1.3", "@intx/authz": "workspace:*", "@intx/crypto": "0.5.0", "@intx/db": "workspace:*", @@ -100,8 +101,8 @@ "dependencies": { "@corbits/credential-mcp": "^0.1.0", "@corbits/error-sink": "workspace:*", - "@corbits/mcp": "^0.3.0", - "@corbits/react-ui": "0.3.0", + "@corbits/mcp": "^0.3.1", + "@corbits/react-ui": "0.3.1", "@corbits/worker": "workspace:*", "@intx/mime": "0.5.0", "@intx/types": "workspace:*", @@ -168,10 +169,10 @@ "name": "@corbits/worker", "version": "0.0.3", "dependencies": { - "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", + "@corbits/artifacts": "0.3.0", "@corbits/deferred-tools": "workspace:*", - "@corbits/mcp": "^0.3.0", - "@corbits/memory": "github:corbitsdev/corbits-memory#8be608becc749fa0795a899826277823dca6db3e", + "@corbits/mcp": "^0.3.1", + "@corbits/memory": "0.2.1", "@intx/tools-mail": "0.5.0", "@intx/tools-posix": "0.5.0", }, @@ -393,8 +394,8 @@ "@corbits/mailbox", ], "overrides": { - "@corbits/oauth-core": "^0.3.0", - "@corbits/openai-responses": "^0.2.1", + "@corbits/oauth-core": "^0.3.2", + "@corbits/openai-responses": "^0.2.2", "@intx/agent": "workspace:*", "@intx/authz": "workspace:*", "@intx/crypto": "0.5.0", @@ -502,11 +503,11 @@ "@copilotkit/aimock": ["@copilotkit/aimock@1.45.1", "", { "peerDependencies": { "jest": ">=29", "vitest": ">=3" }, "optionalPeers": ["jest", "vitest"], "bin": { "aimock": "dist/aimock-cli.js", "llmock": "dist/cli.js" } }, "sha512-JYf6ThCKD8eNMVm0PYsfu1wASy2E2T8VCVoWnEpWHNV2QiSv4yMJ6JHTPm5Gp76azKqme2F43L6siFA1gU7UKQ=="], - "@corbits/agent-token": ["@corbits/agent-token@github:corbitsdev/corbits-agent-token#ed286f4", { "dependencies": { "@intx/db": "^0.3.0", "arktype": "^2.2.0", "drizzle-orm": "^0.45.2", "hono": "^4.11.9", "postgres": "^3.4.8" } }, "corbitsdev-corbits-agent-token-ed286f4", "sha512-k7puSwtCC2ZF7W0lzswtBoKxu4n+VjWwtzf8oa1OWe72FEI4TDdQ5gybqq/oW9XJAUHeCkjPL9bdwxG6C2TfpQ=="], + "@corbits/agent-token": ["@corbits/agent-token@0.2.2", "", { "dependencies": { "arktype": "^2.2.0" }, "peerDependencies": { "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "drizzle-orm": "^0.45.2", "hono": "^4.11.9", "postgres": "^3.4.8" } }, "sha512-w3q9sNAS3d/Z531dFLvNb1l7BewlSWEyC1yG9erZN7lKRsCszbYXZTy69dKWhV+JR/VpXFkbpDmwjqQFqGfvyw=="], - "@corbits/artifacts": ["@corbits/artifacts@github:corbitsdev/corbits-artifacts#4a2a3c2", { "dependencies": { "@hono/standard-validator": "^0.2.3" }, "peerDependencies": { "@intx/agent": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "^2.1.29", "drizzle-orm": "^0.45.2", "hono": "^4.12.32", "hono-openapi": "^1.2.0", "postgres": "^3.4.9" } }, "corbitsdev-corbits-artifacts-4a2a3c2", "sha512-UY5nkQ31kGmYgqgIOSlbigwvm7c/s2kpqEbF6Oqo0rzoS1RejpPp+w/Yim6h2MRpYCl5CXgAET1MhkDboHuWBA=="], + "@corbits/artifacts": ["@corbits/artifacts@0.3.0", "", { "dependencies": { "@hono/standard-validator": "^0.2.3", "arktype": "^2.2.3" }, "peerDependencies": { "@intx/agent": "^0.5.0", "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "@intx/hub-common": "0.5.0", "@intx/types": "^0.5.0", "drizzle-orm": "^0.45.2", "hono": "^4.12.32", "hono-openapi": "^1.2.0", "postgres": "^3.4.9" }, "optionalPeers": ["@intx/agent"] }, "sha512-RMAE0B3iuQDEDc1Fn+3arf9GaTmwosLXEkcjf+EFF1DyjL7flG1BmvGfkoUSdVSN3BOF8/iIvt4GSnFLYP2fMw=="], - "@corbits/codex-provider": ["@corbits/codex-provider@0.1.2", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/oauth-core": "^0.2.0", "@corbits/openai-responses": "^0.2.0", "@intx/inference": "^0.4.0", "@intx/types": "^0.4.0" } }, "sha512-jIKDuydaAOF1xLC6iZgtSEAIcuaXtGGxsvFUm4ZBH30wDDVZDGIAokPHtROZGlbImQ9ElaoTShDfHSY2iP5kdQ=="], + "@corbits/codex-provider": ["@corbits/codex-provider@0.1.3", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/oauth-core": "^0.3.0", "@corbits/openai-responses": "^0.2.2", "@intx/inference": "^0.5.0", "@intx/types": "^0.5.0" } }, "sha512-q0eehHeGSP0/9uaZKmWeW4dR/eNKC/ZtA3jWhGL6idjMXN46Be0rluAUAORm01ZmIYkz7WfXvy7uTXvR4GYb9Q=="], "@corbits/credential-header": ["@corbits/credential-header@0.1.0", "", { "peerDependencies": { "@intx/harness": ">=0.4.0", "@intx/types": ">=0.4.0" } }, "sha512-C/Jse7CeqwrwSA53npZb1Dm0ruEd7FOqhIqFMMsJy/yl5Dorc4Y0i/5bPl3eIT1ggZKiy7rJIo5rRLdJIi382g=="], @@ -514,31 +515,31 @@ "@corbits/credential-mcp": ["@corbits/credential-mcp@0.1.0", "", { "peerDependencies": { "@intx/harness": ">=0.4.0", "@intx/types": ">=0.4.0" } }, "sha512-QVetVsrFHXzAoJGMASpdScHxtz7jshpn4Xg0ZtThifORYSG2DIg0SxmpJD/S8KUYEHx1jRrMiK5ncinJHg+i2Q=="], - "@corbits/cron": ["@corbits/cron@0.2.1", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@intx/db": "^0.4.0", "@intx/hub-api": "^0.4.0", "drizzle-orm": "^0.45.3", "hono": "^4.13.8", "postgres": "^3.4.9" } }, "sha512-eJm1nD9YSf9ehgkpE+1+9oiVfhZFfgb+Ck3jeHw2koTIeh9cwQ5eoLXigHlmDN+hAqqGeE+v/GBaHOuDy6uYbQ=="], + "@corbits/cron": ["@corbits/cron@0.2.2", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "drizzle-orm": "^0.45.3", "hono": "^4.13.8", "postgres": "^3.4.9" } }, "sha512-YVJDY1oApup2Cd2QgO6187T3PEFjkhbv35KylhiDN8/Z00ZCMg1lJVe57hh+IWU0BOx5ODRECWojHS+K0nkY3A=="], "@corbits/deferred-tools": ["@corbits/deferred-tools@workspace:packages/deferred-tools"], "@corbits/error-sink": ["@corbits/error-sink@workspace:packages/error-sink"], - "@corbits/mailbox": ["@corbits/mailbox@github:corbitsdev/corbits-mailbox#65590a8", { "dependencies": { "@hono/standard-validator": "0.2.3", "@intx/crypto": "0.3.0", "@standard-community/standard-json": "0.3.5", "@standard-community/standard-openapi": "0.2.9", "arktype": "2.1.29", "hono-openapi": "1.3.1" }, "peerDependencies": { "@intx/log": "^0.2.2", "drizzle-orm": "^0.45.2", "hono": "^4.12.0", "postgres": "^3.4.0" } }, "corbitsdev-corbits-mailbox-65590a8", "sha512-tK7xXcL780iX3I+5R68gmyqgEA0uwYO1qdTDNn/LF6tOiFIm0lt2szjspJ8JMKMTzKC6SSai03p723rEGM1jOg=="], + "@corbits/mailbox": ["@corbits/mailbox@0.2.1", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "@intx/log": "^0.5.0", "@intx/mailbox": "^0.5.0", "@intx/mime": "^0.5.0", "@intx/types": "^0.5.0", "drizzle-orm": "^0.45.2", "hono": "^4.12.0", "hono-openapi": "^1.3.1", "postgres": "^3.4.0" } }, "sha512-+DOQ6FMD5CwqHENZmGPI2hoTrDMc1Do1yRpqiLB2uOczRMwxYkC4+v4iMvwnvPAaEFZ+gh0NrRl+CFxuUUzH0w=="], "@corbits/mcp": ["@corbits/mcp@0.3.1", "", { "dependencies": { "@corbits/oauth-core": "^0.3.0", "@intx/types": "^0.5.0", "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/credential-http": "^0.1.0", "@intx/agent": "^0.5.0", "@intx/db": "^0.5.0", "@intx/harness": "^0.4.0 || ^0.5.0", "@intx/hub-api": "^0.5.0", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" }, "optionalPeers": ["@corbits/credential-http", "@intx/db", "@intx/hub-api", "drizzle-orm", "hono"] }, "sha512-+IunjzsLvO2D6y6jaUPL5Ma6oF6hhzzjEmwpuzl4r9XiVJ8dWWBbf72/PAIh568Wop5GBMRkmHRgzwIFYgs/+A=="], - "@corbits/memory": ["@corbits/memory@github:corbitsdev/corbits-memory#8be608b", { "dependencies": { "@intx/agent": "0.2.2", "@intx/authz": "0.2.2", "@intx/hub-api": "0.2.2", "@intx/log": "0.2.2", "@intx/types": "^0.3.0", "@intx/workflow": "0.2.2", "arktype": "^2.1.29", "drizzle-orm": "^0.45.1", "hono": "^4.9.0", "hono-openapi": "^1.3.1", "postgres": "^3.4.7" } }, "corbitsdev-corbits-memory-8be608b", "sha512-7NcVcz3IHFTfx6ICQT+4gjX1PBNLqjlCqoEOPDFVW7wc4e2+Ueg9wOZQ+0MVttpQCaDg6MObl4kie410UQm/sQ=="], + "@corbits/memory": ["@corbits/memory@0.2.1", "", { "dependencies": { "arktype": "^2.1.29" }, "peerDependencies": { "@intx/agent": "^0.5.0", "@intx/authz": "^0.5.0", "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "@intx/log": "^0.5.0", "@intx/types": "^0.5.0", "@intx/workflow": "^0.5.0", "drizzle-orm": "^0.45.2", "hono": "^4.12.31", "hono-openapi": "^1.3.1", "postgres": "^3.4.9" } }, "sha512-ybc82319G7wuHRf9YOytbehcjKMvp5V4yNMav+t5EXFo7wkIAiuTUWI/9Ouf+IpK7DHT32DWeX8alWJvyCXuUQ=="], "@corbits/oauth-core": ["@corbits/oauth-core@0.3.2", "", { "dependencies": { "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "@intx/hub-common": "^0.5.0", "@intx/types": "^0.5.0", "arktype": "^2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "sha512-2wBKzaWA/hEObDq+yehuHFh6ZsPDRoyGgBBhBiN/gPPWOw+DCCGogTkzTwL0yPPM04NU+RUKEcgBka7M7LVmGw=="], - "@corbits/ollama-adapter": ["@corbits/ollama-adapter@github:corbitsdev/corbits-ollama-adapter#0387eb1", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-ollama-adapter-0387eb1", "sha512-O1dl5lSwWzNGJibQLlpYDFKJZWpstgHX519bl8oe1ALL/KHszT/tBDlpsD9jN3hmQD3jV2L1jNwvOwLOBLtpyw=="], + "@corbits/ollama-adapter": ["@corbits/ollama-adapter@0.2.1", "", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": "^0.5.0", "@intx/log": "^0.5.0", "@intx/types": "^0.5.0" } }, "sha512-HcrpgpZvfIZYwiSS24UHXvLyV0VUh4JxxN0sGsniwWksFkHSOIQqK/C6plQpB78/qPGdMKz0KHNU2dXQ/bGKag=="], "@corbits/openai-responses": ["@corbits/openai-responses@0.2.2", "", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": "^0.5.0", "@intx/types": "^0.5.0" } }, "sha512-dp9ZVHUL+EuwveezrJr3EMQc963xBVxUlQg32P+1yUP7TZfR1rT7fwCWIUYDQIfAkeaOFTDfl6y/QRTIi6bVxw=="], - "@corbits/react-ui": ["@corbits/react-ui@0.3.0", "", { "dependencies": { "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "tailwind-merge": "^3.3.1" }, "peerDependencies": { "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.2.8", "lucide-react": "^0.545.0 || ^1.0.0", "react": "^18.2.0 || ^19.0.0", "react-dom": "^18.2.0 || ^19.0.0", "sonner": "^2.0.7" }, "optionalPeers": ["@radix-ui/react-dialog", "@radix-ui/react-dropdown-menu", "@radix-ui/react-tooltip", "sonner"] }, "sha512-yOIL6ifFR9tKUiPzCGWQ5ulBYMMmXUBxgnfgV1tK15FUAlIn401FzJ6riPGVrTjsX0VU0LewUyBP7S6vAwz8Fg=="], + "@corbits/react-ui": ["@corbits/react-ui@0.3.1", "", { "dependencies": { "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "tailwind-merge": "^3.3.1" }, "peerDependencies": { "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.2.8", "lucide-react": "^0.545.0 || ^1.0.0", "react": "^18.2.0 || ^19.0.0", "react-dom": "^18.2.0 || ^19.0.0", "sonner": "^2.0.7" }, "optionalPeers": ["@radix-ui/react-dialog", "@radix-ui/react-dropdown-menu", "@radix-ui/react-tooltip", "sonner"] }, "sha512-qAi4UiRuxIfv+0XaiCFdB+nwX39/YEpvhhkW0W0urrA5E81iaX1s7PCqJze9EtaSop9AxvlJ82V7Bd1qgitqaQ=="], - "@corbits/webhooks": ["@corbits/webhooks@github:corbitsdev/webhooks#3f4f831", { "dependencies": { "@intx/crypto": "^0.3.0", "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/mime": "^0.3.0", "@intx/types": "^0.3.0", "hono": "4.11.9" } }, "corbitsdev-webhooks-3f4f831", "sha512-PAeUxc6VzafQikgCOUrfjTFIR8WgZpV2epubarPp3m0fGS4pFreSaOIyoPu+Kl3TgGRirnpb9qlXGV7BOxF8Pg=="], + "@corbits/webhooks": ["@corbits/webhooks@0.3.2", "", { "peerDependencies": { "@intx/crypto": "^0.5.0", "@intx/db": "^0.5.0", "@intx/hub-api": "^0.5.0", "@intx/hub-common": "^0.5.0", "@intx/hub-sessions": "^0.5.0", "@intx/mime": "^0.5.0", "@intx/types": "^0.5.0", "drizzle-orm": "^0.45.1", "hono": "^4.11.9", "postgres": "^3.4.8" } }, "sha512-hE9dw6VFV4lJoMX60vhVChsB5hyhqXguW5YvdCSNFg/ehVHRAZByfnWwBx9owap/KYwL26Z51T8qRVDc2FE6nA=="], "@corbits/worker": ["@corbits/worker@workspace:packages/worker"], - "@corbits/xai-provider": ["@corbits/xai-provider@0.1.2", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/oauth-core": "^0.2.0", "@corbits/openai-responses": "^0.2.0", "@intx/inference": "^0.4.0", "@intx/types": "^0.4.0" } }, "sha512-4gCSLehuE922UG5DeAQLMA3iaq5MzSEgKNuq2Lb4aJqEYsQ85t8rTmu0DXs+T6mHNq2cbCI9eCF4aXuyv420iA=="], + "@corbits/xai-provider": ["@corbits/xai-provider@0.1.3", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@corbits/oauth-core": "^0.3.0", "@corbits/openai-responses": "^0.2.2", "@intx/inference": "^0.5.0", "@intx/types": "^0.5.0" } }, "sha512-/fgvDATeadSabwFq8z/uEuM3TVZjWe7J571logcU26Ce7zwkg/wzUrJeva/JNQOcvqYVmzyCuSLDnNaPbaT8Wg=="], "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], @@ -876,9 +877,9 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.64.12", "", { "os": "win32", "cpu": "x64" }, "sha512-j5rXD4R/4DM5+V991AA9xOnG2Rwu2g3p5TPgu1Eu7apju0NUrLl+Bn/KpWc6wYJtlXqzwz1r+LNV1JeBU4VGvw=="], - "@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="], + "@standard-community/standard-json": ["@standard-community/standard-json@0.3.7", "", { "dependencies": { "quansync": "^0.2.11" }, "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.20.0", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.4.2", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-9reHaeW6k99yMHCgqMnLZgrKGT/fxfwYCdzd8nM4gHL4ez8MO0AlC9seGXoJNVEaO5LiE7DVCKl46wNE4wVFRA=="], - "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.9", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-htj+yldvN1XncyZi4rehbf9kLbu8os2Ke/rfqoZHCMHuw34kiF3LP/yQPdA0tQ940y8nDq3Iou8R3wG+AGGyvg=="], + "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.11", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.20.0", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.4.2", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-Fz+2K6JMJZwf4QoyZrq1smK+YeEwDGXqz2j8Y77rOHgmrk9StcEyTKkMf3n/4xOmEup5WZ2eOp0xiC7Slxx9gg=="], "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -1368,18 +1369,10 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@corbits/mailbox/hono-openapi": ["hono-openapi@1.3.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.11.2", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-NLVeVkhKZ3drmQNEIPac8HX8Y54uf1hJAgIM/7MfDsaeVVmB+QILWQxx5x3R3NvRHgedcbEbOCGY2uR7WQYyMw=="], - "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], "@npmcli/agent/lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="], - "@workbench/hub/@corbits/artifacts": ["@corbits/artifacts@github:corbitsdev/corbits-artifacts#4a2a3c2", { "dependencies": { "@hono/standard-validator": "^0.2.3" }, "peerDependencies": { "@intx/agent": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "^2.1.29", "drizzle-orm": "^0.45.2", "hono": "^4.12.32", "hono-openapi": "^1.2.0", "postgres": "^3.4.9" } }, "corbitsdev-corbits-artifacts-4a2a3c2", "sha512-UY5nkQ31kGmYgqgIOSlbigwvm7c/s2kpqEbF6Oqo0rzoS1RejpPp+w/Yim6h2MRpYCl5CXgAET1MhkDboHuWBA=="], - - "@workbench/hub/@corbits/mailbox": ["@corbits/mailbox@github:corbitsdev/corbits-mailbox#65590a8", { "dependencies": { "@hono/standard-validator": "0.2.3", "@intx/crypto": "0.3.0", "@standard-community/standard-json": "0.3.5", "@standard-community/standard-openapi": "0.2.9", "arktype": "2.1.29", "hono-openapi": "1.3.1" }, "peerDependencies": { "@intx/log": "^0.2.2", "drizzle-orm": "^0.45.2", "hono": "^4.12.0", "postgres": "^3.4.0" } }, "corbitsdev-corbits-mailbox-65590a8", "sha512-tK7xXcL780iX3I+5R68gmyqgEA0uwYO1qdTDNn/LF6tOiFIm0lt2szjspJ8JMKMTzKC6SSai03p723rEGM1jOg=="], - - "@workbench/hub/@corbits/memory": ["@corbits/memory@github:corbitsdev/corbits-memory#8be608b", { "dependencies": { "@intx/agent": "0.2.2", "@intx/authz": "0.2.2", "@intx/hub-api": "0.2.2", "@intx/log": "0.2.2", "@intx/types": "^0.3.0", "@intx/workflow": "0.2.2", "arktype": "^2.1.29", "drizzle-orm": "^0.45.1", "hono": "^4.9.0", "hono-openapi": "^1.3.1", "postgres": "^3.4.7" } }, "corbitsdev-corbits-memory-8be608b", "sha512-7NcVcz3IHFTfx6ICQT+4gjX1PBNLqjlCqoEOPDFVW7wc4e2+Ueg9wOZQ+0MVttpQCaDg6MObl4kie410UQm/sQ=="], - "better-call/@better-auth/utils": ["@better-auth/utils@0.5.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA=="], "cacache/lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="], @@ -1390,10 +1383,6 @@ "hono-openapi/@hono/standard-validator": ["@hono/standard-validator@0.4.0", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "hono": ">=4.11.2" } }, "sha512-MxOm1asDh9j7c1D0KiWwppSbFgAQxTRO4YEhjMrJn3lF1BIcXenPdgjSGKDRfQmZdaTaMA8slQLETgVOKyJxFw=="], - "hono-openapi/@standard-community/standard-json": ["@standard-community/standard-json@0.3.7", "", { "dependencies": { "quansync": "^0.2.11" }, "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.20.0", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.4.2", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-9reHaeW6k99yMHCgqMnLZgrKGT/fxfwYCdzd8nM4gHL4ez8MO0AlC9seGXoJNVEaO5LiE7DVCKl46wNE4wVFRA=="], - - "hono-openapi/@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.11", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.20.0", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.4.2", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-Fz+2K6JMJZwf4QoyZrq1smK+YeEwDGXqz2j8Y77rOHgmrk9StcEyTKkMf3n/4xOmEup5WZ2eOp0xiC7Slxx9gg=="], - "make-fetch-happen/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], "make-fetch-happen/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], @@ -1464,8 +1453,6 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - "@workbench/hub/@corbits/mailbox/hono-openapi": ["hono-openapi@1.3.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.11.2", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-NLVeVkhKZ3drmQNEIPac8HX8Y54uf1hJAgIM/7MfDsaeVVmB+QILWQxx5x3R3NvRHgedcbEbOCGY2uR7WQYyMw=="], - "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], diff --git a/e2e/browser/zero-to-one.test.ts b/e2e/browser/zero-to-one.test.ts index 0c8accbae..3e3eae093 100644 --- a/e2e/browser/zero-to-one.test.ts +++ b/e2e/browser/zero-to-one.test.ts @@ -33,7 +33,7 @@ const FIXTURES: Fixture[] = [ response: { toolCalls: [ { - name: "artifact_create", + name: "artifact_write", arguments: JSON.stringify({ title: ARTIFACT_TITLE, kind: "document", diff --git a/e2e/hub/composition.test.ts b/e2e/hub/composition.test.ts index 7503b078a..6432bbf7a 100644 --- a/e2e/hub/composition.test.ts +++ b/e2e/hub/composition.test.ts @@ -1,15 +1,26 @@ // Exercises the hub's own composition: the stock platform routes answer, -// and a Corbits mount (mailbox) sits inside the native tenant middleware -// rather than replacing it. Platform behavior behind stock routes belongs -// to @intx/hub-api and is not re-proven here. Booting the hub runs package -// migrations, so a reachable DATABASE_URL is required and the suite skips -// without one. +// a Corbits mount (mailbox) sits inside the native tenant middleware rather +// than replacing it, and the hub-root run-scoped routes (`/api/workflow-artifacts` +// and `/api/workflow-memory`) seed the tenant context from the run named by +// `x-workflow-run-address` before `@corbits/agent-token` verifies the bearer. +// Platform behavior behind stock routes belongs to @intx/hub-api and is not +// re-proven here. Booting the hub runs package migrations, so a reachable +// DATABASE_URL is required and the suite skips without one. -import { expect, test } from "bun:test"; +import { afterAll, expect, test } from "bun:test"; +import { mintAgentToken } from "@corbits/agent-token"; +import { createDB } from "@intx/db"; +import { principal, tenant, workflowDefinition, workflowRun } from "@intx/db/schema"; import { dbGate } from "../lib/db-gate"; import { bootHub } from "../lib/hub"; +import { cleanupTenants } from "../lib/tenant-cleanup"; const describeIfDb = dbGate(process.env["DATABASE_URL"] ?? "", import.meta.path); +const seededTenantIds: string[] = []; + +afterAll(async () => { + await cleanupTenants(seededTenantIds); +}); describeIfDb("boot", () => { const first = bootHub(); @@ -35,4 +46,116 @@ describeIfDb("boot", () => { ); expect(gated.status).toBe(401); }); + + test("hub-root workflow routes authenticate an agent token against the run's tenant", async () => { + const opts = first(); + const url = new URL(process.env["DATABASE_URL"] ?? "postgres://x@localhost/x"); + const handle = createDB({ + host: url.hostname, + port: url.port === "" ? 5432 : Number(url.port), + user: decodeURIComponent(url.username), + password: decodeURIComponent(url.password), + database: url.pathname.replace(/^\//, ""), + }); + try { + const suffix = crypto.randomUUID().slice(0, 8); + const tenantId = `tnt_comp_${suffix}`; + const otherTenantId = `tnt_other_${suffix}`; + seededTenantIds.push(tenantId, otherTenantId); + const principalId = `prn_comp_${suffix}`; + const definitionId = `def_comp_${suffix}`; + const runId = `run_comp_${suffix}`; + const runAddress = `${runId}@comp-${suffix}.test`; + + await handle.db.insert(tenant).values([ + { + id: tenantId, + name: "Composition Tenant", + slug: `comp-${suffix}`, + domain: `comp-${suffix}.test`, + }, + { + id: otherTenantId, + name: "Other Tenant", + slug: `other-${suffix}`, + domain: `other-${suffix}.test`, + }, + ]); + await handle.db.insert(principal).values({ + id: principalId, + tenantId, + kind: "workflow", + refId: runId, + status: "active", + }); + await handle.db.insert(workflowDefinition).values({ + id: definitionId, + tenantId, + name: "worker", + }); + await handle.db.insert(workflowRun).values({ + id: runId, + definitionId, + anchorRunId: runId, + tenantId, + principalId, + address: runAddress, + }); + + const minted = await mintAgentToken(handle.db, { + tenantId, + definitionId, + name: "worker-hub", + }); + const otherMinted = await mintAgentToken(handle.db, { + tenantId: otherTenantId, + definitionId, + name: "other-hub", + }); + + const authedArtifacts = await opts.fetch( + new Request("http://localhost/api/workflow-artifacts/artifacts/recent", { + headers: { + authorization: `Bearer ${minted.token}`, + "x-workflow-run-address": runAddress, + }, + }), + ); + expect(authedArtifacts.status).toBe(200); + + const authedMemory = await opts.fetch( + new Request("http://localhost/api/workflow-memory/list", { + headers: { + authorization: `Bearer ${minted.token}`, + "x-workflow-run-address": runAddress, + }, + }), + ); + expect(authedMemory.status).toBe(200); + + const crossTenant = await opts.fetch( + new Request("http://localhost/api/workflow-artifacts/artifacts/recent", { + headers: { + authorization: `Bearer ${otherMinted.token}`, + "x-workflow-run-address": runAddress, + }, + }), + ); + expect(crossTenant.status).toBe(401); + + // Missing or unknown run address must answer 401 rather than throwing 500 + // when `c.get("tenant")` would otherwise be unset. + const missingRun = await opts.fetch( + new Request("http://localhost/api/workflow-memory/list", { + headers: { + authorization: `Bearer ${minted.token}`, + "x-workflow-run-address": `missing-${suffix}@comp-${suffix}.test`, + }, + }), + ); + expect(missingRun.status).toBe(401); + } finally { + await handle.close(); + } + }); }); diff --git a/e2e/hub/mailbox-persist.test.ts b/e2e/hub/mailbox-persist.test.ts index 936d909af..c854dab85 100644 --- a/e2e/hub/mailbox-persist.test.ts +++ b/e2e/hub/mailbox-persist.test.ts @@ -2,15 +2,12 @@ // suite in this directory (`composition.test.ts` boots the whole hub over // HTTP; this suite exercises `persistMail` wiring directly, without a boot). import { afterAll, expect, test } from "bun:test"; -import { eq } from "drizzle-orm"; import { createDB } from "@intx/db"; import { principal, tenant as tenantTable, workflowDefinition, workflowRun } from "@intx/db/schema"; import { createInMemoryMailboxEventBus, - createMailboxDb, createMailboxPersist, - principalMail, - type MailboxDb, + openNativeMailboxStore, } from "@corbits/mailbox"; import { createHubMailboxAuthorizeSender } from "../../apps/hub/src/mailbox-persist"; import { e2eDatabaseUrl } from "../lib/database-url"; @@ -51,11 +48,6 @@ function uid(label: string): string { async function setup() { const { db, close: closeDb } = createDB(dbConfigFromUrl(databaseUrl)); closers.push(closeDb); - const { db: mailboxDb, close: closeMailbox } = createMailboxDb(databaseUrl) as { - db: MailboxDb; - close: () => Promise; - }; - closers.push(closeMailbox); const domain = `${uid("mailbox-persist-wrap")}.test`; const tenantId = uid("tnt_mbxpw"); @@ -115,7 +107,6 @@ async function setup() { return { db, - mailboxDb, domain, tenantId, senderAddress, @@ -127,16 +118,8 @@ async function setup() { describeIfDb("hub persistMail wrapped with createMailboxPersist", () => { test("an outbound frame to two humans and one agent produces exactly two principal_mail rows and an SSE event per mailbox", async () => { - const { - db, - mailboxDb, - domain, - tenantId, - senderAddress, - agentRecipientAddress, - human1Id, - human2Id, - } = await setup(); + const { db, domain, tenantId, senderAddress, agentRecipientAddress, human1Id, human2Id } = + await setup(); const mailboxBus = createInMemoryMailboxEventBus(); const seenPrincipalIds = new Set(); @@ -147,7 +130,7 @@ describeIfDb("hub persistMail wrapped with createMailboxPersist", () => { seenPrincipalIds.add(human2Id); }); - const persistMail = createMailboxPersist(mailboxDb, { + const persistMail = createMailboxPersist(db, { upstream: async () => [], authorizeSender: createHubMailboxAuthorizeSender(db), bus: mailboxBus, @@ -173,21 +156,27 @@ describeIfDb("hub persistMail wrapped with createMailboxPersist", () => { // Exactly two durable mailbox rows -- one per human -- never one for // the agent recipient (a run address is never a mailbox). - const rows = await mailboxDb - .select() - .from(principalMail) - .where(eq(principalMail.tenantId, tenantId)); - expect(rows).toHaveLength(2); - expect(rows.map((r) => r.principalId).sort()).toEqual([human1Id, human2Id].sort()); + const h1Store = await openNativeMailboxStore(db, { + tenantId, + principalId: human1Id, + folder: "INBOX", + }); + const h2Store = await openNativeMailboxStore(db, { + tenantId, + principalId: human2Id, + folder: "INBOX", + }); + expect(h1Store.messages).toHaveLength(1); + expect(h2Store.messages).toHaveLength(1); // Every SSE subscriber fired. expect(seenPrincipalIds).toEqual(new Set([human1Id, human2Id])); }); test("redelivery of the same frame is deduped: a retried persist for the same message and recipient writes no second mailbox row", async () => { - const { db, mailboxDb, domain, tenantId, senderAddress, human1Id } = await setup(); + const { db, domain, tenantId, senderAddress, human1Id } = await setup(); - const persistMail = createMailboxPersist(mailboxDb, { + const persistMail = createMailboxPersist(db, { upstream: async () => [], authorizeSender: createHubMailboxAuthorizeSender(db), }); @@ -214,11 +203,12 @@ describeIfDb("hub persistMail wrapped with createMailboxPersist", () => { raw, }); - const rows = await mailboxDb - .select() - .from(principalMail) - .where(eq(principalMail.tenantId, tenantId)); - expect(rows).toHaveLength(1); + const h1Store = await openNativeMailboxStore(db, { + tenantId, + principalId: human1Id, + folder: "INBOX", + }); + expect(h1Store.messages).toHaveLength(1); }); test("authorizeSender resolves a live sender run to its tenant and mail domain", async () => { diff --git a/e2e/hub/mailbox-send-terminal.test.ts b/e2e/hub/mailbox-send-terminal.test.ts index b2896fd07..30bb866ed 100644 --- a/e2e/hub/mailbox-send-terminal.test.ts +++ b/e2e/hub/mailbox-send-terminal.test.ts @@ -3,13 +3,12 @@ import { afterAll, expect, test } from "bun:test"; import { Hono } from "hono"; import { createDB } from "@intx/db"; +import type { TenantEnv } from "@intx/hub-api"; import { principal, tenant } from "@intx/db/schema"; import { createInMemoryMailboxEventBus, - createMailboxDb, - mountMailbox, + createMailboxRoutes, runMailboxMigrations, - type MailboxDb, } from "@corbits/mailbox"; import { captureMailboxRequest, createMailboxDeliver } from "../../apps/hub/src/mailbox-send"; import { e2eDatabaseUrl } from "../lib/database-url"; @@ -39,13 +38,9 @@ function dbConfigFromUrl(url: string) { describeIfDb("mailbox send through the real mount", () => { async function sendToRun(triggerStatus: number, code: string) { - const { db, close } = createMailboxDb(databaseUrl) as { - db: MailboxDb; - close: () => Promise; - }; - closers.push(close); - await runMailboxMigrations(db); - const { db: hubDb, close: closeHub } = createDB(dbConfigFromUrl(databaseUrl)); + const dbConfig = dbConfigFromUrl(databaseUrl); + await runMailboxMigrations(dbConfig, { schema: "public" }); + const { db: hubDb, close: closeHub } = createDB(dbConfig); closers.push(closeHub); const suffix = crypto.randomUUID().slice(0, 8); const tenantId = `tnt_term_${suffix}`; @@ -60,23 +55,31 @@ describeIfDb("mailbox send through the real mount", () => { await hubDb .insert(principal) .values({ id: principalId, tenantId, kind: "user", refId: principalId, status: "active" }); - const app = new Hono(); + const app = new Hono(); + app.use("/tenants/:tenantId/*", async (c, next) => { + c.set("tenant", { id: tenantId } as TenantEnv["Variables"]["tenant"]); + c.set("principal", { id: principalId } as TenantEnv["Variables"]["principal"]); + await next(); + }); app.use("/tenants/:tenantId/mailbox/me/inbox/send", captureMailboxRequest()); - const mailboxApp = new Hono(); + const mailboxApp = new Hono(); mailboxApp.use("/me/inbox/send", captureMailboxRequest()); - mountMailbox(mailboxApp, { - db, - bus: createInMemoryMailboxEventBus(), - resolvePrincipal: () => ({ tenantId, principalId }), - senderAddressFor: () => "person@term.test", - deliver: createMailboxDeliver({ - app: { - request: () => - Response.json({ error: { code, message: "x" } }, { status: triggerStatus }), - }, - persistMail: () => Promise.resolve(), + mailboxApp.route( + "/", + createMailboxRoutes({ + db: hubDb, + bus: createInMemoryMailboxEventBus(), + requireGrant: () => async (_c, next) => next(), + senderAddressFor: () => "person@term.test", + deliver: createMailboxDeliver({ + app: { + request: () => + Response.json({ error: { code, message: "x" } }, { status: triggerStatus }), + }, + persistMail: () => Promise.resolve(), + }), }), - }); + ); app.route("/tenants/:tenantId/mailbox", mailboxApp); return app.request(`/tenants/${tenantId}/mailbox/me/inbox/send`, { method: "POST", diff --git a/package.json b/package.json index 1f1502eb9..583a2f4f0 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,12 @@ }, "devDependencies": { "@copilotkit/aimock": "^1.43.0", - "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", - "@corbits/codex-provider": "0.1.2", - "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", - "@corbits/ollama-adapter": "github:corbitsdev/corbits-ollama-adapter#0387eb17bee5ac87530e9c48bb3bffd9a6998ded", - "@corbits/xai-provider": "0.1.2", + "@corbits/agent-token": "0.2.2", + "@corbits/artifacts": "0.3.0", + "@corbits/codex-provider": "0.1.3", + "@corbits/mailbox": "0.2.1", + "@corbits/ollama-adapter": "0.2.1", + "@corbits/xai-provider": "0.1.3", "@intx/agent": "workspace:*", "@intx/crypto": "workspace:*", "@intx/db": "workspace:*", @@ -104,7 +105,7 @@ "better-auth": "1.6.29", "hono": "4.13.3", "@intx/mailbox": "0.5.0", - "@corbits/oauth-core": "^0.3.0", - "@corbits/openai-responses": "^0.2.1" + "@corbits/oauth-core": "^0.3.2", + "@corbits/openai-responses": "^0.2.2" } } diff --git a/packages/worker/package.json b/packages/worker/package.json index 2e89e0835..97bf71b69 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -34,10 +34,10 @@ "build": "bun run scripts/build-bundle.ts" }, "dependencies": { - "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", + "@corbits/artifacts": "0.3.0", "@corbits/deferred-tools": "workspace:*", - "@corbits/mcp": "^0.3.0", - "@corbits/memory": "github:corbitsdev/corbits-memory#8be608becc749fa0795a899826277823dca6db3e", + "@corbits/mcp": "^0.3.1", + "@corbits/memory": "0.2.1", "@intx/tools-mail": "0.5.0", "@intx/tools-posix": "0.5.0" }, diff --git a/scripts/db-setup.ts b/scripts/db-setup.ts index df1edd873..520930f0f 100644 --- a/scripts/db-setup.ts +++ b/scripts/db-setup.ts @@ -248,7 +248,14 @@ export async function setupDatabase( // would leave those tables behind. Always dropped alongside the target // schema so a reset is a true clean slate for every installed // package's tables, not only the platform's. -const PACKAGE_SCHEMAS = ["mailbox", "cron", "memory", "artifacts"] as const; +const PACKAGE_SCHEMAS = [ + "mailbox", + "cron", + "memory", + "artifacts", + "agent_token", + "webhooks", +] as const; /** * Drop the target schema and everything in it (platform tables and