From f88034f7547cde69eeb7df95ec0b40770fde9625 Mon Sep 17 00:00:00 2001 From: Yevanchen Date: Tue, 11 Aug 2026 20:19:42 +0800 Subject: [PATCH] feat(sdk): prepare TypeScript public beta --- .github/workflows/release-sdk.yml | 105 + apps/api/openapi/public-api-v1.generated.json | 90 +- .../routes/public-api-openapi-components.ts | 2 +- .../http/routes/public-api-openapi.ts | 8 +- .../modules/files/application/file-store.ts | 43 +- .../public-api/public-thread-api-presenter.ts | 3 + .../public-api/public-thread-events.ts | 87 +- .../public-thread-file-api.service.ts | 43 +- .../public-api/public-thread-presenter.ts | 7 +- .../public-api/public-thread-retrieve.ts | 31 +- .../infrastructure/driver-instance/events.ts | 21 +- .../session-resource-events.service.ts | 4 +- apps/api/tests/api-web-boundary-fixtures.ts | 12 +- apps/api/tests/api-web-boundary.test.ts | 33 +- .../helpers/public-api-http-test-fixture.ts | 42 + .../helpers/public-api-http-wechat-schema.sql | 15 + apps/api/tests/public-thread-api-fixtures.ts | 3 +- apps/api/tests/public-thread-api.e2e.test.ts | 159 +- apps/api/tests/runtime-mcp-delegation.test.ts | 25 + .../api/tests/runtime-session-outputs.test.ts | 104 +- apps/api/tests/session-resource-files.test.ts | 3 + bun.lock | 11 +- docs/architecture.md | 2 +- docs/prd/public-thread-api-surface.md | 3 + docs/prd/session-files.md | 3 +- docs/prd/typescript-sdk-public-beta.md | 80 + docs/sdk-release.md | 30 + dogfood/gogym-agent-backend-reflection.md | 12 +- package.json | 2 +- .../src/http/public-api-core.contract.ts | 14 + .../src/http/public-api-openapi.contract.ts | 62 +- .../db/drizzle/0012_session-run-artifacts.sql | 13 + pkgs/db/drizzle/meta/0012_snapshot.json | 7651 +++++++++++++++++ pkgs/db/drizzle/meta/_journal.json | 7 + pkgs/db/src/schema/file.schema.ts | 35 +- pkgs/public-api-client/CHANGELOG.md | 10 + pkgs/public-api-client/LICENSE | 201 + pkgs/public-api-client/README.md | 51 + pkgs/public-api-client/package.json | 48 +- pkgs/public-api-client/src/delegation.ts | 240 + pkgs/public-api-client/src/index.ts | 362 +- pkgs/public-api-client/src/types.ts | 215 + .../tests/delegation.test.ts | 153 + .../fixtures/cloudflare-worker/src/index.ts | 99 + .../fixtures/cloudflare-worker/wrangler.jsonc | 9 + pkgs/public-api-client/tests/package.test.ts | 183 + .../tests/public-api-client.test.ts | 352 +- .../tests/worker-runtime-smoke.mjs | 239 + pkgs/public-api-client/tsconfig.json | 1 + pkgs/public-api-client/vite.config.ts | 15 + 50 files changed, 10598 insertions(+), 345 deletions(-) create mode 100644 .github/workflows/release-sdk.yml create mode 100644 docs/prd/typescript-sdk-public-beta.md create mode 100644 docs/sdk-release.md create mode 100644 pkgs/db/drizzle/0012_session-run-artifacts.sql create mode 100644 pkgs/db/drizzle/meta/0012_snapshot.json create mode 100644 pkgs/public-api-client/CHANGELOG.md create mode 100644 pkgs/public-api-client/LICENSE create mode 100644 pkgs/public-api-client/README.md create mode 100644 pkgs/public-api-client/src/delegation.ts create mode 100644 pkgs/public-api-client/src/types.ts create mode 100644 pkgs/public-api-client/tests/delegation.test.ts create mode 100644 pkgs/public-api-client/tests/fixtures/cloudflare-worker/src/index.ts create mode 100644 pkgs/public-api-client/tests/fixtures/cloudflare-worker/wrangler.jsonc create mode 100644 pkgs/public-api-client/tests/package.test.ts create mode 100644 pkgs/public-api-client/tests/worker-runtime-smoke.mjs create mode 100644 pkgs/public-api-client/vite.config.ts diff --git a/.github/workflows/release-sdk.yml b/.github/workflows/release-sdk.yml new file mode 100644 index 00000000..df893474 --- /dev/null +++ b/.github/workflows/release-sdk.yml @@ -0,0 +1,105 @@ +name: Release SDK + +on: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: npm-sdk-release + cancel-in-progress: false + +jobs: + verify: + name: Verify SDK on Node.js ${{ matrix.node-version }} + if: github.repository == 'langgenius/mosoo' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + node-version: [22, 24] + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + fetch-depth: 1 + submodules: true + persist-credentials: false + + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: ${{ matrix.node-version }} + package-manager-cache: false + + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: canary + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Verify SDK + run: | + bun run --filter @mosoo/sdk lint + bun run --filter @mosoo/sdk tc + bun run --filter @mosoo/sdk test + + publish: + name: Publish npm Beta + needs: verify + if: github.repository == 'langgenius/mosoo' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 15 + environment: npm + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + fetch-depth: 1 + submodules: true + persist-credentials: false + + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: 24 + registry-url: https://registry.npmjs.org + package-manager-cache: false + + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: canary + + - name: Install release toolchain + run: | + bun install --frozen-lockfile + npm install --global npm@12.0.2 + + - name: Check release version + working-directory: pkgs/public-api-client + run: | + package_version="$(node --print 'require("./package.json").version')" + if [[ "$package_version" != *-beta.* ]]; then + echo "::error::SDK release version must use the beta prerelease channel." + exit 1 + fi + if npm view "@mosoo/sdk@$package_version" version >/dev/null 2>&1; then + echo "::error::@mosoo/sdk@$package_version is already published." + exit 1 + fi + + - name: Inspect package + working-directory: pkgs/public-api-client + run: npm pack --dry-run + + - name: Publish Beta + working-directory: pkgs/public-api-client + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + if [[ -n "$NPM_TOKEN" ]]; then + export NODE_AUTH_TOKEN="$NPM_TOKEN" + fi + npm publish --access public --tag beta --provenance diff --git a/apps/api/openapi/public-api-v1.generated.json b/apps/api/openapi/public-api-v1.generated.json index 9f74a377..52b3f62a 100644 --- a/apps/api/openapi/public-api-v1.generated.json +++ b/apps/api/openapi/public-api-v1.generated.json @@ -199,6 +199,50 @@ "required": ["type", "file_id"], "type": "object" }, + "Artifact": { + "additionalProperties": false, + "description": "A durable output artifact committed by one Agent Run.", + "properties": { + "createdAt": { + "description": "Timestamp (RFC 3339) at which the artifact was committed.", + "format": "date-time", + "type": "string" + }, + "fileId": { + "example": "01ARZ3NDEKTSV4RRFFQ69G5FAV", + "format": "ulid", + "pattern": "^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$", + "type": "string", + "description": "Stable file ID used by the existing file download endpoints." + }, + "kind": { + "const": "artifact", + "description": "Discriminator for Agent-produced output files." + }, + "mimeType": { + "description": "Detected MIME type of the artifact, or null when unknown.", + "type": ["string", "null"] + }, + "name": { + "description": "Artifact file name; names are not unique within a Thread or Run.", + "type": "string" + }, + "runId": { + "example": "01ARZ3NDEKTSV4RRFFQ69G5FAV", + "format": "ulid", + "pattern": "^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$", + "type": "string", + "description": "Stable ID of the Run that committed this artifact." + }, + "size": { + "description": "Artifact size in bytes.", + "minimum": 0, + "type": "integer" + } + }, + "required": ["createdAt", "fileId", "kind", "mimeType", "name", "runId", "size"], + "type": "object" + }, "PublicFile": { "additionalProperties": false, "description": "Public file metadata.", @@ -313,8 +357,12 @@ }, "ThreadEventLogEntry": { "additionalProperties": false, - "description": "A single public event log entry for a Thread. This is the stable read surface and never exposes raw runtime payloads, transcripts, or diagnostics.", + "description": "A single public progress entry for a Thread. Event snapshots may be truncated and are not the canonical final Run output. Raw runtime payloads, transcripts, and diagnostics are never exposed.", "properties": { + "artifact": { + "$ref": "#/components/schemas/Artifact", + "description": "Committed artifact metadata for this event. Present only when this persisted event committed an Agent output file." + }, "content": { "description": "Public content of the event — typically a reference to the associated payload (such as a message ID) rather than the raw runtime data.", "type": "string" @@ -572,12 +620,19 @@ "format": "date-time", "type": "string" }, + "fileId": { + "example": "01ARZ3NDEKTSV4RRFFQ69G5FAV", + "format": "ulid", + "pattern": "^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$", + "type": "string", + "description": "Stable file ID used by file download endpoints." + }, "id": { "example": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "format": "ulid", "pattern": "^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$", "type": "string", - "description": "Unique file ID (bare ULID)." + "description": "Backward-compatible alias of `fileId`." }, "kind": { "description": "Files added through the public API are attachments; artifacts are files produced by the Agent.", @@ -591,6 +646,20 @@ "description": "Original file name.", "type": "string" }, + "runId": { + "description": "Run that committed this artifact, or null for attachments and artifacts created before Run provenance was recorded.", + "oneOf": [ + { + "example": "01ARZ3NDEKTSV4RRFFQ69G5FAV", + "format": "ulid", + "pattern": "^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$", + "type": "string" + }, + { + "type": "null" + } + ] + }, "size": { "description": "File size in bytes.", "minimum": 0, @@ -722,6 +791,13 @@ "additionalProperties": false, "description": "Summary of a single Agent Run on a Thread.", "properties": { + "artifacts": { + "description": "Artifacts committed by this Run. Included on retrieved Run snapshots; an empty array means the Run committed none.", + "items": { + "$ref": "#/components/schemas/Artifact" + }, + "type": "array" + }, "completedAt": { "description": "Timestamp (RFC 3339) at which the Run reached a terminal state, or null while it has not finished.", "format": "date-time", @@ -879,7 +955,7 @@ }, "securitySchemes": { "accessToken": { - "bearerFormat": "mosoo Access Token", + "bearerFormat": "Mosoo API token", "description": "Use Authorization: Bearer mst_... . Access Tokens identify an account and do not carry scopes.", "scheme": "bearer", "type": "http" @@ -887,8 +963,8 @@ } }, "info": { - "description": "Public HTTPS API for creating and retrieving Threads on mosoo Agent API Endpoints. v1 resource identifiers are bare ULIDs, not prefixed IDs. Access Tokens identify the account caller. Runtime execution resolves the Agent API Endpoint owner's capabilities while the Thread is attributed to the token owner.", - "title": "mosoo Public Thread API", + "description": "Public HTTPS API for creating and retrieving Threads on Mosoo Agent API Endpoints. v1 resource identifiers are bare ULIDs, not prefixed IDs. API tokens authenticate the calling Mosoo account. Every Thread also carries a required immutable application `userId` that is delegated during Runs. Runtime execution uses the published Agent configuration.", + "title": "Mosoo Public Thread API", "version": "v1" }, "openapi": "3.1.0", @@ -1291,7 +1367,7 @@ } }, "post": { - "description": "Creates a Thread and the backing AgentSession for the required application `userId`. If input is present, mosoo also queues the initial Run. If input is omitted, the Thread is immediately visible with IDLE status and no run.", + "description": "Creates a Thread and the backing AgentSession for the required application `userId`. If input is present, Mosoo also queues the initial Run. If input is omitted, the Thread is immediately visible with IDLE status and no run.", "parameters": [ { "description": "Agent API Endpoint ID from the Agent's API Access panel. v1 IDs are bare ULIDs.", @@ -1881,10 +1957,12 @@ { "committed": true, "createdAt": "2026-05-19T00:02:00.000Z", + "fileId": "01J0000000000000000000000J", "id": "01J0000000000000000000000J", "kind": "attachment", "mimeType": "text/plain", "name": "brief.txt", + "runId": null, "size": 19 } ] diff --git a/apps/api/src/adapters/http/routes/public-api-openapi-components.ts b/apps/api/src/adapters/http/routes/public-api-openapi-components.ts index 4da0ed84..c8625b7a 100644 --- a/apps/api/src/adapters/http/routes/public-api-openapi-components.ts +++ b/apps/api/src/adapters/http/routes/public-api-openapi-components.ts @@ -54,7 +54,7 @@ export function createPublicApiOpenApiComponents() { schemas: PUBLIC_API_OPENAPI_SCHEMAS, securitySchemes: { accessToken: { - bearerFormat: "mosoo Access Token", + bearerFormat: "Mosoo API token", description: "Use Authorization: Bearer mst_... . Access Tokens identify an account and do not carry scopes.", scheme: "bearer", diff --git a/apps/api/src/adapters/http/routes/public-api-openapi.ts b/apps/api/src/adapters/http/routes/public-api-openapi.ts index 4d15dce9..4cb9c014 100644 --- a/apps/api/src/adapters/http/routes/public-api-openapi.ts +++ b/apps/api/src/adapters/http/routes/public-api-openapi.ts @@ -54,10 +54,12 @@ const ACCESS_TOKEN_SECURITY: AccessTokenSecurity[] = [{ accessToken: [] }]; const EXAMPLE_SESSION_FILE = { committed: true, createdAt: "2026-05-19T00:02:00.000Z", + fileId: EXAMPLE_FILE_ID, id: EXAMPLE_FILE_ID, kind: "attachment", mimeType: "text/plain", name: "brief.txt", + runId: null, size: 19, }; @@ -400,7 +402,7 @@ export function createPublicApiOpenApiDocument(origin: string): PublicApiOpenApi }), post: operation({ description: - "Creates a Thread and the backing AgentSession for the required application `userId`. If input is present, mosoo also queues the initial Run. If input is omitted, the Thread is immediately visible with IDLE status and no run.", + "Creates a Thread and the backing AgentSession for the required application `userId`. If input is present, Mosoo also queues the initial Run. If input is omitted, the Thread is immediately visible with IDLE status and no run.", parameters: [exampleAgentIdParameter, idempotencyKeyParameter], requestBody: jsonRequestBodyExamples( { $ref: "#/components/schemas/CreateThreadRequest" }, @@ -583,8 +585,8 @@ export function createPublicApiOpenApiDocument(origin: string): PublicApiOpenApi components: createPublicApiOpenApiComponents(), info: { description: - "Public HTTPS API for creating and retrieving Threads on mosoo Agent API Endpoints. v1 resource identifiers are bare ULIDs, not prefixed IDs. Access Tokens identify the account caller. Runtime execution resolves the Agent API Endpoint owner's capabilities while the Thread is attributed to the token owner.", - title: "mosoo Public Thread API", + "Public HTTPS API for creating and retrieving Threads on Mosoo Agent API Endpoints. v1 resource identifiers are bare ULIDs, not prefixed IDs. API tokens authenticate the calling Mosoo account. Every Thread also carries a required immutable application `userId` that is delegated during Runs. Runtime execution uses the published Agent configuration.", + title: "Mosoo Public Thread API", version: PUBLIC_API_VERSION, }, openapi: "3.1.0", diff --git a/apps/api/src/modules/files/application/file-store.ts b/apps/api/src/modules/files/application/file-store.ts index 4747294a..0faca066 100644 --- a/apps/api/src/modules/files/application/file-store.ts +++ b/apps/api/src/modules/files/application/file-store.ts @@ -19,14 +19,14 @@ import type { SessionFile, SessionResource, } from "@mosoo/contracts/session"; -import { fileRecordsTable, sessionsTable } from "@mosoo/db"; +import { fileRecordsTable, sessionRunArtifactsTable, sessionsTable } from "@mosoo/db"; import { createPlatformId, parsePlatformId } from "@mosoo/id"; -import type { AccountId, AppId, FileId, SessionId } from "@mosoo/id"; +import type { AccountId, AppId, FileId, RuntimeEventId, SessionId, SessionRunId } from "@mosoo/id"; import { and, asc, desc, eq, inArray, or } from "drizzle-orm"; import type { SQL } from "drizzle-orm"; import type { ApiBindings } from "../../../platform/cloudflare/worker-types"; -import { getAppDatabase } from "../../../platform/db/drizzle"; +import { getAppDatabase, runAppDatabaseBatch } from "../../../platform/db/drizzle"; import { toArrayBuffer } from "../../../shared/bytes"; import { currentTimestampMs } from "../../../time"; import { ensureAppOwnership } from "../../apps/application/app.service"; @@ -96,6 +96,7 @@ export interface RuntimeOutputFileInput { createdBy: AccountId; path: string; sessionId: SessionId; + sessionRunId: SessionRunId; } export interface AgentPackageFileAdmissionInput { @@ -193,7 +194,11 @@ export interface FileStore { database: D1Database, sessionId: SessionId, ): Promise; - listReadySessionArtifactKeys(database: D1Database, sessionId: SessionId): Promise; + listReadySessionArtifactKeys( + database: D1Database, + sessionId: SessionId, + sessionRunId: SessionRunId, + ): Promise; listReadySessionFiles(database: D1Database, sessionId: SessionId): Promise; listSessionResourcePathEntries( database: D1Database, @@ -233,8 +238,9 @@ export interface FileStore { async function publishSessionResourceUpsert( bindings: ApiBindings, file: FileRecord, + options?: { eventId: RuntimeEventId; runId: SessionRunId }, ): Promise { - await publishSessionResourceUpsertEvent(bindings, file); + await publishSessionResourceUpsertEvent(bindings, file, options); } function readRuntimeOutputPathSegments(path: string): string[] { @@ -632,18 +638,21 @@ async function listReadySessionFiles( async function listReadySessionArtifactKeys( database: D1Database, sessionId: SessionId, + sessionRunId: SessionRunId, ): Promise { const rows = await getAppDatabase(database) .select({ parentPath: fileRecordsTable.parentPath, }) .from(fileRecordsTable) + .innerJoin(sessionRunArtifactsTable, eq(sessionRunArtifactsTable.fileId, fileRecordsTable.id)) .where( and( eq(fileRecordsTable.scopeKind, "session"), eq(fileRecordsTable.scopeId, sessionId), eq(fileRecordsTable.status, "ready"), eq(fileRecordsTable.sessionKind, "artifact"), + eq(sessionRunArtifactsTable.sessionRunId, sessionRunId), ), ) .all(); @@ -901,6 +910,7 @@ async function readSessionArtifactBytes( async function recordRuntimeOutput(input: RuntimeOutputFileInput): Promise { const fileId = createPlatformId(); + const committedEventId = createPlatformId(); const name = getRuntimeOutputName(input.path); const contentType = normalizeContentType(input.contentType ?? "application/octet-stream"); const contentSha256 = input.contentSha256 ?? (await createRuntimeOutputContentSha256(input.body)); @@ -922,9 +932,8 @@ async function recordRuntimeOutput(input: RuntimeOutputFileInput): Promise [ + database.insert(fileRecordsTable).values({ committed: true, createdAt: timestampMs, createdByAccountId: input.createdBy, @@ -946,8 +955,17 @@ async function recordRuntimeOutput(input: RuntimeOutputFileInput): Promise, +): PublicThreadArtifact | null { + if ( + row.artifact_created_at === null || + row.artifact_file_id === null || + row.artifact_name === null || + row.artifact_run_id === null || + row.artifact_size === null + ) { + return null; + } + + return { + createdAt: toIsoString(row.artifact_created_at), + fileId: parsePlatformId(row.artifact_file_id, "Artifact file ID"), + kind: "artifact", + mimeType: row.artifact_mime_type, + name: row.artifact_name, + runId: parsePlatformId(row.artifact_run_id, "Artifact Run ID"), + size: row.artifact_size, + }; +} + interface LiveMessageState { filter: OpenAiPrivateCitationStreamFilter; text: string; @@ -315,6 +355,7 @@ function normalizePublicThreadEventsLimit(limit: number): number { } function toPublicThreadEventLogEntry(input: { + artifact: PublicThreadArtifact | null; event: SessionProcessEvent; row: PublicThreadEventProcessRow | undefined; }): PublicThreadEventLogEntry | null { @@ -331,6 +372,7 @@ function toPublicThreadEventLogEntry(input: { } return { + ...(input.artifact === null ? {} : { artifact: input.artifact }), content: sanitizePublicOutput(event.content).text, durationMs: event.durationMs, id: parsePlatformId(event.id, "Runtime event ID") as RuntimeEventId, @@ -356,9 +398,16 @@ function toPublicThreadEventLogEntries( const rowsByEventId = new Map( rows.map((row) => [row.id, row]), ); + const artifactsByEventId = new Map( + rows.flatMap((row) => { + const artifact = toPublicThreadArtifact(row); + return artifact === null ? [] : [[row.id, artifact]]; + }), + ); return createSessionProcessEventsFromSessionEventRows(rows, options).flatMap((event) => { const publicEvent = toPublicThreadEventLogEntry({ + artifact: artifactsByEventId.get(event.id) ?? null, event, row: rowsByEventId.get(event.id), }); @@ -374,6 +423,12 @@ function selectPublicThreadEventRows(input: { }) { return getAppDatabase(input.database) .select({ + artifact_created_at: sessionRunArtifactsTable.createdAt, + artifact_file_id: sessionRunArtifactsTable.fileId, + artifact_mime_type: sessionRunArtifactsTable.mimeType, + artifact_name: sessionRunArtifactsTable.name, + artifact_run_id: sessionRunArtifactsTable.sessionRunId, + artifact_size: sessionRunArtifactsTable.size, content_text: sessionEventsTable.contentText, ended_at: sessionEventsTable.endedAt, event_type: sessionEventsTable.eventType, @@ -389,6 +444,10 @@ function selectPublicThreadEventRows(input: { tokens: sessionEventsTable.tokens, }) .from(sessionEventsTable) + .leftJoin( + sessionRunArtifactsTable, + eq(sessionRunArtifactsTable.committedEventId, sessionEventsTable.sourceEventId), + ) .where(and(...input.filters)) .orderBy(input.order) .limit(input.pageSize) @@ -512,6 +571,30 @@ export async function readPublicThreadRunFinalOutput(input: { }; } +export async function readPublicThreadRunArtifacts(input: { + database: D1Database; + runId: SessionRunId; +}): Promise { + const rows = await getAppDatabase(input.database) + .select({ + artifact_created_at: sessionRunArtifactsTable.createdAt, + artifact_file_id: sessionRunArtifactsTable.fileId, + artifact_mime_type: sessionRunArtifactsTable.mimeType, + artifact_name: sessionRunArtifactsTable.name, + artifact_run_id: sessionRunArtifactsTable.sessionRunId, + artifact_size: sessionRunArtifactsTable.size, + }) + .from(sessionRunArtifactsTable) + .where(eq(sessionRunArtifactsTable.sessionRunId, input.runId)) + .orderBy(asc(sessionRunArtifactsTable.createdAt), asc(sessionRunArtifactsTable.fileId)) + .all(); + + return rows.flatMap((row) => { + const artifact = toPublicThreadArtifact(row); + return artifact === null ? [] : [artifact]; + }); +} + async function resolvePublicThreadEventSessionId( request: ListPublicThreadEventsRequest, ): Promise { diff --git a/apps/api/src/modules/public-api/public-thread-file-api.service.ts b/apps/api/src/modules/public-api/public-thread-file-api.service.ts index 6866b309..be0602cb 100644 --- a/apps/api/src/modules/public-api/public-thread-file-api.service.ts +++ b/apps/api/src/modules/public-api/public-thread-file-api.service.ts @@ -6,10 +6,13 @@ import type { PublicThreadFile, PublicThreadFileListResponse, } from "@mosoo/contracts/public-api"; +import { sessionRunArtifactsTable } from "@mosoo/db"; import { parsePlatformId } from "@mosoo/id"; -import type { AgentId, AppId, FileId, PublicThreadId, SessionId } from "@mosoo/id"; +import type { AgentId, AppId, FileId, PublicThreadId, SessionId, SessionRunId } from "@mosoo/id"; +import { inArray } from "drizzle-orm"; import type { ApiBindings } from "../../platform/cloudflare/worker-types"; +import { getAppDatabase } from "../../platform/db/drizzle"; import type { PublicApiCaller } from "../auth/application/public-api-caller.service"; import { FileControlError } from "../files/application/file-control-errors"; import { fileStore } from "../files/application/file-store"; @@ -52,14 +55,21 @@ function requirePublicThreadFile(file: FileRecord): PublicThreadId { return toPublicThreadId(parsePlatformId(file.scope.id, "File session ID")); } -function toPublicThreadFile(file: FileEntry | FileRecord): PublicThreadFile { +function toPublicThreadFile( + file: FileEntry | FileRecord, + runId: SessionRunId | null, +): PublicThreadFile { + const fileId = parsePlatformId(file.id, "File ID"); + return { committed: true, createdAt: file.createdAt, - id: parsePlatformId(file.id, "File ID"), + fileId, + id: fileId, kind: file.sessionKind ?? "attachment", mimeType: file.mimeType, name: file.name, + runId, size: file.size, }; } @@ -103,13 +113,28 @@ export async function listPublicThreadFiles( threadId: PublicThreadId, ): Promise { const { appId, sessionId } = await admitPublicThreadFileAccess(bindings, caller, threadId); + const files = ( + await fileStore.list(bindings, caller.viewer, { + appId, + sessionId, + }) + ).files; + const fileIds = files.map((file) => parsePlatformId(file.id, "File ID")); + const artifacts = + fileIds.length === 0 + ? [] + : await getAppDatabase(bindings.DB) + .select({ + fileId: sessionRunArtifactsTable.fileId, + runId: sessionRunArtifactsTable.sessionRunId, + }) + .from(sessionRunArtifactsTable) + .where(inArray(sessionRunArtifactsTable.fileId, fileIds)) + .all(); + const runIdsByFileId = new Map(artifacts.map((artifact) => [artifact.fileId, artifact.runId])); + return { - files: ( - await fileStore.list(bindings, caller.viewer, { - appId, - sessionId, - }) - ).files.map(toPublicThreadFile), + files: files.map((file) => toPublicThreadFile(file, runIdsByFileId.get(file.id) ?? null)), }; } diff --git a/apps/api/src/modules/public-api/public-thread-presenter.ts b/apps/api/src/modules/public-api/public-thread-presenter.ts index 0235fd4c..c6229d25 100644 --- a/apps/api/src/modules/public-api/public-thread-presenter.ts +++ b/apps/api/src/modules/public-api/public-thread-presenter.ts @@ -1,6 +1,7 @@ import type { PublicThreadApiCreateThreadResponse, PublicThreadApiRetrieveThreadResponse, + PublicThreadArtifact, PublicThreadFinalOutput, PublicThreadLinks, PublicThreadSummary, @@ -83,6 +84,7 @@ export function toCreateThreadResponse(input: { } export function toRetrieveThreadResponse(input: { + artifacts: PublicThreadArtifact[]; endUserId: string; finalOutput: PublicThreadFinalOutput | null; session: SessionSummary; @@ -94,7 +96,10 @@ export function toRetrieveThreadResponse(input: { run: input.session.lastRun === null ? null - : toPublicThreadRunSummary(input.session.lastRun, { finalOutput: input.finalOutput }), + : toPublicThreadRunSummary(input.session.lastRun, { + artifacts: input.artifacts, + finalOutput: input.finalOutput, + }), thread: toPublicThreadSummary({ endUserId: input.endUserId, session, diff --git a/apps/api/src/modules/public-api/public-thread-retrieve.ts b/apps/api/src/modules/public-api/public-thread-retrieve.ts index 3be823f5..027bd371 100644 --- a/apps/api/src/modules/public-api/public-thread-retrieve.ts +++ b/apps/api/src/modules/public-api/public-thread-retrieve.ts @@ -1,7 +1,10 @@ import type { PublicThreadApiRetrieveThreadResponse } from "@mosoo/contracts/public-api"; import { admitPublicThreadReader } from "./public-thread-admission"; -import { readPublicThreadRunFinalOutput } from "./public-thread-events"; +import { + readPublicThreadRunArtifacts, + readPublicThreadRunFinalOutput, +} from "./public-thread-events"; import { toBackingSessionId } from "./public-thread-ids"; import { toRetrieveThreadResponse } from "./public-thread-presenter"; import { getThreadSnapshot } from "./public-thread-store"; @@ -14,16 +17,26 @@ export async function retrievePublicThread( await admitPublicThreadReader(request.database, request.caller, snapshot); - const finalOutput = - snapshot.session.lastRun?.status === "completed" - ? await readPublicThreadRunFinalOutput({ - database: request.database, - runId: snapshot.session.lastRun.id, - sessionId: toBackingSessionId(request.threadId), - }) - : null; + const lastRun = snapshot.session.lastRun; + const [artifacts, finalOutput] = + lastRun === null + ? [[], null] + : await Promise.all([ + readPublicThreadRunArtifacts({ + database: request.database, + runId: lastRun.id, + }), + lastRun.status === "completed" + ? readPublicThreadRunFinalOutput({ + database: request.database, + runId: lastRun.id, + sessionId: toBackingSessionId(request.threadId), + }) + : null, + ]); return toRetrieveThreadResponse({ + artifacts, endUserId: snapshot.endUserId, finalOutput, session: snapshot.session, diff --git a/apps/api/src/modules/runtime/infrastructure/driver-instance/events.ts b/apps/api/src/modules/runtime/infrastructure/driver-instance/events.ts index 9e5a504d..255a289e 100644 --- a/apps/api/src/modules/runtime/infrastructure/driver-instance/events.ts +++ b/apps/api/src/modules/runtime/infrastructure/driver-instance/events.ts @@ -7,7 +7,7 @@ import { import type { DriverEventEnvelope } from "@mosoo/agent-driver/events"; import { parsePlatformId } from "@mosoo/id"; import type { DriverInstanceId } from "@mosoo/id"; -import type { AccountId, SessionId } from "@mosoo/id"; +import type { AccountId, SessionId, SessionRunId } from "@mosoo/id"; import { parseRuntimeEventEnvelope, readRuntimeEventFileChanges, @@ -142,6 +142,7 @@ async function recordRuntimeSessionOutputFile(input: { path: string; recordedArtifacts: Set; sessionId: SessionId; + sessionRunId: SessionRunId; }): Promise { const contentSha256 = await createRuntimeOutputContentSha256(input.body); const artifactKey = createRuntimeOutputParentPath(input.path, contentSha256); @@ -158,6 +159,7 @@ async function recordRuntimeSessionOutputFile(input: { createdBy: input.createdBy, path: input.path, sessionId: input.sessionId, + sessionRunId: input.sessionRunId, }); input.recordedArtifacts.add(artifactKey); input.existingArtifacts.add(artifactKey); @@ -221,6 +223,7 @@ async function recordRuntimeFileChanges(input: { link: RuntimeSessionLink; }): Promise { const sessionId = input.link.sessionId; + const sessionRunId = input.link.sessionRunId; const sandboxId = input.link.sandboxId; const createdBy = resolveRuntimeOutputCreator(input.link); const changes = readRuntimeEventFileChanges(input.event).filter( @@ -231,12 +234,13 @@ async function recordRuntimeFileChanges(input: { return; } - if (sessionId === null || sandboxId === null || createdBy === null) { + if (sessionId === null || sessionRunId === null || sandboxId === null || createdBy === null) { logWarn("runtime.file_artifact.record_skipped", { driverInstanceId: input.event.driverInstanceId ?? null, hasCreatedBy: createdBy !== null, sandboxId, sessionId, + sessionRunId, }); return; } @@ -267,7 +271,7 @@ async function recordRuntimeFileChanges(input: { const parsedSessionId = parsePlatformId(sessionId, "runtime output session ID"); const existingArtifacts = new Set( - await fileStore.listReadySessionArtifactKeys(input.bindings.DB, parsedSessionId), + await fileStore.listReadySessionArtifactKeys(input.bindings.DB, parsedSessionId, sessionRunId), ); const recordedArtifacts = new Set(); @@ -287,6 +291,7 @@ async function recordRuntimeFileChanges(input: { path: outputFile.artifactPath, recordedArtifacts, sessionId: parsedSessionId, + sessionRunId, }); } catch (error) { logWarn("runtime.file_artifact.record_failed", { @@ -307,10 +312,11 @@ async function recordRuntimeSessionOutputDirectory(input: { link: RuntimeSessionLink; }): Promise { const sessionId = input.link.sessionId; + const sessionRunId = input.link.sessionRunId; const sandboxId = input.link.sandboxId; const createdBy = resolveRuntimeOutputCreator(input.link); - if (sessionId === null || sandboxId === null || createdBy === null) { + if (sessionId === null || sessionRunId === null || sandboxId === null || createdBy === null) { return; } @@ -346,7 +352,11 @@ async function recordRuntimeSessionOutputDirectory(input: { } const existingArtifacts = new Set( - await fileStore.listReadySessionArtifactKeys(input.bindings.DB, parsedSessionId), + await fileStore.listReadySessionArtifactKeys( + input.bindings.DB, + parsedSessionId, + sessionRunId, + ), ); const recordedArtifacts = new Set(); @@ -363,6 +373,7 @@ async function recordRuntimeSessionOutputDirectory(input: { path: artifactPath, recordedArtifacts, sessionId: parsedSessionId, + sessionRunId, }); } catch (error) { logWarn("runtime.file_artifact.output_record_failed", { diff --git a/apps/api/src/modules/sessions/application/session-resource-events.service.ts b/apps/api/src/modules/sessions/application/session-resource-events.service.ts index ceb74f7d..cf905af0 100644 --- a/apps/api/src/modules/sessions/application/session-resource-events.service.ts +++ b/apps/api/src/modules/sessions/application/session-resource-events.service.ts @@ -1,7 +1,7 @@ import type { SessionViewFile } from "@mosoo/ag-ui-session"; import type { FileRecord } from "@mosoo/contracts/file"; import { parsePlatformId } from "@mosoo/id"; -import type { FileId, SessionId } from "@mosoo/id"; +import type { FileId, RuntimeEventId, SessionId, SessionRunId } from "@mosoo/id"; import { createErrorLogContext, logWarn } from "../../../platform/cloudflare/logger"; import type { ApiBindings } from "../../../platform/cloudflare/worker-types"; @@ -41,6 +41,7 @@ async function bestEffortMaterializeSessionResources( export async function publishSessionResourceUpsert( bindings: ApiBindings, file: FileRecord, + options?: { eventId: RuntimeEventId; runId: SessionRunId }, ): Promise { if (file.scope.kind !== "session") { return; @@ -48,6 +49,7 @@ export async function publishSessionResourceUpsert( const sessionId = parsePlatformId(file.scope.id, "Session resource scope ID"); const event = createSessionRuntimeEvent({ + ...(options === undefined ? {} : { id: options.eventId, runId: options.runId }), kind: "session.files.updated", origin: "file", payload: { diff --git a/apps/api/tests/api-web-boundary-fixtures.ts b/apps/api/tests/api-web-boundary-fixtures.ts index a82a756d..965fd563 100644 --- a/apps/api/tests/api-web-boundary-fixtures.ts +++ b/apps/api/tests/api-web-boundary-fixtures.ts @@ -1,5 +1,9 @@ -import type { PublicFile, PublicThreadSummary } from "@mosoo/contracts/public-api"; -import type { SessionFile, SessionSummary } from "@mosoo/contracts/session"; +import type { + PublicFile, + PublicThreadFile, + PublicThreadSummary, +} from "@mosoo/contracts/public-api"; +import type { SessionSummary } from "@mosoo/contracts/session"; import type { SessionRunSummary } from "@mosoo/contracts/session-run"; import { createPublicApiOpenApiDocument } from "../src/adapters/http/routes/public-api-openapi"; @@ -174,14 +178,16 @@ export function publicThreadRequestExamples(): Array<[string, unknown]> { ]); } -export function createSessionFile(): SessionFile { +export function createSessionFile(): PublicThreadFile { return { committed: true, createdAt: "2026-05-19T00:02:00.000Z", + fileId: PUBLIC_API_TEST_IDS.file, id: PUBLIC_API_TEST_IDS.file, kind: "attachment", mimeType: "text/plain", name: "brief.txt", + runId: null, size: 19, }; } diff --git a/apps/api/tests/api-web-boundary.test.ts b/apps/api/tests/api-web-boundary.test.ts index 5b646649..3356ffe4 100644 --- a/apps/api/tests/api-web-boundary.test.ts +++ b/apps/api/tests/api-web-boundary.test.ts @@ -549,6 +549,7 @@ describe("API to web boundary", () => { const runProperties = openApiSchemaProperties("RunSummary"); expectProperties(runProperties, [ + "artifacts", "completedAt", "createdAt", "error", @@ -577,13 +578,41 @@ describe("API to web boundary", () => { ]); const eventProperties = openApiSchemaProperties("ThreadEventLogEntry"); - expectProperties(eventProperties, ["content", "id", "occurredAt", "runId", "status", "type"]); + expectProperties(eventProperties, [ + "artifact", + "content", + "id", + "occurredAt", + "runId", + "status", + "type", + ]); + + const artifactProperties = openApiSchemaProperties("Artifact"); + expectProperties(artifactProperties, [ + "createdAt", + "fileId", + "kind", + "mimeType", + "name", + "runId", + "size", + ]); const sendEventsProperties = openApiSchemaProperties("SendEventsResponse"); expectProperties(sendEventsProperties, ["acceptedAt", "events", "thread", "warnings"]); const fileProperties = openApiSchemaProperties("ThreadFile"); - expectProperties(fileProperties, ["committed", "createdAt", "id", "kind", "name", "size"]); + expectProperties(fileProperties, [ + "committed", + "createdAt", + "fileId", + "id", + "kind", + "name", + "runId", + "size", + ]); expectNoProperties(fileProperties, ["objectKey", "path", "scopeId", "scopeKind"]); const publicFileProperties = openApiSchemaProperties("PublicFile"); diff --git a/apps/api/tests/helpers/public-api-http-test-fixture.ts b/apps/api/tests/helpers/public-api-http-test-fixture.ts index 09e55a76..f411f984 100644 --- a/apps/api/tests/helpers/public-api-http-test-fixture.ts +++ b/apps/api/tests/helpers/public-api-http-test-fixture.ts @@ -11,10 +11,12 @@ import { personalAccessTokensTable, appsTable, sessionExecutionSnapshotsTable, + sessionRunsTable, sessionsTable, vendorCredentialsTable, } from "@mosoo/db"; import type { VendorCredentialId } from "@mosoo/id"; +import { eq } from "drizzle-orm"; import { hashTokenValue } from "../../src/modules/auth/application/personal-access-token.service"; import { storeVendorCredentialSecret } from "../../src/modules/vendor-credentials/application/vendor-credential.secret-resolution"; @@ -513,6 +515,46 @@ export async function insertOwnerSession(database: SqliteD1Database): Promise { + const nowMs = nowMsForTest(); + const runId = input.runId ?? PUBLIC_API_TEST_IDS.run; + const sessionId = input.sessionId ?? PUBLIC_API_TEST_IDS.ownerSession; + + await database + .app() + .insert(sessionRunsTable) + .values({ + agentId: PUBLIC_API_TEST_IDS.agent, + createdAt: nowMs, + createdByAccountId: PUBLIC_API_TEST_IDS.ownerAccount, + id: runId, + sessionId, + startedAt: nowMs, + status: "running", + statusChangedAt: nowMs, + statusEvent: "run.start", + statusSeq: 1, + statusSource: "runtime", + traceId: `trace-${runId}`, + trigger: "user_prompt", + updatedAt: nowMs, + }) + .run(); + + await database + .app() + .update(sessionsTable) + .set({ lastRunId: runId, status: "RUNNING", updatedAt: nowMs }) + .where(eq(sessionsTable.id, sessionId)) + .run(); +} + async function insertPat(input: { accountId: string; database: SqliteD1Database; diff --git a/apps/api/tests/helpers/public-api-http-wechat-schema.sql b/apps/api/tests/helpers/public-api-http-wechat-schema.sql index b0af521c..b4f13141 100644 --- a/apps/api/tests/helpers/public-api-http-wechat-schema.sql +++ b/apps/api/tests/helpers/public-api-http-wechat-schema.sql @@ -400,6 +400,21 @@ CREATE TABLE file_record ( updated_at integer NOT NULL ); +CREATE TABLE session_run_artifact ( + committed_event_id text NOT NULL, + created_at integer NOT NULL, + file_id text PRIMARY KEY NOT NULL, + mime_type text, + name text NOT NULL, + session_run_id text NOT NULL REFERENCES session_run(id) ON DELETE CASCADE, + size integer NOT NULL +); + +CREATE UNIQUE INDEX session_run_artifact_committed_event_idx + ON session_run_artifact (committed_event_id); +CREATE INDEX session_run_artifact_run_created_idx + ON session_run_artifact (session_run_id, created_at, file_id); + CREATE TABLE file_upload ( id text PRIMARY KEY NOT NULL, file_id text NOT NULL, diff --git a/apps/api/tests/public-thread-api-fixtures.ts b/apps/api/tests/public-thread-api-fixtures.ts index 62cebbb3..dac2f85c 100644 --- a/apps/api/tests/public-thread-api-fixtures.ts +++ b/apps/api/tests/public-thread-api-fixtures.ts @@ -90,9 +90,10 @@ export async function requestPublicApiWithBindings( app: Hono, request: Request, bindings: ApiBindings, + executionContext: ExecutionContext = createTestExecutionContext(), ): Promise { return runWithRequestLogContext(request, () => - app.request(request, undefined, bindings, createTestExecutionContext()), + app.request(request, undefined, bindings, executionContext), ); } diff --git a/apps/api/tests/public-thread-api.e2e.test.ts b/apps/api/tests/public-thread-api.e2e.test.ts index e07f7295..80eeb692 100644 --- a/apps/api/tests/public-thread-api.e2e.test.ts +++ b/apps/api/tests/public-thread-api.e2e.test.ts @@ -1,7 +1,14 @@ import { describe, expect, test } from "bun:test"; import { PUBLIC_THREAD_API_THREADS_MAX_LIMIT } from "@mosoo/contracts/public-api"; -import { sessionExecutionSnapshotsTable, sessionRunsTable, sessionsTable } from "@mosoo/db"; +import { + sessionExecutionSnapshotsTable, + sessionRunArtifactsTable, + sessionRunsTable, + sessionsTable, +} from "@mosoo/db"; +import { parsePlatformId } from "@mosoo/id"; +import type { RuntimeEventId, SessionId, SessionRunId } from "@mosoo/id"; import { eq } from "drizzle-orm"; import { fileStore } from "../src/modules/files/application/file-store"; @@ -9,6 +16,10 @@ import { PUBLIC_API_RATE_LIMIT_REQUESTS_PER_MINUTE, enforcePublicApiRateLimit, } from "../src/modules/public-api/public-api-rate-limit.service"; +import { + appendSessionRuntimeEvents, + createSessionRuntimeEvent, +} from "../src/modules/sessions/application/session-event-write.service"; import { insertSessionMessage } from "../src/modules/sessions/infrastructure/session-message-store.repository"; import type { ApiBindings } from "../src/platform/cloudflare/worker-types"; import { @@ -17,6 +28,7 @@ import { TOKENS, createPublicHttpContractDatabase, createPublicHttpTestBindings, + createTestExecutionContext, } from "./helpers/public-api-http-test-fixture"; import { OWNER_VIEWER, @@ -34,6 +46,7 @@ import { } from "./public-thread-api-fixtures"; const PUBLIC_THREAD_ID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; +const ARTIFACT_EVENT_ID = "01J00000000000000000000018"; const FINAL_OUTPUT_CANARY_LINE_COUNT = 160; const FINAL_OUTPUT_CANARY_LINES = Array.from( { length: FINAL_OUTPUT_CANARY_LINE_COUNT }, @@ -383,9 +396,13 @@ describe("Public Thread API e2e", () => { const app = createPublicThreadApiTestApp(); await withProviderProbeMock(async () => { - const response = await requestPublicApi( + const waitUntilTasks: Promise[] = []; + const executionContext = { + ...createTestExecutionContext(), + waitUntil: (task: Promise) => waitUntilTasks.push(task), + }; + const response = await requestPublicApiWithBindings( app, - database, new Request(`https://api.example.com/api/v1/agents/${PUBLIC_API_TEST_IDS.agent}/threads`, { body: JSON.stringify({ input: { @@ -401,7 +418,10 @@ describe("Public Thread API e2e", () => { }, method: "POST", }), + createPublicHttpTestBindings(database) as ApiBindings, + executionContext, ); + await Promise.allSettled(waitUntilTasks); expect(response.status).toBe(201); const body = await readJson(response); @@ -1311,7 +1331,10 @@ describe("Public Thread API e2e", () => { expectArray(expectRecord(await readJson(sendEventResponse))["events"])[0], ); expect(sendEvent["type"]).toBe("user_message"); - expect(["queued", "running"]).toContain(expectRecord(sendEvent["run"])["status"]); + const sentRun = expectRecord(sendEvent["run"]); + expect(["queued", "running"]).toContain(sentRun["status"]); + const runId = parsePlatformId(expectString(sentRun["id"]), "Run ID"); + const sessionId = parsePlatformId(threadId, "Session ID"); const readyFileRow = await database .prepare( @@ -1405,6 +1428,48 @@ describe("Public Thread API e2e", () => { 2, ) .run(); + await database + .app() + .insert(sessionRunArtifactsTable) + .values({ + committedEventId: parsePlatformId(ARTIFACT_EVENT_ID, "Artifact event ID"), + createdAt: 2, + fileId: PUBLIC_API_TEST_IDS.fileAlt, + mimeType: "text/markdown", + name: "summary.md", + sessionRunId: runId, + size: 23, + }) + .run(); + await appendSessionRuntimeEvents({ + bindings: createPublicHttpTestBindings(database, { + fileBucket: bucket as unknown as R2Bucket, + }) as ApiBindings, + events: [ + createSessionRuntimeEvent({ + id: parsePlatformId(ARTIFACT_EVENT_ID, "Artifact event ID"), + kind: "session.files.updated", + origin: "file", + payload: { + change: { + change: "upsert", + file: { + committed: true, + createdAt: new Date(2).toISOString(), + id: PUBLIC_API_TEST_IDS.fileAlt, + kind: "artifact", + mimeType: "text/markdown", + name: "summary.md", + size: 23, + }, + }, + }, + runId, + sessionId, + }), + ], + sessionId, + }); await bucket.put(artifactObjectKey, "runtime summary", { httpMetadata: { contentType: "text/markdown", @@ -1427,18 +1492,104 @@ describe("Public Thread API e2e", () => { }), ); expect(listedFilesById.get(fileId)).toMatchObject({ + fileId, id: fileId, kind: "attachment", name: "launch-note.txt", + runId: null, size: 13, }); expect(listedFilesById.get(PUBLIC_API_TEST_IDS.fileAlt)).toMatchObject({ + fileId: PUBLIC_API_TEST_IDS.fileAlt, id: PUBLIC_API_TEST_IDS.fileAlt, kind: "artifact", name: "summary.md", + runId, size: 23, }); + const listedEventsResponse = await requestThreadApi( + new Request(`https://api.example.com/api/v1/threads/${threadId}/events`, { + headers: { Authorization: bearer(TOKENS.owner) }, + }), + ); + expect(listedEventsResponse.status).toBe(200); + const listedEvents = expectArray( + expectRecord(await readJson(listedEventsResponse))["events"], + ); + const artifactEvent = expectRecord( + listedEvents.find((event) => { + if (typeof event !== "object" || event === null || Array.isArray(event)) { + return false; + } + const artifact = (event as Record)["artifact"]; + return ( + typeof artifact === "object" && + artifact !== null && + !Array.isArray(artifact) && + (artifact as Record)["fileId"] === PUBLIC_API_TEST_IDS.fileAlt + ); + }), + ); + expect(artifactEvent).toMatchObject({ + artifact: { + fileId: PUBLIC_API_TEST_IDS.fileAlt, + kind: "artifact", + mimeType: "text/markdown", + name: "summary.md", + runId, + size: 23, + }, + runId, + type: "session_files.updated", + }); + + const streamResponse = await requestThreadApi( + new Request(`https://api.example.com/api/v1/threads/${threadId}/events/stream?limit=100`, { + headers: { Authorization: bearer(TOKENS.owner) }, + }), + ); + const streamReader = streamResponse.body?.getReader(); + if (!streamReader) { + throw new Error("Expected artifact stream response body."); + } + let streamText = ""; + while (!streamText.includes(`"fileId":"${PUBLIC_API_TEST_IDS.fileAlt}"`)) { + const chunk = await Promise.race([ + streamReader.read(), + Bun.sleep(3_000).then(() => { + throw new Error("Timed out waiting for artifact SSE event."); + }), + ]); + if (chunk.done) { + throw new Error("Artifact SSE closed before the committed event."); + } + streamText += new TextDecoder().decode(chunk.value); + } + await streamReader.cancel(); + expect(streamText).toContain(`"fileId":"${PUBLIC_API_TEST_IDS.fileAlt}"`); + expect(streamText).toContain(`"runId":"${runId}"`); + + await database + .app() + .update(sessionRunsTable) + .set({ completedAt: 3, status: "completed", updatedAt: 3 }) + .where(eq(sessionRunsTable.id, runId)) + .run(); + const terminalThreadResponse = await requestThreadApi( + new Request(`https://api.example.com/api/v1/threads/${threadId}`, { + headers: { Authorization: bearer(TOKENS.owner) }, + }), + ); + const terminalRun = expectRecord(expectRecord(await readJson(terminalThreadResponse))["run"]); + expect(terminalRun["artifacts"]).toEqual([ + expect.objectContaining({ + fileId: PUBLIC_API_TEST_IDS.fileAlt, + name: "summary.md", + runId, + }), + ]); + const downloadAttachmentResponse = await requestThreadApi( new Request(`https://api.example.com/api/v1/files/${fileId}/content`, { headers: { Authorization: bearer(TOKENS.owner) }, diff --git a/apps/api/tests/runtime-mcp-delegation.test.ts b/apps/api/tests/runtime-mcp-delegation.test.ts index 8f2344f6..17ac7e11 100644 --- a/apps/api/tests/runtime-mcp-delegation.test.ts +++ b/apps/api/tests/runtime-mcp-delegation.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "bun:test"; +import { verifyDelegation } from "../../../pkgs/public-api-client/src/delegation.ts"; import { copyProxyRequestHeaders } from "../src/adapters/http/routes/driver-route"; import { RUNTIME_MCP_TOOL_CALL_ID_HEADER, @@ -134,4 +135,28 @@ describe("runtime MCP end-user delegation", () => { readRuntimeMcpToolCallId(new Headers({ [RUNTIME_MCP_TOOL_CALL_ID_HEADER]: " " })), ).toThrow("invalid"); }); + + test("produces tokens accepted by the public SDK verifier", async () => { + const token = await createRuntimeMcpDelegationToken({ + accessToken: "mcp-upstream-secret", + audience: "https://tools.example.com/mcp", + claims, + nowMs: 1_800_000_000_000, + }); + + await expect( + verifyDelegation({ + accessToken: "mcp-upstream-secret", + audience: "https://tools.example.com/mcp", + nowMs: 1_800_000_030_000, + token, + }), + ).resolves.toMatchObject({ + agentId: claims.agentId, + appId: claims.appId, + runId: claims.runId, + threadId: claims.threadId, + userId: claims.endUserId, + }); + }); }); diff --git a/apps/api/tests/runtime-session-outputs.test.ts b/apps/api/tests/runtime-session-outputs.test.ts index 1e71ac03..55f0ad4d 100644 --- a/apps/api/tests/runtime-session-outputs.test.ts +++ b/apps/api/tests/runtime-session-outputs.test.ts @@ -20,6 +20,7 @@ import { createPublicHttpContractDatabase, createPublicHttpTestBindings, insertOwnerSession, + insertRunningSessionRun, nowMsForTest, } from "./helpers/public-api-http-test-fixture"; @@ -136,7 +137,9 @@ async function insertActiveSandboxSession(database: D1Database): Promise { .run(); } -function createRuntimeLink(): RuntimeSessionLink { +function createRuntimeLink( + sessionRunId: RuntimeSessionLink["sessionRunId"] = PUBLIC_API_TEST_IDS.run, +): RuntimeSessionLink { return { agentId: PUBLIC_API_TEST_IDS.agent, appId: PUBLIC_API_TEST_IDS.app, @@ -147,7 +150,7 @@ function createRuntimeLink(): RuntimeSessionLink { sandboxKind: "cattle", sandboxSubjectKind: "session", sessionId: PUBLIC_API_TEST_IDS.ownerSession, - sessionRunId: PUBLIC_API_TEST_IDS.run, + sessionRunId, sessionRunStatus: "running", sessionType: "ui", traceId: "trace-session-outputs", @@ -173,7 +176,7 @@ function createCompletedRunEvent() { }); } -function createFileChangedEvent(path = "outputs/live.txt") { +function createFileChangedEvent(path = "outputs/live.txt", runId = PUBLIC_API_TEST_IDS.run) { return createRuntimeEvent({ driverInstanceId: PUBLIC_API_TEST_IDS.driverOwner, id: API_DRIVER_BOUNDARY_IDS.runtimeEvent, @@ -192,7 +195,7 @@ function createFileChangedEvent(path = "outputs/live.txt") { }, ], }, - runId: PUBLIC_API_TEST_IDS.run, + runId, sessionId: PUBLIC_API_TEST_IDS.ownerSession, }); } @@ -279,6 +282,7 @@ describe("runtime session outputs", () => { test("records files under outputs as session artifacts on run completion", async () => { const database = await createPublicHttpContractDatabase(); await insertOwnerSession(database); + await insertRunningSessionRun(database); await insertActiveSandboxSession(database); const { bindings, bucket } = await createBindings({ @@ -341,12 +345,55 @@ describe("runtime session outputs", () => { size: 9, }, ]); + const artifactReceipts = await database + .prepare( + `SELECT committed_event_id, file_id, name, session_run_id + FROM session_run_artifact + ORDER BY name`, + ) + .all<{ + committed_event_id: string; + file_id: string; + name: string; + session_run_id: string; + }>(); + expect( + artifactReceipts.results.map(({ committed_event_id: _eventId, ...receipt }) => receipt), + ).toEqual([ + { + file_id: expect.any(String), + name: "resume.txt", + session_run_id: PUBLIC_API_TEST_IDS.run, + }, + { + file_id: expect.any(String), + name: "summary.md", + session_run_id: PUBLIC_API_TEST_IDS.run, + }, + ]); + const receiptEventIds = artifactReceipts.results.map((receipt) => receipt.committed_event_id); + const artifactEvents = await database + .prepare( + `SELECT run_id, source_event_id + FROM session_event + WHERE event_type = 'session.files.updated' + ORDER BY source_event_id`, + ) + .all<{ run_id: string; source_event_id: string }>(); + expect(artifactEvents.results.map((event) => event.source_event_id).toSorted()).toEqual( + receiptEventIds.toSorted(), + ); + expect(artifactEvents.results.map((event) => event.run_id)).toEqual([ + PUBLIC_API_TEST_IDS.run, + PUBLIC_API_TEST_IDS.run, + ]); expect([...bucket.objects.values()]).toHaveLength(2); }); test("deduplicates runtime outputs by source path and content", async () => { const database = await createPublicHttpContractDatabase(); await insertOwnerSession(database); + await insertRunningSessionRun(database); await insertActiveSandboxSession(database); const files = new Map([ @@ -393,9 +440,57 @@ describe("runtime session outputs", () => { expect(await readReportCount()).toBe(3); }); + test("keeps identical artifacts distinct across Runs", async () => { + const database = await createPublicHttpContractDatabase(); + await insertOwnerSession(database); + await insertRunningSessionRun(database); + await insertActiveSandboxSession(database); + + const { bindings } = await createBindings({ + database, + files: new Map([["/workspace/session/outputs/report.txt", "same output"]]), + }); + + await dispatchRuntimeEvent({ + bindings, + event: createFileChangedEvent("outputs/report.txt"), + link: createRuntimeLink(), + }); + + await insertRunningSessionRun(database, { runId: PUBLIC_API_TEST_IDS.runAlt }); + await dispatchRuntimeEvent({ + bindings, + event: createFileChangedEvent("outputs/report.txt", PUBLIC_API_TEST_IDS.runAlt), + link: createRuntimeLink(PUBLIC_API_TEST_IDS.runAlt), + }); + + const receipts = await database + .prepare( + `SELECT file_id, name, session_run_id + FROM session_run_artifact + ORDER BY session_run_id`, + ) + .all<{ file_id: string; name: string; session_run_id: string }>(); + + expect(receipts.results).toEqual([ + { + file_id: expect.any(String), + name: "report.txt", + session_run_id: PUBLIC_API_TEST_IDS.run, + }, + { + file_id: expect.any(String), + name: "report.txt", + session_run_id: PUBLIC_API_TEST_IDS.runAlt, + }, + ]); + expect(new Set(receipts.results.map((receipt) => receipt.file_id)).size).toBe(2); + }); + test("records file change events only when the path is under outputs", async () => { const database = await createPublicHttpContractDatabase(); await insertOwnerSession(database); + await insertRunningSessionRun(database); await insertActiveSandboxSession(database); const { bindings } = await createBindings({ @@ -428,6 +523,7 @@ describe("runtime session outputs", () => { test("skips optional output directory when it has no files", async () => { const database = await createPublicHttpContractDatabase(); await insertOwnerSession(database); + await insertRunningSessionRun(database); await insertActiveSandboxSession(database); const { bindings, bucket } = await createBindings({ database }); diff --git a/apps/api/tests/session-resource-files.test.ts b/apps/api/tests/session-resource-files.test.ts index 20315008..33b0880d 100644 --- a/apps/api/tests/session-resource-files.test.ts +++ b/apps/api/tests/session-resource-files.test.ts @@ -18,6 +18,7 @@ import { createPublicHttpContractDatabase, createPublicHttpTestBindings, insertOwnerSession, + insertRunningSessionRun, } from "./helpers/public-api-http-test-fixture"; import { SqliteD1Database } from "./helpers/sqlite-d1"; @@ -576,6 +577,7 @@ describe("session resource files", () => { test("records runtime outputs as session-scoped artifacts", async () => { const database = await createPublicHttpContractDatabase(); await insertOwnerSession(database); + await insertRunningSessionRun(database); const bucket = new PublicApiMemoryFileBucket(); const ownerViewer: AuthenticatedViewer = { email: "owner@example.com", @@ -593,6 +595,7 @@ describe("session resource files", () => { createdBy: PUBLIC_API_TEST_IDS.ownerAccount, path: "outputs/reports/summary.md", sessionId: PUBLIC_API_TEST_IDS.ownerSession, + sessionRunId: PUBLIC_API_TEST_IDS.run, }); expect(file.owner).toEqual({ diff --git a/bun.lock b/bun.lock index 83cceae3..b62067af 100644 --- a/bun.lock +++ b/bun.lock @@ -229,14 +229,13 @@ }, }, "pkgs/public-api-client": { - "name": "@mosoo/public-api-client", - "dependencies": { - "@mosoo/contracts": "workspace:*", - }, + "name": "@mosoo/sdk", + "version": "0.1.0-beta.0", "devDependencies": { "@types/bun": "^1.3.14", "typescript": "^6.0.3", "vite-plus": "^0.1.23", + "wrangler": "^4.120.1", }, }, "pkgs/runtime-catalog": { @@ -808,12 +807,12 @@ "@mosoo/observability": ["@mosoo/observability@workspace:pkgs/observability"], - "@mosoo/public-api-client": ["@mosoo/public-api-client@workspace:pkgs/public-api-client"], - "@mosoo/runtime-catalog": ["@mosoo/runtime-catalog@workspace:pkgs/runtime-catalog"], "@mosoo/runtime-events": ["@mosoo/runtime-events@workspace:pkgs/runtime-events"], + "@mosoo/sdk": ["@mosoo/sdk@workspace:pkgs/public-api-client"], + "@mosoo/session-policy": ["@mosoo/session-policy@workspace:pkgs/session-policy"], "@mosoo/skill-package": ["@mosoo/skill-package@workspace:pkgs/skill-package"], diff --git a/docs/architecture.md b/docs/architecture.md index fc550bf7..9ccc59ac 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -158,7 +158,7 @@ Except for runtime boundaries such as Session Durable Objects and Sandbox instan - **Upload/download data plane**: Browser-side large uploads and downloads may use presigned URLs to avoid API memory pressure. Current user upload targets reject `library`; the Files page lists/downloads accessible records and runtime gets no shared writable library mount. - **Dormant library versioning**: Copy-on-write and `file_version` primitives exist for a future library write path, but no production UI/API currently reaches destructive library overwrite or move-overwrite. They are plumbing, not a shipped recovery guarantee. - **Session file resources**: Session File / Session Resource is the explicit attachment layer for files uploaded by a user or added through the Public API. The File Service stores them as `file_record(scope_kind=session, session_kind=attachment)` plus an R2 object, then injects a readable path manifest into the next Agent input. Session Files are not an automatic snapshot of the entire Session working directory, and Sandbox temporary files are not promoted into long-lived assets by default. - - **Event flow**: Runtime-produced files are recorded as `file_record(scope_kind=session, session_kind=artifact)`. Frontend file events come from explicit Session file upload/delete actions and artifact updates, not from whole-working-directory snapshots. + - **Event flow**: Runtime-produced files are recorded as `file_record(scope_kind=session, session_kind=artifact)` plus an immutable Run artifact receipt. The receipt binds the file ID, producing Run ID, safe metadata, and committed event ID so public list, replay, SSE, and terminal Run projections share one identity. Frontend file events come from explicit Session file upload/delete actions and artifact updates, not from whole-working-directory snapshots. 5. **Environment Service** Environment is a first-class Agent runtime template asset. Like Agent, Skill, and MCP, new App work scopes it by App boundaries first. diff --git a/docs/prd/public-thread-api-surface.md b/docs/prd/public-thread-api-surface.md index fd15af37..a2a2d9fc 100644 --- a/docs/prd/public-thread-api-surface.md +++ b/docs/prd/public-thread-api-surface.md @@ -54,6 +54,9 @@ Agent's API Access panel shows its identifier, token creation, and API reference write-capable integration should enforce a uniqueness boundary such as `(app_id, tool_call_id)` and return its stored result when the call is delivered again. +- A committed artifact carries the same structured `fileId`, `runId`, name, + MIME type, size, and kind in Thread file lists, persisted public events, and + retrieved Run snapshots. Integrations use IDs rather than filename matching. - Thread files include explicit attachments and recorded Agent artifacts, not a complete runtime workspace. Thread history also does not guarantee that every later Run receives prior private runtime state or every earlier file. diff --git a/docs/prd/session-files.md b/docs/prd/session-files.md index d823cb7d..cc13a95f 100644 --- a/docs/prd/session-files.md +++ b/docs/prd/session-files.md @@ -25,7 +25,8 @@ workspace checkpoint used to continue a Task Agent. 3. Outputs that mosoo records from the Agent appear as artifacts in the same Thread. When an Agent reply links to a recorded `outputs/` file, selecting that link opens the artifact in a Thread preview drawer with a download - action. + action. Public integrations receive the producing Run ID and stable file ID + directly; duplicate filenames do not require inference. 4. The Files page lets the Builder search, filter, preview supported formats, and download attachments and artifacts. diff --git a/docs/prd/typescript-sdk-public-beta.md b/docs/prd/typescript-sdk-public-beta.md new file mode 100644 index 00000000..21f9adbb --- /dev/null +++ b/docs/prd/typescript-sdk-public-beta.md @@ -0,0 +1,80 @@ +# Mosoo TypeScript SDK Public Beta PRD + +状态:已确认,进入发布验收。 + +## 目标 + +在一个月内发布 `@mosoo/sdk` Public Beta,让应用开发者在可信后端中调用已发布的 Mosoo Agent,而不必重复实现 Mosoo 的 HTTP 协议、Run 终态判断、错误处理、委托验证和重试安全逻辑。 + +首版成功标准不是“封装所有 API”,而是让一名未参与实现的开发者仅依赖文档,在 15 分钟内创建 Thread、保存恢复 ID,并取得同一个 Run 的类型化终态与规范最终输出。 + +## 用户与场景 + +- Agent App 开发者在 Node.js 后端或 Cloudflare Worker 中调用 Mosoo。 +- 应用负责认证自己的用户,并把不可变的 opaque `userId` 传给 Mosoo。 +- Mosoo API token 只保存在可信后端;浏览器和移动端不直接持有 token。 +- 进程、队列消费或 Worker invocation 可能中断,后续进程必须能通过已保存的 `threadId` 和 `runId` 继续等待,而不重复创建任务。 + +## Public Beta 交付物 + +1. npm 公共包 `@mosoo/sdk@0.1.0-beta.0`,通过 `beta` dist-tag 发布,ESM-only,支持 Node.js 22/24 LTS 和 Cloudflare Workers。 +2. 自包含的 TypeScript wire types,不依赖 Mosoo 私有 workspace package。 +3. 低层 Thread、Run、File 和 event 方法,以及先返回 ID、再等待终态的可恢复任务流程。 +4. 类型化终态、规范 `finalOutput`、结构化错误,以及 #505 定义的 Run 关联 artifacts。 +5. WebCrypto 实现的 `verifyDelegation()`,校验签名、允许算法、issuer、audience、时间边界、最大 lifetime 和必需 claims。 +6. 调用方稳定 `idempotencyKey` 到 `Idempotency-Key` 的映射;示例不得在重试循环内生成新 key。 +7. 英文和简体中文安装、快速开始、恢复、事件、文件、安全边界与错误文档。 +8. 可重复的 npm Beta 发布 workflow、changelog、license 和发布/回滚手册。 +9. GoGym 删除手写 Public Thread client、SSE parser 和 delegation verifier,仅保留应用认证、业务策略与 UI projection。 + +## 核心产品行为 + +### 创建与恢复 + +- `createThread()` 返回 `threadId` 和可选 `runId`,应用在等待前持久化它们。 +- `waitForRun()` / `waitForFinalOutput()` 只以持久化 Thread/Run 快照判断终态。 +- 新进程可用相同 ID 恢复等待;SDK 不静默创建替代 Thread。 +- timeout 与 `AbortSignal` 只停止本地等待,不取消或删除远端任务。 +- 指定 `runId` 与 Thread 最新 Run 不一致时显式失败,避免把其他 Run 的结果误当目标结果。 + +### 最终输出与进度 + +- `run.finalOutput` 是持久化的规范最终回答,不由 `agent.message.delta` 拼接得出。 +- `streamEvents()` 和 event snapshot 是尽力而为的进度面;历史可能截断,不能作为完成正确性的来源。 +- 失败、取消和过期 Run 返回类型化终态;高层 final-output helper 默认抛出结构化 terminal error。 + +### Run artifacts(#505) + +- 已提交 artifact 在持久化 event、SSE、Thread file list 和终态 Run snapshot 中使用同一 `fileId` / `runId`。 +- artifact 包含 `fileId`、`runId`、`name`、`mimeType`、`size` 和 `kind`。 +- 支持多个产物与重名文件;消费者不得用文件名推断身份。 +- 两个 Run 生成相同路径和内容时也各自获得 Run 关联 receipt。 +- 下载继续使用现有 file content endpoint。 + +### 安全与重试 + +- 默认拒绝 browser-like runtime,除非调用方显式选择承担 token 风险。 +- base URL、token、ID、poll interval、timeout 和 delegation claims 在信任边界校验。 +- 同一逻辑 mutation 的重试复用同一 idempotency key;route 或 body 改变时使用新 key。 +- delegation verification 不替代应用登录、业务授权、RLS 或 replay protection。 + +## 明确不在首版范围 + +- 无损、自动重连、跨多页历史对账的 `watchRun()`;需要服务端可分页历史契约后再提供。 +- 浏览器、移动端、Bun 或 Deno runtime 支持。 +- 高层大文件 upload-session / raw-content streaming;首版沿用 `Blob` / `FormData` 和现有 64 MiB API 限制。 +- Go SDK、MCP server 生成、Tool schema、Supabase/RLS、应用 UI 和部署框架。 +- exactly-once 外部副作用、provider-specific reconciliation 或完整产品时间线。 + +## 发布验收 + +- 主仓完整 `just check` 通过;新增迁移从空本地 D1 按 append-only 链成功应用。 +- 同一 tarball 在干净 Node.js 22、Node.js 24 和真实 Cloudflare Workers runtime 中安装、类型检查并运行。 +- GoGym 使用该 tarball 后,根项目与 MCP typecheck/test、Web build 和 Worker dry-run 通过。 +- 英文/中文 OpenAPI 同步、类型检查、lint 和静态站点构建通过。 +- 未参与 SDK 实现的开发者在 15 分钟内打印 `threadId`、`runId` 和终态 `finalOutput.text`。 +- workflow 从已评审的 `main` commit 发布 npm Beta;发布后用 `npm install @mosoo/sdk@beta` 完成 clean-install smoke。 + +## 发布后候选项 + +只有真实采用证据证明需要时,才评估无损 `watchRun()`、原始字节流上传、Go SDK 和更高层 Agent App helper。 diff --git a/docs/sdk-release.md b/docs/sdk-release.md new file mode 100644 index 00000000..a15d839f --- /dev/null +++ b/docs/sdk-release.md @@ -0,0 +1,30 @@ +# TypeScript SDK Beta 发布 + +`@mosoo/sdk` 只从同一个已评审的 `main` commit 发布。发布入口是 GitHub Actions 的 **Release SDK** workflow;它会在 Node.js 22/24 和真实 Cloudflare Workers runtime 中验证打包产物,再通过 npm `beta` dist-tag 发布。 + +范围与发布验收以 [TypeScript SDK Public Beta PRD](./prd/typescript-sdk-public-beta.md) 为准。 + +## 首次发布前置 + +1. 确认维护者拥有 npm `@mosoo` organization 和 `@mosoo/sdk` 的公开发布权限。 +2. 在 GitHub 创建受保护的 `npm` environment,并为发布设置人工审批。 +3. 首次包尚不存在、无法配置 Trusted Publisher 时,只在 `npm` environment 中临时添加最小权限的 `NPM_TOKEN` 完成 bootstrap;workflow 仅在 secret 非空时导出 `NODE_AUTH_TOKEN`,不要把 Token 写入仓库或日志。 +4. 首次发布后,在 npm 包设置中登记 Trusted Publisher:organization `langgenius`、repository `mosoo`、workflow `release-sdk.yml`、environment `npm`、allowed action `npm publish`。 +5. 验证 OIDC 发布成功后删除临时 `NPM_TOKEN`。后续发布只使用 GitHub OIDC;workflow 的 `id-token: write` 不授予仓库写权限。 + +## 每次发布 + +1. 更新 `pkgs/public-api-client/package.json` 的 Beta 版本和 `CHANGELOG.md`,通过 PR 合入 `main`。 +2. 在 Actions 手动运行 **Release SDK**,选择 `main`,通过 `npm` environment 审批。 +3. workflow 会拒绝非 `-beta.*` 版本和已经存在的版本,执行 lint、类型检查、tarball 安装、Node/Worker runtime 测试和 `npm pack --dry-run` 后发布。 +4. 验证 `npm view @mosoo/sdk dist-tags --json`,并在干净目录执行 `npm install @mosoo/sdk@beta`。 +5. 首发后,在 GoGym 根目录和 `mcp/` 各执行一次 `bun install`,提交此前因 npm 404 无法生成的 SDK lockfile,再重跑 typecheck、test 和 Worker dry-run。 +6. 从同一 commit 发布 GitHub Release Notes,复制对应 `CHANGELOG.md` 条目并注明 Beta 限制。 + +## 15 分钟发布验收 + +由一名未参与 SDK 实现的开发者执行。给他一个已发布 Agent、API token、文档预览链接和同一 commit 生成的 `.tgz`;除把 `npm install @mosoo/sdk@beta` 替换为 `npm install /path/to/mosoo-sdk-*.tgz` 外,不提供口头指导。 + +从拿到这些输入开始计时。通过条件是在干净的 Node.js 22 或 24 项目中打印同一 Run 的 `threadId`、`runId` 和终态 `finalOutput.text`,总耗时不超过 15 分钟。记录运行时版本、总耗时和所有文档阻塞;token 与真实业务内容必须脱敏。发布后再用 `npm install @mosoo/sdk@beta` 重跑一次安装 smoke,不重复计算产品验收时间。 + +npm 版本不可覆盖。若 Beta 有缺陷,发布新的 `-beta.N`,移动 `beta` dist-tag;不要把常规回滚建立在 unpublish 上。 diff --git a/dogfood/gogym-agent-backend-reflection.md b/dogfood/gogym-agent-backend-reflection.md index 4e59b813..8edb27d5 100644 --- a/dogfood/gogym-agent-backend-reflection.md +++ b/dogfood/gogym-agent-backend-reflection.md @@ -249,7 +249,7 @@ GoGym still had to write a meaningful amount of mosoo-specific integration code | Fitness Tool schemas and MCP handlers | GoGym | Tool meaning, authorization, and side effects are business logic. | | Browser WebSocket and user-facing progress states | GoGym | The application owns its interaction model and business vocabulary. | | Public Thread API types, authentication, errors, and file upload | mosoo client | These duplicate mosoo's public protocol. | -| SSE parsing, reconnect, history reconciliation, deduplication, and terminal-state checks | mosoo client | Only mosoo can define correct recovery semantics across API versions. | +| SSE parsing plus terminal Run polling and final-output checks | mosoo client | Only mosoo can define its wire format and terminal recovery semantics. | | Delegation JWT parsing and verification | mosoo integration helper | This is a security boundary defined by mosoo's issuer, claims, and signing contract. | | Pairing Tool start and completion events without a stable call ID | mosoo event contract | Applications should not infer identity from FIFO order or Tool names. | | Replacing a missing Thread and uploading attachments again | Neither | This workaround hides data loss and can duplicate work; recovery must be explicit. | @@ -258,13 +258,15 @@ This produces a narrower conclusion than "mosoo should own all Agent App glue." ## A Thin Integration Kit, Not Another Framework -The repository already contains most low-level Public Thread behavior in `@mosoo/public-api-client`, but the package is private. The minimum useful response is to publish and extend that implementation rather than create a parallel SDK. +The repository now evolves the former private Public Thread client directly as the `@mosoo/sdk` Public Beta candidate. The minimum useful response remains to publish that implementation rather than create a parallel SDK. The thin integration surface should add only three high-leverage helpers: -- a resumable `watchRun()` that reconnects, reconciles persisted history, deduplicates stable event IDs, and checks terminal state; +- a recoverable Thread/Run flow that returns IDs before waiting, then reads the persisted terminal Run snapshot and canonical final output from a new process when needed; - a runtime-neutral `verifyDelegation()` that returns a typed mosoo execution context after validating signature, issuer, audience, time bounds, and required claims; -- mutation helpers that accept a caller-stable `requestId` and map it to `Idempotency-Key`, instead of generating a new random key during each retry. +- mutation helpers that accept a caller-stable `idempotencyKey`, instead of generating a new random key during each retry. + +The first Beta deliberately does not promise a lossless `watchRun()`: current event snapshots can be truncated and live SSE is a best-effort progress surface. Completion correctness comes from the terminal Run snapshot; refresh-safe product timelines require a separate server contract. This scope is tracked in [#489](https://github.com/langgenius/mosoo/issues/489). Stable Tool identity and business-side idempotency require the server contract proposed in [#488](https://github.com/langgenius/mosoo/issues/488). Uncertain external effects and provider reconciliation remain separate runtime concerns in [#412](https://github.com/langgenius/mosoo/issues/412) and [#446](https://github.com/langgenius/mosoo/issues/446). @@ -284,7 +286,7 @@ This reflection suggests a focused product sequence: 1. Make `userId` a clear, immutable Thread-level contract and delegate it safely to MCP. 2. Give every structured Tool event a stable `toolCallId` so applications can audit and deduplicate side effects without guessing. -3. Publish the existing Public Thread client with resumable Run watching, delegation verification, and retry-safe idempotency. +3. Publish the existing Public Thread client with recoverable terminal Run snapshots, delegation verification, and retry-safe idempotency. 4. Make missing history, disconnected streams, and lost Threads explicit failure states rather than silent replacement paths. 5. Verify the same MCP tools across every supported Harness and expose capability gaps honestly. 6. Keep mosoo's event vocabulary small and stable while allowing applications to define their own business events. diff --git a/package.json b/package.json index 7d1bf096..ec3bbc29 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "react-doctor:diff": "vp exec react-doctor apps/web --verbose --scope changed --blocking none", "react-doctor:report": "vp exec react-doctor apps/web --json --json-compact --scope full --blocking none", "tc": "vp run -r tc", - "test": "vp exec bun test config/commit-policy.test.ts config/public-api-compatibility.test.ts scripts/public-api-nonproduction-smoke.test.ts scripts/validate-commit-message.test.ts && vp run --filter @mosoo/api --filter @mosoo/agent-driver --filter @mosoo/web --filter @mosoo/ag-ui-session --filter @mosoo/agent-package --filter @mosoo/contracts --filter @mosoo/db --filter @mosoo/id --filter @mosoo/public-api-client --filter @mosoo/runtime-catalog --filter @mosoo/runtime-events --filter @mosoo/session-policy --filter @mosoo/skill-package test && vp run -w graphql:codegen:check" + "test": "vp exec bun test config/commit-policy.test.ts config/public-api-compatibility.test.ts scripts/public-api-nonproduction-smoke.test.ts scripts/validate-commit-message.test.ts && vp run --filter @mosoo/api --filter @mosoo/agent-driver --filter @mosoo/web --filter @mosoo/ag-ui-session --filter @mosoo/agent-package --filter @mosoo/contracts --filter @mosoo/db --filter @mosoo/id --filter @mosoo/public-api-client --filter @mosoo/sdk --filter @mosoo/runtime-catalog --filter @mosoo/runtime-events --filter @mosoo/session-policy --filter @mosoo/skill-package test && vp run -w graphql:codegen:check" }, "devDependencies": { "@graphql-codegen/cli": "^7.2.0", diff --git a/pkgs/contracts/src/http/public-api-core.contract.ts b/pkgs/contracts/src/http/public-api-core.contract.ts index 2156db3a..5822d9b6 100644 --- a/pkgs/contracts/src/http/public-api-core.contract.ts +++ b/pkgs/contracts/src/http/public-api-core.contract.ts @@ -87,6 +87,16 @@ export interface PublicThreadFinalOutputWarning { count: number; } +export interface PublicThreadArtifact { + createdAt: string; + fileId: FileId; + kind: "artifact"; + mimeType: string | null; + name: string; + runId: SessionRunId; + size: number; +} + export interface PublicThreadRunError { code: string; message: string; @@ -94,6 +104,7 @@ export interface PublicThreadRunError { } export interface PublicThreadRunSummary { + artifacts?: PublicThreadArtifact[]; completedAt: string | null; createdAt: string; error: PublicThreadRunError | null; @@ -184,6 +195,7 @@ export interface PublicThreadApiSendEventsResponse { } export interface PublicThreadEventLogEntry { + artifact?: PublicThreadArtifact; content: string; durationMs: number | null; id: RuntimeEventId; @@ -217,10 +229,12 @@ export interface PublicFileResponse { export interface PublicThreadFile { committed: boolean; createdAt: string; + fileId?: FileId; id: FileId; kind: "artifact" | "attachment"; mimeType: string | null; name: string; + runId?: SessionRunId | null; size: number; } diff --git a/pkgs/contracts/src/http/public-api-openapi.contract.ts b/pkgs/contracts/src/http/public-api-openapi.contract.ts index 851e2d60..a17d177a 100644 --- a/pkgs/contracts/src/http/public-api-openapi.contract.ts +++ b/pkgs/contracts/src/http/public-api-openapi.contract.ts @@ -143,6 +143,44 @@ export const PUBLIC_API_OPENAPI_SCHEMAS = { required: ["type", "file_id"], type: "object", }, + Artifact: { + additionalProperties: false, + description: "A durable output artifact committed by one Agent Run.", + properties: { + createdAt: { + description: "Timestamp (RFC 3339) at which the artifact was committed.", + format: "date-time", + type: "string", + }, + fileId: { + ...PLATFORM_ID_SCHEMA, + description: "Stable file ID used by the existing file download endpoints.", + }, + kind: { + const: "artifact", + description: "Discriminator for Agent-produced output files.", + }, + mimeType: { + description: "Detected MIME type of the artifact, or null when unknown.", + type: ["string", "null"], + }, + name: { + description: "Artifact file name; names are not unique within a Thread or Run.", + type: "string", + }, + runId: { + ...PLATFORM_ID_SCHEMA, + description: "Stable ID of the Run that committed this artifact.", + }, + size: { + description: "Artifact size in bytes.", + minimum: 0, + type: "integer", + }, + }, + required: ["createdAt", "fileId", "kind", "mimeType", "name", "runId", "size"], + type: "object", + }, PublicFile: { additionalProperties: false, description: "Public file metadata.", @@ -241,8 +279,13 @@ export const PUBLIC_API_OPENAPI_SCHEMAS = { ThreadEventLogEntry: { additionalProperties: false, description: - "A single public event log entry for a Thread. This is the stable read surface and never exposes raw runtime payloads, transcripts, or diagnostics.", + "A single public progress entry for a Thread. Event snapshots may be truncated and are not the canonical final Run output. Raw runtime payloads, transcripts, and diagnostics are never exposed.", properties: { + artifact: { + $ref: "#/components/schemas/Artifact", + description: + "Committed artifact metadata for this event. Present only when this persisted event committed an Agent output file.", + }, content: { description: "Public content of the event — typically a reference to the associated payload (such as a message ID) rather than the raw runtime data.", @@ -457,9 +500,13 @@ export const PUBLIC_API_OPENAPI_SCHEMAS = { format: "date-time", type: "string", }, + fileId: { + ...PLATFORM_ID_SCHEMA, + description: "Stable file ID used by file download endpoints.", + }, id: { ...PLATFORM_ID_SCHEMA, - description: "Unique file ID (bare ULID).", + description: "Backward-compatible alias of `fileId`.", }, kind: { description: @@ -474,6 +521,11 @@ export const PUBLIC_API_OPENAPI_SCHEMAS = { description: "Original file name.", type: "string", }, + runId: { + description: + "Run that committed this artifact, or null for attachments and artifacts created before Run provenance was recorded.", + oneOf: [PLATFORM_ID_SCHEMA, { type: "null" }], + }, size: { description: "File size in bytes.", minimum: 0, @@ -591,6 +643,12 @@ export const PUBLIC_API_OPENAPI_SCHEMAS = { additionalProperties: false, description: "Summary of a single Agent Run on a Thread.", properties: { + artifacts: { + description: + "Artifacts committed by this Run. Included on retrieved Run snapshots; an empty array means the Run committed none.", + items: { $ref: "#/components/schemas/Artifact" }, + type: "array", + }, completedAt: { description: "Timestamp (RFC 3339) at which the Run reached a terminal state, or null while it has not finished.", diff --git a/pkgs/db/drizzle/0012_session-run-artifacts.sql b/pkgs/db/drizzle/0012_session-run-artifacts.sql new file mode 100644 index 00000000..0229b25c --- /dev/null +++ b/pkgs/db/drizzle/0012_session-run-artifacts.sql @@ -0,0 +1,13 @@ +CREATE TABLE `session_run_artifact` ( + `committed_event_id` text CHECK ("committed_event_id" = upper("committed_event_id") AND length("committed_event_id") = 26 AND substr("committed_event_id", 1, 1) GLOB '[0-7]' AND "committed_event_id" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*') NOT NULL, + `created_at` integer NOT NULL, + `file_id` text CHECK ("file_id" = upper("file_id") AND length("file_id") = 26 AND substr("file_id", 1, 1) GLOB '[0-7]' AND "file_id" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*') PRIMARY KEY NOT NULL, + `mime_type` text, + `name` text NOT NULL, + `session_run_id` text CHECK ("session_run_id" = upper("session_run_id") AND length("session_run_id") = 26 AND substr("session_run_id", 1, 1) GLOB '[0-7]' AND "session_run_id" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*') NOT NULL, + `size` integer NOT NULL, + FOREIGN KEY (`session_run_id`) REFERENCES `session_run`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `session_run_artifact_committed_event_idx` ON `session_run_artifact` (`committed_event_id`);--> statement-breakpoint +CREATE INDEX `session_run_artifact_run_created_idx` ON `session_run_artifact` (`session_run_id`,`created_at`,`file_id`); diff --git a/pkgs/db/drizzle/meta/0012_snapshot.json b/pkgs/db/drizzle/meta/0012_snapshot.json new file mode 100644 index 00000000..8cdd7d29 --- /dev/null +++ b/pkgs/db/drizzle/meta/0012_snapshot.json @@ -0,0 +1,7651 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "fa34ec9c-2b8c-41bb-bcce-cf81c5c71145", + "prevId": "c62b2b37-8e6b-4ae6-bd80-186a6e314043", + "tables": { + "agent_deployment_version": { + "name": "agent_deployment_version", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_json": { + "name": "config_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "environment_id": { + "name": "environment_id", + "type": "text CHECK (\"environment_id\" = upper(\"environment_id\") AND length(\"environment_id\") = 26 AND substr(\"environment_id\", 1, 1) GLOB '[0-7]' AND \"environment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mcp_bindings_json": { + "name": "mcp_bindings_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "runtime_id": { + "name": "runtime_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "skills_json": { + "name": "skills_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "agent_deployment_version_agent_number_idx": { + "name": "agent_deployment_version_agent_number_idx", + "columns": ["agent_id", "version_number"], + "isUnique": true + }, + "agent_deployment_version_agent_created_idx": { + "name": "agent_deployment_version_agent_created_idx", + "columns": ["agent_id", "created_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agent_mcp_binding": { + "name": "agent_mcp_binding", + "columns": { + "agent_credential_id": { + "name": "agent_credential_id", + "type": "text CHECK (\"agent_credential_id\" = upper(\"agent_credential_id\") AND length(\"agent_credential_id\") = 26 AND substr(\"agent_credential_id\", 1, 1) GLOB '[0-7]' AND \"agent_credential_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credential_mode": { + "name": "credential_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'runtime_resolved'" + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "server_id": { + "name": "server_id", + "type": "text CHECK (\"server_id\" = upper(\"server_id\") AND length(\"server_id\") = 26 AND substr(\"server_id\", 1, 1) GLOB '[0-7]' AND \"server_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "agent_mcp_binding_agent_sort_idx": { + "name": "agent_mcp_binding_agent_sort_idx", + "columns": ["agent_id", "sort_order"], + "isUnique": true + }, + "agent_mcp_binding_server_idx": { + "name": "agent_mcp_binding_server_idx", + "columns": ["server_id"], + "isUnique": false + }, + "agent_mcp_binding_profile_server_idx": { + "name": "agent_mcp_binding_profile_server_idx", + "columns": ["agent_id", "server_id"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "agent_mcp_binding_agent_credential_shape_check": { + "name": "agent_mcp_binding_agent_credential_shape_check", + "value": "\n (\"agent_mcp_binding\".\"credential_mode\" = 'agent_bound' AND \"agent_mcp_binding\".\"agent_credential_id\" IS NOT NULL)\n OR (\"agent_mcp_binding\".\"credential_mode\" = 'runtime_resolved' AND \"agent_mcp_binding\".\"agent_credential_id\" IS NULL)\n " + } + } + }, + "agent_skill": { + "name": "agent_skill", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "skill_id": { + "name": "skill_id", + "type": "text CHECK (\"skill_id\" = upper(\"skill_id\") AND length(\"skill_id\") = 26 AND substr(\"skill_id\", 1, 1) GLOB '[0-7]' AND \"skill_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "agent_skill_agent_sort_idx": { + "name": "agent_skill_agent_sort_idx", + "columns": ["agent_id", "sort_order"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "agent_skill_agent_id_skill_id_pk": { + "columns": ["agent_id", "skill_id"], + "name": "agent_skill_agent_id_skill_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agent": { + "name": "agent", + "columns": { + "config_json": { + "name": "config_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "environment_id": { + "name": "environment_id", + "type": "text CHECK (\"environment_id\" = upper(\"environment_id\") AND length(\"environment_id\") = 26 AND substr(\"environment_id\", 1, 1) GLOB '[0-7]' AND \"environment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pet'" + }, + "live_deployment_version_id": { + "name": "live_deployment_version_id", + "type": "text CHECK (\"live_deployment_version_id\" = upper(\"live_deployment_version_id\") AND length(\"live_deployment_version_id\") = 26 AND substr(\"live_deployment_version_id\", 1, 1) GLOB '[0-7]' AND \"live_deployment_version_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "runtime_id": { + "name": "runtime_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'private'" + } + }, + "indexes": { + "agent_app_owner_account_idx": { + "name": "agent_app_owner_account_idx", + "columns": ["app_id", "owner_account_id"], + "isUnique": false + }, + "agent_app_status_idx": { + "name": "agent_app_status_idx", + "columns": ["app_id", "status"], + "isUnique": false + }, + "agent_environment_idx": { + "name": "agent_environment_idx", + "columns": ["environment_id"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "agent_published_live_deployment_version_check": { + "name": "agent_published_live_deployment_version_check", + "value": "\"agent\".\"status\" <> 'published' OR \"agent\".\"live_deployment_version_id\" IS NOT NULL" + } + } + }, + "api_command": { + "name": "api_command", + "columns": { + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "claim_expires_at": { + "name": "claim_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claim_owner": { + "name": "claim_owner", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "api_command_dedupe_idx": { + "name": "api_command_dedupe_idx", + "columns": ["dedupe_key"], + "isUnique": true + }, + "api_command_status_updated_idx": { + "name": "api_command_status_updated_idx", + "columns": ["status", "updated_at"], + "isUnique": false + }, + "api_command_claim_idx": { + "name": "api_command_claim_idx", + "columns": ["status", "claim_expires_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "auth_account": { + "name": "auth_account", + "columns": { + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text CHECK (\"account_id\" = upper(\"account_id\") AND length(\"account_id\") = 26 AND substr(\"account_id\", 1, 1) GLOB '[0-7]' AND \"account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "auth_account_provider_account_idx": { + "name": "auth_account_provider_account_idx", + "columns": ["provider_id", "provider_account_id"], + "isUnique": true + }, + "auth_account_account_id_idx": { + "name": "auth_account_account_id_idx", + "columns": ["account_id"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "auth_session": { + "name": "auth_session", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text CHECK (\"account_id\" = upper(\"account_id\") AND length(\"account_id\") = 26 AND substr(\"account_id\", 1, 1) GLOB '[0-7]' AND \"account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "auth_session_expires_at_idx": { + "name": "auth_session_expires_at_idx", + "columns": ["expires_at"], + "isUnique": false + }, + "auth_session_token_idx": { + "name": "auth_session_token_idx", + "columns": ["token"], + "isUnique": true + }, + "auth_session_account_id_idx": { + "name": "auth_session_account_id_idx", + "columns": ["account_id"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "auth_verification": { + "name": "auth_verification", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "auth_verification_expires_at_idx": { + "name": "auth_verification_expires_at_idx", + "columns": ["expires_at"], + "isUnique": false + }, + "auth_verification_identifier_idx": { + "name": "auth_verification_identifier_idx", + "columns": ["identifier"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "cli_oauth_flow": { + "name": "cli_oauth_flow", + "columns": { + "account_id": { + "name": "account_id", + "type": "text CHECK (\"account_id\" = upper(\"account_id\") AND length(\"account_id\") = 26 AND substr(\"account_id\", 1, 1) GLOB '[0-7]' AND \"account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authorized_at": { + "name": "authorized_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "device_code_hash": { + "name": "device_code_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_code": { + "name": "user_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "cli_oauth_flow_status_expires_idx": { + "name": "cli_oauth_flow_status_expires_idx", + "columns": ["status", "expires_at"], + "isUnique": false + }, + "cli_oauth_flow_device_code_hash_idx": { + "name": "cli_oauth_flow_device_code_hash_idx", + "columns": ["device_code_hash"], + "isUnique": true + }, + "cli_oauth_flow_user_code_idx": { + "name": "cli_oauth_flow_user_code_idx", + "columns": ["user_code"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "personal_access_token": { + "name": "personal_access_token", + "columns": { + "account_id": { + "name": "account_id", + "type": "text CHECK (\"account_id\" = upper(\"account_id\") AND length(\"account_id\") = 26 AND substr(\"account_id\", 1, 1) GLOB '[0-7]' AND \"account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "personal_access_token_account_created_idx": { + "name": "personal_access_token_account_created_idx", + "columns": ["account_id", "created_at"], + "isUnique": false + }, + "personal_access_token_hash_idx": { + "name": "personal_access_token_hash_idx", + "columns": ["token_hash"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agent_channel_binding": { + "name": "agent_channel_binding", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_metadata_json": { + "name": "display_metadata_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "encrypted_creds_secret_id": { + "name": "encrypted_creds_secret_id", + "type": "text CHECK (\"encrypted_creds_secret_id\" = upper(\"encrypted_creds_secret_id\") AND length(\"encrypted_creds_secret_id\") = 26 AND substr(\"encrypted_creds_secret_id\", 1, 1) GLOB '[0-7]' AND \"encrypted_creds_secret_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_bot_id": { + "name": "external_bot_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_tenant_id": { + "name": "external_tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "agent_channel_binding_agent_provider_idx": { + "name": "agent_channel_binding_agent_provider_idx", + "columns": ["agent_id", "provider"], + "isUnique": true + }, + "agent_channel_binding_provider_tenant_bot_idx": { + "name": "agent_channel_binding_provider_tenant_bot_idx", + "columns": ["provider", "external_tenant_id", "external_bot_id"], + "isUnique": true + }, + "agent_channel_binding_agent_status_idx": { + "name": "agent_channel_binding_agent_status_idx", + "columns": ["agent_id", "status"], + "isUnique": false + }, + "agent_channel_binding_app_status_idx": { + "name": "agent_channel_binding_app_status_idx", + "columns": ["app_id", "status"], + "isUnique": false + } + }, + "foreignKeys": { + "agent_channel_binding_agent_id_agent_id_fk": { + "name": "agent_channel_binding_agent_id_agent_id_fk", + "tableFrom": "agent_channel_binding", + "tableTo": "agent", + "columnsFrom": ["agent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_channel_binding_encrypted_creds_secret_id_vault_secret_id_fk": { + "name": "agent_channel_binding_encrypted_creds_secret_id_vault_secret_id_fk", + "tableFrom": "agent_channel_binding", + "tableTo": "vault_secret", + "columnsFrom": ["encrypted_creds_secret_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "agent_channel_binding_app_id_app_id_fk": { + "name": "agent_channel_binding_app_id_app_id_fk", + "tableFrom": "agent_channel_binding", + "tableTo": "app", + "columnsFrom": ["app_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "channel_runtime_state": { + "name": "channel_runtime_state", + "columns": { + "binding_id": { + "name": "binding_id", + "type": "text CHECK (\"binding_id\" = upper(\"binding_id\") AND length(\"binding_id\") = 26 AND substr(\"binding_id\", 1, 1) GLOB '[0-7]' AND \"binding_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_inbound_at": { + "name": "last_inbound_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_poll_at": { + "name": "last_poll_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lease_owner_id": { + "name": "lease_owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "runtime_account_id": { + "name": "runtime_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "runtime_state_json": { + "name": "runtime_state_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_changed_at": { + "name": "status_changed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "channel_runtime_state_provider_binding_account_idx": { + "name": "channel_runtime_state_provider_binding_account_idx", + "columns": ["provider", "binding_id", "runtime_account_id"], + "isUnique": true + }, + "channel_runtime_state_status_lease_idx": { + "name": "channel_runtime_state_status_lease_idx", + "columns": ["status", "lease_expires_at"], + "isUnique": false + }, + "channel_runtime_state_binding_updated_idx": { + "name": "channel_runtime_state_binding_updated_idx", + "columns": ["binding_id", "updated_at"], + "isUnique": false + } + }, + "foreignKeys": { + "channel_runtime_state_binding_id_agent_channel_binding_id_fk": { + "name": "channel_runtime_state_binding_id_agent_channel_binding_id_fk", + "tableFrom": "channel_runtime_state", + "tableTo": "agent_channel_binding", + "columnsFrom": ["binding_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "channel_event_receipt": { + "name": "channel_event_receipt", + "columns": { + "binding_id": { + "name": "binding_id", + "type": "text CHECK (\"binding_id\" = upper(\"binding_id\") AND length(\"binding_id\") = 26 AND substr(\"binding_id\", 1, 1) GLOB '[0-7]' AND \"binding_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_event_id": { + "name": "external_event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_tenant_id": { + "name": "external_tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "channel_event_receipt_provider_tenant_event_idx": { + "name": "channel_event_receipt_provider_tenant_event_idx", + "columns": ["provider", "external_tenant_id", "external_event_id"], + "isUnique": true + }, + "channel_event_receipt_binding_updated_idx": { + "name": "channel_event_receipt_binding_updated_idx", + "columns": ["binding_id", "updated_at"], + "isUnique": false + }, + "channel_event_receipt_expires_idx": { + "name": "channel_event_receipt_expires_idx", + "columns": ["expires_at"], + "isUnique": false + } + }, + "foreignKeys": { + "channel_event_receipt_binding_id_agent_channel_binding_id_fk": { + "name": "channel_event_receipt_binding_id_agent_channel_binding_id_fk", + "tableFrom": "channel_event_receipt", + "tableTo": "agent_channel_binding", + "columnsFrom": ["binding_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "channel_final_delivery_job": { + "name": "channel_final_delivery_job", + "columns": { + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "binding_id": { + "name": "binding_id", + "type": "text CHECK (\"binding_id\" = upper(\"binding_id\") AND length(\"binding_id\") = 26 AND substr(\"binding_id\", 1, 1) GLOB '[0-7]' AND \"binding_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_event_id": { + "name": "external_event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "run_id": { + "name": "run_id", + "type": "text CHECK (\"run_id\" = upper(\"run_id\") AND length(\"run_id\") = 26 AND substr(\"run_id\", 1, 1) GLOB '[0-7]' AND \"run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "channel_final_delivery_provider_binding_event_idx": { + "name": "channel_final_delivery_provider_binding_event_idx", + "columns": ["provider", "binding_id", "external_event_id"], + "isUnique": true + }, + "channel_final_delivery_session_idx": { + "name": "channel_final_delivery_session_idx", + "columns": ["session_id"], + "isUnique": false + }, + "channel_final_delivery_run_idx": { + "name": "channel_final_delivery_run_idx", + "columns": ["run_id"], + "isUnique": false + } + }, + "foreignKeys": { + "channel_final_delivery_job_binding_id_agent_channel_binding_id_fk": { + "name": "channel_final_delivery_job_binding_id_agent_channel_binding_id_fk", + "tableFrom": "channel_final_delivery_job", + "tableTo": "agent_channel_binding", + "columnsFrom": ["binding_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "channel_final_delivery_job_run_id_session_run_id_fk": { + "name": "channel_final_delivery_job_run_id_session_run_id_fk", + "tableFrom": "channel_final_delivery_job", + "tableTo": "session_run", + "columnsFrom": ["run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "channel_final_delivery_job_session_id_session_id_fk": { + "name": "channel_final_delivery_job_session_id_session_id_fk", + "tableFrom": "channel_final_delivery_job", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "channel_thread_session": { + "name": "channel_thread_session", + "columns": { + "binding_id": { + "name": "binding_id", + "type": "text CHECK (\"binding_id\" = upper(\"binding_id\") AND length(\"binding_id\") = 26 AND substr(\"binding_id\", 1, 1) GLOB '[0-7]' AND \"binding_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_thread_id": { + "name": "external_thread_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "channel_thread_session_provider_binding_thread_idx": { + "name": "channel_thread_session_provider_binding_thread_idx", + "columns": ["provider", "binding_id", "external_thread_id"], + "isUnique": true + }, + "channel_thread_session_session_idx": { + "name": "channel_thread_session_session_idx", + "columns": ["session_id"], + "isUnique": false + } + }, + "foreignKeys": { + "channel_thread_session_binding_id_agent_channel_binding_id_fk": { + "name": "channel_thread_session_binding_id_agent_channel_binding_id_fk", + "tableFrom": "channel_thread_session", + "tableTo": "agent_channel_binding", + "columnsFrom": ["binding_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "channel_thread_session_session_id_session_id_fk": { + "name": "channel_thread_session_session_id_session_id_fk", + "tableFrom": "channel_thread_session", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "wechat_channel_account": { + "name": "wechat_channel_account", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cursor": { + "name": "cursor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "encrypted_creds_secret_id": { + "name": "encrypted_creds_secret_id", + "type": "text CHECK (\"encrypted_creds_secret_id\" = upper(\"encrypted_creds_secret_id\") AND length(\"encrypted_creds_secret_id\") = 26 AND substr(\"encrypted_creds_secret_id\", 1, 1) GLOB '[0-7]' AND \"encrypted_creds_secret_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_account_id": { + "name": "external_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_bot_id": { + "name": "external_bot_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_inbound_at": { + "name": "last_inbound_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_poll_at": { + "name": "last_poll_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "runtime_state_json": { + "name": "runtime_state_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_changed_at": { + "name": "status_changed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "wechat_channel_account_agent_idx": { + "name": "wechat_channel_account_agent_idx", + "columns": ["agent_id"], + "isUnique": true + }, + "wechat_channel_account_external_idx": { + "name": "wechat_channel_account_external_idx", + "columns": ["external_account_id", "external_bot_id"], + "isUnique": true + }, + "wechat_channel_account_status_idx": { + "name": "wechat_channel_account_status_idx", + "columns": ["status", "updated_at"], + "isUnique": false + }, + "wechat_channel_account_app_status_idx": { + "name": "wechat_channel_account_app_status_idx", + "columns": ["app_id", "status"], + "isUnique": false + } + }, + "foreignKeys": { + "wechat_channel_account_agent_id_agent_id_fk": { + "name": "wechat_channel_account_agent_id_agent_id_fk", + "tableFrom": "wechat_channel_account", + "tableTo": "agent", + "columnsFrom": ["agent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wechat_channel_account_encrypted_creds_secret_id_vault_secret_id_fk": { + "name": "wechat_channel_account_encrypted_creds_secret_id_vault_secret_id_fk", + "tableFrom": "wechat_channel_account", + "tableTo": "vault_secret", + "columnsFrom": ["encrypted_creds_secret_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "wechat_channel_account_owner_account_id_account_id_fk": { + "name": "wechat_channel_account_owner_account_id_account_id_fk", + "tableFrom": "wechat_channel_account", + "tableTo": "account", + "columnsFrom": ["owner_account_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wechat_channel_account_app_id_app_id_fk": { + "name": "wechat_channel_account_app_id_app_id_fk", + "tableFrom": "wechat_channel_account", + "tableTo": "app", + "columnsFrom": ["app_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "wechat_channel_pairing": { + "name": "wechat_channel_pairing", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_token_hash": { + "name": "qr_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "wechat_channel_pairing_qr_token_hash_idx": { + "name": "wechat_channel_pairing_qr_token_hash_idx", + "columns": ["qr_token_hash"], + "isUnique": true + }, + "wechat_channel_pairing_agent_creator_idx": { + "name": "wechat_channel_pairing_agent_creator_idx", + "columns": ["agent_id", "created_by_account_id", "consumed_at"], + "isUnique": false + }, + "wechat_channel_pairing_app_creator_idx": { + "name": "wechat_channel_pairing_app_creator_idx", + "columns": ["app_id", "created_by_account_id", "consumed_at"], + "isUnique": false + }, + "wechat_channel_pairing_expires_idx": { + "name": "wechat_channel_pairing_expires_idx", + "columns": ["expires_at"], + "isUnique": false + } + }, + "foreignKeys": { + "wechat_channel_pairing_agent_id_agent_id_fk": { + "name": "wechat_channel_pairing_agent_id_agent_id_fk", + "tableFrom": "wechat_channel_pairing", + "tableTo": "agent", + "columnsFrom": ["agent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wechat_channel_pairing_created_by_account_id_account_id_fk": { + "name": "wechat_channel_pairing_created_by_account_id_account_id_fk", + "tableFrom": "wechat_channel_pairing", + "tableTo": "account", + "columnsFrom": ["created_by_account_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wechat_channel_pairing_app_id_app_id_fk": { + "name": "wechat_channel_pairing_app_id_app_id_fk", + "tableFrom": "wechat_channel_pairing", + "tableTo": "app", + "columnsFrom": ["app_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "wechat_context_token": { + "name": "wechat_context_token", + "columns": { + "account_id": { + "name": "account_id", + "type": "text CHECK (\"account_id\" = upper(\"account_id\") AND length(\"account_id\") = 26 AND substr(\"account_id\", 1, 1) GLOB '[0-7]' AND \"account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "context_token_key": { + "name": "context_token_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "encrypted_context_token_secret_id": { + "name": "encrypted_context_token_secret_id", + "type": "text CHECK (\"encrypted_context_token_secret_id\" = upper(\"encrypted_context_token_secret_id\") AND length(\"encrypted_context_token_secret_id\") = 26 AND substr(\"encrypted_context_token_secret_id\", 1, 1) GLOB '[0-7]' AND \"encrypted_context_token_secret_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_account_id": { + "name": "external_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "peer_id": { + "name": "peer_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "to_user_id": { + "name": "to_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "wechat_context_token_key_idx": { + "name": "wechat_context_token_key_idx", + "columns": ["context_token_key"], + "isUnique": true + }, + "wechat_context_token_account_peer_idx": { + "name": "wechat_context_token_account_peer_idx", + "columns": ["account_id", "external_account_id", "peer_id"], + "isUnique": true + }, + "wechat_context_token_account_updated_idx": { + "name": "wechat_context_token_account_updated_idx", + "columns": ["account_id", "updated_at"], + "isUnique": false + } + }, + "foreignKeys": { + "wechat_context_token_account_id_wechat_channel_account_id_fk": { + "name": "wechat_context_token_account_id_wechat_channel_account_id_fk", + "tableFrom": "wechat_context_token", + "tableTo": "wechat_channel_account", + "columnsFrom": ["account_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wechat_context_token_encrypted_context_token_secret_id_vault_secret_id_fk": { + "name": "wechat_context_token_encrypted_context_token_secret_id_vault_secret_id_fk", + "tableFrom": "wechat_context_token", + "tableTo": "vault_secret", + "columnsFrom": ["encrypted_context_token_secret_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "email_log": { + "name": "email_log", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_domain": { + "name": "recipient_domain", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recipient_masked": { + "name": "recipient_masked", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "email_log_created_at_idx": { + "name": "email_log_created_at_idx", + "columns": ["created_at"], + "isUnique": false + }, + "email_log_type_status_idx": { + "name": "email_log_type_status_idx", + "columns": ["type", "status"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "environment_revision": { + "name": "environment_revision", + "columns": { + "allow_mcp_servers": { + "name": "allow_mcp_servers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "allow_package_managers": { + "name": "allow_package_managers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "allowed_hosts_json": { + "name": "allowed_hosts_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "env_vars_json": { + "name": "env_vars_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "environment_id": { + "name": "environment_id", + "type": "text CHECK (\"environment_id\" = upper(\"environment_id\") AND length(\"environment_id\") = 26 AND substr(\"environment_id\", 1, 1) GLOB '[0-7]' AND \"environment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "network_policy": { + "name": "network_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "packages_json": { + "name": "packages_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "setup_script": { + "name": "setup_script", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "environment_revision_environment_created_at_idx": { + "name": "environment_revision_environment_created_at_idx", + "columns": ["environment_id", "created_at"], + "isUnique": false + }, + "environment_revision_app_created_at_idx": { + "name": "environment_revision_app_created_at_idx", + "columns": ["app_id", "created_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "environment_revision_network_policy_check": { + "name": "environment_revision_network_policy_check", + "value": "\"environment_revision\".\"network_policy\" IN ('full', 'limited')" + } + } + }, + "environment": { + "name": "environment", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "current_revision_id": { + "name": "current_revision_id", + "type": "text CHECK (\"current_revision_id\" = upper(\"current_revision_id\") AND length(\"current_revision_id\") = 26 AND substr(\"current_revision_id\", 1, 1) GLOB '[0-7]' AND \"current_revision_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "forked_from_environment_id": { + "name": "forked_from_environment_id", + "type": "text CHECK (\"forked_from_environment_id\" = upper(\"forked_from_environment_id\") AND length(\"forked_from_environment_id\") = 26 AND substr(\"forked_from_environment_id\", 1, 1) GLOB '[0-7]' AND \"forked_from_environment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "forked_from_environment_name": { + "name": "forked_from_environment_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "forked_from_owner_name": { + "name": "forked_from_owner_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "environment_app_updated_at_idx": { + "name": "environment_app_updated_at_idx", + "columns": ["app_id", "updated_at"], + "isUnique": false + }, + "environment_owner_updated_at_idx": { + "name": "environment_owner_updated_at_idx", + "columns": ["owner_account_id", "updated_at"], + "isUnique": false + }, + "environment_owner_name_idx": { + "name": "environment_owner_name_idx", + "columns": ["app_id", "owner_account_id", "name"], + "isUnique": true, + "where": "\"environment\".\"owner_account_id\" IS NOT NULL" + }, + "environment_system_default_idx": { + "name": "environment_system_default_idx", + "columns": ["app_id"], + "isUnique": true, + "where": "\"environment\".\"owner_account_id\" IS NULL" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "file_record": { + "name": "file_record", + "columns": { + "committed": { + "name": "committed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "etag": { + "name": "etag", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text CHECK (\"owner_id\" = upper(\"owner_id\") AND length(\"owner_id\") = 26 AND substr(\"owner_id\", 1, 1) GLOB '[0-7]' AND \"owner_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_kind": { + "name": "owner_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_path": { + "name": "parent_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "purpose": { + "name": "purpose", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope_id": { + "name": "scope_id", + "type": "text CHECK (\"scope_id\" = upper(\"scope_id\") AND length(\"scope_id\") = 26 AND substr(\"scope_id\", 1, 1) GLOB '[0-7]' AND \"scope_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_kind": { + "name": "session_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "file_record_object_key_idx": { + "name": "file_record_object_key_idx", + "columns": ["object_key"], + "isUnique": true + }, + "file_record_unscoped_parent_path_name_status_idx": { + "name": "file_record_unscoped_parent_path_name_status_idx", + "columns": ["scope_kind", "parent_path", "name", "status"], + "isUnique": true, + "where": "\"file_record\".\"scope_id\" IS NULL" + }, + "file_record_scoped_parent_path_name_status_idx": { + "name": "file_record_scoped_parent_path_name_status_idx", + "columns": ["scope_kind", "scope_id", "parent_path", "name", "status"], + "isUnique": true + }, + "file_record_unscoped_pending_path_idx": { + "name": "file_record_unscoped_pending_path_idx", + "columns": ["scope_kind", "path"], + "isUnique": true, + "where": "\"file_record\".\"status\" = 'pending' AND \"file_record\".\"scope_id\" IS NULL" + }, + "file_record_scoped_pending_path_idx": { + "name": "file_record_scoped_pending_path_idx", + "columns": ["scope_kind", "scope_id", "path"], + "isUnique": true, + "where": "\"file_record\".\"status\" = 'pending' AND \"file_record\".\"scope_id\" IS NOT NULL" + }, + "file_record_unscoped_ready_path_idx": { + "name": "file_record_unscoped_ready_path_idx", + "columns": ["scope_kind", "path"], + "isUnique": true, + "where": "\"file_record\".\"status\" = 'ready' AND \"file_record\".\"scope_id\" IS NULL" + }, + "file_record_scoped_ready_path_idx": { + "name": "file_record_scoped_ready_path_idx", + "columns": ["scope_kind", "scope_id", "path"], + "isUnique": true, + "where": "\"file_record\".\"status\" = 'ready' AND \"file_record\".\"scope_id\" IS NOT NULL" + }, + "file_record_governance_idx": { + "name": "file_record_governance_idx", + "columns": ["purpose", "owner_kind", "owner_id", "status", "expires_at"], + "isUnique": false + }, + "file_record_listing_idx": { + "name": "file_record_listing_idx", + "columns": ["scope_kind", "scope_id", "parent_path", "status", "lower(\"name\")"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "file_upload": { + "name": "file_upload", + "columns": { + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expected_size": { + "name": "expected_size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "file_id": { + "name": "file_id", + "type": "text CHECK (\"file_id\" = upper(\"file_id\") AND length(\"file_id\") = 26 AND substr(\"file_id\", 1, 1) GLOB '[0-7]' AND \"file_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "if_match_etag": { + "name": "if_match_etag", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "multipart_upload_id": { + "name": "multipart_upload_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "overwrite": { + "name": "overwrite", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "part_size": { + "name": "part_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope_id": { + "name": "scope_id", + "type": "text CHECK (\"scope_id\" = upper(\"scope_id\") AND length(\"scope_id\") = 26 AND substr(\"scope_id\", 1, 1) GLOB '[0-7]' AND \"scope_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "strategy": { + "name": "strategy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "file_upload_file_id_idx": { + "name": "file_upload_file_id_idx", + "columns": ["file_id"], + "isUnique": true + }, + "file_upload_status_expires_idx": { + "name": "file_upload_status_expires_idx", + "columns": ["status", "expires_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "file_version": { + "name": "file_version", + "columns": { + "committed": { + "name": "committed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "committed_at": { + "name": "committed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "file_id": { + "name": "file_id", + "type": "text CHECK (\"file_id\" = upper(\"file_id\") AND length(\"file_id\") = 26 AND substr(\"file_id\", 1, 1) GLOB '[0-7]' AND \"file_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope_id": { + "name": "scope_id", + "type": "text CHECK (\"scope_id\" = upper(\"scope_id\") AND length(\"scope_id\") = 26 AND substr(\"scope_id\", 1, 1) GLOB '[0-7]' AND \"scope_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_etag": { + "name": "source_etag", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_object_key": { + "name": "source_object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "file_version_object_key_idx": { + "name": "file_version_object_key_idx", + "columns": ["object_key"], + "isUnique": true + }, + "file_version_scope_path_created_idx": { + "name": "file_version_scope_path_created_idx", + "columns": ["scope_kind", "scope_id", "path", "created_at"], + "isUnique": false + }, + "file_version_file_created_idx": { + "name": "file_version_file_created_idx", + "columns": ["file_id", "created_at"], + "isUnique": false + }, + "file_version_pending_idx": { + "name": "file_version_pending_idx", + "columns": ["committed", "created_at"], + "isUnique": false, + "where": "\"file_version\".\"committed\" = 0" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session_run_artifact": { + "name": "session_run_artifact", + "columns": { + "committed_event_id": { + "name": "committed_event_id", + "type": "text CHECK (\"committed_event_id\" = upper(\"committed_event_id\") AND length(\"committed_event_id\") = 26 AND substr(\"committed_event_id\", 1, 1) GLOB '[0-7]' AND \"committed_event_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "file_id": { + "name": "file_id", + "type": "text CHECK (\"file_id\" = upper(\"file_id\") AND length(\"file_id\") = 26 AND substr(\"file_id\", 1, 1) GLOB '[0-7]' AND \"file_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_run_artifact_committed_event_idx": { + "name": "session_run_artifact_committed_event_idx", + "columns": ["committed_event_id"], + "isUnique": true + }, + "session_run_artifact_run_created_idx": { + "name": "session_run_artifact_run_created_idx", + "columns": ["session_run_id", "created_at", "file_id"], + "isUnique": false + } + }, + "foreignKeys": { + "session_run_artifact_session_run_id_session_run_id_fk": { + "name": "session_run_artifact_session_run_id_session_run_id_fk", + "tableFrom": "session_run_artifact", + "tableTo": "session_run", + "columnsFrom": ["session_run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mcp_credential": { + "name": "mcp_credential", + "columns": { + "account_id": { + "name": "account_id", + "type": "text CHECK (\"account_id\" = upper(\"account_id\") AND length(\"account_id\") = 26 AND substr(\"account_id\", 1, 1) GLOB '[0-7]' AND \"account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_refreshed_at": { + "name": "last_refreshed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "oauth_client_secret_secret_id": { + "name": "oauth_client_secret_secret_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_secret_id": { + "name": "refresh_secret_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope_values_json": { + "name": "scope_values_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "secret_id": { + "name": "secret_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "server_id": { + "name": "server_id", + "type": "text CHECK (\"server_id\" = upper(\"server_id\") AND length(\"server_id\") = 26 AND substr(\"server_id\", 1, 1) GLOB '[0-7]' AND \"server_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_label": { + "name": "subject_label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "mcp_credential_server_scope_status_idx": { + "name": "mcp_credential_server_scope_status_idx", + "columns": ["server_id", "scope", "status"], + "isUnique": false + }, + "mcp_credential_app_scope_status_idx": { + "name": "mcp_credential_app_scope_status_idx", + "columns": ["app_id", "scope", "status"], + "isUnique": false + }, + "mcp_credential_app_scope_idx": { + "name": "mcp_credential_app_scope_idx", + "columns": ["server_id", "scope"], + "isUnique": true, + "where": "\"mcp_credential\".\"scope\" = 'app'" + }, + "mcp_credential_agent_scope_idx": { + "name": "mcp_credential_agent_scope_idx", + "columns": ["server_id", "agent_id", "scope"], + "isUnique": true, + "where": "\"mcp_credential\".\"scope\" = 'agent' AND \"mcp_credential\".\"agent_id\" IS NOT NULL" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "mcp_credential_scope_shape_check": { + "name": "mcp_credential_scope_shape_check", + "value": "\n (\"mcp_credential\".\"scope\" = 'app' AND \"mcp_credential\".\"account_id\" IS NULL AND \"mcp_credential\".\"agent_id\" IS NULL)\n OR (\"mcp_credential\".\"scope\" = 'agent' AND \"mcp_credential\".\"account_id\" IS NULL AND \"mcp_credential\".\"agent_id\" IS NOT NULL)\n " + }, + "mcp_credential_scope_values_json_check": { + "name": "mcp_credential_scope_values_json_check", + "value": "\n \"mcp_credential\".\"scope_values_json\" IS NULL\n OR (json_valid(\"mcp_credential\".\"scope_values_json\") AND json_type(\"mcp_credential\".\"scope_values_json\") = 'array')\n " + }, + "mcp_credential_bearer_shape_check": { + "name": "mcp_credential_bearer_shape_check", + "value": "\n \"mcp_credential\".\"auth_type\" != 'bearer'\n OR (\n \"mcp_credential\".\"oauth_client_id\" IS NULL\n AND \"mcp_credential\".\"oauth_client_secret_secret_id\" IS NULL\n AND \"mcp_credential\".\"refresh_secret_id\" IS NULL\n )\n " + } + } + }, + "mcp_oauth_flow": { + "name": "mcp_oauth_flow", + "columns": { + "authorization_endpoint": { + "name": "authorization_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_after": { + "name": "cleanup_after", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "initiator_account_id": { + "name": "initiator_account_id", + "type": "text CHECK (\"initiator_account_id\" = upper(\"initiator_account_id\") AND length(\"initiator_account_id\") = 26 AND substr(\"initiator_account_id\", 1, 1) GLOB '[0-7]' AND \"initiator_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oauth_client_secret_secret_id": { + "name": "oauth_client_secret_secret_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "registration_endpoint": { + "name": "registration_endpoint", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "return_url": { + "name": "return_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope_values_json": { + "name": "scope_values_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "server_id": { + "name": "server_id", + "type": "text CHECK (\"server_id\" = upper(\"server_id\") AND length(\"server_id\") = 26 AND substr(\"server_id\", 1, 1) GLOB '[0-7]' AND \"server_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_label": { + "name": "subject_label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_endpoint": { + "name": "token_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "mcp_oauth_flow_status_cleanup_after_idx": { + "name": "mcp_oauth_flow_status_cleanup_after_idx", + "columns": ["status", "cleanup_after"], + "isUnique": false + }, + "mcp_oauth_flow_expires_at_idx": { + "name": "mcp_oauth_flow_expires_at_idx", + "columns": ["expires_at"], + "isUnique": false + }, + "mcp_oauth_flow_server_account_idx": { + "name": "mcp_oauth_flow_server_account_idx", + "columns": ["server_id", "initiator_account_id"], + "isUnique": false + }, + "mcp_oauth_flow_app_server_account_idx": { + "name": "mcp_oauth_flow_app_server_account_idx", + "columns": ["app_id", "server_id", "initiator_account_id"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "mcp_oauth_flow_scope_values_json_check": { + "name": "mcp_oauth_flow_scope_values_json_check", + "value": "\n \"mcp_oauth_flow\".\"scope_values_json\" IS NULL\n OR (json_valid(\"mcp_oauth_flow\".\"scope_values_json\") AND json_type(\"mcp_oauth_flow\".\"scope_values_json\") = 'array')\n " + } + } + }, + "mcp_server": { + "name": "mcp_server", + "columns": { + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "byo_client_id": { + "name": "byo_client_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "byo_client_secret_secret_id": { + "name": "byo_client_secret_secret_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credential_scope": { + "name": "credential_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oauth_metadata_json": { + "name": "oauth_metadata_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "mcp_server_app_enabled_idx": { + "name": "mcp_server_app_enabled_idx", + "columns": ["app_id", "enabled"], + "isUnique": false + }, + "mcp_server_owner_app_idx": { + "name": "mcp_server_owner_app_idx", + "columns": ["owner_account_id", "app_id"], + "isUnique": false + }, + "mcp_server_app_url_idx": { + "name": "mcp_server_app_url_idx", + "columns": ["app_id", "url"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "mcp_server_source_scope_check": { + "name": "mcp_server_source_scope_check", + "value": "\"mcp_server\".\"source\" = 'app' AND \"mcp_server\".\"credential_scope\" = 'app'" + } + } + }, + "vault_secret": { + "name": "vault_secret", + "columns": { + "algorithm": { + "name": "algorithm", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'AES-GCM'" + }, + "ciphertext": { + "name": "ciphertext", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ciphertext_iv": { + "name": "ciphertext_iv", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "wrapped_dek": { + "name": "wrapped_dek", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "wrapped_dek_iv": { + "name": "wrapped_dek_iv", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "vault_secret_kind_created_at_idx": { + "name": "vault_secret_kind_created_at_idx", + "columns": ["kind", "created_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization": { + "name": "organization", + "columns": { + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "creator_account_id": { + "name": "creator_account_id", + "type": "text CHECK (\"creator_account_id\" = upper(\"creator_account_id\") AND length(\"creator_account_id\") = 26 AND substr(\"creator_account_id\", 1, 1) GLOB '[0-7]' AND \"creator_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organization_creator_account_idx": { + "name": "organization_creator_account_idx", + "columns": ["creator_account_id"], + "isUnique": true, + "where": "\"organization\".\"creator_account_id\" IS NOT NULL" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "app_deployment_run": { + "name": "app_deployment_run", + "columns": { + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deployment_id": { + "name": "deployment_id", + "type": "text CHECK (\"deployment_id\" = upper(\"deployment_id\") AND length(\"deployment_id\") = 26 AND substr(\"deployment_id\", 1, 1) GLOB '[0-7]' AND \"deployment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_deployment_id": { + "name": "external_deployment_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_project_id": { + "name": "external_project_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_version_id": { + "name": "external_version_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "generated_wrangler_config_json": { + "name": "generated_wrangler_config_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "mosoo_config_json": { + "name": "mosoo_config_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_json": { + "name": "plan_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_branch": { + "name": "source_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_commit_sha": { + "name": "source_commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_kind": { + "name": "target_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_project_name": { + "name": "target_project_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_script_name": { + "name": "target_script_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "app_deployment_run_app_id_idx": { + "name": "app_deployment_run_app_id_idx", + "columns": ["app_id", "id"], + "isUnique": false + }, + "app_deployment_run_deployment_id_idx": { + "name": "app_deployment_run_deployment_id_idx", + "columns": ["deployment_id", "id"], + "isUnique": false + }, + "app_deployment_run_active_app_idx": { + "name": "app_deployment_run_active_app_idx", + "columns": ["app_id"], + "isUnique": true, + "where": "\"app_deployment_run\".\"status\" IN ('queued', 'preparing', 'building', 'submitting', 'submitted', 'activating')" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "app_deployment_run_status_check": { + "name": "app_deployment_run_status_check", + "value": "\"app_deployment_run\".\"status\" IN ('queued', 'preparing', 'building', 'submitting', 'submitted', 'activating', 'success', 'failed')" + }, + "app_deployment_run_target_kind_check": { + "name": "app_deployment_run_target_kind_check", + "value": "\"app_deployment_run\".\"target_kind\" IS NULL OR \"app_deployment_run\".\"target_kind\" IN ('cloudflare_pages', 'cloudflare_worker')" + } + } + }, + "app_deployment_secret": { + "name": "app_deployment_secret", + "columns": { + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vault_secret_id": { + "name": "vault_secret_id", + "type": "text CHECK (\"vault_secret_id\" = upper(\"vault_secret_id\") AND length(\"vault_secret_id\") = 26 AND substr(\"vault_secret_id\", 1, 1) GLOB '[0-7]' AND \"vault_secret_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "app_deployment_secret_app_name_idx": { + "name": "app_deployment_secret_app_name_idx", + "columns": ["app_id", "name"], + "isUnique": true + }, + "app_deployment_secret_vault_secret_idx": { + "name": "app_deployment_secret_vault_secret_idx", + "columns": ["vault_secret_id"], + "isUnique": true + } + }, + "foreignKeys": { + "app_deployment_secret_vault_secret_id_vault_secret_id_fk": { + "name": "app_deployment_secret_vault_secret_id_vault_secret_id_fk", + "tableFrom": "app_deployment_secret", + "tableTo": "vault_secret", + "columnsFrom": ["vault_secret_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "app_deployment": { + "name": "app_deployment", + "columns": { + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_successful_url": { + "name": "last_successful_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "latest_run_id": { + "name": "latest_run_id", + "type": "text CHECK (\"latest_run_id\" = upper(\"latest_run_id\") AND length(\"latest_run_id\") = 26 AND substr(\"latest_run_id\", 1, 1) GLOB '[0-7]' AND \"latest_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mosoo_subdomain": { + "name": "mosoo_subdomain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repo_name": { + "name": "repo_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repo_owner": { + "name": "repo_owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repo_url": { + "name": "repo_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "app_deployment_active_app_idx": { + "name": "app_deployment_active_app_idx", + "columns": ["app_id"], + "isUnique": true, + "where": "\"app_deployment\".\"deleted_at\" IS NULL" + }, + "app_deployment_active_subdomain_idx": { + "name": "app_deployment_active_subdomain_idx", + "columns": ["mosoo_subdomain"], + "isUnique": true, + "where": "\"app_deployment\".\"deleted_at\" IS NULL" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "app_deployment_source_kind_check": { + "name": "app_deployment_source_kind_check", + "value": "\"app_deployment\".\"source_kind\" IN ('github_public')" + } + } + }, + "app": { + "name": "app", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_environment_id": { + "name": "default_environment_id", + "type": "text CHECK (\"default_environment_id\" = upper(\"default_environment_id\") AND length(\"default_environment_id\") = 26 AND substr(\"default_environment_id\", 1, 1) GLOB '[0-7]' AND \"default_environment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text CHECK (\"organization_id\" = upper(\"organization_id\") AND length(\"organization_id\") = 26 AND substr(\"organization_id\", 1, 1) GLOB '[0-7]' AND \"organization_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "bound_agent_call_idempotency_key": { + "name": "bound_agent_call_idempotency_key", + "columns": { + "body_hash": { + "name": "body_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "run_id": { + "name": "run_id", + "type": "text CHECK (\"run_id\" = upper(\"run_id\") AND length(\"run_id\") = 26 AND substr(\"run_id\", 1, 1) GLOB '[0-7]' AND \"run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_hash": { + "name": "subject_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "bound_agent_call_idempotency_subject_key_idx": { + "name": "bound_agent_call_idempotency_subject_key_idx", + "columns": ["subject_hash", "idempotency_key"], + "isUnique": true + }, + "bound_agent_call_idempotency_updated_idx": { + "name": "bound_agent_call_idempotency_updated_idx", + "columns": ["updated_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public_api_idempotency_key": { + "name": "public_api_idempotency_key", + "columns": { + "body_hash": { + "name": "body_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "response_json": { + "name": "response_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "response_status": { + "name": "response_status", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "route": { + "name": "route", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_id": { + "name": "token_id", + "type": "text CHECK (\"token_id\" = upper(\"token_id\") AND length(\"token_id\") = 26 AND substr(\"token_id\", 1, 1) GLOB '[0-7]' AND \"token_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "public_api_idempotency_token_key_idx": { + "name": "public_api_idempotency_token_key_idx", + "columns": ["token_id", "idempotency_key"], + "isUnique": true + }, + "public_api_idempotency_updated_idx": { + "name": "public_api_idempotency_updated_idx", + "columns": ["updated_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public_api_rate_limit_window": { + "name": "public_api_rate_limit_window", + "columns": { + "bucket_key": { + "name": "bucket_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "shard": { + "name": "shard", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "window_start": { + "name": "window_start", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "public_api_rate_limit_window_updated_idx": { + "name": "public_api_rate_limit_window_updated_idx", + "columns": ["updated_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "public_api_rate_limit_window_bucket_key_window_start_shard_pk": { + "columns": ["bucket_key", "window_start", "shard"], + "name": "public_api_rate_limit_window_bucket_key_window_start_shard_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "driver_command": { + "name": "driver_command", + "columns": { + "acked_at": { + "name": "acked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_connection_id": { + "name": "delivery_connection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_instance_id": { + "name": "driver_instance_id", + "type": "text CHECK (\"driver_instance_id\" = upper(\"driver_instance_id\") AND length(\"driver_instance_id\") = 26 AND substr(\"driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_json": { + "name": "error_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issued_at": { + "name": "issued_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "result_json": { + "name": "result_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "seq": { + "name": "seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "driver_command_instance_seq_idx": { + "name": "driver_command_instance_seq_idx", + "columns": ["driver_instance_id", "seq"], + "isUnique": true + }, + "driver_command_instance_status_idx": { + "name": "driver_command_instance_status_idx", + "columns": ["driver_instance_id", "status", "expires_at"], + "isUnique": false + } + }, + "foreignKeys": { + "driver_command_driver_instance_id_driver_instance_id_fk": { + "name": "driver_command_driver_instance_id_driver_instance_id_fk", + "tableFrom": "driver_command", + "tableTo": "driver_instance", + "columnsFrom": ["driver_instance_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "driver_instance_mcp_grant": { + "name": "driver_instance_mcp_grant", + "columns": { + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "authorization_state": { + "name": "authorization_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "can_invalidate": { + "name": "can_invalidate", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "can_refresh": { + "name": "can_refresh", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credential_id": { + "name": "credential_id", + "type": "text CHECK (\"credential_id\" = upper(\"credential_id\") AND length(\"credential_id\") = 26 AND substr(\"credential_id\", 1, 1) GLOB '[0-7]' AND \"credential_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_instance_id": { + "name": "driver_instance_id", + "type": "text CHECK (\"driver_instance_id\" = upper(\"driver_instance_id\") AND length(\"driver_instance_id\") = 26 AND substr(\"driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "server_id": { + "name": "server_id", + "type": "text CHECK (\"server_id\" = upper(\"server_id\") AND length(\"server_id\") = 26 AND substr(\"server_id\", 1, 1) GLOB '[0-7]' AND \"server_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "driver_instance_mcp_grant_instance_server_idx": { + "name": "driver_instance_mcp_grant_instance_server_idx", + "columns": ["driver_instance_id", "server_id"], + "isUnique": true + }, + "driver_instance_mcp_grant_instance_credential_idx": { + "name": "driver_instance_mcp_grant_instance_credential_idx", + "columns": ["driver_instance_id", "credential_id"], + "isUnique": false + } + }, + "foreignKeys": { + "driver_instance_mcp_grant_driver_instance_id_driver_instance_id_fk": { + "name": "driver_instance_mcp_grant_driver_instance_id_driver_instance_id_fk", + "tableFrom": "driver_instance_mcp_grant", + "tableTo": "driver_instance", + "columnsFrom": ["driver_instance_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "driver_instance": { + "name": "driver_instance", + "columns": { + "boot_token_expires_at": { + "name": "boot_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "boot_token_hash": { + "name": "boot_token_hash", + "type": "blob", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "boot_token_used_at": { + "name": "boot_token_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "close_code": { + "name": "close_code", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "close_reason": { + "name": "close_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "command_seq_cursor": { + "name": "command_seq_cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "driver_pid": { + "name": "driver_pid", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_started_at": { + "name": "driver_started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_version": { + "name": "driver_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "heartbeat_count": { + "name": "heartbeat_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "protocol": { + "name": "protocol", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "protocol_version": { + "name": "protocol_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "restart_count": { + "name": "restart_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "text CHECK (\"sandbox_id\" = upper(\"sandbox_id\") AND length(\"sandbox_id\") = 26 AND substr(\"sandbox_id\", 1, 1) GLOB '[0-7]' AND \"sandbox_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sandbox_session_id": { + "name": "sandbox_session_id", + "type": "text CHECK (\"sandbox_session_id\" = upper(\"sandbox_session_id\") AND length(\"sandbox_session_id\") = 26 AND substr(\"sandbox_session_id\", 1, 1) GLOB '[0-7]' AND \"sandbox_session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_changed_at": { + "name": "status_changed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status_event": { + "name": "status_event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'driver.provision'" + }, + "status_operation_id": { + "name": "status_operation_id", + "type": "text CHECK (\"status_operation_id\" = upper(\"status_operation_id\") AND length(\"status_operation_id\") = 26 AND substr(\"status_operation_id\", 1, 1) GLOB '[0-7]' AND \"status_operation_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_seq": { + "name": "status_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status_source": { + "name": "status_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "driver_instance_completed_idx": { + "name": "driver_instance_completed_idx", + "columns": ["expires_at", "status"], + "isUnique": false + }, + "driver_instance_connection_idx": { + "name": "driver_instance_connection_idx", + "columns": ["connection_id"], + "isUnique": true, + "where": "\"driver_instance\".\"connection_id\" IS NOT NULL" + }, + "driver_instance_boot_token_expiry_idx": { + "name": "driver_instance_boot_token_expiry_idx", + "columns": ["status", "boot_token_expires_at"], + "isUnique": false, + "where": "\"driver_instance\".\"boot_token_used_at\" IS NULL" + }, + "driver_instance_boot_token_hash_idx": { + "name": "driver_instance_boot_token_hash_idx", + "columns": ["boot_token_hash"], + "isUnique": true + }, + "driver_instance_sandbox_session_idx": { + "name": "driver_instance_sandbox_session_idx", + "columns": ["sandbox_id", "sandbox_session_id", "status", "updated_at"], + "isUnique": false + }, + "driver_instance_live_sandbox_session_idx": { + "name": "driver_instance_live_sandbox_session_idx", + "columns": ["sandbox_id", "sandbox_session_id"], + "isUnique": true, + "where": "\"driver_instance\".\"status\" IN ('provisioning', 'connecting', 'ready', 'stopping')" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "driver_instance_status_check": { + "name": "driver_instance_status_check", + "value": "\"driver_instance\".\"status\" IN ('provisioning', 'connecting', 'ready', 'stopping', 'stopped', 'failed')" + }, + "driver_instance_status_seq_check": { + "name": "driver_instance_status_seq_check", + "value": "\"driver_instance\".\"status_seq\" >= 0" + } + } + }, + "external_tool_effect_attempt": { + "name": "external_tool_effect_attempt", + "columns": { + "attempt": { + "name": "attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "effect_id": { + "name": "effect_id", + "type": "text CHECK (\"effect_id\" = upper(\"effect_id\") AND length(\"effect_id\") = 26 AND substr(\"effect_id\", 1, 1) GLOB '[0-7]' AND \"effect_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_receipt_json": { + "name": "provider_receipt_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result_json": { + "name": "result_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "external_tool_effect_attempt_status_idx": { + "name": "external_tool_effect_attempt_status_idx", + "columns": ["status", "created_at"], + "isUnique": false + } + }, + "foreignKeys": { + "external_tool_effect_attempt_effect_id_external_tool_effect_id_fk": { + "name": "external_tool_effect_attempt_effect_id_external_tool_effect_id_fk", + "tableFrom": "external_tool_effect_attempt", + "tableTo": "external_tool_effect", + "columnsFrom": ["effect_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "external_tool_effect_attempt_effect_id_attempt_pk": { + "columns": ["effect_id", "attempt"], + "name": "external_tool_effect_attempt_effect_id_attempt_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": { + "external_tool_effect_attempt_status_check": { + "name": "external_tool_effect_attempt_status_check", + "value": "\"external_tool_effect_attempt\".\"status\" IN ('executing', 'succeeded', 'unknown')" + } + } + }, + "external_tool_effect": { + "name": "external_tool_effect", + "columns": { + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "command_id": { + "name": "command_id", + "type": "text CHECK (\"command_id\" = upper(\"command_id\") AND length(\"command_id\") = 26 AND substr(\"command_id\", 1, 1) GLOB '[0-7]' AND \"command_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "driver_instance_id": { + "name": "driver_instance_id", + "type": "text CHECK (\"driver_instance_id\" = upper(\"driver_instance_id\") AND length(\"driver_instance_id\") = 26 AND substr(\"driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_receipt_json": { + "name": "provider_receipt_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result_json": { + "name": "result_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "server_id": { + "name": "server_id", + "type": "text CHECK (\"server_id\" = upper(\"server_id\") AND length(\"server_id\") = 26 AND substr(\"server_id\", 1, 1) GLOB '[0-7]' AND \"server_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "external_tool_effect_command_idx": { + "name": "external_tool_effect_command_idx", + "columns": ["command_id"], + "isUnique": true + }, + "external_tool_effect_idempotency_key_idx": { + "name": "external_tool_effect_idempotency_key_idx", + "columns": ["idempotency_key"], + "isUnique": true + }, + "external_tool_effect_run_status_idx": { + "name": "external_tool_effect_run_status_idx", + "columns": ["session_run_id", "status", "id"], + "isUnique": false + }, + "external_tool_effect_driver_status_idx": { + "name": "external_tool_effect_driver_status_idx", + "columns": ["driver_instance_id", "status"], + "isUnique": false + } + }, + "foreignKeys": { + "external_tool_effect_command_id_driver_command_id_fk": { + "name": "external_tool_effect_command_id_driver_command_id_fk", + "tableFrom": "external_tool_effect", + "tableTo": "driver_command", + "columnsFrom": ["command_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "external_tool_effect_driver_instance_id_driver_instance_id_fk": { + "name": "external_tool_effect_driver_instance_id_driver_instance_id_fk", + "tableFrom": "external_tool_effect", + "tableTo": "driver_instance", + "columnsFrom": ["driver_instance_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "external_tool_effect_session_run_id_session_run_id_fk": { + "name": "external_tool_effect_session_run_id_session_run_id_fk", + "tableFrom": "external_tool_effect", + "tableTo": "session_run", + "columnsFrom": ["session_run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "external_tool_effect_status_check": { + "name": "external_tool_effect_status_check", + "value": "\"external_tool_effect\".\"status\" IN ('intent', 'executing', 'succeeded', 'unknown')" + } + } + }, + "native_resume_ref": { + "name": "native_resume_ref", + "columns": { + "committed_session_run_id": { + "name": "committed_session_run_id", + "type": "text CHECK (\"committed_session_run_id\" = upper(\"committed_session_run_id\") AND length(\"committed_session_run_id\") = 26 AND substr(\"committed_session_run_id\", 1, 1) GLOB '[0-7]' AND \"committed_session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "committed_value": { + "name": "committed_value", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "observed_driver_instance_id": { + "name": "observed_driver_instance_id", + "type": "text CHECK (\"observed_driver_instance_id\" = upper(\"observed_driver_instance_id\") AND length(\"observed_driver_instance_id\") = 26 AND substr(\"observed_driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"observed_driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "observed_session_run_id": { + "name": "observed_session_run_id", + "type": "text CHECK (\"observed_session_run_id\" = upper(\"observed_session_run_id\") AND length(\"observed_session_run_id\") = 26 AND substr(\"observed_session_run_id\", 1, 1) GLOB '[0-7]' AND \"observed_session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "runtime_id": { + "name": "runtime_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "native_resume_ref_runtime_updated_idx": { + "name": "native_resume_ref_runtime_updated_idx", + "columns": ["runtime_id", "updated_at"], + "isUnique": false + } + }, + "foreignKeys": { + "native_resume_ref_session_id_session_id_fk": { + "name": "native_resume_ref_session_id_session_id_fk", + "tableFrom": "native_resume_ref", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sandbox_backup": { + "name": "sandbox_backup", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "dir": { + "name": "dir", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "keep": { + "name": "keep", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "text CHECK (\"sandbox_id\" = upper(\"sandbox_id\") AND length(\"sandbox_id\") = 26 AND substr(\"sandbox_id\", 1, 1) GLOB '[0-7]' AND \"sandbox_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ttl_seconds": { + "name": "ttl_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "sandbox_backup_sandbox_status_created_idx": { + "name": "sandbox_backup_sandbox_status_created_idx", + "columns": ["sandbox_id", "status", "created_at"], + "isUnique": false + }, + "sandbox_backup_terminal_checkpoint_idx": { + "name": "sandbox_backup_terminal_checkpoint_idx", + "columns": ["sandbox_id", "dir", "session_run_id"], + "isUnique": true, + "where": "\"sandbox_backup\".\"session_run_id\" IS NOT NULL AND \"sandbox_backup\".\"status\" = 'ready'" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sandbox_session": { + "name": "sandbox_session", + "columns": { + "cloudflare_session_id": { + "name": "cloudflare_session_id", + "type": "text CHECK (\"cloudflare_session_id\" = upper(\"cloudflare_session_id\") AND length(\"cloudflare_session_id\") = 26 AND substr(\"cloudflare_session_id\", 1, 1) GLOB '[0-7]' AND \"cloudflare_session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "origin_json": { + "name": "origin_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "text CHECK (\"sandbox_id\" = upper(\"sandbox_id\") AND length(\"sandbox_id\") = 26 AND substr(\"sandbox_id\", 1, 1) GLOB '[0-7]' AND \"sandbox_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "sandbox_session_sandbox_status_idx": { + "name": "sandbox_session_sandbox_status_idx", + "columns": ["sandbox_id", "status", "updated_at"], + "isUnique": false + }, + "sandbox_session_cloudflare_session_idx": { + "name": "sandbox_session_cloudflare_session_idx", + "columns": ["cloudflare_session_id"], + "isUnique": true + } + }, + "foreignKeys": { + "sandbox_session_session_id_session_id_fk": { + "name": "sandbox_session_session_id_session_id_fk", + "tableFrom": "sandbox_session", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sandbox": { + "name": "sandbox", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bind_mount_ready": { + "name": "bind_mount_ready", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "claim_expires_at": { + "name": "claim_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claim_owner": { + "name": "claim_owner", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "global_mounts_json": { + "name": "global_mounts_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "inactive_deadline_at": { + "name": "inactive_deadline_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_backup_id": { + "name": "last_backup_id", + "type": "text CHECK (\"last_backup_id\" = upper(\"last_backup_id\") AND length(\"last_backup_id\") = 26 AND substr(\"last_backup_id\", 1, 1) GLOB '[0-7]' AND \"last_backup_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_restore_backup_id": { + "name": "last_restore_backup_id", + "type": "text CHECK (\"last_restore_backup_id\" = upper(\"last_restore_backup_id\") AND length(\"last_restore_backup_id\") = 26 AND substr(\"last_restore_backup_id\", 1, 1) GLOB '[0-7]' AND \"last_restore_backup_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_changed_at": { + "name": "status_changed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status_event": { + "name": "status_event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'runtime_subject.cold'" + }, + "status_operation_id": { + "name": "status_operation_id", + "type": "text CHECK (\"status_operation_id\" = upper(\"status_operation_id\") AND length(\"status_operation_id\") = 26 AND substr(\"status_operation_id\", 1, 1) GLOB '[0-7]' AND \"status_operation_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_seq": { + "name": "status_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status_source": { + "name": "status_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "subject_id": { + "name": "subject_id", + "type": "text CHECK (\"subject_id\" = upper(\"subject_id\") AND length(\"subject_id\") = 26 AND substr(\"subject_id\", 1, 1) GLOB '[0-7]' AND \"subject_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_kind": { + "name": "subject_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "sandbox_subject_idx": { + "name": "sandbox_subject_idx", + "columns": ["kind", "subject_kind", "subject_id"], + "isUnique": true + }, + "sandbox_status_deadline_idx": { + "name": "sandbox_status_deadline_idx", + "columns": ["status", "inactive_deadline_at", "updated_at"], + "isUnique": false + }, + "sandbox_claim_idx": { + "name": "sandbox_claim_idx", + "columns": ["claim_expires_at", "claim_owner"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "sandbox_status_check": { + "name": "sandbox_status_check", + "value": "\"sandbox\".\"status\" IN ('cold', 'restoring', 'active', 'backing_up', 'destroying', 'error')" + }, + "sandbox_status_seq_check": { + "name": "sandbox_status_seq_check", + "value": "\"sandbox\".\"status_seq\" >= 0" + } + } + }, + "session_message": { + "name": "session_message", + "columns": { + "content_text": { + "name": "content_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "plan_json": { + "name": "plan_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "segments_json": { + "name": "segments_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "seq": { + "name": "seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "session_message_session_seq_idx": { + "name": "session_message_session_seq_idx", + "columns": ["session_id", "seq"], + "isUnique": true + }, + "session_message_run_idx": { + "name": "session_message_run_idx", + "columns": ["session_run_id"], + "isUnique": false + } + }, + "foreignKeys": { + "session_message_session_id_session_id_fk": { + "name": "session_message_session_id_session_id_fk", + "tableFrom": "session_message", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_user_id": { + "name": "end_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "attributed_user_id": { + "name": "attributed_user_id", + "type": "text CHECK (\"attributed_user_id\" = upper(\"attributed_user_id\") AND length(\"attributed_user_id\") = 26 AND substr(\"attributed_user_id\", 1, 1) GLOB '[0-7]' AND \"attributed_user_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "creator_account_id": { + "name": "creator_account_id", + "type": "text CHECK (\"creator_account_id\" = upper(\"creator_account_id\") AND length(\"creator_account_id\") = 26 AND substr(\"creator_account_id\", 1, 1) GLOB '[0-7]' AND \"creator_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text CHECK (\"deployment_version_id\" = upper(\"deployment_version_id\") AND length(\"deployment_version_id\") = 26 AND substr(\"deployment_version_id\", 1, 1) GLOB '[0-7]' AND \"deployment_version_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deployment_version_number": { + "name": "deployment_version_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_run_id": { + "name": "last_run_id", + "type": "text CHECK (\"last_run_id\" = upper(\"last_run_id\") AND length(\"last_run_id\") = 26 AND substr(\"last_run_id\", 1, 1) GLOB '[0-7]' AND \"last_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "message_seq_cursor": { + "name": "message_seq_cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "metadata_json": { + "name": "metadata_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "renamed": { + "name": "renamed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "runtime_id": { + "name": "runtime_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_operation_id": { + "name": "status_operation_id", + "type": "text CHECK (\"status_operation_id\" = upper(\"status_operation_id\") AND length(\"status_operation_id\") = 26 AND substr(\"status_operation_id\", 1, 1) GLOB '[0-7]' AND \"status_operation_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_seq": { + "name": "status_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "runtime_event_seq_cursor": { + "name": "runtime_event_seq_cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'preview'" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_checkpoint_required": { + "name": "workspace_checkpoint_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": { + "session_agent_updated_idx": { + "name": "session_agent_updated_idx", + "columns": ["agent_id", "updated_at", "id"], + "isUnique": false + }, + "session_app_creator_archived_updated_idx": { + "name": "session_app_creator_archived_updated_idx", + "columns": ["app_id", "creator_account_id", "archived_at", "updated_at", "id"], + "isUnique": false + }, + "session_app_attributed_archived_updated_idx": { + "name": "session_app_attributed_archived_updated_idx", + "columns": ["app_id", "attributed_user_id", "archived_at", "updated_at", "id"], + "isUnique": false + }, + "session_app_creator_type_archived_updated_idx": { + "name": "session_app_creator_type_archived_updated_idx", + "columns": ["app_id", "creator_account_id", "type", "archived_at", "updated_at", "id"], + "isUnique": false + }, + "session_app_attributed_type_archived_updated_idx": { + "name": "session_app_attributed_type_archived_updated_idx", + "columns": ["app_id", "attributed_user_id", "type", "archived_at", "updated_at", "id"], + "isUnique": false + }, + "session_status_operation_updated_idx": { + "name": "session_status_operation_updated_idx", + "columns": ["status", "status_operation_id", "updated_at"], + "isUnique": false + }, + "session_status_updated_idx": { + "name": "session_status_updated_idx", + "columns": ["status", "updated_at", "id"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "session_status_check": { + "name": "session_status_check", + "value": "\"session\".\"status\" IN ('IDLE', 'RUNNING', 'RESCHEDULING', 'TERMINATED')" + }, + "session_status_seq_check": { + "name": "session_status_seq_check", + "value": "\"session\".\"status_seq\" >= 0" + } + } + }, + "session_execution_snapshot": { + "name": "session_execution_snapshot", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "plan_json": { + "name": "plan_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_execution_snapshot_session_id_session_id_fk": { + "name": "session_execution_snapshot_session_id_session_id_fk", + "tableFrom": "session_execution_snapshot", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session_run_skill": { + "name": "session_run_skill", + "columns": { + "blob_sha256": { + "name": "blob_sha256", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "materialization_status": { + "name": "materialization_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mount_path": { + "name": "mount_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resolution_mode": { + "name": "resolution_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "skill_id": { + "name": "skill_id", + "type": "text CHECK (\"skill_id\" = upper(\"skill_id\") AND length(\"skill_id\") = 26 AND substr(\"skill_id\", 1, 1) GLOB '[0-7]' AND \"skill_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "skill_name": { + "name": "skill_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "text CHECK (\"snapshot_id\" = upper(\"snapshot_id\") AND length(\"snapshot_id\") = 26 AND substr(\"snapshot_id\", 1, 1) GLOB '[0-7]' AND \"snapshot_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "warning_code": { + "name": "warning_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "session_run_skill_run_resolution_idx": { + "name": "session_run_skill_run_resolution_idx", + "columns": ["session_run_id", "resolution_mode"], + "isUnique": false + } + }, + "foreignKeys": { + "session_run_skill_session_run_id_session_run_id_fk": { + "name": "session_run_skill_session_run_id_session_run_id_fk", + "tableFrom": "session_run_skill", + "tableTo": "session_run", + "columnsFrom": ["session_run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "session_run_skill_session_run_id_skill_id_pk": { + "columns": ["session_run_id", "skill_id"], + "name": "session_run_skill_session_run_id_skill_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session_run": { + "name": "session_run", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bound_capability_agent_id": { + "name": "bound_capability_agent_id", + "type": "text CHECK (\"bound_capability_agent_id\" = upper(\"bound_capability_agent_id\") AND length(\"bound_capability_agent_id\") = 26 AND substr(\"bound_capability_agent_id\", 1, 1) GLOB '[0-7]' AND \"bound_capability_agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bound_capability_app_id": { + "name": "bound_capability_app_id", + "type": "text CHECK (\"bound_capability_app_id\" = upper(\"bound_capability_app_id\") AND length(\"bound_capability_app_id\") = 26 AND substr(\"bound_capability_app_id\", 1, 1) GLOB '[0-7]' AND \"bound_capability_app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bound_capability_binding_env": { + "name": "bound_capability_binding_env", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bound_capability_binding_name": { + "name": "bound_capability_binding_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bound_capability_deployment_id": { + "name": "bound_capability_deployment_id", + "type": "text CHECK (\"bound_capability_deployment_id\" = upper(\"bound_capability_deployment_id\") AND length(\"bound_capability_deployment_id\") = 26 AND substr(\"bound_capability_deployment_id\", 1, 1) GLOB '[0-7]' AND \"bound_capability_deployment_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bound_capability_deployment_run_id": { + "name": "bound_capability_deployment_run_id", + "type": "text CHECK (\"bound_capability_deployment_run_id\" = upper(\"bound_capability_deployment_run_id\") AND length(\"bound_capability_deployment_run_id\") = 26 AND substr(\"bound_capability_deployment_run_id\", 1, 1) GLOB '[0-7]' AND \"bound_capability_deployment_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_account_id": { + "name": "created_by_account_id", + "type": "text CHECK (\"created_by_account_id\" = upper(\"created_by_account_id\") AND length(\"created_by_account_id\") = 26 AND substr(\"created_by_account_id\", 1, 1) GLOB '[0-7]' AND \"created_by_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text CHECK (\"deployment_version_id\" = upper(\"deployment_version_id\") AND length(\"deployment_version_id\") = 26 AND substr(\"deployment_version_id\", 1, 1) GLOB '[0-7]' AND \"deployment_version_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deployment_version_number": { + "name": "deployment_version_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_instance_id": { + "name": "driver_instance_id", + "type": "text CHECK (\"driver_instance_id\" = upper(\"driver_instance_id\") AND length(\"driver_instance_id\") = 26 AND substr(\"driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_details_json": { + "name": "error_details_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "runtime_id": { + "name": "runtime_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_changed_at": { + "name": "status_changed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status_event": { + "name": "status_event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'run.queue'" + }, + "status_operation_id": { + "name": "status_operation_id", + "type": "text CHECK (\"status_operation_id\" = upper(\"status_operation_id\") AND length(\"status_operation_id\") = 26 AND substr(\"status_operation_id\", 1, 1) GLOB '[0-7]' AND \"status_operation_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_seq": { + "name": "status_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status_source": { + "name": "status_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_run_driver_instance_idx": { + "name": "session_run_driver_instance_idx", + "columns": ["driver_instance_id", "created_at"], + "isUnique": false + }, + "session_run_active_driver_lease_idx": { + "name": "session_run_active_driver_lease_idx", + "columns": ["driver_instance_id"], + "isUnique": true, + "where": "\"session_run\".\"driver_instance_id\" IS NOT NULL AND \"session_run\".\"status\" IN ('queued', 'booting', 'running', 'waiting_input')" + }, + "session_run_session_created_at_idx": { + "name": "session_run_session_created_at_idx", + "columns": ["session_id", "created_at"], + "isUnique": false + }, + "session_run_session_status_idx": { + "name": "session_run_session_status_idx", + "columns": ["session_id", "status"], + "isUnique": false + } + }, + "foreignKeys": { + "session_run_session_id_session_id_fk": { + "name": "session_run_session_id_session_id_fk", + "tableFrom": "session_run", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "session_run_status_check": { + "name": "session_run_status_check", + "value": "\"session_run\".\"status\" IN ('queued', 'booting', 'running', 'waiting_input', 'completed', 'failed', 'cancelled', 'expired')" + }, + "session_run_status_seq_check": { + "name": "session_run_status_seq_check", + "value": "\"session_run\".\"status_seq\" >= 0" + } + } + }, + "session_event": { + "name": "session_event", + "columns": { + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_text": { + "name": "content_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ended_at": { + "name": "ended_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "family": { + "name": "family", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "process_status": { + "name": "process_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "process_type": { + "name": "process_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "run_id": { + "name": "run_id", + "type": "text CHECK (\"run_id\" = upper(\"run_id\") AND length(\"run_id\") = 26 AND substr(\"run_id\", 1, 1) GLOB '[0-7]' AND \"run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "seq": { + "name": "seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_event_id": { + "name": "source_event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tool_call_id": { + "name": "tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tool_input_json": { + "name": "tool_input_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_event_agent_family_created_idx": { + "name": "session_event_agent_family_created_idx", + "columns": ["agent_id", "family", "created_at", "id"], + "isUnique": false + }, + "session_event_agent_visibility_created_idx": { + "name": "session_event_agent_visibility_created_idx", + "columns": ["agent_id", "visibility", "created_at", "id"], + "isUnique": false + }, + "session_event_agent_created_idx": { + "name": "session_event_agent_created_idx", + "columns": ["agent_id", "created_at", "id"], + "isUnique": false + }, + "session_event_session_visibility_seq_idx": { + "name": "session_event_session_visibility_seq_idx", + "columns": ["session_id", "visibility", "seq"], + "isUnique": false + }, + "session_event_run_event_type_idx": { + "name": "session_event_run_event_type_idx", + "columns": ["run_id", "event_type"], + "isUnique": false + }, + "session_event_session_seq_idx": { + "name": "session_event_session_seq_idx", + "columns": ["session_id", "seq"], + "isUnique": true + }, + "session_event_session_source_idx": { + "name": "session_event_session_source_idx", + "columns": ["session_id", "source_event_id"], + "isUnique": true + } + }, + "foreignKeys": { + "session_event_session_id_session_id_fk": { + "name": "session_event_session_id_session_id_fk", + "tableFrom": "session_event", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session_model_call": { + "name": "session_model_call", + "columns": { + "cache_creation_tokens": { + "name": "cache_creation_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "call_key": { + "name": "call_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cost_currency": { + "name": "cost_currency", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "driver_instance_id": { + "name": "driver_instance_id", + "type": "text CHECK (\"driver_instance_id\" = upper(\"driver_instance_id\") AND length(\"driver_instance_id\") = 26 AND substr(\"driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata_json": { + "name": "metadata_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "native_call_id": { + "name": "native_call_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "total_cost_usd_micros": { + "name": "total_cost_usd_micros", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_model_call_run_created_idx": { + "name": "session_model_call_run_created_idx", + "columns": ["session_run_id", "created_at"], + "isUnique": false + }, + "session_model_call_session_created_idx": { + "name": "session_model_call_session_created_idx", + "columns": ["session_id", "created_at"], + "isUnique": false + }, + "session_model_call_run_key_idx": { + "name": "session_model_call_run_key_idx", + "columns": ["session_run_id", "call_key"], + "isUnique": true + }, + "session_model_call_native_idx": { + "name": "session_model_call_native_idx", + "columns": ["driver_instance_id", "native_call_id"], + "isUnique": true + } + }, + "foreignKeys": { + "session_model_call_session_id_session_id_fk": { + "name": "session_model_call_session_id_session_id_fk", + "tableFrom": "session_model_call", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "session_model_call_session_run_id_session_run_id_fk": { + "name": "session_model_call_session_run_id_session_run_id_fk", + "tableFrom": "session_model_call", + "tableTo": "session_run", + "columnsFrom": ["session_run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session_permission_request": { + "name": "session_permission_request", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "driver_instance_id": { + "name": "driver_instance_id", + "type": "text CHECK (\"driver_instance_id\" = upper(\"driver_instance_id\") AND length(\"driver_instance_id\") = 26 AND substr(\"driver_instance_id\", 1, 1) GLOB '[0-7]' AND \"driver_instance_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "raw_input": { + "name": "raw_input", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "run_id": { + "name": "run_id", + "type": "text CHECK (\"run_id\" = upper(\"run_id\") AND length(\"run_id\") = 26 AND substr(\"run_id\", 1, 1) GLOB '[0-7]' AND \"run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tool_call_id": { + "name": "tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tool_kind": { + "name": "tool_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_permission_request_run_idx": { + "name": "session_permission_request_run_idx", + "columns": ["session_id", "run_id"], + "isUnique": false + } + }, + "foreignKeys": { + "session_permission_request_session_id_session_id_fk": { + "name": "session_permission_request_session_id_session_id_fk", + "tableFrom": "session_permission_request", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "session_permission_request_session_id_request_id_pk": { + "columns": ["session_id", "request_id"], + "name": "session_permission_request_session_id_request_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session_readiness_snapshot": { + "name": "session_readiness_snapshot", + "columns": { + "readiness_json": { + "name": "readiness_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_readiness_snapshot_session_id_session_id_fk": { + "name": "session_readiness_snapshot_session_id_session_id_fk", + "tableFrom": "session_readiness_snapshot", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "skill_snapshot_entry": { + "name": "skill_snapshot_entry", + "columns": { + "entry_kind": { + "name": "entry_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_executable": { + "name": "is_executable", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "text CHECK (\"snapshot_id\" = upper(\"snapshot_id\") AND length(\"snapshot_id\") = 26 AND substr(\"snapshot_id\", 1, 1) GLOB '[0-7]' AND \"snapshot_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "skill_snapshot_entry_snapshot_id_path_pk": { + "columns": ["snapshot_id", "path"], + "name": "skill_snapshot_entry_snapshot_id_path_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "skill_snapshot": { + "name": "skill_snapshot", + "columns": { + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "blob_key": { + "name": "blob_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "blob_sha256": { + "name": "blob_sha256", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "blob_size": { + "name": "blob_size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "skill_markdown_path": { + "name": "skill_markdown_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uncompressed_size": { + "name": "uncompressed_size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "skill_snapshot_app_created_at_idx": { + "name": "skill_snapshot_app_created_at_idx", + "columns": ["app_id", "created_at"], + "isUnique": false + }, + "skill_snapshot_blob_sha256_idx": { + "name": "skill_snapshot_blob_sha256_idx", + "columns": ["app_id", "blob_sha256"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "skill": { + "name": "skill", + "columns": { + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "current_snapshot_id": { + "name": "current_snapshot_id", + "type": "text CHECK (\"current_snapshot_id\" = upper(\"current_snapshot_id\") AND length(\"current_snapshot_id\") = 26 AND substr(\"current_snapshot_id\", 1, 1) GLOB '[0-7]' AND \"current_snapshot_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "forked_from_owner_name": { + "name": "forked_from_owner_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "forked_from_skill_id": { + "name": "forked_from_skill_id", + "type": "text CHECK (\"forked_from_skill_id\" = upper(\"forked_from_skill_id\") AND length(\"forked_from_skill_id\") = 26 AND substr(\"forked_from_skill_id\", 1, 1) GLOB '[0-7]' AND \"forked_from_skill_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "forked_from_skill_name": { + "name": "forked_from_skill_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text CHECK (\"owner_account_id\" = upper(\"owner_account_id\") AND length(\"owner_account_id\") = 26 AND substr(\"owner_account_id\", 1, 1) GLOB '[0-7]' AND \"owner_account_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "skill_app_updated_at_idx": { + "name": "skill_app_updated_at_idx", + "columns": ["app_id", "updated_at"], + "isUnique": false + }, + "skill_owner_account_updated_at_idx": { + "name": "skill_owner_account_updated_at_idx", + "columns": ["owner_account_id", "updated_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "account": { + "name": "account", + "columns": { + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_active_organization_id": { + "name": "last_active_organization_id", + "type": "text CHECK (\"last_active_organization_id\" = upper(\"last_active_organization_id\") AND length(\"last_active_organization_id\") = 26 AND substr(\"last_active_organization_id\", 1, 1) GLOB '[0-7]' AND \"last_active_organization_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "system_agent_model": { + "name": "system_agent_model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "account_email_idx": { + "name": "account_email_idx", + "columns": ["email"], + "isUnique": true + }, + "account_last_active_organization_idx": { + "name": "account_last_active_organization_idx", + "columns": ["last_active_organization_id"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "usage_daily_rollup": { + "name": "usage_daily_rollup", + "columns": { + "actor_user_id": { + "name": "actor_user_id", + "type": "text CHECK (\"actor_user_id\" = upper(\"actor_user_id\") AND length(\"actor_user_id\") = 26 AND substr(\"actor_user_id\", 1, 1) GLOB '[0-7]' AND \"actor_user_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_owner_user_id": { + "name": "agent_owner_user_id", + "type": "text CHECK (\"agent_owner_user_id\" = upper(\"agent_owner_user_id\") AND length(\"agent_owner_user_id\") = 26 AND substr(\"agent_owner_user_id\", 1, 1) GLOB '[0-7]' AND \"agent_owner_user_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_publication_state_at_run": { + "name": "agent_publication_state_at_run", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cache_creation_tokens": { + "name": "cache_creation_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text CHECK (\"organization_id\" = upper(\"organization_id\") AND length(\"organization_id\") = 26 AND substr(\"organization_id\", 1, 1) GLOB '[0-7]' AND \"organization_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "run_purpose": { + "name": "run_purpose", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "total_cost_usd_micros": { + "name": "total_cost_usd_micros", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unpriced_request_count": { + "name": "unpriced_request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "usage_daily_rollup_app_date_idx": { + "name": "usage_daily_rollup_app_date_idx", + "columns": ["app_id", "date"], + "isUnique": false + }, + "usage_daily_rollup_organization_date_idx": { + "name": "usage_daily_rollup_organization_date_idx", + "columns": ["organization_id", "date"], + "isUnique": false + }, + "usage_daily_rollup_agent_date_idx": { + "name": "usage_daily_rollup_agent_date_idx", + "columns": ["agent_id", "date"], + "isUnique": false + }, + "usage_daily_rollup_actor_date_idx": { + "name": "usage_daily_rollup_actor_date_idx", + "columns": ["actor_user_id", "date"], + "isUnique": false + }, + "usage_daily_rollup_owner_date_idx": { + "name": "usage_daily_rollup_owner_date_idx", + "columns": ["agent_owner_user_id", "date"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "usage_daily_rollup_organization_id_app_id_agent_id_actor_user_id_agent_owner_user_id_date_agent_publication_state_at_run_run_purpose_provider_model_pk": { + "columns": [ + "organization_id", + "app_id", + "agent_id", + "actor_user_id", + "agent_owner_user_id", + "date", + "agent_publication_state_at_run", + "run_purpose", + "provider", + "model" + ], + "name": "usage_daily_rollup_organization_id_app_id_agent_id_actor_user_id_agent_owner_user_id_date_agent_publication_state_at_run_run_purpose_provider_model_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "usage_event_rollup_receipt": { + "name": "usage_event_rollup_receipt", + "columns": { + "rolled_up_at": { + "name": "rolled_up_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_event_id": { + "name": "source_event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "usage_event_rollup_receipt_rolled_up_at_idx": { + "name": "usage_event_rollup_receipt_rolled_up_at_idx", + "columns": ["rolled_up_at"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "usage_event_rollup_receipt_source_source_event_id_pk": { + "columns": ["source", "source_event_id"], + "name": "usage_event_rollup_receipt_source_source_event_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "usage_event": { + "name": "usage_event", + "columns": { + "actor_user_id": { + "name": "actor_user_id", + "type": "text CHECK (\"actor_user_id\" = upper(\"actor_user_id\") AND length(\"actor_user_id\") = 26 AND substr(\"actor_user_id\", 1, 1) GLOB '[0-7]' AND \"actor_user_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_id": { + "name": "agent_id", + "type": "text CHECK (\"agent_id\" = upper(\"agent_id\") AND length(\"agent_id\") = 26 AND substr(\"agent_id\", 1, 1) GLOB '[0-7]' AND \"agent_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_owner_user_id": { + "name": "agent_owner_user_id", + "type": "text CHECK (\"agent_owner_user_id\" = upper(\"agent_owner_user_id\") AND length(\"agent_owner_user_id\") = 26 AND substr(\"agent_owner_user_id\", 1, 1) GLOB '[0-7]' AND \"agent_owner_user_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_publication_state_at_run": { + "name": "agent_publication_state_at_run", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_revision_id": { + "name": "agent_revision_id", + "type": "text CHECK (\"agent_revision_id\" = upper(\"agent_revision_id\") AND length(\"agent_revision_id\") = 26 AND substr(\"agent_revision_id\", 1, 1) GLOB '[0-7]' AND \"agent_revision_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cache_creation_tokens": { + "name": "cache_creation_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text CHECK (\"organization_id\" = upper(\"organization_id\") AND length(\"organization_id\") = 26 AND substr(\"organization_id\", 1, 1) GLOB '[0-7]' AND \"organization_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_snapshot_json": { + "name": "price_snapshot_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pricing_status": { + "name": "pricing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "run_purpose": { + "name": "run_purpose", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "runtime_id": { + "name": "runtime_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text CHECK (\"session_id\" = upper(\"session_id\") AND length(\"session_id\") = 26 AND substr(\"session_id\", 1, 1) GLOB '[0-7]' AND \"session_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_run_id": { + "name": "session_run_id", + "type": "text CHECK (\"session_run_id\" = upper(\"session_run_id\") AND length(\"session_run_id\") = 26 AND substr(\"session_run_id\", 1, 1) GLOB '[0-7]' AND \"session_run_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_event_id": { + "name": "source_event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "total_cost_usd_micros": { + "name": "total_cost_usd_micros", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "usage_contract": { + "name": "usage_contract", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "usage_event_app_created_idx": { + "name": "usage_event_app_created_idx", + "columns": ["app_id", "created_at"], + "isUnique": false + }, + "usage_event_organization_created_idx": { + "name": "usage_event_organization_created_idx", + "columns": ["organization_id", "created_at"], + "isUnique": false + }, + "usage_event_agent_created_idx": { + "name": "usage_event_agent_created_idx", + "columns": ["agent_id", "created_at"], + "isUnique": false + }, + "usage_event_actor_created_idx": { + "name": "usage_event_actor_created_idx", + "columns": ["actor_user_id", "created_at"], + "isUnique": false + }, + "usage_event_owner_created_idx": { + "name": "usage_event_owner_created_idx", + "columns": ["agent_owner_user_id", "created_at"], + "isUnique": false + }, + "usage_event_session_run_idx": { + "name": "usage_event_session_run_idx", + "columns": ["session_run_id"], + "isUnique": false + }, + "usage_event_source_event_idx": { + "name": "usage_event_source_event_idx", + "columns": ["source", "source_event_id"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "vendor_credential": { + "name": "vendor_credential", + "columns": { + "api_base": { + "name": "api_base", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "api_key_secret_id": { + "name": "api_key_secret_id", + "type": "text CHECK (\"api_key_secret_id\" = upper(\"api_key_secret_id\") AND length(\"api_key_secret_id\") = 26 AND substr(\"api_key_secret_id\", 1, 1) GLOB '[0-7]' AND \"api_key_secret_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text CHECK (\"id\" = upper(\"id\") AND length(\"id\") = 26 AND substr(\"id\", 1, 1) GLOB '[0-7]' AND \"id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "is_default": { + "name": "is_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "models": { + "name": "models", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text CHECK (\"app_id\" = upper(\"app_id\") AND length(\"app_id\") = 26 AND substr(\"app_id\", 1, 1) GLOB '[0-7]' AND \"app_id\" NOT GLOB '*[^0-9A-HJKMNP-TV-Z]*')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vendor_id": { + "name": "vendor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "vendor_credential_app_vendor_idx": { + "name": "vendor_credential_app_vendor_idx", + "columns": ["app_id", "vendor_id"], + "isUnique": false + }, + "vendor_credential_app_vendor_name_idx": { + "name": "vendor_credential_app_vendor_name_idx", + "columns": ["app_id", "vendor_id", "name"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "file_record_listing_idx": { + "columns": { + "lower(\"name\")": { + "isExpression": true + } + } + } + } + } +} diff --git a/pkgs/db/drizzle/meta/_journal.json b/pkgs/db/drizzle/meta/_journal.json index 08e8cec7..c7750297 100644 --- a/pkgs/db/drizzle/meta/_journal.json +++ b/pkgs/db/drizzle/meta/_journal.json @@ -85,6 +85,13 @@ "when": 1787207755791, "tag": "0011_cattle-terminal-checkpoints", "breakpoints": true + }, + { + "idx": 12, + "version": "6", + "when": 1787329170583, + "tag": "0012_session-run-artifacts", + "breakpoints": true } ] } diff --git a/pkgs/db/src/schema/file.schema.ts b/pkgs/db/src/schema/file.schema.ts index 2768b1eb..dbd6a29a 100644 --- a/pkgs/db/src/schema/file.schema.ts +++ b/pkgs/db/src/schema/file.schema.ts @@ -6,11 +6,20 @@ import type { FileUploadStatus, FileUploadStrategy, } from "@mosoo/contracts/file"; -import type { AccountId, FileVersionId, FileId, PlatformId, UploadId } from "@mosoo/id"; +import type { + AccountId, + FileVersionId, + FileId, + PlatformId, + RuntimeEventId, + SessionRunId, + UploadId, +} from "@mosoo/id"; import { sql } from "drizzle-orm"; import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core"; import { platformIdColumn } from "./id-column"; +import { sessionRunsTable } from "./session/runs.schema"; export type FileVersionReason = "delete" | "directory_delete" | "move_overwrite" | "overwrite"; @@ -80,6 +89,29 @@ export const fileRecordsTable = sqliteTable( ], ); +export const sessionRunArtifactsTable = sqliteTable( + "session_run_artifact", + { + committedEventId: platformIdColumn("committed_event_id").notNull(), + createdAt: integer("created_at").notNull(), + fileId: platformIdColumn("file_id").primaryKey(), + mimeType: text("mime_type"), + name: text("name").notNull(), + sessionRunId: platformIdColumn("session_run_id") + .notNull() + .references(() => sessionRunsTable.id, { onDelete: "cascade" }), + size: integer("size").notNull(), + }, + (table) => [ + uniqueIndex("session_run_artifact_committed_event_idx").on(table.committedEventId), + index("session_run_artifact_run_created_idx").on( + table.sessionRunId, + table.createdAt, + table.fileId, + ), + ], +); + export const fileUploadsTable = sqliteTable( "file_upload", { @@ -142,5 +174,6 @@ export const fileVersionsTable = sqliteTable( ); export type FileRecordRow = typeof fileRecordsTable.$inferSelect; +export type SessionRunArtifactRow = typeof sessionRunArtifactsTable.$inferSelect; export type FileUploadRow = typeof fileUploadsTable.$inferSelect; export type FileVersionRow = typeof fileVersionsTable.$inferSelect; diff --git a/pkgs/public-api-client/CHANGELOG.md b/pkgs/public-api-client/CHANGELOG.md new file mode 100644 index 00000000..cfbb3c21 --- /dev/null +++ b/pkgs/public-api-client/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +Every `0.x` breaking change will be called out here. + +## 0.1.0-beta.0 + +- First Public Beta of the self-contained `@mosoo/sdk` package. +- Added recoverable Thread/Run task creation, typed terminal results and canonical final output. +- Added file upload, typed Run artifact references, live event access and delegation-token verification for trusted backends. +- Added Node.js 22/24 LTS and Cloudflare Workers package/runtime checks. diff --git a/pkgs/public-api-client/LICENSE b/pkgs/public-api-client/LICENSE new file mode 100644 index 00000000..445bf93c --- /dev/null +++ b/pkgs/public-api-client/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative + Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 LangGenius, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/pkgs/public-api-client/README.md b/pkgs/public-api-client/README.md new file mode 100644 index 00000000..174ead25 --- /dev/null +++ b/pkgs/public-api-client/README.md @@ -0,0 +1,51 @@ +# `@mosoo/sdk` + +Public Beta TypeScript SDK for calling a published Mosoo Agent from a trusted backend. + +## Install + +```sh +npm install @mosoo/sdk@beta +``` + +## Quickstart + +```ts +import { Mosoo } from "@mosoo/sdk"; + +const mosoo = new Mosoo({ token: process.env.MOSOO_API_TOKEN! }); +const requestId = crypto.randomUUID(); // Persist this value if the request may be retried. + +const created = await mosoo.createThread({ + agentId: process.env.MOSOO_AGENT_ID!, + idempotencyKey: requestId, + input: "Prepare the requested deliverable.", + userId: "your-application-user-id", +}); + +if (created.run === null) { + throw new Error("The Thread did not start a Run."); +} + +// Persist these before waiting so another process can resume the task. +const { id: threadId } = created.thread; +const { id: runId } = created.run; + +const result = await mosoo.waitForFinalOutput({ threadId, runId }); +console.log(result.finalOutput.text); +console.log(result.run.artifacts ?? []); +``` + +`MOSOO_API_TOKEN` is an App-owner secret. Do not expose it to browser or mobile clients. Pass `baseUrl` only for a self-hosted Mosoo deployment. + +Supported runtimes for this ESM-only Beta are Node.js 22/24 LTS and Cloudflare Workers. Bun, Deno, browsers, and mobile clients are not yet supported. + +Live events are a Beta progress surface. Use the terminal Run snapshot as the source of truth for completion and final output. + +Agent output files are exposed as typed `run.artifacts` and on matching `session_files.updated` events. Each artifact carries stable `fileId` and `runId` values; use `listFiles()` for the complete Thread file list, not filename matching for identity. + +The Beta keeps file upload at the existing `Blob`/`FormData` boundary and does not promise a high-level large-file streaming API. + +## License + +Apache-2.0 diff --git a/pkgs/public-api-client/package.json b/pkgs/public-api-client/package.json index 9d336db8..ddb878d7 100644 --- a/pkgs/public-api-client/package.json +++ b/pkgs/public-api-client/package.json @@ -1,21 +1,55 @@ { - "name": "@mosoo/public-api-client", - "private": true, + "name": "@mosoo/sdk", + "version": "0.1.0-beta.0", + "private": false, + "description": "TypeScript SDK for calling published Mosoo Agents from trusted backends.", + "homepage": "https://mosoo.ai/docs", + "bugs": { + "url": "https://github.com/langgenius/mosoo/issues" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/langgenius/mosoo.git", + "directory": "pkgs/public-api-client" + }, + "files": [ + "CHANGELOG.md", + "dist", + "LICENSE", + "README.md" + ], "type": "module", + "sideEffects": false, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { - ".": "./src/index.ts" + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/", + "tag": "beta" }, "scripts": { + "build": "vp pack", "lint": "vp lint .", + "prepack": "vp run build", "tc": "vp exec tsc --noEmit", "test": "vp exec bun test tests" }, - "dependencies": { - "@mosoo/contracts": "workspace:*" - }, "devDependencies": { "@types/bun": "^1.3.14", "typescript": "^6.0.3", - "vite-plus": "^0.1.23" + "vite-plus": "^0.1.23", + "wrangler": "^4.120.1" + }, + "engines": { + "node": "^22.0.0 || ^24.0.0" } } diff --git a/pkgs/public-api-client/src/delegation.ts b/pkgs/public-api-client/src/delegation.ts new file mode 100644 index 00000000..0f394b11 --- /dev/null +++ b/pkgs/public-api-client/src/delegation.ts @@ -0,0 +1,240 @@ +export const MOSOO_DELEGATION_HEADER = "X-Mosoo-Delegation"; + +const DELEGATION_ISSUER = "mosoo"; +const DELEGATION_KEY_PREFIX = "mosoo-mcp-delegation-v1\0"; +const MAX_CLOCK_SKEW_SECONDS = 5; +const MAX_TOKEN_LIFETIME_SECONDS = 60; +const encoder = new TextEncoder(); +const decoder = new TextDecoder("utf-8", { fatal: true }); + +export type MosooDelegationVerificationErrorCode = + | "invalid_claims" + | "invalid_configuration" + | "invalid_format" + | "invalid_header" + | "invalid_signature" + | "missing_token"; + +export class MosooDelegationVerificationError extends Error { + readonly code: MosooDelegationVerificationErrorCode; + + constructor(code: MosooDelegationVerificationErrorCode, message: string) { + super(message); + this.name = "MosooDelegationVerificationError"; + this.code = code; + } +} + +export interface MosooDelegationContext { + agentId: string; + appId: string; + audience: string; + expiresAt: Date; + issuedAt: Date; + runId: string | null; + threadId: string; + tokenId: string; + userId: string; +} + +export interface VerifyMosooDelegationInput { + accessToken: string; + audience: string; + nowMs?: number; + token: string | null | undefined; +} + +interface DelegationClaims { + act: { agent_id: string; app_id: string }; + aud: string; + exp: number; + iat: number; + iss: string; + jti: string; + run_id: string | null; + sub: string; + thread_id: string; +} + +function fail(code: MosooDelegationVerificationErrorCode, message: string): never { + throw new MosooDelegationVerificationError(code, message); +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function isNonEmptyString(value: unknown): value is string { + return typeof value === "string" && value.length > 0; +} + +function toArrayBuffer(bytes: Uint8Array): ArrayBuffer { + const { buffer, byteLength, byteOffset } = bytes; + + if (buffer instanceof ArrayBuffer) { + return byteOffset === 0 && byteLength === buffer.byteLength + ? buffer + : buffer.slice(byteOffset, byteOffset + byteLength); + } + + const copy = new Uint8Array(byteLength); + copy.set(bytes); + return copy.buffer; +} + +function decodeBase64Url(value: string): Uint8Array { + if (!/^[A-Za-z0-9_-]+$/u.test(value) || value.length % 4 === 1) { + fail("invalid_format", "Mosoo delegation token contains invalid base64url data."); + } + + const normalized = value.replaceAll("-", "+").replaceAll("_", "/"); + const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), "="); + + let binary: string; + + try { + binary = atob(padded); + } catch { + fail("invalid_format", "Mosoo delegation token contains invalid base64url data."); + } + + const bytes = new Uint8Array(binary.length); + + for (let index = 0; index < binary.length; index += 1) { + bytes[index] = binary.codePointAt(index) ?? fail("invalid_format", "Invalid token data."); + } + + return bytes; +} + +function parseJsonSegment(value: string, code: MosooDelegationVerificationErrorCode): unknown { + try { + return JSON.parse(decoder.decode(decodeBase64Url(value))) as unknown; + } catch (error) { + if (error instanceof MosooDelegationVerificationError) { + throw error; + } + + fail(code, "Mosoo delegation token contains invalid JSON."); + } +} + +async function verificationKey(accessToken: string): Promise { + if (accessToken.trim().length === 0) { + fail("invalid_configuration", "MCP access token is required for delegation verification."); + } + + const material = encoder.encode(`${DELEGATION_KEY_PREFIX}${accessToken}`); + const digest = await crypto.subtle.digest("SHA-256", toArrayBuffer(material)); + + return crypto.subtle.importKey("raw", digest, { hash: "SHA-256", name: "HMAC" }, false, [ + "verify", + ]); +} + +function readClaims(value: unknown, audience: string, now: number): DelegationClaims { + if (!isRecord(value) || !isRecord(value["act"])) { + fail("invalid_claims", "Mosoo delegation token claims are invalid."); + } + + const act = value["act"]; + const claims = { + act: { agent_id: act["agent_id"], app_id: act["app_id"] }, + aud: value["aud"], + exp: value["exp"], + iat: value["iat"], + iss: value["iss"], + jti: value["jti"], + run_id: value["run_id"], + sub: value["sub"], + thread_id: value["thread_id"], + }; + + if ( + claims.iss !== DELEGATION_ISSUER || + claims.aud !== audience || + !isNonEmptyString(claims.sub) || + !Number.isInteger(claims.iat) || + !Number.isInteger(claims.exp) || + (claims.iat as number) > now + MAX_CLOCK_SKEW_SECONDS || + (claims.exp as number) <= now || + (claims.exp as number) <= (claims.iat as number) || + (claims.exp as number) - (claims.iat as number) > MAX_TOKEN_LIFETIME_SECONDS || + !isNonEmptyString(claims.thread_id) || + (claims.run_id !== null && !isNonEmptyString(claims.run_id)) || + !isNonEmptyString(claims.act.agent_id) || + !isNonEmptyString(claims.act.app_id) || + !isNonEmptyString(claims.jti) + ) { + fail("invalid_claims", "Mosoo delegation token claims are invalid."); + } + + return claims as DelegationClaims; +} + +export async function verifyDelegation( + input: VerifyMosooDelegationInput, +): Promise { + if (input.token === null || input.token === undefined || input.token.length === 0) { + fail("missing_token", `Missing ${MOSOO_DELEGATION_HEADER} header.`); + } + + if (input.audience.trim().length === 0) { + fail("invalid_configuration", "Delegation audience is required."); + } + + if (input.nowMs !== undefined && !Number.isFinite(input.nowMs)) { + fail("invalid_configuration", "Delegation verification time must be finite."); + } + + const [headerSegment, payloadSegment, signatureSegment, extraSegment] = input.token.split("."); + + if ( + headerSegment === undefined || + payloadSegment === undefined || + signatureSegment === undefined || + headerSegment.length === 0 || + payloadSegment.length === 0 || + signatureSegment.length === 0 || + extraSegment !== undefined + ) { + fail("invalid_format", "Mosoo delegation token format is invalid."); + } + + const header = parseJsonSegment(headerSegment, "invalid_header"); + + if (!isRecord(header) || header["alg"] !== "HS256" || header["typ"] !== "JWT") { + fail("invalid_header", "Mosoo delegation token header is invalid."); + } + + const signature = decodeBase64Url(signatureSegment); + const valid = await crypto.subtle.verify( + "HMAC", + await verificationKey(input.accessToken), + toArrayBuffer(signature), + toArrayBuffer(encoder.encode(`${headerSegment}.${payloadSegment}`)), + ); + + if (!valid) { + fail("invalid_signature", "Mosoo delegation token signature is invalid."); + } + + const now = Math.floor((input.nowMs ?? Date.now()) / 1_000); + const claims = readClaims( + parseJsonSegment(payloadSegment, "invalid_claims"), + input.audience, + now, + ); + + return { + agentId: claims.act.agent_id, + appId: claims.act.app_id, + audience: claims.aud, + expiresAt: new Date(claims.exp * 1_000), + issuedAt: new Date(claims.iat * 1_000), + runId: claims.run_id, + threadId: claims.thread_id, + tokenId: claims.jti, + userId: claims.sub, + }; +} diff --git a/pkgs/public-api-client/src/index.ts b/pkgs/public-api-client/src/index.ts index 9307a2df..d4906525 100644 --- a/pkgs/public-api-client/src/index.ts +++ b/pkgs/public-api-client/src/index.ts @@ -1,7 +1,4 @@ -import { - PUBLIC_THREAD_EVENTS_MAX_LIMIT, - PUBLIC_THREAD_RUN_TERMINAL_STATUSES, -} from "@mosoo/contracts/public-api"; +import { PUBLIC_THREAD_RUN_TERMINAL_STATUSES } from "./types.ts"; import type { PublicApiErrorCode, PublicFileResponse, @@ -12,17 +9,23 @@ import type { PublicThreadApiSendEventsResponse, PublicThreadEventLogEntry, PublicThreadFinalOutput, + PublicThreadFileListResponse, PublicThreadRunStatus, PublicThreadRunSummary, PublicThreadRunTerminalStatus, PublicThreadSummary, -} from "@mosoo/contracts/public-api"; +} from "./types.ts"; + +export type * from "./types.ts"; +export * from "./delegation.ts"; export type MosooPublicApiFetch = ( input: RequestInfo | URL, init?: RequestInit, ) => Promise; +export const MOSOO_CLOUD_BASE_URL = "https://cloud.mosoo.ai"; + interface CreateThreadRequestBody { input?: { content: { text: string; type: "text" }[]; @@ -40,7 +43,7 @@ interface SseMessage { export interface MosooPublicThreadClientOptions { allowBrowserToken?: boolean; - baseUrl: string; + baseUrl?: string; fetch?: MosooPublicApiFetch; pollIntervalMs?: number; token: string; @@ -75,10 +78,14 @@ export interface MosooListEventsInput { threadId: string; } +export interface MosooListFilesInput { + signal?: AbortSignal | undefined; + threadId: string; +} + export interface MosooStreamEventsInput extends MosooListEventsInput {} export interface MosooWaitForRunInput { - eventLimit?: number; pollIntervalMs?: number; runId?: string; signal?: AbortSignal | undefined; @@ -87,18 +94,15 @@ export interface MosooWaitForRunInput { } export interface MosooCreateThreadAndWaitInput extends MosooCreateThreadInput { - eventLimit?: number; pollIntervalMs?: number; timeoutMs?: number; throwOnFailedRun?: boolean; } export interface MosooPublicThreadWaitResult { - events: PublicThreadEventLogEntry[]; finalOutput: PublicThreadFinalOutput | null; run: PublicThreadRunSummary; thread: PublicThreadSummary; - truncated: boolean; } export interface MosooCreateThreadAndWaitFinalOutputInput extends MosooCreateThreadAndWaitInput { @@ -124,19 +128,15 @@ export interface MosooPublicThreadUnsuccessfulRunSummary extends PublicThreadRun } export interface MosooPublicThreadFinalOutputResult { - events: PublicThreadEventLogEntry[]; finalOutput: PublicThreadFinalOutput; run: MosooPublicThreadCompletedRunSummary; thread: PublicThreadSummary; - truncated: boolean; } export interface MosooPublicThreadTerminalRunErrorInput { - events: PublicThreadEventLogEntry[]; finalOutput: PublicThreadFinalOutput | null; run: MosooPublicThreadUnsuccessfulRunSummary; thread: PublicThreadSummary; - truncated: boolean; } export interface ExtractFinalOutputOptions { @@ -174,24 +174,42 @@ export class MosooPublicApiTimeoutError extends Error { } } +export class MosooPublicApiAbortError extends Error { + readonly code = "aborted"; + + constructor() { + super("Operation aborted."); + this.name = "MosooPublicApiAbortError"; + } +} + +export class MosooPublicThreadRunMismatchError extends Error { + readonly actualRunId: string; + readonly code = "run_mismatch"; + readonly expectedRunId: string; + + constructor(expectedRunId: string, actualRunId: string) { + super(`Thread current Run is ${actualRunId}, not requested Run ${expectedRunId}.`); + this.name = "MosooPublicThreadRunMismatchError"; + this.actualRunId = actualRunId; + this.expectedRunId = expectedRunId; + } +} + export class MosooPublicThreadTerminalRunError extends Error { readonly code = "run_terminal_failure"; - readonly events: PublicThreadEventLogEntry[]; readonly finalOutput: PublicThreadFinalOutput | null; readonly run: MosooPublicThreadUnsuccessfulRunSummary; readonly runStatus: MosooPublicThreadUnsuccessfulTerminalStatus; readonly thread: PublicThreadSummary; - readonly truncated: boolean; constructor(input: MosooPublicThreadTerminalRunErrorInput) { super(`Public Thread run ${input.run.id} finished with status ${input.run.status}.`); this.name = "MosooPublicThreadTerminalRunError"; - this.events = input.events; this.finalOutput = input.finalOutput; this.run = input.run; this.runStatus = input.run.status; this.thread = input.thread; - this.truncated = input.truncated; } } @@ -204,7 +222,24 @@ function isRecord(value: unknown): value is Record { } function normalizePublicApiBaseUrl(baseUrl: string): string { - const url = new URL(baseUrl); + let url: URL; + + try { + url = new URL(baseUrl); + } catch { + throw new TypeError("Mosoo baseUrl must be a valid absolute URL."); + } + + if (url.username || url.password || url.search || url.hash) { + throw new TypeError("Mosoo baseUrl must not include credentials, query, or fragment."); + } + + const isLoopback = ["127.0.0.1", "::1", "localhost"].includes(url.hostname); + + if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) { + throw new TypeError("Mosoo baseUrl must use HTTPS, except for HTTP loopback development."); + } + const pathname = url.pathname.replace(/\/+$/, ""); if (pathname.endsWith("/api/v1")) { @@ -215,12 +250,23 @@ function normalizePublicApiBaseUrl(baseUrl: string): string { url.pathname = `${pathname}/api/v1`; } - url.hash = ""; - url.search = ""; - return url.toString().replace(/\/$/, ""); } +function encodePathSegment(value: string, name: string): string { + if (value.trim().length === 0) { + throw new TypeError(`${name} must not be empty.`); + } + + return encodeURIComponent(value); +} + +function assertPositiveFinite(value: number, name: string): void { + if (!Number.isFinite(value) || value <= 0) { + throw new RangeError(`${name} must be a positive finite number.`); + } +} + function isBrowserLikeRuntime(): boolean { return typeof window === "object" && typeof document === "object"; } @@ -386,20 +432,20 @@ function parseThreadErrorMessage(message: SseMessage): MosooPublicApiError | nul function delay(ms: number, signal: AbortSignal | undefined): Promise { if (signal?.aborted === true) { - return Promise.reject(new Error("Operation aborted.")); + return Promise.reject(new MosooPublicApiAbortError()); } return new Promise((resolve, reject) => { - const timeout = setTimeout(resolve, ms); + const onAbort = () => { + clearTimeout(timeout); + reject(new MosooPublicApiAbortError()); + }; + const timeout = setTimeout(() => { + signal?.removeEventListener("abort", onAbort); + resolve(); + }, ms); - signal?.addEventListener( - "abort", - () => { - clearTimeout(timeout); - reject(new Error("Operation aborted.")); - }, - { once: true }, - ); + signal?.addEventListener("abort", onAbort, { once: true }); }); } @@ -425,11 +471,9 @@ function assertFinalOutputResult( }; throw new MosooPublicThreadTerminalRunError({ - events: result.events, finalOutput: result.finalOutput, run, thread: result.thread, - truncated: result.truncated, }); } @@ -450,11 +494,9 @@ function assertFinalOutputResult( }; return { - events: result.events, finalOutput: result.finalOutput, run, thread: result.thread, - truncated: result.truncated, }; } @@ -499,19 +541,38 @@ export class MosooPublicThreadClient { throw new Error("MosooPublicThreadClient requires a fetch implementation."); } - this.apiBaseUrl = normalizePublicApiBaseUrl(options.baseUrl); - this.fetchImpl = fetchImpl.bind(globalThis); + if (options.token.trim().length === 0) { + throw new TypeError("Mosoo token must not be empty."); + } + + if (options.pollIntervalMs !== undefined) { + assertPositiveFinite(options.pollIntervalMs, "pollIntervalMs"); + } + + this.apiBaseUrl = normalizePublicApiBaseUrl(options.baseUrl ?? MOSOO_CLOUD_BASE_URL); + + if (options.fetch === undefined) { + this.fetchImpl = fetchImpl.bind(globalThis); + } else { + const suppliedFetch = options.fetch; + this.fetchImpl = (input, init) => suppliedFetch(input, init); + } + this.pollIntervalMs = options.pollIntervalMs ?? 1_000; this.token = options.token; } async createThread(input: MosooCreateThreadInput): Promise { - return this.requestJson("POST", `/agents/${input.agentId}/threads`, { - body: createCreateThreadBody(input), - idempotencyKey: input.idempotencyKey, - signal: input.signal, - status: 201, - }); + return this.requestJson( + "POST", + `/agents/${encodePathSegment(input.agentId, "agentId")}/threads`, + { + body: createCreateThreadBody(input), + idempotencyKey: input.idempotencyKey, + signal: input.signal, + status: 201, + }, + ); } async uploadAgentFile(input: MosooUploadAgentFileInput): Promise { @@ -523,34 +584,53 @@ export class MosooPublicThreadClient { formData.append("file", input.file, input.filename); } - return this.requestJson("POST", `/agents/${input.agentId}/files`, { - body: formData, - signal: input.signal, - status: 201, - }); + return this.requestJson( + "POST", + `/agents/${encodePathSegment(input.agentId, "agentId")}/files`, + { + body: formData, + signal: input.signal, + status: 201, + }, + ); } async retrieveThread( threadId: string, options: { signal?: AbortSignal | undefined } = {}, ): Promise { - return this.requestJson("GET", `/threads/${threadId}`, { + return this.requestJson("GET", `/threads/${encodePathSegment(threadId, "threadId")}`, { signal: options.signal, status: 200, }); } + async listFiles(input: MosooListFilesInput): Promise { + return this.requestJson( + "GET", + `/threads/${encodePathSegment(input.threadId, "threadId")}/files`, + { + signal: input.signal, + status: 200, + }, + ); + } + async sendEvents(input: MosooSendEventsInput): Promise { - return this.requestJson("POST", `/threads/${input.threadId}/events`, { - body: { events: input.events }, - idempotencyKey: input.idempotencyKey, - signal: input.signal, - status: 200, - }); + return this.requestJson( + "POST", + `/threads/${encodePathSegment(input.threadId, "threadId")}/events`, + { + body: { events: input.events }, + idempotencyKey: input.idempotencyKey, + signal: input.signal, + status: 200, + }, + ); } async listEvents(input: MosooListEventsInput): Promise { - const url = this.url(`/threads/${input.threadId}/events`); + const url = this.url(`/threads/${encodePathSegment(input.threadId, "threadId")}/events`); appendQuery(url, "limit", input.limit); return this.requestJsonUrl("GET", url, { @@ -560,7 +640,7 @@ export class MosooPublicThreadClient { } async *streamEvents(input: MosooStreamEventsInput): AsyncGenerator { - const url = this.url(`/threads/${input.threadId}/events/stream`); + const url = this.url(`/threads/${encodePathSegment(input.threadId, "threadId")}/events/stream`); appendQuery(url, "limit", input.limit); const response = await this.requestResponseUrl("GET", url, { @@ -576,90 +656,122 @@ export class MosooPublicThreadClient { const decoder = new TextDecoder(); let buffer = ""; - for (;;) { - const chunk = await reader.read(); - - if (chunk.done) { - buffer += decoder.decode(); - } else { - buffer += decoder.decode(chunk.value, { stream: true }); - } - + try { for (;;) { - const separator = /\r?\n\r?\n/.exec(buffer); + const chunk = await reader.read(); - if (separator === null) { - break; + if (chunk.done) { + buffer += decoder.decode(); + } else { + buffer += decoder.decode(chunk.value, { stream: true }); } - const block = buffer.slice(0, separator.index); - buffer = buffer.slice(separator.index + separator[0].length); - const message = parseSseMessage(block); + for (;;) { + const separator = /\r?\n\r?\n/.exec(buffer); - if (message === null) { - continue; - } + if (separator === null) { + break; + } - const error = parseThreadErrorMessage(message); + const block = buffer.slice(0, separator.index); + buffer = buffer.slice(separator.index + separator[0].length); + const message = parseSseMessage(block); - if (error !== null) { - throw error; - } + if (message === null) { + continue; + } + + const error = parseThreadErrorMessage(message); + + if (error !== null) { + throw error; + } - const event = parseThreadEventMessage(message); + const event = parseThreadEventMessage(message); - if (event !== null) { - yield event; + if (event !== null) { + yield event; + } } - } - if (chunk.done) { - break; + if (chunk.done) { + break; + } } + } finally { + await reader.cancel().catch(() => undefined); + reader.releaseLock(); } } async waitForRun(input: MosooWaitForRunInput): Promise { const timeoutMs = input.timeoutMs ?? 60_000; + const pollIntervalMs = input.pollIntervalMs ?? this.pollIntervalMs; + assertPositiveFinite(timeoutMs, "timeoutMs"); + assertPositiveFinite(pollIntervalMs, "pollIntervalMs"); const startedAt = Date.now(); + const controller = new AbortController(); + let timedOut = false; + let rejectStopped: (reason: Error) => void = () => {}; + const stopped = new Promise((_resolve, reject) => { + rejectStopped = reject; + }); + const onAbort = () => { + controller.abort(); + rejectStopped(new MosooPublicApiAbortError()); + }; + const timeout = setTimeout(() => { + timedOut = true; + controller.abort(); + rejectStopped(new MosooPublicApiTimeoutError(timeoutMs)); + }, timeoutMs); + input.signal?.addEventListener("abort", onAbort, { once: true }); + if (input.signal?.aborted === true) { + onAbort(); + } - for (;;) { - const retrieved = await this.retrieveThread(input.threadId, { signal: input.signal }); - const run = retrieved.run; + try { + for (;;) { + const retrieved = await Promise.race([ + this.retrieveThread(input.threadId, { signal: controller.signal }), + stopped, + ]); + const run = retrieved.run; + + if (run === null) { + throw new Error("Thread does not have a current Run."); + } - if (run === null) { - throw new Error("Thread does not have a current Run."); - } + if (input.runId !== undefined && run.id !== input.runId) { + throw new MosooPublicThreadRunMismatchError(input.runId, run.id); + } - if (input.runId !== undefined && run.id !== input.runId) { - throw new Error(`Thread current Run is ${run.id}, not requested Run ${input.runId}.`); - } + if (isPublicThreadRunTerminalStatus(run.status)) { + return { + finalOutput: run.finalOutput, + run, + thread: retrieved.thread, + }; + } - if (isPublicThreadRunTerminalStatus(run.status)) { - const eventPage = await this.listEvents({ - limit: input.eventLimit ?? PUBLIC_THREAD_EVENTS_MAX_LIMIT, - signal: input.signal, - threadId: input.threadId, - }); - return { - events: eventPage.events, - finalOutput: run.finalOutput, - run, - thread: retrieved.thread, - truncated: eventPage.truncated, - }; - } + const elapsedMs = Date.now() - startedAt; - const elapsedMs = Date.now() - startedAt; + if (elapsedMs >= timeoutMs) { + throw new MosooPublicApiTimeoutError(timeoutMs); + } - if (elapsedMs >= timeoutMs) { + await delay(Math.min(pollIntervalMs, timeoutMs - elapsedMs), controller.signal); + } + } catch (error) { + if (timedOut) { throw new MosooPublicApiTimeoutError(timeoutMs); } - await delay( - Math.min(input.pollIntervalMs ?? this.pollIntervalMs, timeoutMs - elapsedMs), - input.signal, - ); + throw error; + } finally { + clearTimeout(timeout); + input.signal?.removeEventListener("abort", onAbort); + controller.abort(); } } @@ -707,10 +819,6 @@ export class MosooPublicThreadClient { threadId: created.thread.id, }; - if (input.eventLimit !== undefined) { - waitInput.eventLimit = input.eventLimit; - } - if (input.pollIntervalMs !== undefined) { waitInput.pollIntervalMs = input.pollIntervalMs; } @@ -808,7 +916,17 @@ export class MosooPublicThreadClient { init.signal = options.signal; } - const response = await this.fetchImpl(url, init); + let response: Response; + + try { + response = await this.fetchImpl(url, init); + } catch (error) { + if (options.signal?.aborted === true) { + throw new MosooPublicApiAbortError(); + } + + throw error; + } if (!response.ok) { await this.throwPublicApiError(response); @@ -824,8 +942,10 @@ export class MosooPublicThreadClient { throw new MosooPublicApiError({ body, code: payload.code, - message: payload.message ?? `mosoo Public API request failed with HTTP ${response.status}.`, + message: payload.message ?? `Mosoo Public API request failed with HTTP ${response.status}.`, status: response.status, }); } } + +export { MosooPublicThreadClient as Mosoo }; diff --git a/pkgs/public-api-client/src/types.ts b/pkgs/public-api-client/src/types.ts new file mode 100644 index 00000000..a19bf091 --- /dev/null +++ b/pkgs/public-api-client/src/types.ts @@ -0,0 +1,215 @@ +export const PUBLIC_THREAD_EVENTS_MAX_LIMIT = 1_000; + +export const PUBLIC_THREAD_RUN_TERMINAL_STATUSES = [ + "completed", + "failed", + "cancelled", + "expired", +] as const; + +export type PublicApiErrorCode = + | "agent_not_published" + | "forbidden" + | "idempotency_conflict" + | "internal_error" + | "invalid_json" + | "invalid_request" + | "not_found" + | "rate_limited" + | "readiness_blocked" + | "service_inactive" + | "unauthenticated"; + +export type PublicThreadRunStatus = + | "booting" + | "cancelled" + | "completed" + | "expired" + | "failed" + | "queued" + | "running" + | "waiting_input"; + +export type PublicThreadRunTerminalStatus = (typeof PUBLIC_THREAD_RUN_TERMINAL_STATUSES)[number]; + +export type PublicThreadRunTrigger = "resume" | "retry" | "system" | "user_prompt"; + +export interface PublicThreadFinalOutputWarning { + code: "unresolved_provider_citation"; + count: number; +} + +export interface PublicThreadFinalOutput { + text: string; + warnings?: PublicThreadFinalOutputWarning[]; +} + +export interface PublicThreadArtifact { + createdAt: string; + fileId: string; + kind: "artifact"; + mimeType: string | null; + name: string; + runId: string; + size: number; +} + +export interface PublicThreadRunError { + code: string; + message: string; + retryable: boolean; +} + +export interface PublicThreadRunSummary { + artifacts?: PublicThreadArtifact[]; + completedAt: string | null; + createdAt: string; + error: PublicThreadRunError | null; + finalOutput: PublicThreadFinalOutput | null; + id: string; + startedAt: string | null; + status: PublicThreadRunStatus; + trigger: PublicThreadRunTrigger; + updatedAt: string; +} + +export type PublicThreadStatus = "IDLE" | "RESCHEDULING" | "RUNNING" | "TERMINATED"; + +export interface PublicThreadSummary { + agent_id: string; + created_at: string; + id: string; + kind: "cattle" | "pet"; + last_run_id: string | null; + source: "api"; + status: PublicThreadStatus; + title: string | null; + updated_at: string; + userId: string; +} + +export interface PublicThreadLinks { + thread: string; +} + +export interface PublicThreadApiCreateThreadResponse { + links: PublicThreadLinks; + run: PublicThreadRunSummary | null; + thread: PublicThreadSummary; +} + +export interface PublicThreadApiRetrieveThreadResponse { + links: PublicThreadLinks; + run: PublicThreadRunSummary | null; + thread: PublicThreadSummary; +} + +export interface PublicThreadFileResourceInput { + file_id: string; + type: "file"; +} + +export type PublicThreadPermissionDecision = "allow_once" | "reject_once"; + +export type PublicThreadEventInput = + | { + requestId?: string | null; + resources?: PublicThreadFileResourceInput[]; + text: string; + type: "user_message"; + } + | { + decision: PublicThreadPermissionDecision; + requestId: string; + type: "permission_decision"; + } + | { + runId?: string | null; + type: "user_interrupt"; + }; + +export interface PublicThreadApiSendEventsRequest { + events: PublicThreadEventInput[]; +} + +export type PublicThreadEventType = "permission_decision" | "user_interrupt" | "user_message"; + +export interface PublicThreadEventResult { + requestId: string | null; + run: PublicThreadRunSummary | null; + type: PublicThreadEventType; +} + +export interface PublicThreadUserWarning { + code: string; + message: string; +} + +export interface PublicThreadApiSendEventsResponse { + acceptedAt: string; + events: PublicThreadEventResult[]; + thread: PublicThreadSummary; + warnings: PublicThreadUserWarning[]; +} + +export type PublicThreadEventLogType = + | "agent.message.delta" + | "agent.thinking.delta" + | "file.changed" + | "run.completed" + | "run.failed" + | "run.started" + | "session.status" + | "session_files.updated" + | "tool.confirmation.required" + | "tool.use.completed" + | "tool.use.started" + | "usage.updated" + | "user.message"; + +export type PublicThreadEventLogStatus = "available" | "error" | "unsupported"; + +export interface PublicThreadEventLogEntry { + artifact?: PublicThreadArtifact; + content: string; + durationMs: number | null; + id: string; + occurredAt: string; + runId: string | null; + status: PublicThreadEventLogStatus; + tokens: number | null; + type: PublicThreadEventLogType; +} + +export interface PublicThreadApiListThreadEventsResponse { + events: PublicThreadEventLogEntry[]; + truncated: boolean; +} + +export interface PublicFile { + createdAt: string; + id: string; + mimeType: string | null; + name: string; + size: number; +} + +export interface PublicFileResponse { + file: PublicFile; +} + +export interface PublicThreadFile { + committed: boolean; + createdAt: string; + fileId?: string; + id: string; + kind: "artifact" | "attachment"; + mimeType: string | null; + name: string; + runId?: string | null; + size: number; +} + +export interface PublicThreadFileListResponse { + files: PublicThreadFile[]; +} diff --git a/pkgs/public-api-client/tests/delegation.test.ts b/pkgs/public-api-client/tests/delegation.test.ts new file mode 100644 index 00000000..369cda47 --- /dev/null +++ b/pkgs/public-api-client/tests/delegation.test.ts @@ -0,0 +1,153 @@ +import { describe, expect, test } from "bun:test"; + +import { MosooDelegationVerificationError, verifyDelegation } from "../src/delegation.ts"; + +const ACCESS_TOKEN = "mcp-upstream-secret"; +const AUDIENCE = "https://tools.example.com/mcp"; +const NOW_MS = 1_800_000_000_000; +const NOW_SECONDS = NOW_MS / 1_000; +const encoder = new TextEncoder(); + +const baseClaims = { + act: { + agent_id: "01J00000000000000000000009", + app_id: "01J0000000000000000000000Q", + }, + aud: AUDIENCE, + exp: NOW_SECONDS + 60, + iat: NOW_SECONDS, + iss: "mosoo", + jti: "00000000-0000-4000-8000-000000000001", + run_id: "01J0000000000000000000000N", + sub: "customer-123", + thread_id: "01J0000000000000000000000B", +}; + +function toBase64Url(value: Uint8Array | string): string { + const bytes = typeof value === "string" ? encoder.encode(value) : value; + let binary = ""; + + for (const byte of bytes) { + binary += String.fromCodePoint(byte); + } + + return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, ""); +} + +async function createToken( + claims: Record = baseClaims, + header: Record = { alg: "HS256", typ: "JWT" }, +): Promise { + const headerSegment = toBase64Url(JSON.stringify(header)); + const payloadSegment = toBase64Url(JSON.stringify(claims)); + const keyMaterial = await crypto.subtle.digest( + "SHA-256", + encoder.encode(`mosoo-mcp-delegation-v1\0${ACCESS_TOKEN}`), + ); + const key = await crypto.subtle.importKey( + "raw", + keyMaterial, + { hash: "SHA-256", name: "HMAC" }, + false, + ["sign"], + ); + const signature = await crypto.subtle.sign( + "HMAC", + key, + encoder.encode(`${headerSegment}.${payloadSegment}`), + ); + + return `${headerSegment}.${payloadSegment}.${toBase64Url(new Uint8Array(signature))}`; +} + +async function expectCode( + input: Parameters[0], + code: MosooDelegationVerificationError["code"], +): Promise { + try { + await verifyDelegation(input); + throw new Error("Expected delegation verification to fail."); + } catch (error) { + expect(error).toBeInstanceOf(MosooDelegationVerificationError); + expect(error).toMatchObject({ code }); + } +} + +describe("verifyDelegation", () => { + test("returns a typed application-user execution context", async () => { + const token = await createToken(); + + expect( + await verifyDelegation({ + accessToken: ACCESS_TOKEN, + audience: AUDIENCE, + nowMs: NOW_MS + 30_000, + token, + }), + ).toEqual({ + agentId: baseClaims.act.agent_id, + appId: baseClaims.act.app_id, + audience: AUDIENCE, + expiresAt: new Date((NOW_SECONDS + 60) * 1_000), + issuedAt: new Date(NOW_MS), + runId: baseClaims.run_id, + threadId: baseClaims.thread_id, + tokenId: baseClaims.jti, + userId: baseClaims.sub, + }); + }); + + test("rejects missing, malformed, and incorrectly signed tokens", async () => { + await expectCode( + { accessToken: ACCESS_TOKEN, audience: AUDIENCE, nowMs: NOW_MS, token: null }, + "missing_token", + ); + await expectCode( + { accessToken: ACCESS_TOKEN, audience: AUDIENCE, nowMs: NOW_MS, token: "not.jwt" }, + "invalid_format", + ); + + const token = await createToken(); + const forged = `${token.slice(0, -1)}${token.endsWith("A") ? "B" : "A"}`; + await expectCode( + { accessToken: ACCESS_TOKEN, audience: AUDIENCE, nowMs: NOW_MS, token: forged }, + "invalid_signature", + ); + }); + + test("rejects invalid verifier configuration", async () => { + const token = await createToken(); + await expectCode( + { accessToken: ACCESS_TOKEN, audience: " ", nowMs: NOW_MS, token }, + "invalid_configuration", + ); + await expectCode( + { accessToken: ACCESS_TOKEN, audience: AUDIENCE, nowMs: Number.NaN, token }, + "invalid_configuration", + ); + }); + + test("rejects disallowed algorithms before accepting a signature", async () => { + const token = await createToken(baseClaims, { alg: "none", typ: "JWT" }); + await expectCode( + { accessToken: ACCESS_TOKEN, audience: AUDIENCE, nowMs: NOW_MS, token }, + "invalid_header", + ); + }); + + test.each([ + ["wrong issuer", { ...baseClaims, iss: "attacker" }], + ["wrong audience", { ...baseClaims, aud: "https://attacker.example/mcp" }], + ["expired", { ...baseClaims, exp: NOW_SECONDS }], + ["future issued-at", { ...baseClaims, iat: NOW_SECONDS + 6 }], + ["excessive lifetime", { ...baseClaims, exp: NOW_SECONDS + 61 }], + ["empty app id", { ...baseClaims, act: { ...baseClaims.act, app_id: "" } }], + ["missing token id", { ...baseClaims, jti: undefined }], + ])("rejects invalid claims: %s", async (_label, claims) => { + const token = await createToken(claims); + await expectCode( + { accessToken: ACCESS_TOKEN, audience: AUDIENCE, nowMs: NOW_MS, token }, + "invalid_claims", + ); + }); +}); diff --git a/pkgs/public-api-client/tests/fixtures/cloudflare-worker/src/index.ts b/pkgs/public-api-client/tests/fixtures/cloudflare-worker/src/index.ts new file mode 100644 index 00000000..4d6147df --- /dev/null +++ b/pkgs/public-api-client/tests/fixtures/cloudflare-worker/src/index.ts @@ -0,0 +1,99 @@ +import { Mosoo, MosooPublicApiAbortError, verifyDelegation } from "@mosoo/sdk"; + +interface FixtureRequest { + apiBaseUrl: string; + apiToken: string; + delegationAudience: string; + delegationToken: string; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function isFixtureRequest(input: unknown): input is FixtureRequest { + if (!isRecord(input)) { + return false; + } + + return ( + typeof input["apiBaseUrl"] === "string" && + typeof input["apiToken"] === "string" && + typeof input["delegationAudience"] === "string" && + typeof input["delegationToken"] === "string" + ); +} + +export default { + async fetch(request: Request): Promise { + try { + const input: unknown = await request.json(); + + if (!isFixtureRequest(input)) { + return Response.json({ error: "Invalid fixture request." }, { status: 400 }); + } + + const mosoo = new Mosoo({ baseUrl: input.apiBaseUrl, token: input.apiToken }); + const upload = await mosoo.uploadAgentFile({ + agentId: "agent-1", + file: new Blob(["Worker upload."], { type: "text/plain" }), + filename: "worker.txt", + }); + const created = await mosoo.createThread({ + agentId: "agent-1", + idempotencyKey: "worker-operation-1", + input: "Run from a Worker.", + userId: "worker-user", + }); + + if (created.run === null) { + throw new Error("Fixture Thread did not start a Run."); + } + + const terminal = await mosoo.waitForFinalOutput({ + runId: created.run.id, + threadId: created.thread.id, + }); + const events = []; + + for await (const event of mosoo.streamEvents({ threadId: created.thread.id })) { + events.push(event.content); + } + + const controller = new AbortController(); + controller.abort(); + let aborted = false; + + try { + await mosoo.waitForRun({ signal: controller.signal, threadId: created.thread.id }); + } catch (error) { + aborted = error instanceof MosooPublicApiAbortError; + } + + if (!aborted) { + throw new Error("Worker AbortSignal was not preserved as a typed SDK error."); + } + + const delegation = await verifyDelegation({ + accessToken: input.apiToken, + audience: input.delegationAudience, + token: input.delegationToken, + }); + + return Response.json({ + aborted, + delegationUserId: delegation.userId, + eventContent: events.join(""), + fileName: upload.file.name, + finalText: terminal.finalOutput.text, + runId: created.run.id, + threadId: created.thread.id, + }); + } catch (error) { + return Response.json( + { error: error instanceof Error ? error.message : "Unknown Worker fixture error." }, + { status: 500 }, + ); + } + }, +} satisfies ExportedHandler; diff --git a/pkgs/public-api-client/tests/fixtures/cloudflare-worker/wrangler.jsonc b/pkgs/public-api-client/tests/fixtures/cloudflare-worker/wrangler.jsonc new file mode 100644 index 00000000..baf413a3 --- /dev/null +++ b/pkgs/public-api-client/tests/fixtures/cloudflare-worker/wrangler.jsonc @@ -0,0 +1,9 @@ +{ + "name": "mosoo-sdk-worker-smoke", + "main": "src/index.ts", + "compatibility_date": "2026-08-11", + "compatibility_flags": ["nodejs_compat"], + "observability": { + "enabled": true, + }, +} diff --git a/pkgs/public-api-client/tests/package.test.ts b/pkgs/public-api-client/tests/package.test.ts new file mode 100644 index 00000000..cf537706 --- /dev/null +++ b/pkgs/public-api-client/tests/package.test.ts @@ -0,0 +1,183 @@ +import { describe, expect, test } from "bun:test"; +import { cp, mkdtemp, mkdir, readFile, readdir, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; + +const PACKAGE_ROOT = resolve(import.meta.dir, ".."); + +async function run(command: string, args: string[], cwd: string): Promise { + const process = Bun.spawn([command, ...args], { + cwd, + stderr: "pipe", + stdout: "pipe", + }); + const [exitCode, stderr, stdout] = await Promise.all([ + process.exited, + new Response(process.stderr).text(), + new Response(process.stdout).text(), + ]); + + if (exitCode !== 0) { + throw new Error(`${command} ${args.join(" ")} failed:\n${stderr}${stdout}`); + } + + return stdout.trim(); +} + +describe("published package", () => { + test("installs one tarball in clean Node.js and Cloudflare Worker fixtures", async () => { + const temporaryRoot = await mkdtemp(join(tmpdir(), "mosoo-sdk-package-")); + + try { + const packDirectory = join(temporaryRoot, "pack"); + const fixtureDirectory = join(temporaryRoot, "fixture"); + await mkdir(packDirectory); + await mkdir(fixtureDirectory); + + await run("bun", ["run", "build"], PACKAGE_ROOT); + await run( + "bun", + ["pm", "pack", "--destination", packDirectory, "--ignore-scripts", "--quiet"], + PACKAGE_ROOT, + ); + + const tarballName = (await readdir(packDirectory)).find((name) => name.endsWith(".tgz")); + expect(tarballName).toBeDefined(); + + const tarballPath = join(packDirectory, tarballName!); + await run( + "npm", + [ + "install", + "--prefix", + fixtureDirectory, + tarballPath, + "--ignore-scripts", + "--no-audit", + "--no-fund", + ], + fixtureDirectory, + ); + + const installedRoot = join(fixtureDirectory, "node_modules", "@mosoo", "sdk"); + const [declarations, javascript, license, manifestText] = await Promise.all([ + readFile(join(installedRoot, "dist", "index.d.ts"), "utf8"), + readFile(join(installedRoot, "dist", "index.js"), "utf8"), + readFile(join(installedRoot, "LICENSE"), "utf8"), + readFile(join(installedRoot, "package.json"), "utf8"), + ]); + const manifest = JSON.parse(manifestText) as { + dependencies?: Record; + name: string; + private: boolean; + version: string; + }; + + expect(manifest).toMatchObject({ + name: "@mosoo/sdk", + private: false, + version: "0.1.0-beta.0", + }); + expect(manifest.dependencies).toBeUndefined(); + expect(javascript).not.toContain("@mosoo/"); + expect(javascript).not.toContain('from "node:'); + expect(declarations).not.toContain("@mosoo/"); + expect(license).toContain("Apache License"); + + await run( + "node", + [ + "--input-type=module", + "--eval", + [ + 'import { Mosoo, MosooPublicThreadClient } from "@mosoo/sdk";', + "let requestedUrl = null;", + "const client = new Mosoo({", + ' token: "mst_test",', + " fetch: async (input) => {", + " requestedUrl = String(input);", + " return Response.json({ events: [], truncated: false });", + " },", + "});", + 'if (!(client instanceof MosooPublicThreadClient)) throw new Error("Bad Mosoo alias.");', + 'await client.listEvents({ threadId: "thread-1" });', + 'if (requestedUrl !== "https://cloud.mosoo.ai/api/v1/threads/thread-1/events") {', + " throw new Error(`Unexpected URL: ${requestedUrl}`);", + "}", + ].join("\n"), + ], + fixtureDirectory, + ); + + const typecheckPath = join(fixtureDirectory, "typecheck.ts"); + const tsconfigPath = join(fixtureDirectory, "tsconfig.json"); + await Bun.write( + typecheckPath, + [ + 'import { Mosoo, type PublicThreadRunSummary } from "@mosoo/sdk";', + 'const client = new Mosoo({ token: "mst_test" });', + "const run: PublicThreadRunSummary | null = null;", + "void client;", + "void run;", + ].join("\n"), + ); + await Bun.write( + tsconfigPath, + JSON.stringify({ + compilerOptions: { + lib: ["ES2022", "DOM"], + module: "NodeNext", + moduleResolution: "NodeNext", + noEmit: true, + strict: true, + target: "ES2022", + }, + include: ["typecheck.ts"], + }), + ); + await run( + resolve(PACKAGE_ROOT, "node_modules/.bin/tsc"), + ["-p", tsconfigPath], + fixtureDirectory, + ); + + const workerFixtureDirectory = join(fixtureDirectory, "worker"); + await cp( + join(PACKAGE_ROOT, "tests", "fixtures", "cloudflare-worker"), + workerFixtureDirectory, + { recursive: true }, + ); + const workerConfigPath = join(workerFixtureDirectory, "wrangler.jsonc"); + await run( + resolve(PACKAGE_ROOT, "node_modules/.bin/wrangler"), + ["types", "worker-configuration.d.ts", "--config", workerConfigPath], + workerFixtureDirectory, + ); + await Bun.write( + join(workerFixtureDirectory, "tsconfig.json"), + JSON.stringify({ + compilerOptions: { + lib: ["ES2022"], + module: "ESNext", + moduleResolution: "Bundler", + noEmit: true, + strict: true, + }, + include: ["worker-configuration.d.ts", "src/**/*.ts"], + }), + ); + await run( + resolve(PACKAGE_ROOT, "node_modules/.bin/tsc"), + ["-p", join(workerFixtureDirectory, "tsconfig.json")], + workerFixtureDirectory, + ); + await run( + "node", + [resolve(PACKAGE_ROOT, "tests", "worker-runtime-smoke.mjs"), workerConfigPath], + fixtureDirectory, + ); + } finally { + await rm(temporaryRoot, { force: true, recursive: true }); + } + }, 60_000); +}); diff --git a/pkgs/public-api-client/tests/public-api-client.test.ts b/pkgs/public-api-client/tests/public-api-client.test.ts index f6f9e4d3..048505e9 100644 --- a/pkgs/public-api-client/tests/public-api-client.test.ts +++ b/pkgs/public-api-client/tests/public-api-client.test.ts @@ -1,19 +1,24 @@ import { describe, expect, test } from "bun:test"; import type { - PublicFileResponse, + MosooPublicApiError, + MosooPublicApiFetch, + MosooPublicThreadRunMismatchError, +} from "../src/index.ts"; +import { + extractFinalOutput, + MosooPublicApiAbortError, + MosooPublicApiTimeoutError, + MosooPublicThreadClient, + MosooPublicThreadTerminalRunError, +} from "../src/index.ts"; +import type { PublicThreadApiCreateThreadResponse, - PublicThreadApiListThreadEventsResponse, PublicThreadApiRetrieveThreadResponse, PublicThreadEventLogEntry, + PublicThreadArtifact, PublicThreadFinalOutput, - PublicThreadRunSummary, - PublicThreadSummary, -} from "@mosoo/contracts/public-api"; -import type { MosooPublicApiError, MosooPublicApiFetch } from "@mosoo/public-api-client"; -import { MosooPublicThreadClient } from "@mosoo/public-api-client"; -import { MosooPublicThreadTerminalRunError } from "@mosoo/public-api-client"; -import { extractFinalOutput } from "@mosoo/public-api-client"; +} from "../src/types.ts"; interface RecordedRequest { body: unknown; @@ -22,18 +27,22 @@ interface RecordedRequest { url: string; } -const AGENT_ID = "01J00000000000000000000001" as PublicThreadSummary["agent_id"]; -const THREAD_ID = "01J00000000000000000000009" as PublicThreadSummary["id"]; -const RUN_ID = "01J0000000000000000000000A" as NonNullable; -const ALT_RUN_ID = "01J0000000000000000000000B" as NonNullable; -const FILE_ID = "01J0000000000000000000000J" as PublicFileResponse["file"]["id"]; -const EVENT_ID_10 = "01J00000000000000000000010" as PublicThreadEventLogEntry["id"]; -const EVENT_ID_11 = "01J00000000000000000000011" as PublicThreadEventLogEntry["id"]; -const EVENT_ID_12 = "01J00000000000000000000012" as PublicThreadEventLogEntry["id"]; - -function threadResponse(status: "RUNNING" | "IDLE" = "RUNNING"): PublicThreadSummary { +const THREAD_ID = "01J00000000000000000000009"; +const RUN_ID = "01J0000000000000000000000A"; +const FILE_ID = "01J0000000000000000000000J"; +const ARTIFACT = { + createdAt: "2026-05-19T00:00:01.500Z", + fileId: FILE_ID, + kind: "artifact", + mimeType: "text/html", + name: "index.html", + runId: RUN_ID, + size: 42, +} satisfies PublicThreadArtifact; + +function threadResponse(status: "RUNNING" | "IDLE" = "RUNNING") { return { - agent_id: AGENT_ID, + agent_id: "01J00000000000000000000001", created_at: "2026-05-19T00:00:00.000Z", id: THREAD_ID, kind: "pet", @@ -43,13 +52,13 @@ function threadResponse(status: "RUNNING" | "IDLE" = "RUNNING"): PublicThreadSum title: "Say hello", updated_at: "2026-05-19T00:00:01.000Z", userId: "customer-123", - }; + } as const; } function runResponse( - status: "completed" | "failed" | "running" = "running", + status: "cancelled" | "completed" | "expired" | "failed" | "running" = "running", finalOutput: PublicThreadFinalOutput | null = null, -): PublicThreadRunSummary { +) { return { completedAt: status === "running" ? null : "2026-05-19T00:00:02.000Z", createdAt: "2026-05-19T00:00:00.000Z", @@ -67,7 +76,7 @@ function runResponse( status, trigger: "user_prompt", updatedAt: "2026-05-19T00:00:02.000Z", - }; + } as const; } async function readRequestBody(request: Request): Promise { @@ -80,6 +89,16 @@ function jsonResponse(value: unknown, status = 200): Response { return Response.json(value, { status }); } +async function captureRejection(promise: Promise): Promise { + try { + await promise; + } catch (error) { + return error; + } + + throw new Error("Expected promise to reject."); +} + describe("MosooPublicThreadClient", () => { test("maps createThread fileIds to public file resources", async () => { const requests: RecordedRequest[] = []; @@ -193,49 +212,14 @@ describe("MosooPublicThreadClient", () => { if (request.method === "GET" && request.url.endsWith(`/threads/${THREAD_ID}`)) { return jsonResponse({ links: { thread: `/api/v1/threads/${THREAD_ID}` }, - run: runResponse("completed", { text: "最终答复:完整的中文、Markdown 和 😀。" }), + run: { + ...runResponse("completed", { text: "最终答复:完整的中文、Markdown 和 😀。" }), + artifacts: [ARTIFACT], + }, thread: threadResponse("IDLE"), } satisfies PublicThreadApiRetrieveThreadResponse); } - if (request.method === "GET" && request.url.includes(`/threads/${THREAD_ID}/events`)) { - return jsonResponse({ - events: [ - { - content: "Old output", - durationMs: 0, - id: EVENT_ID_10, - occurredAt: "2026-05-19T00:00:00.000Z", - runId: ALT_RUN_ID, - status: "available", - tokens: null, - type: "agent.message.delta", - }, - { - content: "进度:正在生成最终答复。", - durationMs: 0, - id: EVENT_ID_11, - occurredAt: "2026-05-19T00:00:01.000Z", - runId: RUN_ID, - status: "available", - tokens: null, - type: "agent.message.delta", - }, - { - content: "不应被拼入最终答复。", - durationMs: 0, - id: EVENT_ID_12, - occurredAt: "2026-05-19T00:00:02.000Z", - runId: RUN_ID, - status: "available", - tokens: null, - type: "agent.message.delta", - }, - ], - truncated: false, - } satisfies PublicThreadApiListThreadEventsResponse); - } - return jsonResponse({ error: { code: "not_found", message: "Not found." } }, 404); }; @@ -254,6 +238,7 @@ describe("MosooPublicThreadClient", () => { expect(result.finalOutput).toEqual({ text: "最终答复:完整的中文、Markdown 和 😀。" }); expect(result.run.finalOutput).toEqual({ text: "最终答复:完整的中文、Markdown 和 😀。" }); + expect(result.run.artifacts).toEqual([ARTIFACT]); expect(requests[0]?.headers.get("Authorization")).toBe("Bearer mst_test"); expect(requests[0]?.headers.get("Idempotency-Key")).toBe("thread-create-1"); expect(requests[0]?.body).toEqual({ @@ -266,7 +251,6 @@ describe("MosooPublicThreadClient", () => { expect(requests.map((request) => new URL(request.url).pathname)).toEqual([ "/api/v1/agents/agent-1/threads", `/api/v1/threads/${THREAD_ID}`, - `/api/v1/threads/${THREAD_ID}/events`, ]); }); @@ -293,13 +277,6 @@ describe("MosooPublicThreadClient", () => { } satisfies PublicThreadApiRetrieveThreadResponse); } - if (request.method === "GET" && request.url.includes(`/threads/${THREAD_ID}/events`)) { - return jsonResponse({ - events: [], - truncated: false, - } satisfies PublicThreadApiListThreadEventsResponse); - } - return jsonResponse({ error: { code: "not_found", message: "Not found." } }, 404); }; const client = new MosooPublicThreadClient({ @@ -333,7 +310,6 @@ describe("MosooPublicThreadClient", () => { status: "failed", }, runStatus: "failed", - truncated: false, }); }); @@ -360,13 +336,6 @@ describe("MosooPublicThreadClient", () => { } satisfies PublicThreadApiRetrieveThreadResponse); } - if (request.method === "GET" && request.url.includes(`/threads/${THREAD_ID}/events`)) { - return jsonResponse({ - events: [], - truncated: false, - } satisfies PublicThreadApiListThreadEventsResponse); - } - return jsonResponse({ error: { code: "not_found", message: "Not found." } }, 404); }; const client = new MosooPublicThreadClient({ @@ -392,9 +361,11 @@ describe("MosooPublicThreadClient", () => { }); }); - test("does not reconstruct a completed final output from progress events", async () => { + test("does not read progress events to reconstruct a completed final output", async () => { + const requests: string[] = []; const fetchMock: MosooPublicApiFetch = async (input, init) => { const request = new Request(input, init); + requests.push(request.url); if (request.method === "GET" && request.url.endsWith(`/threads/${THREAD_ID}`)) { return jsonResponse({ @@ -404,34 +375,6 @@ describe("MosooPublicThreadClient", () => { } satisfies PublicThreadApiRetrieveThreadResponse); } - if (request.method === "GET" && request.url.includes(`/threads/${THREAD_ID}/events`)) { - return jsonResponse({ - events: [ - { - content: "进度:读取资料。", - durationMs: 0, - id: EVENT_ID_10, - occurredAt: "2026-05-19T00:00:00.000Z", - runId: RUN_ID, - status: "available", - tokens: null, - type: "agent.message.delta", - }, - { - content: "错误的事件拼接候选。", - durationMs: 0, - id: EVENT_ID_11, - occurredAt: "2026-05-19T00:00:01.000Z", - runId: RUN_ID, - status: "available", - tokens: null, - type: "agent.message.delta", - }, - ], - truncated: false, - } satisfies PublicThreadApiListThreadEventsResponse); - } - return jsonResponse({ error: { code: "not_found", message: "Not found." } }, 404); }; const client = new MosooPublicThreadClient({ @@ -440,18 +383,122 @@ describe("MosooPublicThreadClient", () => { token: "mst_test", }); - let thrown: unknown = null; + expect( + await captureRejection(client.waitForFinalOutput({ threadId: THREAD_ID })), + ).toMatchObject({ + message: `Completed Public Thread run ${RUN_ID} did not include final output.`, + }); + expect(requests).toEqual([`https://api.example.com/api/v1/threads/${THREAD_ID}`]); + }); - try { - await client.waitForFinalOutput({ threadId: THREAD_ID }); - } catch (error) { - thrown = error; - } + test("resumes a Run from persisted ids in a new client instance", async () => { + const fetchMock: MosooPublicApiFetch = async () => + jsonResponse({ + links: { thread: `/api/v1/threads/${THREAD_ID}` }, + run: runResponse("completed", { text: "Recovered." }), + thread: threadResponse("IDLE"), + } satisfies PublicThreadApiRetrieveThreadResponse); + + const result = await new MosooPublicThreadClient({ + baseUrl: "https://api.example.com", + fetch: fetchMock, + token: "mst_test", + }).waitForFinalOutput({ runId: RUN_ID, threadId: THREAD_ID }); + + expect(result.finalOutput.text).toBe("Recovered."); + }); + + test("distinguishes timeout, abort, and Run mismatch", async () => { + const runningFetch: MosooPublicApiFetch = async () => + jsonResponse({ + links: { thread: `/api/v1/threads/${THREAD_ID}` }, + run: runResponse("running"), + thread: threadResponse(), + } satisfies PublicThreadApiRetrieveThreadResponse); + const client = new MosooPublicThreadClient({ + baseUrl: "https://api.example.com", + fetch: runningFetch, + pollIntervalMs: 1, + token: "mst_test", + }); + + expect( + await captureRejection(client.waitForRun({ threadId: THREAD_ID, timeoutMs: 1 })), + ).toBeInstanceOf(MosooPublicApiTimeoutError); + + const stalledClient = new MosooPublicThreadClient({ + baseUrl: "https://api.example.com", + fetch: async () => new Promise(() => {}), + token: "mst_test", + }); + expect( + await captureRejection(stalledClient.waitForRun({ threadId: THREAD_ID, timeoutMs: 1 })), + ).toBeInstanceOf(MosooPublicApiTimeoutError); + + const controller = new AbortController(); + controller.abort(); + expect( + await captureRejection(client.waitForRun({ signal: controller.signal, threadId: THREAD_ID })), + ).toBeInstanceOf(MosooPublicApiAbortError); - expect(thrown).toBeInstanceOf(Error); - expect((thrown as Error).message).toBe( - `Completed Public Thread run ${RUN_ID} did not include final output.`, + expect( + await captureRejection(client.waitForRun({ runId: "another-run", threadId: THREAD_ID })), + ).toMatchObject({ + actualRunId: RUN_ID, + code: "run_mismatch", + expectedRunId: "another-run", + } satisfies Partial); + }); + + test("rejects unsafe client configuration before sending a token", () => { + expect(() => new MosooPublicThreadClient({ token: " " })).toThrow( + "Mosoo token must not be empty.", ); + expect( + () => new MosooPublicThreadClient({ baseUrl: "http://api.example.com", token: "mst_test" }), + ).toThrow("Mosoo baseUrl must use HTTPS"); + expect( + () => + new MosooPublicThreadClient({ + baseUrl: "https://user:secret@api.example.com?token=leak", + token: "mst_test", + }), + ).toThrow("must not include credentials"); + expect( + () => + new MosooPublicThreadClient({ + baseUrl: "http://localhost:8787", + token: "mst_test", + }), + ).not.toThrow(); + }); + + test("rejects browser-like runtimes by default", () => { + const documentDescriptor = Object.getOwnPropertyDescriptor(globalThis, "document"); + const windowDescriptor = Object.getOwnPropertyDescriptor(globalThis, "window"); + Object.defineProperty(globalThis, "document", { configurable: true, value: {} }); + Object.defineProperty(globalThis, "window", { configurable: true, value: {} }); + + try { + expect(() => new MosooPublicThreadClient({ token: "mst_test" })).toThrow( + "must run on a backend, Worker, or Node-like runtime", + ); + expect( + () => new MosooPublicThreadClient({ allowBrowserToken: true, token: "mst_test" }), + ).not.toThrow(); + } finally { + if (documentDescriptor === undefined) { + Reflect.deleteProperty(globalThis, "document"); + } else { + Object.defineProperty(globalThis, "document", documentDescriptor); + } + + if (windowDescriptor === undefined) { + Reflect.deleteProperty(globalThis, "window"); + } else { + Object.defineProperty(globalThis, "window", windowDescriptor); + } + } }); test("keeps the deprecated event concatenation helper for compatibility", () => { @@ -461,7 +508,7 @@ describe("MosooPublicThreadClient", () => { { content: "first", durationMs: 0, - id: EVENT_ID_10, + id: "01J00000000000000000000010", occurredAt: "2026-05-19T00:00:00.000Z", runId: RUN_ID, status: "available", @@ -471,9 +518,9 @@ describe("MosooPublicThreadClient", () => { { content: "ignored", durationMs: 0, - id: EVENT_ID_11, + id: "01J00000000000000000000011", occurredAt: "2026-05-19T00:00:01.000Z", - runId: ALT_RUN_ID, + runId: "01J0000000000000000000000B", status: "available", tokens: null, type: "agent.message.delta", @@ -481,7 +528,7 @@ describe("MosooPublicThreadClient", () => { { content: " second", durationMs: 0, - id: EVENT_ID_12, + id: "01J00000000000000000000012", occurredAt: "2026-05-19T00:00:02.000Z", runId: RUN_ID, status: "available", @@ -511,33 +558,62 @@ describe("MosooPublicThreadClient", () => { token: "mst_test", }); - let thrown: unknown = null; - - try { - await client.listEvents({ threadId: THREAD_ID }); - } catch (error) { - thrown = error; - } - - expect(thrown).toMatchObject({ + expect(await captureRejection(client.listEvents({ threadId: THREAD_ID }))).toMatchObject({ code: "rate_limited", message: "Too many requests.", status: 429, } satisfies Partial); }); + test("lists typed Thread files", async () => { + const fetchMock: MosooPublicApiFetch = async (input, init) => { + const request = new Request(input, init); + + expect(request.method).toBe("GET"); + expect(request.url).toBe(`https://api.example.com/api/v1/threads/${THREAD_ID}/files`); + + return jsonResponse({ + files: [ + { + committed: true, + createdAt: ARTIFACT.createdAt, + fileId: FILE_ID, + id: FILE_ID, + kind: "artifact", + mimeType: ARTIFACT.mimeType, + name: ARTIFACT.name, + runId: RUN_ID, + size: ARTIFACT.size, + }, + ], + }); + }; + const client = new MosooPublicThreadClient({ + baseUrl: "https://api.example.com", + fetch: fetchMock, + token: "mst_test", + }); + + const result = await client.listFiles({ threadId: THREAD_ID }); + + expect(result.files[0]).toMatchObject({ fileId: FILE_ID, runId: RUN_ID }); + }); + test("streams thread.event SSE payloads", async () => { const encoder = new TextEncoder(); + let cancelled = false; const fetchMock: MosooPublicApiFetch = async () => new Response( new ReadableStream({ + cancel() { + cancelled = true; + }, start(controller) { controller.enqueue( encoder.encode( - `: connected\n\nevent: thread.event\nid: 01J00000000000000000000010\ndata: {"content":"record_meal","durationMs":0,"id":"01J00000000000000000000010","occurredAt":"2026-05-19T00:00:00.000Z","runId":"${RUN_ID}","status":"available","toolCallId":"tool-1","toolInput":{"calories":420},"toolName":"record_meal","tokens":null,"type":"tool.use.started"}\n\n`, + `: connected\n\nevent: thread.event\nid: 01J00000000000000000000010\ndata: {"artifact":${JSON.stringify(ARTIFACT)},"content":"Session files updated.","durationMs":0,"id":"01J00000000000000000000010","occurredAt":"2026-05-19T00:00:00.000Z","runId":"${RUN_ID}","status":"available","tokens":null,"type":"session_files.updated"}\n\n`, ), ); - controller.close(); }, }), { @@ -554,21 +630,21 @@ describe("MosooPublicThreadClient", () => { for await (const event of client.streamEvents({ threadId: THREAD_ID })) { events.push(event); + break; } + expect(cancelled).toBe(true); expect(events).toEqual([ { - content: "record_meal", + artifact: ARTIFACT, + content: "Session files updated.", durationMs: 0, - id: EVENT_ID_10, + id: "01J00000000000000000000010", occurredAt: "2026-05-19T00:00:00.000Z", runId: RUN_ID, status: "available", - toolCallId: "tool-1", - toolInput: { calories: 420 }, - toolName: "record_meal", tokens: null, - type: "tool.use.started", + type: "session_files.updated", }, ]); }); diff --git a/pkgs/public-api-client/tests/worker-runtime-smoke.mjs b/pkgs/public-api-client/tests/worker-runtime-smoke.mjs new file mode 100644 index 00000000..3497bcad --- /dev/null +++ b/pkgs/public-api-client/tests/worker-runtime-smoke.mjs @@ -0,0 +1,239 @@ +import assert from "node:assert/strict"; +import { Buffer } from "node:buffer"; +import { webcrypto } from "node:crypto"; +import { createServer } from "node:http"; + +import { unstable_startWorker } from "wrangler"; + +const THREAD_ID = "01J00000000000000000000009"; +const RUN_ID = "01J0000000000000000000000A"; +const FILE_ID = "01J0000000000000000000000J"; +const API_TOKEN = "worker-test-token"; +const encoder = new TextEncoder(); + +function json(response, status, body) { + response.writeHead(status, { "Content-Type": "application/json" }); + response.end(JSON.stringify(body)); +} + +async function bodyBuffer(request) { + const chunks = []; + + for await (const chunk of request) { + chunks.push(chunk); + } + + return Buffer.concat(chunks); +} + +async function handleApiRequest(request, response) { + const url = new URL(request.url ?? "/", "http://localhost"); + + if (request.headers.authorization !== `Bearer ${API_TOKEN}`) { + json(response, 401, { error: { code: "unauthenticated" } }); + return; + } + + if (request.method === "POST" && url.pathname.endsWith("/agents/agent-1/files")) { + const body = await bodyBuffer(request); + const contentType = request.headers["content-type"] ?? ""; + + if ( + !contentType.startsWith("multipart/form-data;") || + !body.includes('filename="worker.txt"') || + !body.includes("Worker upload.") + ) { + json(response, 400, { error: { code: "invalid_request" } }); + return; + } + + json(response, 201, { + file: { + createdAt: "2026-08-11T00:00:00.000Z", + id: FILE_ID, + mimeType: "text/plain", + name: "worker.txt", + size: 14, + }, + }); + return; + } + + if (request.method === "POST" && url.pathname.endsWith("/agents/agent-1/threads")) { + if (request.headers["idempotency-key"] !== "worker-operation-1") { + json(response, 400, { error: { code: "invalid_request" } }); + return; + } + + json(response, 201, { + links: { thread: `/api/v1/threads/${THREAD_ID}` }, + run: { + completedAt: null, + createdAt: "2026-08-11T00:00:00.000Z", + error: null, + finalOutput: null, + id: RUN_ID, + startedAt: "2026-08-11T00:00:01.000Z", + status: "running", + trigger: "user_prompt", + updatedAt: "2026-08-11T00:00:01.000Z", + }, + thread: thread("RUNNING", "2026-08-11T00:00:01.000Z"), + }); + return; + } + + if (request.method === "GET" && url.pathname.endsWith(`/threads/${THREAD_ID}`)) { + json(response, 200, { + links: { thread: `/api/v1/threads/${THREAD_ID}` }, + run: { + completedAt: "2026-08-11T00:00:02.000Z", + createdAt: "2026-08-11T00:00:00.000Z", + error: null, + finalOutput: { text: "Worker complete." }, + id: RUN_ID, + startedAt: "2026-08-11T00:00:01.000Z", + status: "completed", + trigger: "user_prompt", + updatedAt: "2026-08-11T00:00:02.000Z", + }, + thread: thread("IDLE", "2026-08-11T00:00:02.000Z"), + }); + return; + } + + if (request.method === "GET" && url.pathname.endsWith(`/threads/${THREAD_ID}/events/stream`)) { + response.writeHead(200, { "Content-Type": "text/event-stream" }); + response.end( + `event: thread.event\nid: event-1\ndata: {"content":"Worker progress.","durationMs":0,"id":"event-1","occurredAt":"2026-08-11T00:00:01.000Z","runId":"${RUN_ID}","status":"available","tokens":null,"type":"agent.message.delta"}\n\n`, + ); + return; + } + + json(response, 404, { error: { code: "not_found" } }); +} + +function thread(status, updatedAt) { + return { + agent_id: "agent-1", + created_at: "2026-08-11T00:00:00.000Z", + id: THREAD_ID, + kind: "pet", + last_run_id: RUN_ID, + source: "api", + status, + title: "Worker smoke", + updated_at: updatedAt, + userId: "worker-user", + }; +} + +async function createDelegationToken(audience) { + const issuedAt = Math.floor(Date.now() / 1_000); + const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url"); + const payload = Buffer.from( + JSON.stringify({ + act: { agent_id: "agent-1", app_id: "app-1" }, + aud: audience, + exp: issuedAt + 60, + iat: issuedAt, + iss: "mosoo", + jti: "00000000-0000-4000-8000-000000000001", + run_id: RUN_ID, + sub: "worker-user", + thread_id: THREAD_ID, + }), + ).toString("base64url"); + const material = await webcrypto.subtle.digest( + "SHA-256", + encoder.encode(`mosoo-mcp-delegation-v1\0${API_TOKEN}`), + ); + const key = await webcrypto.subtle.importKey( + "raw", + material, + { hash: "SHA-256", name: "HMAC" }, + false, + ["sign"], + ); + const signature = await webcrypto.subtle.sign( + "HMAC", + key, + encoder.encode(`${header}.${payload}`), + ); + + return `${header}.${payload}.${Buffer.from(signature).toString("base64url")}`; +} + +function withTimeout(promise, message) { + let timeout; + const expired = new Promise((_, reject) => { + timeout = setTimeout(() => reject(new Error(message)), 15_000); + }); + + return Promise.race([promise, expired]).finally(() => clearTimeout(timeout)); +} + +const config = process.argv[2]; + +if (config === undefined) { + throw new Error("Worker config path is required."); +} + +const apiServer = createServer((request, response) => { + handleApiRequest(request, response).catch((error) => { + json(response, 500, { error: error instanceof Error ? error.message : "Unknown mock error." }); + }); +}); +await new Promise((resolve, reject) => { + apiServer.once("error", reject); + apiServer.listen(0, "127.0.0.1", resolve); +}); +const address = apiServer.address(); + +if (address === null || typeof address === "string") { + throw new Error("Mock API did not bind a TCP port."); +} + +let worker; + +try { + worker = await withTimeout(unstable_startWorker({ config }), "Worker did not start in 15s."); + const delegationAudience = "https://tools.example.com/mcp"; + const response = await withTimeout( + worker.fetch("http://example.com", { + body: JSON.stringify({ + apiBaseUrl: `http://127.0.0.1:${address.port}`, + apiToken: API_TOKEN, + delegationAudience, + delegationToken: await createDelegationToken(delegationAudience), + }), + headers: { "Content-Type": "application/json" }, + method: "POST", + }), + "Worker SDK flow did not finish in 15s.", + ); + const result = await response.json(); + + assert.equal(response.status, 200, JSON.stringify(result)); + assert.deepEqual(result, { + aborted: true, + delegationUserId: "worker-user", + eventContent: "Worker progress.", + fileName: "worker.txt", + finalText: "Worker complete.", + runId: RUN_ID, + threadId: THREAD_ID, + }); +} finally { + await worker?.dispose(); + await new Promise((resolve, reject) => { + apiServer.close((error) => { + if (error !== undefined) { + reject(error); + return; + } + + resolve(); + }); + }); +} diff --git a/pkgs/public-api-client/tsconfig.json b/pkgs/public-api-client/tsconfig.json index db6c47d2..0e013e37 100644 --- a/pkgs/public-api-client/tsconfig.json +++ b/pkgs/public-api-client/tsconfig.json @@ -4,5 +4,6 @@ "lib": ["ESNext", "DOM"], "types": ["bun"] }, + "exclude": ["tests/fixtures/**"], "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/pkgs/public-api-client/vite.config.ts b/pkgs/public-api-client/vite.config.ts new file mode 100644 index 00000000..2813b2dd --- /dev/null +++ b/pkgs/public-api-client/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vite-plus"; + +import rootConfig from "../../vite.config.ts"; + +export default defineConfig({ + ...rootConfig, + pack: { + dts: true, + entry: ["src/index.ts"], + format: ["esm"], + outDir: "dist", + platform: "neutral", + target: "es2022", + }, +});