@opengeni/db 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/browser-sessions.d.ts +26 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/capability-reconciliation.js +2 -2
  4. package/dist/{chunk-4RDUZ5UV.js → chunk-4ZQFT5II.js} +2 -2
  5. package/dist/{chunk-6ORJPCYW.js → chunk-5UOWNKPP.js} +2 -2
  6. package/dist/{chunk-4CCFFLDZ.js → chunk-BG46J5HF.js} +13 -2
  7. package/dist/chunk-BG46J5HF.js.map +1 -0
  8. package/dist/{chunk-CSVHCF3R.js → chunk-IB3SW3JW.js} +11 -4
  9. package/dist/{chunk-CSVHCF3R.js.map → chunk-IB3SW3JW.js.map} +1 -1
  10. package/dist/{chunk-GSB4ISHE.js → chunk-IIPWNNVR.js} +4 -1
  11. package/dist/chunk-IIPWNNVR.js.map +1 -0
  12. package/dist/{chunk-MAMBI2HR.js → chunk-JPYN5XKV.js} +39 -3
  13. package/dist/chunk-JPYN5XKV.js.map +1 -0
  14. package/dist/{chunk-LSLNMENV.js → chunk-KZXWUXWV.js} +4 -4
  15. package/dist/{chunk-LO6BVXXH.js → chunk-LD52IFS3.js} +2 -2
  16. package/dist/{chunk-JILAWE26.js → chunk-M5Z6II2P.js} +2 -2
  17. package/dist/{chunk-PGIFHEBA.js → chunk-RVEOGRMF.js} +2 -2
  18. package/dist/{chunk-H7BC444C.js → chunk-SRAMNVHT.js} +17 -1
  19. package/dist/chunk-SRAMNVHT.js.map +1 -0
  20. package/dist/{chunk-XK6HXON2.js → chunk-UXFBXCHM.js} +2 -2
  21. package/dist/{chunk-RXH5CFG3.js → chunk-VE2XK2KD.js} +12 -8
  22. package/dist/{chunk-RXH5CFG3.js.map → chunk-VE2XK2KD.js.map} +1 -1
  23. package/dist/{chunk-FULLE5CF.js → chunk-WT7HG43Y.js} +3 -3
  24. package/dist/{chunk-I7HQL6FK.js → chunk-ZM5BA3TD.js} +3 -3
  25. package/dist/connect-attempts.js +3 -3
  26. package/dist/database.d.ts +8 -0
  27. package/dist/editable-artifact-durable-export.js +2 -2
  28. package/dist/editable-artifacts.js +3 -3
  29. package/dist/index.d.ts +69 -20
  30. package/dist/index.js +633 -202
  31. package/dist/index.js.map +1 -1
  32. package/dist/knowledge-entries.d.ts +2 -0
  33. package/dist/knowledge-indexing.d.ts +17 -0
  34. package/dist/managed-auth-session-sets.js +3 -3
  35. package/dist/managed-user-setup.d.ts +1 -0
  36. package/dist/mcp-operations.js +2 -2
  37. package/dist/migrate.d.ts +10 -1
  38. package/dist/migrate.js +3 -1
  39. package/dist/organization-integration-policy.js +3 -3
  40. package/dist/organization-membership-lifecycle.d.ts +1 -0
  41. package/dist/provision-roles.js +1 -1
  42. package/dist/retained-provider-commands.d.ts +4 -2
  43. package/dist/retained-provider-commands.js +6 -6
  44. package/dist/runtime-posture.d.ts +1 -1
  45. package/dist/schema.d.ts +25 -8
  46. package/dist/schema.js +1 -1
  47. package/dist/session-background-commands.js +4 -4
  48. package/dist/session-command-output.js +5 -5
  49. package/dist/session-event-slices.js +3 -3
  50. package/dist/session-mcp-credential-rotation.js +4 -4
  51. package/dist/session-tenancy.js +3 -3
  52. package/dist/video-generation.js +3 -3
  53. package/dist/workspace-tool-defaults.js +4 -4
  54. package/drizzle/0508_deadline_command_workspace_capture.sql +75 -0
  55. package/drizzle/0509_verified_signup_trial_credits.sql +76 -0
  56. package/drizzle/0510_knowledge_index_funding_wait.sql +174 -0
  57. package/drizzle/0511_knowledge_visible_index_status.sql +56 -0
  58. package/drizzle/0512_insights_fact_reads_hash_join.sql +31 -0
  59. package/drizzle/0513_message_fork_prefix_compaction.sql +85 -0
  60. package/drizzle/0514_failed_session_variable_set_attach.sql +25 -0
  61. package/package.json +5 -5
  62. package/src/browser-sessions.ts +71 -1
  63. package/src/database.ts +18 -0
  64. package/src/index.ts +706 -252
  65. package/src/insights.ts +44 -36
  66. package/src/knowledge-entries.ts +43 -2
  67. package/src/knowledge-indexing.ts +79 -1
  68. package/src/managed-user-setup.ts +18 -1
  69. package/src/migrate.ts +58 -1
  70. package/src/organization-membership-lifecycle.ts +11 -0
  71. package/src/provision-roles.ts +12 -0
  72. package/src/retained-provider-commands.ts +16 -6
  73. package/src/runtime-posture.ts +4 -0
  74. package/src/schema.ts +3 -0
  75. package/dist/chunk-4CCFFLDZ.js.map +0 -1
  76. package/dist/chunk-GSB4ISHE.js.map +0 -1
  77. package/dist/chunk-H7BC444C.js.map +0 -1
  78. package/dist/chunk-MAMBI2HR.js.map +0 -1
  79. /package/dist/{chunk-4RDUZ5UV.js.map → chunk-4ZQFT5II.js.map} +0 -0
  80. /package/dist/{chunk-6ORJPCYW.js.map → chunk-5UOWNKPP.js.map} +0 -0
  81. /package/dist/{chunk-LSLNMENV.js.map → chunk-KZXWUXWV.js.map} +0 -0
  82. /package/dist/{chunk-LO6BVXXH.js.map → chunk-LD52IFS3.js.map} +0 -0
  83. /package/dist/{chunk-JILAWE26.js.map → chunk-M5Z6II2P.js.map} +0 -0
  84. /package/dist/{chunk-PGIFHEBA.js.map → chunk-RVEOGRMF.js.map} +0 -0
  85. /package/dist/{chunk-XK6HXON2.js.map → chunk-UXFBXCHM.js.map} +0 -0
  86. /package/dist/{chunk-FULLE5CF.js.map → chunk-WT7HG43Y.js.map} +0 -0
  87. /package/dist/{chunk-I7HQL6FK.js.map → chunk-ZM5BA3TD.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  rawRows,
3
3
  withWorkspaceSubjectRls
4
- } from "./chunk-4CCFFLDZ.js";
4
+ } from "./chunk-BG46J5HF.js";
5
5
 
6
6
  // src/connect-attempts.ts
7
7
  import { createHash } from "crypto";
@@ -216,4 +216,4 @@ export {
216
216
  claimConnectOperation,
217
217
  finishConnectOperation
218
218
  };
219
- //# sourceMappingURL=chunk-XK6HXON2.js.map
219
+ //# sourceMappingURL=chunk-UXFBXCHM.js.map
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  appendSessionCommandOutput
3
- } from "./chunk-LSLNMENV.js";
3
+ } from "./chunk-KZXWUXWV.js";
4
4
  import {
5
5
  lockSessionEventWriteRows
6
- } from "./chunk-CSVHCF3R.js";
6
+ } from "./chunk-IB3SW3JW.js";
7
7
  import {
8
8
  withRlsContext,
9
9
  withSessionActivityRlsContext
10
- } from "./chunk-4CCFFLDZ.js";
10
+ } from "./chunk-BG46J5HF.js";
11
11
  import {
12
12
  sandboxRetainedProcesses
13
- } from "./chunk-GSB4ISHE.js";
13
+ } from "./chunk-IIPWNNVR.js";
14
14
 
