@dudousxd/nestjs-agent-core 0.39.0 → 0.41.0

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 (40) hide show
  1. package/README.md +7 -0
  2. package/dist/ag-ui/index.cjs +123 -9
  3. package/dist/ag-ui/index.cjs.map +1 -1
  4. package/dist/ag-ui/index.d.cts +19 -3
  5. package/dist/ag-ui/index.d.ts +19 -3
  6. package/dist/ag-ui/index.js +122 -9
  7. package/dist/ag-ui/index.js.map +1 -1
  8. package/dist/{catalog-CrzetM3_.d.cts → catalog-BKS3Yxo6.d.cts} +2 -0
  9. package/dist/{catalog-CrzetM3_.d.ts → catalog-BKS3Yxo6.d.ts} +2 -0
  10. package/dist/genui/builtins.cjs +2 -0
  11. package/dist/genui/builtins.cjs.map +1 -1
  12. package/dist/genui/builtins.d.cts +1 -1
  13. package/dist/genui/builtins.d.ts +1 -1
  14. package/dist/genui/builtins.js +2 -0
  15. package/dist/genui/builtins.js.map +1 -1
  16. package/dist/genui/index.cjs +780 -106
  17. package/dist/genui/index.cjs.map +1 -1
  18. package/dist/genui/index.d.cts +5 -5
  19. package/dist/genui/index.d.ts +5 -5
  20. package/dist/genui/index.js +770 -105
  21. package/dist/genui/index.js.map +1 -1
  22. package/dist/guardrails/index.cjs +3 -1
  23. package/dist/guardrails/index.cjs.map +1 -1
  24. package/dist/guardrails/index.d.cts +5 -4
  25. package/dist/guardrails/index.d.ts +5 -4
  26. package/dist/guardrails/index.js +3 -1
  27. package/dist/guardrails/index.js.map +1 -1
  28. package/dist/index.cjs +3324 -380
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +388 -96
  31. package/dist/index.d.ts +388 -96
  32. package/dist/index.js +3279 -377
  33. package/dist/index.js.map +1 -1
  34. package/dist/{processors-6p9nrqEi.d.cts → processors-BXM-MNga.d.cts} +1 -1
  35. package/dist/{processors-Bp5XmF6V.d.ts → processors-ByDr5POu.d.ts} +1 -1
  36. package/dist/{stream-events-CbVEowYb.d.cts → stream-events-BGKLD6RX.d.ts} +664 -86
  37. package/dist/{stream-events-CbVEowYb.d.ts → stream-events-CF-6-Y6L.d.cts} +664 -86
  38. package/package.json +1 -1
  39. package/dist/tool-DZYLEKnl.d.cts +0 -121
  40. package/dist/tool-_iq4xRyk.d.ts +0 -121
