@dudousxd/nestjs-agent-core 0.38.1 → 0.40.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.
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,
@@ -73,6 +75,7 @@ __export(src_exports, {
73
75
  DefaultRolesPolicy: () => DefaultRolesPolicy,
74
76
  ELICITATION_INPUT_TYPES: () => ELICITATION_INPUT_TYPES,
75
77
  GLOBAL_SCOPE: () => GLOBAL_SCOPE,
78
+ InMemoryActionProposalStore: () => InMemoryActionProposalStore,
76
79
  InMemoryAgentStore: () => InMemoryAgentStore,
77
80
  InMemoryConfirmTokenStore: () => InMemoryConfirmTokenStore,
78
81
  MAX_ASK_QUESTIONS: () => MAX_ASK_QUESTIONS,
@@ -96,15 +99,25 @@ __export(src_exports, {
96
99
  THREAD_DETAIL_CONTENT_CHARS: () => THREAD_DETAIL_CONTENT_CHARS,
97
100
  ToolDisabledError: () => ToolDisabledError,
98
101
  ToolForbiddenError: () => ToolForbiddenError,
102
+ ToolInputDriftError: () => ToolInputDriftError,
99
103
  ToolInputInvalidError: () => ToolInputInvalidError,
100
104
  ToolNotFoundError: () => ToolNotFoundError,
105
+ ToolPreflightDeniedError: () => ToolPreflightDeniedError,
101
106
  ToolRegistry: () => ToolRegistry,
102
107
  UNFINISHED_TOOL_CALL: () => UNFINISHED_TOOL_CALL,
108
+ actionProposalCreationMatches: () => actionProposalCreationMatches,
109
+ actionProposalOutcomeFenceValid: () => actionProposalOutcomeFenceValid,
110
+ actionProposalOutcomeScope: () => actionProposalOutcomeScope,
111
+ actionProposalOutcomeText: () => actionProposalOutcomeText,
112
+ actionProposalReceipt: () => actionProposalReceipt,
113
+ actionProposalScopeMatches: () => actionProposalScopeMatches,
103
114
  actorScope: () => actorScope,
104
115
  agentDiagnosticKey: () => agentDiagnosticKey,
105
116
  agentFailureCode: () => agentFailureCode,
106
117
  askInputSchema: () => askInputSchema,
107
118
  askToolDefinition: () => askToolDefinition,
119
+ assertIndependentActionCapabilities: () => assertIndependentActionCapabilities,
120
+ attachActionProposalOutcome: () => attachActionProposalOutcome,
108
121
  bucketByActor: () => bucketByActor,
109
122
  bucketByModel: () => bucketByModel,
110
123
  bucketByThread: () => bucketByThread,
@@ -112,12 +125,15 @@ __export(src_exports, {
112
125
  buildMemoryBlock: () => buildMemoryBlock,
113
126
  buildSkillsBlock: () => buildSkillsBlock,
114
127
  canActorUseTool: () => canActorUseTool,
128
+ canonicalActionProposalJson: () => canonicalActionProposalJson,
115
129
  canonicalJson: () => canonicalJson,
130
+ claimActionProposalOutcome: () => claimActionProposalOutcome,
116
131
  closeEmptyRoles: () => closeEmptyRoles,
117
132
  compositeSkillProvider: () => compositeSkillProvider,
118
133
  confirmTokenExpiry: () => confirmTokenExpiry,
119
134
  createFrameBuffer: () => createFrameBuffer,
120
135
  createIncrementalGate: () => createIncrementalGate,
136
+ createNegotiatedUiCollector: () => createNegotiatedUiCollector,
121
137
  createNoopEmitUi: () => createNoopEmitUi,
122
138
  createUiCollector: () => createUiCollector,
123
139
  danglingToolCallIds: () => danglingToolCallIds,
@@ -130,6 +146,7 @@ __export(src_exports, {
130
146
  detachedStarted: () => detachedStarted,
131
147
  detachedUnsettled: () => detachedUnsettled,
132
148
  encodeStreamEvent: () => encodeStreamEvent,
149
+ escapeUnsafeToolUiText: () => escapeUnsafeToolUiText,
133
150
  estimateCost: () => estimateCost,
134
151
  estimateMessageTokens: () => estimateMessageTokens,
135
152
  exhaustedWindow: () => exhaustedWindow,
@@ -140,9 +157,12 @@ __export(src_exports, {
140
157
  filterToolsByEnabled: () => filterToolsByEnabled,
141
158
  filterToolsByRole: () => filterToolsByRole,
142
159
  findCatalogModel: () => findCatalogModel,
160
+ findPersona: () => findPersona,
143
161
  gateFollowUps: () => gateFollowUps,
144
162
  gateTail: () => gateTail,
145
163
  hashConfirmToken: () => hashConfirmToken,
164
+ initialActionProposal: () => initialActionProposal,
165
+ intersectAllowLists: () => intersectAllowLists,
146
166
  invokeWithTransientRetry: () => invokeWithTransientRetry,
147
167
  isChatQueueStore: () => isChatQueueStore,
148
168
  isControlFlowSignal: () => isControlFlowSignal,
@@ -160,6 +180,9 @@ __export(src_exports, {
160
180
  observeTurnFrames: () => observeTurnFrames,
161
181
  offerMemories: () => offerMemories,
162
182
  offerSkills: () => offerSkills,
183
+ parseTextActionProposalCommand: () => parseTextActionProposalCommand,
184
+ personaCatalogEntry: () => personaCatalogEntry,
185
+ prepareActionProposal: () => prepareActionProposal,
163
186
  publishAgentDelegated: () => publishAgentDelegated,
164
187
  publishAgentMemoryResolved: () => publishAgentMemoryResolved,
165
188
  publishAgentMemoryWritten: () => publishAgentMemoryWritten,
@@ -185,11 +208,14 @@ __export(src_exports, {
185
208
  rememberToolDefinition: () => rememberToolDefinition,
186
209
  renderElicitationAnswers: () => renderElicitationAnswers,
187
210
  repairInstruction: () => repairInstruction,
211
+ resolveActionProposalApproval: () => resolveActionProposalApproval,
188
212
  resolveElicitation: () => resolveElicitation,
189
213
  resolveGateLookback: () => resolveGateLookback,
190
214
  resolveMemoryDigest: () => resolveMemoryDigest,
191
215
  resolveOutputGateMode: () => resolveOutputGateMode,
216
+ resolvePersonaAlias: () => resolvePersonaAlias,
192
217
  resolveSkillCatalog: () => resolveSkillCatalog,
218
+ resolveTextActionProposalDecision: () => resolveTextActionProposalDecision,
193
219
  resolveToolTransientRetryNumbers: () => resolveToolTransientRetryNumbers,
194
220
  rollupThreadUsage: () => rollupThreadUsage,
195
221
  runAgentLoop: () => runAgentLoop,
@@ -206,6 +232,7 @@ __export(src_exports, {
206
232
  skillInputSchema: () => skillInputSchema,
207
233
  skillToolDefinition: () => skillToolDefinition,
208
234
  skillWriteVerdict: () => skillWriteVerdict,
235
+ snapshotActionProposal: () => snapshotActionProposal,
209
236
  stampToolKinds: () => stampToolKinds,
210
237
  staticModelCatalog: () => staticModelCatalog,
211
238
  staticSkillProvider: () => staticSkillProvider,
@@ -216,8 +243,18 @@ __export(src_exports, {
216
243
  toolCallContext: () => toolCallContext,
217
244
  traceLlmTurn: () => traceLlmTurn,
218
245
  traceToolExecution: () => traceToolExecution,
246
+ transitionActionProposalClaim: () => transitionActionProposalClaim,
247
+ transitionActionProposalDecision: () => transitionActionProposalDecision,
248
+ transitionActionProposalLease: () => transitionActionProposalLease,
249
+ transitionActionProposalSettlement: () => transitionActionProposalSettlement,
250
+ transitionActionProposalSupersession: () => transitionActionProposalSupersession,
219
251
  truncateDetailContent: () => truncateDetailContent,
220
252
  unwrapToolStepOutput: () => unwrapToolStepOutput,
253
+ validateActionProposalCreation: () => validateActionProposalCreation,
254
+ validateActionProposalDiscoveryIndexBatch: () => validateActionProposalDiscoveryIndexBatch,
255
+ validateActionProposalExpiryBatch: () => validateActionProposalExpiryBatch,
256
+ validateActionProposalListQuery: () => validateActionProposalListQuery,
257
+ validateActionProposalWorkerClaim: () => validateActionProposalWorkerClaim,
221
258
  validateElicitationAnswer: () => validateElicitationAnswer,
222
259
  validateElicitationValue: () => validateElicitationValue,
223
260
  validateStructured: () => validateStructured,
@@ -230,6 +267,7 @@ __export(src_exports, {
230
267
  withSkillTool: () => withSkillTool,
231
268
  withToolTimeout: () => withToolTimeout,
232
269
  withTurnFrames: () => withTurnFrames,
270
+ wrapToolPreflightDenied: () => wrapToolPreflightDenied,
233
271
  wrapToolStepOutput: () => wrapToolStepOutput,
234
272
  writeMemory: () => writeMemory
235
273
  });
@@ -403,16 +441,16 @@ function observeTurnFrames(writer, now = Date.now) {
403
441
  };
404
442
  }
405
443
  __name(observeTurnFrames, "observeTurnFrames");
406
- function withTurnFrames(result, frames) {
444
+ function withTurnFrames(result2, frames) {
407
445
  return {
408
- ...result,
409
- ...result.reasoning === void 0 && frames.reasoning !== void 0 ? {
446
+ ...result2,
447
+ ...result2.reasoning === void 0 && frames.reasoning !== void 0 ? {
410
448
  reasoning: frames.reasoning
411
449
  } : {},
412
- ...result.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
450
+ ...result2.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
413
451
  reasoningMs: frames.reasoningMs
414
452
  } : {},
415
- ...result.ui === void 0 && frames.ui !== void 0 ? {
453
+ ...result2.ui === void 0 && frames.ui !== void 0 ? {
416
454
  ui: frames.ui
417
455
  } : {}
418
456
  };
@@ -421,8 +459,9 @@ __name(withTurnFrames, "withTurnFrames");
421
459
 
422
460
  // src/tool-ui.ts
423
461
  var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
424
- function wrapToolStepOutput(output, ui) {
425
- if (ui.length === 0) {
462
+ function wrapToolStepOutput(output, ui, text) {
463
+ const reserved = typeof output === "object" && output !== null && TOOL_STEP_UI in output;
464
+ if (ui.length === 0 && !reserved && !text) {
426
465
  return output;
427
466
  }
428
467
  const wrapped = {
@@ -430,17 +469,36 @@ function wrapToolStepOutput(output, ui) {
430
469
  output,
431
470
  ui: [
432
471
  ...ui
433
- ]
472
+ ],
473
+ ...text ? {
474
+ text
475
+ } : {}
434
476
  };
435
477
  return wrapped;
436
478
  }
437
479
  __name(wrapToolStepOutput, "wrapToolStepOutput");
480
+ function wrapToolPreflightDenied(reason) {
481
+ const wrapped = {
482
+ [TOOL_STEP_UI]: 1,
483
+ output: null,
484
+ ui: [],
485
+ preflightDenied: reason
486
+ };
487
+ return wrapped;
488
+ }
489
+ __name(wrapToolPreflightDenied, "wrapToolPreflightDenied");
438
490
  function unwrapToolStepOutput(raw) {
439
491
  if (typeof raw === "object" && raw !== null && raw[TOOL_STEP_UI] === 1) {
440
492
  const wrapped = raw;
441
493
  return {
442
494
  output: wrapped.output,
443
- ui: Array.isArray(wrapped.ui) ? wrapped.ui : []
495
+ ui: Array.isArray(wrapped.ui) ? wrapped.ui : [],
496
+ ...typeof wrapped.text === "string" ? {
497
+ text: wrapped.text
498
+ } : {},
499
+ ...typeof wrapped.preflightDenied === "string" ? {
500
+ preflightDenied: wrapped.preflightDenied
501
+ } : {}
444
502
  };
445
503
  }
446
504
  return {
@@ -489,6 +547,14 @@ function createUiCollector(toolCallId, write) {
489
547
  ...options.version !== void 0 ? {
490
548
  version: options.version
491
549
  } : {},
550
+ ...options.fallbackText !== void 0 ? {
551
+ fallbackText: options.fallbackText
552
+ } : {},
553
+ ...options.componentVersions !== void 0 ? {
554
+ componentVersions: {
555
+ ...options.componentVersions
556
+ }
557
+ } : {},
492
558
  toolCallId
493
559
  };
494
560
  pushed.set(id, entry);
@@ -523,6 +589,10 @@ function mergeUi(...lists) {
523
589
  ];
524
590
  }
525
591
  __name(mergeUi, "mergeUi");
592
+ function escapeUnsafeToolUiText(text) {
593
+ return text.replaceAll("\0", "\\u0000").replace(/[\uD800-\uDFFF]/gu, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
594
+ }
595
+ __name(escapeUnsafeToolUiText, "escapeUnsafeToolUiText");
526
596
 
527
597
  // src/tool-presentation.ts
528
598
  var ALL_AGENTS = "*";
@@ -565,6 +635,9 @@ function queuedMessageView(message) {
565
635
  ...message.agentName !== void 0 ? {
566
636
  agentName: message.agentName
567
637
  } : {},
638
+ ...message.persona !== void 0 ? {
639
+ persona: message.persona
640
+ } : {},
568
641
  ...message.model !== void 0 ? {
569
642
  model: message.model
570
643
  } : {},
@@ -731,12 +804,18 @@ function toolCallApprovalFromRow(row) {
731
804
  if (row.approver === null || row.approver === void 0) {
732
805
  return null;
733
806
  }
734
- const status = row.status === "pending_approval" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
807
+ const status = row.status === "pending_approval" || row.status === "proposed" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
735
808
  const expiresAt = row.expiresAt instanceof Date ? row.expiresAt.toISOString() : typeof row.expiresAt === "string" ? row.expiresAt : void 0;
736
809
  const decided = status === "approved" || status === "rejected";
737
810
  return {
738
811
  toolCallId: row.toolCallId,
812
+ ...row.proposalId != null ? {
813
+ proposalId: row.proposalId
814
+ } : {},
739
815
  approver: row.approver,
816
+ ...row.confirmation != null ? {
817
+ confirmation: row.confirmation
818
+ } : {},
740
819
  status,
741
820
  ...expiresAt !== void 0 ? {
742
821
  expiresAt
@@ -787,9 +866,9 @@ function bucketByModel(rows, prices) {
787
866
  bucket.costUsd += rowCost(row, prices);
788
867
  byModel.set(row.modelId, bucket);
789
868
  }
790
- const result = [];
869
+ const result2 = [];
791
870
  for (const [modelId, bucket] of byModel) {
792
- result.push({
871
+ result2.push({
793
872
  modelId,
794
873
  requests: bucket.requests,
795
874
  inputTokens: bucket.inputTokens,
@@ -797,8 +876,8 @@ function bucketByModel(rows, prices) {
797
876
  costUsd: bucket.costUsd
798
877
  });
799
878
  }
800
- result.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
801
- return result;
879
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
880
+ return result2;
802
881
  }
803
882
  __name(bucketByModel, "bucketByModel");
804
883
  function bucketByActor(rows, prices) {
@@ -816,9 +895,9 @@ function bucketByActor(rows, prices) {
816
895
  bucket.threadIds.add(row.threadId);
817
896
  byActor.set(row.actorRef, bucket);
818
897
  }
819
- const result = [];
898
+ const result2 = [];
820
899
  for (const [actorRef, bucket] of byActor) {
821
- result.push({
900
+ result2.push({
822
901
  actorRef,
823
902
  requests: bucket.requests,
824
903
  totalTokens: bucket.totalTokens,
@@ -826,8 +905,8 @@ function bucketByActor(rows, prices) {
826
905
  threadCount: bucket.threadIds.size
827
906
  });
828
907
  }
829
- result.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
830
- return result;
908
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
909
+ return result2;
831
910
  }
832
911
  __name(bucketByActor, "bucketByActor");
833
912
  function bucketByThread(rows, prices, threads, options) {
@@ -843,13 +922,13 @@ function bucketByThread(rows, prices, threads, options) {
843
922
  bucket.costUsd += rowCost(row, prices);
844
923
  byThread.set(row.threadId, bucket);
845
924
  }
846
- const result = [];
925
+ const result2 = [];
847
926
  for (const [threadId, bucket] of byThread) {
848
927
  const thread = threads.get(threadId);
849
928
  if (thread === void 0 && !options.includeUnknownThreads) {
850
929
  continue;
851
930
  }
852
- result.push({
931
+ result2.push({
853
932
  threadId,
854
933
  title: thread?.title ?? "",
855
934
  actorRef: thread?.actorRef ?? "",
@@ -858,8 +937,8 @@ function bucketByThread(rows, prices, threads, options) {
858
937
  costUsd: bucket.costUsd
859
938
  });
860
939
  }
861
- result.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
862
- return result.slice(0, options.limit);
940
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
941
+ return result2.slice(0, options.limit);
863
942
  }
864
943
  __name(bucketByThread, "bucketByThread");
865
944
  function rollupThreadUsage(rows, prices) {
@@ -891,16 +970,16 @@ function bucketUsageTrend(rows, prices) {
891
970
  bucket.costUsd += rowCost(row, prices);
892
971
  byDay.set(row.day, bucket);
893
972
  }
894
- const result = [];
973
+ const result2 = [];
895
974
  for (const [day, bucket] of byDay) {
896
- result.push({
975
+ result2.push({
897
976
  day,
898
977
  totalTokens: bucket.totalTokens,
899
978
  costUsd: bucket.costUsd
900
979
  });
901
980
  }
902
- result.sort((left, right) => left.day.localeCompare(right.day));
903
- return result;
981
+ result2.sort((left, right) => left.day.localeCompare(right.day));
982
+ return result2;
904
983
  }
905
984
  __name(bucketUsageTrend, "bucketUsageTrend");
906
985
  function dayBoundsUtc(range) {
@@ -957,6 +1036,54 @@ function filterToolsByAllowList(tools, allowedTools) {
957
1036
  }
958
1037
  __name(filterToolsByAllowList, "filterToolsByAllowList");
959
1038
 
1039
+ // src/personas.ts
1040
+ function intersectAllowLists(first, second) {
1041
+ if (first === void 0) {
1042
+ return second === void 0 ? void 0 : [
1043
+ ...second
1044
+ ];
1045
+ }
1046
+ if (second === void 0) {
1047
+ return [
1048
+ ...first
1049
+ ];
1050
+ }
1051
+ const allowed = new Set(second);
1052
+ return first.filter((name) => allowed.has(name));
1053
+ }
1054
+ __name(intersectAllowLists, "intersectAllowLists");
1055
+ function findPersona(definition, id) {
1056
+ if (id === void 0) {
1057
+ return void 0;
1058
+ }
1059
+ return definition?.personas?.find((persona) => persona.id === id);
1060
+ }
1061
+ __name(findPersona, "findPersona");
1062
+ function personaCatalogEntry(persona) {
1063
+ return {
1064
+ id: persona.id,
1065
+ label: persona.label,
1066
+ ...persona.description !== void 0 ? {
1067
+ description: persona.description
1068
+ } : {}
1069
+ };
1070
+ }
1071
+ __name(personaCatalogEntry, "personaCatalogEntry");
1072
+ function resolvePersonaAlias(definitions, name) {
1073
+ for (const definition of definitions) {
1074
+ for (const persona of definition.personas ?? []) {
1075
+ if (persona.aliases?.includes(name) === true) {
1076
+ return {
1077
+ agent: definition.name,
1078
+ persona: persona.id
1079
+ };
1080
+ }
1081
+ }
1082
+ }
1083
+ return void 0;
1084
+ }
1085
+ __name(resolvePersonaAlias, "resolvePersonaAlias");
1086
+
960
1087
  // src/history.ts
961
1088
  function estimateMessageTokens(message) {
962
1089
  const extras = (message.toolCalls !== void 0 ? JSON.stringify(message.toolCalls).length : 0) + (message.toolResults !== void 0 ? JSON.stringify(message.toolResults).length : 0);
@@ -1296,13 +1423,13 @@ async function validateStructured(schema, text, reported) {
1296
1423
  issues: []
1297
1424
  };
1298
1425
  }
1299
- const result = await schema["~standard"].validate(candidate);
1300
- return result.issues !== void 0 ? {
1426
+ const result2 = await schema["~standard"].validate(candidate);
1427
+ return result2.issues !== void 0 ? {
1301
1428
  ok: false,
1302
- issues: result.issues
1429
+ issues: result2.issues
1303
1430
  } : {
1304
1431
  ok: true,
1305
- value: result.value
1432
+ value: result2.value
1306
1433
  };
1307
1434
  }
1308
1435
  __name(validateStructured, "validateStructured");
@@ -2481,12 +2608,12 @@ function memoryWriteVerdict(request) {
2481
2608
  };
2482
2609
  }
2483
2610
  __name(memoryWriteVerdict, "memoryWriteVerdict");
2484
- function memoryForgetVerdict({ record, actor }) {
2485
- return record.scope === actorScope(actor) ? {
2611
+ function memoryForgetVerdict({ record: record2, actor }) {
2612
+ return record2.scope === actorScope(actor) ? {
2486
2613
  allowed: true
2487
2614
  } : {
2488
2615
  allowed: false,
2489
- reason: `"${record.scope}" is not this actor's own scope; deleting it is an administrative action`
2616
+ reason: `"${record2.scope}" is not this actor's own scope; deleting it is an administrative action`
2490
2617
  };
2491
2618
  }
2492
2619
  __name(memoryForgetVerdict, "memoryForgetVerdict");
@@ -2529,7 +2656,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2529
2656
  error: verdict.reason
2530
2657
  };
2531
2658
  }
2532
- const record = await write({
2659
+ const record2 = await write({
2533
2660
  key,
2534
2661
  text,
2535
2662
  scope,
@@ -2543,7 +2670,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2543
2670
  });
2544
2671
  return {
2545
2672
  ok: true,
2546
- record
2673
+ record: record2
2547
2674
  };
2548
2675
  }
2549
2676
  __name(writeMemory, "writeMemory");
@@ -2595,7 +2722,7 @@ function danglingToolCallIds(messages) {
2595
2722
  if (message.role !== "assistant" || calls.length === 0) {
2596
2723
  continue;
2597
2724
  }
2598
- const settled = new Set((message.toolResults ?? []).map((result) => result.id));
2725
+ const settled = new Set((message.toolResults ?? []).map((result2) => result2.id));
2599
2726
  for (const call of calls) {
2600
2727
  if (!settled.has(call.id)) {
2601
2728
  ids.push(call.id);
@@ -2663,7 +2790,7 @@ function settleDanglingToolCalls(messages, outcomes = []) {
2663
2790
  return message;
2664
2791
  }
2665
2792
  const results = message.toolResults ?? [];
2666
- const settled = new Set(results.map((result) => result.id));
2793
+ const settled = new Set(results.map((result2) => result2.id));
2667
2794
  const missing = calls.filter((call) => !settled.has(call.id));
2668
2795
  if (missing.length === 0) {
2669
2796
  return message;
@@ -2771,6 +2898,1111 @@ async function settleUnsettledDelegation(args) {
2771
2898
  }
2772
2899
  __name(settleUnsettledDelegation, "settleUnsettledDelegation");
2773
2900
 
2901
+ // src/action-proposal-discovery.ts
2902
+ function validateActionProposalWorkerClaim(command, now) {
2903
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
2904
+ if (typeof command.workerId !== "string" || command.workerId.length === 0) throw new TypeError("workerId must be a nonempty string");
2905
+ if (!Number.isSafeInteger(command.leaseMs) || command.leaseMs <= 0 || !Number.isSafeInteger(now + command.leaseMs)) throw new RangeError("leaseMs must be positive safe integer milliseconds");
2906
+ }
2907
+ __name(validateActionProposalWorkerClaim, "validateActionProposalWorkerClaim");
2908
+ function validateActionProposalExpiryBatch(command, now) {
2909
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
2910
+ validateActionProposalDiscoveryIndexBatch(command);
2911
+ }
2912
+ __name(validateActionProposalExpiryBatch, "validateActionProposalExpiryBatch");
2913
+ function validateActionProposalDiscoveryIndexBatch(command) {
2914
+ if (!Number.isInteger(command.limit) || command.limit < 1 || command.limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
2915
+ }
2916
+ __name(validateActionProposalDiscoveryIndexBatch, "validateActionProposalDiscoveryIndexBatch");
2917
+
2918
+ // src/action-proposal-outcome.ts
2919
+ function attachActionProposalOutcome(row, now, ui = [], text) {
2920
+ if (row.outcome) return row;
2921
+ const execution = row.execution;
2922
+ const outcome = {
2923
+ id: `${row.id}:outcome:1`,
2924
+ proposalId: row.id,
2925
+ outcomeVersion: 1,
2926
+ tenantRef: row.tenantRef,
2927
+ actorRef: row.actorRef,
2928
+ threadId: row.threadId,
2929
+ originRunId: row.originRunId,
2930
+ originToolCallId: row.originToolCallId,
2931
+ toolName: row.toolName,
2932
+ decision: row.decision,
2933
+ ui,
2934
+ createdAt: now,
2935
+ ...text !== void 0 ? {
2936
+ text
2937
+ } : {},
2938
+ ...execution?.status === "succeeded" || execution?.status === "failed" ? {
2939
+ executionStatus: execution.status
2940
+ } : {},
2941
+ ...execution && Object.hasOwn(execution, "result") ? {
2942
+ result: execution.result
2943
+ } : {},
2944
+ ...execution?.error !== void 0 ? {
2945
+ error: execution.error
2946
+ } : row.decisionAudit?.reason !== void 0 ? {
2947
+ error: row.decisionAudit.reason
2948
+ } : {}
2949
+ };
2950
+ return snapshotActionProposal({
2951
+ ...row,
2952
+ outcome,
2953
+ outcomeDelivery: {
2954
+ status: "pending",
2955
+ generation: 0,
2956
+ lease: null
2957
+ }
2958
+ });
2959
+ }
2960
+ __name(attachActionProposalOutcome, "attachActionProposalOutcome");
2961
+ function claimActionProposalOutcome(row, command, now, token) {
2962
+ validateActionProposalWorkerClaim(command, now);
2963
+ const delivery = row.outcomeDelivery;
2964
+ if (!row.outcome || !delivery || delivery.status !== "pending" || delivery.lease && now < delivery.lease.expiresAt) return null;
2965
+ const generation = delivery.generation + 1;
2966
+ return snapshotActionProposal({
2967
+ ...row,
2968
+ outcomeDelivery: {
2969
+ ...delivery,
2970
+ generation,
2971
+ lease: {
2972
+ token,
2973
+ generation,
2974
+ workerId: command.workerId,
2975
+ expiresAt: now + command.leaseMs
2976
+ }
2977
+ }
2978
+ });
2979
+ }
2980
+ __name(claimActionProposalOutcome, "claimActionProposalOutcome");
2981
+ function actionProposalOutcomeFenceValid(row, command, now) {
2982
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid server clock");
2983
+ 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");
2984
+ const delivery = row.outcomeDelivery;
2985
+ return row.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;
2986
+ }
2987
+ __name(actionProposalOutcomeFenceValid, "actionProposalOutcomeFenceValid");
2988
+ function outcomeSummary(outcome) {
2989
+ 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)}` : ""}`;
2990
+ if (outcome.executionStatus === "failed") return `Action ${JSON.stringify(outcome.toolName)} failed (proposal ${JSON.stringify(outcome.proposalId)}): ${JSON.stringify(outcome.error ?? "Execution failed")}`;
2991
+ 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)}` : ""}`;
2992
+ }
2993
+ __name(outcomeSummary, "outcomeSummary");
2994
+ function actionProposalOutcomeScope(outcome) {
2995
+ return {
2996
+ tenantRef: outcome.tenantRef,
2997
+ actorRef: outcome.actorRef,
2998
+ threadId: outcome.threadId
2999
+ };
3000
+ }
3001
+ __name(actionProposalOutcomeScope, "actionProposalOutcomeScope");
3002
+ function actionProposalOutcomeText(outcome) {
3003
+ return outcomeSummary(outcome) + (outcome.text ? ` UI: ${JSON.stringify(outcome.text)}` : "");
3004
+ }
3005
+ __name(actionProposalOutcomeText, "actionProposalOutcomeText");
3006
+
3007
+ // src/genui/schema.ts
3008
+ function isStandardSchema(schema) {
3009
+ return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"]?.validate === "function";
3010
+ }
3011
+ __name(isStandardSchema, "isStandardSchema");
3012
+ function toJsonSchema(schema) {
3013
+ if (!isStandardSchema(schema)) {
3014
+ return schema;
3015
+ }
3016
+ const standard = schema["~standard"];
3017
+ const converter = standard.jsonSchema;
3018
+ if (converter === void 0 || typeof converter.input !== "function") {
3019
+ return void 0;
3020
+ }
3021
+ try {
3022
+ return converter.input({
3023
+ target: "draft-07"
3024
+ });
3025
+ } catch {
3026
+ return void 0;
3027
+ }
3028
+ }
3029
+ __name(toJsonSchema, "toJsonSchema");
3030
+ async function validateProps(schema, value, validator) {
3031
+ if (isStandardSchema(schema)) {
3032
+ const result2 = await schema["~standard"].validate(value);
3033
+ if (result2.issues !== void 0) {
3034
+ return {
3035
+ ok: false,
3036
+ issues: toGenuiIssues(result2.issues)
3037
+ };
3038
+ }
3039
+ return {
3040
+ ok: true,
3041
+ value: result2.value
3042
+ };
3043
+ }
3044
+ const issues = validator.validate(schema, value);
3045
+ return issues.length > 0 ? {
3046
+ ok: false,
3047
+ issues
3048
+ } : {
3049
+ ok: true,
3050
+ value
3051
+ };
3052
+ }
3053
+ __name(validateProps, "validateProps");
3054
+ function validatePropsSync(schema, value, validator) {
3055
+ if (!isStandardSchema(schema)) {
3056
+ const issues = validator.validate(schema, value);
3057
+ return issues.length > 0 ? {
3058
+ ok: false,
3059
+ issues
3060
+ } : {
3061
+ ok: true,
3062
+ value
3063
+ };
3064
+ }
3065
+ const result2 = schema["~standard"].validate(value);
3066
+ if (result2 instanceof Promise) {
3067
+ result2.catch(() => void 0);
3068
+ return void 0;
3069
+ }
3070
+ return result2.issues !== void 0 ? {
3071
+ ok: false,
3072
+ issues: toGenuiIssues(result2.issues)
3073
+ } : {
3074
+ ok: true,
3075
+ value: result2.value
3076
+ };
3077
+ }
3078
+ __name(validatePropsSync, "validatePropsSync");
3079
+ function toGenuiIssues(issues) {
3080
+ return issues.map((issue4) => ({
3081
+ path: (issue4.path ?? []).map((segment) => typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment),
3082
+ message: issue4.message
3083
+ }));
3084
+ }
3085
+ __name(toGenuiIssues, "toGenuiIssues");
3086
+ function formatIssues(issues) {
3087
+ return issues.map((issue4) => `${issue4.path.length > 0 ? formatPath(issue4.path) : "(root)"}: ${issue4.message}`).join("; ");
3088
+ }
3089
+ __name(formatIssues, "formatIssues");
3090
+ function formatPath(path) {
3091
+ return path.map((segment, index) => typeof segment === "number" ? `[${segment}]` : index === 0 ? segment : `.${segment}`).join("");
3092
+ }
3093
+ __name(formatPath, "formatPath");
3094
+ var builtinJsonSchemaValidator = {
3095
+ validate(schema, value) {
3096
+ const issues = [];
3097
+ check(schema, value, [], schema, issues, 0);
3098
+ return issues;
3099
+ }
3100
+ };
3101
+ var MAX_DEPTH = 64;
3102
+ function typeOf(value) {
3103
+ if (value === null) return "null";
3104
+ if (Array.isArray(value)) return "array";
3105
+ return typeof value;
3106
+ }
3107
+ __name(typeOf, "typeOf");
3108
+ function matchesType(type, value) {
3109
+ switch (type) {
3110
+ case "integer":
3111
+ return typeof value === "number" && Number.isInteger(value);
3112
+ case "number":
3113
+ return typeof value === "number" && Number.isFinite(value);
3114
+ case "object":
3115
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3116
+ default:
3117
+ return typeOf(value) === type;
3118
+ }
3119
+ }
3120
+ __name(matchesType, "matchesType");
3121
+ function resolveRef(root, ref) {
3122
+ if (ref === "#") return root;
3123
+ if (!ref.startsWith("#/")) return void 0;
3124
+ let current = root;
3125
+ for (const raw of ref.slice(2).split("/")) {
3126
+ const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
3127
+ if (typeof current !== "object" || current === null) return void 0;
3128
+ current = current[key];
3129
+ }
3130
+ return typeof current === "object" && current !== null ? current : void 0;
3131
+ }
3132
+ __name(resolveRef, "resolveRef");
3133
+ function deepEqual(a, b) {
3134
+ if (a === b) return true;
3135
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
3136
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
3137
+ const keysA = Object.keys(a);
3138
+ const keysB = Object.keys(b);
3139
+ if (keysA.length !== keysB.length) return false;
3140
+ return keysA.every((key) => deepEqual(a[key], b[key]));
3141
+ }
3142
+ __name(deepEqual, "deepEqual");
3143
+ function check(schema, value, path, root, issues, depth) {
3144
+ if (schema === true || schema === void 0) return;
3145
+ if (schema === false) {
3146
+ issues.push({
3147
+ path,
3148
+ message: "is not allowed"
3149
+ });
3150
+ return;
3151
+ }
3152
+ if (typeof schema !== "object" || schema === null) return;
3153
+ if (depth > MAX_DEPTH) {
3154
+ issues.push({
3155
+ path,
3156
+ message: "is nested too deeply"
3157
+ });
3158
+ return;
3159
+ }
3160
+ const s = schema;
3161
+ if (typeof s.$ref === "string") {
3162
+ const target = resolveRef(root, s.$ref);
3163
+ if (target === void 0) {
3164
+ issues.push({
3165
+ path,
3166
+ message: `references unknown schema ${s.$ref}`
3167
+ });
3168
+ return;
3169
+ }
3170
+ check(target, value, path, root, issues, depth + 1);
3171
+ }
3172
+ if (s.type !== void 0) {
3173
+ const types = Array.isArray(s.type) ? s.type : [
3174
+ s.type
3175
+ ];
3176
+ if (!types.some((type) => matchesType(type, value))) {
3177
+ issues.push({
3178
+ path,
3179
+ message: `must be ${types.join(" or ")}`
3180
+ });
3181
+ return;
3182
+ }
3183
+ }
3184
+ if (Array.isArray(s.enum) && !s.enum.some((option) => deepEqual(option, value))) {
3185
+ issues.push({
3186
+ path,
3187
+ message: `must be one of ${s.enum.map((option) => JSON.stringify(option)).join(", ")}`
3188
+ });
3189
+ }
3190
+ if ("const" in s && !deepEqual(s.const, value)) {
3191
+ issues.push({
3192
+ path,
3193
+ message: `must be ${JSON.stringify(s.const)}`
3194
+ });
3195
+ }
3196
+ if (typeof value === "string") {
3197
+ if (typeof s.minLength === "number" && value.length < s.minLength) {
3198
+ issues.push({
3199
+ path,
3200
+ message: `must be at least ${s.minLength} characters`
3201
+ });
3202
+ }
3203
+ if (typeof s.maxLength === "number" && value.length > s.maxLength) {
3204
+ issues.push({
3205
+ path,
3206
+ message: `must be at most ${s.maxLength} characters`
3207
+ });
3208
+ }
3209
+ if (typeof s.pattern === "string") {
3210
+ let pattern;
3211
+ try {
3212
+ pattern = new RegExp(s.pattern, "u");
3213
+ } catch {
3214
+ pattern = void 0;
3215
+ }
3216
+ if (pattern !== void 0 && !pattern.test(value)) {
3217
+ issues.push({
3218
+ path,
3219
+ message: `must match ${s.pattern}`
3220
+ });
3221
+ }
3222
+ }
3223
+ }
3224
+ if (typeof value === "number") {
3225
+ if (typeof s.minimum === "number" && value < s.minimum) {
3226
+ issues.push({
3227
+ path,
3228
+ message: `must be >= ${s.minimum}`
3229
+ });
3230
+ }
3231
+ if (typeof s.maximum === "number" && value > s.maximum) {
3232
+ issues.push({
3233
+ path,
3234
+ message: `must be <= ${s.maximum}`
3235
+ });
3236
+ }
3237
+ }
3238
+ if (Array.isArray(value)) {
3239
+ if (typeof s.minItems === "number" && value.length < s.minItems) {
3240
+ issues.push({
3241
+ path,
3242
+ message: `must have at least ${s.minItems} items`
3243
+ });
3244
+ }
3245
+ if (typeof s.maxItems === "number" && value.length > s.maxItems) {
3246
+ issues.push({
3247
+ path,
3248
+ message: `must have at most ${s.maxItems} items`
3249
+ });
3250
+ }
3251
+ if (s.items !== void 0 && !Array.isArray(s.items)) {
3252
+ value.forEach((item, index) => {
3253
+ check(s.items, item, [
3254
+ ...path,
3255
+ index
3256
+ ], root, issues, depth + 1);
3257
+ });
3258
+ }
3259
+ }
3260
+ if (matchesType("object", value)) {
3261
+ const record2 = value;
3262
+ const properties = typeof s.properties === "object" && s.properties !== null ? s.properties : {};
3263
+ if (Array.isArray(s.required)) {
3264
+ for (const key of s.required) {
3265
+ if (record2[key] === void 0) {
3266
+ issues.push({
3267
+ path: [
3268
+ ...path,
3269
+ key
3270
+ ],
3271
+ message: "is required"
3272
+ });
3273
+ }
3274
+ }
3275
+ }
3276
+ for (const [key, entry] of Object.entries(record2)) {
3277
+ if (key in properties) {
3278
+ if (entry !== void 0) {
3279
+ check(properties[key], entry, [
3280
+ ...path,
3281
+ key
3282
+ ], root, issues, depth + 1);
3283
+ }
3284
+ } else if (s.additionalProperties === false) {
3285
+ issues.push({
3286
+ path: [
3287
+ ...path,
3288
+ key
3289
+ ],
3290
+ message: "is not a known property"
3291
+ });
3292
+ } else if (typeof s.additionalProperties === "object" && s.additionalProperties !== null) {
3293
+ check(s.additionalProperties, entry, [
3294
+ ...path,
3295
+ key
3296
+ ], root, issues, depth + 1);
3297
+ }
3298
+ }
3299
+ }
3300
+ if (Array.isArray(s.allOf)) {
3301
+ for (const sub of s.allOf) check(sub, value, path, root, issues, depth + 1);
3302
+ }
3303
+ if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
3304
+ const options = Array.isArray(s.anyOf) ? s.anyOf : s.oneOf;
3305
+ const passing = options.filter((sub) => {
3306
+ const nested = [];
3307
+ check(sub, value, path, root, nested, depth + 1);
3308
+ return nested.length === 0;
3309
+ }).length;
3310
+ if (Array.isArray(s.anyOf) ? passing === 0 : passing !== 1) {
3311
+ issues.push({
3312
+ path,
3313
+ message: Array.isArray(s.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
3314
+ });
3315
+ }
3316
+ }
3317
+ if (s.not !== void 0) {
3318
+ const nested = [];
3319
+ check(s.not, value, path, root, nested, depth + 1);
3320
+ if (nested.length === 0) {
3321
+ issues.push({
3322
+ path,
3323
+ message: "matches a shape that is not allowed"
3324
+ });
3325
+ }
3326
+ }
3327
+ }
3328
+ __name(check, "check");
3329
+
3330
+ // src/genui/catalog.ts
3331
+ var COMPONENT_NAME = /^[A-Za-z][A-Za-z0-9]{0,63}$/;
3332
+ function defineComponent(definition) {
3333
+ if (!COMPONENT_NAME.test(definition.name)) {
3334
+ throw new Error(`genui: component name "${definition.name}" must be letters and digits, starting with a letter (e.g. DataTable)`);
3335
+ }
3336
+ if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
3337
+ if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
3338
+ return definition;
3339
+ }
3340
+ __name(defineComponent, "defineComponent");
3341
+ function defineCatalog(components, options = {}) {
3342
+ const byName = /* @__PURE__ */ new Map();
3343
+ for (const component of components) {
3344
+ defineComponent(component);
3345
+ if (byName.has(component.name)) {
3346
+ throw new Error(`genui: component "${component.name}" is defined twice in one catalog`);
3347
+ }
3348
+ byName.set(component.name, component);
3349
+ }
3350
+ return buildCatalog(byName, options.jsonSchemaValidator ?? builtinJsonSchemaValidator);
3351
+ }
3352
+ __name(defineCatalog, "defineCatalog");
3353
+ function buildCatalog(byName, validator) {
3354
+ const list = Object.freeze([
3355
+ ...byName.values()
3356
+ ]);
3357
+ return {
3358
+ components: list,
3359
+ validator,
3360
+ get: /* @__PURE__ */ __name((name) => byName.get(name), "get"),
3361
+ has: /* @__PURE__ */ __name((name) => byName.has(name), "has"),
3362
+ modelComponents: /* @__PURE__ */ __name(() => list.filter((component) => component.internal !== true), "modelComponents"),
3363
+ async validate(component, props) {
3364
+ const definition = byName.get(component);
3365
+ if (definition === void 0) {
3366
+ return {
3367
+ ok: false,
3368
+ issues: [
3369
+ {
3370
+ path: [],
3371
+ message: `unknown component "${component}"`
3372
+ }
3373
+ ]
3374
+ };
3375
+ }
3376
+ const result2 = await validateProps(definition.props, props, validator);
3377
+ return result2;
3378
+ },
3379
+ validateSync(component, props) {
3380
+ const definition = byName.get(component);
3381
+ if (definition === void 0) {
3382
+ return {
3383
+ ok: false,
3384
+ issues: [
3385
+ {
3386
+ path: [],
3387
+ message: `unknown component "${component}"`
3388
+ }
3389
+ ]
3390
+ };
3391
+ }
3392
+ return validatePropsSync(definition.props, props, validator);
3393
+ },
3394
+ jsonSchemaFor(component) {
3395
+ const definition = byName.get(component);
3396
+ return definition === void 0 ? void 0 : toJsonSchema(definition.props);
3397
+ },
3398
+ extend(more) {
3399
+ const next = new Map(byName);
3400
+ for (const component of more) {
3401
+ defineComponent(component);
3402
+ next.set(component.name, component);
3403
+ }
3404
+ return buildCatalog(next, validator);
3405
+ }
3406
+ };
3407
+ }
3408
+ __name(buildCatalog, "buildCatalog");
3409
+
3410
+ // src/genui/tree.ts
3411
+ var GENUI_TREE_COMPONENT = "genui:tree";
3412
+ async function validateTree(catalog, tree, limits = {}) {
3413
+ const maxDepth = limits.maxDepth ?? 12;
3414
+ const maxNodes = limits.maxNodes ?? 200;
3415
+ const issues = [];
3416
+ let nodes = 0;
3417
+ const visit = /* @__PURE__ */ __name(async (node, path, depth) => {
3418
+ nodes += 1;
3419
+ if (nodes > maxNodes) {
3420
+ if (nodes === maxNodes + 1) {
3421
+ issues.push({
3422
+ path,
3423
+ message: `the tree has more than ${maxNodes} elements`
3424
+ });
3425
+ }
3426
+ return void 0;
3427
+ }
3428
+ if (depth > maxDepth) {
3429
+ issues.push({
3430
+ path,
3431
+ message: `the tree is nested deeper than ${maxDepth} levels`
3432
+ });
3433
+ return void 0;
3434
+ }
3435
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
3436
+ issues.push({
3437
+ path,
3438
+ message: "must be an element object { type, props, children? }"
3439
+ });
3440
+ return void 0;
3441
+ }
3442
+ const element = node;
3443
+ if (typeof element.type !== "string") {
3444
+ issues.push({
3445
+ path: [
3446
+ ...path,
3447
+ "type"
3448
+ ],
3449
+ message: "must be a component name"
3450
+ });
3451
+ return void 0;
3452
+ }
3453
+ const definition = catalog.get(element.type);
3454
+ if (definition === void 0 || definition.internal === true) {
3455
+ issues.push({
3456
+ path: [
3457
+ ...path,
3458
+ "type"
3459
+ ],
3460
+ message: `unknown component "${element.type}" (allowed: ${catalog.modelComponents().map((component) => component.name).join(", ")})`
3461
+ });
3462
+ return void 0;
3463
+ }
3464
+ const validated = await catalog.validate(element.type, element.props ?? {});
3465
+ let props = {};
3466
+ if (!validated.ok) {
3467
+ for (const issue4 of validated.issues) {
3468
+ issues.push({
3469
+ path: [
3470
+ ...path,
3471
+ "props",
3472
+ ...issue4.path
3473
+ ],
3474
+ message: issue4.message
3475
+ });
3476
+ }
3477
+ } else {
3478
+ props = validated.value;
3479
+ }
3480
+ const rawChildren = parseChildrenIfStringified(element.children);
3481
+ if (rawChildren === void 0 || rawChildren === null) {
3482
+ return {
3483
+ type: element.type,
3484
+ props
3485
+ };
3486
+ }
3487
+ if (!Array.isArray(rawChildren)) {
3488
+ issues.push({
3489
+ path: [
3490
+ ...path,
3491
+ "children"
3492
+ ],
3493
+ message: "must be an array of elements (a literal array, not a string)"
3494
+ });
3495
+ return void 0;
3496
+ }
3497
+ if (rawChildren.length > 0 && definition.children !== true) {
3498
+ issues.push({
3499
+ path: [
3500
+ ...path,
3501
+ "children"
3502
+ ],
3503
+ message: `${element.type} does not take children`
3504
+ });
3505
+ return void 0;
3506
+ }
3507
+ const children = [];
3508
+ for (const [index, child] of rawChildren.entries()) {
3509
+ const next = await visit(child, [
3510
+ ...path,
3511
+ "children",
3512
+ index
3513
+ ], depth + 1);
3514
+ if (next !== void 0) {
3515
+ children.push(next);
3516
+ }
3517
+ }
3518
+ return children.length > 0 ? {
3519
+ type: element.type,
3520
+ props,
3521
+ children
3522
+ } : {
3523
+ type: element.type,
3524
+ props
3525
+ };
3526
+ }, "visit");
3527
+ const root = await visit(tree, [], 1);
3528
+ return issues.length > 0 || root === void 0 ? {
3529
+ ok: false,
3530
+ issues: issues.length > 0 ? issues : [
3531
+ {
3532
+ path: [],
3533
+ message: "is invalid"
3534
+ }
3535
+ ]
3536
+ } : {
3537
+ ok: true,
3538
+ value: root
3539
+ };
3540
+ }
3541
+ __name(validateTree, "validateTree");
3542
+ function parseChildrenIfStringified(value) {
3543
+ if (typeof value !== "string") return value;
3544
+ const trimmed = value.trim();
3545
+ if (!trimmed.startsWith("[")) return value;
3546
+ return leadingJsonArray(trimmed) ?? value;
3547
+ }
3548
+ __name(parseChildrenIfStringified, "parseChildrenIfStringified");
3549
+ function leadingJsonArray(text) {
3550
+ let depth = 0;
3551
+ let inString = false;
3552
+ let escaped = false;
3553
+ for (let index = 0; index < text.length; index += 1) {
3554
+ const char = text[index];
3555
+ if (inString) {
3556
+ if (escaped) escaped = false;
3557
+ else if (char === "\\") escaped = true;
3558
+ else if (char === '"') inString = false;
3559
+ continue;
3560
+ }
3561
+ if (char === '"') inString = true;
3562
+ else if (char === "[" || char === "{") depth += 1;
3563
+ else if (char === "]" || char === "}") {
3564
+ depth -= 1;
3565
+ if (depth === 0) {
3566
+ try {
3567
+ const parsed = JSON.parse(text.slice(0, index + 1));
3568
+ return Array.isArray(parsed) ? parsed : void 0;
3569
+ } catch {
3570
+ return void 0;
3571
+ }
3572
+ }
3573
+ }
3574
+ }
3575
+ return void 0;
3576
+ }
3577
+ __name(leadingJsonArray, "leadingJsonArray");
3578
+
3579
+ // src/genui/text.ts
3580
+ function componentToText(catalog, component, props) {
3581
+ if (component === GENUI_TREE_COMPONENT) {
3582
+ const root = props.root;
3583
+ return root === void 0 ? "" : treeToText(catalog, root);
3584
+ }
3585
+ const fallback = catalog.get(component)?.fallbackText;
3586
+ if (fallback !== void 0) {
3587
+ try {
3588
+ const text = fallback(props);
3589
+ if (typeof text === "string" && text.trim().length > 0) return text;
3590
+ } catch {
3591
+ }
3592
+ }
3593
+ return jsonBlock(props);
3594
+ }
3595
+ __name(componentToText, "componentToText");
3596
+ function treeToText(catalog, root) {
3597
+ const out = [];
3598
+ const visit = /* @__PURE__ */ __name((node, depth) => {
3599
+ if (depth > 32) return;
3600
+ const definition = catalog.get(node.type);
3601
+ if (definition?.fallbackText !== void 0) {
3602
+ try {
3603
+ const text = definition.fallbackText(node.props);
3604
+ out.push(typeof text === "string" && text.trim().length > 0 ? text : jsonBlock(node.props));
3605
+ } catch {
3606
+ out.push(jsonBlock(node.props));
3607
+ }
3608
+ } else if (definition?.children !== true) {
3609
+ out.push(jsonBlock(node.props));
3610
+ }
3611
+ for (const child of node.children ?? []) visit(child, depth + 1);
3612
+ }, "visit");
3613
+ visit(root, 0);
3614
+ return out.filter((line) => line.trim().length > 0).join("\n");
3615
+ }
3616
+ __name(treeToText, "treeToText");
3617
+ function jsonBlock(props) {
3618
+ const title = typeof props.title === "string" && props.title.length > 0 ? `*${props.title}*
3619
+ ` : "";
3620
+ return `${title}\`\`\`
3621
+ ${JSON.stringify(props, null, 2)}
3622
+ \`\`\``;
3623
+ }
3624
+ __name(jsonBlock, "jsonBlock");
3625
+
3626
+ // src/genui/capabilities.ts
3627
+ function validateUiCapabilities(value) {
3628
+ if (!record(value) || Object.keys(value).some((key) => key !== "components") || !Array.isArray(value.components)) throw new TypeError("Invalid UI capabilities");
3629
+ const seen = /* @__PURE__ */ new Set();
3630
+ const components = value.components.map((component) => {
3631
+ 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");
3632
+ seen.add(component.name);
3633
+ return {
3634
+ name: component.name,
3635
+ version: Number(component.version)
3636
+ };
3637
+ });
3638
+ return {
3639
+ components
3640
+ };
3641
+ }
3642
+ __name(validateUiCapabilities, "validateUiCapabilities");
3643
+ function record(value) {
3644
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3645
+ }
3646
+ __name(record, "record");
3647
+ function negotiateCatalog(catalog, capabilities) {
3648
+ if (capabilities === void 0) return catalog;
3649
+ const supported = validateUiCapabilities(capabilities).components;
3650
+ return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), {
3651
+ jsonSchemaValidator: catalog.validator
3652
+ });
3653
+ }
3654
+ __name(negotiateCatalog, "negotiateCatalog");
3655
+ async function prepareUiEmission(catalog, capabilities, component, props, version) {
3656
+ const drawable = negotiateCatalog(catalog, capabilities);
3657
+ if (component === GENUI_TREE_COMPONENT) {
3658
+ if (version !== void 0 && version !== 1) throw new Error("Invalid UI tree version");
3659
+ const validated2 = await validateTree(catalog, props.root);
3660
+ if (!validated2.ok) throw new Error(`Invalid UI tree: ${formatIssues(validated2.issues)}`);
3661
+ const supported = /* @__PURE__ */ __name((node) => drawable.has(node.type) && (node.children ?? []).every(supported), "supported");
3662
+ const versions = /* @__PURE__ */ new Map();
3663
+ const collectVersions = /* @__PURE__ */ __name((node) => {
3664
+ versions.set(node.type, catalog.get(node.type)?.version ?? 1);
3665
+ for (const child of node.children ?? []) collectVersions(child);
3666
+ }, "collectVersions");
3667
+ collectVersions(validated2.value);
3668
+ return supported(validated2.value) ? {
3669
+ kind: "ui",
3670
+ component,
3671
+ props: {
3672
+ root: validated2.value
3673
+ },
3674
+ version: 1,
3675
+ fallbackText: treeToText(catalog, validated2.value),
3676
+ componentVersions: Object.fromEntries(versions)
3677
+ } : {
3678
+ kind: "text",
3679
+ text: treeToText(catalog, validated2.value)
3680
+ };
3681
+ }
3682
+ const definition = catalog.get(component);
3683
+ if (definition === void 0) throw new Error(`Unknown UI component "${component}"`);
3684
+ const currentVersion = definition.version ?? 1;
3685
+ if (version !== void 0 && version !== currentVersion) throw new Error(`Invalid UI component version for "${component}"`);
3686
+ const validated = await catalog.validate(component, props);
3687
+ if (!validated.ok) throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
3688
+ return drawable.has(component) ? {
3689
+ kind: "ui",
3690
+ component,
3691
+ props: validated.value,
3692
+ version: currentVersion,
3693
+ fallbackText: componentToText(catalog, component, validated.value)
3694
+ } : {
3695
+ kind: "text",
3696
+ text: componentToText(catalog, component, validated.value)
3697
+ };
3698
+ }
3699
+ __name(prepareUiEmission, "prepareUiEmission");
3700
+
3701
+ // src/action-proposal-transitions.ts
3702
+ function checkTime(value) {
3703
+ if (!Number.isSafeInteger(value)) throw new RangeError("Action proposal timestamps must be safe integer milliseconds");
3704
+ }
3705
+ __name(checkTime, "checkTime");
3706
+ function checkString(value) {
3707
+ if (typeof value !== "string" || value.length === 0) throw new TypeError("Action proposal identifiers must be nonempty strings");
3708
+ }
3709
+ __name(checkString, "checkString");
3710
+ function validateActionProposalCreation(input) {
3711
+ const allowed = [
3712
+ "id",
3713
+ "tenantRef",
3714
+ "actorRef",
3715
+ "threadId",
3716
+ "originRunId",
3717
+ "originMessageId",
3718
+ "originToolCallId",
3719
+ "toolName",
3720
+ "input",
3721
+ "preparationInput",
3722
+ "executionContext",
3723
+ "confirmation",
3724
+ "approver",
3725
+ "expiresAt",
3726
+ "idempotencyKey",
3727
+ "replacementKey"
3728
+ ];
3729
+ if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("Unknown action proposal creation field");
3730
+ for (const key of [
3731
+ "id",
3732
+ "actorRef",
3733
+ "threadId",
3734
+ "originRunId",
3735
+ "originMessageId",
3736
+ "originToolCallId",
3737
+ "toolName",
3738
+ "approver",
3739
+ "idempotencyKey"
3740
+ ]) checkString(input[key]);
3741
+ if (input.tenantRef !== null && typeof input.tenantRef !== "string") throw new TypeError("tenantRef must be an explicit string or null");
3742
+ for (const value of [
3743
+ input.id,
3744
+ input.actorRef,
3745
+ input.threadId,
3746
+ input.tenantRef
3747
+ ]) {
3748
+ if (value !== null && value.length > 255) throw new RangeError("Action proposal id and scope must fit 255 UTF-16 code units");
3749
+ }
3750
+ if (input.replacementKey !== void 0) {
3751
+ checkString(input.replacementKey);
3752
+ if (input.replacementKey.length > 255) throw new RangeError("replacementKey must fit 255 UTF-16 code units");
3753
+ }
3754
+ if (Object.hasOwn(input, "executionContext")) {
3755
+ const context = input.executionContext;
3756
+ if (context === null || typeof context !== "object" || Array.isArray(context)) throw new TypeError("executionContext must be a JSON object");
3757
+ const allowedContext = [
3758
+ "agentName",
3759
+ "persona",
3760
+ "requestId",
3761
+ "pageContext",
3762
+ "uiCapabilities"
3763
+ ];
3764
+ if (Object.keys(context).some((key) => !allowedContext.includes(key))) throw new TypeError("Unknown execution context field");
3765
+ checkString(context.requestId);
3766
+ if (Object.hasOwn(context, "uiCapabilities")) validateUiCapabilities(context.uiCapabilities);
3767
+ if (Object.hasOwn(context, "agentName")) checkString(context.agentName);
3768
+ if (Object.hasOwn(context, "persona")) checkString(context.persona);
3769
+ if (Object.hasOwn(context, "pageContext")) {
3770
+ const page = context.pageContext;
3771
+ if (page === null || typeof page !== "object" || Array.isArray(page)) throw new TypeError("pageContext must be a JSON object");
3772
+ if (Object.hasOwn(page, "kind") && typeof page.kind !== "string") throw new TypeError("pageContext.kind must be a string");
3773
+ }
3774
+ }
3775
+ if (input.expiresAt !== null) checkTime(input.expiresAt);
3776
+ checkString(input.confirmation?.title);
3777
+ checkString(input.confirmation?.verb);
3778
+ canonicalActionProposalJson(input);
3779
+ }
3780
+ __name(validateActionProposalCreation, "validateActionProposalCreation");
3781
+ function validateActionProposalListQuery(query = {}) {
3782
+ const limit = query.limit === void 0 ? 100 : query.limit;
3783
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
3784
+ if (query.decision !== void 0 && ![
3785
+ "pending",
3786
+ "approved",
3787
+ "rejected",
3788
+ "expired",
3789
+ "superseded"
3790
+ ].includes(query.decision)) throw new TypeError("Unsupported action proposal list decision");
3791
+ if (query.after !== void 0) {
3792
+ const after = query.after;
3793
+ 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");
3794
+ checkTime(after.createdAt);
3795
+ checkString(after.id);
3796
+ if (after.id.length > 255) throw new TypeError("Action proposal cursor id is too long");
3797
+ }
3798
+ return limit;
3799
+ }
3800
+ __name(validateActionProposalListQuery, "validateActionProposalListQuery");
3801
+ function canonicalActionProposalJson(value) {
3802
+ const ancestors = /* @__PURE__ */ new Set();
3803
+ const sort = /* @__PURE__ */ __name((item) => {
3804
+ if (item === null || typeof item === "string" || typeof item === "boolean") return item;
3805
+ if (typeof item === "number" && Number.isFinite(item)) return item;
3806
+ if (typeof item !== "object" || item === null || ancestors.has(item)) throw new TypeError("Action proposals require finite, acyclic JSON values");
3807
+ const prototype = Object.getPrototypeOf(item);
3808
+ if (!Array.isArray(item) && prototype !== Object.prototype && prototype !== null) throw new TypeError("Action proposals require plain JSON objects");
3809
+ ancestors.add(item);
3810
+ const sorted = Array.isArray(item) ? Array.from(item, sort) : Object.fromEntries(Object.keys(item).sort().map((key) => [
3811
+ key,
3812
+ sort(item[key])
3813
+ ]));
3814
+ ancestors.delete(item);
3815
+ return sorted;
3816
+ }, "sort");
3817
+ return JSON.stringify(sort(value));
3818
+ }
3819
+ __name(canonicalActionProposalJson, "canonicalActionProposalJson");
3820
+ function snapshotActionProposal(value) {
3821
+ return JSON.parse(canonicalActionProposalJson(value));
3822
+ }
3823
+ __name(snapshotActionProposal, "snapshotActionProposal");
3824
+ function actionProposalScopeMatches(row, scope) {
3825
+ return row.tenantRef === scope.tenantRef && row.actorRef === scope.actorRef && row.threadId === scope.threadId;
3826
+ }
3827
+ __name(actionProposalScopeMatches, "actionProposalScopeMatches");
3828
+ function initialActionProposal(input, now) {
3829
+ validateActionProposalCreation(input);
3830
+ checkTime(now);
3831
+ return snapshotActionProposal({
3832
+ ...input,
3833
+ decision: "pending",
3834
+ decisionAudit: null,
3835
+ execution: null,
3836
+ createdAt: now,
3837
+ updatedAt: now
3838
+ });
3839
+ }
3840
+ __name(initialActionProposal, "initialActionProposal");
3841
+ function actionProposalCreationMatches(row, input) {
3842
+ validateActionProposalCreation(input);
3843
+ const { decision: _decision, decisionAudit: _audit, execution: _execution, createdAt: _created, updatedAt: _updated, supersededBy: _supersededBy, outcome: _outcome, outcomeDelivery: _delivery, ...original } = row;
3844
+ return canonicalActionProposalJson(original) === canonicalActionProposalJson(input);
3845
+ }
3846
+ __name(actionProposalCreationMatches, "actionProposalCreationMatches");
3847
+ function result(status, proposal) {
3848
+ return {
3849
+ status,
3850
+ proposal: snapshotActionProposal(proposal)
3851
+ };
3852
+ }
3853
+ __name(result, "result");
3854
+ function transitionActionProposalDecision(row, command, now) {
3855
+ checkTime(now);
3856
+ if (![
3857
+ "approved",
3858
+ "rejected",
3859
+ "expired"
3860
+ ].includes(command.decision)) throw new TypeError("Unsupported action proposal decision");
3861
+ checkString(command.actorRef);
3862
+ checkString(command.via);
3863
+ if (command.reason !== void 0 && typeof command.reason !== "string") throw new TypeError("reason must be a string");
3864
+ if (command.remember !== void 0 && typeof command.remember !== "boolean") throw new TypeError("remember must be a boolean");
3865
+ if (row.decision !== "pending") {
3866
+ return result(row.decision === command.decision ? "unchanged" : row.decision === "expired" ? "expired" : "conflict", row);
3867
+ }
3868
+ const next = snapshotActionProposal(row);
3869
+ if (row.expiresAt !== null && now >= row.expiresAt) {
3870
+ next.decision = "expired";
3871
+ next.decisionAudit = {
3872
+ actorRef: "system",
3873
+ via: "expiry",
3874
+ at: now
3875
+ };
3876
+ next.updatedAt = now;
3877
+ return result(command.decision === "expired" ? "applied" : "expired", attachActionProposalOutcome(next, now));
3878
+ }
3879
+ if (command.decision === "expired") return result("conflict", row);
3880
+ const { decision, ...audit } = command;
3881
+ next.decision = decision;
3882
+ next.decisionAudit = {
3883
+ actorRef: audit.actorRef,
3884
+ via: audit.via,
3885
+ at: now,
3886
+ ...audit.reason === void 0 ? {} : {
3887
+ reason: audit.reason
3888
+ },
3889
+ ...audit.remember === void 0 ? {} : {
3890
+ remember: audit.remember
3891
+ }
3892
+ };
3893
+ next.updatedAt = now;
3894
+ if (decision === "approved") next.execution = {
3895
+ status: "queued",
3896
+ generation: 0,
3897
+ lease: null
3898
+ };
3899
+ return result("applied", decision === "approved" ? next : attachActionProposalOutcome(next, now));
3900
+ }
3901
+ __name(transitionActionProposalDecision, "transitionActionProposalDecision");
3902
+ function checkLeaseMs(leaseMs, now) {
3903
+ checkTime(now);
3904
+ if (!Number.isSafeInteger(leaseMs) || leaseMs <= 0 || !Number.isSafeInteger(now + leaseMs)) throw new RangeError("leaseMs must be finite and positive");
3905
+ }
3906
+ __name(checkLeaseMs, "checkLeaseMs");
3907
+ function transitionActionProposalClaim(row, command, now, token) {
3908
+ checkLeaseMs(command.leaseMs, now);
3909
+ checkString(command.workerId);
3910
+ checkString(token);
3911
+ const execution = row.execution;
3912
+ if (row.decision !== "approved" || !execution || execution.status !== "queued" && !(execution.status === "executing" && execution.lease && now >= execution.lease.expiresAt)) return result("conflict", row);
3913
+ const next = snapshotActionProposal(row);
3914
+ const generation = execution.generation + 1;
3915
+ next.execution = {
3916
+ status: "executing",
3917
+ generation,
3918
+ lease: {
3919
+ token,
3920
+ generation,
3921
+ workerId: command.workerId,
3922
+ expiresAt: now + command.leaseMs
3923
+ }
3924
+ };
3925
+ next.updatedAt = now;
3926
+ return result("applied", next);
3927
+ }
3928
+ __name(transitionActionProposalClaim, "transitionActionProposalClaim");
3929
+ function leaseStatus(row, command, now) {
3930
+ checkTime(now);
3931
+ checkString(command.token);
3932
+ if (!Number.isSafeInteger(command.generation) || command.generation < 1) throw new RangeError("Invalid lease generation");
3933
+ const work = row.execution;
3934
+ if (row.decision !== "approved" || work?.status !== "executing" || !work.lease || work.generation !== command.generation || work.lease.generation !== command.generation || work.lease.token !== command.token) return "conflict";
3935
+ return now >= work.lease.expiresAt ? "expired" : "valid";
3936
+ }
3937
+ __name(leaseStatus, "leaseStatus");
3938
+ function transitionActionProposalLease(row, command, now) {
3939
+ checkLeaseMs(command.leaseMs, now);
3940
+ const status = leaseStatus(row, command, now);
3941
+ if (status !== "valid") return result(status, row);
3942
+ const next = snapshotActionProposal(row);
3943
+ if (next.execution?.lease) next.execution.lease.expiresAt = Math.max(next.execution.lease.expiresAt, now + command.leaseMs);
3944
+ next.updatedAt = now;
3945
+ return result("applied", next);
3946
+ }
3947
+ __name(transitionActionProposalLease, "transitionActionProposalLease");
3948
+ function transitionActionProposalSettlement(row, command, now) {
3949
+ checkTime(now);
3950
+ if (command.status !== "succeeded" && command.status !== "failed") throw new TypeError("Unsupported settlement status");
3951
+ 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");
3952
+ const status = leaseStatus(row, command, now);
3953
+ if (status !== "valid") return result(status, row);
3954
+ const next = snapshotActionProposal(row);
3955
+ next.execution = command.status === "succeeded" ? {
3956
+ status: "succeeded",
3957
+ generation: command.generation,
3958
+ lease: null,
3959
+ ...command.result === void 0 ? {} : {
3960
+ result: command.result
3961
+ }
3962
+ } : {
3963
+ status: "failed",
3964
+ generation: command.generation,
3965
+ lease: null,
3966
+ error: command.error
3967
+ };
3968
+ if (command.ui !== void 0) canonicalActionProposalJson(command.ui);
3969
+ if (command.text !== void 0 && typeof command.text !== "string") throw new TypeError("Outcome text must be a string");
3970
+ next.updatedAt = now;
3971
+ return result("applied", attachActionProposalOutcome(next, now, command.ui, command.text));
3972
+ }
3973
+ __name(transitionActionProposalSettlement, "transitionActionProposalSettlement");
3974
+ function transitionActionProposalSupersession(row, replacement, command, now) {
3975
+ checkTime(now);
3976
+ checkString(command.replacementProposalId);
3977
+ checkString(command.actorRef);
3978
+ checkString(command.via);
3979
+ if (replacement.id !== command.replacementProposalId || row.id === replacement.id || !actionProposalScopeMatches(row, replacement) || row.toolName !== replacement.toolName || !row.replacementKey || row.replacementKey !== replacement.replacementKey) return result("conflict", row);
3980
+ if (row.decision === "superseded") return result(row.supersededBy === replacement.id ? "unchanged" : "conflict", row);
3981
+ if (row.decision !== "pending") return result("conflict", row);
3982
+ if (row.expiresAt !== null && now >= row.expiresAt) return {
3983
+ ...transitionActionProposalDecision(row, {
3984
+ decision: "expired",
3985
+ actorRef: "system",
3986
+ via: "expiry"
3987
+ }, now),
3988
+ status: "expired"
3989
+ };
3990
+ const next = snapshotActionProposal({
3991
+ ...row,
3992
+ decision: "superseded",
3993
+ supersededBy: replacement.id,
3994
+ decisionAudit: {
3995
+ actorRef: command.actorRef,
3996
+ via: command.via,
3997
+ replacementProposalId: replacement.id,
3998
+ at: now
3999
+ },
4000
+ updatedAt: now
4001
+ });
4002
+ return result("applied", attachActionProposalOutcome(next, now));
4003
+ }
4004
+ __name(transitionActionProposalSupersession, "transitionActionProposalSupersession");
4005
+
2774
4006
  // src/tool-registry.ts
2775
4007
  var ToolForbiddenError = class extends Error {
2776
4008
  static {
@@ -2813,6 +4045,35 @@ var ToolInputInvalidError = class extends Error {
2813
4045
  this.name = "ToolInputInvalidError";
2814
4046
  }
2815
4047
  };
4048
+ var ToolPreflightDeniedError = class extends Error {
4049
+ static {
4050
+ __name(this, "ToolPreflightDeniedError");
4051
+ }
4052
+ toolName;
4053
+ reason;
4054
+ constructor(toolName, reason) {
4055
+ super(reason), this.toolName = toolName, this.reason = reason;
4056
+ this.name = "ToolPreflightDeniedError";
4057
+ }
4058
+ };
4059
+ var ToolInputDriftError = class extends Error {
4060
+ static {
4061
+ __name(this, "ToolInputDriftError");
4062
+ }
4063
+ toolName;
4064
+ constructor(toolName) {
4065
+ super(`Tool "${toolName}" input differs from its trusted snapshot`), this.toolName = toolName;
4066
+ this.name = "ToolInputDriftError";
4067
+ }
4068
+ };
4069
+ function assertTrustedInput(name, value, approved) {
4070
+ try {
4071
+ if (canonicalActionProposalJson(value) === canonicalActionProposalJson(approved)) return;
4072
+ } catch {
4073
+ }
4074
+ throw new ToolInputDriftError(name);
4075
+ }
4076
+ __name(assertTrustedInput, "assertTrustedInput");
2816
4077
  var ToolRegistry = class {
2817
4078
  static {
2818
4079
  __name(this, "ToolRegistry");
@@ -2860,11 +4121,12 @@ var ToolRegistry = class {
2860
4121
  */
2861
4122
  async definitionsFor(actor, policy, allowedTools, scope = {}) {
2862
4123
  const visible = await this.visibleEntries(actor, policy, allowedTools);
2863
- return Promise.all(visible.map(async ({ spec, handler }) => {
4124
+ const definitions = await Promise.all(visible.map(async ({ spec, handler }) => {
2864
4125
  const override = handler.describe === void 0 ? void 0 : await handler.describe({
2865
4126
  actor,
2866
4127
  ...scope
2867
4128
  });
4129
+ if (override?.available === false) return null;
2868
4130
  return {
2869
4131
  name: spec.name,
2870
4132
  kind: spec.kind,
@@ -2872,6 +4134,7 @@ var ToolRegistry = class {
2872
4134
  inputSchema: override?.inputSchema ?? spec.inputSchema
2873
4135
  };
2874
4136
  }));
4137
+ return definitions.filter((definition) => definition !== null);
2875
4138
  }
2876
4139
  /**
2877
4140
  * The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list,
@@ -2896,7 +4159,41 @@ var ToolRegistry = class {
2896
4159
  * a call can reach here from a replayed durable step or an approval granted before the flag
2897
4160
  * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
2898
4161
  */
2899
- async invoke(name, input, ctx, policy) {
4162
+ async invoke(name, input, ctx, policy, options = {}) {
4163
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
4164
+ const trusted = Object.hasOwn(options, "approvedInput");
4165
+ if (trusted) assertTrustedInput(name, value, options.approvedInput);
4166
+ const executionInput = trusted ? snapshotActionProposal(value) : value;
4167
+ const hookInput = trusted ? snapshotActionProposal(executionInput) : value;
4168
+ const result2 = entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
4169
+ phase: "execute"
4170
+ }) : void 0;
4171
+ if (trusted) assertTrustedInput(name, hookInput, executionInput);
4172
+ if (result2?.status === "denied") throw new ToolPreflightDeniedError(name, result2.reason);
4173
+ if (result2?.status === "completed") return result2.output;
4174
+ return entry.handler.execute(executionInput, withEmit);
4175
+ }
4176
+ /** Authorize and validate without executing. The caller journals this result before approval. */
4177
+ async prepare(name, input, ctx, policy, options = {}) {
4178
+ return (await this.prepareValidated(name, input, ctx, policy, options)).preflight;
4179
+ }
4180
+ /** Expose the single schema parse; snapshotInput opts proposal callers into immutable JSON. */
4181
+ async prepareValidated(name, input, ctx, policy, options = {}) {
4182
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
4183
+ const normalized = options.snapshotInput === true ? snapshotActionProposal(value) : value;
4184
+ const hookInput = options.snapshotInput === true ? snapshotActionProposal(normalized) : value;
4185
+ const preflight = (entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
4186
+ phase: "prepare"
4187
+ }) : void 0) ?? {
4188
+ status: "ready"
4189
+ };
4190
+ if (options.snapshotInput === true) assertTrustedInput(name, hookInput, normalized);
4191
+ return {
4192
+ input: normalized,
4193
+ preflight
4194
+ };
4195
+ }
4196
+ async validated(name, input, ctx, policy, options) {
2900
4197
  const entry = this.entries.get(name);
2901
4198
  if (entry === void 0) {
2902
4199
  throw new ToolNotFoundError(name);
@@ -2910,6 +4207,9 @@ var ToolRegistry = class {
2910
4207
  if (!await canActorUseTool(ctx.actor, entry.handler)) {
2911
4208
  throw new ToolForbiddenError(name);
2912
4209
  }
4210
+ if (options.allowedTools !== void 0 && !options.allowedTools.includes(name)) {
4211
+ throw new ToolForbiddenError(name);
4212
+ }
2913
4213
  const validation = await entry.spec.inputSchema["~standard"].validate(input);
2914
4214
  if (validation.issues !== void 0) {
2915
4215
  throw new ToolInputInvalidError(name, validation.issues);
@@ -2918,7 +4218,11 @@ var ToolRegistry = class {
2918
4218
  ...ctx,
2919
4219
  emitUi: createNoopEmitUi(ctx.requestId)
2920
4220
  };
2921
- return entry.handler.execute(validation.value, withEmit);
4221
+ return {
4222
+ entry,
4223
+ value: validation.value,
4224
+ ctx: withEmit
4225
+ };
2922
4226
  }
2923
4227
  };
2924
4228
  var DefaultRolesPolicy = class {
@@ -2965,6 +4269,69 @@ __name(closeEmptyRoles, "closeEmptyRoles");
2965
4269
  var import_node_crypto = require("crypto");
2966
4270
  var import_nestjs_diagnostics2 = require("@dudousxd/nestjs-diagnostics");
2967
4271
 
4272
+ // src/action-proposal-approval.ts
4273
+ async function resolveActionProposalApproval(input) {
4274
+ const policy = input.policy ?? new DefaultApprovalPolicy();
4275
+ const requirement = await policy.requirementFor(input.tool, input.actor, input.thread);
4276
+ if (!requirement.required) return {
4277
+ mode: "auto"
4278
+ };
4279
+ const remembered = await input.store.rememberedApprovals?.(input.thread.threadId) ?? [];
4280
+ if (remembered.includes(input.tool.name)) return {
4281
+ mode: "remembered",
4282
+ approver: requirement.approver
4283
+ };
4284
+ const roundedTtl = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) ? Math.floor(requirement.ttlMs) : 0;
4285
+ if (roundedTtl <= 0) return {
4286
+ mode: "ask",
4287
+ approver: requirement.approver
4288
+ };
4289
+ const now = (input.clock ?? Date.now)();
4290
+ if (!Number.isSafeInteger(now) || !Number.isSafeInteger(roundedTtl) || !Number.isSafeInteger(now + roundedTtl)) throw new RangeError("Invalid action approval deadline");
4291
+ return {
4292
+ mode: "ask",
4293
+ approver: requirement.approver,
4294
+ ttlMs: roundedTtl,
4295
+ expiresAt: new Date(now + roundedTtl).toISOString()
4296
+ };
4297
+ }
4298
+ __name(resolveActionProposalApproval, "resolveActionProposalApproval");
4299
+
4300
+ // src/action-proposal-preparation.ts
4301
+ async function prepareActionProposal(registry, name, input, ctx, policy, options = {}) {
4302
+ const preparationInput = snapshotActionProposal(input);
4303
+ const prepared = await registry.prepareValidated(name, snapshotActionProposal(preparationInput), ctx, policy, {
4304
+ ...options,
4305
+ snapshotInput: true
4306
+ });
4307
+ let replacementKey;
4308
+ const replacement = registry.spec(name)?.replacementKey;
4309
+ if (prepared.preflight.status === "ready" && replacement !== void 0) {
4310
+ replacementKey = typeof replacement === "string" ? replacement : await replacement(snapshotActionProposal(prepared.input), ctx);
4311
+ 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");
4312
+ }
4313
+ return {
4314
+ ...replacementKey !== void 0 ? {
4315
+ replacementKey
4316
+ } : {},
4317
+ preparationInput,
4318
+ input: prepared.input,
4319
+ // Completed results keep their normal handler contract; they never create a proposal.
4320
+ preflight: prepared.preflight.status === "ready" ? snapshotActionProposal(prepared.preflight) : prepared.preflight
4321
+ };
4322
+ }
4323
+ __name(prepareActionProposal, "prepareActionProposal");
4324
+
4325
+ // src/action-proposal-receipt.ts
4326
+ function actionProposalReceipt(proposalId) {
4327
+ return {
4328
+ proposalId,
4329
+ status: "pending",
4330
+ executed: false
4331
+ };
4332
+ }
4333
+ __name(actionProposalReceipt, "actionProposalReceipt");
4334
+
2968
4335
  // src/diagnostics.ts
2969
4336
  var import_nestjs_diagnostics = require("@dudousxd/nestjs-diagnostics");
2970
4337
  function publishAgentRunStarted(payload) {
@@ -3040,6 +4407,56 @@ function agentDiagnosticKey(event) {
3040
4407
  }
3041
4408
  __name(agentDiagnosticKey, "agentDiagnosticKey");
3042
4409
 
4410
+ // src/negotiated-tool-ui.ts
4411
+ function createNegotiatedUiCollector(toolCallId, scope, resolveCatalog, write) {
4412
+ const collector = createUiCollector(toolCallId, write);
4413
+ const texts = /* @__PURE__ */ new Map();
4414
+ let next = 0;
4415
+ return {
4416
+ components: collector.components,
4417
+ text: /* @__PURE__ */ __name(() => [
4418
+ ...texts.values()
4419
+ ].join("\n"), "text"),
4420
+ restart: /* @__PURE__ */ __name(() => {
4421
+ next = 0;
4422
+ texts.clear();
4423
+ collector.restart();
4424
+ }, "restart"),
4425
+ emit: /* @__PURE__ */ __name(async (component, props, options = {}) => {
4426
+ if (!resolveCatalog) {
4427
+ if (scope.uiCapabilities !== void 0) throw new Error("UI capabilities require an authorized server catalog");
4428
+ return collector.emit(component, props, options);
4429
+ }
4430
+ const capturedProps = snapshotActionProposal(props);
4431
+ const capturedOptions = {
4432
+ ...options
4433
+ };
4434
+ const catalog = await resolveCatalog(scope);
4435
+ const prepared = await prepareUiEmission(catalog, scope.uiCapabilities, component, capturedProps, capturedOptions.version);
4436
+ const id = capturedOptions.id ?? `${toolCallId}:ui:${next++}`;
4437
+ if (prepared.kind === "text") {
4438
+ texts.set(id, prepared.text);
4439
+ await write?.({
4440
+ kind: "text",
4441
+ text: prepared.text
4442
+ });
4443
+ return {
4444
+ id
4445
+ };
4446
+ }
4447
+ return collector.emit(prepared.component, prepared.props, {
4448
+ id,
4449
+ version: prepared.version,
4450
+ fallbackText: prepared.fallbackText,
4451
+ ...prepared.componentVersions !== void 0 ? {
4452
+ componentVersions: prepared.componentVersions
4453
+ } : {}
4454
+ });
4455
+ }, "emit")
4456
+ };
4457
+ }
4458
+ __name(createNegotiatedUiCollector, "createNegotiatedUiCollector");
4459
+
3043
4460
  // src/tool-retry.ts
3044
4461
  function hasTransientShape(error) {
3045
4462
  if (typeof error !== "object" || error === null) {
@@ -3065,6 +4482,7 @@ function hasTransientShape(error) {
3065
4482
  }
3066
4483
  __name(hasTransientShape, "hasTransientShape");
3067
4484
  function isTransientToolError(error) {
4485
+ if (error instanceof ToolPreflightDeniedError) return false;
3068
4486
  if (hasTransientShape(error)) {
3069
4487
  return true;
3070
4488
  }
@@ -3107,7 +4525,7 @@ async function invokeWithTransientRetry(fn, setting, options) {
3107
4525
  try {
3108
4526
  return await fn();
3109
4527
  } catch (error) {
3110
- if (isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
4528
+ if (error instanceof ToolPreflightDeniedError || isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
3111
4529
  throw error;
3112
4530
  }
3113
4531
  const attemptsRemain = attempt < attempts;
@@ -3261,16 +4679,10 @@ async function resolvePrompt(prompt, ctx) {
3261
4679
  return typeof prompt === "function" ? prompt(ctx) : prompt;
3262
4680
  }
3263
4681
  __name(resolvePrompt, "resolvePrompt");
3264
- async function resolveSystemPrompt(deps, input) {
3265
- const ctx = {
3266
- actor: input.actor,
3267
- agentName: input.agentName ?? "default",
3268
- ...input.pageContext !== void 0 ? {
3269
- pageContext: input.pageContext
3270
- } : {}
3271
- };
4682
+ async function resolveSystemPrompt(deps, input, persona) {
4683
+ const ctx = promptContext(input, persona);
3272
4684
  const sections = [
3273
- await resolvePrompt(deps.systemPrompt, ctx)
4685
+ persona?.prompt ?? await resolvePrompt(deps.systemPrompt, ctx)
3274
4686
  ];
3275
4687
  for (const contribute of deps.promptContributors ?? []) {
3276
4688
  const section = await contribute(ctx);
@@ -3281,6 +4693,70 @@ async function resolveSystemPrompt(deps, input) {
3281
4693
  return sections.join("\n\n");
3282
4694
  }
3283
4695
  __name(resolveSystemPrompt, "resolveSystemPrompt");
4696
+ function withoutPersona(input) {
4697
+ const { persona: _dropped, ...rest } = input;
4698
+ return rest;
4699
+ }
4700
+ __name(withoutPersona, "withoutPersona");
4701
+ function promptContext(input, persona) {
4702
+ return {
4703
+ actor: input.actor,
4704
+ agentName: input.agentName ?? "default",
4705
+ ...input.pageContext !== void 0 ? {
4706
+ pageContext: input.pageContext
4707
+ } : {},
4708
+ ...input.uiCapabilities !== void 0 ? {
4709
+ uiCapabilities: input.uiCapabilities
4710
+ } : {},
4711
+ ...persona !== void 0 ? {
4712
+ persona: {
4713
+ id: persona.id,
4714
+ label: persona.label
4715
+ }
4716
+ } : {}
4717
+ };
4718
+ }
4719
+ __name(promptContext, "promptContext");
4720
+ async function resolveTurnPersona(deps, input) {
4721
+ const persona = findPersona(deps, input.persona);
4722
+ if (persona === void 0) {
4723
+ return null;
4724
+ }
4725
+ const ref = {
4726
+ id: persona.id,
4727
+ label: persona.label
4728
+ };
4729
+ let prompt;
4730
+ if (persona.systemPrompt !== void 0) {
4731
+ const ctx = promptContext(input, ref);
4732
+ const basePrompt = typeof persona.systemPrompt === "function" ? await resolvePrompt(deps.systemPrompt, ctx) : void 0;
4733
+ prompt = await resolvePrompt(persona.systemPrompt, {
4734
+ ...ctx,
4735
+ ...basePrompt !== void 0 ? {
4736
+ basePrompt
4737
+ } : {}
4738
+ });
4739
+ }
4740
+ return {
4741
+ ...ref,
4742
+ ...persona.allowedTools !== void 0 ? {
4743
+ allowedTools: [
4744
+ ...persona.allowedTools
4745
+ ]
4746
+ } : {},
4747
+ ...prompt !== void 0 ? {
4748
+ prompt
4749
+ } : {}
4750
+ };
4751
+ }
4752
+ __name(resolveTurnPersona, "resolveTurnPersona");
4753
+ function personaRefusal(persona, toolName) {
4754
+ if (persona?.allowedTools === void 0 || persona.allowedTools.includes(toolName)) {
4755
+ return null;
4756
+ }
4757
+ return `(tool "${toolName}" is not available to the "${persona.id}" persona)`;
4758
+ }
4759
+ __name(personaRefusal, "personaRefusal");
3284
4760
  function extractTask(input) {
3285
4761
  if (typeof input === "object" && input !== null && "task" in input) {
3286
4762
  const task = input.task;
@@ -3428,7 +4904,8 @@ async function loadSelectedHistory(deps, input, hooks) {
3428
4904
  messages: keep,
3429
4905
  dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
3430
4906
  title: thread.title,
3431
- hasAssistantMessage: thread.hasAssistantMessage
4907
+ hasAssistantMessage: thread.hasAssistantMessage,
4908
+ actionApprovalMode: deps.actionApprovalMode ?? "blocking"
3432
4909
  };
3433
4910
  });
3434
4911
  return {
@@ -3522,6 +4999,9 @@ function processorContext(input, step) {
3522
4999
  step,
3523
5000
  ...input.agentName !== void 0 ? {
3524
5001
  agentName: input.agentName
5002
+ } : {},
5003
+ ...input.persona !== void 0 ? {
5004
+ persona: input.persona
3525
5005
  } : {}
3526
5006
  };
3527
5007
  }
@@ -3589,10 +5069,10 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3589
5069
  modelId: turn.modelId
3590
5070
  } : {}
3591
5071
  };
3592
- }, (result) => ({
3593
- inputTokens: result.usage.inputTokens,
3594
- outputTokens: result.usage.outputTokens,
3595
- textLength: result.text.length
5072
+ }, (result2) => ({
5073
+ inputTokens: result2.usage.inputTokens,
5074
+ outputTokens: result2.usage.outputTokens,
5075
+ textLength: result2.text.length
3596
5076
  })));
3597
5077
  await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => deps.store.recordUsage({
3598
5078
  threadId: input.threadId,
@@ -3622,13 +5102,16 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3622
5102
  throw new StructuredOutputError(issues, text, maxAttempts);
3623
5103
  }
3624
5104
  __name(structureAnswer, "structureAnswer");
3625
- function stampToolKinds(result, deps) {
5105
+ function stampToolKinds(result2, deps) {
3626
5106
  return {
3627
- ...result,
3628
- toolCalls: result.toolCalls.map((call) => call.kind === void 0 ? {
3629
- ...call,
3630
- kind: declaredKind(deps, call.name)
3631
- } : call)
5107
+ ...result2,
5108
+ toolCalls: result2.toolCalls.map((call) => {
5109
+ const { preflight: _untrusted, prepared: _untrustedPrepared, actionApproval: _untrustedApproval, ...trusted } = call;
5110
+ return trusted.kind === void 0 ? {
5111
+ ...trusted,
5112
+ kind: declaredKind(deps, trusted.name)
5113
+ } : trusted;
5114
+ })
3632
5115
  };
3633
5116
  }
3634
5117
  __name(stampToolKinds, "stampToolKinds");
@@ -3685,9 +5168,15 @@ function toolContext(deps, input, hooks) {
3685
5168
  ...input.agentName !== void 0 ? {
3686
5169
  agentName: input.agentName
3687
5170
  } : {},
5171
+ ...input.persona !== void 0 ? {
5172
+ persona: input.persona
5173
+ } : {},
3688
5174
  ...input.pageContext !== void 0 ? {
3689
5175
  pageContext: input.pageContext
3690
5176
  } : {},
5177
+ ...input.uiCapabilities !== void 0 ? {
5178
+ uiCapabilities: input.uiCapabilities
5179
+ } : {},
3691
5180
  ...deps.host !== void 0 ? {
3692
5181
  host: deps.host
3693
5182
  } : {},
@@ -3732,6 +5221,9 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3732
5221
  ],
3733
5222
  ...input.agentName !== void 0 ? {
3734
5223
  agentName: input.agentName
5224
+ } : {},
5225
+ ...input.persona !== void 0 ? {
5226
+ persona: input.persona
3735
5227
  } : {}
3736
5228
  });
3737
5229
  await deps.store.recordToolCall({
@@ -3760,16 +5252,16 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3760
5252
  }
3761
5253
  const messageId = asked.messageId;
3762
5254
  const reply = await awaitElicitation(hooks, request, toolContext(deps, input, hooks));
3763
- const result = settleElicitation(request, reply);
5255
+ const result2 = settleElicitation(request, reply);
3764
5256
  await hooks.step("intake:answers", async () => {
3765
5257
  await deps.store.updateToolCall({
3766
5258
  toolCallId: request.id,
3767
5259
  // A skip is not an answer. Both leave the agent holding the same values, but only one of them
3768
5260
  // is evidence the user chose them, and a reader auditing what the agent was told has to be
3769
5261
  // able to tell those apart.
3770
- status: result.skipped ? "rejected" : "executed",
3771
- output: result,
3772
- ...result.skipped ? {
5262
+ status: result2.skipped ? "rejected" : "executed",
5263
+ output: result2,
5264
+ ...result2.skipped ? {
3773
5265
  error: "skipped by the user"
3774
5266
  } : {},
3775
5267
  ...reply.answeredByRef !== void 0 ? {
@@ -3780,13 +5272,13 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3780
5272
  {
3781
5273
  id: request.id,
3782
5274
  name: ASK_TOOL_NAME,
3783
- output: result
5275
+ output: result2
3784
5276
  }
3785
5277
  ]);
3786
5278
  await writer.write(encodeStreamEvent({
3787
5279
  kind: "tool-output",
3788
5280
  id: request.id,
3789
- output: result
5281
+ output: result2
3790
5282
  }));
3791
5283
  });
3792
5284
  return {
@@ -3799,7 +5291,7 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3799
5291
  {
3800
5292
  id: request.id,
3801
5293
  name: ASK_TOOL_NAME,
3802
- output: result
5294
+ output: result2
3803
5295
  }
3804
5296
  ]
3805
5297
  };
@@ -3845,16 +5337,140 @@ async function claimToolCall(turn, call) {
3845
5337
  const spec = deps.registry.spec(call.name);
3846
5338
  const kind = call.kind ?? declaredKind(deps, call.name);
3847
5339
  const awaitsHuman = kind === "action" || kind === "ask";
3848
- const approval = kind === "action" ? await claimApproval(turn, call, spec) : void 0;
3849
- const parks = kind === "ask" || approval?.mode === "ask";
5340
+ const independentApproval = turn.actionApprovalMode === "independent" && kind === "action" ? hooks.dispatchLlm !== void 0 && call.actionApproval !== void 0 ? call.actionApproval : await resolveActionProposalApproval({
5341
+ actor: input.actor,
5342
+ store: deps.store,
5343
+ tool: {
5344
+ name: call.name,
5345
+ kind: "action",
5346
+ ...spec !== void 0 ? {
5347
+ spec
5348
+ } : {}
5349
+ },
5350
+ thread: {
5351
+ threadId: input.threadId,
5352
+ runId: hooks.runId,
5353
+ ...input.agentName !== void 0 ? {
5354
+ agentName: input.agentName
5355
+ } : {}
5356
+ },
5357
+ ...deps.approvalPolicy !== void 0 ? {
5358
+ policy: deps.approvalPolicy
5359
+ } : {}
5360
+ }) : void 0;
5361
+ let preflight;
5362
+ let prepared;
5363
+ if (turn.actionApprovalMode === "independent" && call.prepared !== void 0 && hooks.dispatchLlm !== void 0) prepared = call.prepared;
5364
+ if (kind === "action" && hooks.dispatchLlm !== void 0 && call.preflight !== void 0) {
5365
+ preflight = call.preflight;
5366
+ } else if (kind === "action" && hooks.dispatchLlm !== void 0 && spec === void 0) {
5367
+ preflight = {
5368
+ status: "ready"
5369
+ };
5370
+ } else if (kind === "action") {
5371
+ try {
5372
+ const allowedTools = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
5373
+ if (independentApproval?.mode === "ask") {
5374
+ prepared = await prepareActionProposal(deps.registry, call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5375
+ allowedTools
5376
+ });
5377
+ preflight = prepared.preflight;
5378
+ } else {
5379
+ preflight = await deps.registry.prepare(call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5380
+ allowedTools
5381
+ });
5382
+ }
5383
+ } catch (error) {
5384
+ if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) throw error;
5385
+ preflight = {
5386
+ status: "failed",
5387
+ error: error instanceof Error ? error.message : String(error)
5388
+ };
5389
+ }
5390
+ }
5391
+ let confirmation = preflight?.status === "ready" ? preflight.confirmation : void 0;
5392
+ const approval = kind === "action" && preflight?.status === "ready" ? independentApproval ?? await claimApproval(turn, call, spec) : void 0;
5393
+ let proposalId;
5394
+ if (turn.actionApprovalMode === "independent" && approval?.mode === "ask") {
5395
+ if (!prepared) throw new Error("Independent action lacks trusted prepared input");
5396
+ const proposalStore = deps.store;
5397
+ if (typeof proposalStore.createActionProposal !== "function") throw new Error("Independent action proposal store unavailable");
5398
+ proposalId = `proposal-${(0, import_node_crypto.createHash)("sha256").update(JSON.stringify([
5399
+ hooks.runId,
5400
+ call.id
5401
+ ])).digest("hex")}`;
5402
+ const display = confirmation ?? spec?.presentation?.confirm ?? {
5403
+ title: `Allow ${call.name}?`,
5404
+ verb: "Approve"
5405
+ };
5406
+ const fill = /* @__PURE__ */ __name((value) => value.replace(/\{([^}]+)\}/g, (_match, path) => {
5407
+ let current = prepared?.input;
5408
+ for (const part of path.split(".")) current = current !== null && typeof current === "object" ? current[part] : void 0;
5409
+ return current === void 0 ? "" : String(current);
5410
+ }), "fill");
5411
+ const create = prepared.replacementKey === void 0 ? proposalStore.createActionProposal.bind(proposalStore) : (() => {
5412
+ const store = proposalStore;
5413
+ if (typeof store.createReplacingActionProposal !== "function") throw new Error("Independent replacement requires atomic supersession store");
5414
+ return store.createReplacingActionProposal.bind(store);
5415
+ })();
5416
+ const created = await create({
5417
+ ...prepared.replacementKey !== void 0 ? {
5418
+ replacementKey: prepared.replacementKey
5419
+ } : {},
5420
+ id: proposalId,
5421
+ tenantRef: input.actor.tenantRef ?? null,
5422
+ actorRef: input.actor.id,
5423
+ threadId: input.threadId,
5424
+ originRunId: hooks.runId,
5425
+ originMessageId: messageId,
5426
+ originToolCallId: call.id,
5427
+ toolName: call.name,
5428
+ input: prepared.input,
5429
+ preparationInput: prepared.preparationInput,
5430
+ executionContext: {
5431
+ requestId: hooks.runId,
5432
+ ...input.agentName !== void 0 ? {
5433
+ agentName: input.agentName
5434
+ } : {},
5435
+ ...input.persona !== void 0 ? {
5436
+ persona: input.persona
5437
+ } : {},
5438
+ ...input.pageContext !== void 0 ? {
5439
+ pageContext: input.pageContext
5440
+ } : {},
5441
+ ...input.uiCapabilities !== void 0 ? {
5442
+ uiCapabilities: input.uiCapabilities
5443
+ } : {}
5444
+ },
5445
+ confirmation: confirmation ?? {
5446
+ title: fill(display.title),
5447
+ verb: fill(display.verb),
5448
+ ...display.detail !== void 0 ? {
5449
+ detail: fill(display.detail)
5450
+ } : {}
5451
+ },
5452
+ approver: approval.approver,
5453
+ expiresAt: approval.expiresAt === void 0 ? null : Date.parse(approval.expiresAt),
5454
+ idempotencyKey: toolCallContext(toolContext(deps, input, hooks), call.id).idempotencyKey
5455
+ });
5456
+ if (created.status === "conflict") throw new Error("Action proposal replay payload conflict");
5457
+ confirmation = created.proposal?.confirmation ?? confirmation;
5458
+ }
5459
+ const parks = kind === "ask" || approval?.mode === "ask" && proposalId === void 0;
3850
5460
  await deps.store.recordToolCall({
3851
5461
  toolCallId: call.id,
3852
5462
  messageId,
3853
5463
  toolName: call.name,
3854
5464
  toolType: awaitsHuman ? "action" : "read",
3855
5465
  input: call.input,
3856
- status: parks ? "pending_approval" : "auto_executed",
5466
+ status: proposalId !== void 0 ? "proposed" : parks ? "pending_approval" : "auto_executed",
5467
+ ...proposalId !== void 0 ? {
5468
+ proposalId
5469
+ } : {},
3857
5470
  runId: hooks.runId,
5471
+ ...confirmation !== void 0 ? {
5472
+ confirmation
5473
+ } : {},
3858
5474
  ...approval !== void 0 && approval.mode !== "auto" ? {
3859
5475
  approver: approval.approver
3860
5476
  } : {},
@@ -3866,7 +5482,16 @@ async function claimToolCall(turn, call) {
3866
5482
  await turn.writer.write(encodeStreamEvent({
3867
5483
  kind: "approval-requested",
3868
5484
  id: call.id,
5485
+ ...proposalId !== void 0 ? {
5486
+ target: {
5487
+ kind: "proposal",
5488
+ proposalId
5489
+ }
5490
+ } : {},
3869
5491
  approver: approval.approver,
5492
+ ...confirmation !== void 0 ? {
5493
+ confirmation
5494
+ } : {},
3870
5495
  ...approval.expiresAt !== void 0 ? {
3871
5496
  expiresAt: approval.expiresAt
3872
5497
  } : {}
@@ -3889,6 +5514,12 @@ async function claimToolCall(turn, call) {
3889
5514
  } : {},
3890
5515
  ...approval !== void 0 ? {
3891
5516
  approval
5517
+ } : {},
5518
+ ...preflight !== void 0 ? {
5519
+ preflight
5520
+ } : {},
5521
+ ...proposalId !== void 0 ? {
5522
+ proposalId
3892
5523
  } : {}
3893
5524
  };
3894
5525
  });
@@ -3914,6 +5545,12 @@ async function claimToolCall(turn, call) {
3914
5545
  ...toolType === "action" && persisted?.approval !== void 0 ? {
3915
5546
  approval: persisted.approval
3916
5547
  } : {},
5548
+ ...persisted?.preflight !== void 0 ? {
5549
+ preflight: persisted.preflight
5550
+ } : {},
5551
+ ...persisted?.proposalId !== void 0 ? {
5552
+ proposalId: persisted.proposalId
5553
+ } : {},
3917
5554
  ctx: toolContext(deps, input, hooks)
3918
5555
  };
3919
5556
  }
@@ -4061,6 +5698,9 @@ function skillContext(input) {
4061
5698
  } : {},
4062
5699
  ...input.pageContext !== void 0 ? {
4063
5700
  pageContext: input.pageContext
5701
+ } : {},
5702
+ ...input.uiCapabilities !== void 0 ? {
5703
+ uiCapabilities: input.uiCapabilities
4064
5704
  } : {}
4065
5705
  };
4066
5706
  }
@@ -4077,6 +5717,7 @@ async function invokeClaimedTool(turn, claimed) {
4077
5717
  if (claimed.toolType === "memory") {
4078
5718
  return await rememberIntoTurn(turn, claimed, startedAt);
4079
5719
  }
5720
+ const invokeAllowList = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
4080
5721
  let raw;
4081
5722
  if (hooks.dispatchTool) {
4082
5723
  const stepCtx = {
@@ -4087,14 +5728,23 @@ async function invokeClaimedTool(turn, claimed) {
4087
5728
  ...input.agentName !== void 0 ? {
4088
5729
  agentName: input.agentName
4089
5730
  } : {},
5731
+ ...input.persona !== void 0 ? {
5732
+ persona: input.persona
5733
+ } : {},
4090
5734
  ...input.pageContext !== void 0 ? {
4091
5735
  pageContext: input.pageContext
5736
+ } : {},
5737
+ ...input.uiCapabilities !== void 0 ? {
5738
+ uiCapabilities: input.uiCapabilities
4092
5739
  } : {}
4093
5740
  };
4094
5741
  const envelope = {
4095
5742
  toolName: call.name,
4096
5743
  input: call.input,
4097
5744
  ctx: stepCtx,
5745
+ ...invokeAllowList !== void 0 ? {
5746
+ allowedTools: invokeAllowList
5747
+ } : {},
4098
5748
  ...deps.toolTimeoutMs !== void 0 ? {
4099
5749
  timeoutMs: deps.toolTimeoutMs
4100
5750
  } : {},
@@ -4107,35 +5757,49 @@ async function invokeClaimedTool(turn, claimed) {
4107
5757
  raw = await hooks.dispatchTool(resolvedCall, envelope);
4108
5758
  } else {
4109
5759
  const invocation = hooks.step(`tool:${call.id}`, async () => {
4110
- const ui2 = createUiCollector(call.id, (event) => turn.writer.write(encodeStreamEvent(event)));
4111
- const output2 = await traceToolExecution(hooks.runId, {
4112
- toolCallId: call.id,
4113
- toolName: call.name,
4114
- toolType
4115
- }, () => invokeWithTransientRetry(() => {
4116
- ui2.restart();
4117
- return deps.registry.invoke(call.name, call.input, {
4118
- ...toolCallContext(ctx, call.id),
4119
- emitUi: ui2.emit
4120
- }, deps.rolesPolicy);
4121
- }, deps.toolTransientRetry ?? {}, {
4122
- ...hooks.isControlFlowError !== void 0 ? {
4123
- isControlFlowError: hooks.isControlFlowError
4124
- } : {},
4125
- onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
4126
- publishAgentToolRetry({
4127
- toolName: call.name,
4128
- toolCallId: call.id,
4129
- attempt,
4130
- message: retryError instanceof Error ? retryError.message : String(retryError)
4131
- });
4132
- }, "onRetry")
4133
- }));
4134
- return wrapToolStepOutput(output2, ui2.components());
5760
+ const ui2 = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
5761
+ let output2;
5762
+ try {
5763
+ output2 = await traceToolExecution(hooks.runId, {
5764
+ toolCallId: call.id,
5765
+ toolName: call.name,
5766
+ toolType
5767
+ }, () => invokeWithTransientRetry(() => {
5768
+ ui2.restart();
5769
+ return deps.registry.invoke(call.name, call.input, {
5770
+ ...toolCallContext(ctx, call.id),
5771
+ emitUi: ui2.emit
5772
+ }, deps.rolesPolicy, invokeAllowList !== void 0 ? {
5773
+ allowedTools: invokeAllowList
5774
+ } : {});
5775
+ }, deps.toolTransientRetry ?? {}, {
5776
+ ...hooks.isControlFlowError !== void 0 ? {
5777
+ isControlFlowError: hooks.isControlFlowError
5778
+ } : {},
5779
+ onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
5780
+ publishAgentToolRetry({
5781
+ toolName: call.name,
5782
+ toolCallId: call.id,
5783
+ attempt,
5784
+ message: retryError instanceof Error ? retryError.message : String(retryError)
5785
+ });
5786
+ }, "onRetry")
5787
+ }));
5788
+ } catch (error) {
5789
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") return wrapToolPreflightDenied(error.message);
5790
+ throw error;
5791
+ }
5792
+ return wrapToolStepOutput(output2, ui2.components(), ui2.text());
4135
5793
  });
4136
5794
  raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
4137
5795
  }
4138
- const { output, ui } = unwrapToolStepOutput(raw);
5796
+ const { output, ui, preflightDenied, text } = unwrapToolStepOutput(raw);
5797
+ if (text) turn.toolText.set(call.id, text);
5798
+ if (preflightDenied !== void 0) return {
5799
+ status: "denied",
5800
+ reason: preflightDenied,
5801
+ executionMs: Date.now() - startedAt
5802
+ };
4139
5803
  if (ui.length > 0) {
4140
5804
  turn.toolUi.set(call.id, ui);
4141
5805
  }
@@ -4151,6 +5815,13 @@ async function invokeClaimedTool(turn, claimed) {
4151
5815
  if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) {
4152
5816
  throw error;
4153
5817
  }
5818
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") {
5819
+ return {
5820
+ status: "denied",
5821
+ reason: error.message,
5822
+ executionMs: Date.now() - startedAt
5823
+ };
5824
+ }
4154
5825
  return {
4155
5826
  status: "failed",
4156
5827
  error: error instanceof Error ? error.message : String(error),
@@ -4192,6 +5863,27 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef, meta) {
4192
5863
  } : {}
4193
5864
  }));
4194
5865
  }, "settle");
5866
+ if (outcome.status === "denied") {
5867
+ await hooks.step(`persist:toolreject:${call.id}`, async () => {
5868
+ await deps.store.updateToolCall({
5869
+ toolCallId: call.id,
5870
+ status: meta === void 0 ? "rejected" : "failed",
5871
+ error: outcome.reason,
5872
+ ...decided
5873
+ });
5874
+ await settle();
5875
+ });
5876
+ return {
5877
+ id: call.id,
5878
+ name: call.name,
5879
+ output: {
5880
+ rejected: true,
5881
+ reason: outcome.reason
5882
+ },
5883
+ denied: true,
5884
+ error: `This action was denied by its preflight check: ${outcome.reason}. The requested mutation did not run.`
5885
+ };
5886
+ }
4195
5887
  if (outcome.status === "failed") {
4196
5888
  await hooks.step(`persist:toolfail:${call.id}`, async () => {
4197
5889
  await deps.store.updateToolCall({
@@ -4248,7 +5940,7 @@ async function delegateToolCall(turn, claimed) {
4248
5940
  const { deps, input, hooks } = turn;
4249
5941
  const { call, targetAgent = call.name } = claimed;
4250
5942
  const task = extractTask(call.input);
4251
- const refusal = delegationRefusal({
5943
+ const refusal = personaRefusal(turn.persona, call.name) ?? delegationRefusal({
4252
5944
  deps,
4253
5945
  input,
4254
5946
  targetAgent
@@ -4332,12 +6024,12 @@ async function elicitToolCall(turn, claimed) {
4332
6024
  }));
4333
6025
  });
4334
6026
  const reply = await awaitElicitation(hooks, request, ctx);
4335
- const result = settleElicitation(request, reply);
4336
- await hooks.step(result.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
6027
+ const result2 = settleElicitation(request, reply);
6028
+ await hooks.step(result2.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
4337
6029
  toolCallId: call.id,
4338
- status: result.skipped ? "rejected" : "executed",
4339
- output: result,
4340
- ...result.skipped ? {
6030
+ status: result2.skipped ? "rejected" : "executed",
6031
+ output: result2,
6032
+ ...result2.skipped ? {
4341
6033
  error: "skipped by the user"
4342
6034
  } : {},
4343
6035
  ...reply.answeredByRef !== void 0 ? {
@@ -4348,24 +6040,50 @@ async function elicitToolCall(turn, claimed) {
4348
6040
  runId: hooks.runId,
4349
6041
  toolName: call.name,
4350
6042
  toolType: "action",
4351
- status: result.skipped ? "rejected" : "executed"
6043
+ status: result2.skipped ? "rejected" : "executed"
4352
6044
  });
4353
6045
  return {
4354
6046
  id: call.id,
4355
6047
  name: call.name,
4356
- output: result
6048
+ output: result2
4357
6049
  };
4358
6050
  }
4359
6051
  __name(elicitToolCall, "elicitToolCall");
4360
6052
  async function runClaimedToolCall(turn, claimed) {
4361
6053
  const { deps, input, hooks } = turn;
4362
6054
  const { call, toolType, ctx } = claimed;
6055
+ if (claimed.proposalId !== void 0) {
6056
+ turn.halt.independentProposal = true;
6057
+ return {
6058
+ id: call.id,
6059
+ name: call.name,
6060
+ output: actionProposalReceipt(claimed.proposalId)
6061
+ };
6062
+ }
4363
6063
  if (toolType === "agent") {
4364
6064
  return delegateToolCall(turn, claimed);
4365
6065
  }
4366
6066
  if (toolType === "ask") {
4367
6067
  return elicitToolCall(turn, claimed);
4368
6068
  }
6069
+ if (claimed.preflight !== void 0 && claimed.preflight.status !== "ready") {
6070
+ const result2 = claimed.preflight;
6071
+ if (result2.status === "completed" && claimed.terminal === true) turn.halt.terminal = true;
6072
+ const outcome = result2.status === "completed" ? {
6073
+ status: "executed",
6074
+ output: result2.output,
6075
+ executionMs: 0
6076
+ } : result2.status === "denied" ? {
6077
+ status: "denied",
6078
+ reason: result2.reason,
6079
+ executionMs: 0
6080
+ } : {
6081
+ status: "failed",
6082
+ error: result2.error,
6083
+ executionMs: 0
6084
+ };
6085
+ return recordToolOutcome(turn, claimed, outcome, input.actor.id);
6086
+ }
4369
6087
  let deciderRef = input.actor.id;
4370
6088
  let meta;
4371
6089
  if (toolType === "action" && claimed.approval?.mode === "remembered") {
@@ -4490,30 +6208,30 @@ function expiryNarrative() {
4490
6208
  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.";
4491
6209
  }
4492
6210
  __name(expiryNarrative, "expiryNarrative");
4493
- function outputFrame(result) {
4494
- if (result.denied === true) {
4495
- const reason = refusalReason(result);
6211
+ function outputFrame(result2) {
6212
+ if (result2.denied === true) {
6213
+ const reason = refusalReason(result2);
4496
6214
  return {
4497
6215
  kind: "tool-output-denied",
4498
- id: result.id,
6216
+ id: result2.id,
4499
6217
  ...reason !== void 0 ? {
4500
6218
  reason
4501
6219
  } : {}
4502
6220
  };
4503
6221
  }
4504
- return result.error !== void 0 ? {
6222
+ return result2.error !== void 0 ? {
4505
6223
  kind: "tool-output-error",
4506
- id: result.id,
4507
- error: result.error
6224
+ id: result2.id,
6225
+ error: result2.error
4508
6226
  } : {
4509
6227
  kind: "tool-output",
4510
- id: result.id,
4511
- output: result.output
6228
+ id: result2.id,
6229
+ output: result2.output
4512
6230
  };
4513
6231
  }
4514
6232
  __name(outputFrame, "outputFrame");
4515
- function refusalReason(result) {
4516
- const { output } = result;
6233
+ function refusalReason(result2) {
6234
+ const { output } = result2;
4517
6235
  if (output !== null && typeof output === "object" && "reason" in output) {
4518
6236
  const reason = output.reason;
4519
6237
  return typeof reason === "string" && reason !== DEFAULT_REFUSAL_REASON ? reason : void 0;
@@ -4544,14 +6262,16 @@ async function invokeClaimedToolsTogether(turn, parallel, claimed) {
4544
6262
  return results;
4545
6263
  }
4546
6264
  __name(invokeClaimedToolsTogether, "invokeClaimedToolsTogether");
4547
- async function runAgentLoop(boundDeps, input, hooks) {
6265
+ async function runAgentLoop(boundDeps, requested, hooks) {
6266
+ const persona = requested.persona === void 0 ? void 0 : await hooks.step("persona:resolve", () => resolveTurnPersona(boundDeps, requested)) ?? void 0;
6267
+ const input = persona === void 0 || persona.id !== requested.persona ? withoutPersona(requested) : requested;
4548
6268
  const deps = input.model === void 0 ? boundDeps : {
4549
6269
  ...boundDeps,
4550
6270
  model: withSelectedModel(boundDeps.model, input.model),
4551
6271
  modelId: input.model
4552
6272
  };
4553
6273
  const maxSteps = deps.maxSteps ?? 8;
4554
- let system = await resolveSystemPrompt(deps, input);
6274
+ let system = await resolveSystemPrompt(deps, input, persona);
4555
6275
  const inputProcessors = deps.inputProcessors ?? [];
4556
6276
  const outputProcessors = deps.outputProcessors ?? [];
4557
6277
  const gateMode = resolveOutputGateMode(outputProcessors);
@@ -4592,6 +6312,9 @@ async function runAgentLoop(boundDeps, input, hooks) {
4592
6312
  role: "user",
4593
6313
  content: input.userText,
4594
6314
  runId: hooks.runId,
6315
+ ...input.persona !== void 0 ? {
6316
+ persona: input.persona
6317
+ } : {},
4595
6318
  ...input.attachments !== void 0 ? {
4596
6319
  attachments: input.attachments
4597
6320
  } : {}
@@ -4614,6 +6337,9 @@ async function runAgentLoop(boundDeps, input, hooks) {
4614
6337
  actorId: input.actor.id,
4615
6338
  ...input.agentName !== void 0 ? {
4616
6339
  agentName: input.agentName
6340
+ } : {},
6341
+ ...input.persona !== void 0 ? {
6342
+ persona: input.persona
4617
6343
  } : {}
4618
6344
  });
4619
6345
  const stages = await resolvePromptStages(deps, hooks);
@@ -4745,19 +6471,44 @@ ${block}`;
4745
6471
  messages: prompt.messages,
4746
6472
  actor: input.actor,
4747
6473
  threadId: input.threadId,
6474
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
6475
+ preflightContext: {
6476
+ actor: input.actor,
6477
+ threadId: input.threadId,
6478
+ runId: hooks.runId,
6479
+ requestId: hooks.runId,
6480
+ ...input.agentName !== void 0 ? {
6481
+ agentName: input.agentName
6482
+ } : {},
6483
+ ...input.persona !== void 0 ? {
6484
+ persona: input.persona
6485
+ } : {},
6486
+ ...input.pageContext !== void 0 ? {
6487
+ pageContext: input.pageContext
6488
+ } : {},
6489
+ ...input.uiCapabilities !== void 0 ? {
6490
+ uiCapabilities: input.uiCapabilities
6491
+ } : {}
6492
+ },
4748
6493
  ...gated ? {
4749
6494
  bufferOutput: true
4750
6495
  } : {},
4751
6496
  ...input.model !== void 0 ? {
4752
6497
  model: input.model
6498
+ } : {},
6499
+ ...persona?.allowedTools !== void 0 ? {
6500
+ personaAllowedTools: persona.allowedTools
4753
6501
  } : {}
4754
6502
  });
4755
6503
  } else {
4756
6504
  const tools = withMemoryTool({
4757
6505
  tools: withSkillTool({
4758
6506
  tools: withAskTool({
4759
- tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList, {
6507
+ tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, intersectAllowLists(deps.toolAllowList, persona?.allowedTools), {
4760
6508
  threadId: input.threadId,
6509
+ ...input.uiCapabilities !== void 0 ? {
6510
+ uiCapabilities: input.uiCapabilities
6511
+ } : {},
4761
6512
  ...input.agentName !== void 0 ? {
4762
6513
  agentName: input.agentName
4763
6514
  } : {}
@@ -4777,7 +6528,7 @@ ${block}`;
4777
6528
  }) : void 0;
4778
6529
  const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
4779
6530
  const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
4780
- const result = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
6531
+ const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
4781
6532
  system: prompt.system,
4782
6533
  messages: prompt.messages,
4783
6534
  tools,
@@ -4787,15 +6538,15 @@ ${block}`;
4787
6538
  await incremental.settled();
4788
6539
  const refusal = incremental.rejection();
4789
6540
  return {
4790
- ...result,
6541
+ ...result2,
4791
6542
  releasedText: incremental.released(),
4792
6543
  ...refusal !== void 0 ? {
4793
6544
  gateRejection: refusal
4794
6545
  } : {}
4795
6546
  };
4796
6547
  }
4797
- return buffer === void 0 ? result : {
4798
- ...result,
6548
+ return buffer === void 0 ? result2 : {
6549
+ ...result2,
4799
6550
  bufferedFrames: buffer.frames()
4800
6551
  };
4801
6552
  });
@@ -4889,12 +6640,12 @@ ${block}`;
4889
6640
  role: "assistant",
4890
6641
  content: turn.text
4891
6642
  }
4892
- ], count), (result) => ({
4893
- followUps: result.followUps.length,
4894
- inputTokens: result.usage.inputTokens,
4895
- outputTokens: result.usage.outputTokens,
4896
- ...result.modelId !== void 0 ? {
4897
- modelId: result.modelId
6643
+ ], count), (result2) => ({
6644
+ followUps: result2.followUps.length,
6645
+ inputTokens: result2.usage.inputTokens,
6646
+ outputTokens: result2.usage.outputTokens,
6647
+ ...result2.modelId !== void 0 ? {
6648
+ modelId: result2.modelId
4898
6649
  } : {}
4899
6650
  })));
4900
6651
  let suggestions = generated.followUps;
@@ -4971,6 +6722,9 @@ ${block}`;
4971
6722
  ...input.agentName !== void 0 ? {
4972
6723
  agentName: input.agentName
4973
6724
  } : {},
6725
+ ...input.persona !== void 0 ? {
6726
+ persona: input.persona
6727
+ } : {},
4974
6728
  ...turn.reasoning !== void 0 ? {
4975
6729
  reasoning: turn.reasoning
4976
6730
  } : {},
@@ -5002,7 +6756,7 @@ ${block}`;
5002
6756
  };
5003
6757
  modelMessages.push(assistantMessage);
5004
6758
  for (const entry of synthetic) {
5005
- const { call, result } = entry;
6759
+ const { call, result: result2 } = entry;
5006
6760
  await hooks.step(`${entry.step}:${assistant.id}`, async () => {
5007
6761
  await deps.store.recordToolCall({
5008
6762
  toolCallId: call.id,
@@ -5016,7 +6770,7 @@ ${block}`;
5016
6770
  await deps.store.updateToolCall({
5017
6771
  toolCallId: call.id,
5018
6772
  status: "executed",
5019
- output: result.output
6773
+ output: result2.output
5020
6774
  });
5021
6775
  await writer.write(encodeStreamEvent({
5022
6776
  kind: "tool-input-available",
@@ -5028,7 +6782,7 @@ ${block}`;
5028
6782
  await writer.write(encodeStreamEvent({
5029
6783
  kind: "tool-output",
5030
6784
  id: call.id,
5031
- output: result.output
6785
+ output: result2.output
5032
6786
  }));
5033
6787
  });
5034
6788
  }
@@ -5057,14 +6811,19 @@ ${block}`;
5057
6811
  messageId: assistant.id,
5058
6812
  writer,
5059
6813
  toolUi: /* @__PURE__ */ new Map(),
6814
+ toolText: /* @__PURE__ */ new Map(),
5060
6815
  halt: {
5061
6816
  terminal: false
5062
6817
  },
6818
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
5063
6819
  ...skillOffer !== void 0 ? {
5064
6820
  skills: skillOffer
5065
6821
  } : {},
5066
6822
  ...memoryDigest !== void 0 ? {
5067
6823
  memory: memoryDigest
6824
+ } : {},
6825
+ ...persona !== void 0 ? {
6826
+ persona
5068
6827
  } : {}
5069
6828
  };
5070
6829
  const parallel = hooks.parallel;
@@ -5089,17 +6848,35 @@ ${block}`;
5089
6848
  ...results,
5090
6849
  ...syntheticResults
5091
6850
  ];
6851
+ const toolText = escapeUnsafeToolUiText(toolCallsWithKind.map((call) => turnCalls.toolText.get(call.id)).filter((text) => text !== void 0).join("\n"));
5092
6852
  assistantMessage.toolResults = settledResults;
5093
6853
  await hooks.step(`stream:tool-outputs:${i}`, async () => {
5094
6854
  await deps.store.setMessageToolResults(assistant.id, settledResults);
6855
+ if (toolText) await deps.store.appendMessage({
6856
+ threadId: input.threadId,
6857
+ role: "assistant",
6858
+ content: toolText,
6859
+ runId: hooks.runId,
6860
+ ...input.agentName !== void 0 ? {
6861
+ agentName: input.agentName
6862
+ } : {}
6863
+ });
5095
6864
  const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
5096
6865
  if (toolUi.length > 0) {
5097
6866
  await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
5098
6867
  }
5099
- for (const result of results) {
5100
- await writer.write(encodeStreamEvent(outputFrame(result)));
6868
+ for (const result2 of results) {
6869
+ await writer.write(encodeStreamEvent(outputFrame(result2)));
5101
6870
  }
5102
6871
  });
6872
+ if (toolText) {
6873
+ modelMessages.push({
6874
+ role: "assistant",
6875
+ content: toolText
6876
+ });
6877
+ lastText += `
6878
+ ${toolText}`;
6879
+ }
5103
6880
  await hooks.step(`stream:step-finish:${i}`, async () => {
5104
6881
  await writer.write(encodeStreamEvent({
5105
6882
  kind: "step-finish",
@@ -5113,7 +6890,7 @@ ${block}`;
5113
6890
  } : {}
5114
6891
  }));
5115
6892
  });
5116
- if (turnCalls.halt.terminal) {
6893
+ if (turnCalls.halt.terminal || turnCalls.halt.independentProposal) {
5117
6894
  break;
5118
6895
  }
5119
6896
  }
@@ -5169,8 +6946,152 @@ ${block}`;
5169
6946
  }
5170
6947
  __name(runAgentLoop, "runAgentLoop");
5171
6948
 
6949
+ // src/in-memory-action-proposal-store.ts
6950
+ var import_node_crypto2 = require("crypto");
6951
+ var InMemoryActionProposalStore = class {
6952
+ static {
6953
+ __name(this, "InMemoryActionProposalStore");
6954
+ }
6955
+ rows = /* @__PURE__ */ new Map();
6956
+ clock;
6957
+ constructor(options = {}) {
6958
+ this.clock = options.clock ?? Date.now;
6959
+ }
6960
+ async createActionProposal(input) {
6961
+ validateActionProposalCreation(input);
6962
+ const row = this.rows.get(input.id);
6963
+ if (row) {
6964
+ if (!actionProposalScopeMatches(row, input)) return {
6965
+ status: "conflict"
6966
+ };
6967
+ return {
6968
+ status: actionProposalCreationMatches(row, input) ? "unchanged" : "conflict",
6969
+ proposal: snapshotActionProposal(row)
6970
+ };
6971
+ }
6972
+ const proposal = initialActionProposal(input, this.clock());
6973
+ this.rows.set(input.id, proposal);
6974
+ return {
6975
+ status: "created",
6976
+ proposal: snapshotActionProposal(proposal)
6977
+ };
6978
+ }
6979
+ async getActionProposal(scope, id) {
6980
+ const row = this.rows.get(id);
6981
+ return row && actionProposalScopeMatches(row, scope) ? snapshotActionProposal(row) : null;
6982
+ }
6983
+ async listActionProposals(scope, query = {}) {
6984
+ const limit = validateActionProposalListQuery(query);
6985
+ return [
6986
+ ...this.rows.values()
6987
+ ].filter((row) => actionProposalScopeMatches(row, scope) && (query.decision === void 0 || row.decision === query.decision) && (query.after === void 0 || row.createdAt > query.after.createdAt || row.createdAt === query.after.createdAt && row.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);
6988
+ }
6989
+ mutate(scope, id, transition) {
6990
+ const row = this.rows.get(id);
6991
+ if (!row || !actionProposalScopeMatches(row, scope)) return {
6992
+ status: "not_found"
6993
+ };
6994
+ const result2 = transition(row, this.clock());
6995
+ if (result2.proposal) this.rows.set(id, snapshotActionProposal(result2.proposal));
6996
+ return result2;
6997
+ }
6998
+ async decideActionProposal(scope, id, command) {
6999
+ return this.mutate(scope, id, (row, now) => transitionActionProposalDecision(row, command, now));
7000
+ }
7001
+ async claimActionProposal(scope, id, command) {
7002
+ return this.mutate(scope, id, (row, now) => transitionActionProposalClaim(row, command, now, (0, import_node_crypto2.randomUUID)()));
7003
+ }
7004
+ async extendActionProposalLease(scope, id, command) {
7005
+ return this.mutate(scope, id, (row, now) => transitionActionProposalLease(row, command, now));
7006
+ }
7007
+ async settleActionProposal(scope, id, command) {
7008
+ return this.mutate(scope, id, (row, now) => transitionActionProposalSettlement(row, command, now));
7009
+ }
7010
+ async claimNextActionProposal(command) {
7011
+ const now = this.clock();
7012
+ validateActionProposalWorkerClaim(command, now);
7013
+ const candidates = [
7014
+ ...this.rows.values()
7015
+ ].filter((row) => row.decision === "approved" && (row.execution?.status === "queued" || row.execution?.status === "executing" && row.execution.lease !== null && now >= row.execution.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
7016
+ for (const candidate of candidates) {
7017
+ const claimed = await this.claimActionProposal(candidate, candidate.id, command);
7018
+ if (claimed.status === "applied" && claimed.proposal) return claimed.proposal;
7019
+ }
7020
+ return null;
7021
+ }
7022
+ async expireActionProposals(command) {
7023
+ const now = this.clock();
7024
+ validateActionProposalExpiryBatch(command, now);
7025
+ const candidates = [
7026
+ ...this.rows.values()
7027
+ ].filter((row) => row.decision === "pending" && row.expiresAt !== null && now >= row.expiresAt).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, command.limit);
7028
+ let expired = 0;
7029
+ for (const candidate of candidates) {
7030
+ const result2 = await this.decideActionProposal(candidate, candidate.id, {
7031
+ decision: "expired",
7032
+ actorRef: "system",
7033
+ via: "expiry"
7034
+ });
7035
+ if (result2.status === "applied") expired++;
7036
+ }
7037
+ return expired;
7038
+ }
7039
+ async claimNextActionProposalOutcome(command) {
7040
+ const now = this.clock();
7041
+ validateActionProposalWorkerClaim(command, now);
7042
+ const candidates = [
7043
+ ...this.rows.values()
7044
+ ].filter((row) => row.outcomeDelivery?.status === "pending" && (!row.outcomeDelivery.lease || now >= row.outcomeDelivery.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
7045
+ for (const candidate of candidates) {
7046
+ const row = this.rows.get(candidate.id);
7047
+ if (!row) continue;
7048
+ const next = claimActionProposalOutcome(row, command, this.clock(), (0, import_node_crypto2.randomUUID)());
7049
+ if (!next?.outcome || !next.outcomeDelivery?.lease) continue;
7050
+ this.rows.set(next.id, next);
7051
+ return {
7052
+ outcome: snapshotActionProposal(next.outcome),
7053
+ lease: {
7054
+ outcomeId: next.outcome.id,
7055
+ token: next.outcomeDelivery.lease.token,
7056
+ generation: next.outcomeDelivery.generation
7057
+ }
7058
+ };
7059
+ }
7060
+ return null;
7061
+ }
7062
+ async createReplacingActionProposal(input) {
7063
+ validateActionProposalCreation(input);
7064
+ const existing = this.rows.get(input.id);
7065
+ if (existing) return this.createActionProposal(input);
7066
+ const proposal = initialActionProposal(input, this.clock());
7067
+ const replacements = [];
7068
+ if (input.replacementKey) for (const row of this.rows.values()) {
7069
+ if (row.decision !== "pending" || !actionProposalScopeMatches(row, input) || row.toolName !== input.toolName || row.replacementKey !== input.replacementKey) continue;
7070
+ const result2 = transitionActionProposalSupersession(row, proposal, {
7071
+ replacementProposalId: proposal.id,
7072
+ actorRef: input.actorRef,
7073
+ via: "supersession"
7074
+ }, this.clock());
7075
+ if (result2.proposal) replacements.push(result2.proposal);
7076
+ }
7077
+ this.rows.set(proposal.id, proposal);
7078
+ for (const row of replacements) this.rows.set(row.id, row);
7079
+ return {
7080
+ status: "created",
7081
+ proposal: snapshotActionProposal(proposal)
7082
+ };
7083
+ }
7084
+ async supersedeActionProposal(scope, id, command) {
7085
+ const replacement = this.rows.get(command.replacementProposalId);
7086
+ if (!replacement || !actionProposalScopeMatches(replacement, scope)) return {
7087
+ status: "not_found"
7088
+ };
7089
+ return this.mutate(scope, id, (row, now) => transitionActionProposalSupersession(row, replacement, command, now));
7090
+ }
7091
+ };
7092
+
5172
7093
  // src/in-memory-store.ts
5173
- var InMemoryAgentStore = class {
7094
+ var InMemoryAgentStore = class extends InMemoryActionProposalStore {
5174
7095
  static {
5175
7096
  __name(this, "InMemoryAgentStore");
5176
7097
  }
@@ -5193,11 +7114,15 @@ var InMemoryAgentStore = class {
5193
7114
  const row = {
5194
7115
  id,
5195
7116
  actorRef: input.actor.id,
7117
+ tenantRef: input.actor.tenantRef ?? null,
5196
7118
  title: input.title ?? "New chat",
5197
7119
  transient: input.transient ?? false,
5198
7120
  createdAt: ts,
5199
7121
  updatedAt: ts,
5200
- messages: []
7122
+ messages: [],
7123
+ ...input.persona !== void 0 ? {
7124
+ persona: input.persona
7125
+ } : {}
5201
7126
  };
5202
7127
  this.threads.set(id, row);
5203
7128
  return this.toSummary(row);
@@ -5225,8 +7150,12 @@ var InMemoryAgentStore = class {
5225
7150
  }
5226
7151
  const approval = toolCallApprovalFromRow({
5227
7152
  toolCallId: call.toolCallId,
7153
+ ...call.proposalId !== void 0 ? {
7154
+ proposalId: call.proposalId
7155
+ } : {},
5228
7156
  status: call.status,
5229
7157
  approver: call.approver,
7158
+ confirmation: call.confirmation,
5230
7159
  expiresAt: call.expiresAt,
5231
7160
  remember: call.remember,
5232
7161
  executedByRef: call.executedByRef,
@@ -5249,6 +7178,10 @@ var InMemoryAgentStore = class {
5249
7178
  names.add(call.toolName);
5250
7179
  }
5251
7180
  }
7181
+ const thread = this.threads.get(threadId);
7182
+ if (thread) {
7183
+ 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);
7184
+ }
5252
7185
  return [
5253
7186
  ...names
5254
7187
  ];
@@ -5289,6 +7222,7 @@ var InMemoryAgentStore = class {
5289
7222
  const row = {
5290
7223
  id,
5291
7224
  actorRef: source.actorRef,
7225
+ tenantRef: source.tenantRef,
5292
7226
  title: source.title,
5293
7227
  transient: false,
5294
7228
  createdAt: ts,
@@ -5302,6 +7236,9 @@ var InMemoryAgentStore = class {
5302
7236
  } : {},
5303
7237
  ...source.model != null ? {
5304
7238
  model: source.model
7239
+ } : {},
7240
+ ...source.persona != null ? {
7241
+ persona: source.persona
5305
7242
  } : {}
5306
7243
  };
5307
7244
  this.threads.set(id, row);
@@ -5353,6 +7290,9 @@ var InMemoryAgentStore = class {
5353
7290
  if (patch.model !== void 0) {
5354
7291
  row.model = patch.model;
5355
7292
  }
7293
+ if (patch.persona !== void 0) {
7294
+ row.persona = patch.persona;
7295
+ }
5356
7296
  row.updatedAt = this.now();
5357
7297
  }
5358
7298
  async activeRunForThread(threadId) {
@@ -5366,6 +7306,10 @@ var InMemoryAgentStore = class {
5366
7306
  async defaultAgentForThread(threadId) {
5367
7307
  return this.threads.get(threadId)?.defaultAgent ?? null;
5368
7308
  }
7309
+ /** The thread's persona, projected like {@link defaultAgentForThread}. */
7310
+ async personaForThread(threadId) {
7311
+ return this.threads.get(threadId)?.persona ?? null;
7312
+ }
5369
7313
  /** The thread's pinned model, projected like {@link defaultAgentForThread}. */
5370
7314
  async modelForThread(threadId) {
5371
7315
  return this.threads.get(threadId)?.model ?? null;
@@ -5470,12 +7414,18 @@ var InMemoryAgentStore = class {
5470
7414
  ...input.agentName !== void 0 ? {
5471
7415
  agentName: input.agentName
5472
7416
  } : {},
7417
+ ...input.persona !== void 0 ? {
7418
+ persona: input.persona
7419
+ } : {},
5473
7420
  ...input.model !== void 0 ? {
5474
7421
  model: input.model
5475
7422
  } : {},
5476
7423
  ...input.pageContext !== void 0 ? {
5477
7424
  pageContext: input.pageContext
5478
7425
  } : {},
7426
+ ...input.uiCapabilities !== void 0 ? {
7427
+ uiCapabilities: input.uiCapabilities
7428
+ } : {},
5479
7429
  ...input.interrupt === true ? {
5480
7430
  interrupt: true
5481
7431
  } : {},
@@ -5580,6 +7530,78 @@ var InMemoryAgentStore = class {
5580
7530
  }
5581
7531
  return void 0;
5582
7532
  }
7533
+ async getThreadActionProposalScope(threadId) {
7534
+ const row = this.threads.get(threadId);
7535
+ return row ? {
7536
+ threadId,
7537
+ actorRef: row.actorRef,
7538
+ tenantRef: row.tenantRef
7539
+ } : null;
7540
+ }
7541
+ async admitActionProposalOutcome(command) {
7542
+ const row = [
7543
+ ...this.rows.values()
7544
+ ].find((row2) => row2.outcome?.id === command.outcomeId);
7545
+ if (!row?.outcome || !row.outcomeDelivery) return {
7546
+ status: "not_found"
7547
+ };
7548
+ if (row.outcomeDelivery.status === "admitted") return {
7549
+ status: "unchanged",
7550
+ ...row.outcomeDelivery.messageId !== void 0 ? {
7551
+ messageId: row.outcomeDelivery.messageId
7552
+ } : {}
7553
+ };
7554
+ if (row.outcomeDelivery.status === "discarded") return {
7555
+ status: "discarded"
7556
+ };
7557
+ if (!actionProposalOutcomeFenceValid(row, command, this.clock())) return {
7558
+ status: "conflict"
7559
+ };
7560
+ const thread = this.threads.get(row.threadId);
7561
+ if (!thread) {
7562
+ this.rows.set(row.id, snapshotActionProposal({
7563
+ ...row,
7564
+ outcomeDelivery: {
7565
+ ...row.outcomeDelivery,
7566
+ status: "discarded",
7567
+ lease: null
7568
+ }
7569
+ }));
7570
+ return {
7571
+ status: "discarded"
7572
+ };
7573
+ }
7574
+ if (thread.actorRef !== row.actorRef || thread.tenantRef !== row.tenantRef) return {
7575
+ status: "conflict"
7576
+ };
7577
+ if (thread.activeStreamId) return {
7578
+ status: "busy"
7579
+ };
7580
+ const message = {
7581
+ id: crypto.randomUUID(),
7582
+ role: "assistant",
7583
+ content: actionProposalOutcomeText(row.outcome),
7584
+ createdAt: this.now(),
7585
+ actionProposalOutcome: snapshotActionProposal(row.outcome),
7586
+ ui: snapshotActionProposal(row.outcome.ui)
7587
+ };
7588
+ const next = snapshotActionProposal({
7589
+ ...row,
7590
+ outcomeDelivery: {
7591
+ ...row.outcomeDelivery,
7592
+ status: "admitted",
7593
+ lease: null,
7594
+ messageId: message.id
7595
+ }
7596
+ });
7597
+ thread.messages.push(message);
7598
+ thread.updatedAt = message.createdAt;
7599
+ this.rows.set(row.id, next);
7600
+ return {
7601
+ status: "applied",
7602
+ messageId: message.id
7603
+ };
7604
+ }
5583
7605
  async appendMessage(input) {
5584
7606
  const row = this.threads.get(input.threadId);
5585
7607
  if (row === void 0) {
@@ -5608,6 +7630,9 @@ var InMemoryAgentStore = class {
5608
7630
  ...input.agentName !== void 0 ? {
5609
7631
  agentName: input.agentName
5610
7632
  } : {},
7633
+ ...input.persona !== void 0 ? {
7634
+ persona: input.persona
7635
+ } : {},
5611
7636
  ...input.runId !== void 0 ? {
5612
7637
  runId: input.runId
5613
7638
  } : {},
@@ -5619,6 +7644,9 @@ var InMemoryAgentStore = class {
5619
7644
  } : {},
5620
7645
  ...input.ui !== void 0 ? {
5621
7646
  ui: input.ui
7647
+ } : {},
7648
+ ...input.actionProposalOutcome !== void 0 ? {
7649
+ actionProposalOutcome: input.actionProposalOutcome
5622
7650
  } : {}
5623
7651
  };
5624
7652
  row.messages.push(message);
@@ -5710,6 +7738,9 @@ var InMemoryAgentStore = class {
5710
7738
  async recordToolCall(input) {
5711
7739
  this.toolCalls.set(input.toolCallId, {
5712
7740
  toolCallId: input.toolCallId,
7741
+ ...input.proposalId !== void 0 ? {
7742
+ proposalId: input.proposalId
7743
+ } : {},
5713
7744
  messageId: input.messageId,
5714
7745
  threadId: this.threadIdForMessage(input.messageId) ?? "",
5715
7746
  toolName: input.toolName,
@@ -5723,6 +7754,9 @@ var InMemoryAgentStore = class {
5723
7754
  ...input.approver !== void 0 ? {
5724
7755
  approver: input.approver
5725
7756
  } : {},
7757
+ ...input.confirmation !== void 0 ? {
7758
+ confirmation: structuredClone(input.confirmation)
7759
+ } : {},
5726
7760
  ...input.expiresAt !== void 0 ? {
5727
7761
  expiresAt: input.expiresAt
5728
7762
  } : {}
@@ -6024,13 +8058,14 @@ var InMemoryAgentStore = class {
6024
8058
  defaultAgent: row.defaultAgent ?? null,
6025
8059
  ...row.model != null ? {
6026
8060
  model: row.model
6027
- } : {}
8061
+ } : {},
8062
+ persona: row.persona ?? null
6028
8063
  };
6029
8064
  }
6030
8065
  };
6031
8066
 
6032
8067
  // src/confirm-token.ts
6033
- var import_node_crypto2 = require("crypto");
8068
+ var import_node_crypto3 = require("crypto");
6034
8069
  var DEFAULT_CONFIRM_TTL_MS = 15 * 60 * 1e3;
6035
8070
  function canonicalJson(value) {
6036
8071
  if (value === void 0) return "null";
@@ -6047,7 +8082,7 @@ function signature(subject, expiresAt, secret) {
6047
8082
  if (secret === "") {
6048
8083
  throw new Error("confirm token: the secret is empty");
6049
8084
  }
6050
- return (0, import_node_crypto2.createHmac)("sha256", secret).update(canonicalJson([
8085
+ return (0, import_node_crypto3.createHmac)("sha256", secret).update(canonicalJson([
6051
8086
  "agent-confirm",
6052
8087
  subject.tool,
6053
8088
  subject.actorId,
@@ -6074,11 +8109,11 @@ function verifyConfirmToken(token, subject, options) {
6074
8109
  if (expiresAt === void 0 || expiresAt < (options.now ?? Date.now())) return false;
6075
8110
  const expected = Buffer.from(signature(subject, expiresAt, options.secret));
6076
8111
  const given = Buffer.from(token.slice(token.indexOf(".") + 1));
6077
- return expected.length === given.length && (0, import_node_crypto2.timingSafeEqual)(expected, given);
8112
+ return expected.length === given.length && (0, import_node_crypto3.timingSafeEqual)(expected, given);
6078
8113
  }
6079
8114
  __name(verifyConfirmToken, "verifyConfirmToken");
6080
8115
  function hashConfirmToken(token) {
6081
- return (0, import_node_crypto2.createHash)("sha256").update(token).digest("hex");
8116
+ return (0, import_node_crypto3.createHash)("sha256").update(token).digest("hex");
6082
8117
  }
6083
8118
  __name(hashConfirmToken, "hashConfirmToken");
6084
8119
  var InMemoryConfirmTokenStore = class {
@@ -6226,12 +8261,12 @@ function withConfirmFields(schema) {
6226
8261
  });
6227
8262
  }
6228
8263
  const { args, fields } = splitConfirm(value);
6229
- const result = await inner.validate(args);
6230
- if (result.issues !== void 0) {
8264
+ const result2 = await inner.validate(args);
8265
+ if (result2.issues !== void 0) {
6231
8266
  return {
6232
8267
  issues: [
6233
8268
  ...issues,
6234
- ...result.issues
8269
+ ...result2.issues
6235
8270
  ]
6236
8271
  };
6237
8272
  }
@@ -6241,10 +8276,10 @@ function withConfirmFields(schema) {
6241
8276
  };
6242
8277
  }
6243
8278
  return {
6244
- value: isRecord(result.value) ? {
6245
- ...result.value,
8279
+ value: isRecord(result2.value) ? {
8280
+ ...result2.value,
6246
8281
  ...fields
6247
- } : result.value
8282
+ } : result2.value
6248
8283
  };
6249
8284
  }, "validate"),
6250
8285
  jsonSchema: {
@@ -6576,6 +8611,309 @@ var SqlTokenStreamSink = class {
6576
8611
  });
6577
8612
  }
6578
8613
  };
8614
+
8615
+ // src/action-proposal-text.ts
8616
+ function resolveTextActionProposalDecision(text, proposals) {
8617
+ const command = parseTextActionProposalCommand(text);
8618
+ if (command.status === "unmatched") return command;
8619
+ const { decision, remember } = command;
8620
+ const pending = proposals.filter((proposal) => proposal.decision === "pending");
8621
+ const explicitId = command.proposalId;
8622
+ if (explicitId !== void 0) {
8623
+ if (!pending.some((proposal) => proposal.id === explicitId)) return {
8624
+ status: "unmatched"
8625
+ };
8626
+ return {
8627
+ status: "decision",
8628
+ proposalId: explicitId,
8629
+ decision,
8630
+ remember
8631
+ };
8632
+ }
8633
+ if (pending.length === 0) return {
8634
+ status: "unmatched"
8635
+ };
8636
+ if (pending.length > 1) return {
8637
+ status: "ambiguous",
8638
+ proposalIds: pending.map(({ id }) => id)
8639
+ };
8640
+ const candidate = pending[0];
8641
+ if (!candidate) return {
8642
+ status: "unmatched"
8643
+ };
8644
+ return {
8645
+ status: "decision",
8646
+ proposalId: candidate.id,
8647
+ decision,
8648
+ remember
8649
+ };
8650
+ }
8651
+ __name(resolveTextActionProposalDecision, "resolveTextActionProposalDecision");
8652
+ function parseTextActionProposalCommand(text) {
8653
+ if (/[\r\n]/u.test(text)) return {
8654
+ status: "unmatched"
8655
+ };
8656
+ const normalized = text.trim();
8657
+ const match = /^(sim|confirmo|confirmar|aprovar|aprovo|cancelar|rejeitar|rejeito|não|nao)(?: (sempre nesta conversa))?(?: #([^\s]+?))?[.!]?$/iu.exec(normalized);
8658
+ if (!match) return {
8659
+ status: "unmatched"
8660
+ };
8661
+ const verb = match[1]?.toLocaleLowerCase("pt-BR");
8662
+ if (verb === void 0) return {
8663
+ status: "unmatched"
8664
+ };
8665
+ const decision = [
8666
+ "cancelar",
8667
+ "rejeitar",
8668
+ "rejeito",
8669
+ "n\xE3o",
8670
+ "nao"
8671
+ ].includes(verb) ? "rejected" : "approved";
8672
+ const remember = match[2] !== void 0;
8673
+ if (remember && decision !== "approved") return {
8674
+ status: "unmatched"
8675
+ };
8676
+ return {
8677
+ status: "command",
8678
+ decision,
8679
+ remember,
8680
+ ...match[3] !== void 0 ? {
8681
+ proposalId: match[3]
8682
+ } : {}
8683
+ };
8684
+ }
8685
+ __name(parseTextActionProposalCommand, "parseTextActionProposalCommand");
8686
+
8687
+ // src/action-proposal-executor.ts
8688
+ var ActionProposalExecutor = class {
8689
+ static {
8690
+ __name(this, "ActionProposalExecutor");
8691
+ }
8692
+ options;
8693
+ constructor(options) {
8694
+ this.options = options;
8695
+ }
8696
+ async execute(proposal) {
8697
+ let ui = createNegotiatedUiCollector(proposal.originToolCallId, {
8698
+ actor: {
8699
+ id: proposal.actorRef
8700
+ },
8701
+ threadId: proposal.threadId
8702
+ });
8703
+ try {
8704
+ const actor = await this.options.resolver.resolve({
8705
+ actorRef: proposal.actorRef,
8706
+ tenantRef: proposal.tenantRef
8707
+ });
8708
+ if (!actor || actor.id !== proposal.actorRef || (actor.tenantRef ?? null) !== proposal.tenantRef) throw new Error("Action proposal requester no longer exists or scope changed");
8709
+ const deps = await this.options.resolveExecution(proposal, actor);
8710
+ if (deps.registry.spec(proposal.toolName)?.kind !== "action") throw new Error("Action proposal tool no longer exists as an action");
8711
+ const context = proposal.executionContext;
8712
+ ui = createNegotiatedUiCollector(proposal.originToolCallId, {
8713
+ actor,
8714
+ threadId: proposal.threadId,
8715
+ ...context?.agentName !== void 0 ? {
8716
+ agentName: context.agentName
8717
+ } : {},
8718
+ ...context?.uiCapabilities !== void 0 ? {
8719
+ uiCapabilities: context.uiCapabilities
8720
+ } : {}
8721
+ }, deps.resolveUiCatalog);
8722
+ const output = await deps.registry.invoke(proposal.toolName, snapshotActionProposal(Object.hasOwn(proposal, "preparationInput") ? proposal.preparationInput : proposal.input), {
8723
+ actor,
8724
+ threadId: proposal.threadId,
8725
+ runId: proposal.originRunId,
8726
+ requestId: context?.requestId ?? proposal.originRunId,
8727
+ toolCallId: proposal.originToolCallId,
8728
+ idempotencyKey: proposal.idempotencyKey,
8729
+ ...context?.agentName !== void 0 ? {
8730
+ agentName: context.agentName
8731
+ } : {},
8732
+ ...context?.persona !== void 0 ? {
8733
+ persona: context.persona
8734
+ } : {},
8735
+ ...context?.pageContext !== void 0 ? {
8736
+ pageContext: context.pageContext
8737
+ } : {},
8738
+ ...context?.uiCapabilities !== void 0 ? {
8739
+ uiCapabilities: context.uiCapabilities
8740
+ } : {},
8741
+ ...deps.host !== void 0 ? {
8742
+ host: deps.host
8743
+ } : {},
8744
+ emitUi: ui.emit
8745
+ }, deps.rolesPolicy, {
8746
+ approvedInput: proposal.input,
8747
+ ...deps.allowedTools !== void 0 ? {
8748
+ allowedTools: deps.allowedTools
8749
+ } : {}
8750
+ });
8751
+ return snapshotActionProposal({
8752
+ status: "succeeded",
8753
+ ...output !== void 0 ? {
8754
+ result: output
8755
+ } : {},
8756
+ ui: ui.components(),
8757
+ ...ui.text() ? {
8758
+ text: ui.text()
8759
+ } : {}
8760
+ });
8761
+ } catch (error) {
8762
+ return {
8763
+ status: "failed",
8764
+ error: error instanceof Error ? error.message : String(error),
8765
+ ui: ui.components(),
8766
+ ...ui.text() ? {
8767
+ text: ui.text()
8768
+ } : {}
8769
+ };
8770
+ }
8771
+ }
8772
+ };
8773
+
8774
+ // src/action-proposal-worker.ts
8775
+ var ActionProposalWorker = class {
8776
+ static {
8777
+ __name(this, "ActionProposalWorker");
8778
+ }
8779
+ options;
8780
+ leaseMs;
8781
+ pollIntervalMs;
8782
+ maxConcurrency;
8783
+ timer;
8784
+ stopped = false;
8785
+ renewals = /* @__PURE__ */ new Set();
8786
+ active;
8787
+ constructor(options) {
8788
+ this.options = options;
8789
+ this.leaseMs = options.leaseMs ?? 3e4;
8790
+ this.pollIntervalMs = options.pollIntervalMs ?? 1e3;
8791
+ this.maxConcurrency = options.maxConcurrency ?? 1;
8792
+ for (const value of [
8793
+ this.leaseMs,
8794
+ this.pollIntervalMs,
8795
+ this.maxConcurrency
8796
+ ]) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError("Worker configuration must be positive safe integers");
8797
+ if (this.pollIntervalMs >= this.leaseMs / 3) throw new RangeError("Worker poll interval must be below one third of lease");
8798
+ if (!options.workerId) throw new TypeError("workerId is required");
8799
+ }
8800
+ start() {
8801
+ if (this.timer !== void 0 || this.stopped) return;
8802
+ const tick = /* @__PURE__ */ __name(async () => {
8803
+ if (this.stopped) return;
8804
+ try {
8805
+ await this.runOnce();
8806
+ } catch (error) {
8807
+ this.options.onError?.(error);
8808
+ }
8809
+ if (!this.stopped) this.timer = setTimeout(tick, this.pollIntervalMs);
8810
+ }, "tick");
8811
+ this.timer = setTimeout(tick, 0);
8812
+ }
8813
+ async stop() {
8814
+ this.stopped = true;
8815
+ for (const timer of this.renewals) clearInterval(timer);
8816
+ this.renewals.clear();
8817
+ if (this.timer !== void 0) clearTimeout(this.timer);
8818
+ await this.active;
8819
+ }
8820
+ runOnce() {
8821
+ if (this.active) return this.active;
8822
+ if (this.stopped) return Promise.resolve();
8823
+ const task = this.tick();
8824
+ this.active = task;
8825
+ return task.finally(() => {
8826
+ if (this.active === task) this.active = void 0;
8827
+ });
8828
+ }
8829
+ async tick() {
8830
+ await this.options.store.expireActionProposals({
8831
+ limit: 100
8832
+ });
8833
+ const work = [];
8834
+ for (let index = 0; index < this.maxConcurrency && !this.stopped; index++) {
8835
+ const proposal = await this.options.store.claimNextActionProposal({
8836
+ workerId: this.options.workerId,
8837
+ leaseMs: this.leaseMs
8838
+ });
8839
+ if (!proposal) break;
8840
+ work.push(this.execute(proposal));
8841
+ }
8842
+ await Promise.all(work);
8843
+ for (let index = 0; index < 32 && !this.stopped; index++) {
8844
+ const delivery = await this.options.store.claimNextActionProposalOutcome({
8845
+ workerId: this.options.workerId,
8846
+ leaseMs: this.leaseMs
8847
+ });
8848
+ if (!delivery) break;
8849
+ await this.options.store.admitActionProposalOutcome(delivery.lease);
8850
+ }
8851
+ }
8852
+ async execute(proposal) {
8853
+ const lease = proposal.execution?.lease;
8854
+ if (!lease) throw new Error("Claimed action lacks execution lease");
8855
+ let fenced = true;
8856
+ let renewing;
8857
+ const renew = /* @__PURE__ */ __name(() => {
8858
+ if (renewing || !fenced) return;
8859
+ renewing = this.options.store.extendActionProposalLease(proposal, proposal.id, {
8860
+ token: lease.token,
8861
+ generation: lease.generation,
8862
+ leaseMs: this.leaseMs
8863
+ }).then((result2) => {
8864
+ if (result2.status !== "applied") fenced = false;
8865
+ }, (error) => {
8866
+ fenced = false;
8867
+ this.options.onError?.(error);
8868
+ }).finally(() => {
8869
+ renewing = void 0;
8870
+ });
8871
+ }, "renew");
8872
+ const timer = setInterval(renew, this.leaseMs / 3);
8873
+ this.renewals.add(timer);
8874
+ try {
8875
+ const result2 = await this.options.executor.execute(proposal);
8876
+ clearInterval(timer);
8877
+ await renewing;
8878
+ if (!fenced) return;
8879
+ const settled = await this.options.store.settleActionProposal(proposal, proposal.id, {
8880
+ ...result2,
8881
+ token: lease.token,
8882
+ generation: lease.generation
8883
+ });
8884
+ if (settled.status === "applied" && settled.proposal) await this.options.onSettled?.(settled.proposal);
8885
+ } finally {
8886
+ clearInterval(timer);
8887
+ this.renewals.delete(timer);
8888
+ await renewing;
8889
+ }
8890
+ }
8891
+ };
8892
+
8893
+ // src/action-proposal-capabilities.ts
8894
+ function assertIndependentActionCapabilities(store, resolver) {
8895
+ const candidate = store;
8896
+ if (!resolver || typeof resolver.resolve !== "function") throw new Error("Independent action mode requires a BackgroundActorResolver");
8897
+ if (!isChatQueueStore(store) || candidate.actionProposalAdmissionSupported === false) throw new Error("Independent action mode requires transactional thread admission");
8898
+ const required = [
8899
+ "createActionProposal",
8900
+ "getActionProposal",
8901
+ "listActionProposals",
8902
+ "decideActionProposal",
8903
+ "claimActionProposal",
8904
+ "extendActionProposalLease",
8905
+ "settleActionProposal",
8906
+ "claimNextActionProposal",
8907
+ "expireActionProposals",
8908
+ "getThreadActionProposalScope",
8909
+ "claimNextActionProposalOutcome",
8910
+ "admitActionProposalOutcome",
8911
+ "createReplacingActionProposal",
8912
+ "supersedeActionProposal"
8913
+ ];
8914
+ 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");
8915
+ }
8916
+ __name(assertIndependentActionCapabilities, "assertIndependentActionCapabilities");
6579
8917
  // Annotate the CommonJS export names for ESM import in node:
6580
8918
  0 && (module.exports = {
6581
8919
  AGENT_ACTOR_DIRECTORY,
@@ -6610,6 +8948,8 @@ var SqlTokenStreamSink = class {
6610
8948
  APPROVAL_EXPIRED_REASON,
6611
8949
  ASK_TOOL_DESCRIPTION,
6612
8950
  ASK_TOOL_NAME,
8951
+ ActionProposalExecutor,
8952
+ ActionProposalWorker,
6613
8953
  AgentRegistry,
6614
8954
  AgentStreamError,
6615
8955
  CONFIRM_JSON_SCHEMA_PROPERTIES,
@@ -6630,6 +8970,7 @@ var SqlTokenStreamSink = class {
6630
8970
  DefaultRolesPolicy,
6631
8971
  ELICITATION_INPUT_TYPES,
6632
8972
  GLOBAL_SCOPE,
8973
+ InMemoryActionProposalStore,
6633
8974
  InMemoryAgentStore,
6634
8975
  InMemoryConfirmTokenStore,
6635
8976
  MAX_ASK_QUESTIONS,
@@ -6653,15 +8994,25 @@ var SqlTokenStreamSink = class {
6653
8994
  THREAD_DETAIL_CONTENT_CHARS,
6654
8995
  ToolDisabledError,
6655
8996
  ToolForbiddenError,
8997
+ ToolInputDriftError,
6656
8998
  ToolInputInvalidError,
6657
8999
  ToolNotFoundError,
9000
+ ToolPreflightDeniedError,
6658
9001
  ToolRegistry,
6659
9002
  UNFINISHED_TOOL_CALL,
9003
+ actionProposalCreationMatches,
9004
+ actionProposalOutcomeFenceValid,
9005
+ actionProposalOutcomeScope,
9006
+ actionProposalOutcomeText,
9007
+ actionProposalReceipt,
9008
+ actionProposalScopeMatches,
6660
9009
  actorScope,
6661
9010
  agentDiagnosticKey,
6662
9011
  agentFailureCode,
6663
9012
  askInputSchema,
6664
9013
  askToolDefinition,
9014
+ assertIndependentActionCapabilities,
9015
+ attachActionProposalOutcome,
6665
9016
  bucketByActor,
6666
9017
  bucketByModel,
6667
9018
  bucketByThread,
@@ -6669,12 +9020,15 @@ var SqlTokenStreamSink = class {
6669
9020
  buildMemoryBlock,
6670
9021
  buildSkillsBlock,
6671
9022
  canActorUseTool,
9023
+ canonicalActionProposalJson,
6672
9024
  canonicalJson,
9025
+ claimActionProposalOutcome,
6673
9026
  closeEmptyRoles,
6674
9027
  compositeSkillProvider,
6675
9028
  confirmTokenExpiry,
6676
9029
  createFrameBuffer,
6677
9030
  createIncrementalGate,
9031
+ createNegotiatedUiCollector,
6678
9032
  createNoopEmitUi,
6679
9033
  createUiCollector,
6680
9034
  danglingToolCallIds,
@@ -6687,6 +9041,7 @@ var SqlTokenStreamSink = class {
6687
9041
  detachedStarted,
6688
9042
  detachedUnsettled,
6689
9043
  encodeStreamEvent,
9044
+ escapeUnsafeToolUiText,
6690
9045
  estimateCost,
6691
9046
  estimateMessageTokens,
6692
9047
  exhaustedWindow,
@@ -6697,9 +9052,12 @@ var SqlTokenStreamSink = class {
6697
9052
  filterToolsByEnabled,
6698
9053
  filterToolsByRole,
6699
9054
  findCatalogModel,
9055
+ findPersona,
6700
9056
  gateFollowUps,
6701
9057
  gateTail,
6702
9058
  hashConfirmToken,
9059
+ initialActionProposal,
9060
+ intersectAllowLists,
6703
9061
  invokeWithTransientRetry,
6704
9062
  isChatQueueStore,
6705
9063
  isControlFlowSignal,
@@ -6717,6 +9075,9 @@ var SqlTokenStreamSink = class {
6717
9075
  observeTurnFrames,
6718
9076
  offerMemories,
6719
9077
  offerSkills,
9078
+ parseTextActionProposalCommand,
9079
+ personaCatalogEntry,
9080
+ prepareActionProposal,
6720
9081
  publishAgentDelegated,
6721
9082
  publishAgentMemoryResolved,
6722
9083
  publishAgentMemoryWritten,
@@ -6742,11 +9103,14 @@ var SqlTokenStreamSink = class {
6742
9103
  rememberToolDefinition,
6743
9104
  renderElicitationAnswers,
6744
9105
  repairInstruction,
9106
+ resolveActionProposalApproval,
6745
9107
  resolveElicitation,
6746
9108
  resolveGateLookback,
6747
9109
  resolveMemoryDigest,
6748
9110
  resolveOutputGateMode,
9111
+ resolvePersonaAlias,
6749
9112
  resolveSkillCatalog,
9113
+ resolveTextActionProposalDecision,
6750
9114
  resolveToolTransientRetryNumbers,
6751
9115
  rollupThreadUsage,
6752
9116
  runAgentLoop,
@@ -6763,6 +9127,7 @@ var SqlTokenStreamSink = class {
6763
9127
  skillInputSchema,
6764
9128
  skillToolDefinition,
6765
9129
  skillWriteVerdict,
9130
+ snapshotActionProposal,
6766
9131
  stampToolKinds,
6767
9132
  staticModelCatalog,
6768
9133
  staticSkillProvider,
@@ -6773,8 +9138,18 @@ var SqlTokenStreamSink = class {
6773
9138
  toolCallContext,
6774
9139
  traceLlmTurn,
6775
9140
  traceToolExecution,
9141
+ transitionActionProposalClaim,
9142
+ transitionActionProposalDecision,
9143
+ transitionActionProposalLease,
9144
+ transitionActionProposalSettlement,
9145
+ transitionActionProposalSupersession,
6776
9146
  truncateDetailContent,
6777
9147
  unwrapToolStepOutput,
9148
+ validateActionProposalCreation,
9149
+ validateActionProposalDiscoveryIndexBatch,
9150
+ validateActionProposalExpiryBatch,
9151
+ validateActionProposalListQuery,
9152
+ validateActionProposalWorkerClaim,
6778
9153
  validateElicitationAnswer,
6779
9154
  validateElicitationValue,
6780
9155
  validateStructured,
@@ -6787,6 +9162,7 @@ var SqlTokenStreamSink = class {
6787
9162
  withSkillTool,
6788
9163
  withToolTimeout,
6789
9164
  withTurnFrames,
9165
+ wrapToolPreflightDenied,
6790
9166
  wrapToolStepOutput,
6791
9167
  writeMemory
6792
9168
  });