15
15
  // src/retained-provider-commands.ts
16
16
  import { and, eq, sql } from "drizzle-orm";
@@ -242,7 +242,7 @@ async function retainedProcessCancellationRequested(db, scope) {
242
242
  async function requestRetainedProcessDeadlineCancellation(db, scope) {
243
243
  return withRlsContext(db, scope, async (tx) => {
244
244
  const [row] = await tx.select().from(sandboxRetainedProcesses).where(processWhere(scope)).for("update").limit(1);
245
- if (!row || row.state !== "active" || !row.providerCommand || !supervisionDescriptor(row.providerCommand))
245
+ if (!row || row.state !== "active" || row.providerBackend !== "modal" || row.routeTargetId !== null)
246
246
  return false;
247
247
  const [lease] = await tx.execute(sql`
248
248
  select id from sandbox_leases where id = ${row.leaseId}
@@ -252,8 +252,12 @@ async function requestRetainedProcessDeadlineCancellation(db, scope) {
252
252
  and rotation_requested_at is not null and rotation_reason = 'provider_deadline' for update
253
253
  `);
254
254
  if (!lease) return false;
255
- if (!row.cancellationRequestedAt)
256
- await tx.update(sandboxRetainedProcesses).set({ cancellationRequestedAt: /* @__PURE__ */ new Date(), cancellationReason: "provider_deadline" }).where(processWhere(scope));
255
+ if (!row.deadlineCancellationRequestedAt || !row.cancellationRequestedAt) {
256
+ await tx.update(sandboxRetainedProcesses).set({
257
+ ...!row.cancellationRequestedAt ? { cancellationRequestedAt: sql`now()`, cancellationReason: "provider_deadline" } : {},
258
+ ...!row.deadlineCancellationRequestedAt ? { deadlineCancellationRequestedAt: sql`now()` } : {}
259
+ }).where(processWhere(scope));
260
+ }
257
261
  return true;
258
262
  });
259
263
  }
@@ -273,4 +277,4 @@ export {
273
277
  retainedProcessCancellationRequested,
274
278
  requestRetainedProcessDeadlineCancellation
275
279
  };
276
- //# sourceMappingURL=chunk-RXH5CFG3.js.map
280
+ //# sourceMappingURL=chunk-VE2XK2KD.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/retained-provider-commands.ts"],"sourcesContent":["import { and, eq, sql } from \"drizzle-orm\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport {\n SandboxProviderCommand,\n ModalRouterProviderCommand,\n CommandSupervisionReceipt,\n type SessionEvent,\n} from \"@opengeni/contracts\";\nimport { withRlsContext, withSessionActivityRlsContext, type Database } from \"./database\";\nimport { lockSessionEventWriteRows } from \"./session-control\";\nimport { appendSessionCommandOutput } from \"./session-command-output\";\nimport * as schema from \"./schema\";\n\nexport type ProcessScope = {\n accountId: string;\n workspaceId: string;\n sessionId: string;\n processId: string;\n};\n\n/** Bound to an already-authorized exact process scope by runtime wiring. */\nexport function retainedProviderCommandPersistence(\n db: Database,\n scope: ProcessScope,\n publish?: (events: SessionEvent[]) => Promise<void>,\n) {\n return {\n recordSupervisionReceipt: (receipt: SupervisionReceipt) =>\n recordRetainedSupervisionReceipt(db, scope, receipt),\n loadSupervisionReceipt: () => loadRetainedSupervisionReceipt(db, scope),\n requestCancellation: (reason: \"provider_deadline\" | \"explicit_stop\") =>\n requestRetainedProcessCancellation(db, scope, reason),\n cancellationRequested: () => retainedProcessCancellationRequested(db, scope),\n load: () => getRetainedProviderCommand(db, scope),\n acknowledge: (command: SandboxProviderCommand) =>\n acknowledgeRetainedProviderOutput(db, scope, command),\n reserveInput: (byteLength?: number) => reserveRetainedProviderInput(db, scope, byteLength),\n captureRouterPage: async (page: RetainedRouterOutputPage) => {\n const result = await captureRetainedRouterOutput(db, scope, page);\n // A committed capture is authoritative even if live fanout fails.\n if (result.events.length && publish) await publish(result.events).catch(() => undefined);\n return { command: result.command, captured: result.captured };\n },\n };\n}\n\nfunction processWhere(scope: ProcessScope) {\n return and(\n eq(schema.sandboxRetainedProcesses.accountId, scope.accountId),\n eq(schema.sandboxRetainedProcesses.workspaceId, scope.workspaceId),\n eq(schema.sandboxRetainedProcesses.sessionId, scope.sessionId),\n eq(schema.sandboxRetainedProcesses.id, scope.processId),\n );\n}\n\nfunction sameExecution(a: SandboxProviderCommand, b: SandboxProviderCommand): boolean {\n return (\n a.kind === b.kind &&\n a.sandboxId === b.sandboxId &&\n a.taskId === b.taskId &&\n a.execId === b.execId &&\n Boolean(a.pty) === Boolean(b.pty) &&\n isDeepStrictEqual(supervisionDescriptor(a), supervisionDescriptor(b))\n );\n}\n\n/** Promotion and the opaque provider locator commit together. Preserve the\n * existing durable-but-output-rejected promotion contract: its typed rejection\n * is rethrown only AFTER this outer transaction has committed the locator. */\nexport function createProviderCommandRetainer<Input extends ProcessScope, Process>(\n promote: (db: Database, input: Input) => Promise<Process>,\n recoverDurablePromotion: (error: unknown) => Process | null,\n) {\n return async function retainWorkspaceProviderCommand(\n db: Database,\n input: Input & { providerCommand?: SandboxProviderCommand | null },\n ): Promise<Process> {\n if (!input.providerCommand) return promote(db, input);\n const command = SandboxProviderCommand.parse(input.providerCommand);\n const result = await withRlsContext(db, input, async (tx) => {\n let process: Process;\n let rejection: unknown = null;\n try {\n process = await promote(tx, input);\n } catch (error) {\n const durable = recoverDurablePromotion(error);\n if (durable === null) throw error;\n process = durable;\n rejection = error;\n }\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(input))\n .for(\"update\")\n .limit(1);\n if (!row || row.providerBackend !== \"modal\" || row.providerInstanceId !== command.sandboxId)\n throw new Error(\"Provider command does not match its retained sandbox\");\n if (row.providerCommand) {\n if (!sameExecution(SandboxProviderCommand.parse(row.providerCommand), command))\n throw new Error(\"Retained process already has a different provider execution\");\n } else {\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ providerCommand: command })\n .where(processWhere(input));\n }\n return { process, rejection };\n });\n if (result.rejection) throw result.rejection;\n return result.process;\n };\n}\n\nexport async function getRetainedProviderCommand(\n db: Database,\n scope: ProcessScope,\n): Promise<SandboxProviderCommand | null> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select({ command: schema.sandboxRetainedProcesses.providerCommand })\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .limit(1);\n return row?.command ? SandboxProviderCommand.parse(row.command) : null;\n });\n}\n\n/** Call only after all output in this provider page has been durably captured.\n * A stale acknowledgment cannot roll back a cursor or erase terminal evidence. */\nexport async function acknowledgeRetainedProviderOutput(\n db: Database,\n scope: ProcessScope,\n candidate: SandboxProviderCommand,\n): Promise<SandboxProviderCommand> {\n const next = SandboxProviderCommand.parse(candidate);\n if (next.kind !== \"modal-control-v1\")\n throw new Error(\"Byte-offset command output requires atomic capture\");\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand) throw new Error(\"Retained provider command is unavailable\");\n const previous = SandboxProviderCommand.parse(row.providerCommand);\n if (previous.kind !== \"modal-control-v1\")\n throw new Error(\"Legacy acknowledgment cannot advance byte-offset output\");\n if (!sameExecution(previous, next))\n throw new Error(\"Output acknowledgment changed provider execution identity\");\n const merged = structuredClone(previous);\n for (const stream of [\"stdout\", \"stderr\"] as const) {\n const oldCursor = previous.streams[stream];\n const newCursor = next.streams[stream];\n if (newCursor.batchIndex < oldCursor.batchIndex) continue;\n if (\n (newCursor.batchIndex === oldCursor.batchIndex || oldCursor.exitCode !== null) &&\n !isDeepStrictEqual(oldCursor, newCursor)\n )\n throw new Error(\"Provider output acknowledgment conflicts with retained evidence\");\n merged.streams[stream] = newCursor;\n }\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ providerCommand: merged })\n .where(processWhere(scope));\n return merged;\n });\n}\n\nexport type RetainedRouterOutputPage = {\n expected: ModalRouterProviderCommand;\n command: ModalRouterProviderCommand;\n stdout: string;\n stderr: string;\n};\n\nexport function validateRouterOutputAdvance(page: RetainedRouterOutputPage): void {\n const before = ModalRouterProviderCommand.parse(page.expected);\n const after = ModalRouterProviderCommand.parse(page.command);\n if (!sameExecution(before, after)) throw new Error(\"Output capture changed execution identity\");\n for (const stream of [\"stdout\", \"stderr\"] as const) {\n const oldCursor = before.streams[stream],\n cursor = after.streams[stream];\n if (\n cursor.byteOffset < oldCursor.byteOffset ||\n (oldCursor.eof && (!cursor.eof || cursor.byteOffset !== oldCursor.byteOffset)) ||\n (oldCursor.exitCode !== null && !isDeepStrictEqual(oldCursor, cursor))\n )\n throw new Error(\"Output capture cannot regress or rewrite terminal evidence\");\n if (\n cursor.byteOffset === oldCursor.byteOffset &&\n !(!oldCursor.eof && cursor.eof) &&\n (page[stream].length > 0 || cursor.utf8Remainder !== oldCursor.utf8Remainder)\n )\n throw new Error(\"Output without byte advancement requires the first EOF\");\n if (cursor.eof && cursor.utf8Remainder !== \"\")\n throw new Error(\"EOF must flush pending UTF-8 bytes\");\n if (Buffer.byteLength(page[stream]) > 4 * 1024 * 1024)\n throw new Error(\"Command output page exceeds its capture bound\");\n }\n}\n\n/** Capture and offsets commit atomically. The session lock is acquired before\n * the process lock, matching the event-write lock order. A concurrent reader\n * with a stale cursor discards its page and reloads; it never appends overlapping\n * output or silently advances the other reader's cursor. */\nexport async function captureRetainedRouterOutput(\n db: Database,\n scope: ProcessScope,\n page: RetainedRouterOutputPage,\n): Promise<{ command: ModalRouterProviderCommand; events: SessionEvent[]; captured: boolean }> {\n validateRouterOutputAdvance(page);\n return await withSessionActivityRlsContext(db, scope, async (tx) => {\n const locks = await lockSessionEventWriteRows(tx, {\n workspaceId: scope.workspaceId,\n sessionIds: [scope.sessionId],\n controlLock: \"share\",\n });\n if (locks.sessions[0]?.accountId !== scope.accountId)\n throw new Error(\"Command output session not found\");\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand || row.providerBackend !== \"modal\")\n throw new Error(\"Retained provider command is unavailable\");\n const current = ModalRouterProviderCommand.parse(row.providerCommand);\n if (!sameExecution(current, page.expected))\n throw new Error(\"Output capture does not own this execution\");\n if (!isDeepStrictEqual(current, page.expected))\n return { command: current, events: [], captured: false };\n const events: SessionEvent[] = [];\n for (const stream of [\"stdout\", \"stderr\"] as const) {\n const before = current.streams[stream],\n after = page.command.streams[stream];\n if (page[stream])\n events.push(\n ...(await appendSessionCommandOutput(tx, {\n ...scope,\n commandId: scope.processId,\n stream,\n streamFidelity: current.pty ? \"merged\" : \"separate\",\n chunkId: `modal-router:${current.execId}:${stream}:${before.byteOffset}:${after.byteOffset}:${after.eof ? 1 : 0}`,\n chunk: page[stream],\n })),\n );\n }\n if (supervisionDescriptor(page.command)) {\n await tx.execute(\n sql`select set_config('opengeni.supervised_output_capture_process_id', ${scope.processId}, true)`,\n );\n }\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({\n providerCommand: page.command,\n ...(supervisionDescriptor(page.command) &&\n page.command.streams.stdout.eof &&\n page.command.streams.stderr.eof &&\n page.command.streams.stdout.exitCode === 0 &&\n page.command.streams.stderr.exitCode === 0\n ? { supervisionOutputCaptured: true }\n : {}),\n })\n .where(processWhere(scope));\n if (supervisionDescriptor(page.command)) {\n await tx.execute(\n sql`select set_config('opengeni.supervised_output_capture_process_id', '', true)`,\n );\n }\n return { command: page.command, events, captured: true };\n });\n}\n\n/** Reserve a strictly increasing provider stdin index before dispatch. An\n * ambiguous send consumes its index and is never silently replayed. */\nexport async function reserveRetainedProviderInput(\n db: Database,\n scope: ProcessScope,\n byteLength?: number,\n): Promise<number> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand || row.state !== \"active\")\n throw new Error(\"Active retained provider command is unavailable\");\n if (row.cancellationRequestedAt || row.supervisionReceipt)\n throw new Error(\"Retained command input is closed\");\n const router = row.providerCommand.kind === \"modal-router-v1\";\n if (router && (!Number.isSafeInteger(byteLength) || byteLength! <= 0))\n throw new Error(\"Byte-offset stdin requires a positive byte reservation\");\n const index = row.providerCommandInputIndex + (router ? byteLength! : 1);\n if (!Number.isSafeInteger(index)) throw new Error(\"Provider stdin sequence exhausted\");\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ providerCommandInputIndex: index })\n .where(processWhere(scope));\n return router ? row.providerCommandInputIndex : index;\n });\n}\n\nexport type SupervisionReceipt = CommandSupervisionReceipt;\n\nfunction supervisionDescriptor(command: SandboxProviderCommand) {\n return command.kind === \"modal-router-v1\" ? command.supervision : undefined;\n}\n\n/** Fail before provider start, not after discovering an unprotected DB at retention. */\nexport async function supervisedCommandProtocolReady(db: Database): Promise<boolean> {\n const [row] = await db.execute<{ ready: boolean }>(sql`\n select count(*) = 5 as ready from pg_catalog.pg_trigger\n where (tgrelid, tgname) in (\n ('sandbox_retained_processes'::regclass, 'supervised_command_guard'),\n ('sandbox_retained_processes'::regclass, 'supervised_provider_loss_commit_guard'),\n ('sandbox_workspace_mutation_admissions'::regclass, 'supervised_command_admission_guard'),\n ('sandbox_lease_holders'::regclass, 'supervised_command_holder_guard'),\n ('sandbox_leases'::regclass, 'supervised_command_capture_guard')\n ) and tgenabled in ('O', 'A') and not tgisinternal\n `);\n return row?.ready === true;\n}\n\n/** Only an authenticated control-channel receipt; never parsed stdout/stderr.\n * ACK the supervisor only after this transaction returns successfully. */\nexport async function recordRetainedSupervisionReceipt(\n db: Database,\n scope: ProcessScope,\n receipt: SupervisionReceipt,\n): Promise<void> {\n const proof = CommandSupervisionReceipt.parse(receipt);\n await withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand || !supervisionDescriptor(row.providerCommand))\n throw new Error(\"Supervised retained command is unavailable\");\n // Trigger validates invocation binding, immutable replay and unsettled input.\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ supervisionReceipt: proof })\n .where(processWhere(scope));\n });\n}\n\nexport async function loadRetainedSupervisionReceipt(\n db: Database,\n scope: ProcessScope,\n): Promise<SupervisionReceipt | null> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select({ receipt: schema.sandboxRetainedProcesses.supervisionReceipt })\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .limit(1);\n if (!row) throw new Error(\"Retained command is unavailable\");\n return row.receipt ? CommandSupervisionReceipt.parse(row.receipt) : null;\n });\n}\n\nexport async function requestRetainedProcessCancellation(\n db: Database,\n scope: ProcessScope,\n reason: \"provider_deadline\" | \"explicit_stop\",\n): Promise<void> {\n if (reason !== \"provider_deadline\" && reason !== \"explicit_stop\")\n throw new Error(\"Invalid cancellation reason\");\n await withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row) throw new Error(\"Retained command is unavailable\");\n if (row.cancellationRequestedAt || row.state !== \"active\") return;\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ cancellationRequestedAt: new Date(), cancellationReason: reason })\n .where(processWhere(scope));\n });\n}\n\nexport async function retainedProcessCancellationRequested(\n db: Database,\n scope: ProcessScope,\n): Promise<boolean> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select({ at: schema.sandboxRetainedProcesses.cancellationRequestedAt })\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .limit(1);\n if (!row) throw new Error(\"Retained command is unavailable\");\n return row.at !== null;\n });\n}\n\n/** Called by the reaper for an exact retained command. Ordinary completed turns\n * are intentionally irrelevant. Lock order remains process -> lease. */\nexport async function requestRetainedProcessDeadlineCancellation(\n db: Database,\n scope: ProcessScope,\n): Promise<boolean> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (\n !row ||\n row.state !== \"active\" ||\n !row.providerCommand ||\n !supervisionDescriptor(row.providerCommand)\n )\n return false;\n const [lease] = await tx.execute<{ id: string }>(sql`\n select id from sandbox_leases where id = ${row.leaseId}\n and account_id = ${row.accountId} and workspace_id = ${row.workspaceId}\n and sandbox_group_id = ${row.sandboxGroupId} and lease_epoch = ${row.leaseEpoch}\n and backend = ${row.providerBackend} and instance_id = ${row.providerInstanceId}\n and rotation_requested_at is not null and rotation_reason = 'provider_deadline' for update\n `);\n if (!lease) return false;\n if (!row.cancellationRequestedAt)\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ cancellationRequestedAt: new Date(), cancellationReason: \"provider_deadline\" })\n .where(processWhere(scope));\n return true;\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/retained-provider-commands.ts"],"sourcesContent":["import { and, eq, sql } from \"drizzle-orm\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport {\n SandboxProviderCommand,\n ModalRouterProviderCommand,\n CommandSupervisionReceipt,\n type SessionEvent,\n} from \"@opengeni/contracts\";\nimport { withRlsContext, withSessionActivityRlsContext, type Database } from \"./database\";\nimport { lockSessionEventWriteRows } from \"./session-control\";\nimport { appendSessionCommandOutput } from \"./session-command-output\";\nimport * as schema from \"./schema\";\n\nexport type ProcessScope = {\n accountId: string;\n workspaceId: string;\n sessionId: string;\n processId: string;\n};\n\n/** Bound to an already-authorized exact process scope by runtime wiring. */\nexport function retainedProviderCommandPersistence(\n db: Database,\n scope: ProcessScope,\n publish?: (events: SessionEvent[]) => Promise<void>,\n) {\n return {\n recordSupervisionReceipt: (receipt: SupervisionReceipt) =>\n recordRetainedSupervisionReceipt(db, scope, receipt),\n loadSupervisionReceipt: () => loadRetainedSupervisionReceipt(db, scope),\n requestCancellation: (reason: \"provider_deadline\" | \"explicit_stop\") =>\n requestRetainedProcessCancellation(db, scope, reason),\n cancellationRequested: () => retainedProcessCancellationRequested(db, scope),\n load: () => getRetainedProviderCommand(db, scope),\n acknowledge: (command: SandboxProviderCommand) =>\n acknowledgeRetainedProviderOutput(db, scope, command),\n reserveInput: (byteLength?: number) => reserveRetainedProviderInput(db, scope, byteLength),\n captureRouterPage: async (page: RetainedRouterOutputPage) => {\n const result = await captureRetainedRouterOutput(db, scope, page);\n // A committed capture is authoritative even if live fanout fails.\n if (result.events.length && publish) await publish(result.events).catch(() => undefined);\n return { command: result.command, captured: result.captured };\n },\n };\n}\n\nfunction processWhere(scope: ProcessScope) {\n return and(\n eq(schema.sandboxRetainedProcesses.accountId, scope.accountId),\n eq(schema.sandboxRetainedProcesses.workspaceId, scope.workspaceId),\n eq(schema.sandboxRetainedProcesses.sessionId, scope.sessionId),\n eq(schema.sandboxRetainedProcesses.id, scope.processId),\n );\n}\n\nfunction sameExecution(a: SandboxProviderCommand, b: SandboxProviderCommand): boolean {\n return (\n a.kind === b.kind &&\n a.sandboxId === b.sandboxId &&\n a.taskId === b.taskId &&\n a.execId === b.execId &&\n Boolean(a.pty) === Boolean(b.pty) &&\n isDeepStrictEqual(supervisionDescriptor(a), supervisionDescriptor(b))\n );\n}\n\n/** Promotion and the opaque provider locator commit together. Preserve the\n * existing durable-but-output-rejected promotion contract: its typed rejection\n * is rethrown only AFTER this outer transaction has committed the locator. */\nexport function createProviderCommandRetainer<Input extends ProcessScope, Process>(\n promote: (db: Database, input: Input) => Promise<Process>,\n recoverDurablePromotion: (error: unknown) => Process | null,\n) {\n return async function retainWorkspaceProviderCommand(\n db: Database,\n input: Input & { providerCommand?: SandboxProviderCommand | null },\n ): Promise<Process> {\n if (!input.providerCommand) return promote(db, input);\n const command = SandboxProviderCommand.parse(input.providerCommand);\n const result = await withRlsContext(db, input, async (tx) => {\n let process: Process;\n let rejection: unknown = null;\n try {\n process = await promote(tx, input);\n } catch (error) {\n const durable = recoverDurablePromotion(error);\n if (durable === null) throw error;\n process = durable;\n rejection = error;\n }\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(input))\n .for(\"update\")\n .limit(1);\n if (!row || row.providerBackend !== \"modal\" || row.providerInstanceId !== command.sandboxId)\n throw new Error(\"Provider command does not match its retained sandbox\");\n if (row.providerCommand) {\n if (!sameExecution(SandboxProviderCommand.parse(row.providerCommand), command))\n throw new Error(\"Retained process already has a different provider execution\");\n } else {\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ providerCommand: command })\n .where(processWhere(input));\n }\n return { process, rejection };\n });\n if (result.rejection) throw result.rejection;\n return result.process;\n };\n}\n\nexport async function getRetainedProviderCommand(\n db: Database,\n scope: ProcessScope,\n): Promise<SandboxProviderCommand | null> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select({ command: schema.sandboxRetainedProcesses.providerCommand })\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .limit(1);\n return row?.command ? SandboxProviderCommand.parse(row.command) : null;\n });\n}\n\n/** Call only after all output in this provider page has been durably captured.\n * A stale acknowledgment cannot roll back a cursor or erase terminal evidence. */\nexport async function acknowledgeRetainedProviderOutput(\n db: Database,\n scope: ProcessScope,\n candidate: SandboxProviderCommand,\n): Promise<SandboxProviderCommand> {\n const next = SandboxProviderCommand.parse(candidate);\n if (next.kind !== \"modal-control-v1\")\n throw new Error(\"Byte-offset command output requires atomic capture\");\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand) throw new Error(\"Retained provider command is unavailable\");\n const previous = SandboxProviderCommand.parse(row.providerCommand);\n if (previous.kind !== \"modal-control-v1\")\n throw new Error(\"Legacy acknowledgment cannot advance byte-offset output\");\n if (!sameExecution(previous, next))\n throw new Error(\"Output acknowledgment changed provider execution identity\");\n const merged = structuredClone(previous);\n for (const stream of [\"stdout\", \"stderr\"] as const) {\n const oldCursor = previous.streams[stream];\n const newCursor = next.streams[stream];\n if (newCursor.batchIndex < oldCursor.batchIndex) continue;\n if (\n (newCursor.batchIndex === oldCursor.batchIndex || oldCursor.exitCode !== null) &&\n !isDeepStrictEqual(oldCursor, newCursor)\n )\n throw new Error(\"Provider output acknowledgment conflicts with retained evidence\");\n merged.streams[stream] = newCursor;\n }\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ providerCommand: merged })\n .where(processWhere(scope));\n return merged;\n });\n}\n\nexport type RetainedRouterOutputPage = {\n expected: ModalRouterProviderCommand;\n command: ModalRouterProviderCommand;\n stdout: string;\n stderr: string;\n};\n\nexport function validateRouterOutputAdvance(page: RetainedRouterOutputPage): void {\n const before = ModalRouterProviderCommand.parse(page.expected);\n const after = ModalRouterProviderCommand.parse(page.command);\n if (!sameExecution(before, after)) throw new Error(\"Output capture changed execution identity\");\n for (const stream of [\"stdout\", \"stderr\"] as const) {\n const oldCursor = before.streams[stream],\n cursor = after.streams[stream];\n if (\n cursor.byteOffset < oldCursor.byteOffset ||\n (oldCursor.eof && (!cursor.eof || cursor.byteOffset !== oldCursor.byteOffset)) ||\n (oldCursor.exitCode !== null && !isDeepStrictEqual(oldCursor, cursor))\n )\n throw new Error(\"Output capture cannot regress or rewrite terminal evidence\");\n if (\n cursor.byteOffset === oldCursor.byteOffset &&\n !(!oldCursor.eof && cursor.eof) &&\n (page[stream].length > 0 || cursor.utf8Remainder !== oldCursor.utf8Remainder)\n )\n throw new Error(\"Output without byte advancement requires the first EOF\");\n if (cursor.eof && cursor.utf8Remainder !== \"\")\n throw new Error(\"EOF must flush pending UTF-8 bytes\");\n if (Buffer.byteLength(page[stream]) > 4 * 1024 * 1024)\n throw new Error(\"Command output page exceeds its capture bound\");\n }\n}\n\n/** Capture and offsets commit atomically. The session lock is acquired before\n * the process lock, matching the event-write lock order. A concurrent reader\n * with a stale cursor discards its page and reloads; it never appends overlapping\n * output or silently advances the other reader's cursor. */\nexport async function captureRetainedRouterOutput(\n db: Database,\n scope: ProcessScope,\n page: RetainedRouterOutputPage,\n): Promise<{ command: ModalRouterProviderCommand; events: SessionEvent[]; captured: boolean }> {\n validateRouterOutputAdvance(page);\n return await withSessionActivityRlsContext(db, scope, async (tx) => {\n const locks = await lockSessionEventWriteRows(tx, {\n workspaceId: scope.workspaceId,\n sessionIds: [scope.sessionId],\n controlLock: \"share\",\n });\n if (locks.sessions[0]?.accountId !== scope.accountId)\n throw new Error(\"Command output session not found\");\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand || row.providerBackend !== \"modal\")\n throw new Error(\"Retained provider command is unavailable\");\n const current = ModalRouterProviderCommand.parse(row.providerCommand);\n if (!sameExecution(current, page.expected))\n throw new Error(\"Output capture does not own this execution\");\n if (!isDeepStrictEqual(current, page.expected))\n return { command: current, events: [], captured: false };\n const events: SessionEvent[] = [];\n for (const stream of [\"stdout\", \"stderr\"] as const) {\n const before = current.streams[stream],\n after = page.command.streams[stream];\n if (page[stream])\n events.push(\n ...(await appendSessionCommandOutput(tx, {\n ...scope,\n commandId: scope.processId,\n stream,\n streamFidelity: current.pty ? \"merged\" : \"separate\",\n chunkId: `modal-router:${current.execId}:${stream}:${before.byteOffset}:${after.byteOffset}:${after.eof ? 1 : 0}`,\n chunk: page[stream],\n })),\n );\n }\n if (supervisionDescriptor(page.command)) {\n await tx.execute(\n sql`select set_config('opengeni.supervised_output_capture_process_id', ${scope.processId}, true)`,\n );\n }\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({\n providerCommand: page.command,\n ...(supervisionDescriptor(page.command) &&\n page.command.streams.stdout.eof &&\n page.command.streams.stderr.eof &&\n page.command.streams.stdout.exitCode === 0 &&\n page.command.streams.stderr.exitCode === 0\n ? { supervisionOutputCaptured: true }\n : {}),\n })\n .where(processWhere(scope));\n if (supervisionDescriptor(page.command)) {\n await tx.execute(\n sql`select set_config('opengeni.supervised_output_capture_process_id', '', true)`,\n );\n }\n return { command: page.command, events, captured: true };\n });\n}\n\n/** Reserve a strictly increasing provider stdin index before dispatch. An\n * ambiguous send consumes its index and is never silently replayed. */\nexport async function reserveRetainedProviderInput(\n db: Database,\n scope: ProcessScope,\n byteLength?: number,\n): Promise<number> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand || row.state !== \"active\")\n throw new Error(\"Active retained provider command is unavailable\");\n if (row.cancellationRequestedAt || row.supervisionReceipt)\n throw new Error(\"Retained command input is closed\");\n const router = row.providerCommand.kind === \"modal-router-v1\";\n if (router && (!Number.isSafeInteger(byteLength) || byteLength! <= 0))\n throw new Error(\"Byte-offset stdin requires a positive byte reservation\");\n const index = row.providerCommandInputIndex + (router ? byteLength! : 1);\n if (!Number.isSafeInteger(index)) throw new Error(\"Provider stdin sequence exhausted\");\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ providerCommandInputIndex: index })\n .where(processWhere(scope));\n return router ? row.providerCommandInputIndex : index;\n });\n}\n\nexport type SupervisionReceipt = CommandSupervisionReceipt;\n\nfunction supervisionDescriptor(command: SandboxProviderCommand) {\n return command.kind === \"modal-router-v1\" ? command.supervision : undefined;\n}\n\n/** Fail before provider start, not after discovering an unprotected DB at retention. */\nexport async function supervisedCommandProtocolReady(db: Database): Promise<boolean> {\n const [row] = await db.execute<{ ready: boolean }>(sql`\n select count(*) = 5 as ready from pg_catalog.pg_trigger\n where (tgrelid, tgname) in (\n ('sandbox_retained_processes'::regclass, 'supervised_command_guard'),\n ('sandbox_retained_processes'::regclass, 'supervised_provider_loss_commit_guard'),\n ('sandbox_workspace_mutation_admissions'::regclass, 'supervised_command_admission_guard'),\n ('sandbox_lease_holders'::regclass, 'supervised_command_holder_guard'),\n ('sandbox_leases'::regclass, 'supervised_command_capture_guard')\n ) and tgenabled in ('O', 'A') and not tgisinternal\n `);\n return row?.ready === true;\n}\n\n/** Only an authenticated control-channel receipt; never parsed stdout/stderr.\n * ACK the supervisor only after this transaction returns successfully. */\nexport async function recordRetainedSupervisionReceipt(\n db: Database,\n scope: ProcessScope,\n receipt: SupervisionReceipt,\n): Promise<void> {\n const proof = CommandSupervisionReceipt.parse(receipt);\n await withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row?.providerCommand || !supervisionDescriptor(row.providerCommand))\n throw new Error(\"Supervised retained command is unavailable\");\n // Trigger validates invocation binding, immutable replay and unsettled input.\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ supervisionReceipt: proof })\n .where(processWhere(scope));\n });\n}\n\nexport async function loadRetainedSupervisionReceipt(\n db: Database,\n scope: ProcessScope,\n): Promise<SupervisionReceipt | null> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select({ receipt: schema.sandboxRetainedProcesses.supervisionReceipt })\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .limit(1);\n if (!row) throw new Error(\"Retained command is unavailable\");\n return row.receipt ? CommandSupervisionReceipt.parse(row.receipt) : null;\n });\n}\n\nexport async function requestRetainedProcessCancellation(\n db: Database,\n scope: ProcessScope,\n reason: \"provider_deadline\" | \"explicit_stop\",\n): Promise<void> {\n if (reason !== \"provider_deadline\" && reason !== \"explicit_stop\")\n throw new Error(\"Invalid cancellation reason\");\n await withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (!row) throw new Error(\"Retained command is unavailable\");\n if (row.cancellationRequestedAt || row.state !== \"active\") return;\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({ cancellationRequestedAt: new Date(), cancellationReason: reason })\n .where(processWhere(scope));\n });\n}\n\nexport async function retainedProcessCancellationRequested(\n db: Database,\n scope: ProcessScope,\n): Promise<boolean> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select({ at: schema.sandboxRetainedProcesses.cancellationRequestedAt })\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .limit(1);\n if (!row) throw new Error(\"Retained command is unavailable\");\n return row.at !== null;\n });\n}\n\n/** Record deadline cancellation for an exact retained Modal command. Legacy\n * PTY callers send Ctrl-C first because cancellation fences stdin; non-PTY\n * callers only probe, since Ctrl-C would be data. An earlier explicit stop\n * remains immutable; the deadline uses its own clock. Lock order: process -> lease. */\nexport async function requestRetainedProcessDeadlineCancellation(\n db: Database,\n scope: ProcessScope,\n): Promise<boolean> {\n return withRlsContext(db, scope, async (tx) => {\n const [row] = await tx\n .select()\n .from(schema.sandboxRetainedProcesses)\n .where(processWhere(scope))\n .for(\"update\")\n .limit(1);\n if (\n !row ||\n row.state !== \"active\" ||\n row.providerBackend !== \"modal\" ||\n row.routeTargetId !== null\n )\n return false;\n const [lease] = await tx.execute<{ id: string }>(sql`\n select id from sandbox_leases where id = ${row.leaseId}\n and account_id = ${row.accountId} and workspace_id = ${row.workspaceId}\n and sandbox_group_id = ${row.sandboxGroupId} and lease_epoch = ${row.leaseEpoch}\n and backend = ${row.providerBackend} and instance_id = ${row.providerInstanceId}\n and rotation_requested_at is not null and rotation_reason = 'provider_deadline' for update\n `);\n if (!lease) return false;\n if (!row.deadlineCancellationRequestedAt || !row.cancellationRequestedAt) {\n await tx\n .update(schema.sandboxRetainedProcesses)\n .set({\n ...(!row.cancellationRequestedAt\n ? { cancellationRequestedAt: sql`now()`, cancellationReason: \"provider_deadline\" }\n : {}),\n ...(!row.deadlineCancellationRequestedAt\n ? { deadlineCancellationRequestedAt: sql`now()` }\n : {}),\n })\n .where(processWhere(scope));\n }\n return true;\n });\n}\n"],"mappings":"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","names":[]}
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  rawRows,
3
3
  withWorkspaceRls
4
- } from "./chunk-4CCFFLDZ.js";
4
+ } from "./chunk-BG46J5HF.js";
5
5
  import {
6
6
  sessionEvents,
7
7
  sessionTurns
8
- } from "./chunk-GSB4ISHE.js";
8
+ } from "./chunk-IIPWNNVR.js";
9
9
  import {
10
10
  LOSSLESS_JSON_STRING_PREFIX,
11
11
  fromPostgresLosslessJson
@@ -244,4 +244,4 @@ export {
244
244
  readSessionMessageSliceInScope,
245
245
  listSessionEventSlices
246
246
  };
247
- //# sourceMappingURL=chunk-FULLE5CF.js.map
247
+ //# sourceMappingURL=chunk-WT7HG43Y.js.map
@@ -2,7 +2,7 @@ import {
2
2
  rawRows,
3
3
  withRlsContext,
4
4
  withWorkspaceRls
5
- } from "./chunk-4CCFFLDZ.js";
5
+ } from "./chunk-BG46J5HF.js";
6
6
  import {
7
7
  connections,
8
8
  creditLedgerEntries,
@@ -14,7 +14,7 @@ import {
14
14
  workspaceVideoGenerationPolicies,
15
15
  workspaceVideoGenerationQuotas,
16
16
  workspaces
17
- } from "./chunk-GSB4ISHE.js";
17
+ } from "./chunk-IIPWNNVR.js";
18
18
 
