@tangle-network/agent-app 0.43.76 → 0.44.1

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 (153) hide show
  1. package/README.md +1 -1
  2. package/dist/DesignCanvas-Z362I3R3.js +10 -0
  3. package/dist/{DesignCanvasEditor-FJIVYOA2.js → DesignCanvasEditor-ZFE2W4OJ.js} +4 -4
  4. package/dist/assets/index.d.ts +4 -4
  5. package/dist/assets/index.js +223 -11
  6. package/dist/assets/index.js.map +1 -1
  7. package/dist/assistant/index.d.ts +7 -7
  8. package/dist/assistant/index.js +3 -3
  9. package/dist/assistant/index.js.map +1 -1
  10. package/dist/{attachment-validation-Dv1A_Puy.d.ts → attachment-validation-DRuLEjAE.d.ts} +1 -1
  11. package/dist/{auth-_FU8w01b.d.ts → auth-anc7mv2W.d.ts} +2 -2
  12. package/dist/chat-routes/index.d.ts +14 -11
  13. package/dist/chat-routes/index.js +16 -17
  14. package/dist/chat-routes/index.js.map +1 -1
  15. package/dist/chat-store/index.d.ts +26 -7
  16. package/dist/chat-store/index.js +14 -5
  17. package/dist/chat-store/index.js.map +1 -1
  18. package/dist/{chunk-ROLFDOMJ.js → chunk-4TI7QWLW.js} +3 -3
  19. package/dist/{chunk-3JUTOVUH.js → chunk-6KMAM7BL.js} +1 -1
  20. package/dist/chunk-6KMAM7BL.js.map +1 -0
  21. package/dist/{chunk-WL7XHLDK.js → chunk-ICOHEZK6.js} +77 -5
  22. package/dist/chunk-ICOHEZK6.js.map +1 -0
  23. package/dist/{chunk-22GLESKE.js → chunk-O63WWYET.js} +2 -4
  24. package/dist/chunk-O63WWYET.js.map +1 -0
  25. package/dist/{chunk-E7N67FYI.js → chunk-Q3FONVAK.js} +5 -5
  26. package/dist/{chunk-E7N67FYI.js.map → chunk-Q3FONVAK.js.map} +1 -1
  27. package/dist/{chunk-RXOTWZ4G.js → chunk-TKRI463Z.js} +4 -4
  28. package/dist/chunk-TKRI463Z.js.map +1 -0
  29. package/dist/config/index.d.ts +1 -1
  30. package/dist/config/index.js +123 -4
  31. package/dist/config/index.js.map +1 -1
  32. package/dist/{contract-B3h7peV3.d.ts → contract-CEewO6DI.d.ts} +1 -1
  33. package/dist/design-canvas/index.d.ts +4 -4
  34. package/dist/design-canvas-react/index.js +7 -7
  35. package/dist/design-canvas-react/lazy.js +1 -1
  36. package/dist/eval/index.d.ts +1 -1
  37. package/dist/eval/index.js +22 -8
  38. package/dist/eval/index.js.map +1 -1
  39. package/dist/{flow-types-CqomVAUN.d.ts → flow-types-CJxEmaRy.d.ts} +1 -1
  40. package/dist/integrations/index.js +67 -5
  41. package/dist/integrations/index.js.map +1 -1
  42. package/dist/interactions/index.d.ts +6 -4
  43. package/dist/interactions/index.js +1 -2
  44. package/dist/knowledge/index.js +63 -4
  45. package/dist/knowledge/index.js.map +1 -1
  46. package/dist/knowledge-loop/index.d.ts +1 -1
  47. package/dist/knowledge-loop/index.js +94 -4
  48. package/dist/knowledge-loop/index.js.map +1 -1
  49. package/dist/{mcp-BjPqQ-3C.d.ts → mcp-D3qVzbE1.d.ts} +2 -2
  50. package/dist/{mcp-rpc-BfUOsalw.d.ts → mcp-rpc-CzU5LWWT.d.ts} +1 -1
  51. package/dist/missions/index.js +1143 -23
  52. package/dist/missions/index.js.map +1 -1
  53. package/dist/{model-DmdkIteM.d.ts → model-CdCDfBA9.d.ts} +1 -1
  54. package/dist/object-store/index.js +113 -8
  55. package/dist/object-store/index.js.map +1 -1
  56. package/dist/{parts-2ymE5cs-.d.ts → parts-Co_lFgLI.d.ts} +3 -3
  57. package/dist/platform/index.d.ts +1 -1
  58. package/dist/preflight/cli.d.ts +13 -0
  59. package/dist/preflight/cli.js +50 -0
  60. package/dist/preflight/cli.js.map +1 -0
  61. package/dist/preset-cloudflare/index.d.ts +1 -1
  62. package/dist/preset-cloudflare/index.js +284 -11
  63. package/dist/preset-cloudflare/index.js.map +1 -1
  64. package/dist/{queue-C24V13h9.d.ts → queue-CD5-_VTX.d.ts} +1 -1
  65. package/dist/redact/index.js +116 -8
  66. package/dist/redact/index.js.map +1 -1
  67. package/dist/runtime/index.d.ts +19 -111
  68. package/dist/runtime/index.js +234 -14
  69. package/dist/runtime/index.js.map +1 -1
  70. package/dist/sandbox/index.d.ts +3 -3
  71. package/dist/sandbox/index.js +2 -3
  72. package/dist/sequences/index.d.ts +4 -4
  73. package/dist/stream/index.d.ts +4 -4
  74. package/dist/{stream-normalizer-QYUl4vnl.d.ts → stream-normalizer-DnuqkZvw.d.ts} +2 -2
  75. package/dist/tangle/index.js +42 -4
  76. package/dist/tangle/index.js.map +1 -1
  77. package/dist/teams/index.js +5 -5
  78. package/dist/teams/invitations-api.js +4 -4
  79. package/dist/teams-react/index.js +3 -3
  80. package/dist/theme/index.js +0 -1
  81. package/dist/tools/index.d.ts +6 -6
  82. package/dist/tools/index.js +5 -7
  83. package/dist/trace/index.d.ts +1 -1
  84. package/dist/trace/index.js +179 -10
  85. package/dist/trace/index.js.map +1 -1
  86. package/dist/{turn-buffer-BaSgxVmK.d.ts → turn-buffer-BcPfhr1_.d.ts} +1 -1
  87. package/dist/turn-stream/index.d.ts +1 -1
  88. package/dist/{types-CBRyqijY.d.ts → types-BCxK0wyS.d.ts} +1 -1
  89. package/dist/{types-CEchbvgz.d.ts → types-XIZqIdX1.d.ts} +1 -1
  90. package/dist/web-react/index.d.ts +10 -10
  91. package/dist/web-react/index.js +5 -5
  92. package/dist/work-product/index.d.ts +4 -4
  93. package/dist/work-product/index.js +735 -15
  94. package/dist/work-product/index.js.map +1 -1
  95. package/dist/work-product-react/index.d.ts +1 -1
  96. package/dist/work-product-react/index.js.map +1 -1
  97. package/package.json +4 -19
  98. package/dist/DesignCanvas-UNL34LS2.js +0 -12
  99. package/dist/chunk-22GLESKE.js.map +0 -1
  100. package/dist/chunk-3JUTOVUH.js.map +0 -1
  101. package/dist/chunk-5EQCITY3.js +0 -20
  102. package/dist/chunk-5EQCITY3.js.map +0 -1
  103. package/dist/chunk-5SD6HT2V.js +0 -236
  104. package/dist/chunk-5SD6HT2V.js.map +0 -1
  105. package/dist/chunk-5TESA4SN.js +0 -74
  106. package/dist/chunk-5TESA4SN.js.map +0 -1
  107. package/dist/chunk-6GQ5DKFK.js +0 -920
  108. package/dist/chunk-6GQ5DKFK.js.map +0 -1
  109. package/dist/chunk-7ESQUSAC.js +0 -343
  110. package/dist/chunk-7ESQUSAC.js.map +0 -1
  111. package/dist/chunk-7LNGJDNA.js +0 -1
  112. package/dist/chunk-7LNGJDNA.js.map +0 -1
  113. package/dist/chunk-AP2UXKUK.js +0 -69
  114. package/dist/chunk-AP2UXKUK.js.map +0 -1
  115. package/dist/chunk-FA4XR66Y.js +0 -32
  116. package/dist/chunk-FA4XR66Y.js.map +0 -1
  117. package/dist/chunk-FKCB5FTD.js +0 -126
  118. package/dist/chunk-FKCB5FTD.js.map +0 -1
  119. package/dist/chunk-GPBRL2XR.js +0 -102
  120. package/dist/chunk-GPBRL2XR.js.map +0 -1
  121. package/dist/chunk-HRH7ASAG.js +0 -759
  122. package/dist/chunk-HRH7ASAG.js.map +0 -1
  123. package/dist/chunk-HYTIHYNV.js +0 -297
  124. package/dist/chunk-HYTIHYNV.js.map +0 -1
  125. package/dist/chunk-LGBO24FL.js +0 -190
  126. package/dist/chunk-LGBO24FL.js.map +0 -1
  127. package/dist/chunk-NLSSFLLS.js +0 -1168
  128. package/dist/chunk-NLSSFLLS.js.map +0 -1
  129. package/dist/chunk-OSMNYPOG.js +0 -129
  130. package/dist/chunk-OSMNYPOG.js.map +0 -1
  131. package/dist/chunk-QYOD3K56.js +0 -124
  132. package/dist/chunk-QYOD3K56.js.map +0 -1
  133. package/dist/chunk-RXOTWZ4G.js.map +0 -1
  134. package/dist/chunk-UAKFCMWK.js +0 -1
  135. package/dist/chunk-UAKFCMWK.js.map +0 -1
  136. package/dist/chunk-UKTYKG57.js +0 -48
  137. package/dist/chunk-UKTYKG57.js.map +0 -1
  138. package/dist/chunk-UOAYS72M.js +0 -80
  139. package/dist/chunk-UOAYS72M.js.map +0 -1
  140. package/dist/chunk-WL7XHLDK.js.map +0 -1
  141. package/dist/composer/index.d.ts +0 -2
  142. package/dist/composer/index.js +0 -22
  143. package/dist/composer/index.js.map +0 -1
  144. package/dist/core-7qIM7svy.d.ts +0 -21
  145. package/dist/durable-chat/index.d.ts +0 -464
  146. package/dist/durable-chat/index.js +0 -59
  147. package/dist/durable-chat/index.js.map +0 -1
  148. package/dist/index.d.ts +0 -51
  149. package/dist/index.js +0 -827
  150. package/dist/index.js.map +0 -1
  151. /package/dist/{DesignCanvas-UNL34LS2.js.map → DesignCanvas-Z362I3R3.js.map} +0 -0
  152. /package/dist/{DesignCanvasEditor-FJIVYOA2.js.map → DesignCanvasEditor-ZFE2W4OJ.js.map} +0 -0
  153. /package/dist/{chunk-ROLFDOMJ.js.map → chunk-4TI7QWLW.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/work-product/service.ts","../src/work-product/provenance.ts","../src/work-product/tools.ts","../src/work-product/route.ts"],"sourcesContent":["/**\n * The guarded work-product status machine — the `/missions` service PATTERN\n * (load → validate against a transition table → CAS-write guarded on what was\n * read → audit event) over {@link WorkProductStorePort}. Deliberately NOT a\n * reuse of the mission service: missions' cursor/plan/budget machinery does\n * not apply here, and the six-status review machine is a different contract.\n *\n * Concurrency contract: a scope's draft is driven by a single serialized turn\n * owner, so real contention is rare. The service is the typed guard layer,\n * not a serializer — every mutation re-reads the record and CAS-writes\n * guarded on the `{status, version}` it read. A guard miss surfaces as\n * `{ succeeded: false, conflict: true }` (retryable: re-read and re-apply),\n * never a silent clobber.\n */\n\nimport {\n unresolvedBlockingExceptions,\n type EvidenceEntry,\n type ExceptionEntry,\n type QualityCheck,\n type WorkProductArtifact,\n type WorkProductAuditEvent,\n type WorkProductPatch,\n type WorkProductProvenance,\n type WorkProductRecord,\n type WorkProductStatus,\n type WorkProductStorePort,\n type WorkProductVersionEntry,\n} from './types'\n\n/** Discriminated outcome for guarded operations — `conflict` distinguishes a\n * lost guarded race (retryable) from a logic rejection (illegal edge,\n * missing row — deterministic, never retried). */\nexport type WorkProductOutcome<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: string; conflict: boolean }\n\n// Legal status transitions. A target absent from a source's set is rejected\n// by the guarded helpers. `superseded` is terminal.\nconst WORK_PRODUCT_TRANSITIONS: Record<WorkProductStatus, ReadonlySet<WorkProductStatus>> = {\n draft: new Set<WorkProductStatus>(['blocked', 'ready']),\n blocked: new Set<WorkProductStatus>(['draft']),\n ready: new Set<WorkProductStatus>(['changes_requested', 'approved', 'superseded']),\n changes_requested: new Set<WorkProductStatus>(['draft', 'superseded']),\n approved: new Set<WorkProductStatus>(['superseded']),\n superseded: new Set<WorkProductStatus>(),\n}\n\n/** Whether a legal edge exists from `from` to `to` in the review machine. */\nexport function canTransitionWorkProduct(from: WorkProductStatus, to: WorkProductStatus): boolean {\n return WORK_PRODUCT_TRANSITIONS[from].has(to)\n}\n\n/** Statuses a work product can never leave. */\nexport function isWorkProductTerminal(status: WorkProductStatus): boolean {\n return WORK_PRODUCT_TRANSITIONS[status].size === 0\n}\n\n/** Define the input required to create a new draft work product row */\nexport interface CreateWorkProductInput {\n /** Explicit row id — omit to use the service's generator. The MODEL never\n * supplies ids; this is for deterministic server-side creation. */\n id?: string\n workspaceId: string\n threadId: string | null\n scopeKey: string\n /** Version this draft will become; default 1. The tool layer passes\n * `last reviewed version + 1` when a scope is re-engaged after approval. */\n version?: number\n provenance: WorkProductProvenance\n /** Opaque product-column values handed VERBATIM to the store's insert. */\n extras?: Record<string, unknown>\n}\n\n/** Payload for the draft→ready submit transition */\nexport interface SubmitWorkProductInput {\n artifact: WorkProductArtifact\n checks: QualityCheck[]\n provenance: WorkProductProvenance\n /** Frozen snapshot ref of this version's body for the history entry;\n * defaults to `artifact.path`. */\n artifactPath?: string\n}\n\n/** Reviewer verdict payload for the ready→approved / ready→changes_requested transition */\nexport interface WorkProductVerdictInput {\n verdict: 'approve' | 'request_changes'\n reviewedBy: string\n note?: string\n}\n\n/** Guarded mutation surface over the work-product store */\nexport interface WorkProductService {\n create(input: CreateWorkProductInput): Promise<WorkProductRecord>\n get(id: string): Promise<WorkProductRecord | null>\n /** The scope's open accumulator row (`draft`/`blocked`), or null. */\n openDraft(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>\n /** The scope's `changes_requested` row awaiting its correction turn, or null. */\n awaitingCorrection(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>\n /** The scope's `ready` row awaiting review, or null. */\n awaitingReview(workspaceId: string, scopeKey: string): Promise<WorkProductRecord | null>\n /** `max(version over the scope's approved/superseded rows) + 1` — the\n * version a fresh draft for the scope should carry. */\n nextVersion(workspaceId: string, scopeKey: string): Promise<number>\n /** Re-open a `changes_requested` row as the next draft: version bumps +1\n * and the correction turn accumulates into the same scope row. */\n reopen(id: string): Promise<WorkProductOutcome<WorkProductRecord>>\n /** Merge evidence entries by id (same id replaces — the agent can correct\n * itself mid-turn). Legal only while `draft`/`blocked`. */\n upsertEvidence(id: string, entries: readonly EvidenceEntry[]): Promise<WorkProductOutcome<WorkProductRecord>>\n /** Merge exception entries by id, then reconcile the blocked flag: any\n * unresolved blocking entry parks `draft`→`blocked`; resolving the last\n * one releases `blocked`→`draft`. */\n upsertExceptions(id: string, entries: readonly ExceptionEntry[]): Promise<WorkProductOutcome<WorkProductRecord>>\n /** Persist a checks array without transitioning — how a failed platform\n * gate (e.g. evidence_coverage) stays visible on the still-draft row. */\n recordChecks(id: string, checks: readonly QualityCheck[]): Promise<WorkProductOutcome<WorkProductRecord>>\n /** The terminal agent call: CAS `draft`→`ready` with artifact + checks +\n * provenance, appending the version-history entry. Refuses while an\n * unresolved blocking exception exists. */\n submit(id: string, input: SubmitWorkProductInput): Promise<WorkProductOutcome<WorkProductRecord>>\n /** Reviewer verdict: CAS `ready`→`approved`/`changes_requested` + history\n * entry. Approval also supersedes the scope's prior approved versions so\n * exactly one approved version is current per scope. */\n applyVerdict(id: string, input: WorkProductVerdictInput): Promise<WorkProductOutcome<WorkProductRecord>>\n /** Explicit replacement: CAS to `superseded` (legal from ready /\n * changes_requested / approved). */\n supersede(id: string): Promise<WorkProductOutcome<WorkProductRecord>>\n}\n\n/** Configuration options for creating a work product service */\nexport interface WorkProductServiceOptions {\n store: WorkProductStorePort\n /** Injectable clock (epoch ms). Default `Date.now`. */\n now?: () => number\n /** Row-id generator. Default `crypto.randomUUID()`. */\n generateId?: () => string\n}\n\nfunction rejected<T>(error: string): WorkProductOutcome<T> {\n return { succeeded: false, error, conflict: false }\n}\n\nfunction lostRace<T>(id: string): WorkProductOutcome<T> {\n return { succeeded: false, error: `Work product ${id} changed concurrently`, conflict: true }\n}\n\n/** Merge-by-id upsert: existing order preserved, replaced in place, new\n * entries appended in call order. */\nfunction mergeById<T extends { id: string }>(existing: readonly T[], incoming: readonly T[]): T[] {\n const merged = existing.slice()\n const indexById = new Map(merged.map((entry, index) => [entry.id, index] as const))\n for (const entry of incoming) {\n const at = indexById.get(entry.id)\n if (at === undefined) {\n indexById.set(entry.id, merged.length)\n merged.push(entry)\n } else {\n merged[at] = entry\n }\n }\n return merged\n}\n\n/** Create the guarded work-product service over a store port */\nexport function createWorkProductService(options: WorkProductServiceOptions): WorkProductService {\n const { store } = options\n const now = options.now ?? (() => Date.now())\n const generateId = options.generateId ?? (() => crypto.randomUUID())\n\n async function appendEvent(\n record: WorkProductRecord,\n step: string,\n message: string,\n metadata: Record<string, unknown> = {},\n ): Promise<void> {\n await store.appendEvent({\n workProductId: record.id,\n workspaceId: record.workspaceId,\n step,\n message,\n metadata,\n at: now(),\n })\n }\n\n // Guarded status transition: load → validate the edge → CAS guarded on the\n // {status, version} read → audit event. The loser of a racing transition\n // gets a conflict instead of silently violating the machine.\n async function transition(\n id: string,\n to: WorkProductStatus,\n patch: Omit<WorkProductPatch, 'status'> = {},\n eventMeta: Record<string, unknown> = {},\n ): Promise<WorkProductOutcome<WorkProductRecord>> {\n const record = await store.load(id)\n if (!record) return rejected(`Work product ${id} not found`)\n const from = record.status\n if (isWorkProductTerminal(from)) {\n return rejected(`Work product ${id} is terminal (${from}); cannot transition to ${to}`)\n }\n if (!canTransitionWorkProduct(from, to)) {\n return rejected(`Illegal work-product transition ${from} -> ${to} for ${id}`)\n }\n const updated = await store.update(\n id,\n { status: from, version: record.version },\n { status: to, updatedAt: now(), ...patch },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, `wp.${to}`, `Work product ${from} -> ${to}`, { from, to, ...eventMeta })\n return { succeeded: true, value: updated }\n }\n\n // Merge-style guarded write with no status change (evidence / exceptions /\n // checks accumulate on the open row).\n async function guardedMerge(\n id: string,\n legalStatuses: readonly WorkProductStatus[],\n build: (record: WorkProductRecord) => Omit<WorkProductPatch, 'status' | 'version'>,\n event: { step: string; message: (record: WorkProductRecord) => string; metadata?: (record: WorkProductRecord) => Record<string, unknown> },\n ): Promise<WorkProductOutcome<WorkProductRecord>> {\n const record = await store.load(id)\n if (!record) return rejected(`Work product ${id} not found`)\n if (!legalStatuses.includes(record.status)) {\n return rejected(`Work product ${id} is ${record.status}; expected ${legalStatuses.join('/')}`)\n }\n const updated = await store.update(\n id,\n { status: record.status, version: record.version },\n { updatedAt: now(), ...build(record) },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, event.step, event.message(updated), event.metadata?.(updated) ?? {})\n return { succeeded: true, value: updated }\n }\n\n const create: WorkProductService['create'] = async (input) => {\n const at = now()\n const record = await store.insert(\n {\n id: input.id ?? generateId(),\n workspaceId: input.workspaceId,\n threadId: input.threadId,\n scopeKey: input.scopeKey,\n status: 'draft',\n version: input.version ?? 1,\n artifact: null,\n evidence: [],\n exceptions: [],\n checks: [],\n provenance: input.provenance,\n history: [],\n createdAt: at,\n updatedAt: at,\n },\n input.extras,\n )\n await appendEvent(record, 'wp.created', `Work product draft v${record.version} created for ${record.scopeKey}`, {\n scopeKey: record.scopeKey,\n version: record.version,\n threadId: record.threadId,\n })\n return record\n }\n\n async function findByScopeAndStatus(\n workspaceId: string,\n scopeKey: string,\n status: WorkProductStatus,\n ): Promise<WorkProductRecord | null> {\n const rows = await store.listByWorkspace(workspaceId, { status: [status] })\n return rows.find((row) => row.scopeKey === scopeKey) ?? null\n }\n\n const nextVersion: WorkProductService['nextVersion'] = async (workspaceId, scopeKey) => {\n const reviewed = await store.listByWorkspace(workspaceId, { status: ['approved', 'superseded'] })\n const versions = reviewed.filter((row) => row.scopeKey === scopeKey).map((row) => row.version)\n return versions.length === 0 ? 1 : Math.max(...versions) + 1\n }\n\n const reopen: WorkProductService['reopen'] = async (id) => {\n const record = await store.load(id)\n if (!record) return rejected(`Work product ${id} not found`)\n if (record.status !== 'changes_requested') {\n return rejected(`Work product ${id} is ${record.status}; only changes_requested reopens`)\n }\n const updated = await store.update(\n id,\n { status: 'changes_requested', version: record.version },\n { status: 'draft', version: record.version + 1, updatedAt: now() },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'wp.reopened', `Correction draft v${updated.version} opened`, {\n from: record.version,\n to: updated.version,\n })\n return { succeeded: true, value: updated }\n }\n\n const upsertEvidence: WorkProductService['upsertEvidence'] = (id, entries) =>\n guardedMerge(\n id,\n ['draft', 'blocked'],\n (record) => ({ evidence: mergeById(record.evidence, entries) }),\n {\n step: 'wp.evidence',\n message: (record) => `Evidence upserted (${entries.length} entries, ${record.evidence.length} total)`,\n metadata: () => ({ upserted: entries.map((entry) => entry.id) }),\n },\n )\n\n const upsertExceptions: WorkProductService['upsertExceptions'] = async (id, entries) => {\n const merged = await guardedMerge(\n id,\n ['draft', 'blocked'],\n (record) => ({ exceptions: mergeById(record.exceptions, entries) }),\n {\n step: 'wp.exception',\n message: (record) =>\n `Exceptions upserted (${entries.length} entries, ${unresolvedBlockingExceptions(record.exceptions).length} blocking unresolved)`,\n metadata: () => ({ upserted: entries.map((entry) => entry.id) }),\n },\n )\n if (!merged.succeeded) return merged\n // Reconcile the blocked flag AFTER the merge commits: an unresolved\n // blocking entry parks the draft; resolving the last one releases it.\n const record = merged.value\n const blocking = unresolvedBlockingExceptions(record.exceptions).length\n if (record.status === 'draft' && blocking > 0) {\n return transition(id, 'blocked', {}, { blocking })\n }\n if (record.status === 'blocked' && blocking === 0) {\n return transition(id, 'draft', {}, { blocking })\n }\n return merged\n }\n\n const recordChecks: WorkProductService['recordChecks'] = (id, checks) =>\n guardedMerge(\n id,\n ['draft', 'blocked'],\n () => ({ checks: checks.slice() }),\n {\n step: 'wp.checks',\n message: () => `Checks recorded (${checks.length}, ${checks.filter((check) => !check.passed).length} failed)`,\n metadata: () => ({ failed: checks.filter((check) => !check.passed).map((check) => check.name) }),\n },\n )\n\n const submit: WorkProductService['submit'] = async (id, input) => {\n const record = await store.load(id)\n if (!record) return rejected(`Work product ${id} not found`)\n if (record.status !== 'draft') {\n return rejected(`Work product ${id} is ${record.status}; only a draft submits`)\n }\n const blocking = unresolvedBlockingExceptions(record.exceptions)\n if (blocking.length > 0) {\n return rejected(\n `Work product ${id} has ${blocking.length} unresolved blocking exception(s): ${blocking.map((entry) => entry.id).join(', ')}`,\n )\n }\n const artifactPath = input.artifactPath ?? input.artifact.path\n const entry: WorkProductVersionEntry = {\n version: record.version,\n status: 'ready',\n provenance: input.provenance,\n ...(artifactPath === undefined ? {} : { artifactPath }),\n at: now(),\n }\n return transition(\n id,\n 'ready',\n {\n artifact: input.artifact,\n checks: input.checks.slice(),\n provenance: input.provenance,\n history: [...record.history, entry],\n },\n { version: record.version, failedChecks: input.checks.filter((check) => !check.passed).length },\n )\n }\n\n const applyVerdict: WorkProductService['applyVerdict'] = async (id, input) => {\n const record = await store.load(id)\n if (!record) return rejected(`Work product ${id} not found`)\n if (record.status !== 'ready') {\n return rejected(`Work product ${id} is ${record.status}; a verdict applies only to ready`)\n }\n const to: WorkProductStatus = input.verdict === 'approve' ? 'approved' : 'changes_requested'\n const entry: WorkProductVersionEntry = {\n version: record.version,\n status: to,\n provenance: record.provenance,\n ...(record.artifact?.path === undefined ? {} : { artifactPath: record.artifact.path }),\n reviewedBy: input.reviewedBy,\n ...(input.note === undefined ? {} : { reviewNote: input.note }),\n at: now(),\n }\n const outcome = await transition(\n id,\n to,\n { history: [...record.history, entry] },\n { verdict: input.verdict, reviewedBy: input.reviewedBy },\n )\n if (!outcome.succeeded || to !== 'approved') return outcome\n // Exactly one approved version per scope: supersede prior approved rows.\n const priorApproved = await store.listByWorkspace(record.workspaceId, { status: ['approved'] })\n for (const prior of priorApproved) {\n if (prior.id === id || prior.scopeKey !== record.scopeKey) continue\n await transition(prior.id, 'superseded', {}, { supersededBy: id })\n }\n return outcome\n }\n\n return {\n create,\n get: (id) => store.load(id),\n openDraft: (workspaceId, scopeKey) => store.findDraft(workspaceId, scopeKey),\n awaitingCorrection: (workspaceId, scopeKey) => findByScopeAndStatus(workspaceId, scopeKey, 'changes_requested'),\n awaitingReview: (workspaceId, scopeKey) => findByScopeAndStatus(workspaceId, scopeKey, 'ready'),\n nextVersion,\n reopen,\n upsertEvidence,\n upsertExceptions,\n recordChecks,\n submit,\n applyVerdict,\n supersede: (id) => transition(id, 'superseded'),\n }\n}\n\n// ── in-memory store ──────────────────────────────────────────────────────────\n\n/** In-memory store surface with audit trail access and unguarded direct writes for tests */\nexport interface InMemoryWorkProductStore extends WorkProductStorePort {\n /** The full audit trail, append order. */\n events(): WorkProductAuditEvent[]\n /** Unguarded direct write — simulates a concurrent owner in tests. */\n put(record: WorkProductRecord): void\n}\n\n/**\n * In-memory {@link WorkProductStorePort} — the portable backend for tests and\n * reference assemblies. Records are deep-copied on every boundary so callers\n * can never mutate stored state around the guards.\n */\nexport function createInMemoryWorkProductStore(): InMemoryWorkProductStore {\n const rows = new Map<string, WorkProductRecord>()\n const events: WorkProductAuditEvent[] = []\n\n return {\n async load(id) {\n const record = rows.get(id)\n return record ? structuredClone(record) : null\n },\n async findDraft(workspaceId, scopeKey) {\n for (const record of rows.values()) {\n if (\n record.workspaceId === workspaceId &&\n record.scopeKey === scopeKey &&\n (record.status === 'draft' || record.status === 'blocked')\n ) {\n return structuredClone(record)\n }\n }\n return null\n },\n async listByWorkspace(workspaceId, opts) {\n const out: WorkProductRecord[] = []\n for (const record of rows.values()) {\n if (record.workspaceId !== workspaceId) continue\n if (opts?.status && !opts.status.includes(record.status)) continue\n out.push(structuredClone(record))\n }\n return out\n },\n async insert(record) {\n if (rows.has(record.id)) throw new Error(`Work product ${record.id} already exists`)\n rows.set(record.id, structuredClone(record))\n return structuredClone(record)\n },\n async update(id, guard, patch) {\n const current = rows.get(id)\n if (!current) return null\n if (guard.status !== undefined && current.status !== guard.status) return null\n if (guard.version !== undefined && current.version !== guard.version) return null\n const next: WorkProductRecord = { ...current }\n if (patch.status !== undefined) next.status = patch.status\n if (patch.version !== undefined) next.version = patch.version\n if (patch.artifact !== undefined) next.artifact = patch.artifact\n if (patch.evidence !== undefined) next.evidence = patch.evidence\n if (patch.exceptions !== undefined) next.exceptions = patch.exceptions\n if (patch.checks !== undefined) next.checks = patch.checks\n if (patch.provenance !== undefined) next.provenance = patch.provenance\n if (patch.history !== undefined) next.history = patch.history\n if (patch.updatedAt !== undefined) next.updatedAt = patch.updatedAt\n rows.set(id, structuredClone(next))\n return structuredClone(next)\n },\n async appendEvent(event) {\n events.push(structuredClone(event))\n },\n events() {\n return events.map((event) => structuredClone(event))\n },\n put(record) {\n rows.set(record.id, structuredClone(record))\n },\n }\n}\n","/**\n * Provenance stamping — the backtest spine. Every version of every work\n * product carries `profileHash + runId + servingModels`, stamped at the two\n * honest moments:\n *\n * 1. At dispatch, the product route composes the turn's profile and computes\n * agent-eval's `agentProfileHash(profile)` — the SAME hash that keys the\n * app's scorecard cells — and closes it (plus runId/sessionId) into the\n * `provenance` seam `buildWorkProductTools` receives, so the model can\n * neither omit nor forge it.\n * 2. At turn completion, the route's existing lifecycle seam calls\n * {@link finalizeWorkProductProvenance}: serving model and cost come from\n * the usage receipt, because only the completed turn knows what actually\n * served. `servingModels` is honestly EMPTY until then.\n *\n * The trust bridge ({@link workProductTrustInputs}) maps judge-sourced\n * quality verdicts into `/eval-campaign`'s `trustVerdicts()` input with\n * type-only imports — products call `trustVerdicts` from\n * `@tangle-network/agent-app/eval-campaign` (which they already import for\n * their ensemble loop). Deliberately NOT a value re-export here: it would put\n * agent-eval's runtime on `/work-product`'s import path and drag the eval\n * engine into every product worker bundle.\n */\n\nimport type { JudgeVerdict } from '@tangle-network/agent-eval'\nimport type { TrustItem } from '../eval-campaign/trust-gate'\nimport type {\n WorkProductProvenance,\n WorkProductRecord,\n WorkProductStorePort,\n} from './types'\n\nexport type { TrustItem }\n\n/** The dispatch-time provenance closure's output: everything the route knows\n * before the turn completes. */\nexport type WorkProductProvenanceBase = Omit<WorkProductProvenance, 'servingModels' | 'producedAt'>\n\n/** Stamp a full provenance from the dispatch-time base. `servingModels` starts\n * empty (honestly absent — never guessed) until the completion back-fill. */\nexport function stampProvenance(base: WorkProductProvenanceBase, now: () => number = Date.now): WorkProductProvenance {\n return { ...base, servingModels: [], producedAt: now() }\n}\n\n/** Completion receipt for one run, from the turn's usage/lifecycle seam. */\nexport interface FinalizeWorkProductProvenanceInput {\n workspaceId: string\n /** The chat turnId / mission-step run id the records were stamped with. */\n runId: string\n /** What actually served, from the usage receipt / serving-model header. */\n servingModels: readonly string[]\n costUsd?: number\n logger?: Pick<Console, 'warn'>\n}\n\n/**\n * Back-fill `servingModels`/`costUsd` onto every record (and its history\n * entries) stamped with `runId` — wire it into the chat route's existing\n * `lifecycle.onTurnComplete` seam; no new hook. Returns the updated records.\n * A CAS miss on one record is logged and skipped (the next completion or a\n * re-read re-applies); it never throws mid-fleet.\n */\nexport async function finalizeWorkProductProvenance(\n store: WorkProductStorePort,\n input: FinalizeWorkProductProvenanceInput,\n): Promise<WorkProductRecord[]> {\n const rows = await store.listByWorkspace(input.workspaceId)\n const updated: WorkProductRecord[] = []\n for (const record of rows) {\n const recordMatches = record.provenance.runId === input.runId\n const historyMatches = record.history.some((entry) => entry.provenance.runId === input.runId)\n if (!recordMatches && !historyMatches) continue\n const finalize = (provenance: WorkProductProvenance): WorkProductProvenance => ({\n ...provenance,\n servingModels: [...input.servingModels],\n ...(input.costUsd === undefined ? {} : { costUsd: input.costUsd }),\n })\n const next = await store.update(\n record.id,\n { status: record.status, version: record.version },\n {\n ...(recordMatches ? { provenance: finalize(record.provenance) } : {}),\n history: record.history.map((entry) =>\n entry.provenance.runId === input.runId ? { ...entry, provenance: finalize(entry.provenance) } : entry,\n ),\n },\n )\n if (!next) {\n input.logger?.warn(`[work-product] provenance back-fill lost a race on ${record.id}; skipped`)\n continue\n }\n await store.appendEvent({\n workProductId: record.id,\n workspaceId: record.workspaceId,\n step: 'wp.provenance',\n message: `Serving models back-filled for run ${input.runId}`,\n metadata: { runId: input.runId, servingModels: [...input.servingModels], costUsd: input.costUsd ?? null },\n at: Date.now(),\n })\n updated.push(next)\n }\n return updated\n}\n\n/**\n * Trust-gate bridge: one `TrustItem` per work product whose production\n * quality was scored by the product's eval-campaign ensemble. `verdictsFor`\n * returns the per-judge raw verdicts the product retained for a record (the\n * same verdicts `aggregateJudgeVerdicts` reduces); records without verdicts\n * are omitted. Feed the result to `/eval-campaign`'s `trustVerdicts()` —\n * an untrusted verdict renders as \"quality: unverified\", never a naked\n * number. Zero statistics code here: pure mapping.\n */\nexport function workProductTrustInputs<D extends string = string>(\n records: readonly WorkProductRecord[],\n verdictsFor: (record: WorkProductRecord) => readonly JudgeVerdict<D>[] | undefined,\n): TrustItem<D>[] {\n const items: TrustItem<D>[] = []\n for (const record of records) {\n const verdicts = verdictsFor(record)\n if (!verdicts || verdicts.length === 0) continue\n items.push({ itemId: record.id, verdicts })\n }\n return items\n}\n","/**\n * The agent-facing work-product side channel — three registry `customTools`\n * built on `/tools`' `defineAppTool`, dispatched through `dispatchAppTool`'s\n * single validation/outcome path (a thrown `ToolInputError` → correctable 4xx\n * back to the model; any other throw → internal error; a call never silently\n * succeeds without its effect). Deliberately NOT new `/tools` built-ins:\n * extending `AppToolHandlers` would break every existing consumer, and the\n * registry seam exists precisely for a product tool family like this.\n *\n * Partial-emission contract across a long turn: the draft ROW is the\n * accumulator. Evidence and exceptions stream in as found via small batched\n * calls; the artifact arrives once at the end. The agent addresses everything\n * by `scopeKey` — the server mints row ids, the model never invents them, and\n * identity (userId/workspaceId/threadId) rides the trusted `AppToolContext`\n * from headers, never model args. A turn that dies mid-emission leaves a\n * consistent draft the next turn resumes by the same scopeKey.\n */\n\nimport { ToolInputError } from '../tools/errors'\nimport { defineAppTool, type AppToolDefinition } from '../tools/registry'\nimport type { AppToolContext } from '../tools/types'\nimport { createWorkProductService, type WorkProductOutcome, type WorkProductService } from './service'\nimport { stampProvenance, type WorkProductProvenanceBase } from './provenance'\nimport {\n parseAgentCheckInput,\n parseArtifactInput,\n parseEvidenceInput,\n parseExceptionInput,\n unresolvedBlockingExceptions,\n type EvidenceEntry,\n type ExceptionEntry,\n type QualityCheck,\n type WorkProductArtifact,\n type WorkProductRecord,\n type WorkProductStorePort,\n} from './types'\n\n/** Max entries per `upsert_evidence`/`flag_exception` call — keeps each call a\n * small batch the model can correct precisely on a named-index failure. */\nexport const MAX_WORK_PRODUCT_BATCH = 50\n\n/** The shell's one generic platform check: every material target has ≥1\n * evidence row. Recorded as `QualityCheck{source:'platform'}`. */\nexport const EVIDENCE_COVERAGE_CHECK = 'evidence_coverage'\n\n/** Domain seams for the three work-product tools — every domain word is a\n * parameter; the shell bakes none. */\nexport interface WorkProductToolConfig {\n store: WorkProductStorePort\n /** PARAMETER — accepted `artifact.kind` values, validated on submit. */\n artifactKinds: readonly string[]\n /** PARAMETER — accepted exception `kind` values. */\n exceptionKinds: readonly string[]\n /** Fail-loud source check: resolve an evidence `sourceRef` to existence\n * (vault stat / attachment lookup). A dangling ref is a `ToolInputError`\n * naming the entry index — lineage can never point at nothing. */\n resolveSourceRef: (ref: string, ctx: AppToolContext) => Promise<boolean>\n /** The material targets the platform coverage check requires evidence for.\n * Product-owned vocabulary; omit to skip the coverage gate. */\n materialTargets?: (artifact: WorkProductArtifact) => string[]\n /** Per-turn provenance closure the ROUTE supplies (profileHash + runId are\n * known at dispatch; trusted, never read from model args). */\n provenance: (ctx: AppToolContext) => WorkProductProvenanceBase\n /** Called on the draft→ready commit so the route persists the transcript\n * anchor part and the queue projection updates. */\n onReady?: (record: WorkProductRecord, ctx: AppToolContext) => void | Promise<void>\n /** Injectable clock / id generator (tests, deterministic ids). */\n now?: () => number\n generateId?: () => string\n}\n\n/** Unwrap a guarded outcome, retrying ONCE on a lost race (the single\n * serialized turn owner makes real contention rare; one re-read-and-retry\n * absorbs the benign case). A deterministic rejection maps to a correctable\n * `ToolInputError` so the model learns exactly why. */\nasync function unwrap<T>(\n run: () => Promise<WorkProductOutcome<T>>,\n code: string,\n): Promise<T> {\n let outcome = await run()\n if (!outcome.succeeded && outcome.conflict) outcome = await run()\n if (!outcome.succeeded) throw new ToolInputError(code, outcome.error, outcome.conflict ? 409 : 400)\n return outcome.value\n}\n\nfunction requireScopeKey(args: Record<string, unknown>): string {\n const scopeKey = typeof args.scopeKey === 'string' ? args.scopeKey.trim() : ''\n if (!scopeKey) throw new ToolInputError('missing_scope_key', 'scopeKey is required — the engagement key this work product belongs to.')\n return scopeKey\n}\n\nfunction requireBatch(args: Record<string, unknown>, field: string): unknown[] {\n const raw = args[field]\n if (!Array.isArray(raw) || raw.length === 0) {\n throw new ToolInputError('missing_entries', `${field} must be a non-empty array.`)\n }\n if (raw.length > MAX_WORK_PRODUCT_BATCH) {\n throw new ToolInputError('batch_too_large', `${field} accepts at most ${MAX_WORK_PRODUCT_BATCH} entries per call — send smaller batches.`)\n }\n return raw\n}\n\n/**\n * Resolve the scope's open draft row, creating or reopening as needed:\n * an open `draft`/`blocked` row resumes; a `changes_requested` row reopens as\n * the correction draft (version +1); a `ready` row refuses — the package is\n * awaiting review and must receive a verdict first; otherwise a fresh draft\n * is created at `last reviewed version + 1` with dispatch-stamped provenance.\n */\nasync function resolveDraft(\n service: WorkProductService,\n config: WorkProductToolConfig,\n scopeKey: string,\n ctx: AppToolContext,\n): Promise<WorkProductRecord> {\n const open = await service.openDraft(ctx.workspaceId, scopeKey)\n if (open) return open\n const awaitingReview = await service.awaitingReview(ctx.workspaceId, scopeKey)\n if (awaitingReview) {\n throw new ToolInputError(\n 'awaiting_review',\n `Work product for ${scopeKey} (v${awaitingReview.version}) is awaiting review — no further emission until a reviewer verdict.`,\n 409,\n )\n }\n const awaitingCorrection = await service.awaitingCorrection(ctx.workspaceId, scopeKey)\n if (awaitingCorrection) {\n return unwrap(() => service.reopen(awaitingCorrection.id), 'reopen_failed')\n }\n return service.create({\n workspaceId: ctx.workspaceId,\n threadId: ctx.threadId,\n scopeKey,\n version: await service.nextVersion(ctx.workspaceId, scopeKey),\n provenance: stampProvenance(config.provenance(ctx), config.now),\n })\n}\n\n/** Build the three work-product tools for `customTools` registration on the\n * MCP server / HTTP handler / runtime executor. */\nexport function buildWorkProductTools(config: WorkProductToolConfig): AppToolDefinition[] {\n const service = createWorkProductService({\n store: config.store,\n ...(config.now ? { now: config.now } : {}),\n ...(config.generateId ? { generateId: config.generateId } : {}),\n })\n\n const upsertEvidence = defineAppTool({\n name: 'upsert_evidence',\n description:\n 'Record source→field lineage for the current work product, incrementally as you find it. Each entry links a source document (sourceRef + locator) to one artifact target and states the claim it supports. Re-emitting an entry id replaces that entry. Address the work product by scopeKey; the first call creates the draft.',\n parameters: {\n type: 'object',\n properties: {\n scopeKey: { type: 'string', description: 'Engagement key for this work product.' },\n entries: {\n type: 'array',\n minItems: 1,\n maxItems: MAX_WORK_PRODUCT_BATCH,\n items: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Stable entry id — re-emit to replace.' },\n sourceRef: { type: 'string', description: 'Vault path / attachment id of the SOURCE document.' },\n locator: {\n type: 'object',\n properties: {\n page: { type: 'number' },\n range: { type: 'string', description: \"Free-form location: 'L120-L134' | 'B7' | '¶4'.\" },\n quote: { type: 'string', description: 'Verbatim supporting quote.' },\n },\n },\n target: { type: 'string', description: 'Artifact field/claim this evidence supports.' },\n claim: { type: 'string', description: 'The value/assertion at the target.' },\n confidence: { type: 'number', minimum: 0, maximum: 1 },\n },\n required: ['id', 'sourceRef', 'target', 'claim'],\n },\n },\n },\n required: ['scopeKey', 'entries'],\n },\n async execute(args: Record<string, unknown>, ctx: AppToolContext) {\n const scopeKey = requireScopeKey(args)\n const raw = requireBatch(args, 'entries')\n const entries: EvidenceEntry[] = []\n for (let index = 0; index < raw.length; index += 1) {\n const parsed = parseEvidenceInput(raw[index], `entries[${index}]`)\n if (!parsed.ok) throw new ToolInputError('invalid_evidence', `${parsed.field}: ${parsed.error}`)\n entries.push(parsed.value)\n }\n // Fail-loud source resolution: lineage can never point at nothing.\n for (let index = 0; index < entries.length; index += 1) {\n const entry = entries[index]!\n if (!(await config.resolveSourceRef(entry.sourceRef, ctx))) {\n throw new ToolInputError(\n 'unknown_source_ref',\n `entries[${index}].sourceRef: \"${entry.sourceRef}\" does not resolve to an existing source document.`,\n )\n }\n }\n const draft = await resolveDraft(service, config, scopeKey, ctx)\n const record = await unwrap(() => service.upsertEvidence(draft.id, entries), 'evidence_rejected')\n return { workProductId: record.id, version: record.version, evidenceCount: record.evidence.length }\n },\n })\n\n const flagException = defineAppTool({\n name: 'flag_exception',\n description:\n 'Flag problems with the current work product (missing documents, inconsistent sources, …). An unresolved blocking exception parks the work product until it is resolved; re-emit the same id with resolved:true to release it. Address by scopeKey.',\n parameters: {\n type: 'object',\n properties: {\n scopeKey: { type: 'string', description: 'Engagement key for this work product.' },\n exceptions: {\n type: 'array',\n minItems: 1,\n maxItems: MAX_WORK_PRODUCT_BATCH,\n items: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Stable entry id — re-emit to replace.' },\n severity: { type: 'string', enum: ['blocking', 'material', 'advisory'] },\n kind: { type: 'string', description: 'Exception kind from the product vocabulary.' },\n message: { type: 'string' },\n targets: { type: 'array', items: { type: 'string' } },\n resolved: { type: 'boolean' },\n resolutionNote: { type: 'string' },\n },\n required: ['id', 'severity', 'kind', 'message'],\n },\n },\n },\n required: ['scopeKey', 'exceptions'],\n },\n async execute(args: Record<string, unknown>, ctx: AppToolContext) {\n const scopeKey = requireScopeKey(args)\n const raw = requireBatch(args, 'exceptions')\n const entries: ExceptionEntry[] = []\n for (let index = 0; index < raw.length; index += 1) {\n const parsed = parseExceptionInput(raw[index], `exceptions[${index}]`)\n if (!parsed.ok) throw new ToolInputError('invalid_exception', `${parsed.field}: ${parsed.error}`)\n if (!config.exceptionKinds.includes(parsed.value.kind)) {\n throw new ToolInputError(\n 'invalid_exception',\n `exceptions[${index}].kind: must be one of: ${config.exceptionKinds.join(', ')}`,\n )\n }\n // An agent resolving its own exception is tagged as such; the reviewer\n // tag is reserved for the verdict route.\n if (parsed.value.resolved && parsed.value.resolvedBy === undefined) parsed.value.resolvedBy = 'agent'\n entries.push(parsed.value)\n }\n const draft = await resolveDraft(service, config, scopeKey, ctx)\n const record = await unwrap(() => service.upsertExceptions(draft.id, entries), 'exception_rejected')\n return {\n workProductId: record.id,\n version: record.version,\n status: record.status,\n unresolvedBlocking: unresolvedBlockingExceptions(record.exceptions).length,\n }\n },\n })\n\n const submitWorkProduct = defineAppTool({\n name: 'submit_work_product',\n description:\n 'Submit the finished work product for professional review — the terminal call after evidence and exceptions are recorded. Refused while a blocking exception is unresolved, or when a material target lacks evidence. Include your own quality checks in `checks`.',\n parameters: {\n type: 'object',\n properties: {\n scopeKey: { type: 'string', description: 'Engagement key for this work product.' },\n artifact: {\n type: 'object',\n properties: {\n kind: { type: 'string', description: 'Artifact kind from the product vocabulary.' },\n title: { type: 'string' },\n path: { type: 'string', description: 'Vault/object-store ref of the rendered document.' },\n content: { type: 'string', description: 'Inline body when small (markdown/JSON).' },\n mediaType: { type: 'string' },\n baseline: {\n type: 'object',\n properties: { path: { type: 'string' }, content: { type: 'string' } },\n description: 'For diff-first artifacts: the source document being redlined.',\n },\n fields: { type: 'object', description: 'Structured field map lineage targets anchor to.' },\n },\n required: ['kind', 'title'],\n },\n checks: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n name: { type: 'string' },\n passed: { type: 'boolean' },\n detail: { type: 'string' },\n },\n required: ['id', 'name', 'passed'],\n },\n description: 'Your own quality self-checks (recorded as agent-sourced).',\n },\n },\n required: ['scopeKey', 'artifact'],\n },\n async execute(args: Record<string, unknown>, ctx: AppToolContext) {\n const scopeKey = requireScopeKey(args)\n const parsedArtifact = parseArtifactInput(args.artifact)\n if (!parsedArtifact.ok) throw new ToolInputError('invalid_artifact', `${parsedArtifact.field}: ${parsedArtifact.error}`)\n const artifact = parsedArtifact.value\n if (!config.artifactKinds.includes(artifact.kind)) {\n throw new ToolInputError('invalid_artifact', `artifact.kind: must be one of: ${config.artifactKinds.join(', ')}`)\n }\n const agentChecks: QualityCheck[] = []\n if (args.checks !== undefined) {\n if (!Array.isArray(args.checks)) throw new ToolInputError('invalid_checks', 'checks must be an array when present.')\n for (let index = 0; index < args.checks.length; index += 1) {\n const parsed = parseAgentCheckInput(args.checks[index], `checks[${index}]`)\n if (!parsed.ok) throw new ToolInputError('invalid_checks', `${parsed.field}: ${parsed.error}`)\n agentChecks.push({ ...parsed.value, source: 'agent' })\n }\n }\n\n const draft = await resolveDraft(service, config, scopeKey, ctx)\n const blocking = unresolvedBlockingExceptions(draft.exceptions)\n if (blocking.length > 0) {\n throw new ToolInputError(\n 'blocking_exceptions_unresolved',\n `Cannot submit: ${blocking.length} unresolved blocking exception(s) (${blocking.map((entry) => entry.id).join(', ')}). Resolve each via flag_exception with resolved:true, or downgrade its severity with a resolutionNote justifying why it does not block.`,\n 409,\n )\n }\n\n // The shell's ONE generic platform check: every material target has ≥1\n // evidence row. A failing coverage check is BOTH recorded on the row\n // (visible to the queue) and rejected fail-loud — a lineage-free package\n // can never reach a reviewer.\n const checks: QualityCheck[] = [...agentChecks]\n if (config.materialTargets) {\n const targets = config.materialTargets(artifact)\n const covered = new Set(draft.evidence.map((entry) => entry.target))\n const missing = targets.filter((target) => !covered.has(target))\n const coverage: QualityCheck = {\n id: EVIDENCE_COVERAGE_CHECK,\n name: EVIDENCE_COVERAGE_CHECK,\n passed: missing.length === 0,\n detail:\n missing.length === 0\n ? `${targets.length}/${targets.length} material targets evidenced`\n : `Missing evidence for: ${missing.join(', ')}`,\n source: 'platform',\n }\n checks.unshift(coverage)\n if (missing.length > 0) {\n await unwrap(() => service.recordChecks(draft.id, checks), 'checks_rejected')\n throw new ToolInputError(\n 'evidence_coverage_failed',\n `Cannot submit: material targets lack evidence: ${missing.join(', ')}. Add upsert_evidence entries targeting each, then resubmit.`,\n )\n }\n }\n\n const provenance = stampProvenance(config.provenance(ctx), config.now)\n const record = await unwrap(\n () => service.submit(draft.id, { artifact, checks, provenance }),\n 'submit_rejected',\n )\n await config.onReady?.(record, ctx)\n return {\n workProductId: record.id,\n version: record.version,\n status: record.status,\n checks: record.checks.map((check) => ({ name: check.name, passed: check.passed, source: check.source })),\n }\n },\n })\n\n return [upsertEvidence, flagException, submitWorkProduct]\n}\n","/**\n * Framework-neutral work-product review endpoints — the\n * `createInteractionAnswerRoute` factory pattern: web-standard\n * `Request`/`Response`, ONE product-supplied `authorize` seam (session auth,\n * workspace access, reviewer identity, rate limits), everything behind it\n * mechanism. Server-only and subpath-only: never reachable from a client\n * bundle (enforced by the browser-safety test).\n *\n * The verdict endpoint is deliberately NOT a second approval broker: agent\n * asks stay on `/interactions`; this is the REVIEWER's plain authorized\n * verdict on a ready package — one human-in-the-loop channel per direction.\n * A `request_changes` note re-enters chat as the correction turn via the\n * `onVerdict` seam; chat remains the driver surface.\n */\n\nimport {\n createWorkProductService,\n type WorkProductService,\n} from './service'\nimport {\n workProductToPersistedPart,\n type WorkProductPersistedPart,\n type WorkProductRecord,\n type WorkProductStorePort,\n} from './types'\n\n/** Reviewer verdict wire body for the POST endpoint. */\nexport type WorkProductVerdictBody =\n | { ok: true; id: string; verdict: 'approve'; note?: string }\n | { ok: true; id: string; verdict: 'request_changes'; note: string }\n | { ok: false; error: string }\n\n/** Validate the verdict POST body: `{ id, verdict, note? }`; a\n * `request_changes` verdict REQUIRES a non-empty note — the note IS the\n * correction turn the agent works from. */\nexport function validateWorkProductVerdictBody(body: Record<string, unknown>): WorkProductVerdictBody {\n const id = typeof body.id === 'string' && body.id.trim() ? body.id.trim() : null\n if (!id) return { ok: false, error: 'Missing work product id' }\n const verdict = body.verdict\n if (verdict !== 'approve' && verdict !== 'request_changes') {\n return { ok: false, error: 'Invalid verdict: expected approve or request_changes' }\n }\n const note = body.note === undefined ? undefined : typeof body.note === 'string' ? body.note.trim() : null\n if (note === null) return { ok: false, error: 'Invalid note: expected a string' }\n if (verdict === 'request_changes') {\n if (!note) return { ok: false, error: 'request_changes requires a note — it becomes the correction instruction in chat' }\n return { ok: true, id, verdict, note }\n }\n return note ? { ok: true, id, verdict, note } : { ok: true, id, verdict }\n}\n\n/** The product seam's verdict for one request: authenticated reviewer +\n * workspace, or a product-authored short-circuit Response (401/403/429…). */\nexport type WorkProductRouteAuthorization =\n | { ok: true; workspaceId: string; reviewedBy: string }\n | { ok: false; response: Response }\n\n/** Auth seam arguments carrying the request, the endpoint intent, and the parsed verdict body */\nexport interface WorkProductAuthorizeArgs {\n request: Request\n intent: 'list' | 'detail' | 'verdict'\n /** The parsed, validated POST body (verdict intent only). */\n body?: Record<string, unknown>\n}\n\n/** Configuration options assembling the review endpoints from the store and product seams */\nexport interface WorkProductRoutesOptions {\n store: WorkProductStorePort\n /** Authenticate + authorize the caller; the ONLY product access step. */\n authorize: (args: WorkProductAuthorizeArgs) => Promise<WorkProductRouteAuthorization>\n /** Post-verdict product seam: post the `request_changes` note into the\n * driving chat thread as the correction turn, notify, etc. Runs after the\n * transition commits; a throw is logged, never unwinds the verdict. */\n onVerdict?: (args: {\n record: WorkProductRecord\n verdict: 'approve' | 'request_changes'\n note?: string\n reviewedBy: string\n }) => void | Promise<void>\n /** Persist/update the transcript anchor part reflecting the new status, so\n * the chat card flips with the verdict. */\n persistAnchorPart?: (part: WorkProductPersistedPart, record: WorkProductRecord) => void | Promise<void>\n /** Future integration point fired on approval (push to a DMS/CRM/export\n * pipeline). A stub seam by design — export itself stays the product's\n * signed object-store download. */\n onExport?: (record: WorkProductRecord) => void | Promise<void>\n logger?: Pick<Console, 'warn' | 'error'>\n now?: () => number\n}\n\n/** Assembled review endpoints returning web-standard Responses */\nexport interface WorkProductRoutes {\n /** GET — the workspace's records for the queue projection. Optional\n * `?status=a,b` filter. */\n list: (request: Request) => Promise<Response>\n /** GET — one record by id (404 when absent or outside the workspace). */\n detail: (request: Request, id: string) => Promise<Response>\n /** POST `{ id, verdict, note? }` — the reviewer verdict: CAS transition +\n * history entry + product seams. 409 when the record is no longer ready. */\n verdict: (request: Request) => Promise<Response>\n}\n\n/** Create the work-product review endpoints over the store port and product seams */\nexport function createWorkProductRoutes(options: WorkProductRoutesOptions): WorkProductRoutes {\n const logger = options.logger ?? console\n const service: WorkProductService = createWorkProductService({\n store: options.store,\n ...(options.now ? { now: options.now } : {}),\n })\n\n async function list(request: Request): Promise<Response> {\n const auth = await options.authorize({ request, intent: 'list' })\n if (!auth.ok) return auth.response\n const url = new URL(request.url)\n const statusParam = url.searchParams.get('status')\n const statuses = statusParam\n ? statusParam.split(',').map((value) => value.trim()).filter(Boolean)\n : null\n const workProducts = await options.store.listByWorkspace(\n auth.workspaceId,\n statuses ? { status: statuses as WorkProductRecord['status'][] } : undefined,\n )\n return Response.json({ workProducts })\n }\n\n async function detail(request: Request, id: string): Promise<Response> {\n const auth = await options.authorize({ request, intent: 'detail' })\n if (!auth.ok) return auth.response\n const record = await options.store.load(id)\n if (!record || record.workspaceId !== auth.workspaceId) {\n return Response.json({ error: 'Work product not found' }, { status: 404 })\n }\n return Response.json({ workProduct: record })\n }\n\n async function verdict(request: Request): Promise<Response> {\n const body = (await request.json().catch(() => null)) as Record<string, unknown> | null\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n return Response.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const validation = validateWorkProductVerdictBody(body)\n if (!validation.ok) return Response.json({ error: validation.error }, { status: 400 })\n\n const auth = await options.authorize({ request, intent: 'verdict', body })\n if (!auth.ok) return auth.response\n\n const existing = await options.store.load(validation.id)\n if (!existing || existing.workspaceId !== auth.workspaceId) {\n return Response.json({ error: 'Work product not found' }, { status: 404 })\n }\n\n const outcome = await service.applyVerdict(validation.id, {\n verdict: validation.verdict,\n reviewedBy: auth.reviewedBy,\n ...(validation.note === undefined ? {} : { note: validation.note }),\n })\n if (!outcome.succeeded) {\n // A lost race and an illegal edge both mean \"the row is no longer the\n // ready version you looked at\" — 409 so the client re-reads.\n return Response.json({ code: 'VERDICT_CONFLICT', error: outcome.error }, { status: 409 })\n }\n const record = outcome.value\n\n // Product seams run AFTER the committed transition; their failures are\n // logged, never unwound — the verdict is durable truth at this point.\n try {\n await options.persistAnchorPart?.(workProductToPersistedPart(record), record)\n } catch (error) {\n logger.error('[work-product] persistAnchorPart failed:', error)\n }\n try {\n await options.onVerdict?.({\n record,\n verdict: validation.verdict,\n ...(validation.note === undefined ? {} : { note: validation.note }),\n reviewedBy: auth.reviewedBy,\n })\n } catch (error) {\n logger.error('[work-product] onVerdict failed:', error)\n }\n if (validation.verdict === 'approve') {\n try {\n await options.onExport?.(record)\n } catch (error) {\n logger.error('[work-product] onExport failed:', error)\n }\n }\n\n return Response.json({ ok: true, workProduct: record })\n }\n\n return { list, detail, verdict }\n}\n"],"mappings":"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","names":["record","entry"]}