package/dist/index.cjs CHANGED
@@ -53,6 +53,8 @@ __export(src_exports, {
53
53
  APPROVAL_EXPIRED_REASON: () => APPROVAL_EXPIRED_REASON,
54
54
  ASK_TOOL_DESCRIPTION: () => ASK_TOOL_DESCRIPTION,
55
55
  ASK_TOOL_NAME: () => ASK_TOOL_NAME,
56
+ ActionProposalExecutor: () => ActionProposalExecutor,
57
+ ActionProposalWorker: () => ActionProposalWorker,
56
58
  AgentRegistry: () => AgentRegistry,
57
59
  AgentStreamError: () => AgentStreamError,
58
60
  CONFIRM_JSON_SCHEMA_PROPERTIES: () => CONFIRM_JSON_SCHEMA_PROPERTIES,
@@ -67,12 +69,15 @@ __export(src_exports, {
67
69
  DEFAULT_MAX_SKILLS: () => DEFAULT_MAX_SKILLS,
68
70
  DEFAULT_REFUSAL_REASON: () => DEFAULT_REFUSAL_REASON,
69
71
  DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION: () => DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION,
72
+ DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES: () => DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES,
73
+ DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY: () => DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY,
70
74
  DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS: () => DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS,
71
75
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS: () => DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
72
76
  DefaultApprovalPolicy: () => DefaultApprovalPolicy,
73
77
  DefaultRolesPolicy: () => DefaultRolesPolicy,
74
78
  ELICITATION_INPUT_TYPES: () => ELICITATION_INPUT_TYPES,
75
79
  GLOBAL_SCOPE: () => GLOBAL_SCOPE,
80
+ InMemoryActionProposalStore: () => InMemoryActionProposalStore,
76
81
  InMemoryAgentStore: () => InMemoryAgentStore,
77
82
  InMemoryConfirmTokenStore: () => InMemoryConfirmTokenStore,
78
83
  MAX_ASK_QUESTIONS: () => MAX_ASK_QUESTIONS,
@@ -96,15 +101,25 @@ __export(src_exports, {
96
101
  THREAD_DETAIL_CONTENT_CHARS: () => THREAD_DETAIL_CONTENT_CHARS,
97
102
  ToolDisabledError: () => ToolDisabledError,
98
103
  ToolForbiddenError: () => ToolForbiddenError,
104
+ ToolInputDriftError: () => ToolInputDriftError,
99
105
  ToolInputInvalidError: () => ToolInputInvalidError,
100
106
  ToolNotFoundError: () => ToolNotFoundError,
107
+ ToolPreflightDeniedError: () => ToolPreflightDeniedError,
101
108
  ToolRegistry: () => ToolRegistry,
102
109
  UNFINISHED_TOOL_CALL: () => UNFINISHED_TOOL_CALL,
110
+ actionProposalCreationMatches: () => actionProposalCreationMatches,
111
+ actionProposalOutcomeFenceValid: () => actionProposalOutcomeFenceValid,
112
+ actionProposalOutcomeScope: () => actionProposalOutcomeScope,
113
+ actionProposalOutcomeText: () => actionProposalOutcomeText,
114
+ actionProposalReceipt: () => actionProposalReceipt,
115
+ actionProposalScopeMatches: () => actionProposalScopeMatches,
103
116
  actorScope: () => actorScope,
104
117
  agentDiagnosticKey: () => agentDiagnosticKey,
105
118
  agentFailureCode: () => agentFailureCode,
106
119
  askInputSchema: () => askInputSchema,
107
120
  askToolDefinition: () => askToolDefinition,
121
+ assertIndependentActionCapabilities: () => assertIndependentActionCapabilities,
122
+ attachActionProposalOutcome: () => attachActionProposalOutcome,
108
123
  bucketByActor: () => bucketByActor,
109
124
  bucketByModel: () => bucketByModel,
110
125
  bucketByThread: () => bucketByThread,
@@ -112,12 +127,16 @@ __export(src_exports, {
112
127
  buildMemoryBlock: () => buildMemoryBlock,
113
128
  buildSkillsBlock: () => buildSkillsBlock,
114
129
  canActorUseTool: () => canActorUseTool,
115
- canonicalJson: () => canonicalJson,
130
+ canonicalActionProposalJson: () => canonicalActionProposalJson,
131
+ canonicalJson: () => canonicalJson2,
132
+ claimActionProposalOutcome: () => claimActionProposalOutcome,
116
133
  closeEmptyRoles: () => closeEmptyRoles,
117
134
  compositeSkillProvider: () => compositeSkillProvider,
118
135
  confirmTokenExpiry: () => confirmTokenExpiry,
119
136
  createFrameBuffer: () => createFrameBuffer,
137
+ createFunctionalTool: () => createFunctionalTool,
120
138
  createIncrementalGate: () => createIncrementalGate,
139
+ createNegotiatedUiCollector: () => createNegotiatedUiCollector,
121
140
  createNoopEmitUi: () => createNoopEmitUi,
122
141
  createUiCollector: () => createUiCollector,
123
142
  danglingToolCallIds: () => danglingToolCallIds,
@@ -129,7 +148,9 @@ __export(src_exports, {
129
148
  detachedDelivered: () => detachedDelivered,
130
149
  detachedStarted: () => detachedStarted,
131
150
  detachedUnsettled: () => detachedUnsettled,
151
+ emitToolPresentations: () => emitToolPresentations,
132
152
  encodeStreamEvent: () => encodeStreamEvent,
153
+ escapeUnsafeToolUiText: () => escapeUnsafeToolUiText,
133
154
  estimateCost: () => estimateCost,
134
155
  estimateMessageTokens: () => estimateMessageTokens,
135
156
  exhaustedWindow: () => exhaustedWindow,
@@ -144,6 +165,7 @@ __export(src_exports, {
144
165
  gateFollowUps: () => gateFollowUps,
145
166
  gateTail: () => gateTail,
146
167
  hashConfirmToken: () => hashConfirmToken,
168
+ initialActionProposal: () => initialActionProposal,
147
169
  intersectAllowLists: () => intersectAllowLists,
148
170
  invokeWithTransientRetry: () => invokeWithTransientRetry,
149
171
  isChatQueueStore: () => isChatQueueStore,
@@ -162,7 +184,10 @@ __export(src_exports, {
162
184
  observeTurnFrames: () => observeTurnFrames,
163
185
  offerMemories: () => offerMemories,
164
186
  offerSkills: () => offerSkills,
187
+ parseTextActionProposalCommand: () => parseTextActionProposalCommand,
165
188
  personaCatalogEntry: () => personaCatalogEntry,
189
+ prepareActionProposal: () => prepareActionProposal,
190
+ ptBrActionProposalText: () => ptBrActionProposalText,
166
191
  publishAgentDelegated: () => publishAgentDelegated,
167
192
  publishAgentMemoryResolved: () => publishAgentMemoryResolved,
168
193
  publishAgentMemoryWritten: () => publishAgentMemoryWritten,
@@ -188,12 +213,14 @@ __export(src_exports, {
188
213
  rememberToolDefinition: () => rememberToolDefinition,
189
214
  renderElicitationAnswers: () => renderElicitationAnswers,
190
215
  repairInstruction: () => repairInstruction,
216
+ resolveActionProposalApproval: () => resolveActionProposalApproval,
191
217
  resolveElicitation: () => resolveElicitation,
192
218
  resolveGateLookback: () => resolveGateLookback,
193
219
  resolveMemoryDigest: () => resolveMemoryDigest,
194
220
  resolveOutputGateMode: () => resolveOutputGateMode,
195
221
  resolvePersonaAlias: () => resolvePersonaAlias,
196
222
  resolveSkillCatalog: () => resolveSkillCatalog,
223
+ resolveTextActionProposalDecision: () => resolveTextActionProposalDecision,
197
224
  resolveToolTransientRetryNumbers: () => resolveToolTransientRetryNumbers,
198
225
  rollupThreadUsage: () => rollupThreadUsage,
199
226
  runAgentLoop: () => runAgentLoop,
@@ -210,18 +237,32 @@ __export(src_exports, {
210
237
  skillInputSchema: () => skillInputSchema,
211
238
  skillToolDefinition: () => skillToolDefinition,
212
239
  skillWriteVerdict: () => skillWriteVerdict,
240
+ snapshotActionProposal: () => snapshotActionProposal,
213
241
  stampToolKinds: () => stampToolKinds,
214
242
  staticModelCatalog: () => staticModelCatalog,
215
243
  staticSkillProvider: () => staticSkillProvider,
216
244
  streamFailure: () => streamFailure,
217
245
  summarizeWithModel: () => summarizeWithModel,
218
246
  tenantScope: () => tenantScope,
247
+ textActionProposalReply: () => textActionProposalReply,
248
+ toActionProposalMutationView: () => toActionProposalMutationView,
249
+ toActionProposalView: () => toActionProposalView,
219
250
  toolCallApprovalFromRow: () => toolCallApprovalFromRow,
220
251
  toolCallContext: () => toolCallContext,
221
252
  traceLlmTurn: () => traceLlmTurn,
222
253
  traceToolExecution: () => traceToolExecution,
254
+ transitionActionProposalClaim: () => transitionActionProposalClaim,
255
+ transitionActionProposalDecision: () => transitionActionProposalDecision,
256
+ transitionActionProposalLease: () => transitionActionProposalLease,
257
+ transitionActionProposalSettlement: () => transitionActionProposalSettlement,
258
+ transitionActionProposalSupersession: () => transitionActionProposalSupersession,
223
259
  truncateDetailContent: () => truncateDetailContent,
224
260
  unwrapToolStepOutput: () => unwrapToolStepOutput,
261
+ validateActionProposalCreation: () => validateActionProposalCreation,
262
+ validateActionProposalDiscoveryIndexBatch: () => validateActionProposalDiscoveryIndexBatch,
263
+ validateActionProposalExpiryBatch: () => validateActionProposalExpiryBatch,
264
+ validateActionProposalListQuery: () => validateActionProposalListQuery,
265
+ validateActionProposalWorkerClaim: () => validateActionProposalWorkerClaim,
225
266
  validateElicitationAnswer: () => validateElicitationAnswer,
226
267
  validateElicitationValue: () => validateElicitationValue,
227
268
  validateStructured: () => validateStructured,
@@ -234,6 +275,7 @@ __export(src_exports, {
234
275
  withSkillTool: () => withSkillTool,
235
276
  withToolTimeout: () => withToolTimeout,
236
277
  withTurnFrames: () => withTurnFrames,
278
+ wrapToolPreflightDenied: () => wrapToolPreflightDenied,
237
279
  wrapToolStepOutput: () => wrapToolStepOutput,
238
280
  writeMemory: () => writeMemory
239
281
  });
@@ -407,16 +449,16 @@ function observeTurnFrames(writer, now = Date.now) {
407
449
  };
408
450
  }
409
451
  __name(observeTurnFrames, "observeTurnFrames");
410
- function withTurnFrames(result, frames) {
452
+ function withTurnFrames(result2, frames) {
411
453
  return {
412
- ...result,
413
- ...result.reasoning === void 0 && frames.reasoning !== void 0 ? {
454
+ ...result2,
455
+ ...result2.reasoning === void 0 && frames.reasoning !== void 0 ? {
414
456
  reasoning: frames.reasoning
415
457
  } : {},
416
- ...result.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
458
+ ...result2.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
417
459
  reasoningMs: frames.reasoningMs
418
460
  } : {},
419
- ...result.ui === void 0 && frames.ui !== void 0 ? {
461
+ ...result2.ui === void 0 && frames.ui !== void 0 ? {
420
462
  ui: frames.ui
421
463
  } : {}
422
464
  };
@@ -425,8 +467,9 @@ __name(withTurnFrames, "withTurnFrames");
425
467
 
426
468
  // src/tool-ui.ts
427
469
  var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
428
- function wrapToolStepOutput(output, ui) {
429
- if (ui.length === 0) {
470
+ function wrapToolStepOutput(output, ui, text) {
471
+ const reserved = typeof output === "object" && output !== null && TOOL_STEP_UI in output;
472
+ if (ui.length === 0 && !reserved && !text) {
430
473
  return output;
431
474
  }
432
475
  const wrapped = {
@@ -434,17 +477,36 @@ function wrapToolStepOutput(output, ui) {
434
477
  output,
435
478
  ui: [
436
479
  ...ui
437
- ]
480
+ ],
481
+ ...text ? {
482
+ text
483
+ } : {}
438
484
  };
439
485
  return wrapped;
440
486
  }
441
487
  __name(wrapToolStepOutput, "wrapToolStepOutput");
488
+ function wrapToolPreflightDenied(reason) {
489
+ const wrapped = {
490
+ [TOOL_STEP_UI]: 1,
491
+ output: null,
492
+ ui: [],
493
+ preflightDenied: reason
494
+ };
495
+ return wrapped;
496
+ }
497
+ __name(wrapToolPreflightDenied, "wrapToolPreflightDenied");
442
498
  function unwrapToolStepOutput(raw) {
443
499
  if (typeof raw === "object" && raw !== null && raw[TOOL_STEP_UI] === 1) {
444
500
  const wrapped = raw;
445
501
  return {
446
502
  output: wrapped.output,
447
- ui: Array.isArray(wrapped.ui) ? wrapped.ui : []
503
+ ui: Array.isArray(wrapped.ui) ? wrapped.ui : [],
504
+ ...typeof wrapped.text === "string" ? {
505
+ text: wrapped.text
506
+ } : {},
507
+ ...typeof wrapped.preflightDenied === "string" ? {
508
+ preflightDenied: wrapped.preflightDenied
509
+ } : {}
448
510
  };
449
511
  }
450
512
  return {
@@ -493,6 +555,14 @@ function createUiCollector(toolCallId, write) {
493
555
  ...options.version !== void 0 ? {
494
556
  version: options.version
495
557
  } : {},
558
+ ...options.fallbackText !== void 0 ? {
559
+ fallbackText: options.fallbackText
560
+ } : {},
561
+ ...options.componentVersions !== void 0 ? {
562
+ componentVersions: {
563
+ ...options.componentVersions
564
+ }
565
+ } : {},
496
566
  toolCallId
497
567
  };
498
568
  pushed.set(id, entry);
@@ -527,6 +597,10 @@ function mergeUi(...lists) {
527
597
  ];
528
598
  }
529
599
  __name(mergeUi, "mergeUi");
600
+ function escapeUnsafeToolUiText(text) {
601
+ return text.replaceAll("\0", "\\u0000").replace(/[\uD800-\uDFFF]/gu, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
602
+ }
603
+ __name(escapeUnsafeToolUiText, "escapeUnsafeToolUiText");
530
604
 
531
605
  // src/tool-presentation.ts
532
606
  var ALL_AGENTS = "*";
@@ -734,31 +808,37 @@ async function mayDecideApproval(policy, actor, decision) {
734
808
  return defaultCanDecide(actor, decision);
735
809
  }
736
810
  __name(mayDecideApproval, "mayDecideApproval");
737
- function toolCallApprovalFromRow(row) {
738
- if (row.approver === null || row.approver === void 0) {
811
+ function toolCallApprovalFromRow(row2) {
812
+ if (row2.approver === null || row2.approver === void 0) {
739
813
  return null;
740
814
  }
741
- const status = row.status === "pending_approval" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
742
- const expiresAt = row.expiresAt instanceof Date ? row.expiresAt.toISOString() : typeof row.expiresAt === "string" ? row.expiresAt : void 0;
815
+ const status = row2.status === "pending_approval" || row2.status === "proposed" ? "pending" : row2.status === "rejected" ? "rejected" : row2.status === "expired" ? "expired" : "approved";
816
+ const expiresAt = row2.expiresAt instanceof Date ? row2.expiresAt.toISOString() : typeof row2.expiresAt === "string" ? row2.expiresAt : void 0;
743
817
  const decided = status === "approved" || status === "rejected";
744
818
  return {
745
- toolCallId: row.toolCallId,
746
- approver: row.approver,
819
+ toolCallId: row2.toolCallId,
820
+ ...row2.proposalId != null ? {
821
+ proposalId: row2.proposalId
822
+ } : {},
823
+ approver: row2.approver,
824
+ ...row2.confirmation != null ? {
825
+ confirmation: row2.confirmation
826
+ } : {},
747
827
  status,
748
828
  ...expiresAt !== void 0 ? {
749
829
  expiresAt
750
830
  } : {},
751
- ...row.remember === true ? {
831
+ ...row2.remember === true ? {
752
832
  remember: true
753
833
  } : {},
754
- ...decided && typeof row.executedByRef === "string" ? {
755
- decidedBy: row.executedByRef
834
+ ...decided && typeof row2.executedByRef === "string" ? {
835
+ decidedBy: row2.executedByRef
756
836
  } : {},
757
- ...decided && typeof row.decidedVia === "string" ? {
758
- decidedVia: row.decidedVia
837
+ ...decided && typeof row2.decidedVia === "string" ? {
838
+ decidedVia: row2.decidedVia
759
839
  } : {},
760
- ...status === "rejected" && typeof row.error === "string" ? {
761
- reason: row.error
840
+ ...status === "rejected" && typeof row2.error === "string" ? {
841
+ reason: row2.error
762
842
  } : {}
763
843
  };
764
844
  }
@@ -775,28 +855,28 @@ function estimateCost(usage, price) {
775
855
  return uncachedInputTokens / 1e6 * price.inputPricePer1m + cacheWriteTokens / 1e6 * (price.cacheWritePricePer1m ?? price.inputPricePer1m) + cacheReadTokens / 1e6 * (price.cacheReadPricePer1m ?? price.inputPricePer1m) + usage.outputTokens / 1e6 * price.outputPricePer1m;
776
856
  }
777
857
  __name(estimateCost, "estimateCost");
778
- function rowCost(row, prices) {
779
- return row.costUsd ?? estimateCost(row, prices.get(row.modelId));
858
+ function rowCost(row2, prices) {
859
+ return row2.costUsd ?? estimateCost(row2, prices.get(row2.modelId));
780
860
  }
781
861
  __name(rowCost, "rowCost");
782
862
  function bucketByModel(rows, prices) {
783
863
  const byModel = /* @__PURE__ */ new Map();
784
- for (const row of rows) {
785
- const bucket = byModel.get(row.modelId) ?? {
864
+ for (const row2 of rows) {
865
+ const bucket = byModel.get(row2.modelId) ?? {
786
866
  requests: 0,
787
867
  inputTokens: 0,
788
868
  outputTokens: 0,
789
869
  costUsd: 0
790
870
  };
791
871
  bucket.requests += 1;
792
- bucket.inputTokens += row.inputTokens;
793
- bucket.outputTokens += row.outputTokens;
794
- bucket.costUsd += rowCost(row, prices);
795
- byModel.set(row.modelId, bucket);
872
+ bucket.inputTokens += row2.inputTokens;
873
+ bucket.outputTokens += row2.outputTokens;
874
+ bucket.costUsd += rowCost(row2, prices);
875
+ byModel.set(row2.modelId, bucket);
796
876
  }
797
- const result = [];
877
+ const result2 = [];
798
878
  for (const [modelId, bucket] of byModel) {
799
- result.push({
879
+ result2.push({
800
880
  modelId,
801
881
  requests: bucket.requests,
802
882
  inputTokens: bucket.inputTokens,
@@ -804,28 +884,28 @@ function bucketByModel(rows, prices) {
804
884
  costUsd: bucket.costUsd
805
885
  });
806
886
  }
807
- result.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
808
- return result;
887
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
888
+ return result2;
809
889
  }
810
890
  __name(bucketByModel, "bucketByModel");
811
891
  function bucketByActor(rows, prices) {
812
892
  const byActor = /* @__PURE__ */ new Map();
813
- for (const row of rows) {
814
- const bucket = byActor.get(row.actorRef) ?? {
893
+ for (const row2 of rows) {
894
+ const bucket = byActor.get(row2.actorRef) ?? {
815
895
  requests: 0,
816
896
  totalTokens: 0,
817
897
  costUsd: 0,
818
898
  threadIds: /* @__PURE__ */ new Set()
819
899
  };
820
900
  bucket.requests += 1;
821
- bucket.totalTokens += row.inputTokens + row.outputTokens;
822
- bucket.costUsd += rowCost(row, prices);
823
- bucket.threadIds.add(row.threadId);
824
- byActor.set(row.actorRef, bucket);
901
+ bucket.totalTokens += row2.inputTokens + row2.outputTokens;
902
+ bucket.costUsd += rowCost(row2, prices);
903
+ bucket.threadIds.add(row2.threadId);
904
+ byActor.set(row2.actorRef, bucket);
825
905
  }
826
- const result = [];
906
+ const result2 = [];
827
907
  for (const [actorRef, bucket] of byActor) {
828
- result.push({
908
+ result2.push({
829
909
  actorRef,
830
910
  requests: bucket.requests,
831
911
  totalTokens: bucket.totalTokens,
@@ -833,30 +913,30 @@ function bucketByActor(rows, prices) {
833
913
  threadCount: bucket.threadIds.size
834
914
  });
835
915
  }
836
- result.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
837
- return result;
916
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
917
+ return result2;
838
918
  }
839
919
  __name(bucketByActor, "bucketByActor");
840
920
  function bucketByThread(rows, prices, threads, options) {
841
921
  const byThread = /* @__PURE__ */ new Map();
842
- for (const row of rows) {
843
- const bucket = byThread.get(row.threadId) ?? {
922
+ for (const row2 of rows) {
923
+ const bucket = byThread.get(row2.threadId) ?? {
844
924
  requests: 0,
845
925
  totalTokens: 0,
846
926
  costUsd: 0
847
927
  };
848
928
  bucket.requests += 1;
849
- bucket.totalTokens += row.inputTokens + row.outputTokens;
850
- bucket.costUsd += rowCost(row, prices);
851
- byThread.set(row.threadId, bucket);
929
+ bucket.totalTokens += row2.inputTokens + row2.outputTokens;
930
+ bucket.costUsd += rowCost(row2, prices);
931
+ byThread.set(row2.threadId, bucket);
852
932
  }
853
- const result = [];
933
+ const result2 = [];
854
934
  for (const [threadId, bucket] of byThread) {
855
935
  const thread = threads.get(threadId);
856
936
  if (thread === void 0 && !options.includeUnknownThreads) {
857
937
  continue;
858
938
  }
859
- result.push({
939
+ result2.push({
860
940
  threadId,
861
941
  title: thread?.title ?? "",
862
942
  actorRef: thread?.actorRef ?? "",
@@ -865,8 +945,8 @@ function bucketByThread(rows, prices, threads, options) {
865
945
  costUsd: bucket.costUsd
866
946
  });
867
947
  }
868
- result.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
869
- return result.slice(0, options.limit);
948
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
949
+ return result2.slice(0, options.limit);
870
950
  }
871
951
  __name(bucketByThread, "bucketByThread");
872
952
  function rollupThreadUsage(rows, prices) {
@@ -877,37 +957,37 @@ function rollupThreadUsage(rows, prices) {
877
957
  totalTokens: 0,
878
958
  costUsd: 0
879
959
  };
880
- for (const row of rows) {
960
+ for (const row2 of rows) {
881
961
  rollup.requests += 1;
882
- rollup.inputTokens += row.inputTokens;
883
- rollup.outputTokens += row.outputTokens;
884
- rollup.totalTokens += row.inputTokens + row.outputTokens;
885
- rollup.costUsd += rowCost(row, prices);
962
+ rollup.inputTokens += row2.inputTokens;
963
+ rollup.outputTokens += row2.outputTokens;
964
+ rollup.totalTokens += row2.inputTokens + row2.outputTokens;
965
+ rollup.costUsd += rowCost(row2, prices);
886
966
  }
887
967
  return rollup;
888
968
  }
889
969
  __name(rollupThreadUsage, "rollupThreadUsage");
890
970
  function bucketUsageTrend(rows, prices) {
891
971
  const byDay = /* @__PURE__ */ new Map();
892
- for (const row of rows) {
893
- const bucket = byDay.get(row.day) ?? {
972
+ for (const row2 of rows) {
973
+ const bucket = byDay.get(row2.day) ?? {
894
974
  totalTokens: 0,
895
975
  costUsd: 0
896
976
  };
897
- bucket.totalTokens += row.inputTokens + row.outputTokens;
898
- bucket.costUsd += rowCost(row, prices);
899
- byDay.set(row.day, bucket);
977
+ bucket.totalTokens += row2.inputTokens + row2.outputTokens;
978
+ bucket.costUsd += rowCost(row2, prices);
979
+ byDay.set(row2.day, bucket);
900
980
  }
901
- const result = [];
981
+ const result2 = [];
902
982
  for (const [day, bucket] of byDay) {
903
- result.push({
983
+ result2.push({
904
984
  day,
905
985
  totalTokens: bucket.totalTokens,
906
986
  costUsd: bucket.costUsd
907
987
  });
908
988
  }
909
- result.sort((left, right) => left.day.localeCompare(right.day));
910
- return result;
989
+ result2.sort((left, right) => left.day.localeCompare(right.day));
990
+ return result2;
911
991
  }
912
992
  __name(bucketUsageTrend, "bucketUsageTrend");
913
993
  function dayBoundsUtc(range) {
@@ -932,7 +1012,7 @@ async function filterToolsByEnabled(entries) {
932
1012
  entry,
933
1013
  enabled: await isToolEnabled(entry.spec, entry.handler)
934
1014
  })));
935
- return checked.filter((row) => row.enabled).map((row) => row.entry);
1015
+ return checked.filter((row2) => row2.enabled).map((row2) => row2.entry);
936
1016
  }
937
1017
  __name(filterToolsByEnabled, "filterToolsByEnabled");
938
1018
  async function canActorUseTool(actor, handler) {
@@ -944,7 +1024,7 @@ async function filterToolsByCanUse(entries, actor) {
944
1024
  entry,
945
1025
  allowed: await canActorUseTool(actor, entry.handler)
946
1026
  })));
947
- return checked.filter((row) => row.allowed).map((row) => row.entry);
1027
+ return checked.filter((row2) => row2.allowed).map((row2) => row2.entry);
948
1028
  }
949
1029
  __name(filterToolsByCanUse, "filterToolsByCanUse");
950
1030
  async function filterToolsByRole(tools, actor, policy) {
@@ -1351,13 +1431,13 @@ async function validateStructured(schema, text, reported) {
1351
1431
  issues: []
1352
1432
  };
1353
1433
  }
1354
- const result = await schema["~standard"].validate(candidate);
1355
- return result.issues !== void 0 ? {
1434
+ const result2 = await schema["~standard"].validate(candidate);
1435
+ return result2.issues !== void 0 ? {
1356
1436
  ok: false,
1357
- issues: result.issues
1437
+ issues: result2.issues
1358
1438
  } : {
1359
1439
  ok: true,
1360
- value: result.value
1440
+ value: result2.value
1361
1441
  };
1362
1442
  }
1363
1443
  __name(validateStructured, "validateStructured");
@@ -2536,12 +2616,12 @@ function memoryWriteVerdict(request) {
2536
2616
  };
2537
2617
  }
2538
2618
  __name(memoryWriteVerdict, "memoryWriteVerdict");
2539
- function memoryForgetVerdict({ record, actor }) {
2540
- return record.scope === actorScope(actor) ? {
2619
+ function memoryForgetVerdict({ record: record2, actor }) {
2620
+ return record2.scope === actorScope(actor) ? {
2541
2621
  allowed: true
2542
2622
  } : {
2543
2623
  allowed: false,
2544
- reason: `"${record.scope}" is not this actor's own scope; deleting it is an administrative action`
2624
+ reason: `"${record2.scope}" is not this actor's own scope; deleting it is an administrative action`
2545
2625
  };
2546
2626
  }
2547
2627
  __name(memoryForgetVerdict, "memoryForgetVerdict");
@@ -2584,7 +2664,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2584
2664
  error: verdict.reason
2585
2665
  };
2586
2666
  }
2587
- const record = await write({
2667
+ const record2 = await write({
2588
2668
  key,
2589
2669
  text,
2590
2670
  scope,
@@ -2598,7 +2678,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2598
2678
  });
2599
2679
  return {
2600
2680
  ok: true,
2601
- record
2681
+ record: record2
2602
2682
  };
2603
2683
  }
2604
2684
  __name(writeMemory, "writeMemory");
@@ -2650,7 +2730,7 @@ function danglingToolCallIds(messages) {
2650
2730
  if (message.role !== "assistant" || calls.length === 0) {
2651
2731
  continue;
2652
2732
  }
2653
- const settled = new Set((message.toolResults ?? []).map((result) => result.id));
2733
+ const settled = new Set((message.toolResults ?? []).map((result2) => result2.id));
2654
2734
  for (const call of calls) {
2655
2735
  if (!settled.has(call.id)) {
2656
2736
  ids.push(call.id);
@@ -2718,7 +2798,7 @@ function settleDanglingToolCalls(messages, outcomes = []) {
2718
2798
  return message;
2719
2799
  }
2720
2800
  const results = message.toolResults ?? [];
2721
- const settled = new Set(results.map((result) => result.id));
2801
+ const settled = new Set(results.map((result2) => result2.id));
2722
2802
  const missing = calls.filter((call) => !settled.has(call.id));
2723
2803
  if (missing.length === 0) {
2724
2804
  return message;
@@ -2826,6 +2906,1665 @@ async function settleUnsettledDelegation(args) {
2826
2906
  }
2827
2907
  __name(settleUnsettledDelegation, "settleUnsettledDelegation");
2828
2908
 
2909
+ // src/action-proposal-discovery.ts
2910
+ function validateActionProposalWorkerClaim(command, now) {
2911
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
2912
+ if (typeof command.workerId !== "string" || command.workerId.length === 0) throw new TypeError("workerId must be a nonempty string");
2913
+ if (!Number.isSafeInteger(command.leaseMs) || command.leaseMs <= 0 || !Number.isSafeInteger(now + command.leaseMs)) throw new RangeError("leaseMs must be positive safe integer milliseconds");
2914
+ }
2915
+ __name(validateActionProposalWorkerClaim, "validateActionProposalWorkerClaim");
2916
+ function validateActionProposalExpiryBatch(command, now) {
2917
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
2918
+ validateActionProposalDiscoveryIndexBatch(command);
2919
+ }
2920
+ __name(validateActionProposalExpiryBatch, "validateActionProposalExpiryBatch");
2921
+ function validateActionProposalDiscoveryIndexBatch(command) {
2922
+ if (!Number.isInteger(command.limit) || command.limit < 1 || command.limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
2923
+ }
2924
+ __name(validateActionProposalDiscoveryIndexBatch, "validateActionProposalDiscoveryIndexBatch");
2925
+
2926
+ // src/action-proposal-outcome.ts
2927
+ function attachActionProposalOutcome(row2, now, ui = [], text) {
2928
+ if (row2.outcome) return row2;
2929
+ const execution = row2.execution;
2930
+ const outcome = {
2931
+ id: `${row2.id}:outcome:1`,
2932
+ proposalId: row2.id,
2933
+ outcomeVersion: 1,
2934
+ tenantRef: row2.tenantRef,
2935
+ actorRef: row2.actorRef,
2936
+ threadId: row2.threadId,
2937
+ originRunId: row2.originRunId,
2938
+ originToolCallId: row2.originToolCallId,
2939
+ toolName: row2.toolName,
2940
+ decision: row2.decision,
2941
+ ui,
2942
+ createdAt: now,
2943
+ ...text !== void 0 ? {
2944
+ text
2945
+ } : {},
2946
+ ...execution?.status === "succeeded" || execution?.status === "failed" ? {
2947
+ executionStatus: execution.status
2948
+ } : {},
2949
+ ...execution && Object.hasOwn(execution, "result") ? {
2950
+ result: execution.result
2951
+ } : {},
2952
+ ...execution?.error !== void 0 ? {
2953
+ error: execution.error
2954
+ } : row2.decisionAudit?.reason !== void 0 ? {
2955
+ error: row2.decisionAudit.reason
2956
+ } : {}
2957
+ };
2958
+ return snapshotActionProposal({
2959
+ ...row2,
2960
+ outcome,
2961
+ outcomeDelivery: {
2962
+ status: "pending",
2963
+ generation: 0,
2964
+ lease: null
2965
+ }
2966
+ });
2967
+ }
2968
+ __name(attachActionProposalOutcome, "attachActionProposalOutcome");
2969
+ function claimActionProposalOutcome(row2, command, now, token) {
2970
+ validateActionProposalWorkerClaim(command, now);
2971
+ const delivery = row2.outcomeDelivery;
2972
+ if (!row2.outcome || !delivery || delivery.status !== "pending" || delivery.lease && now < delivery.lease.expiresAt) return null;
2973
+ const generation = delivery.generation + 1;
2974
+ return snapshotActionProposal({
2975
+ ...row2,
2976
+ outcomeDelivery: {
2977
+ ...delivery,
2978
+ generation,
2979
+ lease: {
2980
+ token,
2981
+ generation,
2982
+ workerId: command.workerId,
2983
+ expiresAt: now + command.leaseMs
2984
+ }
2985
+ }
2986
+ });
2987
+ }
2988
+ __name(claimActionProposalOutcome, "claimActionProposalOutcome");
2989
+ function actionProposalOutcomeFenceValid(row2, command, now) {
2990
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid server clock");
2991
+ if (typeof command.token !== "string" || !command.token || !Number.isSafeInteger(command.generation) || command.generation < 1 || typeof command.outcomeId !== "string" || !command.outcomeId) throw new TypeError("Invalid outcome fence");
2992
+ const delivery = row2.outcomeDelivery;
2993
+ return row2.outcome?.id === command.outcomeId && delivery?.status === "pending" && delivery.generation === command.generation && delivery.lease?.token === command.token && delivery.lease.generation === command.generation && now < delivery.lease.expiresAt;
2994
+ }
2995
+ __name(actionProposalOutcomeFenceValid, "actionProposalOutcomeFenceValid");
2996
+ function outcomeSummary(outcome) {
2997
+ if (outcome.executionStatus === "succeeded") return `Action ${JSON.stringify(outcome.toolName)} completed (proposal ${JSON.stringify(outcome.proposalId)}).${Object.hasOwn(outcome, "result") ? ` Result: ${JSON.stringify(outcome.result)}` : ""}`;
2998
+ if (outcome.executionStatus === "failed") return `Action ${JSON.stringify(outcome.toolName)} failed (proposal ${JSON.stringify(outcome.proposalId)}): ${JSON.stringify(outcome.error ?? "Execution failed")}`;
2999
+ return `Action ${JSON.stringify(outcome.toolName)} was ${outcome.decision} (proposal ${JSON.stringify(outcome.proposalId)}) and was not executed.${outcome.error ? ` Reason: ${JSON.stringify(outcome.error)}` : ""}`;
3000
+ }
3001
+ __name(outcomeSummary, "outcomeSummary");
3002
+ function actionProposalOutcomeScope(outcome) {
3003
+ return {
3004
+ tenantRef: outcome.tenantRef,
3005
+ actorRef: outcome.actorRef,
3006
+ threadId: outcome.threadId
3007
+ };
3008
+ }
3009
+ __name(actionProposalOutcomeScope, "actionProposalOutcomeScope");
3010
+ function actionProposalOutcomeText(outcome) {
3011
+ return outcomeSummary(outcome) + (outcome.text ? ` UI: ${JSON.stringify(outcome.text)}` : "");
3012
+ }
3013
+ __name(actionProposalOutcomeText, "actionProposalOutcomeText");
3014
+
3015
+ // src/genui/schema.ts
3016
+ function isStandardSchema(schema) {
3017
+ return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"]?.validate === "function";
3018
+ }
3019
+ __name(isStandardSchema, "isStandardSchema");
3020
+ function toJsonSchema(schema) {
3021
+ if (!isStandardSchema(schema)) {
3022
+ return schema;
3023
+ }
3024
+ const standard = schema["~standard"];
3025
+ const converter = standard.jsonSchema;
3026
+ if (converter === void 0 || typeof converter.input !== "function") {
3027
+ return void 0;
3028
+ }
3029
+ try {
3030
+ return converter.input({
3031
+ target: "draft-07"
3032
+ });
3033
+ } catch {
3034
+ return void 0;
3035
+ }
3036
+ }
3037
+ __name(toJsonSchema, "toJsonSchema");
3038
+ async function validateProps(schema, value, validator) {
3039
+ if (isStandardSchema(schema)) {
3040
+ const result2 = await schema["~standard"].validate(value);
3041
+ if (result2.issues !== void 0) {
3042
+ return {
3043
+ ok: false,
3044
+ issues: toGenuiIssues(result2.issues)
3045
+ };
3046
+ }
3047
+ return {
3048
+ ok: true,
3049
+ value: result2.value
3050
+ };
3051
+ }
3052
+ const issues = validator.validate(schema, value);
3053
+ return issues.length > 0 ? {
3054
+ ok: false,
3055
+ issues
3056
+ } : {
3057
+ ok: true,
3058
+ value
3059
+ };
3060
+ }
3061
+ __name(validateProps, "validateProps");
3062
+ function validatePropsSync(schema, value, validator) {
3063
+ if (!isStandardSchema(schema)) {
3064
+ const issues = validator.validate(schema, value);
3065
+ return issues.length > 0 ? {
3066
+ ok: false,
3067
+ issues
3068
+ } : {
3069
+ ok: true,
3070
+ value
3071
+ };
3072
+ }
3073
+ const result2 = schema["~standard"].validate(value);
3074
+ if (result2 instanceof Promise) {
3075
+ result2.catch(() => void 0);
3076
+ return void 0;
3077
+ }
3078
+ return result2.issues !== void 0 ? {
3079
+ ok: false,
3080
+ issues: toGenuiIssues(result2.issues)
3081
+ } : {
3082
+ ok: true,
3083
+ value: result2.value
3084
+ };
3085
+ }
3086
+ __name(validatePropsSync, "validatePropsSync");
3087
+ function toGenuiIssues(issues) {
3088
+ return issues.map((issue4) => ({
3089
+ path: (issue4.path ?? []).map((segment) => typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment),
3090
+ message: issue4.message
3091
+ }));
3092
+ }
3093
+ __name(toGenuiIssues, "toGenuiIssues");
3094
+ function formatIssues(issues) {
3095
+ return issues.map((issue4) => `${issue4.path.length > 0 ? formatPath(issue4.path) : "(root)"}: ${issue4.message}`).join("; ");
3096
+ }
3097
+ __name(formatIssues, "formatIssues");
3098
+ function formatPath(path) {
3099
+ return path.map((segment, index) => typeof segment === "number" ? `[${segment}]` : index === 0 ? segment : `.${segment}`).join("");
3100
+ }
3101
+ __name(formatPath, "formatPath");
3102
+ var builtinJsonSchemaValidator = {
3103
+ validate(schema, value) {
3104
+ const issues = [];
3105
+ check(schema, value, [], schema, issues, 0);
3106
+ return issues;
3107
+ }
3108
+ };
3109
+ var MAX_DEPTH = 64;
3110
+ function typeOf(value) {
3111
+ if (value === null) return "null";
3112
+ if (Array.isArray(value)) return "array";
3113
+ return typeof value;
3114
+ }
3115
+ __name(typeOf, "typeOf");
3116
+ function matchesType(type, value) {
3117
+ switch (type) {
3118
+ case "integer":
3119
+ return typeof value === "number" && Number.isInteger(value);
3120
+ case "number":
3121
+ return typeof value === "number" && Number.isFinite(value);
3122
+ case "object":
3123
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3124
+ default:
3125
+ return typeOf(value) === type;
3126
+ }
3127
+ }
3128
+ __name(matchesType, "matchesType");
3129
+ function resolveRef(root, ref) {
3130
+ if (ref === "#") return root;
3131
+ if (!ref.startsWith("#/")) return void 0;
3132
+ let current = root;
3133
+ for (const raw of ref.slice(2).split("/")) {
3134
+ const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
3135
+ if (typeof current !== "object" || current === null) return void 0;
3136
+ current = current[key];
3137
+ }
3138
+ return typeof current === "object" && current !== null ? current : void 0;
3139
+ }
3140
+ __name(resolveRef, "resolveRef");
3141
+ function deepEqual(a, b) {
3142
+ if (a === b) return true;
3143
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
3144
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
3145
+ const keysA = Object.keys(a);
3146
+ const keysB = Object.keys(b);
3147
+ if (keysA.length !== keysB.length) return false;
3148
+ return keysA.every((key) => deepEqual(a[key], b[key]));
3149
+ }
3150
+ __name(deepEqual, "deepEqual");
3151
+ function check(schema, value, path, root, issues, depth) {
3152
+ if (schema === true || schema === void 0) return;
3153
+ if (schema === false) {
3154
+ issues.push({
3155
+ path,
3156
+ message: "is not allowed"
3157
+ });
3158
+ return;
3159
+ }
3160
+ if (typeof schema !== "object" || schema === null) return;
3161
+ if (depth > MAX_DEPTH) {
3162
+ issues.push({
3163
+ path,
3164
+ message: "is nested too deeply"
3165
+ });
3166
+ return;
3167
+ }
3168
+ const s2 = schema;
3169
+ if (typeof s2.$ref === "string") {
3170
+ const target = resolveRef(root, s2.$ref);
3171
+ if (target === void 0) {
3172
+ issues.push({
3173
+ path,
3174
+ message: `references unknown schema ${s2.$ref}`
3175
+ });
3176
+ return;
3177
+ }
3178
+ check(target, value, path, root, issues, depth + 1);
3179
+ }
3180
+ if (s2.type !== void 0) {
3181
+ const types = Array.isArray(s2.type) ? s2.type : [
3182
+ s2.type
3183
+ ];
3184
+ if (!types.some((type) => matchesType(type, value))) {
3185
+ issues.push({
3186
+ path,
3187
+ message: `must be ${types.join(" or ")}`
3188
+ });
3189
+ return;
3190
+ }
3191
+ }
3192
+ if (Array.isArray(s2.enum) && !s2.enum.some((option) => deepEqual(option, value))) {
3193
+ issues.push({
3194
+ path,
3195
+ message: `must be one of ${s2.enum.map((option) => JSON.stringify(option)).join(", ")}`
3196
+ });
3197
+ }
3198
+ if ("const" in s2 && !deepEqual(s2.const, value)) {
3199
+ issues.push({
3200
+ path,
3201
+ message: `must be ${JSON.stringify(s2.const)}`
3202
+ });
3203
+ }
3204
+ if (typeof value === "string") {
3205
+ if (typeof s2.minLength === "number" && value.length < s2.minLength) {
3206
+ issues.push({
3207
+ path,
3208
+ message: `must be at least ${s2.minLength} characters`
3209
+ });
3210
+ }
3211
+ if (typeof s2.maxLength === "number" && value.length > s2.maxLength) {
3212
+ issues.push({
3213
+ path,
3214
+ message: `must be at most ${s2.maxLength} characters`
3215
+ });
3216
+ }
3217
+ if (typeof s2.pattern === "string") {
3218
+ let pattern;
3219
+ try {
3220
+ pattern = new RegExp(s2.pattern, "u");
3221
+ } catch {
3222
+ pattern = void 0;
3223
+ }
3224
+ if (pattern !== void 0 && !pattern.test(value)) {
3225
+ issues.push({
3226
+ path,
3227
+ message: `must match ${s2.pattern}`
3228
+ });
3229
+ }
3230
+ }
3231
+ }
3232
+ if (typeof value === "number") {
3233
+ if (typeof s2.minimum === "number" && value < s2.minimum) {
3234
+ issues.push({
3235
+ path,
3236
+ message: `must be >= ${s2.minimum}`
3237
+ });
3238
+ }
3239
+ if (typeof s2.maximum === "number" && value > s2.maximum) {
3240
+ issues.push({
3241
+ path,
3242
+ message: `must be <= ${s2.maximum}`
3243
+ });
3244
+ }
3245
+ }
3246
+ if (Array.isArray(value)) {
3247
+ if (typeof s2.minItems === "number" && value.length < s2.minItems) {
3248
+ issues.push({
3249
+ path,
3250
+ message: `must have at least ${s2.minItems} items`
3251
+ });
3252
+ }
3253
+ if (typeof s2.maxItems === "number" && value.length > s2.maxItems) {
3254
+ issues.push({
3255
+ path,
3256
+ message: `must have at most ${s2.maxItems} items`
3257
+ });
3258
+ }
3259
+ if (s2.items !== void 0 && !Array.isArray(s2.items)) {
3260
+ value.forEach((item, index) => {
3261
+ check(s2.items, item, [
3262
+ ...path,
3263
+ index
3264
+ ], root, issues, depth + 1);
3265
+ });
3266
+ }
3267
+ }
3268
+ if (matchesType("object", value)) {
3269
+ const record2 = value;
3270
+ const properties = typeof s2.properties === "object" && s2.properties !== null ? s2.properties : {};
3271
+ if (Array.isArray(s2.required)) {
3272
+ for (const key of s2.required) {
3273
+ if (record2[key] === void 0) {
3274
+ issues.push({
3275
+ path: [
3276
+ ...path,
3277
+ key
3278
+ ],
3279
+ message: "is required"
3280
+ });
3281
+ }
3282
+ }
3283
+ }
3284
+ for (const [key, entry] of Object.entries(record2)) {
3285
+ if (key in properties) {
3286
+ if (entry !== void 0) {
3287
+ check(properties[key], entry, [
3288
+ ...path,
3289
+ key
3290
+ ], root, issues, depth + 1);
3291
+ }
3292
+ } else if (s2.additionalProperties === false) {
3293
+ issues.push({
3294
+ path: [
3295
+ ...path,
3296
+ key
3297
+ ],
3298
+ message: "is not a known property"
3299
+ });
3300
+ } else if (typeof s2.additionalProperties === "object" && s2.additionalProperties !== null) {
3301
+ check(s2.additionalProperties, entry, [
3302
+ ...path,
3303
+ key
3304
+ ], root, issues, depth + 1);
3305
+ }
3306
+ }
3307
+ }
3308
+ if (Array.isArray(s2.allOf)) {
3309
+ for (const sub of s2.allOf) check(sub, value, path, root, issues, depth + 1);
3310
+ }
3311
+ if (Array.isArray(s2.anyOf) || Array.isArray(s2.oneOf)) {
3312
+ const options = Array.isArray(s2.anyOf) ? s2.anyOf : s2.oneOf;
3313
+ const passing = options.filter((sub) => {
3314
+ const nested = [];
3315
+ check(sub, value, path, root, nested, depth + 1);
3316
+ return nested.length === 0;
3317
+ }).length;
3318
+ if (Array.isArray(s2.anyOf) ? passing === 0 : passing !== 1) {
3319
+ issues.push({
3320
+ path,
3321
+ message: Array.isArray(s2.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
3322
+ });
3323
+ }
3324
+ }
3325
+ if (s2.not !== void 0) {
3326
+ const nested = [];
3327
+ check(s2.not, value, path, root, nested, depth + 1);
3328
+ if (nested.length === 0) {
3329
+ issues.push({
3330
+ path,
3331
+ message: "matches a shape that is not allowed"
3332
+ });
3333
+ }
3334
+ }
3335
+ }
3336
+ __name(check, "check");
3337
+
3338
+ // src/genui/catalog.ts
3339
+ var COMPONENT_NAME = /^[A-Za-z][A-Za-z0-9]{0,63}$/;
3340
+ function defineComponent(definition) {
3341
+ if (!COMPONENT_NAME.test(definition.name)) {
3342
+ throw new Error(`genui: component name "${definition.name}" must be letters and digits, starting with a letter (e.g. DataTable)`);
3343
+ }
3344
+ if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
3345
+ if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
3346
+ return definition;
3347
+ }
3348
+ __name(defineComponent, "defineComponent");
3349
+ function defineCatalog(components, options = {}) {
3350
+ const byName = /* @__PURE__ */ new Map();
3351
+ for (const component of components) {
3352
+ defineComponent(component);
3353
+ if (byName.has(component.name)) {
3354
+ throw new Error(`genui: component "${component.name}" is defined twice in one catalog`);
3355
+ }
3356
+ byName.set(component.name, component);
3357
+ }
3358
+ return buildCatalog(byName, options.jsonSchemaValidator ?? builtinJsonSchemaValidator);
3359
+ }
3360
+ __name(defineCatalog, "defineCatalog");
3361
+ function buildCatalog(byName, validator) {
3362
+ const list = Object.freeze([
3363
+ ...byName.values()
3364
+ ]);
3365
+ return {
3366
+ components: list,
3367
+ validator,
3368
+ get: /* @__PURE__ */ __name((name) => byName.get(name), "get"),
3369
+ has: /* @__PURE__ */ __name((name) => byName.has(name), "has"),
3370
+ modelComponents: /* @__PURE__ */ __name(() => list.filter((component) => component.internal !== true), "modelComponents"),
3371
+ async validate(component, props) {
3372
+ const definition = byName.get(component);
3373
+ if (definition === void 0) {
3374
+ return {
3375
+ ok: false,
3376
+ issues: [
3377
+ {
3378
+ path: [],
3379
+ message: `unknown component "${component}"`
3380
+ }
3381
+ ]
3382
+ };
3383
+ }
3384
+ const result2 = await validateProps(definition.outputProps ?? definition.props, props, validator);
3385
+ return result2;
3386
+ },
3387
+ validateSync(component, props) {
3388
+ const definition = byName.get(component);
3389
+ if (definition === void 0) {
3390
+ return {
3391
+ ok: false,
3392
+ issues: [
3393
+ {
3394
+ path: [],
3395
+ message: `unknown component "${component}"`
3396
+ }
3397
+ ]
3398
+ };
3399
+ }
3400
+ return validatePropsSync(definition.outputProps ?? definition.props, props, validator);
3401
+ },
3402
+ jsonSchemaFor(component) {
3403
+ const definition = byName.get(component);
3404
+ return definition === void 0 ? void 0 : toJsonSchema(definition.outputProps ?? definition.props);
3405
+ },
3406
+ extend(more) {
3407
+ const next = new Map(byName);
3408
+ for (const component of more) {
3409
+ defineComponent(component);
3410
+ next.set(component.name, component);
3411
+ }
3412
+ return buildCatalog(next, validator);
3413
+ }
3414
+ };
3415
+ }
3416
+ __name(buildCatalog, "buildCatalog");
3417
+
3418
+ // src/genui/tree.ts
3419
+ var GENUI_TREE_COMPONENT = "genui:tree";
3420
+ async function validateTree(catalog, tree, limits = {}, phase = "output") {
3421
+ const maxDepth = limits.maxDepth ?? 12;
3422
+ const maxNodes = limits.maxNodes ?? 200;
3423
+ const issues = [];
3424
+ let nodes = 0;
3425
+ const visit = /* @__PURE__ */ __name(async (node, path, depth) => {
3426
+ nodes += 1;
3427
+ if (nodes > maxNodes) {
3428
+ if (nodes === maxNodes + 1) {
3429
+ issues.push({
3430
+ path,
3431
+ message: `the tree has more than ${maxNodes} elements`
3432
+ });
3433
+ }
3434
+ return void 0;
3435
+ }
3436
+ if (depth > maxDepth) {
3437
+ issues.push({
3438
+ path,
3439
+ message: `the tree is nested deeper than ${maxDepth} levels`
3440
+ });
3441
+ return void 0;
3442
+ }
3443
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
3444
+ issues.push({
3445
+ path,
3446
+ message: "must be an element object { type, props, children? }"
3447
+ });
3448
+ return void 0;
3449
+ }
3450
+ const element = node;
3451
+ if (typeof element.type !== "string") {
3452
+ issues.push({
3453
+ path: [
3454
+ ...path,
3455
+ "type"
3456
+ ],
3457
+ message: "must be a component name"
3458
+ });
3459
+ return void 0;
3460
+ }
3461
+ const definition = catalog.get(element.type);
3462
+ if (definition === void 0 || definition.internal === true) {
3463
+ issues.push({
3464
+ path: [
3465
+ ...path,
3466
+ "type"
3467
+ ],
3468
+ message: `unknown component "${element.type}" (allowed: ${catalog.modelComponents().map((component) => component.name).join(", ")})`
3469
+ });
3470
+ return void 0;
3471
+ }
3472
+ const validated = phase === "input" ? await validateProps(definition.props, element.props ?? {}, catalog.validator) : await catalog.validate(element.type, element.props ?? {});
3473
+ let props = {};
3474
+ if (!validated.ok) {
3475
+ for (const issue4 of validated.issues) {
3476
+ issues.push({
3477
+ path: [
3478
+ ...path,
3479
+ "props",
3480
+ ...issue4.path
3481
+ ],
3482
+ message: issue4.message
3483
+ });
3484
+ }
3485
+ } else {
3486
+ props = validated.value;
3487
+ }
3488
+ const rawChildren = parseChildrenIfStringified(element.children);
3489
+ if (rawChildren === void 0 || rawChildren === null) {
3490
+ return {
3491
+ type: element.type,
3492
+ props
3493
+ };
3494
+ }
3495
+ if (!Array.isArray(rawChildren)) {
3496
+ issues.push({
3497
+ path: [
3498
+ ...path,
3499
+ "children"
3500
+ ],
3501
+ message: "must be an array of elements (a literal array, not a string)"
3502
+ });
3503
+ return void 0;
3504
+ }
3505
+ if (rawChildren.length > 0 && definition.children !== true) {
3506
+ issues.push({
3507
+ path: [
3508
+ ...path,
3509
+ "children"
3510
+ ],
3511
+ message: `${element.type} does not take children`
3512
+ });
3513
+ return void 0;
3514
+ }
3515
+ const children = [];
3516
+ for (const [index, child] of rawChildren.entries()) {
3517
+ const next = await visit(child, [
3518
+ ...path,
3519
+ "children",
3520
+ index
3521
+ ], depth + 1);
3522
+ if (next !== void 0) {
3523
+ children.push(next);
3524
+ }
3525
+ }
3526
+ return children.length > 0 ? {
3527
+ type: element.type,
3528
+ props,
3529
+ children
3530
+ } : {
3531
+ type: element.type,
3532
+ props
3533
+ };
3534
+ }, "visit");
3535
+ const root = await visit(tree, [], 1);
3536
+ return issues.length > 0 || root === void 0 ? {
3537
+ ok: false,
3538
+ issues: issues.length > 0 ? issues : [
3539
+ {
3540
+ path: [],
3541
+ message: "is invalid"
3542
+ }
3543
+ ]
3544
+ } : {
3545
+ ok: true,
3546
+ value: root
3547
+ };
3548
+ }
3549
+ __name(validateTree, "validateTree");
3550
+ function parseChildrenIfStringified(value) {
3551
+ if (typeof value !== "string") return value;
3552
+ const trimmed = value.trim();
3553
+ if (!trimmed.startsWith("[")) return value;
3554
+ return leadingJsonArray(trimmed) ?? value;
3555
+ }
3556
+ __name(parseChildrenIfStringified, "parseChildrenIfStringified");
3557
+ function leadingJsonArray(text) {
3558
+ let depth = 0;
3559
+ let inString = false;
3560
+ let escaped = false;
3561
+ for (let index = 0; index < text.length; index += 1) {
3562
+ const char = text[index];
3563
+ if (inString) {
3564
+ if (escaped) escaped = false;
3565
+ else if (char === "\\") escaped = true;
3566
+ else if (char === '"') inString = false;
3567
+ continue;
3568
+ }
3569
+ if (char === '"') inString = true;
3570
+ else if (char === "[" || char === "{") depth += 1;
3571
+ else if (char === "]" || char === "}") {
3572
+ depth -= 1;
3573
+ if (depth === 0) {
3574
+ try {
3575
+ const parsed = JSON.parse(text.slice(0, index + 1));
3576
+ return Array.isArray(parsed) ? parsed : void 0;
3577
+ } catch {
3578
+ return void 0;
3579
+ }
3580
+ }
3581
+ }
3582
+ }
3583
+ return void 0;
3584
+ }
3585
+ __name(leadingJsonArray, "leadingJsonArray");
3586
+
3587
+ // src/genui/text.ts
3588
+ function componentToText(catalog, component, props) {
3589
+ if (component === GENUI_TREE_COMPONENT) {
3590
+ const root = props.root;
3591
+ return root === void 0 ? "" : treeToText(catalog, root);
3592
+ }
3593
+ const fallback = catalog.get(component)?.fallbackText;
3594
+ if (fallback !== void 0) {
3595
+ try {
3596
+ const text = fallback(props);
3597
+ if (typeof text === "string" && text.trim().length > 0) return text;
3598
+ } catch {
3599
+ }
3600
+ }
3601
+ return jsonBlock(props);
3602
+ }
3603
+ __name(componentToText, "componentToText");
3604
+ function treeToText(catalog, root) {
3605
+ const out = [];
3606
+ const visit = /* @__PURE__ */ __name((node, depth) => {
3607
+ if (depth > 32) return;
3608
+ const definition = catalog.get(node.type);
3609
+ if (definition?.fallbackText !== void 0) {
3610
+ try {
3611
+ const text = definition.fallbackText(node.props);
3612
+ out.push(typeof text === "string" && text.trim().length > 0 ? text : jsonBlock(node.props));
3613
+ } catch {
3614
+ out.push(jsonBlock(node.props));
3615
+ }
3616
+ } else if (definition?.children !== true) {
3617
+ out.push(jsonBlock(node.props));
3618
+ }
3619
+ for (const child of node.children ?? []) visit(child, depth + 1);
3620
+ }, "visit");
3621
+ visit(root, 0);
3622
+ return out.filter((line) => line.trim().length > 0).join("\n");
3623
+ }
3624
+ __name(treeToText, "treeToText");
3625
+ function jsonBlock(props) {
3626
+ const title = typeof props.title === "string" && props.title.length > 0 ? `*${props.title}*
3627
+ ` : "";
3628
+ return `${title}\`\`\`
3629
+ ${JSON.stringify(props, null, 2)}
3630
+ \`\`\``;
3631
+ }
3632
+ __name(jsonBlock, "jsonBlock");
3633
+
3634
+ // src/genui/capabilities.ts
3635
+ function validateUiCapabilities(value) {
3636
+ if (!record(value) || Object.keys(value).some((key) => key !== "components") || !Array.isArray(value.components)) throw new TypeError("Invalid UI capabilities");
3637
+ const seen = /* @__PURE__ */ new Set();
3638
+ const components = value.components.map((component) => {
3639
+ if (!record(component) || Object.keys(component).some((key) => key !== "name" && key !== "version") || typeof component.name !== "string" || !COMPONENT_NAME.test(component.name) || !Number.isSafeInteger(component.version) || Number(component.version) <= 0 || seen.has(component.name)) throw new TypeError("Invalid UI component capability");
3640
+ seen.add(component.name);
3641
+ return {
3642
+ name: component.name,
3643
+ version: Number(component.version)
3644
+ };
3645
+ });
3646
+ return {
3647
+ components
3648
+ };
3649
+ }
3650
+ __name(validateUiCapabilities, "validateUiCapabilities");
3651
+ function record(value) {
3652
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3653
+ }
3654
+ __name(record, "record");
3655
+ function negotiateCatalog(catalog, capabilities) {
3656
+ if (capabilities === void 0) return catalog;
3657
+ const supported = validateUiCapabilities(capabilities).components;
3658
+ return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), {
3659
+ jsonSchemaValidator: catalog.validator
3660
+ });
3661
+ }
3662
+ __name(negotiateCatalog, "negotiateCatalog");
3663
+ async function prepareUiEmission(catalog, capabilities, component, props, version) {
3664
+ const drawable = negotiateCatalog(catalog, capabilities);
3665
+ if (component === GENUI_TREE_COMPONENT) {
3666
+ if (version !== void 0 && version !== 1) throw new Error("Invalid UI tree version");
3667
+ const validated2 = await validateTree(catalog, props.root);
3668
+ if (!validated2.ok) throw new Error(`Invalid UI tree: ${formatIssues(validated2.issues)}`);
3669
+ const supported = /* @__PURE__ */ __name((node) => drawable.has(node.type) && (node.children ?? []).every(supported), "supported");
3670
+ const versions = /* @__PURE__ */ new Map();
3671
+ const collectVersions = /* @__PURE__ */ __name((node) => {
3672
+ versions.set(node.type, catalog.get(node.type)?.version ?? 1);
3673
+ for (const child of node.children ?? []) collectVersions(child);
3674
+ }, "collectVersions");
3675
+ collectVersions(validated2.value);
3676
+ return supported(validated2.value) ? {
3677
+ kind: "ui",
3678
+ component,
3679
+ props: {
3680
+ root: validated2.value
3681
+ },
3682
+ version: 1,
3683
+ fallbackText: treeToText(catalog, validated2.value),
3684
+ componentVersions: Object.fromEntries(versions)
3685
+ } : {
3686
+ kind: "text",
3687
+ text: treeToText(catalog, validated2.value)
3688
+ };
3689
+ }
3690
+ const definition = catalog.get(component);
3691
+ if (definition === void 0) throw new Error(`Unknown UI component "${component}"`);
3692
+ const currentVersion = definition.version ?? 1;
3693
+ if (version !== void 0 && version !== currentVersion) throw new Error(`Invalid UI component version for "${component}"`);
3694
+ const validated = await catalog.validate(component, props);
3695
+ if (!validated.ok) throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
3696
+ return drawable.has(component) ? {
3697
+ kind: "ui",
3698
+ component,
3699
+ props: validated.value,
3700
+ version: currentVersion,
3701
+ fallbackText: componentToText(catalog, component, validated.value)
3702
+ } : {
3703
+ kind: "text",
3704
+ text: componentToText(catalog, component, validated.value)
3705
+ };
3706
+ }
3707
+ __name(prepareUiEmission, "prepareUiEmission");
3708
+
3709
+ // src/action-proposal-transitions.ts
3710
+ function checkTime(value) {
3711
+ if (!Number.isSafeInteger(value)) throw new RangeError("Action proposal timestamps must be safe integer milliseconds");
3712
+ }
3713
+ __name(checkTime, "checkTime");
3714
+ function checkString(value) {
3715
+ if (typeof value !== "string" || value.length === 0) throw new TypeError("Action proposal identifiers must be nonempty strings");
3716
+ }
3717
+ __name(checkString, "checkString");
3718
+ function validateActionProposalCreation(input) {
3719
+ const allowed = [
3720
+ "id",
3721
+ "tenantRef",
3722
+ "actorRef",
3723
+ "threadId",
3724
+ "originRunId",
3725
+ "originMessageId",
3726
+ "originToolCallId",
3727
+ "toolName",
3728
+ "input",
3729
+ "preparationInput",
3730
+ "executionContext",
3731
+ "confirmation",
3732
+ "approver",
3733
+ "expiresAt",
3734
+ "idempotencyKey",
3735
+ "replacementKey"
3736
+ ];
3737
+ if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("Unknown action proposal creation field");
3738
+ for (const key of [
3739
+ "id",
3740
+ "actorRef",
3741
+ "threadId",
3742
+ "originRunId",
3743
+ "originMessageId",
3744
+ "originToolCallId",
3745
+ "toolName",
3746
+ "approver",
3747
+ "idempotencyKey"
3748
+ ]) checkString(input[key]);
3749
+ if (input.tenantRef !== null && typeof input.tenantRef !== "string") throw new TypeError("tenantRef must be an explicit string or null");
3750
+ for (const value of [
3751
+ input.id,
3752
+ input.actorRef,
3753
+ input.threadId,
3754
+ input.tenantRef
3755
+ ]) {
3756
+ if (value !== null && value.length > 255) throw new RangeError("Action proposal id and scope must fit 255 UTF-16 code units");
3757
+ }
3758
+ if (input.replacementKey !== void 0) {
3759
+ checkString(input.replacementKey);
3760
+ if (input.replacementKey.length > 255) throw new RangeError("replacementKey must fit 255 UTF-16 code units");
3761
+ }
3762
+ if (Object.hasOwn(input, "executionContext")) {
3763
+ const context = input.executionContext;
3764
+ if (context === null || typeof context !== "object" || Array.isArray(context)) throw new TypeError("executionContext must be a JSON object");
3765
+ const allowedContext = [
3766
+ "agentName",
3767
+ "persona",
3768
+ "requestId",
3769
+ "pageContext",
3770
+ "uiCapabilities"
3771
+ ];
3772
+ if (Object.keys(context).some((key) => !allowedContext.includes(key))) throw new TypeError("Unknown execution context field");
3773
+ checkString(context.requestId);
3774
+ if (Object.hasOwn(context, "uiCapabilities")) validateUiCapabilities(context.uiCapabilities);
3775
+ if (Object.hasOwn(context, "agentName")) checkString(context.agentName);
3776
+ if (Object.hasOwn(context, "persona")) checkString(context.persona);
3777
+ if (Object.hasOwn(context, "pageContext")) {
3778
+ const page = context.pageContext;
3779
+ if (page === null || typeof page !== "object" || Array.isArray(page)) throw new TypeError("pageContext must be a JSON object");
3780
+ if (Object.hasOwn(page, "kind") && typeof page.kind !== "string") throw new TypeError("pageContext.kind must be a string");
3781
+ }
3782
+ }
3783
+ if (input.expiresAt !== null) checkTime(input.expiresAt);
3784
+ checkString(input.confirmation?.title);
3785
+ checkString(input.confirmation?.verb);
3786
+ canonicalActionProposalJson(input);
3787
+ }
3788
+ __name(validateActionProposalCreation, "validateActionProposalCreation");
3789
+ function validateActionProposalListQuery(query = {}) {
3790
+ const limit = query.limit === void 0 ? 100 : query.limit;
3791
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
3792
+ if (query.decision !== void 0 && ![
3793
+ "pending",
3794
+ "approved",
3795
+ "rejected",
3796
+ "expired",
3797
+ "superseded"
3798
+ ].includes(query.decision)) throw new TypeError("Unsupported action proposal list decision");
3799
+ if (query.after !== void 0) {
3800
+ const after = query.after;
3801
+ if (typeof after !== "object" || after === null || Array.isArray(after) || Object.keys(after).some((key) => key !== "createdAt" && key !== "id")) throw new TypeError("Invalid action proposal cursor");
3802
+ checkTime(after.createdAt);
3803
+ checkString(after.id);
3804
+ if (after.id.length > 255) throw new TypeError("Action proposal cursor id is too long");
3805
+ }
3806
+ return limit;
3807
+ }
3808
+ __name(validateActionProposalListQuery, "validateActionProposalListQuery");
3809
+ function canonicalActionProposalJson(value) {
3810
+ const ancestors = /* @__PURE__ */ new Set();
3811
+ const sort = /* @__PURE__ */ __name((item) => {
3812
+ if (item === null || typeof item === "string" || typeof item === "boolean") return item;
3813
+ if (typeof item === "number" && Number.isFinite(item)) return item;
3814
+ if (typeof item !== "object" || item === null || ancestors.has(item)) throw new TypeError("Action proposals require finite, acyclic JSON values");
3815
+ const prototype = Object.getPrototypeOf(item);
3816
+ if (!Array.isArray(item) && prototype !== Object.prototype && prototype !== null) throw new TypeError("Action proposals require plain JSON objects");
3817
+ ancestors.add(item);
3818
+ const sorted = Array.isArray(item) ? Array.from(item, sort) : Object.fromEntries(Object.keys(item).sort().map((key) => [
3819
+ key,
3820
+ sort(item[key])
3821
+ ]));
3822
+ ancestors.delete(item);
3823
+ return sorted;
3824
+ }, "sort");
3825
+ return JSON.stringify(sort(value));
3826
+ }
3827
+ __name(canonicalActionProposalJson, "canonicalActionProposalJson");
3828
+ function snapshotActionProposal(value) {
3829
+ return JSON.parse(canonicalActionProposalJson(value));
3830
+ }
3831
+ __name(snapshotActionProposal, "snapshotActionProposal");
3832
+ function actionProposalScopeMatches(row2, scope) {
3833
+ return row2.tenantRef === scope.tenantRef && row2.actorRef === scope.actorRef && row2.threadId === scope.threadId;
3834
+ }
3835
+ __name(actionProposalScopeMatches, "actionProposalScopeMatches");
3836
+ function initialActionProposal(input, now) {
3837
+ validateActionProposalCreation(input);
3838
+ checkTime(now);
3839
+ return snapshotActionProposal({
3840
+ ...input,
3841
+ decision: "pending",
3842
+ decisionAudit: null,
3843
+ execution: null,
3844
+ createdAt: now,
3845
+ updatedAt: now
3846
+ });
3847
+ }
3848
+ __name(initialActionProposal, "initialActionProposal");
3849
+ function actionProposalCreationMatches(row2, input) {
3850
+ validateActionProposalCreation(input);
3851
+ const { decision: _decision, decisionAudit: _audit, execution: _execution, createdAt: _created, updatedAt: _updated, supersededBy: _supersededBy, outcome: _outcome, outcomeDelivery: _delivery, ...original } = row2;
3852
+ return canonicalActionProposalJson(original) === canonicalActionProposalJson(input);
3853
+ }
3854
+ __name(actionProposalCreationMatches, "actionProposalCreationMatches");
3855
+ function result(status, proposal) {
3856
+ return {
3857
+ status,
3858
+ proposal: snapshotActionProposal(proposal)
3859
+ };
3860
+ }
3861
+ __name(result, "result");
3862
+ function transitionActionProposalDecision(row2, command, now) {
3863
+ checkTime(now);
3864
+ if (![
3865
+ "approved",
3866
+ "rejected",
3867
+ "expired"
3868
+ ].includes(command.decision)) throw new TypeError("Unsupported action proposal decision");
3869
+ checkString(command.actorRef);
3870
+ checkString(command.via);
3871
+ if (command.reason !== void 0 && typeof command.reason !== "string") throw new TypeError("reason must be a string");
3872
+ if (command.remember !== void 0 && typeof command.remember !== "boolean") throw new TypeError("remember must be a boolean");
3873
+ if (row2.decision !== "pending") {
3874
+ return result(row2.decision === command.decision ? "unchanged" : row2.decision === "expired" ? "expired" : "conflict", row2);
3875
+ }
3876
+ const next = snapshotActionProposal(row2);
3877
+ if (row2.expiresAt !== null && now >= row2.expiresAt) {
3878
+ next.decision = "expired";
3879
+ next.decisionAudit = {
3880
+ actorRef: "system",
3881
+ via: "expiry",
3882
+ at: now
3883
+ };
3884
+ next.updatedAt = now;
3885
+ return result(command.decision === "expired" ? "applied" : "expired", attachActionProposalOutcome(next, now));
3886
+ }
3887
+ if (command.decision === "expired") return result("conflict", row2);
3888
+ const { decision, ...audit } = command;
3889
+ next.decision = decision;
3890
+ next.decisionAudit = {
3891
+ actorRef: audit.actorRef,
3892
+ via: audit.via,
3893
+ at: now,
3894
+ ...audit.reason === void 0 ? {} : {
3895
+ reason: audit.reason
3896
+ },
3897
+ ...audit.remember === void 0 ? {} : {
3898
+ remember: audit.remember
3899
+ }
3900
+ };
3901
+ next.updatedAt = now;
3902
+ if (decision === "approved") next.execution = {
3903
+ status: "queued",
3904
+ generation: 0,
3905
+ lease: null
3906
+ };
3907
+ return result("applied", decision === "approved" ? next : attachActionProposalOutcome(next, now));
3908
+ }
3909
+ __name(transitionActionProposalDecision, "transitionActionProposalDecision");
3910
+ function checkLeaseMs(leaseMs, now) {
3911
+ checkTime(now);
3912
+ if (!Number.isSafeInteger(leaseMs) || leaseMs <= 0 || !Number.isSafeInteger(now + leaseMs)) throw new RangeError("leaseMs must be finite and positive");
3913
+ }
3914
+ __name(checkLeaseMs, "checkLeaseMs");
3915
+ function transitionActionProposalClaim(row2, command, now, token) {
3916
+ checkLeaseMs(command.leaseMs, now);
3917
+ checkString(command.workerId);
3918
+ checkString(token);
3919
+ const execution = row2.execution;
3920
+ if (row2.decision !== "approved" || !execution || execution.status !== "queued" && !(execution.status === "executing" && execution.lease && now >= execution.lease.expiresAt)) return result("conflict", row2);
3921
+ const next = snapshotActionProposal(row2);
3922
+ const generation = execution.generation + 1;
3923
+ next.execution = {
3924
+ status: "executing",
3925
+ generation,
3926
+ lease: {
3927
+ token,
3928
+ generation,
3929
+ workerId: command.workerId,
3930
+ expiresAt: now + command.leaseMs
3931
+ }
3932
+ };
3933
+ next.updatedAt = now;
3934
+ return result("applied", next);
3935
+ }
3936
+ __name(transitionActionProposalClaim, "transitionActionProposalClaim");
3937
+ function leaseStatus(row2, command, now) {
3938
+ checkTime(now);
3939
+ checkString(command.token);
3940
+ if (!Number.isSafeInteger(command.generation) || command.generation < 1) throw new RangeError("Invalid lease generation");
3941
+ const work = row2.execution;
3942
+ if (row2.decision !== "approved" || work?.status !== "executing" || !work.lease || work.generation !== command.generation || work.lease.generation !== command.generation || work.lease.token !== command.token) return "conflict";
3943
+ return now >= work.lease.expiresAt ? "expired" : "valid";
3944
+ }
3945
+ __name(leaseStatus, "leaseStatus");
3946
+ function transitionActionProposalLease(row2, command, now) {
3947
+ checkLeaseMs(command.leaseMs, now);
3948
+ const status = leaseStatus(row2, command, now);
3949
+ if (status !== "valid") return result(status, row2);
3950
+ const next = snapshotActionProposal(row2);
3951
+ if (next.execution?.lease) next.execution.lease.expiresAt = Math.max(next.execution.lease.expiresAt, now + command.leaseMs);
3952
+ next.updatedAt = now;
3953
+ return result("applied", next);
3954
+ }
3955
+ __name(transitionActionProposalLease, "transitionActionProposalLease");
3956
+ function transitionActionProposalSettlement(row2, command, now) {
3957
+ checkTime(now);
3958
+ if (command.status !== "succeeded" && command.status !== "failed") throw new TypeError("Unsupported settlement status");
3959
+ if (command.status === "succeeded" && command.error !== void 0 || command.status === "failed" && (typeof command.error !== "string" || command.result !== void 0)) throw new TypeError("Ambiguous action proposal settlement");
3960
+ const status = leaseStatus(row2, command, now);
3961
+ if (status !== "valid") return result(status, row2);
3962
+ const next = snapshotActionProposal(row2);
3963
+ next.execution = command.status === "succeeded" ? {
3964
+ status: "succeeded",
3965
+ generation: command.generation,
3966
+ lease: null,
3967
+ ...command.result === void 0 ? {} : {
3968
+ result: command.result
3969
+ }
3970
+ } : {
3971
+ status: "failed",
3972
+ generation: command.generation,
3973
+ lease: null,
3974
+ error: command.error
3975
+ };
3976
+ if (command.ui !== void 0) canonicalActionProposalJson(command.ui);
3977
+ if (command.text !== void 0 && typeof command.text !== "string") throw new TypeError("Outcome text must be a string");
3978
+ next.updatedAt = now;
3979
+ return result("applied", attachActionProposalOutcome(next, now, command.ui, command.text));
3980
+ }
3981
+ __name(transitionActionProposalSettlement, "transitionActionProposalSettlement");
3982
+ function transitionActionProposalSupersession(row2, replacement, command, now) {
3983
+ checkTime(now);
3984
+ checkString(command.replacementProposalId);
3985
+ checkString(command.actorRef);
3986
+ checkString(command.via);
3987
+ if (replacement.id !== command.replacementProposalId || row2.id === replacement.id || !actionProposalScopeMatches(row2, replacement) || row2.toolName !== replacement.toolName || !row2.replacementKey || row2.replacementKey !== replacement.replacementKey) return result("conflict", row2);
3988
+ if (row2.decision === "superseded") return result(row2.supersededBy === replacement.id ? "unchanged" : "conflict", row2);
3989
+ if (row2.decision !== "pending") return result("conflict", row2);
3990
+ if (row2.expiresAt !== null && now >= row2.expiresAt) return {
3991
+ ...transitionActionProposalDecision(row2, {
3992
+ decision: "expired",
3993
+ actorRef: "system",
3994
+ via: "expiry"
3995
+ }, now),
3996
+ status: "expired"
3997
+ };
3998
+ const next = snapshotActionProposal({
3999
+ ...row2,
4000
+ decision: "superseded",
4001
+ supersededBy: replacement.id,
4002
+ decisionAudit: {
4003
+ actorRef: command.actorRef,
4004
+ via: command.via,
4005
+ replacementProposalId: replacement.id,
4006
+ at: now
4007
+ },
4008
+ updatedAt: now
4009
+ });
4010
+ return result("applied", attachActionProposalOutcome(next, now));
4011
+ }
4012
+ __name(transitionActionProposalSupersession, "transitionActionProposalSupersession");
4013
+
4014
+ // src/genui/builtins.ts
4015
+ var str = /* @__PURE__ */ __name((description, extra = {}) => ({
4016
+ type: "string",
4017
+ ...description !== void 0 ? {
4018
+ description
4019
+ } : {},
4020
+ ...extra
4021
+ }), "str");
4022
+ var obj = /* @__PURE__ */ __name((properties, required = []) => ({
4023
+ type: "object",
4024
+ properties,
4025
+ required,
4026
+ additionalProperties: false
4027
+ }), "obj");
4028
+ var arr = /* @__PURE__ */ __name((items, extra = {}) => ({
4029
+ type: "array",
4030
+ items,
4031
+ ...extra
4032
+ }), "arr");
4033
+ var cell = {
4034
+ type: [
4035
+ "string",
4036
+ "number",
4037
+ "boolean",
4038
+ "null"
4039
+ ]
4040
+ };
4041
+ var row = {
4042
+ type: "object",
4043
+ additionalProperties: cell
4044
+ };
4045
+ var s = /* @__PURE__ */ __name((value) => value === null || value === void 0 ? "" : String(value), "s");
4046
+ var heading = /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*
4047
+ ` : "", "heading");
4048
+ function textTable(columns, rows, max = 15) {
4049
+ const shown = rows.slice(0, max);
4050
+ const widths = columns.map((column) => Math.min(28, Math.max(column.label.length, ...shown.map((r) => s(r[column.key]).length))));
4051
+ const format = /* @__PURE__ */ __name((values) => values.map((value, index) => value.slice(0, widths[index]).padEnd(widths[index] ?? 0)).join(" "), "format");
4052
+ const lines = [
4053
+ format(columns.map((column) => column.label)),
4054
+ widths.map((width) => "-".repeat(width)).join(" "),
4055
+ ...shown.map((r) => format(columns.map((column) => s(r[column.key]))))
4056
+ ];
4057
+ if (rows.length > max) lines.push(`\u2026 ${rows.length - max} more rows`);
4058
+ return `\`\`\`
4059
+ ${lines.join("\n")}
4060
+ \`\`\``;
4061
+ }
4062
+ __name(textTable, "textTable");
4063
+ function lineDiff(before, after) {
4064
+ const a = before.split("\n");
4065
+ const b = after.split("\n");
4066
+ if (a.length * b.length > 25e4) {
4067
+ return [
4068
+ ...a.map((text) => ({
4069
+ op: "del",
4070
+ text
4071
+ })),
4072
+ ...b.map((text) => ({
4073
+ op: "add",
4074
+ text
4075
+ }))
4076
+ ];
4077
+ }
4078
+ const dp = Array.from({
4079
+ length: a.length + 1
4080
+ }, () => new Array(b.length + 1).fill(0));
4081
+ const at = /* @__PURE__ */ __name((i2, j2) => dp[i2]?.[j2] ?? 0, "at");
4082
+ for (let i2 = a.length - 1; i2 >= 0; i2--) {
4083
+ for (let j2 = b.length - 1; j2 >= 0; j2--) {
4084
+ dp[i2][j2] = a[i2] === b[j2] ? at(i2 + 1, j2 + 1) + 1 : Math.max(at(i2 + 1, j2), at(i2, j2 + 1));
4085
+ }
4086
+ }
4087
+ const out = [];
4088
+ let i = 0;
4089
+ let j = 0;
4090
+ while (i < a.length && j < b.length) {
4091
+ if (a[i] === b[j]) {
4092
+ out.push({
4093
+ op: "same",
4094
+ text: a[i]
4095
+ });
4096
+ i++;
4097
+ j++;
4098
+ } else if (at(i + 1, j) >= at(i, j + 1)) {
4099
+ out.push({
4100
+ op: "del",
4101
+ text: a[i++]
4102
+ });
4103
+ } else {
4104
+ out.push({
4105
+ op: "add",
4106
+ text: b[j++]
4107
+ });
4108
+ }
4109
+ }
4110
+ while (i < a.length) out.push({
4111
+ op: "del",
4112
+ text: a[i++]
4113
+ });
4114
+ while (j < b.length) out.push({
4115
+ op: "add",
4116
+ text: b[j++]
4117
+ });
4118
+ return out;
4119
+ }
4120
+ __name(lineDiff, "lineDiff");
4121
+ function bar(value, max, width = 20) {
4122
+ const n = max > 0 ? Math.round(Math.abs(value) / max * width) : 0;
4123
+ return "\u2588".repeat(n) || "\u258F";
4124
+ }
4125
+ __name(bar, "bar");
4126
+ var DataTable = defineComponent({
4127
+ name: "DataTable",
4128
+ title: "Data table",
4129
+ description: "Tabular data (records with the same fields) as a sortable table. Prefer it over markdown tables for more than 3 rows.",
4130
+ props: obj({
4131
+ title: str("Short caption"),
4132
+ columns: arr(obj({
4133
+ key: str("Field name in each row"),
4134
+ label: str("Column header"),
4135
+ align: str(void 0, {
4136
+ enum: [
4137
+ "left",
4138
+ "right",
4139
+ "center"
4140
+ ]
4141
+ }),
4142
+ format: str("How to format values", {
4143
+ enum: [
4144
+ "text",
4145
+ "number",
4146
+ "currency",
4147
+ "percent",
4148
+ "date",
4149
+ "link"
4150
+ ]
4151
+ })
4152
+ }, [
4153
+ "key",
4154
+ "label"
4155
+ ]), {
4156
+ minItems: 1,
4157
+ maxItems: 12
4158
+ }),
4159
+ rows: arr(row, {
4160
+ maxItems: 500
4161
+ })
4162
+ }, [
4163
+ "columns",
4164
+ "rows"
4165
+ ]),
4166
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${textTable(props.columns ?? [], props.rows ?? [])}`, "fallbackText")
4167
+ });
4168
+ var Chart = defineComponent({
4169
+ name: "Chart",
4170
+ title: "Chart",
4171
+ description: "`bar` to compare categories, `line` for a trend over time. `data` is a list of points; `xKey` names the category/time field and each series names a numeric field.",
4172
+ props: obj({
4173
+ type: str("Chart type", {
4174
+ enum: [
4175
+ "bar",
4176
+ "line"
4177
+ ]
4178
+ }),
4179
+ title: str(),
4180
+ xKey: str("Field of each point used for the x axis"),
4181
+ series: arr(obj({
4182
+ key: str("Numeric field"),
4183
+ label: str()
4184
+ }, [
4185
+ "key"
4186
+ ]), {
4187
+ minItems: 1,
4188
+ maxItems: 6
4189
+ }),
4190
+ data: arr(row, {
4191
+ minItems: 1,
4192
+ maxItems: 200
4193
+ }),
4194
+ unit: str("Unit shown after values, e.g. `KB` or `%`")
4195
+ }, [
4196
+ "type",
4197
+ "xKey",
4198
+ "series",
4199
+ "data"
4200
+ ]),
4201
+ fallbackText: /* @__PURE__ */ __name((props) => {
4202
+ const series = props.series ?? [];
4203
+ const data = props.data ?? [];
4204
+ const first = series[0]?.key ?? "";
4205
+ const max = Math.max(0, ...data.map((point) => Math.abs(Number(point[first]) || 0)));
4206
+ const unit = props.unit ? ` ${s(props.unit)}` : "";
4207
+ const lines = data.slice(0, 20).map((point) => `${s(point[props.xKey]).slice(0, 18).padEnd(18)} ${bar(Number(point[first]) || 0, max)} ${s(point[first])}${unit}`);
4208
+ return `${heading(props)}\`\`\`
4209
+ ${lines.join("\n")}
4210
+ \`\`\``;
4211
+ }, "fallbackText")
4212
+ });
4213
+ var KpiCards = defineComponent({
4214
+ name: "KpiCards",
4215
+ title: "KPI cards",
4216
+ description: "A few key numbers (metrics, totals, counts) as cards.",
4217
+ props: obj({
4218
+ title: str(),
4219
+ items: arr(obj({
4220
+ label: str(),
4221
+ value: {
4222
+ type: [
4223
+ "string",
4224
+ "number"
4225
+ ]
4226
+ },
4227
+ delta: str("Change, e.g. `+12%`"),
4228
+ trend: str(void 0, {
4229
+ enum: [
4230
+ "up",
4231
+ "down",
4232
+ "flat"
4233
+ ]
4234
+ }),
4235
+ hint: str()
4236
+ }, [
4237
+ "label",
4238
+ "value"
4239
+ ]), {
4240
+ minItems: 1,
4241
+ maxItems: 8
4242
+ })
4243
+ }, [
4244
+ "items"
4245
+ ]),
4246
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `\u2022 *${s(item.label)}:* ${s(item.value)}${item.delta ? ` (${s(item.delta)})` : ""}`).join("\n")}`, "fallbackText")
4247
+ });
4248
+ var SourceCards = defineComponent({
4249
+ name: "SourceCards",
4250
+ title: "Sources",
4251
+ description: "The documents, pages or links an answer is based on, as cards.",
4252
+ props: obj({
4253
+ title: str(),
4254
+ items: arr(obj({
4255
+ title: str(),
4256
+ url: str(),
4257
+ snippet: str(),
4258
+ source: str("e.g. Wiki, GitHub")
4259
+ }, [
4260
+ "title"
4261
+ ]), {
4262
+ minItems: 1,
4263
+ maxItems: 12
4264
+ })
4265
+ }, [
4266
+ "items"
4267
+ ]),
4268
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `\u2022 ${item.url ? `<${s(item.url)}|${s(item.title)}>` : s(item.title)}${item.source ? ` \u2014 ${s(item.source)}` : ""}`).join("\n")}`, "fallbackText")
4269
+ });
4270
+ var Checklist = defineComponent({
4271
+ name: "Checklist",
4272
+ title: "Checklist",
4273
+ description: "Steps or a to-do list with done/not-done state.",
4274
+ props: obj({
4275
+ title: str(),
4276
+ items: arr(obj({
4277
+ label: str(),
4278
+ done: {
4279
+ type: "boolean"
4280
+ },
4281
+ note: str()
4282
+ }, [
4283
+ "label"
4284
+ ]), {
4285
+ minItems: 1,
4286
+ maxItems: 50
4287
+ })
4288
+ }, [
4289
+ "items"
4290
+ ]),
4291
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `${item.done ? "\u2611" : "\u2610"} ${s(item.label)}${item.note ? ` \u2014 _${s(item.note)}_` : ""}`).join("\n")}`, "fallbackText")
4292
+ });
4293
+ var Timeline = defineComponent({
4294
+ name: "Timeline",
4295
+ title: "Timeline",
4296
+ description: "Events in chronological order (the history of a project, an incident, a deal).",
4297
+ props: obj({
4298
+ title: str(),
4299
+ events: arr(obj({
4300
+ date: str(),
4301
+ title: str(),
4302
+ description: str()
4303
+ }, [
4304
+ "title"
4305
+ ]), {
4306
+ minItems: 1,
4307
+ maxItems: 50
4308
+ })
4309
+ }, [
4310
+ "events"
4311
+ ]),
4312
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.events ?? []).map((event) => `\u2022 ${event.date ? `*${s(event.date)}* ` : ""}${s(event.title)}${event.description ? ` \u2014 ${s(event.description)}` : ""}`).join("\n")}`, "fallbackText")
4313
+ });
4314
+ var CodeBlock = defineComponent({
4315
+ name: "CodeBlock",
4316
+ title: "Code",
4317
+ description: "A code snippet or file excerpt with syntax highlighting.",
4318
+ props: obj({
4319
+ title: str(),
4320
+ language: str(),
4321
+ code: str()
4322
+ }, [
4323
+ "code"
4324
+ ]),
4325
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
4326
+ ${s(props.code)}
4327
+ \`\`\``, "fallbackText")
4328
+ });
4329
+ var Diff = defineComponent({
4330
+ name: "Diff",
4331
+ title: "Diff",
4332
+ description: "A before/after change to a text or code file, as a line diff.",
4333
+ props: obj({
4334
+ title: str(),
4335
+ language: str(),
4336
+ before: str(),
4337
+ after: str()
4338
+ }, [
4339
+ "before",
4340
+ "after"
4341
+ ]),
4342
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
4343
+ ${lineDiff(s(props.before), s(props.after)).map((line) => `${line.op === "add" ? "+" : line.op === "del" ? "-" : " "} ${line.text}`).join("\n")}
4344
+ \`\`\``, "fallbackText")
4345
+ });
4346
+ var Callout = defineComponent({
4347
+ name: "Callout",
4348
+ title: "Callout",
4349
+ description: "A short note, warning or success message, emphasized.",
4350
+ props: obj({
4351
+ tone: str(void 0, {
4352
+ enum: [
4353
+ "info",
4354
+ "success",
4355
+ "warning",
4356
+ "danger"
4357
+ ]
4358
+ }),
4359
+ title: str(),
4360
+ text: str()
4361
+ }, [
4362
+ "text"
4363
+ ]),
4364
+ fallbackText: /* @__PURE__ */ __name((props) => `${props.tone === "warning" || props.tone === "danger" ? ":warning: " : ":information_source: "}${heading(props)}${s(props.text)}`, "fallbackText")
4365
+ });
4366
+ var Stack = defineComponent({
4367
+ name: "Stack",
4368
+ title: "Stack",
4369
+ description: "Lays its children out vertically (or horizontally with direction=row).",
4370
+ props: obj({
4371
+ direction: str(void 0, {
4372
+ enum: [
4373
+ "column",
4374
+ "row"
4375
+ ]
4376
+ }),
4377
+ gap: {
4378
+ type: "number"
4379
+ }
4380
+ }),
4381
+ children: true,
4382
+ fallbackText: /* @__PURE__ */ __name(() => "", "fallbackText")
4383
+ });
4384
+ var Card = defineComponent({
4385
+ name: "Card",
4386
+ title: "Card",
4387
+ description: "A box with an optional title around its children.",
4388
+ props: obj({
4389
+ title: str(),
4390
+ subtitle: str()
4391
+ }),
4392
+ children: true,
4393
+ fallbackText: /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*${props.subtitle ? ` \u2014 ${s(props.subtitle)}` : ""}` : "", "fallbackText")
4394
+ });
4395
+ var Heading = defineComponent({
4396
+ name: "Heading",
4397
+ title: "Heading",
4398
+ description: "A heading.",
4399
+ props: obj({
4400
+ text: str()
4401
+ }, [
4402
+ "text"
4403
+ ]),
4404
+ fallbackText: /* @__PURE__ */ __name((props) => `*${s(props.text)}*`, "fallbackText")
4405
+ });
4406
+ var Text = defineComponent({
4407
+ name: "Text",
4408
+ title: "Text",
4409
+ description: "A paragraph of plain text (no markdown).",
4410
+ props: obj({
4411
+ text: str(),
4412
+ muted: {
4413
+ type: "boolean"
4414
+ }
4415
+ }, [
4416
+ "text"
4417
+ ]),
4418
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.text), "fallbackText")
4419
+ });
4420
+ var Badge = defineComponent({
4421
+ name: "Badge",
4422
+ title: "Badge",
4423
+ description: "A small label.",
4424
+ props: obj({
4425
+ text: str(),
4426
+ tone: str(void 0, {
4427
+ enum: [
4428
+ "neutral",
4429
+ "brand",
4430
+ "success",
4431
+ "warning",
4432
+ "danger"
4433
+ ]
4434
+ })
4435
+ }, [
4436
+ "text"
4437
+ ]),
4438
+ fallbackText: /* @__PURE__ */ __name((props) => `[${s(props.text)}]`, "fallbackText")
4439
+ });
4440
+ var Link = defineComponent({
4441
+ name: "Link",
4442
+ title: "Link",
4443
+ description: "A link to an http(s) URL.",
4444
+ props: obj({
4445
+ text: str(),
4446
+ url: str(void 0, {
4447
+ pattern: "^https?://"
4448
+ })
4449
+ }, [
4450
+ "text",
4451
+ "url"
4452
+ ]),
4453
+ fallbackText: /* @__PURE__ */ __name((props) => `<${s(props.url)}|${s(props.text || props.url)}>`, "fallbackText")
4454
+ });
4455
+ var Image = defineComponent({
4456
+ name: "Image",
4457
+ title: "Image",
4458
+ description: "An image from an https URL.",
4459
+ props: obj({
4460
+ url: str(void 0, {
4461
+ pattern: "^https://"
4462
+ }),
4463
+ alt: str()
4464
+ }, [
4465
+ "url"
4466
+ ]),
4467
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.url), "fallbackText")
4468
+ });
4469
+
4470
+ // src/genui/registry.ts
4471
+ function snapshotComponentPresentation(value) {
4472
+ const seen = /* @__PURE__ */ new Set();
4473
+ const check2 = /* @__PURE__ */ __name((entry) => {
4474
+ if (entry === null || typeof entry === "string" || typeof entry === "boolean") return;
4475
+ if (typeof entry === "number" && Number.isFinite(entry)) return;
4476
+ if (typeof entry !== "object" || entry === null) throw new TypeError("genui: presentation must contain only JSON values");
4477
+ if (seen.has(entry)) throw new TypeError("genui: presentation must contain acyclic JSON values");
4478
+ if (!Array.isArray(entry) && Object.getPrototypeOf(entry) !== Object.prototype && Object.getPrototypeOf(entry) !== null) throw new TypeError("genui: presentation must contain plain JSON objects");
4479
+ seen.add(entry);
4480
+ for (const item of Object.values(entry)) check2(item);
4481
+ seen.delete(entry);
4482
+ }, "check");
4483
+ check2(value);
4484
+ return JSON.parse(JSON.stringify(value));
4485
+ }
4486
+ __name(snapshotComponentPresentation, "snapshotComponentPresentation");
4487
+ function createComponent(definition, options = {}) {
4488
+ defineComponent(definition);
4489
+ const catalog = defineCatalog([
4490
+ definition
4491
+ ], options);
4492
+ const factory = /* @__PURE__ */ __name(async (props) => {
4493
+ const result2 = await validateProps(definition.props, props, catalog.validator);
4494
+ if (!result2.ok) throw new Error(`genui: invalid ${definition.name} props: ${formatIssues(result2.issues)}`);
4495
+ const normalized = snapshotComponentPresentation(result2.value);
4496
+ if (typeof normalized !== "object" || normalized === null || Array.isArray(normalized)) throw new TypeError("genui: component props must be a JSON object");
4497
+ const portable = await catalog.validate(definition.name, normalized);
4498
+ if (!portable.ok || canonicalJson(snapshotComponentPresentation(portable.value)) !== canonicalJson(normalized)) {
4499
+ throw new Error(`genui: ${definition.name} requires outputProps accepting stable normalized JSON props`);
4500
+ }
4501
+ return {
4502
+ component: definition.name,
4503
+ props: normalized,
4504
+ version: definition.version ?? 1,
4505
+ fallbackText: componentToText(catalog, definition.name, normalized)
4506
+ };
4507
+ }, "factory");
4508
+ return Object.assign(factory, {
4509
+ definition
4510
+ });
4511
+ }
4512
+ __name(createComponent, "createComponent");
4513
+ var table = createComponent(DataTable);
4514
+ var chart = createComponent(Chart);
4515
+ function validatePresentationBatch(presentations) {
4516
+ const batch = Array.isArray(presentations) ? presentations : [
4517
+ presentations
4518
+ ];
4519
+ return batch.map((item) => {
4520
+ const presentation = snapshotComponentPresentation(item);
4521
+ if (typeof presentation !== "object" || presentation === null || typeof presentation.component !== "string" || !/^[A-Za-z][A-Za-z0-9]{0,63}$/.test(presentation.component) || !Number.isSafeInteger(presentation.version) || presentation.version <= 0 || typeof presentation.fallbackText !== "string" || typeof presentation.props !== "object" || presentation.props === null || Array.isArray(presentation.props)) {
4522
+ throw new TypeError("genui: invalid component presentation");
4523
+ }
4524
+ return presentation;
4525
+ });
4526
+ }
4527
+ __name(validatePresentationBatch, "validatePresentationBatch");
4528
+ function canonicalJson(value) {
4529
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
4530
+ if (typeof value === "object" && value !== null) {
4531
+ const record2 = value;
4532
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
4533
+ }
4534
+ return JSON.stringify(value);
4535
+ }
4536
+ __name(canonicalJson, "canonicalJson");
4537
+
4538
+ // src/tool-presentations.ts
4539
+ async function emitToolPresentations(handler, output, ctx, toolName) {
4540
+ if (handler.present === void 0) return;
4541
+ try {
4542
+ const result2 = await handler.present(output, ctx);
4543
+ if (result2 === void 0) return;
4544
+ const batch = validatePresentationBatch(result2);
4545
+ for (const presentation of batch) {
4546
+ await ctx.emitUi(presentation.component, presentation.props, {
4547
+ version: presentation.version,
4548
+ fallbackText: presentation.fallbackText
4549
+ });
4550
+ }
4551
+ } catch (error) {
4552
+ if (ctx.onPresentationError !== void 0) {
4553
+ try {
4554
+ await ctx.onPresentationError(error, {
4555
+ toolName
4556
+ });
4557
+ return;
4558
+ } catch (reportError) {
4559
+ console.warn(`Tool "${toolName}" presentation error reporter failed`, reportError);
4560
+ }
4561
+ } else {
4562
+ console.warn(`Tool "${toolName}" presentation failed after successful execution`, error);
4563
+ }
4564
+ }
4565
+ }
4566
+ __name(emitToolPresentations, "emitToolPresentations");
4567
+
2829
4568
  // src/tool-registry.ts
2830
4569
  var ToolForbiddenError = class extends Error {
2831
4570
  static {
@@ -2868,6 +4607,35 @@ var ToolInputInvalidError = class extends Error {
2868
4607
  this.name = "ToolInputInvalidError";
2869
4608
  }
2870
4609
  };
4610
+ var ToolPreflightDeniedError = class extends Error {
4611
+ static {
4612
+ __name(this, "ToolPreflightDeniedError");
4613
+ }
4614
+ toolName;
4615
+ reason;
4616
+ constructor(toolName, reason) {
4617
+ super(reason), this.toolName = toolName, this.reason = reason;
4618
+ this.name = "ToolPreflightDeniedError";
4619
+ }
4620
+ };
4621
+ var ToolInputDriftError = class extends Error {
4622
+ static {
4623
+ __name(this, "ToolInputDriftError");
4624
+ }
4625
+ toolName;
4626
+ constructor(toolName) {
4627
+ super(`Tool "${toolName}" input differs from its trusted snapshot`), this.toolName = toolName;
4628
+ this.name = "ToolInputDriftError";
4629
+ }
4630
+ };
4631
+ function assertTrustedInput(name, value, approved) {
4632
+ try {
4633
+ if (canonicalActionProposalJson(value) === canonicalActionProposalJson(approved)) return;
4634
+ } catch {
4635
+ }
4636
+ throw new ToolInputDriftError(name);
4637
+ }
4638
+ __name(assertTrustedInput, "assertTrustedInput");
2871
4639
  var ToolRegistry = class {
2872
4640
  static {
2873
4641
  __name(this, "ToolRegistry");
@@ -2879,6 +4647,15 @@ var ToolRegistry = class {
2879
4647
  handler
2880
4648
  });
2881
4649
  }
4650
+ /** Whether a successful preflight shortcut has UI to journal. */
4651
+ hasPresentation(name) {
4652
+ return typeof this.entries.get(name)?.handler.present === "function";
4653
+ }
4654
+ /** Present an already authorized successful domain result without executing its action again. */
4655
+ async presentOutput(name, output, ctx) {
4656
+ const entry = this.entries.get(name);
4657
+ if (entry !== void 0) await emitToolPresentations(entry.handler, output, ctx, name);
4658
+ }
2882
4659
  has(name) {
2883
4660
  return this.entries.has(name);
2884
4661
  }
@@ -2915,11 +4692,12 @@ var ToolRegistry = class {
2915
4692
  */
2916
4693
  async definitionsFor(actor, policy, allowedTools, scope = {}) {
2917
4694
  const visible = await this.visibleEntries(actor, policy, allowedTools);
2918
- return Promise.all(visible.map(async ({ spec, handler }) => {
4695
+ const definitions = await Promise.all(visible.map(async ({ spec, handler }) => {
2919
4696
  const override = handler.describe === void 0 ? void 0 : await handler.describe({
2920
4697
  actor,
2921
4698
  ...scope
2922
4699
  });
4700
+ if (override?.available === false) return null;
2923
4701
  return {
2924
4702
  name: spec.name,
2925
4703
  kind: spec.kind,
@@ -2927,6 +4705,7 @@ var ToolRegistry = class {
2927
4705
  inputSchema: override?.inputSchema ?? spec.inputSchema
2928
4706
  };
2929
4707
  }));
4708
+ return definitions.filter((definition) => definition !== null);
2930
4709
  }
2931
4710
  /**
2932
4711
  * The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list,
@@ -2952,6 +4731,41 @@ var ToolRegistry = class {
2952
4731
  * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
2953
4732
  */
2954
4733
  async invoke(name, input, ctx, policy, options = {}) {
4734
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
4735
+ const trusted = Object.hasOwn(options, "approvedInput");
4736
+ if (trusted) assertTrustedInput(name, value, options.approvedInput);
4737
+ const executionInput = trusted ? snapshotActionProposal(value) : value;
4738
+ const hookInput = trusted ? snapshotActionProposal(executionInput) : value;
4739
+ const result2 = entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
4740
+ phase: "execute"
4741
+ }) : void 0;
4742
+ if (trusted) assertTrustedInput(name, hookInput, executionInput);
4743
+ if (result2?.status === "denied") throw new ToolPreflightDeniedError(name, result2.reason);
4744
+ const output = result2?.status === "completed" ? result2.output : await entry.handler.execute(executionInput, withEmit);
4745
+ await emitToolPresentations(entry.handler, output, withEmit, name);
4746
+ return output;
4747
+ }
4748
+ /** Authorize and validate without executing. The caller journals this result before approval. */
4749
+ async prepare(name, input, ctx, policy, options = {}) {
4750
+ return (await this.prepareValidated(name, input, ctx, policy, options)).preflight;
4751
+ }
4752
+ /** Expose the single schema parse; snapshotInput opts proposal callers into immutable JSON. */
4753
+ async prepareValidated(name, input, ctx, policy, options = {}) {
4754
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
4755
+ const normalized = options.snapshotInput === true ? snapshotActionProposal(value) : value;
4756
+ const hookInput = options.snapshotInput === true ? snapshotActionProposal(normalized) : value;
4757
+ const preflight = (entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
4758
+ phase: "prepare"
4759
+ }) : void 0) ?? {
4760
+ status: "ready"
4761
+ };
4762
+ if (options.snapshotInput === true) assertTrustedInput(name, hookInput, normalized);
4763
+ return {
4764
+ input: normalized,
4765
+ preflight
4766
+ };
4767
+ }
4768
+ async validated(name, input, ctx, policy, options) {
2955
4769
  const entry = this.entries.get(name);
2956
4770
  if (entry === void 0) {
2957
4771
  throw new ToolNotFoundError(name);
@@ -2976,7 +4790,11 @@ var ToolRegistry = class {
2976
4790
  ...ctx,
2977
4791
  emitUi: createNoopEmitUi(ctx.requestId)
2978
4792
  };
2979
- return entry.handler.execute(validation.value, withEmit);
4793
+ return {
4794
+ entry,
4795
+ value: validation.value,
4796
+ ctx: withEmit
4797
+ };
2980
4798
  }
2981
4799
  };
2982
4800
  var DefaultRolesPolicy = class {
@@ -2991,6 +4809,7 @@ var DefaultRolesPolicy = class {
2991
4809
  this.emptyRoles = options.emptyRoles ?? "allow";
2992
4810
  }
2993
4811
  can(actor, tool) {
4812
+ if (tool.ability !== void 0 && tool.roles === void 0) return false;
2994
4813
  const allowed = tool.roles ?? this.defaultRoles;
2995
4814
  if (allowed.length === 0) return this.emptyRoles === "allow";
2996
4815
  return (actor.roles ?? []).some((role) => allowed.includes(role));
@@ -3023,6 +4842,69 @@ __name(closeEmptyRoles, "closeEmptyRoles");
3023
4842
  var import_node_crypto = require("crypto");
3024
4843
  var import_nestjs_diagnostics2 = require("@dudousxd/nestjs-diagnostics");
3025
4844
 
4845
+ // src/action-proposal-approval.ts
4846
+ async function resolveActionProposalApproval(input) {
4847
+ const policy = input.policy ?? new DefaultApprovalPolicy();
4848
+ const requirement = await policy.requirementFor(input.tool, input.actor, input.thread);
4849
+ if (!requirement.required) return {
4850
+ mode: "auto"
4851
+ };
4852
+ const remembered = await input.store.rememberedApprovals?.(input.thread.threadId) ?? [];
4853
+ if (remembered.includes(input.tool.name)) return {
4854
+ mode: "remembered",
4855
+ approver: requirement.approver
4856
+ };
4857
+ const roundedTtl = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) ? Math.floor(requirement.ttlMs) : 0;
4858
+ if (roundedTtl <= 0) return {
4859
+ mode: "ask",
4860
+ approver: requirement.approver
4861
+ };
4862
+ const now = (input.clock ?? Date.now)();
4863
+ if (!Number.isSafeInteger(now) || !Number.isSafeInteger(roundedTtl) || !Number.isSafeInteger(now + roundedTtl)) throw new RangeError("Invalid action approval deadline");
4864
+ return {
4865
+ mode: "ask",
4866
+ approver: requirement.approver,
4867
+ ttlMs: roundedTtl,
4868
+ expiresAt: new Date(now + roundedTtl).toISOString()
4869
+ };
4870
+ }
4871
+ __name(resolveActionProposalApproval, "resolveActionProposalApproval");
4872
+
4873
+ // src/action-proposal-preparation.ts
4874
+ async function prepareActionProposal(registry, name, input, ctx, policy, options = {}) {
4875
+ const preparationInput = snapshotActionProposal(input);
4876
+ const prepared = await registry.prepareValidated(name, snapshotActionProposal(preparationInput), ctx, policy, {
4877
+ ...options,
4878
+ snapshotInput: true
4879
+ });
4880
+ let replacementKey;
4881
+ const replacement = registry.spec(name)?.replacementKey;
4882
+ if (prepared.preflight.status === "ready" && replacement !== void 0) {
4883
+ replacementKey = typeof replacement === "string" ? replacement : await replacement(snapshotActionProposal(prepared.input), ctx);
4884
+ if (replacementKey !== void 0 && (typeof replacementKey !== "string" || !replacementKey || replacementKey.length > 255)) throw new TypeError("replacementKey must be a nonempty string of at most 255 UTF-16 code units");
4885
+ }
4886
+ return {
4887
+ ...replacementKey !== void 0 ? {
4888
+ replacementKey
4889
+ } : {},
4890
+ preparationInput,
4891
+ input: prepared.input,
4892
+ // Completed results keep their normal handler contract; they never create a proposal.
4893
+ preflight: prepared.preflight.status === "ready" ? snapshotActionProposal(prepared.preflight) : prepared.preflight
4894
+ };
4895
+ }
4896
+ __name(prepareActionProposal, "prepareActionProposal");
4897
+
4898
+ // src/action-proposal-receipt.ts
4899
+ function actionProposalReceipt(proposalId) {
4900
+ return {
4901
+ proposalId,
4902
+ status: "pending",
4903
+ executed: false
4904
+ };
4905
+ }
4906
+ __name(actionProposalReceipt, "actionProposalReceipt");
4907
+
3026
4908
  // src/diagnostics.ts
3027
4909
  var import_nestjs_diagnostics = require("@dudousxd/nestjs-diagnostics");
3028
4910
  function publishAgentRunStarted(payload) {
@@ -3077,7 +4959,8 @@ var AGENT_SPAN_EVENTS = [
3077
4959
  "llm.turn",
3078
4960
  "tool.execution",
3079
4961
  "retrieval",
3080
- "follow-ups"
4962
+ "follow-ups",
4963
+ "structured-output"
3081
4964
  ];
3082
4965
  var AGENT_DIAGNOSTIC_EVENTS = [
3083
4966
  "run.started",
@@ -3098,6 +4981,67 @@ function agentDiagnosticKey(event) {
3098
4981
  }
3099
4982
  __name(agentDiagnosticKey, "agentDiagnosticKey");
3100
4983
 
4984
+ // src/negotiated-tool-ui.ts
4985
+ function createNegotiatedUiCollector(toolCallId, scope, resolveCatalog, write) {
4986
+ const collector = createUiCollector(toolCallId, write);
4987
+ const texts = /* @__PURE__ */ new Map();
4988
+ let next = 0;
4989
+ return {
4990
+ components: collector.components,
4991
+ text: /* @__PURE__ */ __name(() => [
4992
+ ...texts.values()
4993
+ ].join("\n"), "text"),
4994
+ restart: /* @__PURE__ */ __name(() => {
4995
+ next = 0;
4996
+ texts.clear();
4997
+ collector.restart();
4998
+ }, "restart"),
4999
+ emit: /* @__PURE__ */ __name(async (component, props, options = {}) => {
5000
+ if (!resolveCatalog) {
5001
+ const declared = scope.uiCapabilities === void 0 || scope.uiCapabilities.components.some((entry) => entry.name === component && entry.version === (options.version ?? 1));
5002
+ if (declared) return collector.emit(component, props, options);
5003
+ const id2 = options.id ?? `${toolCallId}:ui:${next++}`;
5004
+ if (options.fallbackText !== void 0 && options.fallbackText.length > 0) {
5005
+ texts.set(id2, options.fallbackText);
5006
+ await write?.({
5007
+ kind: "text",
5008
+ text: options.fallbackText
5009
+ });
5010
+ }
5011
+ return {
5012
+ id: id2
5013
+ };
5014
+ }
5015
+ const capturedProps = snapshotActionProposal(props);
5016
+ const capturedOptions = {
5017
+ ...options
5018
+ };
5019
+ const catalog = await resolveCatalog(scope);
5020
+ const prepared = await prepareUiEmission(catalog, scope.uiCapabilities, component, capturedProps, capturedOptions.version);
5021
+ const id = capturedOptions.id ?? `${toolCallId}:ui:${next++}`;
5022
+ if (prepared.kind === "text") {
5023
+ texts.set(id, prepared.text);
5024
+ await write?.({
5025
+ kind: "text",
5026
+ text: prepared.text
5027
+ });
5028
+ return {
5029
+ id
5030
+ };
5031
+ }
5032
+ return collector.emit(prepared.component, prepared.props, {
5033
+ id,
5034
+ version: prepared.version,
5035
+ fallbackText: prepared.fallbackText,
5036
+ ...prepared.componentVersions !== void 0 ? {
5037
+ componentVersions: prepared.componentVersions
5038
+ } : {}
5039
+ });
5040
+ }, "emit")
5041
+ };
5042
+ }
5043
+ __name(createNegotiatedUiCollector, "createNegotiatedUiCollector");
5044
+
3101
5045
  // src/tool-retry.ts
3102
5046
  function hasTransientShape(error) {
3103
5047
  if (typeof error !== "object" || error === null) {
@@ -3123,6 +5067,7 @@ function hasTransientShape(error) {
3123
5067
  }
3124
5068
  __name(hasTransientShape, "hasTransientShape");
3125
5069
  function isTransientToolError(error) {
5070
+ if (error instanceof ToolPreflightDeniedError) return false;
3126
5071
  if (hasTransientShape(error)) {
3127
5072
  return true;
3128
5073
  }
@@ -3165,7 +5110,7 @@ async function invokeWithTransientRetry(fn, setting, options) {
3165
5110
  try {
3166
5111
  return await fn();
3167
5112
  } catch (error) {
3168
- if (isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
5113
+ if (error instanceof ToolPreflightDeniedError || isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
3169
5114
  throw error;
3170
5115
  }
3171
5116
  const attemptsRemain = attempt < attempts;
@@ -3345,6 +5290,9 @@ function promptContext(input, persona) {
3345
5290
  ...input.pageContext !== void 0 ? {
3346
5291
  pageContext: input.pageContext
3347
5292
  } : {},
5293
+ ...input.uiCapabilities !== void 0 ? {
5294
+ uiCapabilities: input.uiCapabilities
5295
+ } : {},
3348
5296
  ...persona !== void 0 ? {
3349
5297
  persona: {
3350
5298
  id: persona.id,
@@ -3541,7 +5489,8 @@ async function loadSelectedHistory(deps, input, hooks) {
3541
5489
  messages: keep,
3542
5490
  dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
3543
5491
  title: thread.title,
3544
- hasAssistantMessage: thread.hasAssistantMessage
5492
+ hasAssistantMessage: thread.hasAssistantMessage,
5493
+ actionApprovalMode: deps.actionApprovalMode ?? "blocking"
3545
5494
  };
3546
5495
  });
3547
5496
  return {
@@ -3705,10 +5654,10 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3705
5654
  modelId: turn.modelId
3706
5655
  } : {}
3707
5656
  };
3708
- }, (result) => ({
3709
- inputTokens: result.usage.inputTokens,
3710
- outputTokens: result.usage.outputTokens,
3711
- textLength: result.text.length
5657
+ }, (result2) => ({
5658
+ inputTokens: result2.usage.inputTokens,
5659
+ outputTokens: result2.usage.outputTokens,
5660
+ textLength: result2.text.length
3712
5661
  })));
3713
5662
  await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => deps.store.recordUsage({
3714
5663
  threadId: input.threadId,
@@ -3738,13 +5687,16 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3738
5687
  throw new StructuredOutputError(issues, text, maxAttempts);
3739
5688
  }
3740
5689
  __name(structureAnswer, "structureAnswer");
3741
- function stampToolKinds(result, deps) {
5690
+ function stampToolKinds(result2, deps) {
3742
5691
  return {
3743
- ...result,
3744
- toolCalls: result.toolCalls.map((call) => call.kind === void 0 ? {
3745
- ...call,
3746
- kind: declaredKind(deps, call.name)
3747
- } : call)
5692
+ ...result2,
5693
+ toolCalls: result2.toolCalls.map((call) => {
5694
+ const { preflight: _untrusted, prepared: _untrustedPrepared, actionApproval: _untrustedApproval, ...trusted } = call;
5695
+ return trusted.kind === void 0 ? {
5696
+ ...trusted,
5697
+ kind: declaredKind(deps, trusted.name)
5698
+ } : trusted;
5699
+ })
3748
5700
  };
3749
5701
  }
3750
5702
  __name(stampToolKinds, "stampToolKinds");
@@ -3807,6 +5759,9 @@ function toolContext(deps, input, hooks) {
3807
5759
  ...input.pageContext !== void 0 ? {
3808
5760
  pageContext: input.pageContext
3809
5761
  } : {},
5762
+ ...input.uiCapabilities !== void 0 ? {
5763
+ uiCapabilities: input.uiCapabilities
5764
+ } : {},
3810
5765
  ...deps.host !== void 0 ? {
3811
5766
  host: deps.host
3812
5767
  } : {},
@@ -3882,16 +5837,16 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3882
5837
  }
3883
5838
  const messageId = asked.messageId;
3884
5839
  const reply = await awaitElicitation(hooks, request, toolContext(deps, input, hooks));
3885
- const result = settleElicitation(request, reply);
5840
+ const result2 = settleElicitation(request, reply);
3886
5841
  await hooks.step("intake:answers", async () => {
3887
5842
  await deps.store.updateToolCall({
3888
5843
  toolCallId: request.id,
3889
5844
  // A skip is not an answer. Both leave the agent holding the same values, but only one of them
3890
5845
  // is evidence the user chose them, and a reader auditing what the agent was told has to be
3891
5846
  // able to tell those apart.
3892
- status: result.skipped ? "rejected" : "executed",
3893
- output: result,
3894
- ...result.skipped ? {
5847
+ status: result2.skipped ? "rejected" : "executed",
5848
+ output: result2,
5849
+ ...result2.skipped ? {
3895
5850
  error: "skipped by the user"
3896
5851
  } : {},
3897
5852
  ...reply.answeredByRef !== void 0 ? {
@@ -3902,13 +5857,13 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3902
5857
  {
3903
5858
  id: request.id,
3904
5859
  name: ASK_TOOL_NAME,
3905
- output: result
5860
+ output: result2
3906
5861
  }
3907
5862
  ]);
3908
5863
  await writer.write(encodeStreamEvent({
3909
5864
  kind: "tool-output",
3910
5865
  id: request.id,
3911
- output: result
5866
+ output: result2
3912
5867
  }));
3913
5868
  });
3914
5869
  return {
@@ -3921,7 +5876,7 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3921
5876
  {
3922
5877
  id: request.id,
3923
5878
  name: ASK_TOOL_NAME,
3924
- output: result
5879
+ output: result2
3925
5880
  }
3926
5881
  ]
3927
5882
  };
@@ -3967,16 +5922,140 @@ async function claimToolCall(turn, call) {
3967
5922
  const spec = deps.registry.spec(call.name);
3968
5923
  const kind = call.kind ?? declaredKind(deps, call.name);
3969
5924
  const awaitsHuman = kind === "action" || kind === "ask";
3970
- const approval = kind === "action" ? await claimApproval(turn, call, spec) : void 0;
3971
- const parks = kind === "ask" || approval?.mode === "ask";
5925
+ const independentApproval = turn.actionApprovalMode === "independent" && kind === "action" ? hooks.dispatchLlm !== void 0 && call.actionApproval !== void 0 ? call.actionApproval : await resolveActionProposalApproval({
5926
+ actor: input.actor,
5927
+ store: deps.store,
5928
+ tool: {
5929
+ name: call.name,
5930
+ kind: "action",
5931
+ ...spec !== void 0 ? {
5932
+ spec
5933
+ } : {}
5934
+ },
5935
+ thread: {
5936
+ threadId: input.threadId,
5937
+ runId: hooks.runId,
5938
+ ...input.agentName !== void 0 ? {
5939
+ agentName: input.agentName
5940
+ } : {}
5941
+ },
5942
+ ...deps.approvalPolicy !== void 0 ? {
5943
+ policy: deps.approvalPolicy
5944
+ } : {}
5945
+ }) : void 0;
5946
+ let preflight;
5947
+ let prepared;
5948
+ if (turn.actionApprovalMode === "independent" && call.prepared !== void 0 && hooks.dispatchLlm !== void 0) prepared = call.prepared;
5949
+ if (kind === "action" && hooks.dispatchLlm !== void 0 && call.preflight !== void 0) {
5950
+ preflight = call.preflight;
5951
+ } else if (kind === "action" && hooks.dispatchLlm !== void 0 && spec === void 0) {
5952
+ preflight = {
5953
+ status: "ready"
5954
+ };
5955
+ } else if (kind === "action") {
5956
+ try {
5957
+ const allowedTools = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
5958
+ if (independentApproval?.mode === "ask") {
5959
+ prepared = await prepareActionProposal(deps.registry, call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5960
+ allowedTools
5961
+ });
5962
+ preflight = prepared.preflight;
5963
+ } else {
5964
+ preflight = await deps.registry.prepare(call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5965
+ allowedTools
5966
+ });
5967
+ }
5968
+ } catch (error) {
5969
+ if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) throw error;
5970
+ preflight = {
5971
+ status: "failed",
5972
+ error: error instanceof Error ? error.message : String(error)
5973
+ };
5974
+ }
5975
+ }
5976
+ let confirmation = preflight?.status === "ready" ? preflight.confirmation : void 0;
5977
+ const approval = kind === "action" && preflight?.status === "ready" ? independentApproval ?? await claimApproval(turn, call, spec) : void 0;
5978
+ let proposalId;
5979
+ if (turn.actionApprovalMode === "independent" && approval?.mode === "ask") {
5980
+ if (!prepared) throw new Error("Independent action lacks trusted prepared input");
5981
+ const proposalStore = deps.store;
5982
+ if (typeof proposalStore.createActionProposal !== "function") throw new Error("Independent action proposal store unavailable");
5983
+ proposalId = `proposal-${(0, import_node_crypto.createHash)("sha256").update(JSON.stringify([
5984
+ hooks.runId,
5985
+ call.id
5986
+ ])).digest("hex")}`;
5987
+ const display = confirmation ?? spec?.presentation?.confirm ?? {
5988
+ title: `Allow ${call.name}?`,
5989
+ verb: "Approve"
5990
+ };
5991
+ const fill = /* @__PURE__ */ __name((value) => value.replace(/\{([^}]+)\}/g, (_match, path) => {
5992
+ let current = prepared?.input;
5993
+ for (const part of path.split(".")) current = current !== null && typeof current === "object" ? current[part] : void 0;
5994
+ return current === void 0 ? "" : String(current);
5995
+ }), "fill");
5996
+ const create = prepared.replacementKey === void 0 ? proposalStore.createActionProposal.bind(proposalStore) : (() => {
5997
+ const store = proposalStore;
5998
+ if (typeof store.createReplacingActionProposal !== "function") throw new Error("Independent replacement requires atomic supersession store");
5999
+ return store.createReplacingActionProposal.bind(store);
6000
+ })();
6001
+ const created = await create({
6002
+ ...prepared.replacementKey !== void 0 ? {
6003
+ replacementKey: prepared.replacementKey
6004
+ } : {},
6005
+ id: proposalId,
6006
+ tenantRef: input.actor.tenantRef ?? null,
6007
+ actorRef: input.actor.id,
6008
+ threadId: input.threadId,
6009
+ originRunId: hooks.runId,
6010
+ originMessageId: messageId,
6011
+ originToolCallId: call.id,
6012
+ toolName: call.name,
6013
+ input: prepared.input,
6014
+ preparationInput: prepared.preparationInput,
6015
+ executionContext: {
6016
+ requestId: hooks.runId,
6017
+ ...input.agentName !== void 0 ? {
6018
+ agentName: input.agentName
6019
+ } : {},
6020
+ ...input.persona !== void 0 ? {
6021
+ persona: input.persona
6022
+ } : {},
6023
+ ...input.pageContext !== void 0 ? {
6024
+ pageContext: input.pageContext
6025
+ } : {},
6026
+ ...input.uiCapabilities !== void 0 ? {
6027
+ uiCapabilities: input.uiCapabilities
6028
+ } : {}
6029
+ },
6030
+ confirmation: confirmation ?? {
6031
+ title: fill(display.title),
6032
+ verb: fill(display.verb),
6033
+ ...display.detail !== void 0 ? {
6034
+ detail: fill(display.detail)
6035
+ } : {}
6036
+ },
6037
+ approver: approval.approver,
6038
+ expiresAt: approval.expiresAt === void 0 ? null : Date.parse(approval.expiresAt),
6039
+ idempotencyKey: toolCallContext(toolContext(deps, input, hooks), call.id).idempotencyKey
6040
+ });
6041
+ if (created.status === "conflict") throw new Error("Action proposal replay payload conflict");
6042
+ confirmation = created.proposal?.confirmation ?? confirmation;
6043
+ }
6044
+ const parks = kind === "ask" || approval?.mode === "ask" && proposalId === void 0;
3972
6045
  await deps.store.recordToolCall({
3973
6046
  toolCallId: call.id,
3974
6047
  messageId,
3975
6048
  toolName: call.name,
3976
6049
  toolType: awaitsHuman ? "action" : "read",
3977
6050
  input: call.input,
3978
- status: parks ? "pending_approval" : "auto_executed",
6051
+ status: proposalId !== void 0 ? "proposed" : parks ? "pending_approval" : "auto_executed",
6052
+ ...proposalId !== void 0 ? {
6053
+ proposalId
6054
+ } : {},
3979
6055
  runId: hooks.runId,
6056
+ ...confirmation !== void 0 ? {
6057
+ confirmation
6058
+ } : {},
3980
6059
  ...approval !== void 0 && approval.mode !== "auto" ? {
3981
6060
  approver: approval.approver
3982
6061
  } : {},
@@ -3988,7 +6067,16 @@ async function claimToolCall(turn, call) {
3988
6067
  await turn.writer.write(encodeStreamEvent({
3989
6068
  kind: "approval-requested",
3990
6069
  id: call.id,
6070
+ ...proposalId !== void 0 ? {
6071
+ target: {
6072
+ kind: "proposal",
6073
+ proposalId
6074
+ }
6075
+ } : {},
3991
6076
  approver: approval.approver,
6077
+ ...confirmation !== void 0 ? {
6078
+ confirmation
6079
+ } : {},
3992
6080
  ...approval.expiresAt !== void 0 ? {
3993
6081
  expiresAt: approval.expiresAt
3994
6082
  } : {}
@@ -4009,8 +6097,18 @@ async function claimToolCall(turn, call) {
4009
6097
  ...spec?.terminal === true ? {
4010
6098
  terminal: true
4011
6099
  } : {},
6100
+ // Journal this extra checkpoint's opt-in; historical claims without it keep their sequence.
6101
+ ...preflight?.status === "completed" && deps.registry.hasPresentation(call.name) ? {
6102
+ presentCompletedPreflight: true
6103
+ } : {},
4012
6104
  ...approval !== void 0 ? {
4013
6105
  approval
6106
+ } : {},
6107
+ ...preflight !== void 0 ? {
6108
+ preflight
6109
+ } : {},
6110
+ ...proposalId !== void 0 ? {
6111
+ proposalId
4014
6112
  } : {}
4015
6113
  };
4016
6114
  });
@@ -4033,9 +6131,18 @@ async function claimToolCall(turn, call) {
4033
6131
  ...persisted?.terminal === true ? {
4034
6132
  terminal: true
4035
6133
  } : {},
6134
+ ...persisted?.presentCompletedPreflight === true ? {
6135
+ presentCompletedPreflight: true
6136
+ } : {},
4036
6137
  ...toolType === "action" && persisted?.approval !== void 0 ? {
4037
6138
  approval: persisted.approval
4038
6139
  } : {},
6140
+ ...persisted?.preflight !== void 0 ? {
6141
+ preflight: persisted.preflight
6142
+ } : {},
6143
+ ...persisted?.proposalId !== void 0 ? {
6144
+ proposalId: persisted.proposalId
6145
+ } : {},
4039
6146
  ctx: toolContext(deps, input, hooks)
4040
6147
  };
4041
6148
  }
@@ -4183,6 +6290,9 @@ function skillContext(input) {
4183
6290
  } : {},
4184
6291
  ...input.pageContext !== void 0 ? {
4185
6292
  pageContext: input.pageContext
6293
+ } : {},
6294
+ ...input.uiCapabilities !== void 0 ? {
6295
+ uiCapabilities: input.uiCapabilities
4186
6296
  } : {}
4187
6297
  };
4188
6298
  }
@@ -4199,7 +6309,7 @@ async function invokeClaimedTool(turn, claimed) {
4199
6309
  if (claimed.toolType === "memory") {
4200
6310
  return await rememberIntoTurn(turn, claimed, startedAt);
4201
6311
  }
4202
- const invokeAllowList = turn.persona?.allowedTools === void 0 ? void 0 : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
6312
+ const invokeAllowList = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
4203
6313
  let raw;
4204
6314
  if (hooks.dispatchTool) {
4205
6315
  const stepCtx = {
@@ -4215,6 +6325,9 @@ async function invokeClaimedTool(turn, claimed) {
4215
6325
  } : {},
4216
6326
  ...input.pageContext !== void 0 ? {
4217
6327
  pageContext: input.pageContext
6328
+ } : {},
6329
+ ...input.uiCapabilities !== void 0 ? {
6330
+ uiCapabilities: input.uiCapabilities
4218
6331
  } : {}
4219
6332
  };
4220
6333
  const envelope = {
@@ -4236,37 +6349,49 @@ async function invokeClaimedTool(turn, claimed) {
4236
6349
  raw = await hooks.dispatchTool(resolvedCall, envelope);
4237
6350
  } else {
4238
6351
  const invocation = hooks.step(`tool:${call.id}`, async () => {
4239
- const ui2 = createUiCollector(call.id, (event) => turn.writer.write(encodeStreamEvent(event)));
4240
- const output2 = await traceToolExecution(hooks.runId, {
4241
- toolCallId: call.id,
4242
- toolName: call.name,
4243
- toolType
4244
- }, () => invokeWithTransientRetry(() => {
4245
- ui2.restart();
4246
- return deps.registry.invoke(call.name, call.input, {
4247
- ...toolCallContext(ctx, call.id),
4248
- emitUi: ui2.emit
4249
- }, deps.rolesPolicy, invokeAllowList !== void 0 ? {
4250
- allowedTools: invokeAllowList
4251
- } : {});
4252
- }, deps.toolTransientRetry ?? {}, {
4253
- ...hooks.isControlFlowError !== void 0 ? {
4254
- isControlFlowError: hooks.isControlFlowError
4255
- } : {},
4256
- onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
4257
- publishAgentToolRetry({
4258
- toolName: call.name,
4259
- toolCallId: call.id,
4260
- attempt,
4261
- message: retryError instanceof Error ? retryError.message : String(retryError)
4262
- });
4263
- }, "onRetry")
4264
- }));
4265
- return wrapToolStepOutput(output2, ui2.components());
6352
+ const ui2 = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
6353
+ let output2;
6354
+ try {
6355
+ output2 = await traceToolExecution(hooks.runId, {
6356
+ toolCallId: call.id,
6357
+ toolName: call.name,
6358
+ toolType
6359
+ }, () => invokeWithTransientRetry(() => {
6360
+ ui2.restart();
6361
+ return deps.registry.invoke(call.name, call.input, {
6362
+ ...toolCallContext(ctx, call.id),
6363
+ emitUi: ui2.emit
6364
+ }, deps.rolesPolicy, invokeAllowList !== void 0 ? {
6365
+ allowedTools: invokeAllowList
6366
+ } : {});
6367
+ }, deps.toolTransientRetry ?? {}, {
6368
+ ...hooks.isControlFlowError !== void 0 ? {
6369
+ isControlFlowError: hooks.isControlFlowError
6370
+ } : {},
6371
+ onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
6372
+ publishAgentToolRetry({
6373
+ toolName: call.name,
6374
+ toolCallId: call.id,
6375
+ attempt,
6376
+ message: retryError instanceof Error ? retryError.message : String(retryError)
6377
+ });
6378
+ }, "onRetry")
6379
+ }));
6380
+ } catch (error) {
6381
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") return wrapToolPreflightDenied(error.message);
6382
+ throw error;
6383
+ }
6384
+ return wrapToolStepOutput(output2, ui2.components(), ui2.text());
4266
6385
  });
4267
6386
  raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
4268
6387
  }
4269
- const { output, ui } = unwrapToolStepOutput(raw);
6388
+ const { output, ui, preflightDenied, text } = unwrapToolStepOutput(raw);
6389
+ if (text) turn.toolText.set(call.id, text);
6390
+ if (preflightDenied !== void 0) return {
6391
+ status: "denied",
6392
+ reason: preflightDenied,
6393
+ executionMs: Date.now() - startedAt
6394
+ };
4270
6395
  if (ui.length > 0) {
4271
6396
  turn.toolUi.set(call.id, ui);
4272
6397
  }
@@ -4282,6 +6407,13 @@ async function invokeClaimedTool(turn, claimed) {
4282
6407
  if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) {
4283
6408
  throw error;
4284
6409
  }
6410
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") {
6411
+ return {
6412
+ status: "denied",
6413
+ reason: error.message,
6414
+ executionMs: Date.now() - startedAt
6415
+ };
6416
+ }
4285
6417
  return {
4286
6418
  status: "failed",
4287
6419
  error: error instanceof Error ? error.message : String(error),
@@ -4323,6 +6455,27 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef, meta) {
4323
6455
  } : {}
4324
6456
  }));
4325
6457
  }, "settle");
6458
+ if (outcome.status === "denied") {
6459
+ await hooks.step(`persist:toolreject:${call.id}`, async () => {
6460
+ await deps.store.updateToolCall({
6461
+ toolCallId: call.id,
6462
+ status: meta === void 0 ? "rejected" : "failed",
6463
+ error: outcome.reason,
6464
+ ...decided
6465
+ });
6466
+ await settle();
6467
+ });
6468
+ return {
6469
+ id: call.id,
6470
+ name: call.name,
6471
+ output: {
6472
+ rejected: true,
6473
+ reason: outcome.reason
6474
+ },
6475
+ denied: true,
6476
+ error: `This action was denied by its preflight check: ${outcome.reason}. The requested mutation did not run.`
6477
+ };
6478
+ }
4326
6479
  if (outcome.status === "failed") {
4327
6480
  await hooks.step(`persist:toolfail:${call.id}`, async () => {
4328
6481
  await deps.store.updateToolCall({
@@ -4463,12 +6616,12 @@ async function elicitToolCall(turn, claimed) {
4463
6616
  }));
4464
6617
  });
4465
6618
  const reply = await awaitElicitation(hooks, request, ctx);
4466
- const result = settleElicitation(request, reply);
4467
- await hooks.step(result.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
6619
+ const result2 = settleElicitation(request, reply);
6620
+ await hooks.step(result2.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
4468
6621
  toolCallId: call.id,
4469
- status: result.skipped ? "rejected" : "executed",
4470
- output: result,
4471
- ...result.skipped ? {
6622
+ status: result2.skipped ? "rejected" : "executed",
6623
+ output: result2,
6624
+ ...result2.skipped ? {
4472
6625
  error: "skipped by the user"
4473
6626
  } : {},
4474
6627
  ...reply.answeredByRef !== void 0 ? {
@@ -4479,24 +6632,63 @@ async function elicitToolCall(turn, claimed) {
4479
6632
  runId: hooks.runId,
4480
6633
  toolName: call.name,
4481
6634
  toolType: "action",
4482
- status: result.skipped ? "rejected" : "executed"
6635
+ status: result2.skipped ? "rejected" : "executed"
4483
6636
  });
4484
6637
  return {
4485
6638
  id: call.id,
4486
6639
  name: call.name,
4487
- output: result
6640
+ output: result2
4488
6641
  };
4489
6642
  }
4490
6643
  __name(elicitToolCall, "elicitToolCall");
4491
6644
  async function runClaimedToolCall(turn, claimed) {
4492
6645
  const { deps, input, hooks } = turn;
4493
6646
  const { call, toolType, ctx } = claimed;
6647
+ if (claimed.proposalId !== void 0) {
6648
+ turn.halt.independentProposal = true;
6649
+ return {
6650
+ id: call.id,
6651
+ name: call.name,
6652
+ output: actionProposalReceipt(claimed.proposalId)
6653
+ };
6654
+ }
4494
6655
  if (toolType === "agent") {
4495
6656
  return delegateToolCall(turn, claimed);
4496
6657
  }
4497
6658
  if (toolType === "ask") {
4498
6659
  return elicitToolCall(turn, claimed);
4499
6660
  }
6661
+ if (claimed.preflight !== void 0 && claimed.preflight.status !== "ready") {
6662
+ const result2 = claimed.preflight;
6663
+ if (result2.status === "completed" && claimed.presentCompletedPreflight === true) {
6664
+ const raw = await hooks.step(`present:${call.id}`, async () => {
6665
+ const ui = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
6666
+ await deps.registry.presentOutput(call.name, result2.output, {
6667
+ ...toolCallContext(ctx, call.id),
6668
+ emitUi: ui.emit
6669
+ });
6670
+ return wrapToolStepOutput(result2.output, ui.components(), ui.text());
6671
+ });
6672
+ const presented = unwrapToolStepOutput(raw);
6673
+ if (presented.ui.length > 0) turn.toolUi.set(call.id, presented.ui);
6674
+ if (presented.text) turn.toolText.set(call.id, presented.text);
6675
+ }
6676
+ if (result2.status === "completed" && claimed.terminal === true) turn.halt.terminal = true;
6677
+ const outcome = result2.status === "completed" ? {
6678
+ status: "executed",
6679
+ output: result2.output,
6680
+ executionMs: 0
6681
+ } : result2.status === "denied" ? {
6682
+ status: "denied",
6683
+ reason: result2.reason,
6684
+ executionMs: 0
6685
+ } : {
6686
+ status: "failed",
6687
+ error: result2.error,
6688
+ executionMs: 0
6689
+ };
6690
+ return recordToolOutcome(turn, claimed, outcome, input.actor.id);
6691
+ }
4500
6692
  let deciderRef = input.actor.id;
4501
6693
  let meta;
4502
6694
  if (toolType === "action" && claimed.approval?.mode === "remembered") {
@@ -4621,30 +6813,30 @@ function expiryNarrative() {
4621
6813
  return "This action needed approval, and the approval request expired before anyone decided. Nothing ran and nothing changed. Do not run it again on your own; tell the person it was not approved in time and ask whether they still want it.";
4622
6814
  }
4623
6815
  __name(expiryNarrative, "expiryNarrative");
4624
- function outputFrame(result) {
4625
- if (result.denied === true) {
4626
- const reason = refusalReason(result);
6816
+ function outputFrame(result2) {
6817
+ if (result2.denied === true) {
6818
+ const reason = refusalReason(result2);
4627
6819
  return {
4628
6820
  kind: "tool-output-denied",
4629
- id: result.id,
6821
+ id: result2.id,
4630
6822
  ...reason !== void 0 ? {
4631
6823
  reason
4632
6824
  } : {}
4633
6825
  };
4634
6826
  }
4635
- return result.error !== void 0 ? {
6827
+ return result2.error !== void 0 ? {
4636
6828
  kind: "tool-output-error",
4637
- id: result.id,
4638
- error: result.error
6829
+ id: result2.id,
6830
+ error: result2.error
4639
6831
  } : {
4640
6832
  kind: "tool-output",
4641
- id: result.id,
4642
- output: result.output
6833
+ id: result2.id,
6834
+ output: result2.output
4643
6835
  };
4644
6836
  }
4645
6837
  __name(outputFrame, "outputFrame");
4646
- function refusalReason(result) {
4647
- const { output } = result;
6838
+ function refusalReason(result2) {
6839
+ const { output } = result2;
4648
6840
  if (output !== null && typeof output === "object" && "reason" in output) {
4649
6841
  const reason = output.reason;
4650
6842
  return typeof reason === "string" && reason !== DEFAULT_REFUSAL_REASON ? reason : void 0;
@@ -4884,6 +7076,25 @@ ${block}`;
4884
7076
  messages: prompt.messages,
4885
7077
  actor: input.actor,
4886
7078
  threadId: input.threadId,
7079
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
7080
+ preflightContext: {
7081
+ actor: input.actor,
7082
+ threadId: input.threadId,
7083
+ runId: hooks.runId,
7084
+ requestId: hooks.runId,
7085
+ ...input.agentName !== void 0 ? {
7086
+ agentName: input.agentName
7087
+ } : {},
7088
+ ...input.persona !== void 0 ? {
7089
+ persona: input.persona
7090
+ } : {},
7091
+ ...input.pageContext !== void 0 ? {
7092
+ pageContext: input.pageContext
7093
+ } : {},
7094
+ ...input.uiCapabilities !== void 0 ? {
7095
+ uiCapabilities: input.uiCapabilities
7096
+ } : {}
7097
+ },
4887
7098
  ...gated ? {
4888
7099
  bufferOutput: true
4889
7100
  } : {},
@@ -4900,6 +7111,9 @@ ${block}`;
4900
7111
  tools: withAskTool({
4901
7112
  tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, intersectAllowLists(deps.toolAllowList, persona?.allowedTools), {
4902
7113
  threadId: input.threadId,
7114
+ ...input.uiCapabilities !== void 0 ? {
7115
+ uiCapabilities: input.uiCapabilities
7116
+ } : {},
4903
7117
  ...input.agentName !== void 0 ? {
4904
7118
  agentName: input.agentName
4905
7119
  } : {}
@@ -4919,7 +7133,7 @@ ${block}`;
4919
7133
  }) : void 0;
4920
7134
  const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
4921
7135
  const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
4922
- const result = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
7136
+ const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
4923
7137
  system: prompt.system,
4924
7138
  messages: prompt.messages,
4925
7139
  tools,
@@ -4929,15 +7143,15 @@ ${block}`;
4929
7143
  await incremental.settled();
4930
7144
  const refusal = incremental.rejection();
4931
7145
  return {
4932
- ...result,
7146
+ ...result2,
4933
7147
  releasedText: incremental.released(),
4934
7148
  ...refusal !== void 0 ? {
4935
7149
  gateRejection: refusal
4936
7150
  } : {}
4937
7151
  };
4938
7152
  }
4939
- return buffer === void 0 ? result : {
4940
- ...result,
7153
+ return buffer === void 0 ? result2 : {
7154
+ ...result2,
4941
7155
  bufferedFrames: buffer.frames()
4942
7156
  };
4943
7157
  });
@@ -5031,12 +7245,12 @@ ${block}`;
5031
7245
  role: "assistant",
5032
7246
  content: turn.text
5033
7247
  }
5034
- ], count), (result) => ({
5035
- followUps: result.followUps.length,
5036
- inputTokens: result.usage.inputTokens,
5037
- outputTokens: result.usage.outputTokens,
5038
- ...result.modelId !== void 0 ? {
5039
- modelId: result.modelId
7248
+ ], count), (result2) => ({
7249
+ followUps: result2.followUps.length,
7250
+ inputTokens: result2.usage.inputTokens,
7251
+ outputTokens: result2.usage.outputTokens,
7252
+ ...result2.modelId !== void 0 ? {
7253
+ modelId: result2.modelId
5040
7254
  } : {}
5041
7255
  })));
5042
7256
  let suggestions = generated.followUps;
@@ -5147,7 +7361,7 @@ ${block}`;
5147
7361
  };
5148
7362
  modelMessages.push(assistantMessage);
5149
7363
  for (const entry of synthetic) {
5150
- const { call, result } = entry;
7364
+ const { call, result: result2 } = entry;
5151
7365
  await hooks.step(`${entry.step}:${assistant.id}`, async () => {
5152
7366
  await deps.store.recordToolCall({
5153
7367
  toolCallId: call.id,
@@ -5161,7 +7375,7 @@ ${block}`;
5161
7375
  await deps.store.updateToolCall({
5162
7376
  toolCallId: call.id,
5163
7377
  status: "executed",
5164
- output: result.output
7378
+ output: result2.output
5165
7379
  });
5166
7380
  await writer.write(encodeStreamEvent({
5167
7381
  kind: "tool-input-available",
@@ -5173,7 +7387,7 @@ ${block}`;
5173
7387
  await writer.write(encodeStreamEvent({
5174
7388
  kind: "tool-output",
5175
7389
  id: call.id,
5176
- output: result.output
7390
+ output: result2.output
5177
7391
  }));
5178
7392
  });
5179
7393
  }
@@ -5202,9 +7416,11 @@ ${block}`;
5202
7416
  messageId: assistant.id,
5203
7417
  writer,
5204
7418
  toolUi: /* @__PURE__ */ new Map(),
7419
+ toolText: /* @__PURE__ */ new Map(),
5205
7420
  halt: {
5206
7421
  terminal: false
5207
7422
  },
7423
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
5208
7424
  ...skillOffer !== void 0 ? {
5209
7425
  skills: skillOffer
5210
7426
  } : {},
@@ -5237,17 +7453,35 @@ ${block}`;
5237
7453
  ...results,
5238
7454
  ...syntheticResults
5239
7455
  ];
7456
+ const toolText = escapeUnsafeToolUiText(toolCallsWithKind.map((call) => turnCalls.toolText.get(call.id)).filter((text) => text !== void 0).join("\n"));
5240
7457
  assistantMessage.toolResults = settledResults;
5241
7458
  await hooks.step(`stream:tool-outputs:${i}`, async () => {
5242
7459
  await deps.store.setMessageToolResults(assistant.id, settledResults);
7460
+ if (toolText) await deps.store.appendMessage({
7461
+ threadId: input.threadId,
7462
+ role: "assistant",
7463
+ content: toolText,
7464
+ runId: hooks.runId,
7465
+ ...input.agentName !== void 0 ? {
7466
+ agentName: input.agentName
7467
+ } : {}
7468
+ });
5243
7469
  const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
5244
7470
  if (toolUi.length > 0) {
5245
7471
  await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
5246
7472
  }
5247
- for (const result of results) {
5248
- await writer.write(encodeStreamEvent(outputFrame(result)));
7473
+ for (const result2 of results) {
7474
+ await writer.write(encodeStreamEvent(outputFrame(result2)));
5249
7475
  }
5250
7476
  });
7477
+ if (toolText) {
7478
+ modelMessages.push({
7479
+ role: "assistant",
7480
+ content: toolText
7481
+ });
7482
+ lastText += `
7483
+ ${toolText}`;
7484
+ }
5251
7485
  await hooks.step(`stream:step-finish:${i}`, async () => {
5252
7486
  await writer.write(encodeStreamEvent({
5253
7487
  kind: "step-finish",
@@ -5261,7 +7495,7 @@ ${block}`;
5261
7495
  } : {}
5262
7496
  }));
5263
7497
  });
5264
- if (turnCalls.halt.terminal) {
7498
+ if (turnCalls.halt.terminal || turnCalls.halt.independentProposal) {
5265
7499
  break;
5266
7500
  }
5267
7501
  }
@@ -5317,8 +7551,152 @@ ${block}`;
5317
7551
  }
5318
7552
  __name(runAgentLoop, "runAgentLoop");
5319
7553
 
7554
+ // src/in-memory-action-proposal-store.ts
7555
+ var import_node_crypto2 = require("crypto");
7556
+ var InMemoryActionProposalStore = class {
7557
+ static {
7558
+ __name(this, "InMemoryActionProposalStore");
7559
+ }
7560
+ rows = /* @__PURE__ */ new Map();
7561
+ clock;
7562
+ constructor(options = {}) {
7563
+ this.clock = options.clock ?? Date.now;
7564
+ }
7565
+ async createActionProposal(input) {
7566
+ validateActionProposalCreation(input);
7567
+ const row2 = this.rows.get(input.id);
7568
+ if (row2) {
7569
+ if (!actionProposalScopeMatches(row2, input)) return {
7570
+ status: "conflict"
7571
+ };
7572
+ return {
7573
+ status: actionProposalCreationMatches(row2, input) ? "unchanged" : "conflict",
7574
+ proposal: snapshotActionProposal(row2)
7575
+ };
7576
+ }
7577
+ const proposal = initialActionProposal(input, this.clock());
7578
+ this.rows.set(input.id, proposal);
7579
+ return {
7580
+ status: "created",
7581
+ proposal: snapshotActionProposal(proposal)
7582
+ };
7583
+ }
7584
+ async getActionProposal(scope, id) {
7585
+ const row2 = this.rows.get(id);
7586
+ return row2 && actionProposalScopeMatches(row2, scope) ? snapshotActionProposal(row2) : null;
7587
+ }
7588
+ async listActionProposals(scope, query = {}) {
7589
+ const limit = validateActionProposalListQuery(query);
7590
+ return [
7591
+ ...this.rows.values()
7592
+ ].filter((row2) => actionProposalScopeMatches(row2, scope) && (query.decision === void 0 || row2.decision === query.decision) && (query.after === void 0 || row2.createdAt > query.after.createdAt || row2.createdAt === query.after.createdAt && row2.id > query.after.id)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, limit).map(snapshotActionProposal);
7593
+ }
7594
+ mutate(scope, id, transition) {
7595
+ const row2 = this.rows.get(id);
7596
+ if (!row2 || !actionProposalScopeMatches(row2, scope)) return {
7597
+ status: "not_found"
7598
+ };
7599
+ const result2 = transition(row2, this.clock());
7600
+ if (result2.proposal) this.rows.set(id, snapshotActionProposal(result2.proposal));
7601
+ return result2;
7602
+ }
7603
+ async decideActionProposal(scope, id, command) {
7604
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalDecision(row2, command, now));
7605
+ }
7606
+ async claimActionProposal(scope, id, command) {
7607
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalClaim(row2, command, now, (0, import_node_crypto2.randomUUID)()));
7608
+ }
7609
+ async extendActionProposalLease(scope, id, command) {
7610
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalLease(row2, command, now));
7611
+ }
7612
+ async settleActionProposal(scope, id, command) {
7613
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalSettlement(row2, command, now));
7614
+ }
7615
+ async claimNextActionProposal(command) {
7616
+ const now = this.clock();
7617
+ validateActionProposalWorkerClaim(command, now);
7618
+ const candidates = [
7619
+ ...this.rows.values()
7620
+ ].filter((row2) => row2.decision === "approved" && (row2.execution?.status === "queued" || row2.execution?.status === "executing" && row2.execution.lease !== null && now >= row2.execution.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
7621
+ for (const candidate of candidates) {
7622
+ const claimed = await this.claimActionProposal(candidate, candidate.id, command);
7623
+ if (claimed.status === "applied" && claimed.proposal) return claimed.proposal;
7624
+ }
7625
+ return null;
7626
+ }
7627
+ async expireActionProposals(command) {
7628
+ const now = this.clock();
7629
+ validateActionProposalExpiryBatch(command, now);
7630
+ const candidates = [
7631
+ ...this.rows.values()
7632
+ ].filter((row2) => row2.decision === "pending" && row2.expiresAt !== null && now >= row2.expiresAt).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, command.limit);
7633
+ let expired = 0;
7634
+ for (const candidate of candidates) {
7635
+ const result2 = await this.decideActionProposal(candidate, candidate.id, {
7636
+ decision: "expired",
7637
+ actorRef: "system",
7638
+ via: "expiry"
7639
+ });
7640
+ if (result2.status === "applied") expired++;
7641
+ }
7642
+ return expired;
7643
+ }
7644
+ async claimNextActionProposalOutcome(command) {
7645
+ const now = this.clock();
7646
+ validateActionProposalWorkerClaim(command, now);
7647
+ const candidates = [
7648
+ ...this.rows.values()
7649
+ ].filter((row2) => row2.outcomeDelivery?.status === "pending" && (!row2.outcomeDelivery.lease || now >= row2.outcomeDelivery.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
7650
+ for (const candidate of candidates) {
7651
+ const row2 = this.rows.get(candidate.id);
7652
+ if (!row2) continue;
7653
+ const next = claimActionProposalOutcome(row2, command, this.clock(), (0, import_node_crypto2.randomUUID)());
7654
+ if (!next?.outcome || !next.outcomeDelivery?.lease) continue;
7655
+ this.rows.set(next.id, next);
7656
+ return {
7657
+ outcome: snapshotActionProposal(next.outcome),
7658
+ lease: {
7659
+ outcomeId: next.outcome.id,
7660
+ token: next.outcomeDelivery.lease.token,
7661
+ generation: next.outcomeDelivery.generation
7662
+ }
7663
+ };
7664
+ }
7665
+ return null;
7666
+ }
7667
+ async createReplacingActionProposal(input) {
7668
+ validateActionProposalCreation(input);
7669
+ const existing = this.rows.get(input.id);
7670
+ if (existing) return this.createActionProposal(input);
7671
+ const proposal = initialActionProposal(input, this.clock());
7672
+ const replacements = [];
7673
+ if (input.replacementKey) for (const row2 of this.rows.values()) {
7674
+ if (row2.decision !== "pending" || !actionProposalScopeMatches(row2, input) || row2.toolName !== input.toolName || row2.replacementKey !== input.replacementKey) continue;
7675
+ const result2 = transitionActionProposalSupersession(row2, proposal, {
7676
+ replacementProposalId: proposal.id,
7677
+ actorRef: input.actorRef,
7678
+ via: "supersession"
7679
+ }, this.clock());
7680
+ if (result2.proposal) replacements.push(result2.proposal);
7681
+ }
7682
+ this.rows.set(proposal.id, proposal);
7683
+ for (const row2 of replacements) this.rows.set(row2.id, row2);
7684
+ return {
7685
+ status: "created",
7686
+ proposal: snapshotActionProposal(proposal)
7687
+ };
7688
+ }
7689
+ async supersedeActionProposal(scope, id, command) {
7690
+ const replacement = this.rows.get(command.replacementProposalId);
7691
+ if (!replacement || !actionProposalScopeMatches(replacement, scope)) return {
7692
+ status: "not_found"
7693
+ };
7694
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalSupersession(row2, replacement, command, now));
7695
+ }
7696
+ };
7697
+
5320
7698
  // src/in-memory-store.ts
5321
- var InMemoryAgentStore = class {
7699
+ var InMemoryAgentStore = class extends InMemoryActionProposalStore {
5322
7700
  static {
5323
7701
  __name(this, "InMemoryAgentStore");
5324
7702
  }
@@ -5338,9 +7716,10 @@ var InMemoryAgentStore = class {
5338
7716
  throw new Error(`thread ${id} already exists`);
5339
7717
  }
5340
7718
  const ts = this.now();
5341
- const row = {
7719
+ const row2 = {
5342
7720
  id,
5343
7721
  actorRef: input.actor.id,
7722
+ tenantRef: input.actor.tenantRef ?? null,
5344
7723
  title: input.title ?? "New chat",
5345
7724
  transient: input.transient ?? false,
5346
7725
  createdAt: ts,
@@ -5350,20 +7729,20 @@ var InMemoryAgentStore = class {
5350
7729
  persona: input.persona
5351
7730
  } : {}
5352
7731
  };
5353
- this.threads.set(id, row);
5354
- return this.toSummary(row);
7732
+ this.threads.set(id, row2);
7733
+ return this.toSummary(row2);
5355
7734
  }
5356
7735
  async getThread(threadId) {
5357
- const row = this.threads.get(threadId);
5358
- if (row === void 0) {
7736
+ const row2 = this.threads.get(threadId);
7737
+ if (row2 === void 0) {
5359
7738
  return null;
5360
7739
  }
5361
7740
  return {
5362
- ...this.toSummary(row),
5363
- messages: row.messages.map((message) => this.withApprovals(message)),
7741
+ ...this.toSummary(row2),
7742
+ messages: row2.messages.map((message) => this.withApprovals(message)),
5364
7743
  // The run streaming right now — the one field the read-model reports it under.
5365
- ...row.activeStreamId !== void 0 ? {
5366
- activeRunId: row.activeStreamId
7744
+ ...row2.activeStreamId !== void 0 ? {
7745
+ activeRunId: row2.activeStreamId
5367
7746
  } : {}
5368
7747
  };
5369
7748
  }
@@ -5376,8 +7755,12 @@ var InMemoryAgentStore = class {
5376
7755
  }
5377
7756
  const approval = toolCallApprovalFromRow({
5378
7757
  toolCallId: call.toolCallId,
7758
+ ...call.proposalId !== void 0 ? {
7759
+ proposalId: call.proposalId
7760
+ } : {},
5379
7761
  status: call.status,
5380
7762
  approver: call.approver,
7763
+ confirmation: call.confirmation,
5381
7764
  expiresAt: call.expiresAt,
5382
7765
  remember: call.remember,
5383
7766
  executedByRef: call.executedByRef,
@@ -5400,6 +7783,10 @@ var InMemoryAgentStore = class {
5400
7783
  names.add(call.toolName);
5401
7784
  }
5402
7785
  }
7786
+ const thread = this.threads.get(threadId);
7787
+ if (thread) {
7788
+ for (const proposal of this.rows.values()) if (proposal.threadId === threadId && proposal.actorRef === thread.actorRef && proposal.tenantRef === thread.tenantRef && proposal.decisionAudit?.remember === true && (proposal.execution?.status === "succeeded" || proposal.execution?.status === "failed")) names.add(proposal.toolName);
7789
+ }
5403
7790
  return [
5404
7791
  ...names
5405
7792
  ];
@@ -5421,7 +7808,7 @@ var InMemoryAgentStore = class {
5421
7808
  async listThreads(actorRef, limit = 50) {
5422
7809
  return [
5423
7810
  ...this.threads.values()
5424
- ].filter((row) => row.actorRef === actorRef && !row.transient).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).slice(0, limit).map((row) => this.toSummary(row));
7811
+ ].filter((row2) => row2.actorRef === actorRef && !row2.transient).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).slice(0, limit).map((row2) => this.toSummary(row2));
5425
7812
  }
5426
7813
  async softDeleteThread(threadId) {
5427
7814
  this.threads.delete(threadId);
@@ -5437,9 +7824,10 @@ var InMemoryAgentStore = class {
5437
7824
  ];
5438
7825
  const id = crypto.randomUUID();
5439
7826
  const ts = this.now();
5440
- const row = {
7827
+ const row2 = {
5441
7828
  id,
5442
7829
  actorRef: source.actorRef,
7830
+ tenantRef: source.tenantRef,
5443
7831
  title: source.title,
5444
7832
  transient: false,
5445
7833
  createdAt: ts,
@@ -5458,8 +7846,8 @@ var InMemoryAgentStore = class {
5458
7846
  persona: source.persona
5459
7847
  } : {}
5460
7848
  };
5461
- this.threads.set(id, row);
5462
- return this.toSummary(row);
7849
+ this.threads.set(id, row2);
7850
+ return this.toSummary(row2);
5463
7851
  }
5464
7852
  async ownerOfThread(threadId) {
5465
7853
  return this.threads.get(threadId)?.actorRef ?? null;
@@ -5479,38 +7867,38 @@ var InMemoryAgentStore = class {
5479
7867
  return call.runId ?? this.threads.get(call.threadId)?.activeStreamId ?? null;
5480
7868
  }
5481
7869
  async ownerOfActiveStream(runId) {
5482
- for (const row of this.threads.values()) {
5483
- if (row.activeStreamId === runId) {
5484
- return row.actorRef;
7870
+ for (const row2 of this.threads.values()) {
7871
+ if (row2.activeStreamId === runId) {
7872
+ return row2.actorRef;
5485
7873
  }
5486
7874
  }
5487
7875
  return null;
5488
7876
  }
5489
7877
  async setTitle(threadId, title) {
5490
- const row = this.threads.get(threadId);
5491
- if (row !== void 0) {
5492
- row.title = title;
5493
- row.updatedAt = this.now();
7878
+ const row2 = this.threads.get(threadId);
7879
+ if (row2 !== void 0) {
7880
+ row2.title = title;
7881
+ row2.updatedAt = this.now();
5494
7882
  }
5495
7883
  }
5496
7884
  async updateThread(threadId, patch) {
5497
- const row = this.threads.get(threadId);
5498
- if (row === void 0) {
7885
+ const row2 = this.threads.get(threadId);
7886
+ if (row2 === void 0) {
5499
7887
  return;
5500
7888
  }
5501
7889
  if (patch.title !== void 0) {
5502
- row.title = patch.title;
7890
+ row2.title = patch.title;
5503
7891
  }
5504
7892
  if (patch.defaultAgent !== void 0) {
5505
- row.defaultAgent = patch.defaultAgent;
7893
+ row2.defaultAgent = patch.defaultAgent;
5506
7894
  }
5507
7895
  if (patch.model !== void 0) {
5508
- row.model = patch.model;
7896
+ row2.model = patch.model;
5509
7897
  }
5510
7898
  if (patch.persona !== void 0) {
5511
- row.persona = patch.persona;
7899
+ row2.persona = patch.persona;
5512
7900
  }
5513
- row.updatedAt = this.now();
7901
+ row2.updatedAt = this.now();
5514
7902
  }
5515
7903
  async activeRunForThread(threadId) {
5516
7904
  return this.threads.get(threadId)?.activeStreamId ?? null;
@@ -5558,64 +7946,64 @@ var InMemoryAgentStore = class {
5558
7946
  }
5559
7947
  /** Settle a run's outcome. A no-op when the run is unknown (mirrors `setTitle`/`updateThread`). */
5560
7948
  async recordRunEnd(end) {
5561
- const row = this.runs.get(end.runId);
5562
- if (row === void 0) {
7949
+ const row2 = this.runs.get(end.runId);
7950
+ if (row2 === void 0) {
5563
7951
  return;
5564
7952
  }
5565
- row.status = end.status;
5566
- row.settledAt = this.now();
7953
+ row2.status = end.status;
7954
+ row2.settledAt = this.now();
5567
7955
  if (end.durationMs !== void 0) {
5568
- row.durationMs = end.durationMs;
7956
+ row2.durationMs = end.durationMs;
5569
7957
  }
5570
7958
  if (end.errorCode !== void 0) {
5571
- row.errorCode = end.errorCode;
7959
+ row2.errorCode = end.errorCode;
5572
7960
  }
5573
7961
  if (end.errorMessage !== void 0) {
5574
- row.errorMessage = end.errorMessage;
7962
+ row2.errorMessage = end.errorMessage;
5575
7963
  }
5576
7964
  }
5577
7965
  /** Bump the run's llm-step retry counter. */
5578
7966
  async bumpRunRetries(runId) {
5579
- const row = this.runs.get(runId);
5580
- if (row !== void 0) {
5581
- row.retries += 1;
7967
+ const row2 = this.runs.get(runId);
7968
+ if (row2 !== void 0) {
7969
+ row2.retries += 1;
5582
7970
  }
5583
7971
  }
5584
7972
  async promoteThread(threadId) {
5585
- const row = this.threads.get(threadId);
5586
- if (row?.transient) {
5587
- row.transient = false;
5588
- row.updatedAt = this.now();
7973
+ const row2 = this.threads.get(threadId);
7974
+ if (row2?.transient) {
7975
+ row2.transient = false;
7976
+ row2.updatedAt = this.now();
5589
7977
  }
5590
7978
  }
5591
7979
  async setActiveStream(threadId, runId) {
5592
- const row = this.threads.get(threadId);
5593
- if (row !== void 0) {
7980
+ const row2 = this.threads.get(threadId);
7981
+ if (row2 !== void 0) {
5594
7982
  if (runId === null) {
5595
- delete row.activeStreamId;
7983
+ delete row2.activeStreamId;
5596
7984
  } else {
5597
- row.activeStreamId = runId;
7985
+ row2.activeStreamId = runId;
5598
7986
  }
5599
7987
  }
5600
7988
  }
5601
7989
  async claimActiveStream(threadId, runId, options = {}) {
5602
- const row = this.threads.get(threadId);
5603
- if (row === void 0) {
7990
+ const row2 = this.threads.get(threadId);
7991
+ if (row2 === void 0) {
5604
7992
  return false;
5605
7993
  }
5606
- const holder = row.activeStreamId;
7994
+ const holder = row2.activeStreamId;
5607
7995
  if (holder !== void 0 && holder !== runId && holder !== options.replacing) {
5608
7996
  return false;
5609
7997
  }
5610
- row.activeStreamId = runId;
7998
+ row2.activeStreamId = runId;
5611
7999
  return true;
5612
8000
  }
5613
8001
  async releaseActiveStream(threadId, runId) {
5614
- const row = this.threads.get(threadId);
5615
- if (row?.activeStreamId !== runId) {
8002
+ const row2 = this.threads.get(threadId);
8003
+ if (row2?.activeStreamId !== runId) {
5616
8004
  return false;
5617
8005
  }
5618
- delete row.activeStreamId;
8006
+ delete row2.activeStreamId;
5619
8007
  return true;
5620
8008
  }
5621
8009
  async enqueueMessage(input) {
@@ -5640,6 +8028,9 @@ var InMemoryAgentStore = class {
5640
8028
  ...input.pageContext !== void 0 ? {
5641
8029
  pageContext: input.pageContext
5642
8030
  } : {},
8031
+ ...input.uiCapabilities !== void 0 ? {
8032
+ uiCapabilities: input.uiCapabilities
8033
+ } : {},
5643
8034
  ...input.interrupt === true ? {
5644
8035
  interrupt: true
5645
8036
  } : {},
@@ -5744,9 +8135,81 @@ var InMemoryAgentStore = class {
5744
8135
  }
5745
8136
  return void 0;
5746
8137
  }
8138
+ async getThreadActionProposalScope(threadId) {
8139
+ const row2 = this.threads.get(threadId);
8140
+ return row2 ? {
8141
+ threadId,
8142
+ actorRef: row2.actorRef,
8143
+ tenantRef: row2.tenantRef
8144
+ } : null;
8145
+ }
8146
+ async admitActionProposalOutcome(command) {
8147
+ const row2 = [
8148
+ ...this.rows.values()
8149
+ ].find((row3) => row3.outcome?.id === command.outcomeId);
8150
+ if (!row2?.outcome || !row2.outcomeDelivery) return {
8151
+ status: "not_found"
8152
+ };
8153
+ if (row2.outcomeDelivery.status === "admitted") return {
8154
+ status: "unchanged",
8155
+ ...row2.outcomeDelivery.messageId !== void 0 ? {
8156
+ messageId: row2.outcomeDelivery.messageId
8157
+ } : {}
8158
+ };
8159
+ if (row2.outcomeDelivery.status === "discarded") return {
8160
+ status: "discarded"
8161
+ };
8162
+ if (!actionProposalOutcomeFenceValid(row2, command, this.clock())) return {
8163
+ status: "conflict"
8164
+ };
8165
+ const thread = this.threads.get(row2.threadId);
8166
+ if (!thread) {
8167
+ this.rows.set(row2.id, snapshotActionProposal({
8168
+ ...row2,
8169
+ outcomeDelivery: {
8170
+ ...row2.outcomeDelivery,
8171
+ status: "discarded",
8172
+ lease: null
8173
+ }
8174
+ }));
8175
+ return {
8176
+ status: "discarded"
8177
+ };
8178
+ }
8179
+ if (thread.actorRef !== row2.actorRef || thread.tenantRef !== row2.tenantRef) return {
8180
+ status: "conflict"
8181
+ };
8182
+ if (thread.activeStreamId) return {
8183
+ status: "busy"
8184
+ };
8185
+ const message = {
8186
+ id: crypto.randomUUID(),
8187
+ role: "assistant",
8188
+ content: actionProposalOutcomeText(row2.outcome),
8189
+ createdAt: this.now(),
8190
+ actionProposalOutcome: snapshotActionProposal(row2.outcome),
8191
+ ui: snapshotActionProposal(row2.outcome.ui)
8192
+ };
8193
+ const next = snapshotActionProposal({
8194
+ ...row2,
8195
+ outcomeDelivery: {
8196
+ ...row2.outcomeDelivery,
8197
+ status: "admitted",
8198
+ lease: null,
8199
+ messageId: message.id
8200
+ }
8201
+ });
8202
+ thread.messages.push(message);
8203
+ thread.updatedAt = message.createdAt;
8204
+ this.rows.set(row2.id, next);
8205
+ return {
8206
+ status: "applied",
8207
+ messageId: message.id
8208
+ };
8209
+ }
5747
8210
  async appendMessage(input) {
5748
- const row = this.threads.get(input.threadId);
5749
- if (row === void 0) {
8211
+ const row2 = this.threads.get(input.threadId);
8212
+ if (row2 === void 0) {
5750
8213
  throw new Error(`thread ${input.threadId} not found`);
5751
8214
  }
5752
8215
  const message = {
@@ -5786,15 +8249,18 @@ var InMemoryAgentStore = class {
5786
8249
  } : {},
5787
8250
  ...input.ui !== void 0 ? {
5788
8251
  ui: input.ui
8252
+ } : {},
8253
+ ...input.actionProposalOutcome !== void 0 ? {
8254
+ actionProposalOutcome: input.actionProposalOutcome
5789
8255
  } : {}
5790
8256
  };
5791
- row.messages.push(message);
5792
- row.updatedAt = message.createdAt;
8257
+ row2.messages.push(message);
8258
+ row2.updatedAt = message.createdAt;
5793
8259
  return message;
5794
8260
  }
5795
8261
  async setMessageToolResults(messageId, results) {
5796
- for (const row of this.threads.values()) {
5797
- const message = row.messages.find((candidate) => candidate.id === messageId);
8262
+ for (const row2 of this.threads.values()) {
8263
+ const message = row2.messages.find((candidate) => candidate.id === messageId);
5798
8264
  if (message !== void 0) {
5799
8265
  message.toolResults = results;
5800
8266
  return;
@@ -5802,12 +8268,12 @@ var InMemoryAgentStore = class {
5802
8268
  }
5803
8269
  }
5804
8270
  async setMessageUi(messageId, ui) {
5805
- for (const row of this.threads.values()) {
5806
- const index = row.messages.findIndex((candidate) => candidate.id === messageId);
5807
- const message = row.messages[index];
8271
+ for (const row2 of this.threads.values()) {
8272
+ const index = row2.messages.findIndex((candidate) => candidate.id === messageId);
8273
+ const message = row2.messages[index];
5808
8274
  if (message !== void 0) {
5809
8275
  const { ui: _previous, ...rest } = message;
5810
- row.messages[index] = ui.length > 0 ? {
8276
+ row2.messages[index] = ui.length > 0 ? {
5811
8277
  ...rest,
5812
8278
  ui
5813
8279
  } : rest;
@@ -5819,12 +8285,12 @@ var InMemoryAgentStore = class {
5819
8285
  return this.threadIdForMessage(messageId) ?? null;
5820
8286
  }
5821
8287
  async setMessageFeedback(messageId, feedback) {
5822
- for (const row of this.threads.values()) {
5823
- const index = row.messages.findIndex((candidate) => candidate.id === messageId);
5824
- const message = row.messages[index];
8288
+ for (const row2 of this.threads.values()) {
8289
+ const index = row2.messages.findIndex((candidate) => candidate.id === messageId);
8290
+ const message = row2.messages[index];
5825
8291
  if (message !== void 0) {
5826
8292
  const { feedback: _previous, ...rest } = message;
5827
- row.messages[index] = feedback !== null ? {
8293
+ row2.messages[index] = feedback !== null ? {
5828
8294
  ...rest,
5829
8295
  feedback
5830
8296
  } : rest;
@@ -5833,13 +8299,13 @@ var InMemoryAgentStore = class {
5833
8299
  }
5834
8300
  }
5835
8301
  async truncateFrom(threadId, messageId) {
5836
- const row = this.threads.get(threadId);
5837
- if (row === void 0) {
8302
+ const row2 = this.threads.get(threadId);
8303
+ if (row2 === void 0) {
5838
8304
  return;
5839
8305
  }
5840
- const cutoff = row.messages.findIndex((message) => message.id === messageId);
8306
+ const cutoff = row2.messages.findIndex((message) => message.id === messageId);
5841
8307
  if (cutoff >= 0) {
5842
- row.messages = row.messages.slice(0, cutoff);
8308
+ row2.messages = row2.messages.slice(0, cutoff);
5843
8309
  }
5844
8310
  }
5845
8311
  /**
@@ -5877,6 +8343,9 @@ var InMemoryAgentStore = class {
5877
8343
  async recordToolCall(input) {
5878
8344
  this.toolCalls.set(input.toolCallId, {
5879
8345
  toolCallId: input.toolCallId,
8346
+ ...input.proposalId !== void 0 ? {
8347
+ proposalId: input.proposalId
8348
+ } : {},
5880
8349
  messageId: input.messageId,
5881
8350
  threadId: this.threadIdForMessage(input.messageId) ?? "",
5882
8351
  toolName: input.toolName,
@@ -5890,6 +8359,9 @@ var InMemoryAgentStore = class {
5890
8359
  ...input.approver !== void 0 ? {
5891
8360
  approver: input.approver
5892
8361
  } : {},
8362
+ ...input.confirmation !== void 0 ? {
8363
+ confirmation: structuredClone(input.confirmation)
8364
+ } : {},
5893
8365
  ...input.expiresAt !== void 0 ? {
5894
8366
  expiresAt: input.expiresAt
5895
8367
  } : {}
@@ -5898,16 +8370,16 @@ var InMemoryAgentStore = class {
5898
8370
  async toolCallOutcomes(toolCallIds) {
5899
8371
  const outcomes = [];
5900
8372
  for (const id of toolCallIds) {
5901
- const row = this.toolCalls.get(id);
5902
- if (row !== void 0) {
8373
+ const row2 = this.toolCalls.get(id);
8374
+ if (row2 !== void 0) {
5903
8375
  outcomes.push({
5904
8376
  id,
5905
- status: row.status,
5906
- ...row.output !== void 0 ? {
5907
- output: row.output
8377
+ status: row2.status,
8378
+ ...row2.output !== void 0 ? {
8379
+ output: row2.output
5908
8380
  } : {},
5909
- ...row.error !== void 0 ? {
5910
- error: row.error
8381
+ ...row2.error !== void 0 ? {
8382
+ error: row2.error
5911
8383
  } : {}
5912
8384
  });
5913
8385
  }
@@ -5916,38 +8388,38 @@ var InMemoryAgentStore = class {
5916
8388
  }
5917
8389
  async failUnsettledToolCalls(runId, error) {
5918
8390
  let settled = 0;
5919
- for (const row of this.toolCalls.values()) {
5920
- if (row.runId === runId && row.status === "pending_approval") {
5921
- row.status = "failed";
5922
- row.error = error;
8391
+ for (const row2 of this.toolCalls.values()) {
8392
+ if (row2.runId === runId && row2.status === "pending_approval") {
8393
+ row2.status = "failed";
8394
+ row2.error = error;
5923
8395
  settled += 1;
5924
8396
  }
5925
8397
  }
5926
8398
  return settled;
5927
8399
  }
5928
8400
  async updateToolCall(input) {
5929
- const row = this.toolCalls.get(input.toolCallId);
5930
- if (row === void 0) {
8401
+ const row2 = this.toolCalls.get(input.toolCallId);
8402
+ if (row2 === void 0) {
5931
8403
  return;
5932
8404
  }
5933
- row.status = input.status;
8405
+ row2.status = input.status;
5934
8406
  if (input.output !== void 0) {
5935
- row.output = input.output;
8407
+ row2.output = input.output;
5936
8408
  }
5937
8409
  if (input.error !== void 0) {
5938
- row.error = input.error;
8410
+ row2.error = input.error;
5939
8411
  }
5940
8412
  if (input.executionMs !== void 0) {
5941
- row.executionMs = input.executionMs;
8413
+ row2.executionMs = input.executionMs;
5942
8414
  }
5943
8415
  if (input.executedByRef !== void 0) {
5944
- row.executedByRef = input.executedByRef;
8416
+ row2.executedByRef = input.executedByRef;
5945
8417
  }
5946
8418
  if (input.remember !== void 0) {
5947
- row.remember = input.remember;
8419
+ row2.remember = input.remember;
5948
8420
  }
5949
8421
  if (input.decidedVia !== void 0) {
5950
- row.decidedVia = input.decidedVia;
8422
+ row2.decidedVia = input.decidedVia;
5951
8423
  }
5952
8424
  }
5953
8425
  async recordUsage(input) {
@@ -5975,9 +8447,9 @@ var InMemoryAgentStore = class {
5975
8447
  return this.usageBetween(actorRef, day, day);
5976
8448
  }
5977
8449
  async usageBetween(actorRef, fromDay, toDay) {
5978
- const rows = this.usage.filter((row) => row.actorRef === actorRef && row.day >= fromDay && row.day <= toDay);
5979
- const usedTokens = rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);
5980
- const costUsd = rows.reduce((sum, row) => sum + (row.costUsd ?? 0), 0);
8450
+ const rows = this.usage.filter((row2) => row2.actorRef === actorRef && row2.day >= fromDay && row2.day <= toDay);
8451
+ const usedTokens = rows.reduce((sum, row2) => sum + row2.inputTokens + row2.outputTokens, 0);
8452
+ const costUsd = rows.reduce((sum, row2) => sum + (row2.costUsd ?? 0), 0);
5981
8453
  return {
5982
8454
  usedTokens,
5983
8455
  costUsd
@@ -5985,30 +8457,30 @@ var InMemoryAgentStore = class {
5985
8457
  }
5986
8458
  /** Test helper: read the recorded usage rows (modelId + token totals). */
5987
8459
  usageRows() {
5988
- return this.usage.map((row) => ({
5989
- actorRef: row.actorRef,
5990
- tokens: row.inputTokens + row.outputTokens,
5991
- modelId: row.modelId
8460
+ return this.usage.map((row2) => ({
8461
+ actorRef: row2.actorRef,
8462
+ tokens: row2.inputTokens + row2.outputTokens,
8463
+ modelId: row2.modelId
5992
8464
  }));
5993
8465
  }
5994
8466
  /** Governance read-model feed: recorded usage rows with the input/output split + thread/day. */
5995
8467
  governanceUsage() {
5996
- return this.usage.map((row) => ({
5997
- actorRef: row.actorRef,
5998
- threadId: row.threadId,
5999
- modelId: row.modelId,
6000
- inputTokens: row.inputTokens,
6001
- outputTokens: row.outputTokens,
6002
- day: row.day,
6003
- createdAt: row.createdAt,
6004
- ...row.cacheWriteTokens !== void 0 ? {
6005
- cacheWriteTokens: row.cacheWriteTokens
8468
+ return this.usage.map((row2) => ({
8469
+ actorRef: row2.actorRef,
8470
+ threadId: row2.threadId,
8471
+ modelId: row2.modelId,
8472
+ inputTokens: row2.inputTokens,
8473
+ outputTokens: row2.outputTokens,
8474
+ day: row2.day,
8475
+ createdAt: row2.createdAt,
8476
+ ...row2.cacheWriteTokens !== void 0 ? {
8477
+ cacheWriteTokens: row2.cacheWriteTokens
6006
8478
  } : {},
6007
- ...row.cacheReadTokens !== void 0 ? {
6008
- cacheReadTokens: row.cacheReadTokens
8479
+ ...row2.cacheReadTokens !== void 0 ? {
8480
+ cacheReadTokens: row2.cacheReadTokens
6009
8481
  } : {},
6010
- ...row.costUsd !== void 0 ? {
6011
- costUsd: row.costUsd
8482
+ ...row2.costUsd !== void 0 ? {
8483
+ costUsd: row2.costUsd
6012
8484
  } : {}
6013
8485
  }));
6014
8486
  }
@@ -6016,22 +8488,22 @@ var InMemoryAgentStore = class {
6016
8488
  governanceToolCalls() {
6017
8489
  return [
6018
8490
  ...this.toolCalls.values()
6019
- ].map((row) => ({
6020
- toolCallId: row.toolCallId,
6021
- toolName: row.toolName,
6022
- toolType: row.toolType,
6023
- status: row.status,
6024
- threadId: row.threadId,
6025
- messageId: row.messageId,
6026
- createdAt: row.createdAt,
6027
- ...row.executionMs !== void 0 ? {
6028
- executionMs: row.executionMs
8491
+ ].map((row2) => ({
8492
+ toolCallId: row2.toolCallId,
8493
+ toolName: row2.toolName,
8494
+ toolType: row2.toolType,
8495
+ status: row2.status,
8496
+ threadId: row2.threadId,
8497
+ messageId: row2.messageId,
8498
+ createdAt: row2.createdAt,
8499
+ ...row2.executionMs !== void 0 ? {
8500
+ executionMs: row2.executionMs
6029
8501
  } : {},
6030
- ...row.error !== void 0 ? {
6031
- error: row.error
8502
+ ...row2.error !== void 0 ? {
8503
+ error: row2.error
6032
8504
  } : {},
6033
- ...row.runId !== void 0 ? {
6034
- runId: row.runId
8505
+ ...row2.runId !== void 0 ? {
8506
+ runId: row2.runId
6035
8507
  } : {}
6036
8508
  }));
6037
8509
  }
@@ -6091,51 +8563,51 @@ var InMemoryAgentStore = class {
6091
8563
  governanceThreads() {
6092
8564
  return [
6093
8565
  ...this.threads.values()
6094
- ].map((row) => ({
6095
- threadId: row.id,
6096
- title: row.title,
6097
- actorRef: row.actorRef,
6098
- messageCount: row.messages.length,
6099
- updatedAt: row.updatedAt
8566
+ ].map((row2) => ({
8567
+ threadId: row2.id,
8568
+ title: row2.title,
8569
+ actorRef: row2.actorRef,
8570
+ messageCount: row2.messages.length,
8571
+ updatedAt: row2.updatedAt
6100
8572
  }));
6101
8573
  }
6102
8574
  /** Governance read-model feed: recorded run outcomes (the reliability surfaces). */
6103
8575
  governanceRuns() {
6104
8576
  return [
6105
8577
  ...this.runs.values()
6106
- ].map((row) => ({
6107
- runId: row.runId,
6108
- threadId: row.threadId,
6109
- actorRef: row.actorRef,
6110
- status: row.status,
6111
- retries: row.retries,
6112
- startedAt: row.startedAt,
6113
- ...row.agentName !== void 0 ? {
6114
- agentName: row.agentName
8578
+ ].map((row2) => ({
8579
+ runId: row2.runId,
8580
+ threadId: row2.threadId,
8581
+ actorRef: row2.actorRef,
8582
+ status: row2.status,
8583
+ retries: row2.retries,
8584
+ startedAt: row2.startedAt,
8585
+ ...row2.agentName !== void 0 ? {
8586
+ agentName: row2.agentName
6115
8587
  } : {},
6116
- ...row.parentRunId !== void 0 ? {
6117
- parentRunId: row.parentRunId
8588
+ ...row2.parentRunId !== void 0 ? {
8589
+ parentRunId: row2.parentRunId
6118
8590
  } : {},
6119
- ...row.durationMs !== void 0 ? {
6120
- durationMs: row.durationMs
8591
+ ...row2.durationMs !== void 0 ? {
8592
+ durationMs: row2.durationMs
6121
8593
  } : {},
6122
- ...row.errorCode !== void 0 ? {
6123
- errorCode: row.errorCode
8594
+ ...row2.errorCode !== void 0 ? {
8595
+ errorCode: row2.errorCode
6124
8596
  } : {},
6125
- ...row.errorMessage !== void 0 ? {
6126
- errorMessage: row.errorMessage
8597
+ ...row2.errorMessage !== void 0 ? {
8598
+ errorMessage: row2.errorMessage
6127
8599
  } : {},
6128
- ...row.settledAt !== void 0 ? {
6129
- settledAt: row.settledAt
8600
+ ...row2.settledAt !== void 0 ? {
8601
+ settledAt: row2.settledAt
6130
8602
  } : {},
6131
- ...row.promptHash !== void 0 ? {
6132
- promptHash: row.promptHash
8603
+ ...row2.promptHash !== void 0 ? {
8604
+ promptHash: row2.promptHash
6133
8605
  } : {}
6134
8606
  }));
6135
8607
  }
6136
8608
  threadIdForMessage(messageId) {
6137
- for (const [threadId, row] of this.threads) {
6138
- if (row.messages.some((message) => message.id === messageId)) {
8609
+ for (const [threadId, row2] of this.threads) {
8610
+ if (row2.messages.some((message) => message.id === messageId)) {
6139
8611
  return threadId;
6140
8612
  }
6141
8613
  }
@@ -6145,77 +8617,77 @@ var InMemoryAgentStore = class {
6145
8617
  toolCallRows() {
6146
8618
  return [
6147
8619
  ...this.toolCalls.values()
6148
- ].map((row) => ({
6149
- toolCallId: row.toolCallId,
6150
- toolName: row.toolName,
6151
- toolType: row.toolType,
6152
- status: row.status,
6153
- input: row.input,
6154
- ...row.output !== void 0 ? {
6155
- output: row.output
8620
+ ].map((row2) => ({
8621
+ toolCallId: row2.toolCallId,
8622
+ toolName: row2.toolName,
8623
+ toolType: row2.toolType,
8624
+ status: row2.status,
8625
+ input: row2.input,
8626
+ ...row2.output !== void 0 ? {
8627
+ output: row2.output
6156
8628
  } : {},
6157
- ...row.error !== void 0 ? {
6158
- error: row.error
8629
+ ...row2.error !== void 0 ? {
8630
+ error: row2.error
6159
8631
  } : {},
6160
- ...row.runId !== void 0 ? {
6161
- runId: row.runId
8632
+ ...row2.runId !== void 0 ? {
8633
+ runId: row2.runId
6162
8634
  } : {},
6163
- ...row.executedByRef !== void 0 ? {
6164
- executedByRef: row.executedByRef
8635
+ ...row2.executedByRef !== void 0 ? {
8636
+ executedByRef: row2.executedByRef
6165
8637
  } : {},
6166
- ...row.approver !== void 0 ? {
6167
- approver: row.approver
8638
+ ...row2.approver !== void 0 ? {
8639
+ approver: row2.approver
6168
8640
  } : {},
6169
- ...row.expiresAt !== void 0 ? {
6170
- expiresAt: row.expiresAt
8641
+ ...row2.expiresAt !== void 0 ? {
8642
+ expiresAt: row2.expiresAt
6171
8643
  } : {},
6172
- ...row.remember !== void 0 ? {
6173
- remember: row.remember
8644
+ ...row2.remember !== void 0 ? {
8645
+ remember: row2.remember
6174
8646
  } : {},
6175
- ...row.decidedVia !== void 0 ? {
6176
- decidedVia: row.decidedVia
8647
+ ...row2.decidedVia !== void 0 ? {
8648
+ decidedVia: row2.decidedVia
6177
8649
  } : {}
6178
8650
  }));
6179
8651
  }
6180
- toSummary(row) {
6181
- const last = row.messages[row.messages.length - 1];
8652
+ toSummary(row2) {
8653
+ const last = row2.messages[row2.messages.length - 1];
6182
8654
  return {
6183
- id: row.id,
6184
- title: row.title,
6185
- transient: row.transient,
6186
- createdAt: row.createdAt,
6187
- updatedAt: row.updatedAt,
8655
+ id: row2.id,
8656
+ title: row2.title,
8657
+ transient: row2.transient,
8658
+ createdAt: row2.createdAt,
8659
+ updatedAt: row2.updatedAt,
6188
8660
  ...last !== void 0 ? {
6189
8661
  lastMessagePreview: last.content.slice(0, 120)
6190
8662
  } : {},
6191
- defaultAgent: row.defaultAgent ?? null,
6192
- ...row.model != null ? {
6193
- model: row.model
8663
+ defaultAgent: row2.defaultAgent ?? null,
8664
+ ...row2.model != null ? {
8665
+ model: row2.model
6194
8666
  } : {},
6195
- persona: row.persona ?? null
8667
+ persona: row2.persona ?? null
6196
8668
  };
6197
8669
  }
6198
8670
  };
6199
8671
 
6200
8672
  // src/confirm-token.ts
6201
- var import_node_crypto2 = require("crypto");
8673
+ var import_node_crypto3 = require("crypto");
6202
8674
  var DEFAULT_CONFIRM_TTL_MS = 15 * 60 * 1e3;
6203
- function canonicalJson(value) {
8675
+ function canonicalJson2(value) {
6204
8676
  if (value === void 0) return "null";
6205
8677
  if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
6206
- if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
8678
+ if (Array.isArray(value)) return `[${value.map(canonicalJson2).join(",")}]`;
6207
8679
  if (typeof value.toJSON === "function") {
6208
- return canonicalJson(value.toJSON());
8680
+ return canonicalJson2(value.toJSON());
6209
8681
  }
6210
8682
  const entries = Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
6211
- return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
8683
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson2(entry)}`).join(",")}}`;
6212
8684
  }
6213
- __name(canonicalJson, "canonicalJson");
8685
+ __name(canonicalJson2, "canonicalJson");
6214
8686
  function signature(subject, expiresAt, secret) {
6215
8687
  if (secret === "") {
6216
8688
  throw new Error("confirm token: the secret is empty");
6217
8689
  }
6218
- return (0, import_node_crypto2.createHmac)("sha256", secret).update(canonicalJson([
8690
+ return (0, import_node_crypto3.createHmac)("sha256", secret).update(canonicalJson2([
6219
8691
  "agent-confirm",
6220
8692
  subject.tool,
6221
8693
  subject.actorId,
@@ -6242,11 +8714,11 @@ function verifyConfirmToken(token, subject, options) {
6242
8714
  if (expiresAt === void 0 || expiresAt < (options.now ?? Date.now())) return false;
6243
8715
  const expected = Buffer.from(signature(subject, expiresAt, options.secret));
6244
8716
  const given = Buffer.from(token.slice(token.indexOf(".") + 1));
6245
- return expected.length === given.length && (0, import_node_crypto2.timingSafeEqual)(expected, given);
8717
+ return expected.length === given.length && (0, import_node_crypto3.timingSafeEqual)(expected, given);
6246
8718
  }
6247
8719
  __name(verifyConfirmToken, "verifyConfirmToken");
6248
8720
  function hashConfirmToken(token) {
6249
- return (0, import_node_crypto2.createHash)("sha256").update(token).digest("hex");
8721
+ return (0, import_node_crypto3.createHash)("sha256").update(token).digest("hex");
6250
8722
  }
6251
8723
  __name(hashConfirmToken, "hashConfirmToken");
6252
8724
  var InMemoryConfirmTokenStore = class {
@@ -6394,12 +8866,12 @@ function withConfirmFields(schema) {
6394
8866
  });
6395
8867
  }
6396
8868
  const { args, fields } = splitConfirm(value);
6397
- const result = await inner.validate(args);
6398
- if (result.issues !== void 0) {
8869
+ const result2 = await inner.validate(args);
8870
+ if (result2.issues !== void 0) {
6399
8871
  return {
6400
8872
  issues: [
6401
8873
  ...issues,
6402
- ...result.issues
8874
+ ...result2.issues
6403
8875
  ]
6404
8876
  };
6405
8877
  }
@@ -6409,10 +8881,10 @@ function withConfirmFields(schema) {
6409
8881
  };
6410
8882
  }
6411
8883
  return {
6412
- value: isRecord(result.value) ? {
6413
- ...result.value,
8884
+ value: isRecord(result2.value) ? {
8885
+ ...result2.value,
6414
8886
  ...fields
6415
- } : result.value
8887
+ } : result2.value
6416
8888
  };
6417
8889
  }, "validate"),
6418
8890
  jsonSchema: {
@@ -6543,8 +9015,8 @@ var SqlTokenStreamSink = class {
6543
9015
  autoPurge;
6544
9016
  writes = /* @__PURE__ */ new Map();
6545
9017
  lastAutoPurge = 0;
6546
- constructor(table, options = {}) {
6547
- this.table = table;
9018
+ constructor(table2, options = {}) {
9019
+ this.table = table2;
6548
9020
  this.pollIntervalMs = Math.max(1, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
6549
9021
  this.idlePollIntervalMs = Math.max(this.pollIntervalMs, options.idlePollIntervalMs ?? DEFAULT_IDLE_POLL_INTERVAL_MS);
6550
9022
  this.flushMs = Math.max(0, options.flushMs ?? DEFAULT_FLUSH_MS);
@@ -6686,15 +9158,15 @@ var SqlTokenStreamSink = class {
6686
9158
  let quietSince = Date.now();
6687
9159
  while (true) {
6688
9160
  const rows = await this.table.read(runId, cursor, READ_PAGE);
6689
- for (const row of rows) {
6690
- cursor = row.seq;
6691
- if (row.frame === null) {
6692
- if (row.error !== null) {
6693
- throw new AgentStreamError(JSON.parse(row.error));
9161
+ for (const row2 of rows) {
9162
+ cursor = row2.seq;
9163
+ if (row2.frame === null) {
9164
+ if (row2.error !== null) {
9165
+ throw new AgentStreamError(JSON.parse(row2.error));
6694
9166
  }
6695
9167
  return;
6696
9168
  }
6697
- yield encoder2.encode(row.frame);
9169
+ yield encoder2.encode(row2.frame);
6698
9170
  }
6699
9171
  if (rows.length > 0) {
6700
9172
  quietSince = Date.now();
@@ -6744,6 +9216,436 @@ var SqlTokenStreamSink = class {
6744
9216
  });
6745
9217
  }
6746
9218
  };
9219
+
9220
+ // src/action-proposal-text.ts
9221
+ function resolveTextActionProposalDecision(text, proposals, vocabulary) {
9222
+ const command = parseTextActionProposalCommand(text, vocabulary);
9223
+ if (command.status === "unmatched") return command;
9224
+ const { decision, remember } = command;
9225
+ const pending = proposals.filter((proposal) => proposal.decision === "pending");
9226
+ const explicitId = command.proposalId;
9227
+ if (explicitId !== void 0) {
9228
+ if (!pending.some((proposal) => proposal.id === explicitId)) return {
9229
+ status: "unmatched"
9230
+ };
9231
+ return {
9232
+ status: "decision",
9233
+ proposalId: explicitId,
9234
+ decision,
9235
+ remember
9236
+ };
9237
+ }
9238
+ if (pending.length === 0) return {
9239
+ status: "unmatched"
9240
+ };
9241
+ if (pending.length > 1) return {
9242
+ status: "ambiguous",
9243
+ proposalIds: pending.map(({ id }) => id)
9244
+ };
9245
+ const candidate = pending[0];
9246
+ if (!candidate) return {
9247
+ status: "unmatched"
9248
+ };
9249
+ return {
9250
+ status: "decision",
9251
+ proposalId: candidate.id,
9252
+ decision,
9253
+ remember
9254
+ };
9255
+ }
9256
+ __name(resolveTextActionProposalDecision, "resolveTextActionProposalDecision");
9257
+ var DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY = {
9258
+ approve: [
9259
+ "yes",
9260
+ "confirm",
9261
+ "approve",
9262
+ "approved",
9263
+ "ok"
9264
+ ],
9265
+ reject: [
9266
+ "no",
9267
+ "cancel",
9268
+ "reject",
9269
+ "deny"
9270
+ ],
9271
+ remember: [
9272
+ "always in this conversation"
9273
+ ]
9274
+ };
9275
+ var DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES = {
9276
+ approved: "Proposal approved and queued to run.",
9277
+ rejected: "Proposal rejected; nothing was run.",
9278
+ expired: "The proposal expired; nothing was run.",
9279
+ unchanged: "This proposal could not be changed; refresh the list to see where it stands.",
9280
+ ambiguous: /* @__PURE__ */ __name((ids) => `Which proposal? Reply confirm #ID or cancel #ID: ${ids.join(", ")}`, "ambiguous"),
9281
+ tooMany: "There are several proposals. Confirm or reject one with an explicit #ID."
9282
+ };
9283
+ function textActionProposalReply(result2, decision, replies = DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES) {
9284
+ return result2.status === "expired" || result2.proposal?.decision === "expired" ? replies.expired : result2.status === "applied" || result2.status === "unchanged" ? decision === "approved" ? replies.approved : replies.rejected : replies.unchanged;
9285
+ }
9286
+ __name(textActionProposalReply, "textActionProposalReply");
9287
+ var escapeRegExp = /* @__PURE__ */ __name((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "escapeRegExp");
9288
+ var alternatives = /* @__PURE__ */ __name((words) => words.map((word) => word.trim()).filter((word) => word.length > 0).map((word) => escapeRegExp(word).replace(/\s+/g, "\\s+")).join("|"), "alternatives");
9289
+ var fold = /* @__PURE__ */ __name((word) => word.trim().toLowerCase(), "fold");
9290
+ function parseTextActionProposalCommand(text, vocabulary = DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY) {
9291
+ if (/[\r\n]/u.test(text)) return {
9292
+ status: "unmatched"
9293
+ };
9294
+ const verbs = alternatives([
9295
+ ...vocabulary.approve,
9296
+ ...vocabulary.reject
9297
+ ]);
9298
+ if (verbs.length === 0) return {
9299
+ status: "unmatched"
9300
+ };
9301
+ const remember = alternatives(vocabulary.remember);
9302
+ const match = new RegExp(`^(${verbs})${remember.length > 0 ? `(?: (${remember}))?` : "()"}(?: #([^\\s]+?))?[.!]?$`, "iu").exec(text.trim());
9303
+ if (!match) return {
9304
+ status: "unmatched"
9305
+ };
9306
+ const verb = match[1] === void 0 ? void 0 : fold(match[1]);
9307
+ if (verb === void 0) return {
9308
+ status: "unmatched"
9309
+ };
9310
+ const decision = vocabulary.reject.some((word) => fold(word) === verb) ? "rejected" : "approved";
9311
+ const remembers = match[2] !== void 0 && match[2] !== "";
9312
+ if (remembers && decision !== "approved") return {
9313
+ status: "unmatched"
9314
+ };
9315
+ return {
9316
+ status: "command",
9317
+ decision,
9318
+ remember: remembers,
9319
+ ...match[3] !== void 0 ? {
9320
+ proposalId: match[3]
9321
+ } : {}
9322
+ };
9323
+ }
9324
+ __name(parseTextActionProposalCommand, "parseTextActionProposalCommand");
9325
+ var ptBrActionProposalText = {
9326
+ vocabulary: {
9327
+ approve: [
9328
+ "sim",
9329
+ "confirmo",
9330
+ "confirmar",
9331
+ "aprovar",
9332
+ "aprovo",
9333
+ "pode",
9334
+ ...DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY.approve
9335
+ ],
9336
+ reject: [
9337
+ "n\xE3o",
9338
+ "nao",
9339
+ "cancelar",
9340
+ "cancela",
9341
+ "rejeitar",
9342
+ "rejeito",
9343
+ ...DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY.reject
9344
+ ],
9345
+ remember: [
9346
+ "sempre nesta conversa",
9347
+ ...DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY.remember
9348
+ ]
9349
+ },
9350
+ replies: {
9351
+ approved: "Proposta aprovada e enfileirada para execu\xE7\xE3o.",
9352
+ rejected: "Proposta rejeitada; nenhuma a\xE7\xE3o foi executada.",
9353
+ expired: "A proposta expirou; nenhuma a\xE7\xE3o foi executada.",
9354
+ unchanged: "N\xE3o foi poss\xEDvel alterar esta proposta; atualize a lista para consultar seu estado.",
9355
+ ambiguous: /* @__PURE__ */ __name((ids) => `Qual proposta? Responda confirmar #ID ou cancelar #ID: ${ids.join(", ")}`, "ambiguous"),
9356
+ tooMany: "H\xE1 v\xE1rias propostas. Confirme ou rejeite usando #ID expl\xEDcito."
9357
+ }
9358
+ };
9359
+
9360
+ // src/action-proposal-view.ts
9361
+ function toActionProposalView(proposal) {
9362
+ const { execution, outcomeDelivery, idempotencyKey: _idempotencyKey, preparationInput: _preparationInput, executionContext: _executionContext, ...rest } = proposal;
9363
+ return {
9364
+ ...rest,
9365
+ execution: execution === null ? null : withoutLease(execution),
9366
+ ...outcomeDelivery !== void 0 ? {
9367
+ outcomeDelivery: withoutLease(outcomeDelivery)
9368
+ } : {}
9369
+ };
9370
+ }
9371
+ __name(toActionProposalView, "toActionProposalView");
9372
+ function toActionProposalMutationView(result2) {
9373
+ const { proposal, ...rest } = result2;
9374
+ return {
9375
+ ...rest,
9376
+ ...proposal !== void 0 ? {
9377
+ proposal: toActionProposalView(proposal)
9378
+ } : {}
9379
+ };
9380
+ }
9381
+ __name(toActionProposalMutationView, "toActionProposalMutationView");
9382
+ function withoutLease(value) {
9383
+ const { lease: _lease, ...rest } = value;
9384
+ return rest;
9385
+ }
9386
+ __name(withoutLease, "withoutLease");
9387
+
9388
+ // src/action-proposal-executor.ts
9389
+ var ActionProposalExecutor = class {
9390
+ static {
9391
+ __name(this, "ActionProposalExecutor");
9392
+ }
9393
+ options;
9394
+ constructor(options) {
9395
+ this.options = options;
9396
+ }
9397
+ async execute(proposal) {
9398
+ let ui = createNegotiatedUiCollector(proposal.originToolCallId, {
9399
+ actor: {
9400
+ id: proposal.actorRef
9401
+ },
9402
+ threadId: proposal.threadId
9403
+ });
9404
+ try {
9405
+ const actor = await this.options.resolver.resolve({
9406
+ actorRef: proposal.actorRef,
9407
+ tenantRef: proposal.tenantRef
9408
+ });
9409
+ if (!actor || actor.id !== proposal.actorRef || (actor.tenantRef ?? null) !== proposal.tenantRef) throw new Error("Action proposal requester no longer exists or scope changed");
9410
+ const deps = await this.options.resolveExecution(proposal, actor);
9411
+ if (deps.registry.spec(proposal.toolName)?.kind !== "action") throw new Error("Action proposal tool no longer exists as an action");
9412
+ const context = proposal.executionContext;
9413
+ ui = createNegotiatedUiCollector(proposal.originToolCallId, {
9414
+ actor,
9415
+ threadId: proposal.threadId,
9416
+ ...context?.agentName !== void 0 ? {
9417
+ agentName: context.agentName
9418
+ } : {},
9419
+ ...context?.uiCapabilities !== void 0 ? {
9420
+ uiCapabilities: context.uiCapabilities
9421
+ } : {}
9422
+ }, deps.resolveUiCatalog);
9423
+ const output = await deps.registry.invoke(proposal.toolName, snapshotActionProposal(Object.hasOwn(proposal, "preparationInput") ? proposal.preparationInput : proposal.input), {
9424
+ actor,
9425
+ threadId: proposal.threadId,
9426
+ runId: proposal.originRunId,
9427
+ requestId: context?.requestId ?? proposal.originRunId,
9428
+ toolCallId: proposal.originToolCallId,
9429
+ idempotencyKey: proposal.idempotencyKey,
9430
+ ...context?.agentName !== void 0 ? {
9431
+ agentName: context.agentName
9432
+ } : {},
9433
+ ...context?.persona !== void 0 ? {
9434
+ persona: context.persona
9435
+ } : {},
9436
+ ...context?.pageContext !== void 0 ? {
9437
+ pageContext: context.pageContext
9438
+ } : {},
9439
+ ...context?.uiCapabilities !== void 0 ? {
9440
+ uiCapabilities: context.uiCapabilities
9441
+ } : {},
9442
+ ...deps.host !== void 0 ? {
9443
+ host: deps.host
9444
+ } : {},
9445
+ emitUi: ui.emit
9446
+ }, deps.rolesPolicy, {
9447
+ approvedInput: proposal.input,
9448
+ ...deps.allowedTools !== void 0 ? {
9449
+ allowedTools: deps.allowedTools
9450
+ } : {}
9451
+ });
9452
+ return snapshotActionProposal({
9453
+ status: "succeeded",
9454
+ ...output !== void 0 ? {
9455
+ result: output
9456
+ } : {},
9457
+ ui: ui.components(),
9458
+ ...ui.text() ? {
9459
+ text: ui.text()
9460
+ } : {}
9461
+ });
9462
+ } catch (error) {
9463
+ return {
9464
+ status: "failed",
9465
+ error: error instanceof Error ? error.message : String(error),
9466
+ ui: ui.components(),
9467
+ ...ui.text() ? {
9468
+ text: ui.text()
9469
+ } : {}
9470
+ };
9471
+ }
9472
+ }
9473
+ };
9474
+
9475
+ // src/action-proposal-worker.ts
9476
+ var ActionProposalWorker = class {
9477
+ static {
9478
+ __name(this, "ActionProposalWorker");
9479
+ }
9480
+ options;
9481
+ leaseMs;
9482
+ pollIntervalMs;
9483
+ maxConcurrency;
9484
+ timer;
9485
+ stopped = false;
9486
+ renewals = /* @__PURE__ */ new Set();
9487
+ active;
9488
+ constructor(options) {
9489
+ this.options = options;
9490
+ this.leaseMs = options.leaseMs ?? 3e4;
9491
+ this.pollIntervalMs = options.pollIntervalMs ?? 1e3;
9492
+ this.maxConcurrency = options.maxConcurrency ?? 1;
9493
+ for (const value of [
9494
+ this.leaseMs,
9495
+ this.pollIntervalMs,
9496
+ this.maxConcurrency
9497
+ ]) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError("Worker configuration must be positive safe integers");
9498
+ if (this.pollIntervalMs >= this.leaseMs / 3) throw new RangeError("Worker poll interval must be below one third of lease");
9499
+ if (!options.workerId) throw new TypeError("workerId is required");
9500
+ }
9501
+ start() {
9502
+ if (this.timer !== void 0 || this.stopped) return;
9503
+ const tick = /* @__PURE__ */ __name(async () => {
9504
+ if (this.stopped) return;
9505
+ try {
9506
+ await this.runOnce();
9507
+ } catch (error) {
9508
+ this.options.onError?.(error);
9509
+ }
9510
+ if (!this.stopped) this.timer = setTimeout(tick, this.pollIntervalMs);
9511
+ }, "tick");
9512
+ this.timer = setTimeout(tick, 0);
9513
+ }
9514
+ async stop() {
9515
+ this.stopped = true;
9516
+ for (const timer of this.renewals) clearInterval(timer);
9517
+ this.renewals.clear();
9518
+ if (this.timer !== void 0) clearTimeout(this.timer);
9519
+ await this.active;
9520
+ }
9521
+ runOnce() {
9522
+ if (this.active) return this.active;
9523
+ if (this.stopped) return Promise.resolve();
9524
+ const task = this.tick();
9525
+ this.active = task;
9526
+ return task.finally(() => {
9527
+ if (this.active === task) this.active = void 0;
9528
+ });
9529
+ }
9530
+ async tick() {
9531
+ await this.options.store.expireActionProposals({
9532
+ limit: 100
9533
+ });
9534
+ const work = [];
9535
+ for (let index = 0; index < this.maxConcurrency && !this.stopped; index++) {
9536
+ const proposal = await this.options.store.claimNextActionProposal({
9537
+ workerId: this.options.workerId,
9538
+ leaseMs: this.leaseMs
9539
+ });
9540
+ if (!proposal) break;
9541
+ work.push(this.execute(proposal));
9542
+ }
9543
+ await Promise.all(work);
9544
+ for (let index = 0; index < 32 && !this.stopped; index++) {
9545
+ const delivery = await this.options.store.claimNextActionProposalOutcome({
9546
+ workerId: this.options.workerId,
9547
+ leaseMs: this.leaseMs
9548
+ });
9549
+ if (!delivery) break;
9550
+ await this.options.store.admitActionProposalOutcome(delivery.lease);
9551
+ }
9552
+ }
9553
+ async execute(proposal) {
9554
+ const lease = proposal.execution?.lease;
9555
+ if (!lease) throw new Error("Claimed action lacks execution lease");
9556
+ let fenced = true;
9557
+ let renewing;
9558
+ const renew = /* @__PURE__ */ __name(() => {
9559
+ if (renewing || !fenced) return;
9560
+ renewing = this.options.store.extendActionProposalLease(proposal, proposal.id, {
9561
+ token: lease.token,
9562
+ generation: lease.generation,
9563
+ leaseMs: this.leaseMs
9564
+ }).then((result2) => {
9565
+ if (result2.status !== "applied") fenced = false;
9566
+ }, (error) => {
9567
+ fenced = false;
9568
+ this.options.onError?.(error);
9569
+ }).finally(() => {
9570
+ renewing = void 0;
9571
+ });
9572
+ }, "renew");
9573
+ const timer = setInterval(renew, this.leaseMs / 3);
9574
+ this.renewals.add(timer);
9575
+ try {
9576
+ const result2 = await this.options.executor.execute(proposal);
9577
+ clearInterval(timer);
9578
+ await renewing;
9579
+ if (!fenced) return;
9580
+ const settled = await this.options.store.settleActionProposal(proposal, proposal.id, {
9581
+ ...result2,
9582
+ token: lease.token,
9583
+ generation: lease.generation
9584
+ });
9585
+ if (settled.status === "applied" && settled.proposal) await this.options.onSettled?.(settled.proposal);
9586
+ } finally {
9587
+ clearInterval(timer);
9588
+ this.renewals.delete(timer);
9589
+ await renewing;
9590
+ }
9591
+ }
9592
+ };
9593
+
9594
+ // src/action-proposal-capabilities.ts
9595
+ function assertIndependentActionCapabilities(store, resolver) {
9596
+ const candidate = store;
9597
+ if (!resolver || typeof resolver.resolve !== "function") throw new Error("Independent action mode requires a BackgroundActorResolver");
9598
+ if (!isChatQueueStore(store) || candidate.actionProposalAdmissionSupported === false) throw new Error("Independent action mode requires transactional thread admission");
9599
+ const required = [
9600
+ "createActionProposal",
9601
+ "getActionProposal",
9602
+ "listActionProposals",
9603
+ "decideActionProposal",
9604
+ "claimActionProposal",
9605
+ "extendActionProposalLease",
9606
+ "settleActionProposal",
9607
+ "claimNextActionProposal",
9608
+ "expireActionProposals",
9609
+ "getThreadActionProposalScope",
9610
+ "claimNextActionProposalOutcome",
9611
+ "admitActionProposalOutcome",
9612
+ "createReplacingActionProposal",
9613
+ "supersedeActionProposal"
9614
+ ];
9615
+ if (required.some((method) => typeof candidate[method] !== "function")) throw new Error("Independent action mode requires proposal, worker, outcome and admission capabilities on the conversation store");
9616
+ }
9617
+ __name(assertIndependentActionCapabilities, "assertIndependentActionCapabilities");
9618
+
9619
+ // src/functional-tool.ts
9620
+ function createFunctionalTool(definition) {
9621
+ const { input, execute, present, preflight, isEnabled, canUse, describe, kind, ...metadata } = definition;
9622
+ return {
9623
+ spec: {
9624
+ ...metadata,
9625
+ kind: kind ?? "read",
9626
+ inputSchema: input
9627
+ },
9628
+ handler: {
9629
+ execute: execute.bind(definition),
9630
+ ...present === void 0 ? {} : {
9631
+ present: present.bind(definition)
9632
+ },
9633
+ ...preflight === void 0 ? {} : {
9634
+ preflight: preflight.bind(definition)
9635
+ },
9636
+ ...isEnabled === void 0 ? {} : {
9637
+ isEnabled: isEnabled.bind(definition)
9638
+ },
9639
+ ...canUse === void 0 ? {} : {
9640
+ canUse: canUse.bind(definition)
9641
+ },
9642
+ ...describe === void 0 ? {} : {
9643
+ describe: describe.bind(definition)
9644
+ }
9645
+ }
9646
+ };
9647
+ }
9648
+ __name(createFunctionalTool, "createFunctionalTool");
6747
9649
  // Annotate the CommonJS export names for ESM import in node:
6748
9650
  0 && (module.exports = {
6749
9651
  AGENT_ACTOR_DIRECTORY,
@@ -6778,6 +9680,8 @@ var SqlTokenStreamSink = class {
6778
9680
  APPROVAL_EXPIRED_REASON,
6779
9681
  ASK_TOOL_DESCRIPTION,
6780
9682
  ASK_TOOL_NAME,
9683
+ ActionProposalExecutor,
9684
+ ActionProposalWorker,
6781
9685
  AgentRegistry,
6782
9686
  AgentStreamError,
6783
9687
  CONFIRM_JSON_SCHEMA_PROPERTIES,
@@ -6792,12 +9696,15 @@ var SqlTokenStreamSink = class {
6792
9696
  DEFAULT_MAX_SKILLS,
6793
9697
  DEFAULT_REFUSAL_REASON,
6794
9698
  DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION,
9699
+ DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES,
9700
+ DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY,
6795
9701
  DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS,
6796
9702
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
6797
9703
  DefaultApprovalPolicy,
6798
9704
  DefaultRolesPolicy,
6799
9705
  ELICITATION_INPUT_TYPES,
6800
9706
  GLOBAL_SCOPE,
9707
+ InMemoryActionProposalStore,
6801
9708
  InMemoryAgentStore,
6802
9709
  InMemoryConfirmTokenStore,
6803
9710
  MAX_ASK_QUESTIONS,
@@ -6821,15 +9728,25 @@ var SqlTokenStreamSink = class {
6821
9728
  THREAD_DETAIL_CONTENT_CHARS,
6822
9729
  ToolDisabledError,
6823
9730
  ToolForbiddenError,
9731
+ ToolInputDriftError,
6824
9732
  ToolInputInvalidError,
6825
9733
  ToolNotFoundError,
9734
+ ToolPreflightDeniedError,
6826
9735
  ToolRegistry,
6827
9736
  UNFINISHED_TOOL_CALL,
9737
+ actionProposalCreationMatches,
9738
+ actionProposalOutcomeFenceValid,
9739
+ actionProposalOutcomeScope,
9740
+ actionProposalOutcomeText,
9741
+ actionProposalReceipt,
9742
+ actionProposalScopeMatches,
6828
9743
  actorScope,
6829
9744
  agentDiagnosticKey,
6830
9745
  agentFailureCode,
6831
9746
  askInputSchema,
6832
9747
  askToolDefinition,
9748
+ assertIndependentActionCapabilities,
9749
+ attachActionProposalOutcome,
6833
9750
  bucketByActor,
6834
9751
  bucketByModel,
6835
9752
  bucketByThread,
@@ -6837,12 +9754,16 @@ var SqlTokenStreamSink = class {
6837
9754
  buildMemoryBlock,
6838
9755
  buildSkillsBlock,
6839
9756
  canActorUseTool,
9757
+ canonicalActionProposalJson,
6840
9758
  canonicalJson,
9759
+ claimActionProposalOutcome,
6841
9760
  closeEmptyRoles,
6842
9761
  compositeSkillProvider,
6843
9762
  confirmTokenExpiry,
6844
9763
  createFrameBuffer,
9764
+ createFunctionalTool,
6845
9765
  createIncrementalGate,
9766
+ createNegotiatedUiCollector,
6846
9767
  createNoopEmitUi,
6847
9768
  createUiCollector,
6848
9769
  danglingToolCallIds,
@@ -6854,7 +9775,9 @@ var SqlTokenStreamSink = class {
6854
9775
  detachedDelivered,
6855
9776
  detachedStarted,
6856
9777
  detachedUnsettled,
9778
+ emitToolPresentations,
6857
9779
  encodeStreamEvent,
9780
+ escapeUnsafeToolUiText,
6858
9781
  estimateCost,
6859
9782
  estimateMessageTokens,
6860
9783
  exhaustedWindow,
@@ -6869,6 +9792,7 @@ var SqlTokenStreamSink = class {
6869
9792
  gateFollowUps,
6870
9793
  gateTail,
6871
9794
  hashConfirmToken,
9795
+ initialActionProposal,
6872
9796
  intersectAllowLists,
6873
9797
  invokeWithTransientRetry,
6874
9798
  isChatQueueStore,
@@ -6887,7 +9811,10 @@ var SqlTokenStreamSink = class {
6887
9811
  observeTurnFrames,
6888
9812
  offerMemories,
6889
9813
  offerSkills,
9814
+ parseTextActionProposalCommand,
6890
9815
  personaCatalogEntry,
9816
+ prepareActionProposal,
9817
+ ptBrActionProposalText,
6891
9818
  publishAgentDelegated,
6892
9819
  publishAgentMemoryResolved,
6893
9820
  publishAgentMemoryWritten,
@@ -6913,12 +9840,14 @@ var SqlTokenStreamSink = class {
6913
9840
  rememberToolDefinition,
6914
9841
  renderElicitationAnswers,
6915
9842
  repairInstruction,
9843
+ resolveActionProposalApproval,
6916
9844
  resolveElicitation,
6917
9845
  resolveGateLookback,
6918
9846
  resolveMemoryDigest,
6919
9847
  resolveOutputGateMode,
6920
9848
  resolvePersonaAlias,
6921
9849
  resolveSkillCatalog,
9850
+ resolveTextActionProposalDecision,
6922
9851
  resolveToolTransientRetryNumbers,
6923
9852
  rollupThreadUsage,
6924
9853
  runAgentLoop,
@@ -6935,18 +9864,32 @@ var SqlTokenStreamSink = class {
6935
9864
  skillInputSchema,
6936
9865
  skillToolDefinition,
6937
9866
  skillWriteVerdict,
9867
+ snapshotActionProposal,
6938
9868
  stampToolKinds,
6939
9869
  staticModelCatalog,
6940
9870
  staticSkillProvider,
6941
9871
  streamFailure,
6942
9872
  summarizeWithModel,
6943
9873
  tenantScope,
9874
+ textActionProposalReply,
9875
+ toActionProposalMutationView,
9876
+ toActionProposalView,
6944
9877
  toolCallApprovalFromRow,
6945
9878
  toolCallContext,
6946
9879
  traceLlmTurn,
6947
9880
  traceToolExecution,
9881
+ transitionActionProposalClaim,
9882
+ transitionActionProposalDecision,
9883
+ transitionActionProposalLease,
9884
+ transitionActionProposalSettlement,
9885
+ transitionActionProposalSupersession,
6948
9886
  truncateDetailContent,
6949
9887
  unwrapToolStepOutput,
9888
+ validateActionProposalCreation,
9889
+ validateActionProposalDiscoveryIndexBatch,
9890
+ validateActionProposalExpiryBatch,
9891
+ validateActionProposalListQuery,
9892
+ validateActionProposalWorkerClaim,
6950
9893
  validateElicitationAnswer,
6951
9894
  validateElicitationValue,
6952
9895
  validateStructured,
@@ -6959,6 +9902,7 @@ var SqlTokenStreamSink = class {
6959
9902
  withSkillTool,
6960
9903
  withToolTimeout,
6961
9904
  withTurnFrames,
9905
+ wrapToolPreflightDenied,
6962
9906
  wrapToolStepOutput,
6963
9907
  writeMemory
6964
9908
  });