19
19
  // src/video-generation.ts
20
20
  import {
@@ -1006,4 +1006,4 @@ export {
1006
1006
  mediaGenerationResultForStoredOperation,
1007
1007
  getVideoGenerationOperationSummary
1008
1008
  };
1009
- //# sourceMappingURL=chunk-I7HQL6FK.js.map
1009
+ //# sourceMappingURL=chunk-ZM5BA3TD.js.map
@@ -7,9 +7,9 @@ import {
7
7
  getConnectAttempt,
8
8
  getConnectBeginReplay,
9
9
  listPendingConnectAttempts
10
- } from "./chunk-XK6HXON2.js";
11
- import "./chunk-4CCFFLDZ.js";
12
- import "./chunk-GSB4ISHE.js";
10
+ } from "./chunk-UXFBXCHM.js";
11
+ import "./chunk-BG46J5HF.js";
12
+ import "./chunk-IIPWNNVR.js";
13
13
  import "./chunk-S2TZ3F4L.js";
14
14
  import "./chunk-PZ5AY32C.js";
15
15
  export {
@@ -31,6 +31,14 @@ export type SessionRlsActorContext = {
31
31
  initiatingHumanSubjectId?: string | null;
32
32
  };
33
33
  export declare function currentSessionAttachmentReadAccess(): SessionAttachmentReadAccess | undefined;
34
+ /**
35
+ * Stable identity of the ambient session RLS actor: every field that
36
+ * `setRlsContext` turns into database-visible GUCs or that scoped reads
37
+ * consult. Two callers with equal keys see exactly the same rows, so the key
38
+ * is safe for sharing a read result between them; `null` means no actor, in
39
+ * which case the subject GUC stays empty for every such caller.
40
+ */
41
+ export declare function currentSessionRlsActorIdentityKey(): string | null;
34
42
  export declare function withSessionRlsActorContext<T>(actor: SessionRlsActorContext, fn: () => Promise<T>): Promise<T>;
35
43
  /**
36
44
  * RLS posture for the connection OpenGeni's query layer runs over (Step I, §7.7).
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  rawRows,
3
3
  withRlsContext
4
- } from "./chunk-4CCFFLDZ.js";
5
- import "./chunk-GSB4ISHE.js";
4
+ } from "./chunk-BG46J5HF.js";
5
+ import "./chunk-IIPWNNVR.js";
6
6
  import "./chunk-S2TZ3F4L.js";
7
7
  import "./chunk-PZ5AY32C.js";
8
8
 
@@ -9,9 +9,9 @@ import {
9
9
  listEditableArtifactIdsForSession,
10
10
  touchEditableArtifactSessionLink,
11
11
  transactionallyAuthorizeEditableArtifactActor
12
- } from "./chunk-6ORJPCYW.js";
13
- import "./chunk-4CCFFLDZ.js";
14
- import "./chunk-GSB4ISHE.js";
12
+ } from "./chunk-5UOWNKPP.js";
13
+ import "./chunk-BG46J5HF.js";
14
+ import "./chunk-IIPWNNVR.js";
15
15
  import "./chunk-S2TZ3F4L.js";
16
16
  import "./chunk-PZ5AY32C.js";
17
17
  export {
package/dist/index.d.ts CHANGED
@@ -122,7 +122,7 @@ export * from "./memory-domain.js";
122
122
  export * from "./model-catalog.js";
123
123
  import { type Database, type SessionActivityDatabase } from "./database.js";
124
124
  export { createDb, registerDbBinding, retrySessionActivityRls, rlsContextForWorkspace, rlsStrategyFor, setRlsContext, setSubjectRlsContext, withAccountRls, withDatabaseStatementTimeout, withRlsContext, withSessionActivityRlsContext, withSessionActivitySavepoint, withWorkspaceRls, withWorkspaceSessionActivityRls, withWorkspaceSubjectRls, withWorkspaceSubjectSessionActivityRls, withWorkspaceUsageLock, withSandboxProviderReadLock, SandboxProviderReadLockUnavailableError, type CreateDbOptions, type Database, type DbClient, type SessionActivityDatabase, type RlsContext, type SessionRlsActorContext, type RlsStrategy, type SandboxProviderReadLockIdentity, type ManagedUserProfile, type UserLookup, type UserProfileLookup, } from "./database.js";
125
- export { withSessionRlsActorContext } from "./database.js";
125
+ export { currentSessionRlsActorIdentityKey, withSessionRlsActorContext } from "./database.js";
126
126
  export { withDatabaseTimingObserver, type DatabaseTimingObservation } from "./database-timing.js";
127
127
  export { normalizedCredentialHeaders } from "./connection-token-resolver.js";
128
128
  import { buildCodexTokenResolver as buildCodexTokenResolverCore, fetchCodexRateLimitResetCreditsForAccount as fetchCodexRateLimitResetCreditsForAccountCore, fetchCodexUsageForAccount as fetchCodexUsageForAccountCore, type CodexAccountUsageSnapshot, type CodexAuthDeps, type CodexCredentialCooldownKind, type CodexCredentialForRun } from "./codex-token-resolver.js";
@@ -843,6 +843,26 @@ export declare function applyCreditDebitUpToBalance(db: Database, input: {
843
843
  balance: BillingBalance;
844
844
  debitedMicros: number;
845
845
  }>;
846
+ /**
847
+ * Settle an already-consumed, OpenGeni-funded resource for its full cost.
848
+ * Admission happens before use; an in-flight operation can take the account
849
+ * below zero. Future grants and purchased top-ups net against that balance.
850
+ * Never use this helper to admit a new operation or for externally paid use.
851
+ */
852
+ export declare function applyCreditDebitAfterUse(db: Database, input: {
853
+ accountId: string;
854
+ workspaceId?: string | null;
855
+ type: string;
856
+ amountMicros: number;
857
+ sourceType: string;
858
+ sourceId: string;
859
+ idempotencyKey: string;
860
+ metadata?: Record<string, unknown>;
861
+ occurredAt?: Date;
862
+ }): Promise<{
863
+ balance: BillingBalance;
864
+ debitedMicros: number;
865
+ }>;
846
866
  export declare function hasCreditLedgerEntry(db: Database, accountId: string, idempotencyKey: string): Promise<boolean>;
847
867
  export declare function getBillingCustomer(db: Database, accountId: string, provider?: string): Promise<{
848
868
  accountId: string;
@@ -1820,6 +1840,9 @@ export declare function recordRetainedScreenshotArtifactError(db: Database, inpu
1820
1840
  }): Promise<void>;
1821
1841
  /** Session-qualified lookup: a screenshot from another session is indistinguishable from absent. */
1822
1842
  export declare function getRetainedScreenshotArtifact(db: Database, workspaceId: string, sessionId: string, artifactId: string): Promise<RetainedScreenshotArtifact | null>;
1843
+ /** Recover a legacy damaged receipt only when its tool call has one exact
1844
+ * screenshot in this session. Never infer an artifact across fork ancestry. */
1845
+ export declare function getRetainedScreenshotArtifactForToolCall(db: Database, workspaceId: string, sessionId: string, toolCallId: string): Promise<RetainedScreenshotArtifact | null>;
1823
1846
  export type RetainedScreenshotMaintenanceClaim = {
1824
1847
  action: "reconcile" | "delete";
1825
1848
  claimId: string;
@@ -6111,6 +6134,8 @@ export type PendingSessionToolCallInput = {
6111
6134
  * The receipt belongs to the logical turn so an approval resume can
6112
6135
  * settle it from a newer attempt. Duplicate SDK delivery converges on the
6113
6136
  * unique (turn, call) identity.
6137
+ * Pass the root database handle so each rollback retry starts a fresh
6138
+ * transaction, not a savepoint in a caller-owned transaction.
6114
6139
  */
6115
6140
  export declare function registerPendingSessionToolCall(db: Database, input: PendingSessionToolCallInput): Promise<{
6116
6141
  accepted: boolean;
@@ -6756,6 +6781,12 @@ export interface AcquireLeaseInput {
6756
6781
  * attach, mirrored into the scoped stream token's claims. */
6757
6782
  viewerAuthorityEpoch?: number;
6758
6783
  backend: string;
6784
+ /** Explicit compute policy at this admission boundary. Omission is legacy
6785
+ * usage-only; only a priced credits backend needs prepaid admission. */
6786
+ warmBilling?: {
6787
+ mode: "usage_only" | "shadow" | "credits";
6788
+ rateMicrosPerSecond: number;
6789
+ };
6759
6790
  os?: string;
6760
6791
  image?: string | null;
6761
6792
  rigVersionId?: string | null;
@@ -6872,6 +6903,24 @@ export declare class SandboxViewerAdmissionBlockedError extends Error {
6872
6903
  readonly name = "SandboxViewerAdmissionBlockedError";
6873
6904
  constructor(workspaceId: string, sandboxGroupId: string, reason: SandboxViewerForceDrainReason);
6874
6905
  }
6906
+ export declare class SandboxPaidComputeAdmissionError extends Error {
6907
+ readonly workspaceId: string;
6908
+ readonly sandboxGroupId: string;
6909
+ readonly name = "SandboxPaidComputeAdmissionError";
6910
+ constructor(workspaceId: string, sandboxGroupId: string);
6911
+ }
6912
+ /**
6913
+ * Fence the warm-time meter BEFORE requesting provider stop, including free
6914
+ * and shadow usage. The immutable cutoff survives a stop-success/settlement-
6915
+ * failure retry. A failed stop can undercount an idle retry, never count time
6916
+ * after the provider stopped.
6917
+ */
6918
+ export declare function markWarmBillingStopCutoff(db: Database, input: {
6919
+ accountId: string;
6920
+ workspaceId: string;
6921
+ sandboxGroupId: string;
6922
+ expectedEpoch: number;
6923
+ }): Promise<Date | null>;
6875
6924
  export declare class SandboxLeaseRecoveryBlockedError extends Error {
6876
6925
  readonly sandboxGroupId: string;
6877
6926
  readonly leaseEpoch: number;
@@ -7475,7 +7524,7 @@ export type LeaseHolderHeartbeatStatus = {
7475
7524
  } | {
7476
7525
  holderAlive: true;
7477
7526
  leaseExtended: false;
7478
- fence: "epoch" | "liveness" | "rotation_requested";
7527
+ fence: "epoch" | "liveness" | "rotation_requested" | "funding";
7479
7528
  };
7480
7529
  export declare function heartbeatLeaseHolderStatus(db: Database, input: {
7481
7530
  accountId: string;
@@ -7485,6 +7534,7 @@ export declare function heartbeatLeaseHolderStatus(db: Database, input: {
7485
7534
  holderId: string;
7486
7535
  leaseTtlMs: number;
7487
7536
  expectedEpoch: number;
7537
+ billingMode?: "usage_only" | "shadow" | "credits";
7488
7538
  }): Promise<LeaseHolderHeartbeatStatus>;
7489
7539
  export declare function heartbeatLeaseHolder(db: Database, input: {
7490
7540
  accountId: string;
@@ -7494,6 +7544,7 @@ export declare function heartbeatLeaseHolder(db: Database, input: {
7494
7544
  holderId: string;
7495
7545
  leaseTtlMs: number;
7496
7546
  expectedEpoch: number;
7547
+ billingMode?: "usage_only" | "shadow" | "credits";
7497
7548
  }): Promise<boolean>;
7498
7549
  export type ReinstateTurnLeaseHolderResult = {
7499
7550
  status: "present" | "reinstated";
@@ -7620,6 +7671,7 @@ export declare function reArmDrainingLease(db: Database, input: {
7620
7671
  workspaceId: string;
7621
7672
  sandboxGroupId: string;
7622
7673
  leaseTtlMs: number;
7674
+ warmBilling?: AcquireLeaseInput["warmBilling"];
7623
7675
  }): Promise<{
7624
7676
  rearmed: boolean;
7625
7677
  }>;
@@ -7713,6 +7765,9 @@ export type SandboxRetainedProcess = {
7713
7765
  reconcileClaimedAt: string | null;
7714
7766
  reconcileAttempts: number;
7715
7767
  lastReconcileOutcome: string | null;
7768
+ cancellationRequestedAt: string | null;
7769
+ cancellationReason: string | null;
7770
+ deadlineCancellationRequestedAt: string | null;
7716
7771
  reconcileProofOutcome: "exited" | "lost" | null;
7717
7772
  reconcileProofExitCode: number | null;
7718
7773
  reconcileProofReason: "provider_exit_banner" | "provider_session_lost_banner" | "provider_instance_not_found" | "provider_instance_terminated" | null;
@@ -9293,22 +9348,17 @@ export interface AccrueWarmSecondsResult {
9293
9348
  seconds: number;
9294
9349
  /** The monotonic tick index this accrual was recorded under. */
9295
9350
  tick: number;
9296
- /** usd_micros charged for this tick (0 when rate is 0). */
9351
+ /** Internal usd_micros estimate for this tick (0 in usage_only mode). */
9297
9352
  costMicros: number;
9298
9353
  }
9299
9354
  /**
9300
9355
  * Accrue warm-seconds for the elapsed wall-clock since the lease's last meter
9301
9356
  * cursor, idempotent on (sandbox_group_id, lease_epoch, tick). EPOCH-FENCED +
9302
- * liveness-guarded (warm only): a stale-epoch tick or a draining/cold lease is a
9303
- * no-op, so a superseded writer that re-fires cannot mis-meter. The FIRST tick on
9304
- * a never-metered lease (last_meter_at IS NULL) only SEEDS the cursor — it
9305
- * accrues nothing (there is no prior cursor to diff against), matching the
9306
- * "delta since last tick" contract. warmRateMicrosPerSecond > 0 also records a
9307
- * sandbox.warm_cost event (cost = seconds x rate) AND debits the same micros from
9308
- * the credit balance via applyCreditDebitUpToBalance (the model-cost precedent),
9309
- * idempotent on the SAME (group, epoch, tick) key. The usage event is the
9310
- * REQUESTED cost; the ledger is the ACTUAL debit (they legitimately differ when
9311
- * balance is low — M2). Set debitCredits:false to meter without debiting.
9357
+ * liveness-guarded (warm, or draining for a final provider-stop tick). The first
9358
+ * interval begins at the warm transition's recovery timestamp. A draining tick
9359
+ * is only allowed by the provider-stop path, never by the periodic reaper list.
9360
+ * In credits mode the signed full-cost ledger debit, both usage rows and cursor
9361
+ * commit together; a settlement failure rolls back the tick for retry.
9312
9362
  */
9313
9363
  export declare function accrueWarmSeconds(db: Database, input: {
9314
9364
  accountId: string;
@@ -9321,16 +9371,16 @@ export declare function accrueWarmSeconds(db: Database, input: {
9321
9371
  /** Optional attribution: the founding/observing session (visibility only — the
9322
9372
  * group meter key makes the workspace charge correct regardless). */
9323
9373
  subjectId?: string | null;
9324
- /** Debit credits for warm-cost (default true). The force-drain at 0 balance
9325
- * depends on this decrementing the balance. */
9326
- debitCredits?: boolean;
9374
+ billingMode?: "usage_only" | "shadow" | "credits";
9375
+ /** Final provider-stop tick, after provider termination and before cold CAS. */
9376
+ finalDrain?: boolean;
9327
9377
  }): Promise<AccrueWarmSecondsResult>;
9328
9378
  export interface ForceDrainResult {
9329
- /** Whether the workspace was over a limit (0 balance or over the warm cap). */
9379
+ /** Whether the workspace exceeded its optional warm cap. */
9330
9380
  overLimit: boolean;
9331
9381
  /** The reason, for observability. */
9332
9382
  reason: SandboxViewerForceDrainReason | null;
9333
- /** The (workspaceId, sandboxGroupId) viewer-only boxes CASed warm->draining. */
9383
+ /** The (workspaceId, sandboxGroupId) holderless boxes CASed warm->draining. */
9334
9384
  drained: {
9335
9385
  workspaceId: string;
9336
9386
  sandboxGroupId: string;
@@ -9338,8 +9388,7 @@ export interface ForceDrainResult {
9338
9388
  }
9339
9389
  export declare function forceDrainOverLimitViewerOnlyBoxes(db: Database, input: {
9340
9390
  workspaceId: string;
9341
- /** account balance gate: when <= 0 (and a billing/managed mode is on) drain. */
9342
- balanceMicros: number;
9391
+ /** Legacy caller option; balance belongs to paid admission, not draining. */
9343
9392
  enforceBalance: boolean;
9344
9393
  /** warm-second cap (cumulative this UTC month). 0 = unbounded (no cap gate). */
9345
9394
  maxWarmSecondsPerWorkspace: number;