@@ -1,297 +0,0 @@
1
- import {
2
- createFieldCrypto
3
- } from "./chunk-TA5Q4I2K.js";
4
- import {
5
- createWorkspaceKeyManager
6
- } from "./chunk-GXE6KHTS.js";
7
-
8
- // src/preset-cloudflare/index.ts
9
- var PRESET_TABLES = {
10
- proposals: {
11
- name: "proposals",
12
- columns: {
13
- id: "id",
14
- workspaceId: "workspace_id",
15
- threadId: "thread_id",
16
- type: "type",
17
- title: "title",
18
- description: "description",
19
- status: "status",
20
- createdBy: "created_by",
21
- createdAt: "created_at"
22
- }
23
- },
24
- knowledge: {
25
- name: "knowledge",
26
- columns: {
27
- id: "id",
28
- workspaceId: "workspace_id",
29
- path: "path",
30
- kind: "kind",
31
- label: "label",
32
- content: "content",
33
- createdAt: "created_at"
34
- }
35
- },
36
- deadlines: {
37
- name: "deadlines",
38
- columns: {
39
- id: "id",
40
- workspaceId: "workspace_id",
41
- threadId: "thread_id",
42
- title: "title",
43
- dueDate: "due_date",
44
- priority: "priority",
45
- status: "status",
46
- createdAt: "created_at"
47
- }
48
- },
49
- workspaceKeys: {
50
- name: "workspace_keys",
51
- columns: {
52
- id: "id",
53
- workspaceId: "workspace_id",
54
- keyId: "key_id",
55
- keyEncrypted: "key_encrypted",
56
- budgetUsd: "budget_usd",
57
- expiresAt: "expires_at",
58
- revokedAt: "revoked_at",
59
- createdAt: "created_at"
60
- }
61
- }
62
- };
63
- var PRESET_MIGRATION_SQL = [
64
- `CREATE TABLE IF NOT EXISTS proposals (
65
- id TEXT PRIMARY KEY,
66
- workspace_id TEXT NOT NULL,
67
- thread_id TEXT,
68
- type TEXT NOT NULL,
69
- title TEXT NOT NULL,
70
- description TEXT,
71
- status TEXT NOT NULL DEFAULT 'pending',
72
- created_by TEXT,
73
- created_at INTEGER NOT NULL
74
- )`,
75
- `CREATE TABLE IF NOT EXISTS knowledge (
76
- id TEXT PRIMARY KEY,
77
- workspace_id TEXT NOT NULL,
78
- path TEXT NOT NULL,
79
- kind TEXT NOT NULL,
80
- label TEXT,
81
- content TEXT,
82
- created_at INTEGER NOT NULL
83
- )`,
84
- `CREATE TABLE IF NOT EXISTS deadlines (
85
- id TEXT PRIMARY KEY,
86
- workspace_id TEXT NOT NULL,
87
- thread_id TEXT,
88
- title TEXT NOT NULL,
89
- due_date TEXT NOT NULL,
90
- priority TEXT,
91
- status TEXT NOT NULL DEFAULT 'scheduled',
92
- created_at INTEGER NOT NULL
93
- )`,
94
- `CREATE TABLE IF NOT EXISTS workspace_keys (
95
- id TEXT PRIMARY KEY,
96
- workspace_id TEXT NOT NULL,
97
- key_id TEXT NOT NULL,
98
- key_encrypted TEXT NOT NULL,
99
- budget_usd REAL NOT NULL,
100
- expires_at INTEGER NOT NULL,
101
- revoked_at INTEGER,
102
- created_at INTEGER NOT NULL
103
- )`,
104
- `CREATE INDEX IF NOT EXISTS idx_proposals_ws ON proposals (workspace_id, status)`,
105
- `CREATE INDEX IF NOT EXISTS idx_deadlines_ws ON deadlines (workspace_id, status)`,
106
- `CREATE INDEX IF NOT EXISTS idx_knowledge_ws ON knowledge (workspace_id)`,
107
- `CREATE INDEX IF NOT EXISTS idx_workspace_keys_ws ON workspace_keys (workspace_id, revoked_at)`
108
- ];
109
- function createPresetDrizzleSchema(d) {
110
- const { sqliteTable, text, integer, real } = d;
111
- const C = PRESET_TABLES;
112
- return {
113
- proposals: sqliteTable(C.proposals.name, {
114
- id: text(C.proposals.columns.id).primaryKey(),
115
- workspaceId: text(C.proposals.columns.workspaceId).notNull(),
116
- threadId: text(C.proposals.columns.threadId),
117
- type: text(C.proposals.columns.type).notNull(),
118
- title: text(C.proposals.columns.title).notNull(),
119
- description: text(C.proposals.columns.description),
120
- status: text(C.proposals.columns.status).notNull().default("pending"),
121
- createdBy: text(C.proposals.columns.createdBy),
122
- createdAt: integer(C.proposals.columns.createdAt).notNull()
123
- }),
124
- knowledge: sqliteTable(C.knowledge.name, {
125
- id: text(C.knowledge.columns.id).primaryKey(),
126
- workspaceId: text(C.knowledge.columns.workspaceId).notNull(),
127
- path: text(C.knowledge.columns.path).notNull(),
128
- kind: text(C.knowledge.columns.kind).notNull(),
129
- label: text(C.knowledge.columns.label),
130
- content: text(C.knowledge.columns.content),
131
- createdAt: integer(C.knowledge.columns.createdAt).notNull()
132
- }),
133
- deadlines: sqliteTable(C.deadlines.name, {
134
- id: text(C.deadlines.columns.id).primaryKey(),
135
- workspaceId: text(C.deadlines.columns.workspaceId).notNull(),
136
- threadId: text(C.deadlines.columns.threadId),
137
- title: text(C.deadlines.columns.title).notNull(),
138
- dueDate: text(C.deadlines.columns.dueDate).notNull(),
139
- priority: text(C.deadlines.columns.priority),
140
- status: text(C.deadlines.columns.status).notNull().default("scheduled"),
141
- createdAt: integer(C.deadlines.columns.createdAt).notNull()
142
- }),
143
- workspaceKeys: sqliteTable(C.workspaceKeys.name, {
144
- id: text(C.workspaceKeys.columns.id).primaryKey(),
145
- workspaceId: text(C.workspaceKeys.columns.workspaceId).notNull(),
146
- keyId: text(C.workspaceKeys.columns.keyId).notNull(),
147
- keyEncrypted: text(C.workspaceKeys.columns.keyEncrypted).notNull(),
148
- budgetUsd: real(C.workspaceKeys.columns.budgetUsd).notNull(),
149
- expiresAt: integer(C.workspaceKeys.columns.expiresAt).notNull(),
150
- revokedAt: integer(C.workspaceKeys.columns.revokedAt),
151
- createdAt: integer(C.workspaceKeys.columns.createdAt).notNull()
152
- })
153
- };
154
- }
155
- function slug(value) {
156
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "item";
157
- }
158
- function createPresetToolHandlers(opts) {
159
- const { db, vault } = opts;
160
- const newId = opts.newId ?? (() => crypto.randomUUID());
161
- const now = opts.now ?? (() => Date.now());
162
- const uiPrefix = opts.uiPathPrefix ?? "ui";
163
- const citationPrefix = opts.citationPathPrefix ?? "citations";
164
- const P = PRESET_TABLES.proposals;
165
- const D = PRESET_TABLES.deadlines;
166
- const K = PRESET_TABLES.knowledge;
167
- async function persistArtifact(path, body) {
168
- await vault.put(path, body);
169
- }
170
- async function insertKnowledge(workspaceId, path, kind, label, content) {
171
- await db.prepare(
172
- `INSERT INTO ${K.name} (${K.columns.id}, ${K.columns.workspaceId}, ${K.columns.path}, ${K.columns.kind}, ${K.columns.label}, ${K.columns.content}, ${K.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, ?)`
173
- ).bind(newId(), workspaceId, path, kind, label, content, now()).run();
174
- }
175
- return {
176
- async submitProposal(args, ctx) {
177
- const existing = await db.prepare(`SELECT ${P.columns.id} AS id FROM ${P.name} WHERE ${P.columns.workspaceId} = ? AND ${P.columns.title} = ? LIMIT 1`).bind(ctx.workspaceId, args.title).first();
178
- if (existing) return { proposalId: existing.id, deduped: true };
179
- const id = newId();
180
- await db.prepare(
181
- `INSERT INTO ${P.name} (${P.columns.id}, ${P.columns.workspaceId}, ${P.columns.threadId}, ${P.columns.type}, ${P.columns.title}, ${P.columns.description}, ${P.columns.status}, ${P.columns.createdBy}, ${P.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?)`
182
- ).bind(id, ctx.workspaceId, ctx.threadId, args.type, args.title, args.description ?? null, ctx.userId, now()).run();
183
- return { proposalId: id, deduped: false };
184
- },
185
- async scheduleFollowup(args, ctx) {
186
- const existing = await db.prepare(
187
- `SELECT ${D.columns.id} AS id, ${D.columns.dueDate} AS dueDate FROM ${D.name} WHERE ${D.columns.workspaceId} = ? AND ${D.columns.title} = ? AND ${D.columns.dueDate} = ? LIMIT 1`
188
- ).bind(ctx.workspaceId, args.title, args.dueDate).first();
189
- if (existing) return { id: existing.id, dueDate: existing.dueDate, deduped: true };
190
- const id = newId();
191
- await db.prepare(
192
- `INSERT INTO ${D.name} (${D.columns.id}, ${D.columns.workspaceId}, ${D.columns.threadId}, ${D.columns.title}, ${D.columns.dueDate}, ${D.columns.priority}, ${D.columns.status}, ${D.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'scheduled', ?)`
193
- ).bind(id, ctx.workspaceId, ctx.threadId, args.title, args.dueDate, args.priority ?? null, now()).run();
194
- return { id, dueDate: args.dueDate, deduped: false };
195
- },
196
- async renderUi(args, ctx) {
197
- const content = JSON.stringify(args.schema);
198
- const path = `${uiPrefix}/${ctx.threadId ?? "global"}/${slug(args.title)}.json`;
199
- await persistArtifact(path, content);
200
- await insertKnowledge(ctx.workspaceId, path, "ui", args.title, content);
201
- return { path, content };
202
- },
203
- async addCitation(args, ctx) {
204
- const citationId = newId();
205
- const path = `${citationPrefix}/${slug(args.label ?? args.path)}-${citationId.slice(0, 8)}.json`;
206
- const body = JSON.stringify({ sourcePath: args.path, quote: args.quote, label: args.label ?? null });
207
- await persistArtifact(path, body);
208
- await insertKnowledge(ctx.workspaceId, path, "citation", args.label ?? null, body);
209
- return { citationId, path };
210
- }
211
- };
212
- }
213
- function readDotPath(obj, path) {
214
- let cur = obj;
215
- for (const part of path.split(".")) {
216
- if (cur == null || typeof cur !== "object") return void 0;
217
- cur = cur[part];
218
- }
219
- return cur;
220
- }
221
- function createD1KnowledgeStateAccessor(opts) {
222
- const { db, workspaceId } = opts;
223
- const defaultWhere = opts.defaultWhereColumn ?? "workspace_id";
224
- const configFn = typeof opts.config === "function" ? opts.config : (path) => readDotPath(opts.config, path);
225
- const isIdentifier = (s) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s);
226
- return {
227
- config: configFn,
228
- async count(query) {
229
- if (!isIdentifier(query.table)) throw new Error(`unsafe table identifier: ${query.table}`);
230
- const whereCol = query.where ?? defaultWhere;
231
- if (!isIdentifier(whereCol)) throw new Error(`unsafe where identifier: ${whereCol}`);
232
- let sql = `SELECT count(*) AS n FROM ${query.table} WHERE ${whereCol} = ?`;
233
- const binds = [workspaceId];
234
- if (query.statusIn && query.statusIn.length > 0) {
235
- sql += ` AND status IN (${query.statusIn.map(() => "?").join(", ")})`;
236
- binds.push(...query.statusIn);
237
- }
238
- const row = await db.prepare(sql).bind(...binds).first();
239
- return row?.n ?? 0;
240
- }
241
- };
242
- }
243
- function createPresetFieldCrypto(key) {
244
- return createFieldCrypto(key);
245
- }
246
- function createPresetWorkspaceKeyStore(db) {
247
- const W = PRESET_TABLES.workspaceKeys;
248
- return {
249
- async getActive(workspaceId) {
250
- const row = await db.prepare(
251
- `SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId, ${W.columns.keyEncrypted} AS keyEncrypted, ${W.columns.budgetUsd} AS budgetUsd, ${W.columns.expiresAt} AS expiresAt FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL ORDER BY ${W.columns.createdAt} DESC LIMIT 1`
252
- ).bind(workspaceId).first();
253
- if (!row) return null;
254
- return {
255
- id: row.id,
256
- keyId: row.keyId,
257
- keyEncrypted: row.keyEncrypted,
258
- budgetUsd: row.budgetUsd,
259
- expiresAt: row.expiresAt == null ? null : new Date(row.expiresAt)
260
- };
261
- },
262
- async listActive(workspaceId) {
263
- const res = await db.prepare(`SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL`).bind(workspaceId).all();
264
- return res.results;
265
- },
266
- async insert(record) {
267
- await db.prepare(
268
- `INSERT INTO ${W.name} (${W.columns.id}, ${W.columns.workspaceId}, ${W.columns.keyId}, ${W.columns.keyEncrypted}, ${W.columns.budgetUsd}, ${W.columns.expiresAt}, ${W.columns.revokedAt}, ${W.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, NULL, ?)`
269
- ).bind(crypto.randomUUID(), record.workspaceId, record.keyId, record.keyEncrypted, record.budgetUsd, record.expiresAt.getTime(), Date.now()).run();
270
- },
271
- async markRevoked(id, now) {
272
- await db.prepare(`UPDATE ${W.name} SET ${W.columns.revokedAt} = ? WHERE ${W.columns.id} = ?`).bind(now.getTime(), id).run();
273
- }
274
- };
275
- }
276
- function createPresetWorkspaceKeyManager(opts) {
277
- return createWorkspaceKeyManager({
278
- provisioner: opts.provisioner,
279
- store: createPresetWorkspaceKeyStore(opts.db),
280
- crypto: createPresetFieldCrypto(opts.encryptionKey),
281
- defaultBudgetUsd: opts.defaultBudgetUsd,
282
- now: opts.now,
283
- product: opts.product
284
- });
285
- }
286
-
287
- export {
288
- PRESET_TABLES,
289
- PRESET_MIGRATION_SQL,
290
- createPresetDrizzleSchema,
291
- createPresetToolHandlers,
292
- createD1KnowledgeStateAccessor,
293
- createPresetFieldCrypto,
294
- createPresetWorkspaceKeyStore,
295
- createPresetWorkspaceKeyManager
296
- };
297
- //# sourceMappingURL=chunk-HYTIHYNV.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/preset-cloudflare/index.ts"],"sourcesContent":["/**\n * `@tangle-network/agent-app/preset-cloudflare` — the batteries-included default\n * stack.\n *\n * Every fleet agent runs the SAME backend: Cloudflare D1 (SQLite) through\n * Drizzle for state, a KV namespace as the artifact vault, AES-GCM field crypto\n * for PII, and per-workspace budget-capped model keys. The other agent-app\n * modules are pure SEAMS — `./tools` needs an `AppToolHandlers`, `./knowledge`\n * needs a `KnowledgeStateAccessor`, `./billing` needs a `WorkspaceKeyStore` +\n * `KeyCrypto`. This module is the ONE implementation of those seams against the\n * house stack, so a consumer that runs D1 + KV stands the whole shell up with\n * config + bindings and ZERO handler code.\n *\n * Layering:\n * - Drizzle is a PEER (the consumer installs `drizzle-orm`, never bundled). The\n * schema is therefore expressed two ways that need no import here: the plain\n * DDL ({@link PRESET_MIGRATION_SQL}) a consumer runs to create the tables, and\n * a {@link createPresetDrizzleSchema} factory that takes the consumer's\n * `drizzle-orm/sqlite-core` builder module and returns the typed tables. The\n * column names in {@link PRESET_TABLES} are the contract the handlers,\n * accessor, and DDL all agree on.\n * - D1 + KV are STRUCTURAL: {@link D1Like} (Cloudflare `D1Database` satisfies it)\n * and `KvLike` from `../web` (Cloudflare `KVNamespace` satisfies it). No\n * `@cloudflare/workers-types` dependency.\n * - Crypto/billing reuse `../crypto` + `../billing` exactly — this only wires\n * them to the D1 key table.\n */\n\nimport { createFieldCrypto } from '../crypto/index'\nimport {\n createWorkspaceKeyManager,\n type KeyCrypto,\n type KeyProvisioner,\n type WorkspaceKeyManager,\n type WorkspaceKeyRecord,\n type WorkspaceKeyStore,\n} from '../billing/index'\nimport type { KnowledgeStateAccessor } from '../knowledge/index'\nimport type {\n AddCitationArgs,\n AddCitationResult,\n AppToolContext,\n AppToolHandlers,\n RenderUiArgs,\n RenderUiResult,\n ScheduleFollowupArgs,\n ScheduleFollowupResult,\n SubmitProposalArgs,\n SubmitProposalResult,\n} from '../tools/index'\nimport type { KvLike } from '../web/index'\n\n// ---------------------------------------------------------------------------\n// D1 structural seam\n//\n// The minimal surface of Cloudflare's `D1Database` the handlers + accessor use.\n// `D1Database` satisfies it structurally, so the consumer passes `env.DB`\n// directly and tests pass an in-memory fake. We keep it to the\n// prepare/bind/first/run/all shape D1 already exposes — no Drizzle here, so the\n// default handlers run on a fresh Worker with only the D1 binding.\n// ---------------------------------------------------------------------------\n\n/** A prepared, bound D1 statement. */\nexport interface D1PreparedLike {\n bind(...values: unknown[]): D1PreparedLike\n first<T = Record<string, unknown>>(colName?: string): Promise<T | null>\n run(): Promise<unknown>\n all<T = Record<string, unknown>>(): Promise<{ results: T[] }>\n}\n\n/** The D1 surface the preset needs. Cloudflare `D1Database` satisfies it. */\nexport interface D1Like {\n prepare(query: string): D1PreparedLike\n}\n\n// ---------------------------------------------------------------------------\n// Default schema\n//\n// The four tables the default handlers + accessor read/write. Column names are\n// the single source of truth shared by the DDL, the Drizzle factory, the\n// handlers, and the accessor. Every table is workspace-scoped on `workspace_id`\n// (the accessor's default `where` column) so the knowledge `count` rule and the\n// tool writes agree without per-consumer wiring.\n//\n// Deliberately NOT here: chat thread/message tables. `/chat-store` is the\n// single thread-schema owner (`createChatTables` + `createChatStore`); the\n// preset once declared an unconsumed `threads` DDL, removed so two schemas\n// can't drift.\n// ---------------------------------------------------------------------------\n\n/** The preset table + column names — the contract the DDL, Drizzle schema,\n * handlers, and accessor share. Exposed so a consumer can reference a column\n * without a string literal. */\nexport const PRESET_TABLES = {\n proposals: {\n name: 'proposals',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n threadId: 'thread_id',\n type: 'type',\n title: 'title',\n description: 'description',\n status: 'status',\n createdBy: 'created_by',\n createdAt: 'created_at',\n },\n },\n knowledge: {\n name: 'knowledge',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n path: 'path',\n kind: 'kind',\n label: 'label',\n content: 'content',\n createdAt: 'created_at',\n },\n },\n deadlines: {\n name: 'deadlines',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n threadId: 'thread_id',\n title: 'title',\n dueDate: 'due_date',\n priority: 'priority',\n status: 'status',\n createdAt: 'created_at',\n },\n },\n workspaceKeys: {\n name: 'workspace_keys',\n columns: {\n id: 'id',\n workspaceId: 'workspace_id',\n keyId: 'key_id',\n keyEncrypted: 'key_encrypted',\n budgetUsd: 'budget_usd',\n expiresAt: 'expires_at',\n revokedAt: 'revoked_at',\n createdAt: 'created_at',\n },\n },\n} as const\n\n/**\n * Plain DDL for the preset schema — run by a consumer to create the tables with\n * ZERO drizzle (`for (const sql of PRESET_MIGRATION_SQL) await db.prepare(sql).run()`,\n * or paste into a `.sql` migration). One statement per table so D1's\n * single-statement `prepare` accepts each. Matches {@link PRESET_TABLES} exactly.\n */\nexport const PRESET_MIGRATION_SQL: readonly string[] = [\n `CREATE TABLE IF NOT EXISTS proposals (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n thread_id TEXT,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n created_by TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS knowledge (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n path TEXT NOT NULL,\n kind TEXT NOT NULL,\n label TEXT,\n content TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS deadlines (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n thread_id TEXT,\n title TEXT NOT NULL,\n due_date TEXT NOT NULL,\n priority TEXT,\n status TEXT NOT NULL DEFAULT 'scheduled',\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS workspace_keys (\n id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n key_id TEXT NOT NULL,\n key_encrypted TEXT NOT NULL,\n budget_usd REAL NOT NULL,\n expires_at INTEGER NOT NULL,\n revoked_at INTEGER,\n created_at INTEGER NOT NULL\n )`,\n `CREATE INDEX IF NOT EXISTS idx_proposals_ws ON proposals (workspace_id, status)`,\n `CREATE INDEX IF NOT EXISTS idx_deadlines_ws ON deadlines (workspace_id, status)`,\n `CREATE INDEX IF NOT EXISTS idx_knowledge_ws ON knowledge (workspace_id)`,\n `CREATE INDEX IF NOT EXISTS idx_workspace_keys_ws ON workspace_keys (workspace_id, revoked_at)`,\n]\n\n/** A chainable column builder — every modifier returns the builder so calls\n * like `.notNull().default('pending')` typecheck. The concrete drizzle builders\n * satisfy this structurally. */\nexport interface DrizzleColumnLike {\n primaryKey: () => DrizzleColumnLike\n notNull: () => DrizzleColumnLike\n default: (v: unknown) => DrizzleColumnLike\n}\n\n/** The shape of a `drizzle-orm/sqlite-core` module — the few builders the\n * preset schema uses. The consumer passes the real module; agent-app never\n * imports it (it stays a peer). */\nexport interface DrizzleSqliteCoreLike {\n sqliteTable: (name: string, columns: Record<string, DrizzleColumnLike>) => unknown\n text: (name?: string) => DrizzleColumnLike\n integer: (name?: string, config?: unknown) => DrizzleColumnLike\n real: (name?: string) => DrizzleColumnLike\n}\n\n/**\n * Build the typed Drizzle schema for the preset, given the consumer's\n * `drizzle-orm/sqlite-core` module. Returns one table object per\n * {@link PRESET_TABLES} entry — pass to `drizzle(db, { schema })` for typed\n * queries, or to drizzle-kit for migration generation. agent-app never imports\n * drizzle; the builder module is the seam.\n *\n * ```ts\n * import * as d from 'drizzle-orm/sqlite-core'\n * const schema = createPresetDrizzleSchema(d)\n * ```\n */\nexport function createPresetDrizzleSchema(d: DrizzleSqliteCoreLike) {\n const { sqliteTable, text, integer, real } = d\n const C = PRESET_TABLES\n return {\n proposals: sqliteTable(C.proposals.name, {\n id: text(C.proposals.columns.id).primaryKey(),\n workspaceId: text(C.proposals.columns.workspaceId).notNull(),\n threadId: text(C.proposals.columns.threadId),\n type: text(C.proposals.columns.type).notNull(),\n title: text(C.proposals.columns.title).notNull(),\n description: text(C.proposals.columns.description),\n status: text(C.proposals.columns.status).notNull().default('pending'),\n createdBy: text(C.proposals.columns.createdBy),\n createdAt: integer(C.proposals.columns.createdAt).notNull(),\n }),\n knowledge: sqliteTable(C.knowledge.name, {\n id: text(C.knowledge.columns.id).primaryKey(),\n workspaceId: text(C.knowledge.columns.workspaceId).notNull(),\n path: text(C.knowledge.columns.path).notNull(),\n kind: text(C.knowledge.columns.kind).notNull(),\n label: text(C.knowledge.columns.label),\n content: text(C.knowledge.columns.content),\n createdAt: integer(C.knowledge.columns.createdAt).notNull(),\n }),\n deadlines: sqliteTable(C.deadlines.name, {\n id: text(C.deadlines.columns.id).primaryKey(),\n workspaceId: text(C.deadlines.columns.workspaceId).notNull(),\n threadId: text(C.deadlines.columns.threadId),\n title: text(C.deadlines.columns.title).notNull(),\n dueDate: text(C.deadlines.columns.dueDate).notNull(),\n priority: text(C.deadlines.columns.priority),\n status: text(C.deadlines.columns.status).notNull().default('scheduled'),\n createdAt: integer(C.deadlines.columns.createdAt).notNull(),\n }),\n workspaceKeys: sqliteTable(C.workspaceKeys.name, {\n id: text(C.workspaceKeys.columns.id).primaryKey(),\n workspaceId: text(C.workspaceKeys.columns.workspaceId).notNull(),\n keyId: text(C.workspaceKeys.columns.keyId).notNull(),\n keyEncrypted: text(C.workspaceKeys.columns.keyEncrypted).notNull(),\n budgetUsd: real(C.workspaceKeys.columns.budgetUsd).notNull(),\n expiresAt: integer(C.workspaceKeys.columns.expiresAt).notNull(),\n revokedAt: integer(C.workspaceKeys.columns.revokedAt),\n createdAt: integer(C.workspaceKeys.columns.createdAt).notNull(),\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Default AppToolHandlers over D1 + KV\n// ---------------------------------------------------------------------------\n\n/** The KV-backed vault. `KvLike` (from `../web`) is the structural KV contract;\n * Cloudflare `KVNamespace` satisfies it. Artifacts are stored under their path. */\nexport type VaultKv = KvLike\n\n/** Define configuration options for handling preset tools including database, vault, and optional utilities */\nexport interface PresetToolHandlerOptions {\n /** The D1 database (Cloudflare `D1Database` satisfies {@link D1Like}). */\n db: D1Like\n /** The KV namespace used as the artifact vault. */\n vault: VaultKv\n /** Id generator. Default `crypto.randomUUID`. Injectable for deterministic tests. */\n newId?: () => string\n /** Clock (epoch ms). Default `Date.now`. Injectable for deterministic tests. */\n now?: () => number\n /** Vault path prefix for `render_ui` artifacts. Default `'ui'`. */\n uiPathPrefix?: string\n /** Vault path prefix for `add_citation` artifacts. Default `'citations'`. */\n citationPathPrefix?: string\n}\n\nfunction slug(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64) || 'item'\n}\n\n/**\n * The default {@link AppToolHandlers} for the house stack:\n * - `submit_proposal` → insert a `proposals` row (`status='pending'`), deduped\n * on (workspace, title) so a retried turn doesn't double-queue.\n * - `schedule_followup` → insert a `deadlines` row, deduped on (workspace, title, due_date).\n * - `render_ui` → write the schema JSON as a `ui/<thread>/<slug>.json`\n * vault artifact AND a `knowledge` row pointing at it.\n * - `add_citation` → write the quote as a `citations/<slug>.json` artifact AND\n * a `knowledge` row.\n *\n * Returns the EXACT persisted content from `render_ui` (per the seam contract) so\n * a completion oracle sees real bytes. Pure seam wiring: a consumer that runs\n * D1 + KV gets all four tools with no handler code.\n */\nexport function createPresetToolHandlers(opts: PresetToolHandlerOptions): AppToolHandlers {\n const { db, vault } = opts\n const newId = opts.newId ?? (() => crypto.randomUUID())\n const now = opts.now ?? (() => Date.now())\n const uiPrefix = opts.uiPathPrefix ?? 'ui'\n const citationPrefix = opts.citationPathPrefix ?? 'citations'\n const P = PRESET_TABLES.proposals\n const D = PRESET_TABLES.deadlines\n const K = PRESET_TABLES.knowledge\n\n async function persistArtifact(path: string, body: string): Promise<void> {\n await vault.put(path, body)\n }\n\n async function insertKnowledge(workspaceId: string, path: string, kind: string, label: string | null, content: string): Promise<void> {\n await db\n .prepare(\n `INSERT INTO ${K.name} (${K.columns.id}, ${K.columns.workspaceId}, ${K.columns.path}, ${K.columns.kind}, ${K.columns.label}, ${K.columns.content}, ${K.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n )\n .bind(newId(), workspaceId, path, kind, label, content, now())\n .run()\n }\n\n return {\n async submitProposal(args: SubmitProposalArgs, ctx: AppToolContext): Promise<SubmitProposalResult> {\n const existing = await db\n .prepare(`SELECT ${P.columns.id} AS id FROM ${P.name} WHERE ${P.columns.workspaceId} = ? AND ${P.columns.title} = ? LIMIT 1`)\n .bind(ctx.workspaceId, args.title)\n .first<{ id: string }>()\n if (existing) return { proposalId: existing.id, deduped: true }\n\n const id = newId()\n await db\n .prepare(\n `INSERT INTO ${P.name} (${P.columns.id}, ${P.columns.workspaceId}, ${P.columns.threadId}, ${P.columns.type}, ${P.columns.title}, ${P.columns.description}, ${P.columns.status}, ${P.columns.createdBy}, ${P.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?)`,\n )\n .bind(id, ctx.workspaceId, ctx.threadId, args.type, args.title, args.description ?? null, ctx.userId, now())\n .run()\n return { proposalId: id, deduped: false }\n },\n\n async scheduleFollowup(args: ScheduleFollowupArgs, ctx: AppToolContext): Promise<ScheduleFollowupResult> {\n const existing = await db\n .prepare(\n `SELECT ${D.columns.id} AS id, ${D.columns.dueDate} AS dueDate FROM ${D.name} WHERE ${D.columns.workspaceId} = ? AND ${D.columns.title} = ? AND ${D.columns.dueDate} = ? LIMIT 1`,\n )\n .bind(ctx.workspaceId, args.title, args.dueDate)\n .first<{ id: string; dueDate: string }>()\n if (existing) return { id: existing.id, dueDate: existing.dueDate, deduped: true }\n\n const id = newId()\n await db\n .prepare(\n `INSERT INTO ${D.name} (${D.columns.id}, ${D.columns.workspaceId}, ${D.columns.threadId}, ${D.columns.title}, ${D.columns.dueDate}, ${D.columns.priority}, ${D.columns.status}, ${D.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, 'scheduled', ?)`,\n )\n .bind(id, ctx.workspaceId, ctx.threadId, args.title, args.dueDate, args.priority ?? null, now())\n .run()\n return { id, dueDate: args.dueDate, deduped: false }\n },\n\n async renderUi(args: RenderUiArgs, ctx: AppToolContext): Promise<RenderUiResult> {\n const content = JSON.stringify(args.schema)\n const path = `${uiPrefix}/${ctx.threadId ?? 'global'}/${slug(args.title)}.json`\n await persistArtifact(path, content)\n await insertKnowledge(ctx.workspaceId, path, 'ui', args.title, content)\n return { path, content }\n },\n\n async addCitation(args: AddCitationArgs, ctx: AppToolContext): Promise<AddCitationResult> {\n const citationId = newId()\n const path = `${citationPrefix}/${slug(args.label ?? args.path)}-${citationId.slice(0, 8)}.json`\n const body = JSON.stringify({ sourcePath: args.path, quote: args.quote, label: args.label ?? null })\n await persistArtifact(path, body)\n await insertKnowledge(ctx.workspaceId, path, 'citation', args.label ?? null, body)\n return { citationId, path }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// D1-backed KnowledgeStateAccessor\n// ---------------------------------------------------------------------------\n\n/** Define options for accessing preset knowledge scoped to a specific workspace and configuration */\nexport interface PresetKnowledgeAccessorOptions {\n db: D1Like\n /** The active workspace — every `count` is scoped to it. */\n workspaceId: string\n /** Workspace config the `satisfiedBy: { config }` rules read. A resolved\n * object (dot-path lookup), or a function the accessor calls per path. */\n config: Record<string, unknown> | ((path: string) => unknown)\n /** The default workspace fk column a `count` rule scopes on when its rule\n * omits `where`. Default `'workspace_id'` (the preset schema convention). */\n defaultWhereColumn?: string\n}\n\nfunction readDotPath(obj: Record<string, unknown>, path: string): unknown {\n let cur: unknown = obj\n for (const part of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\n/**\n * The {@link KnowledgeStateAccessor} over the preset D1 schema — the seam that\n * lets the declarative `satisfiedBy` rules resolve with ZERO consumer code:\n * - `config(path)` reads the supplied workspace config by dot-path.\n * - `count({ table, where, statusIn })` runs `SELECT count(*)` scoped to the\n * active workspace (the rule's `where` column, default `workspace_id`),\n * optionally filtered to `statusIn` via a parameterized `IN (...)`.\n *\n * Identifiers (table/column) are validated against a safe pattern before\n * interpolation — they originate from the product's own config, never model\n * input, but we fail loud rather than build a malformed/injectable query.\n */\nexport function createD1KnowledgeStateAccessor(opts: PresetKnowledgeAccessorOptions): KnowledgeStateAccessor {\n const { db, workspaceId } = opts\n const defaultWhere = opts.defaultWhereColumn ?? 'workspace_id'\n const configFn = typeof opts.config === 'function' ? opts.config : (path: string) => readDotPath(opts.config as Record<string, unknown>, path)\n\n const isIdentifier = (s: string) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s)\n\n return {\n config: configFn,\n async count(query) {\n if (!isIdentifier(query.table)) throw new Error(`unsafe table identifier: ${query.table}`)\n const whereCol = query.where ?? defaultWhere\n if (!isIdentifier(whereCol)) throw new Error(`unsafe where identifier: ${whereCol}`)\n\n let sql = `SELECT count(*) AS n FROM ${query.table} WHERE ${whereCol} = ?`\n const binds: unknown[] = [workspaceId]\n if (query.statusIn && query.statusIn.length > 0) {\n sql += ` AND status IN (${query.statusIn.map(() => '?').join(', ')})`\n binds.push(...query.statusIn)\n }\n const row = await db.prepare(sql).bind(...binds).first<{ n: number }>()\n return row?.n ?? 0\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Crypto + per-workspace key wiring\n// ---------------------------------------------------------------------------\n\n/** Build the {@link KeyCrypto} the billing key store uses — AES-256-GCM field\n * crypto bound to the product's 64-char-hex `ENCRYPTION_KEY` (or a resolver).\n * This is the concrete impl behind the `../billing` `KeyCrypto` seam. */\nexport function createPresetFieldCrypto(key: string | (() => string)): KeyCrypto {\n return createFieldCrypto(key)\n}\n\n/**\n * The {@link WorkspaceKeyStore} over the preset `workspace_keys` table — the\n * persistence seam the per-workspace key manager needs. \"Active\" = a row with a\n * null `revoked_at`. Pure D1 wiring; no key minting (that's the provisioner).\n */\nexport function createPresetWorkspaceKeyStore(db: D1Like): WorkspaceKeyStore {\n const W = PRESET_TABLES.workspaceKeys\n return {\n async getActive(workspaceId: string): Promise<WorkspaceKeyRecord | null> {\n const row = await db\n .prepare(\n `SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId, ${W.columns.keyEncrypted} AS keyEncrypted, ${W.columns.budgetUsd} AS budgetUsd, ${W.columns.expiresAt} AS expiresAt FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL ORDER BY ${W.columns.createdAt} DESC LIMIT 1`,\n )\n .bind(workspaceId)\n .first<{ id: string; keyId: string; keyEncrypted: string; budgetUsd: number; expiresAt: number | null }>()\n if (!row) return null\n return {\n id: row.id,\n keyId: row.keyId,\n keyEncrypted: row.keyEncrypted,\n budgetUsd: row.budgetUsd,\n expiresAt: row.expiresAt == null ? null : new Date(row.expiresAt),\n }\n },\n async listActive(workspaceId: string): Promise<Array<{ id: string; keyId: string }>> {\n const res = await db\n .prepare(`SELECT ${W.columns.id} AS id, ${W.columns.keyId} AS keyId FROM ${W.name} WHERE ${W.columns.workspaceId} = ? AND ${W.columns.revokedAt} IS NULL`)\n .bind(workspaceId)\n .all<{ id: string; keyId: string }>()\n return res.results\n },\n async insert(record): Promise<void> {\n await db\n .prepare(\n `INSERT INTO ${W.name} (${W.columns.id}, ${W.columns.workspaceId}, ${W.columns.keyId}, ${W.columns.keyEncrypted}, ${W.columns.budgetUsd}, ${W.columns.expiresAt}, ${W.columns.revokedAt}, ${W.columns.createdAt}) VALUES (?, ?, ?, ?, ?, ?, NULL, ?)`,\n )\n .bind(crypto.randomUUID(), record.workspaceId, record.keyId, record.keyEncrypted, record.budgetUsd, record.expiresAt.getTime(), Date.now())\n .run()\n },\n async markRevoked(id: string, now: Date): Promise<void> {\n await db\n .prepare(`UPDATE ${W.name} SET ${W.columns.revokedAt} = ? WHERE ${W.columns.id} = ?`)\n .bind(now.getTime(), id)\n .run()\n },\n }\n}\n\n/** Define preset billing options including database, provisioner, encryption key, budget, and optional settings */\nexport interface PresetBillingOptions {\n db: D1Like\n /** The key provisioner (`@tangle-network/tcloud`'s client satisfies it structurally). */\n provisioner: KeyProvisioner\n /** Field-crypto key (64-char hex) or resolver — encrypts the minted key at rest. */\n encryptionKey: string | (() => string)\n /** Default monthly USD allowance when a call doesn't specify one. */\n defaultBudgetUsd: number\n /** Injectable clock. */\n now?: () => Date\n /** tcloud product the key is scoped to. Default `'router'`. */\n product?: string\n}\n\n/**\n * Stand up the per-workspace budget-capped {@link WorkspaceKeyManager} on the\n * house stack: the preset `workspace_keys` D1 store + AES-GCM field crypto +\n * the consumer's tcloud provisioner. The mint/rotate/rollover/usage LOGIC lives\n * in `../billing`; this only binds it to the preset table + crypto.\n */\nexport function createPresetWorkspaceKeyManager(opts: PresetBillingOptions): WorkspaceKeyManager {\n return createWorkspaceKeyManager({\n provisioner: opts.provisioner,\n store: createPresetWorkspaceKeyStore(opts.db),\n crypto: createPresetFieldCrypto(opts.encryptionKey),\n defaultBudgetUsd: opts.defaultBudgetUsd,\n now: opts.now,\n product: opts.product,\n })\n}\n"],"mappings":"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","names":[]}