@dudousxd/nestjs-agent-core 0.39.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +7 -0
  2. package/dist/ag-ui/index.cjs +123 -9
  3. package/dist/ag-ui/index.cjs.map +1 -1
  4. package/dist/ag-ui/index.d.cts +19 -3
  5. package/dist/ag-ui/index.d.ts +19 -3
  6. package/dist/ag-ui/index.js +122 -9
  7. package/dist/ag-ui/index.js.map +1 -1
  8. package/dist/{catalog-CrzetM3_.d.cts → catalog-BKS3Yxo6.d.cts} +2 -0
  9. package/dist/{catalog-CrzetM3_.d.ts → catalog-BKS3Yxo6.d.ts} +2 -0
  10. package/dist/genui/builtins.cjs +2 -0
  11. package/dist/genui/builtins.cjs.map +1 -1
  12. package/dist/genui/builtins.d.cts +1 -1
  13. package/dist/genui/builtins.d.ts +1 -1
  14. package/dist/genui/builtins.js +2 -0
  15. package/dist/genui/builtins.js.map +1 -1
  16. package/dist/genui/index.cjs +780 -106
  17. package/dist/genui/index.cjs.map +1 -1
  18. package/dist/genui/index.d.cts +5 -5
  19. package/dist/genui/index.d.ts +5 -5
  20. package/dist/genui/index.js +770 -105
  21. package/dist/genui/index.js.map +1 -1
  22. package/dist/guardrails/index.cjs +3 -1
  23. package/dist/guardrails/index.cjs.map +1 -1
  24. package/dist/guardrails/index.d.cts +5 -4
  25. package/dist/guardrails/index.d.ts +5 -4
  26. package/dist/guardrails/index.js +3 -1
  27. package/dist/guardrails/index.js.map +1 -1
  28. package/dist/index.cjs +3324 -380
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +388 -96
  31. package/dist/index.d.ts +388 -96
  32. package/dist/index.js +3279 -377
  33. package/dist/index.js.map +1 -1
  34. package/dist/{processors-6p9nrqEi.d.cts → processors-BXM-MNga.d.cts} +1 -1
  35. package/dist/{processors-Bp5XmF6V.d.ts → processors-ByDr5POu.d.ts} +1 -1
  36. package/dist/{stream-events-CbVEowYb.d.cts → stream-events-BGKLD6RX.d.ts} +664 -86
  37. package/dist/{stream-events-CbVEowYb.d.ts → stream-events-CF-6-Y6L.d.cts} +664 -86
  38. package/package.json +1 -1
  39. package/dist/tool-DZYLEKnl.d.cts +0 -121
  40. package/dist/tool-_iq4xRyk.d.ts +0 -121
package/dist/index.js CHANGED
@@ -169,16 +169,16 @@ function observeTurnFrames(writer, now = Date.now) {
169
169
  };
170
170
  }
171
171
  __name(observeTurnFrames, "observeTurnFrames");
172
- function withTurnFrames(result, frames) {
172
+ function withTurnFrames(result2, frames) {
173
173
  return {
174
- ...result,
175
- ...result.reasoning === void 0 && frames.reasoning !== void 0 ? {
174
+ ...result2,
175
+ ...result2.reasoning === void 0 && frames.reasoning !== void 0 ? {
176
176
  reasoning: frames.reasoning
177
177
  } : {},
178
- ...result.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
178
+ ...result2.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
179
179
  reasoningMs: frames.reasoningMs
180
180
  } : {},
181
- ...result.ui === void 0 && frames.ui !== void 0 ? {
181
+ ...result2.ui === void 0 && frames.ui !== void 0 ? {
182
182
  ui: frames.ui
183
183
  } : {}
184
184
  };
@@ -187,8 +187,9 @@ __name(withTurnFrames, "withTurnFrames");
187
187
 
188
188
  // src/tool-ui.ts
189
189
  var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
190
- function wrapToolStepOutput(output, ui) {
191
- if (ui.length === 0) {
190
+ function wrapToolStepOutput(output, ui, text) {
191
+ const reserved = typeof output === "object" && output !== null && TOOL_STEP_UI in output;
192
+ if (ui.length === 0 && !reserved && !text) {
192
193
  return output;
193
194
  }
194
195
  const wrapped = {
@@ -196,17 +197,36 @@ function wrapToolStepOutput(output, ui) {
196
197
  output,
197
198
  ui: [
198
199
  ...ui
199
- ]
200
+ ],
201
+ ...text ? {
202
+ text
203
+ } : {}
200
204
  };
201
205
  return wrapped;
202
206
  }
203
207
  __name(wrapToolStepOutput, "wrapToolStepOutput");
208
+ function wrapToolPreflightDenied(reason) {
209
+ const wrapped = {
210
+ [TOOL_STEP_UI]: 1,
211
+ output: null,
212
+ ui: [],
213
+ preflightDenied: reason
214
+ };
215
+ return wrapped;
216
+ }
217
+ __name(wrapToolPreflightDenied, "wrapToolPreflightDenied");
204
218
  function unwrapToolStepOutput(raw) {
205
219
  if (typeof raw === "object" && raw !== null && raw[TOOL_STEP_UI] === 1) {
206
220
  const wrapped = raw;
207
221
  return {
208
222
  output: wrapped.output,
209
- ui: Array.isArray(wrapped.ui) ? wrapped.ui : []
223
+ ui: Array.isArray(wrapped.ui) ? wrapped.ui : [],
224
+ ...typeof wrapped.text === "string" ? {
225
+ text: wrapped.text
226
+ } : {},
227
+ ...typeof wrapped.preflightDenied === "string" ? {
228
+ preflightDenied: wrapped.preflightDenied
229
+ } : {}
210
230
  };
211
231
  }
212
232
  return {
@@ -255,6 +275,14 @@ function createUiCollector(toolCallId, write) {
255
275
  ...options.version !== void 0 ? {
256
276
  version: options.version
257
277
  } : {},
278
+ ...options.fallbackText !== void 0 ? {
279
+ fallbackText: options.fallbackText
280
+ } : {},
281
+ ...options.componentVersions !== void 0 ? {
282
+ componentVersions: {
283
+ ...options.componentVersions
284
+ }
285
+ } : {},
258
286
  toolCallId
259
287
  };
260
288
  pushed.set(id, entry);
@@ -289,6 +317,10 @@ function mergeUi(...lists) {
289
317
  ];
290
318
  }
291
319
  __name(mergeUi, "mergeUi");
320
+ function escapeUnsafeToolUiText(text) {
321
+ return text.replaceAll("\0", "\\u0000").replace(/[\uD800-\uDFFF]/gu, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
322
+ }
323
+ __name(escapeUnsafeToolUiText, "escapeUnsafeToolUiText");
292
324
 
293
325
  // src/tool-presentation.ts
294
326
  var ALL_AGENTS = "*";
@@ -496,31 +528,37 @@ async function mayDecideApproval(policy, actor, decision) {
496
528
  return defaultCanDecide(actor, decision);
497
529
  }
498
530
  __name(mayDecideApproval, "mayDecideApproval");
499
- function toolCallApprovalFromRow(row) {
500
- if (row.approver === null || row.approver === void 0) {
531
+ function toolCallApprovalFromRow(row2) {
532
+ if (row2.approver === null || row2.approver === void 0) {
501
533
  return null;
502
534
  }
503
- const status = row.status === "pending_approval" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
504
- const expiresAt = row.expiresAt instanceof Date ? row.expiresAt.toISOString() : typeof row.expiresAt === "string" ? row.expiresAt : void 0;
535
+ const status = row2.status === "pending_approval" || row2.status === "proposed" ? "pending" : row2.status === "rejected" ? "rejected" : row2.status === "expired" ? "expired" : "approved";
536
+ const expiresAt = row2.expiresAt instanceof Date ? row2.expiresAt.toISOString() : typeof row2.expiresAt === "string" ? row2.expiresAt : void 0;
505
537
  const decided = status === "approved" || status === "rejected";
506
538
  return {
507
- toolCallId: row.toolCallId,
508
- approver: row.approver,
539
+ toolCallId: row2.toolCallId,
540
+ ...row2.proposalId != null ? {
541
+ proposalId: row2.proposalId
542
+ } : {},
543
+ approver: row2.approver,
544
+ ...row2.confirmation != null ? {
545
+ confirmation: row2.confirmation
546
+ } : {},
509
547
  status,
510
548
  ...expiresAt !== void 0 ? {
511
549
  expiresAt
512
550
  } : {},
513
- ...row.remember === true ? {
551
+ ...row2.remember === true ? {
514
552
  remember: true
515
553
  } : {},
516
- ...decided && typeof row.executedByRef === "string" ? {
517
- decidedBy: row.executedByRef
554
+ ...decided && typeof row2.executedByRef === "string" ? {
555
+ decidedBy: row2.executedByRef
518
556
  } : {},
519
- ...decided && typeof row.decidedVia === "string" ? {
520
- decidedVia: row.decidedVia
557
+ ...decided && typeof row2.decidedVia === "string" ? {
558
+ decidedVia: row2.decidedVia
521
559
  } : {},
522
- ...status === "rejected" && typeof row.error === "string" ? {
523
- reason: row.error
560
+ ...status === "rejected" && typeof row2.error === "string" ? {
561
+ reason: row2.error
524
562
  } : {}
525
563
  };
526
564
  }
@@ -537,28 +575,28 @@ function estimateCost(usage, price) {
537
575
  return uncachedInputTokens / 1e6 * price.inputPricePer1m + cacheWriteTokens / 1e6 * (price.cacheWritePricePer1m ?? price.inputPricePer1m) + cacheReadTokens / 1e6 * (price.cacheReadPricePer1m ?? price.inputPricePer1m) + usage.outputTokens / 1e6 * price.outputPricePer1m;
538
576
  }
539
577
  __name(estimateCost, "estimateCost");
540
- function rowCost(row, prices) {
541
- return row.costUsd ?? estimateCost(row, prices.get(row.modelId));
578
+ function rowCost(row2, prices) {
579
+ return row2.costUsd ?? estimateCost(row2, prices.get(row2.modelId));
542
580
  }
543
581
  __name(rowCost, "rowCost");
544
582
  function bucketByModel(rows, prices) {
545
583
  const byModel = /* @__PURE__ */ new Map();
546
- for (const row of rows) {
547
- const bucket = byModel.get(row.modelId) ?? {
584
+ for (const row2 of rows) {
585
+ const bucket = byModel.get(row2.modelId) ?? {
548
586
  requests: 0,
549
587
  inputTokens: 0,
550
588
  outputTokens: 0,
551
589
  costUsd: 0
552
590
  };
553
591
  bucket.requests += 1;
554
- bucket.inputTokens += row.inputTokens;
555
- bucket.outputTokens += row.outputTokens;
556
- bucket.costUsd += rowCost(row, prices);
557
- byModel.set(row.modelId, bucket);
592
+ bucket.inputTokens += row2.inputTokens;
593
+ bucket.outputTokens += row2.outputTokens;
594
+ bucket.costUsd += rowCost(row2, prices);
595
+ byModel.set(row2.modelId, bucket);
558
596
  }
559
- const result = [];
597
+ const result2 = [];
560
598
  for (const [modelId, bucket] of byModel) {
561
- result.push({
599
+ result2.push({
562
600
  modelId,
563
601
  requests: bucket.requests,
564
602
  inputTokens: bucket.inputTokens,
@@ -566,28 +604,28 @@ function bucketByModel(rows, prices) {
566
604
  costUsd: bucket.costUsd
567
605
  });
568
606
  }
569
- result.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
570
- return result;
607
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
608
+ return result2;
571
609
  }
572
610
  __name(bucketByModel, "bucketByModel");
573
611
  function bucketByActor(rows, prices) {
574
612
  const byActor = /* @__PURE__ */ new Map();
575
- for (const row of rows) {
576
- const bucket = byActor.get(row.actorRef) ?? {
613
+ for (const row2 of rows) {
614
+ const bucket = byActor.get(row2.actorRef) ?? {
577
615
  requests: 0,
578
616
  totalTokens: 0,
579
617
  costUsd: 0,
580
618
  threadIds: /* @__PURE__ */ new Set()
581
619
  };
582
620
  bucket.requests += 1;
583
- bucket.totalTokens += row.inputTokens + row.outputTokens;
584
- bucket.costUsd += rowCost(row, prices);
585
- bucket.threadIds.add(row.threadId);
586
- byActor.set(row.actorRef, bucket);
621
+ bucket.totalTokens += row2.inputTokens + row2.outputTokens;
622
+ bucket.costUsd += rowCost(row2, prices);
623
+ bucket.threadIds.add(row2.threadId);
624
+ byActor.set(row2.actorRef, bucket);
587
625
  }
588
- const result = [];
626
+ const result2 = [];
589
627
  for (const [actorRef, bucket] of byActor) {
590
- result.push({
628
+ result2.push({
591
629
  actorRef,
592
630
  requests: bucket.requests,
593
631
  totalTokens: bucket.totalTokens,
@@ -595,30 +633,30 @@ function bucketByActor(rows, prices) {
595
633
  threadCount: bucket.threadIds.size
596
634
  });
597
635
  }
598
- result.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
599
- return result;
636
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
637
+ return result2;
600
638
  }
601
639
  __name(bucketByActor, "bucketByActor");
602
640
  function bucketByThread(rows, prices, threads, options) {
603
641
  const byThread = /* @__PURE__ */ new Map();
604
- for (const row of rows) {
605
- const bucket = byThread.get(row.threadId) ?? {
642
+ for (const row2 of rows) {
643
+ const bucket = byThread.get(row2.threadId) ?? {
606
644
  requests: 0,
607
645
  totalTokens: 0,
608
646
  costUsd: 0
609
647
  };
610
648
  bucket.requests += 1;
611
- bucket.totalTokens += row.inputTokens + row.outputTokens;
612
- bucket.costUsd += rowCost(row, prices);
613
- byThread.set(row.threadId, bucket);
649
+ bucket.totalTokens += row2.inputTokens + row2.outputTokens;
650
+ bucket.costUsd += rowCost(row2, prices);
651
+ byThread.set(row2.threadId, bucket);
614
652
  }
615
- const result = [];
653
+ const result2 = [];
616
654
  for (const [threadId, bucket] of byThread) {
617
655
  const thread = threads.get(threadId);
618
656
  if (thread === void 0 && !options.includeUnknownThreads) {
619
657
  continue;
620
658
  }
621
- result.push({
659
+ result2.push({
622
660
  threadId,
623
661
  title: thread?.title ?? "",
624
662
  actorRef: thread?.actorRef ?? "",
@@ -627,8 +665,8 @@ function bucketByThread(rows, prices, threads, options) {
627
665
  costUsd: bucket.costUsd
628
666
  });
629
667
  }
630
- result.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
631
- return result.slice(0, options.limit);
668
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
669
+ return result2.slice(0, options.limit);
632
670
  }
633
671
  __name(bucketByThread, "bucketByThread");
634
672
  function rollupThreadUsage(rows, prices) {
@@ -639,37 +677,37 @@ function rollupThreadUsage(rows, prices) {
639
677
  totalTokens: 0,
640
678
  costUsd: 0
641
679
  };
642
- for (const row of rows) {
680
+ for (const row2 of rows) {
643
681
  rollup.requests += 1;
644
- rollup.inputTokens += row.inputTokens;
645
- rollup.outputTokens += row.outputTokens;
646
- rollup.totalTokens += row.inputTokens + row.outputTokens;
647
- rollup.costUsd += rowCost(row, prices);
682
+ rollup.inputTokens += row2.inputTokens;
683
+ rollup.outputTokens += row2.outputTokens;
684
+ rollup.totalTokens += row2.inputTokens + row2.outputTokens;
685
+ rollup.costUsd += rowCost(row2, prices);
648
686
  }
649
687
  return rollup;
650
688
  }
651
689
  __name(rollupThreadUsage, "rollupThreadUsage");
652
690
  function bucketUsageTrend(rows, prices) {
653
691
  const byDay = /* @__PURE__ */ new Map();
654
- for (const row of rows) {
655
- const bucket = byDay.get(row.day) ?? {
692
+ for (const row2 of rows) {
693
+ const bucket = byDay.get(row2.day) ?? {
656
694
  totalTokens: 0,
657
695
  costUsd: 0
658
696
  };
659
- bucket.totalTokens += row.inputTokens + row.outputTokens;
660
- bucket.costUsd += rowCost(row, prices);
661
- byDay.set(row.day, bucket);
697
+ bucket.totalTokens += row2.inputTokens + row2.outputTokens;
698
+ bucket.costUsd += rowCost(row2, prices);
699
+ byDay.set(row2.day, bucket);
662
700
  }
663
- const result = [];
701
+ const result2 = [];
664
702
  for (const [day, bucket] of byDay) {
665
- result.push({
703
+ result2.push({
666
704
  day,
667
705
  totalTokens: bucket.totalTokens,
668
706
  costUsd: bucket.costUsd
669
707
  });
670
708
  }
671
- result.sort((left, right) => left.day.localeCompare(right.day));
672
- return result;
709
+ result2.sort((left, right) => left.day.localeCompare(right.day));
710
+ return result2;
673
711
  }
674
712
  __name(bucketUsageTrend, "bucketUsageTrend");
675
713
  function dayBoundsUtc(range) {
@@ -694,7 +732,7 @@ async function filterToolsByEnabled(entries) {
694
732
  entry,
695
733
  enabled: await isToolEnabled(entry.spec, entry.handler)
696
734
  })));
697
- return checked.filter((row) => row.enabled).map((row) => row.entry);
735
+ return checked.filter((row2) => row2.enabled).map((row2) => row2.entry);
698
736
  }
699
737
  __name(filterToolsByEnabled, "filterToolsByEnabled");
700
738
  async function canActorUseTool(actor, handler) {
@@ -706,7 +744,7 @@ async function filterToolsByCanUse(entries, actor) {
706
744
  entry,
707
745
  allowed: await canActorUseTool(actor, entry.handler)
708
746
  })));
709
- return checked.filter((row) => row.allowed).map((row) => row.entry);
747
+ return checked.filter((row2) => row2.allowed).map((row2) => row2.entry);
710
748
  }
711
749
  __name(filterToolsByCanUse, "filterToolsByCanUse");
712
750
  async function filterToolsByRole(tools, actor, policy) {
@@ -1113,13 +1151,13 @@ async function validateStructured(schema, text, reported) {
1113
1151
  issues: []
1114
1152
  };
1115
1153
  }
1116
- const result = await schema["~standard"].validate(candidate);
1117
- return result.issues !== void 0 ? {
1154
+ const result2 = await schema["~standard"].validate(candidate);
1155
+ return result2.issues !== void 0 ? {
1118
1156
  ok: false,
1119
- issues: result.issues
1157
+ issues: result2.issues
1120
1158
  } : {
1121
1159
  ok: true,
1122
- value: result.value
1160
+ value: result2.value
1123
1161
  };
1124
1162
  }
1125
1163
  __name(validateStructured, "validateStructured");
@@ -2298,12 +2336,12 @@ function memoryWriteVerdict(request) {
2298
2336
  };
2299
2337
  }
2300
2338
  __name(memoryWriteVerdict, "memoryWriteVerdict");
2301
- function memoryForgetVerdict({ record, actor }) {
2302
- return record.scope === actorScope(actor) ? {
2339
+ function memoryForgetVerdict({ record: record2, actor }) {
2340
+ return record2.scope === actorScope(actor) ? {
2303
2341
  allowed: true
2304
2342
  } : {
2305
2343
  allowed: false,
2306
- reason: `"${record.scope}" is not this actor's own scope; deleting it is an administrative action`
2344
+ reason: `"${record2.scope}" is not this actor's own scope; deleting it is an administrative action`
2307
2345
  };
2308
2346
  }
2309
2347
  __name(memoryForgetVerdict, "memoryForgetVerdict");
@@ -2346,7 +2384,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2346
2384
  error: verdict.reason
2347
2385
  };
2348
2386
  }
2349
- const record = await write({
2387
+ const record2 = await write({
2350
2388
  key,
2351
2389
  text,
2352
2390
  scope,
@@ -2360,7 +2398,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2360
2398
  });
2361
2399
  return {
2362
2400
  ok: true,
2363
- record
2401
+ record: record2
2364
2402
  };
2365
2403
  }
2366
2404
  __name(writeMemory, "writeMemory");
@@ -2412,7 +2450,7 @@ function danglingToolCallIds(messages) {
2412
2450
  if (message.role !== "assistant" || calls.length === 0) {
2413
2451
  continue;
2414
2452
  }
2415
- const settled = new Set((message.toolResults ?? []).map((result) => result.id));
2453
+ const settled = new Set((message.toolResults ?? []).map((result2) => result2.id));
2416
2454
  for (const call of calls) {
2417
2455
  if (!settled.has(call.id)) {
2418
2456
  ids.push(call.id);
@@ -2480,7 +2518,7 @@ function settleDanglingToolCalls(messages, outcomes = []) {
2480
2518
  return message;
2481
2519
  }
2482
2520
  const results = message.toolResults ?? [];
2483
- const settled = new Set(results.map((result) => result.id));
2521
+ const settled = new Set(results.map((result2) => result2.id));
2484
2522
  const missing = calls.filter((call) => !settled.has(call.id));
2485
2523
  if (missing.length === 0) {
2486
2524
  return message;
@@ -2588,6 +2626,1665 @@ async function settleUnsettledDelegation(args) {
2588
2626
  }
2589
2627
  __name(settleUnsettledDelegation, "settleUnsettledDelegation");
2590
2628
 
2629
+ // src/action-proposal-discovery.ts
2630
+ function validateActionProposalWorkerClaim(command, now) {
2631
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
2632
+ if (typeof command.workerId !== "string" || command.workerId.length === 0) throw new TypeError("workerId must be a nonempty string");
2633
+ if (!Number.isSafeInteger(command.leaseMs) || command.leaseMs <= 0 || !Number.isSafeInteger(now + command.leaseMs)) throw new RangeError("leaseMs must be positive safe integer milliseconds");
2634
+ }
2635
+ __name(validateActionProposalWorkerClaim, "validateActionProposalWorkerClaim");
2636
+ function validateActionProposalExpiryBatch(command, now) {
2637
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid action proposal clock");
2638
+ validateActionProposalDiscoveryIndexBatch(command);
2639
+ }
2640
+ __name(validateActionProposalExpiryBatch, "validateActionProposalExpiryBatch");
2641
+ function validateActionProposalDiscoveryIndexBatch(command) {
2642
+ if (!Number.isInteger(command.limit) || command.limit < 1 || command.limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
2643
+ }
2644
+ __name(validateActionProposalDiscoveryIndexBatch, "validateActionProposalDiscoveryIndexBatch");
2645
+
2646
+ // src/action-proposal-outcome.ts
2647
+ function attachActionProposalOutcome(row2, now, ui = [], text) {
2648
+ if (row2.outcome) return row2;
2649
+ const execution = row2.execution;
2650
+ const outcome = {
2651
+ id: `${row2.id}:outcome:1`,
2652
+ proposalId: row2.id,
2653
+ outcomeVersion: 1,
2654
+ tenantRef: row2.tenantRef,
2655
+ actorRef: row2.actorRef,
2656
+ threadId: row2.threadId,
2657
+ originRunId: row2.originRunId,
2658
+ originToolCallId: row2.originToolCallId,
2659
+ toolName: row2.toolName,
2660
+ decision: row2.decision,
2661
+ ui,
2662
+ createdAt: now,
2663
+ ...text !== void 0 ? {
2664
+ text
2665
+ } : {},
2666
+ ...execution?.status === "succeeded" || execution?.status === "failed" ? {
2667
+ executionStatus: execution.status
2668
+ } : {},
2669
+ ...execution && Object.hasOwn(execution, "result") ? {
2670
+ result: execution.result
2671
+ } : {},
2672
+ ...execution?.error !== void 0 ? {
2673
+ error: execution.error
2674
+ } : row2.decisionAudit?.reason !== void 0 ? {
2675
+ error: row2.decisionAudit.reason
2676
+ } : {}
2677
+ };
2678
+ return snapshotActionProposal({
2679
+ ...row2,
2680
+ outcome,
2681
+ outcomeDelivery: {
2682
+ status: "pending",
2683
+ generation: 0,
2684
+ lease: null
2685
+ }
2686
+ });
2687
+ }
2688
+ __name(attachActionProposalOutcome, "attachActionProposalOutcome");
2689
+ function claimActionProposalOutcome(row2, command, now, token) {
2690
+ validateActionProposalWorkerClaim(command, now);
2691
+ const delivery = row2.outcomeDelivery;
2692
+ if (!row2.outcome || !delivery || delivery.status !== "pending" || delivery.lease && now < delivery.lease.expiresAt) return null;
2693
+ const generation = delivery.generation + 1;
2694
+ return snapshotActionProposal({
2695
+ ...row2,
2696
+ outcomeDelivery: {
2697
+ ...delivery,
2698
+ generation,
2699
+ lease: {
2700
+ token,
2701
+ generation,
2702
+ workerId: command.workerId,
2703
+ expiresAt: now + command.leaseMs
2704
+ }
2705
+ }
2706
+ });
2707
+ }
2708
+ __name(claimActionProposalOutcome, "claimActionProposalOutcome");
2709
+ function actionProposalOutcomeFenceValid(row2, command, now) {
2710
+ if (!Number.isSafeInteger(now)) throw new RangeError("Invalid server clock");
2711
+ 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");
2712
+ const delivery = row2.outcomeDelivery;
2713
+ return row2.outcome?.id === command.outcomeId && delivery?.status === "pending" && delivery.generation === command.generation && delivery.lease?.token === command.token && delivery.lease.generation === command.generation && now < delivery.lease.expiresAt;
2714
+ }
2715
+ __name(actionProposalOutcomeFenceValid, "actionProposalOutcomeFenceValid");
2716
+ function outcomeSummary(outcome) {
2717
+ 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)}` : ""}`;
2718
+ if (outcome.executionStatus === "failed") return `Action ${JSON.stringify(outcome.toolName)} failed (proposal ${JSON.stringify(outcome.proposalId)}): ${JSON.stringify(outcome.error ?? "Execution failed")}`;
2719
+ 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)}` : ""}`;
2720
+ }
2721
+ __name(outcomeSummary, "outcomeSummary");
2722
+ function actionProposalOutcomeScope(outcome) {
2723
+ return {
2724
+ tenantRef: outcome.tenantRef,
2725
+ actorRef: outcome.actorRef,
2726
+ threadId: outcome.threadId
2727
+ };
2728
+ }
2729
+ __name(actionProposalOutcomeScope, "actionProposalOutcomeScope");
2730
+ function actionProposalOutcomeText(outcome) {
2731
+ return outcomeSummary(outcome) + (outcome.text ? ` UI: ${JSON.stringify(outcome.text)}` : "");
2732
+ }
2733
+ __name(actionProposalOutcomeText, "actionProposalOutcomeText");
2734
+
2735
+ // src/genui/schema.ts
2736
+ function isStandardSchema(schema) {
2737
+ return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"]?.validate === "function";
2738
+ }
2739
+ __name(isStandardSchema, "isStandardSchema");
2740
+ function toJsonSchema(schema) {
2741
+ if (!isStandardSchema(schema)) {
2742
+ return schema;
2743
+ }
2744
+ const standard = schema["~standard"];
2745
+ const converter = standard.jsonSchema;
2746
+ if (converter === void 0 || typeof converter.input !== "function") {
2747
+ return void 0;
2748
+ }
2749
+ try {
2750
+ return converter.input({
2751
+ target: "draft-07"
2752
+ });
2753
+ } catch {
2754
+ return void 0;
2755
+ }
2756
+ }
2757
+ __name(toJsonSchema, "toJsonSchema");
2758
+ async function validateProps(schema, value, validator) {
2759
+ if (isStandardSchema(schema)) {
2760
+ const result2 = await schema["~standard"].validate(value);
2761
+ if (result2.issues !== void 0) {
2762
+ return {
2763
+ ok: false,
2764
+ issues: toGenuiIssues(result2.issues)
2765
+ };
2766
+ }
2767
+ return {
2768
+ ok: true,
2769
+ value: result2.value
2770
+ };
2771
+ }
2772
+ const issues = validator.validate(schema, value);
2773
+ return issues.length > 0 ? {
2774
+ ok: false,
2775
+ issues
2776
+ } : {
2777
+ ok: true,
2778
+ value
2779
+ };
2780
+ }
2781
+ __name(validateProps, "validateProps");
2782
+ function validatePropsSync(schema, value, validator) {
2783
+ if (!isStandardSchema(schema)) {
2784
+ const issues = validator.validate(schema, value);
2785
+ return issues.length > 0 ? {
2786
+ ok: false,
2787
+ issues
2788
+ } : {
2789
+ ok: true,
2790
+ value
2791
+ };
2792
+ }
2793
+ const result2 = schema["~standard"].validate(value);
2794
+ if (result2 instanceof Promise) {
2795
+ result2.catch(() => void 0);
2796
+ return void 0;
2797
+ }
2798
+ return result2.issues !== void 0 ? {
2799
+ ok: false,
2800
+ issues: toGenuiIssues(result2.issues)
2801
+ } : {
2802
+ ok: true,
2803
+ value: result2.value
2804
+ };
2805
+ }
2806
+ __name(validatePropsSync, "validatePropsSync");
2807
+ function toGenuiIssues(issues) {
2808
+ return issues.map((issue4) => ({
2809
+ path: (issue4.path ?? []).map((segment) => typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment),
2810
+ message: issue4.message
2811
+ }));
2812
+ }
2813
+ __name(toGenuiIssues, "toGenuiIssues");
2814
+ function formatIssues(issues) {
2815
+ return issues.map((issue4) => `${issue4.path.length > 0 ? formatPath(issue4.path) : "(root)"}: ${issue4.message}`).join("; ");
2816
+ }
2817
+ __name(formatIssues, "formatIssues");
2818
+ function formatPath(path) {
2819
+ return path.map((segment, index) => typeof segment === "number" ? `[${segment}]` : index === 0 ? segment : `.${segment}`).join("");
2820
+ }
2821
+ __name(formatPath, "formatPath");
2822
+ var builtinJsonSchemaValidator = {
2823
+ validate(schema, value) {
2824
+ const issues = [];
2825
+ check(schema, value, [], schema, issues, 0);
2826
+ return issues;
2827
+ }
2828
+ };
2829
+ var MAX_DEPTH = 64;
2830
+ function typeOf(value) {
2831
+ if (value === null) return "null";
2832
+ if (Array.isArray(value)) return "array";
2833
+ return typeof value;
2834
+ }
2835
+ __name(typeOf, "typeOf");
2836
+ function matchesType(type, value) {
2837
+ switch (type) {
2838
+ case "integer":
2839
+ return typeof value === "number" && Number.isInteger(value);
2840
+ case "number":
2841
+ return typeof value === "number" && Number.isFinite(value);
2842
+ case "object":
2843
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2844
+ default:
2845
+ return typeOf(value) === type;
2846
+ }
2847
+ }
2848
+ __name(matchesType, "matchesType");
2849
+ function resolveRef(root, ref) {
2850
+ if (ref === "#") return root;
2851
+ if (!ref.startsWith("#/")) return void 0;
2852
+ let current = root;
2853
+ for (const raw of ref.slice(2).split("/")) {
2854
+ const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
2855
+ if (typeof current !== "object" || current === null) return void 0;
2856
+ current = current[key];
2857
+ }
2858
+ return typeof current === "object" && current !== null ? current : void 0;
2859
+ }
2860
+ __name(resolveRef, "resolveRef");
2861
+ function deepEqual(a, b) {
2862
+ if (a === b) return true;
2863
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
2864
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2865
+ const keysA = Object.keys(a);
2866
+ const keysB = Object.keys(b);
2867
+ if (keysA.length !== keysB.length) return false;
2868
+ return keysA.every((key) => deepEqual(a[key], b[key]));
2869
+ }
2870
+ __name(deepEqual, "deepEqual");
2871
+ function check(schema, value, path, root, issues, depth) {
2872
+ if (schema === true || schema === void 0) return;
2873
+ if (schema === false) {
2874
+ issues.push({
2875
+ path,
2876
+ message: "is not allowed"
2877
+ });
2878
+ return;
2879
+ }
2880
+ if (typeof schema !== "object" || schema === null) return;
2881
+ if (depth > MAX_DEPTH) {
2882
+ issues.push({
2883
+ path,
2884
+ message: "is nested too deeply"
2885
+ });
2886
+ return;
2887
+ }
2888
+ const s2 = schema;
2889
+ if (typeof s2.$ref === "string") {
2890
+ const target = resolveRef(root, s2.$ref);
2891
+ if (target === void 0) {
2892
+ issues.push({
2893
+ path,
2894
+ message: `references unknown schema ${s2.$ref}`
2895
+ });
2896
+ return;
2897
+ }
2898
+ check(target, value, path, root, issues, depth + 1);
2899
+ }
2900
+ if (s2.type !== void 0) {
2901
+ const types = Array.isArray(s2.type) ? s2.type : [
2902
+ s2.type
2903
+ ];
2904
+ if (!types.some((type) => matchesType(type, value))) {
2905
+ issues.push({
2906
+ path,
2907
+ message: `must be ${types.join(" or ")}`
2908
+ });
2909
+ return;
2910
+ }
2911
+ }
2912
+ if (Array.isArray(s2.enum) && !s2.enum.some((option) => deepEqual(option, value))) {
2913
+ issues.push({
2914
+ path,
2915
+ message: `must be one of ${s2.enum.map((option) => JSON.stringify(option)).join(", ")}`
2916
+ });
2917
+ }
2918
+ if ("const" in s2 && !deepEqual(s2.const, value)) {
2919
+ issues.push({
2920
+ path,
2921
+ message: `must be ${JSON.stringify(s2.const)}`
2922
+ });
2923
+ }
2924
+ if (typeof value === "string") {
2925
+ if (typeof s2.minLength === "number" && value.length < s2.minLength) {
2926
+ issues.push({
2927
+ path,
2928
+ message: `must be at least ${s2.minLength} characters`
2929
+ });
2930
+ }
2931
+ if (typeof s2.maxLength === "number" && value.length > s2.maxLength) {
2932
+ issues.push({
2933
+ path,
2934
+ message: `must be at most ${s2.maxLength} characters`
2935
+ });
2936
+ }
2937
+ if (typeof s2.pattern === "string") {
2938
+ let pattern;
2939
+ try {
2940
+ pattern = new RegExp(s2.pattern, "u");
2941
+ } catch {
2942
+ pattern = void 0;
2943
+ }
2944
+ if (pattern !== void 0 && !pattern.test(value)) {
2945
+ issues.push({
2946
+ path,
2947
+ message: `must match ${s2.pattern}`
2948
+ });
2949
+ }
2950
+ }
2951
+ }
2952
+ if (typeof value === "number") {
2953
+ if (typeof s2.minimum === "number" && value < s2.minimum) {
2954
+ issues.push({
2955
+ path,
2956
+ message: `must be >= ${s2.minimum}`
2957
+ });
2958
+ }
2959
+ if (typeof s2.maximum === "number" && value > s2.maximum) {
2960
+ issues.push({
2961
+ path,
2962
+ message: `must be <= ${s2.maximum}`
2963
+ });
2964
+ }
2965
+ }
2966
+ if (Array.isArray(value)) {
2967
+ if (typeof s2.minItems === "number" && value.length < s2.minItems) {
2968
+ issues.push({
2969
+ path,
2970
+ message: `must have at least ${s2.minItems} items`
2971
+ });
2972
+ }
2973
+ if (typeof s2.maxItems === "number" && value.length > s2.maxItems) {
2974
+ issues.push({
2975
+ path,
2976
+ message: `must have at most ${s2.maxItems} items`
2977
+ });
2978
+ }
2979
+ if (s2.items !== void 0 && !Array.isArray(s2.items)) {
2980
+ value.forEach((item, index) => {
2981
+ check(s2.items, item, [
2982
+ ...path,
2983
+ index
2984
+ ], root, issues, depth + 1);
2985
+ });
2986
+ }
2987
+ }
2988
+ if (matchesType("object", value)) {
2989
+ const record2 = value;
2990
+ const properties = typeof s2.properties === "object" && s2.properties !== null ? s2.properties : {};
2991
+ if (Array.isArray(s2.required)) {
2992
+ for (const key of s2.required) {
2993
+ if (record2[key] === void 0) {
2994
+ issues.push({
2995
+ path: [
2996
+ ...path,
2997
+ key
2998
+ ],
2999
+ message: "is required"
3000
+ });
3001
+ }
3002
+ }
3003
+ }
3004
+ for (const [key, entry] of Object.entries(record2)) {
3005
+ if (key in properties) {
3006
+ if (entry !== void 0) {
3007
+ check(properties[key], entry, [
3008
+ ...path,
3009
+ key
3010
+ ], root, issues, depth + 1);
3011
+ }
3012
+ } else if (s2.additionalProperties === false) {
3013
+ issues.push({
3014
+ path: [
3015
+ ...path,
3016
+ key
3017
+ ],
3018
+ message: "is not a known property"
3019
+ });
3020
+ } else if (typeof s2.additionalProperties === "object" && s2.additionalProperties !== null) {
3021
+ check(s2.additionalProperties, entry, [
3022
+ ...path,
3023
+ key
3024
+ ], root, issues, depth + 1);
3025
+ }
3026
+ }
3027
+ }
3028
+ if (Array.isArray(s2.allOf)) {
3029
+ for (const sub of s2.allOf) check(sub, value, path, root, issues, depth + 1);
3030
+ }
3031
+ if (Array.isArray(s2.anyOf) || Array.isArray(s2.oneOf)) {
3032
+ const options = Array.isArray(s2.anyOf) ? s2.anyOf : s2.oneOf;
3033
+ const passing = options.filter((sub) => {
3034
+ const nested = [];
3035
+ check(sub, value, path, root, nested, depth + 1);
3036
+ return nested.length === 0;
3037
+ }).length;
3038
+ if (Array.isArray(s2.anyOf) ? passing === 0 : passing !== 1) {
3039
+ issues.push({
3040
+ path,
3041
+ message: Array.isArray(s2.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
3042
+ });
3043
+ }
3044
+ }
3045
+ if (s2.not !== void 0) {
3046
+ const nested = [];
3047
+ check(s2.not, value, path, root, nested, depth + 1);
3048
+ if (nested.length === 0) {
3049
+ issues.push({
3050
+ path,
3051
+ message: "matches a shape that is not allowed"
3052
+ });
3053
+ }
3054
+ }
3055
+ }
3056
+ __name(check, "check");
3057
+
3058
+ // src/genui/catalog.ts
3059
+ var COMPONENT_NAME = /^[A-Za-z][A-Za-z0-9]{0,63}$/;
3060
+ function defineComponent(definition) {
3061
+ if (!COMPONENT_NAME.test(definition.name)) {
3062
+ throw new Error(`genui: component name "${definition.name}" must be letters and digits, starting with a letter (e.g. DataTable)`);
3063
+ }
3064
+ if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
3065
+ if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
3066
+ return definition;
3067
+ }
3068
+ __name(defineComponent, "defineComponent");
3069
+ function defineCatalog(components, options = {}) {
3070
+ const byName = /* @__PURE__ */ new Map();
3071
+ for (const component of components) {
3072
+ defineComponent(component);
3073
+ if (byName.has(component.name)) {
3074
+ throw new Error(`genui: component "${component.name}" is defined twice in one catalog`);
3075
+ }
3076
+ byName.set(component.name, component);
3077
+ }
3078
+ return buildCatalog(byName, options.jsonSchemaValidator ?? builtinJsonSchemaValidator);
3079
+ }
3080
+ __name(defineCatalog, "defineCatalog");
3081
+ function buildCatalog(byName, validator) {
3082
+ const list = Object.freeze([
3083
+ ...byName.values()
3084
+ ]);
3085
+ return {
3086
+ components: list,
3087
+ validator,
3088
+ get: /* @__PURE__ */ __name((name) => byName.get(name), "get"),
3089
+ has: /* @__PURE__ */ __name((name) => byName.has(name), "has"),
3090
+ modelComponents: /* @__PURE__ */ __name(() => list.filter((component) => component.internal !== true), "modelComponents"),
3091
+ async validate(component, props) {
3092
+ const definition = byName.get(component);
3093
+ if (definition === void 0) {
3094
+ return {
3095
+ ok: false,
3096
+ issues: [
3097
+ {
3098
+ path: [],
3099
+ message: `unknown component "${component}"`
3100
+ }
3101
+ ]
3102
+ };
3103
+ }
3104
+ const result2 = await validateProps(definition.outputProps ?? definition.props, props, validator);
3105
+ return result2;
3106
+ },
3107
+ validateSync(component, props) {
3108
+ const definition = byName.get(component);
3109
+ if (definition === void 0) {
3110
+ return {
3111
+ ok: false,
3112
+ issues: [
3113
+ {
3114
+ path: [],
3115
+ message: `unknown component "${component}"`
3116
+ }
3117
+ ]
3118
+ };
3119
+ }
3120
+ return validatePropsSync(definition.outputProps ?? definition.props, props, validator);
3121
+ },
3122
+ jsonSchemaFor(component) {
3123
+ const definition = byName.get(component);
3124
+ return definition === void 0 ? void 0 : toJsonSchema(definition.outputProps ?? definition.props);
3125
+ },
3126
+ extend(more) {
3127
+ const next = new Map(byName);
3128
+ for (const component of more) {
3129
+ defineComponent(component);
3130
+ next.set(component.name, component);
3131
+ }
3132
+ return buildCatalog(next, validator);
3133
+ }
3134
+ };
3135
+ }
3136
+ __name(buildCatalog, "buildCatalog");
3137
+
3138
+ // src/genui/tree.ts
3139
+ var GENUI_TREE_COMPONENT = "genui:tree";
3140
+ async function validateTree(catalog, tree, limits = {}, phase = "output") {
3141
+ const maxDepth = limits.maxDepth ?? 12;
3142
+ const maxNodes = limits.maxNodes ?? 200;
3143
+ const issues = [];
3144
+ let nodes = 0;
3145
+ const visit = /* @__PURE__ */ __name(async (node, path, depth) => {
3146
+ nodes += 1;
3147
+ if (nodes > maxNodes) {
3148
+ if (nodes === maxNodes + 1) {
3149
+ issues.push({
3150
+ path,
3151
+ message: `the tree has more than ${maxNodes} elements`
3152
+ });
3153
+ }
3154
+ return void 0;
3155
+ }
3156
+ if (depth > maxDepth) {
3157
+ issues.push({
3158
+ path,
3159
+ message: `the tree is nested deeper than ${maxDepth} levels`
3160
+ });
3161
+ return void 0;
3162
+ }
3163
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
3164
+ issues.push({
3165
+ path,
3166
+ message: "must be an element object { type, props, children? }"
3167
+ });
3168
+ return void 0;
3169
+ }
3170
+ const element = node;
3171
+ if (typeof element.type !== "string") {
3172
+ issues.push({
3173
+ path: [
3174
+ ...path,
3175
+ "type"
3176
+ ],
3177
+ message: "must be a component name"
3178
+ });
3179
+ return void 0;
3180
+ }
3181
+ const definition = catalog.get(element.type);
3182
+ if (definition === void 0 || definition.internal === true) {
3183
+ issues.push({
3184
+ path: [
3185
+ ...path,
3186
+ "type"
3187
+ ],
3188
+ message: `unknown component "${element.type}" (allowed: ${catalog.modelComponents().map((component) => component.name).join(", ")})`
3189
+ });
3190
+ return void 0;
3191
+ }
3192
+ const validated = phase === "input" ? await validateProps(definition.props, element.props ?? {}, catalog.validator) : await catalog.validate(element.type, element.props ?? {});
3193
+ let props = {};
3194
+ if (!validated.ok) {
3195
+ for (const issue4 of validated.issues) {
3196
+ issues.push({
3197
+ path: [
3198
+ ...path,
3199
+ "props",
3200
+ ...issue4.path
3201
+ ],
3202
+ message: issue4.message
3203
+ });
3204
+ }
3205
+ } else {
3206
+ props = validated.value;
3207
+ }
3208
+ const rawChildren = parseChildrenIfStringified(element.children);
3209
+ if (rawChildren === void 0 || rawChildren === null) {
3210
+ return {
3211
+ type: element.type,
3212
+ props
3213
+ };
3214
+ }
3215
+ if (!Array.isArray(rawChildren)) {
3216
+ issues.push({
3217
+ path: [
3218
+ ...path,
3219
+ "children"
3220
+ ],
3221
+ message: "must be an array of elements (a literal array, not a string)"
3222
+ });
3223
+ return void 0;
3224
+ }
3225
+ if (rawChildren.length > 0 && definition.children !== true) {
3226
+ issues.push({
3227
+ path: [
3228
+ ...path,
3229
+ "children"
3230
+ ],
3231
+ message: `${element.type} does not take children`
3232
+ });
3233
+ return void 0;
3234
+ }
3235
+ const children = [];
3236
+ for (const [index, child] of rawChildren.entries()) {
3237
+ const next = await visit(child, [
3238
+ ...path,
3239
+ "children",
3240
+ index
3241
+ ], depth + 1);
3242
+ if (next !== void 0) {
3243
+ children.push(next);
3244
+ }
3245
+ }
3246
+ return children.length > 0 ? {
3247
+ type: element.type,
3248
+ props,
3249
+ children
3250
+ } : {
3251
+ type: element.type,
3252
+ props
3253
+ };
3254
+ }, "visit");
3255
+ const root = await visit(tree, [], 1);
3256
+ return issues.length > 0 || root === void 0 ? {
3257
+ ok: false,
3258
+ issues: issues.length > 0 ? issues : [
3259
+ {
3260
+ path: [],
3261
+ message: "is invalid"
3262
+ }
3263
+ ]
3264
+ } : {
3265
+ ok: true,
3266
+ value: root
3267
+ };
3268
+ }
3269
+ __name(validateTree, "validateTree");
3270
+ function parseChildrenIfStringified(value) {
3271
+ if (typeof value !== "string") return value;
3272
+ const trimmed = value.trim();
3273
+ if (!trimmed.startsWith("[")) return value;
3274
+ return leadingJsonArray(trimmed) ?? value;
3275
+ }
3276
+ __name(parseChildrenIfStringified, "parseChildrenIfStringified");
3277
+ function leadingJsonArray(text) {
3278
+ let depth = 0;
3279
+ let inString = false;
3280
+ let escaped = false;
3281
+ for (let index = 0; index < text.length; index += 1) {
3282
+ const char = text[index];
3283
+ if (inString) {
3284
+ if (escaped) escaped = false;
3285
+ else if (char === "\\") escaped = true;
3286
+ else if (char === '"') inString = false;
3287
+ continue;
3288
+ }
3289
+ if (char === '"') inString = true;
3290
+ else if (char === "[" || char === "{") depth += 1;
3291
+ else if (char === "]" || char === "}") {
3292
+ depth -= 1;
3293
+ if (depth === 0) {
3294
+ try {
3295
+ const parsed = JSON.parse(text.slice(0, index + 1));
3296
+ return Array.isArray(parsed) ? parsed : void 0;
3297
+ } catch {
3298
+ return void 0;
3299
+ }
3300
+ }
3301
+ }
3302
+ }
3303
+ return void 0;
3304
+ }
3305
+ __name(leadingJsonArray, "leadingJsonArray");
3306
+
3307
+ // src/genui/text.ts
3308
+ function componentToText(catalog, component, props) {
3309
+ if (component === GENUI_TREE_COMPONENT) {
3310
+ const root = props.root;
3311
+ return root === void 0 ? "" : treeToText(catalog, root);
3312
+ }
3313
+ const fallback = catalog.get(component)?.fallbackText;
3314
+ if (fallback !== void 0) {
3315
+ try {
3316
+ const text = fallback(props);
3317
+ if (typeof text === "string" && text.trim().length > 0) return text;
3318
+ } catch {
3319
+ }
3320
+ }
3321
+ return jsonBlock(props);
3322
+ }
3323
+ __name(componentToText, "componentToText");
3324
+ function treeToText(catalog, root) {
3325
+ const out = [];
3326
+ const visit = /* @__PURE__ */ __name((node, depth) => {
3327
+ if (depth > 32) return;
3328
+ const definition = catalog.get(node.type);
3329
+ if (definition?.fallbackText !== void 0) {
3330
+ try {
3331
+ const text = definition.fallbackText(node.props);
3332
+ out.push(typeof text === "string" && text.trim().length > 0 ? text : jsonBlock(node.props));
3333
+ } catch {
3334
+ out.push(jsonBlock(node.props));
3335
+ }
3336
+ } else if (definition?.children !== true) {
3337
+ out.push(jsonBlock(node.props));
3338
+ }
3339
+ for (const child of node.children ?? []) visit(child, depth + 1);
3340
+ }, "visit");
3341
+ visit(root, 0);
3342
+ return out.filter((line) => line.trim().length > 0).join("\n");
3343
+ }
3344
+ __name(treeToText, "treeToText");
3345
+ function jsonBlock(props) {
3346
+ const title = typeof props.title === "string" && props.title.length > 0 ? `*${props.title}*
3347
+ ` : "";
3348
+ return `${title}\`\`\`
3349
+ ${JSON.stringify(props, null, 2)}
3350
+ \`\`\``;
3351
+ }
3352
+ __name(jsonBlock, "jsonBlock");
3353
+
3354
+ // src/genui/capabilities.ts
3355
+ function validateUiCapabilities(value) {
3356
+ if (!record(value) || Object.keys(value).some((key) => key !== "components") || !Array.isArray(value.components)) throw new TypeError("Invalid UI capabilities");
3357
+ const seen = /* @__PURE__ */ new Set();
3358
+ const components = value.components.map((component) => {
3359
+ 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");
3360
+ seen.add(component.name);
3361
+ return {
3362
+ name: component.name,
3363
+ version: Number(component.version)
3364
+ };
3365
+ });
3366
+ return {
3367
+ components
3368
+ };
3369
+ }
3370
+ __name(validateUiCapabilities, "validateUiCapabilities");
3371
+ function record(value) {
3372
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3373
+ }
3374
+ __name(record, "record");
3375
+ function negotiateCatalog(catalog, capabilities) {
3376
+ if (capabilities === void 0) return catalog;
3377
+ const supported = validateUiCapabilities(capabilities).components;
3378
+ return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), {
3379
+ jsonSchemaValidator: catalog.validator
3380
+ });
3381
+ }
3382
+ __name(negotiateCatalog, "negotiateCatalog");
3383
+ async function prepareUiEmission(catalog, capabilities, component, props, version) {
3384
+ const drawable = negotiateCatalog(catalog, capabilities);
3385
+ if (component === GENUI_TREE_COMPONENT) {
3386
+ if (version !== void 0 && version !== 1) throw new Error("Invalid UI tree version");
3387
+ const validated2 = await validateTree(catalog, props.root);
3388
+ if (!validated2.ok) throw new Error(`Invalid UI tree: ${formatIssues(validated2.issues)}`);
3389
+ const supported = /* @__PURE__ */ __name((node) => drawable.has(node.type) && (node.children ?? []).every(supported), "supported");
3390
+ const versions = /* @__PURE__ */ new Map();
3391
+ const collectVersions = /* @__PURE__ */ __name((node) => {
3392
+ versions.set(node.type, catalog.get(node.type)?.version ?? 1);
3393
+ for (const child of node.children ?? []) collectVersions(child);
3394
+ }, "collectVersions");
3395
+ collectVersions(validated2.value);
3396
+ return supported(validated2.value) ? {
3397
+ kind: "ui",
3398
+ component,
3399
+ props: {
3400
+ root: validated2.value
3401
+ },
3402
+ version: 1,
3403
+ fallbackText: treeToText(catalog, validated2.value),
3404
+ componentVersions: Object.fromEntries(versions)
3405
+ } : {
3406
+ kind: "text",
3407
+ text: treeToText(catalog, validated2.value)
3408
+ };
3409
+ }
3410
+ const definition = catalog.get(component);
3411
+ if (definition === void 0) throw new Error(`Unknown UI component "${component}"`);
3412
+ const currentVersion = definition.version ?? 1;
3413
+ if (version !== void 0 && version !== currentVersion) throw new Error(`Invalid UI component version for "${component}"`);
3414
+ const validated = await catalog.validate(component, props);
3415
+ if (!validated.ok) throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
3416
+ return drawable.has(component) ? {
3417
+ kind: "ui",
3418
+ component,
3419
+ props: validated.value,
3420
+ version: currentVersion,
3421
+ fallbackText: componentToText(catalog, component, validated.value)
3422
+ } : {
3423
+ kind: "text",
3424
+ text: componentToText(catalog, component, validated.value)
3425
+ };
3426
+ }
3427
+ __name(prepareUiEmission, "prepareUiEmission");
3428
+
3429
+ // src/action-proposal-transitions.ts
3430
+ function checkTime(value) {
3431
+ if (!Number.isSafeInteger(value)) throw new RangeError("Action proposal timestamps must be safe integer milliseconds");
3432
+ }
3433
+ __name(checkTime, "checkTime");
3434
+ function checkString(value) {
3435
+ if (typeof value !== "string" || value.length === 0) throw new TypeError("Action proposal identifiers must be nonempty strings");
3436
+ }
3437
+ __name(checkString, "checkString");
3438
+ function validateActionProposalCreation(input) {
3439
+ const allowed = [
3440
+ "id",
3441
+ "tenantRef",
3442
+ "actorRef",
3443
+ "threadId",
3444
+ "originRunId",
3445
+ "originMessageId",
3446
+ "originToolCallId",
3447
+ "toolName",
3448
+ "input",
3449
+ "preparationInput",
3450
+ "executionContext",
3451
+ "confirmation",
3452
+ "approver",
3453
+ "expiresAt",
3454
+ "idempotencyKey",
3455
+ "replacementKey"
3456
+ ];
3457
+ if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("Unknown action proposal creation field");
3458
+ for (const key of [
3459
+ "id",
3460
+ "actorRef",
3461
+ "threadId",
3462
+ "originRunId",
3463
+ "originMessageId",
3464
+ "originToolCallId",
3465
+ "toolName",
3466
+ "approver",
3467
+ "idempotencyKey"
3468
+ ]) checkString(input[key]);
3469
+ if (input.tenantRef !== null && typeof input.tenantRef !== "string") throw new TypeError("tenantRef must be an explicit string or null");
3470
+ for (const value of [
3471
+ input.id,
3472
+ input.actorRef,
3473
+ input.threadId,
3474
+ input.tenantRef
3475
+ ]) {
3476
+ if (value !== null && value.length > 255) throw new RangeError("Action proposal id and scope must fit 255 UTF-16 code units");
3477
+ }
3478
+ if (input.replacementKey !== void 0) {
3479
+ checkString(input.replacementKey);
3480
+ if (input.replacementKey.length > 255) throw new RangeError("replacementKey must fit 255 UTF-16 code units");
3481
+ }
3482
+ if (Object.hasOwn(input, "executionContext")) {
3483
+ const context = input.executionContext;
3484
+ if (context === null || typeof context !== "object" || Array.isArray(context)) throw new TypeError("executionContext must be a JSON object");
3485
+ const allowedContext = [
3486
+ "agentName",
3487
+ "persona",
3488
+ "requestId",
3489
+ "pageContext",
3490
+ "uiCapabilities"
3491
+ ];
3492
+ if (Object.keys(context).some((key) => !allowedContext.includes(key))) throw new TypeError("Unknown execution context field");
3493
+ checkString(context.requestId);
3494
+ if (Object.hasOwn(context, "uiCapabilities")) validateUiCapabilities(context.uiCapabilities);
3495
+ if (Object.hasOwn(context, "agentName")) checkString(context.agentName);
3496
+ if (Object.hasOwn(context, "persona")) checkString(context.persona);
3497
+ if (Object.hasOwn(context, "pageContext")) {
3498
+ const page = context.pageContext;
3499
+ if (page === null || typeof page !== "object" || Array.isArray(page)) throw new TypeError("pageContext must be a JSON object");
3500
+ if (Object.hasOwn(page, "kind") && typeof page.kind !== "string") throw new TypeError("pageContext.kind must be a string");
3501
+ }
3502
+ }
3503
+ if (input.expiresAt !== null) checkTime(input.expiresAt);
3504
+ checkString(input.confirmation?.title);
3505
+ checkString(input.confirmation?.verb);
3506
+ canonicalActionProposalJson(input);
3507
+ }
3508
+ __name(validateActionProposalCreation, "validateActionProposalCreation");
3509
+ function validateActionProposalListQuery(query = {}) {
3510
+ const limit = query.limit === void 0 ? 100 : query.limit;
3511
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1e3) throw new RangeError("limit must be an integer from 1 to 1000");
3512
+ if (query.decision !== void 0 && ![
3513
+ "pending",
3514
+ "approved",
3515
+ "rejected",
3516
+ "expired",
3517
+ "superseded"
3518
+ ].includes(query.decision)) throw new TypeError("Unsupported action proposal list decision");
3519
+ if (query.after !== void 0) {
3520
+ const after = query.after;
3521
+ 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");
3522
+ checkTime(after.createdAt);
3523
+ checkString(after.id);
3524
+ if (after.id.length > 255) throw new TypeError("Action proposal cursor id is too long");
3525
+ }
3526
+ return limit;
3527
+ }
3528
+ __name(validateActionProposalListQuery, "validateActionProposalListQuery");
3529
+ function canonicalActionProposalJson(value) {
3530
+ const ancestors = /* @__PURE__ */ new Set();
3531
+ const sort = /* @__PURE__ */ __name((item) => {
3532
+ if (item === null || typeof item === "string" || typeof item === "boolean") return item;
3533
+ if (typeof item === "number" && Number.isFinite(item)) return item;
3534
+ if (typeof item !== "object" || item === null || ancestors.has(item)) throw new TypeError("Action proposals require finite, acyclic JSON values");
3535
+ const prototype = Object.getPrototypeOf(item);
3536
+ if (!Array.isArray(item) && prototype !== Object.prototype && prototype !== null) throw new TypeError("Action proposals require plain JSON objects");
3537
+ ancestors.add(item);
3538
+ const sorted = Array.isArray(item) ? Array.from(item, sort) : Object.fromEntries(Object.keys(item).sort().map((key) => [
3539
+ key,
3540
+ sort(item[key])
3541
+ ]));
3542
+ ancestors.delete(item);
3543
+ return sorted;
3544
+ }, "sort");
3545
+ return JSON.stringify(sort(value));
3546
+ }
3547
+ __name(canonicalActionProposalJson, "canonicalActionProposalJson");
3548
+ function snapshotActionProposal(value) {
3549
+ return JSON.parse(canonicalActionProposalJson(value));
3550
+ }
3551
+ __name(snapshotActionProposal, "snapshotActionProposal");
3552
+ function actionProposalScopeMatches(row2, scope) {
3553
+ return row2.tenantRef === scope.tenantRef && row2.actorRef === scope.actorRef && row2.threadId === scope.threadId;
3554
+ }
3555
+ __name(actionProposalScopeMatches, "actionProposalScopeMatches");
3556
+ function initialActionProposal(input, now) {
3557
+ validateActionProposalCreation(input);
3558
+ checkTime(now);
3559
+ return snapshotActionProposal({
3560
+ ...input,
3561
+ decision: "pending",
3562
+ decisionAudit: null,
3563
+ execution: null,
3564
+ createdAt: now,
3565
+ updatedAt: now
3566
+ });
3567
+ }
3568
+ __name(initialActionProposal, "initialActionProposal");
3569
+ function actionProposalCreationMatches(row2, input) {
3570
+ validateActionProposalCreation(input);
3571
+ const { decision: _decision, decisionAudit: _audit, execution: _execution, createdAt: _created, updatedAt: _updated, supersededBy: _supersededBy, outcome: _outcome, outcomeDelivery: _delivery, ...original } = row2;
3572
+ return canonicalActionProposalJson(original) === canonicalActionProposalJson(input);
3573
+ }
3574
+ __name(actionProposalCreationMatches, "actionProposalCreationMatches");
3575
+ function result(status, proposal) {
3576
+ return {
3577
+ status,
3578
+ proposal: snapshotActionProposal(proposal)
3579
+ };
3580
+ }
3581
+ __name(result, "result");
3582
+ function transitionActionProposalDecision(row2, command, now) {
3583
+ checkTime(now);
3584
+ if (![
3585
+ "approved",
3586
+ "rejected",
3587
+ "expired"
3588
+ ].includes(command.decision)) throw new TypeError("Unsupported action proposal decision");
3589
+ checkString(command.actorRef);
3590
+ checkString(command.via);
3591
+ if (command.reason !== void 0 && typeof command.reason !== "string") throw new TypeError("reason must be a string");
3592
+ if (command.remember !== void 0 && typeof command.remember !== "boolean") throw new TypeError("remember must be a boolean");
3593
+ if (row2.decision !== "pending") {
3594
+ return result(row2.decision === command.decision ? "unchanged" : row2.decision === "expired" ? "expired" : "conflict", row2);
3595
+ }
3596
+ const next = snapshotActionProposal(row2);
3597
+ if (row2.expiresAt !== null && now >= row2.expiresAt) {
3598
+ next.decision = "expired";
3599
+ next.decisionAudit = {
3600
+ actorRef: "system",
3601
+ via: "expiry",
3602
+ at: now
3603
+ };
3604
+ next.updatedAt = now;
3605
+ return result(command.decision === "expired" ? "applied" : "expired", attachActionProposalOutcome(next, now));
3606
+ }
3607
+ if (command.decision === "expired") return result("conflict", row2);
3608
+ const { decision, ...audit } = command;
3609
+ next.decision = decision;
3610
+ next.decisionAudit = {
3611
+ actorRef: audit.actorRef,
3612
+ via: audit.via,
3613
+ at: now,
3614
+ ...audit.reason === void 0 ? {} : {
3615
+ reason: audit.reason
3616
+ },
3617
+ ...audit.remember === void 0 ? {} : {
3618
+ remember: audit.remember
3619
+ }
3620
+ };
3621
+ next.updatedAt = now;
3622
+ if (decision === "approved") next.execution = {
3623
+ status: "queued",
3624
+ generation: 0,
3625
+ lease: null
3626
+ };
3627
+ return result("applied", decision === "approved" ? next : attachActionProposalOutcome(next, now));
3628
+ }
3629
+ __name(transitionActionProposalDecision, "transitionActionProposalDecision");
3630
+ function checkLeaseMs(leaseMs, now) {
3631
+ checkTime(now);
3632
+ if (!Number.isSafeInteger(leaseMs) || leaseMs <= 0 || !Number.isSafeInteger(now + leaseMs)) throw new RangeError("leaseMs must be finite and positive");
3633
+ }
3634
+ __name(checkLeaseMs, "checkLeaseMs");
3635
+ function transitionActionProposalClaim(row2, command, now, token) {
3636
+ checkLeaseMs(command.leaseMs, now);
3637
+ checkString(command.workerId);
3638
+ checkString(token);
3639
+ const execution = row2.execution;
3640
+ if (row2.decision !== "approved" || !execution || execution.status !== "queued" && !(execution.status === "executing" && execution.lease && now >= execution.lease.expiresAt)) return result("conflict", row2);
3641
+ const next = snapshotActionProposal(row2);
3642
+ const generation = execution.generation + 1;
3643
+ next.execution = {
3644
+ status: "executing",
3645
+ generation,
3646
+ lease: {
3647
+ token,
3648
+ generation,
3649
+ workerId: command.workerId,
3650
+ expiresAt: now + command.leaseMs
3651
+ }
3652
+ };
3653
+ next.updatedAt = now;
3654
+ return result("applied", next);
3655
+ }
3656
+ __name(transitionActionProposalClaim, "transitionActionProposalClaim");
3657
+ function leaseStatus(row2, command, now) {
3658
+ checkTime(now);
3659
+ checkString(command.token);
3660
+ if (!Number.isSafeInteger(command.generation) || command.generation < 1) throw new RangeError("Invalid lease generation");
3661
+ const work = row2.execution;
3662
+ if (row2.decision !== "approved" || work?.status !== "executing" || !work.lease || work.generation !== command.generation || work.lease.generation !== command.generation || work.lease.token !== command.token) return "conflict";
3663
+ return now >= work.lease.expiresAt ? "expired" : "valid";
3664
+ }
3665
+ __name(leaseStatus, "leaseStatus");
3666
+ function transitionActionProposalLease(row2, command, now) {
3667
+ checkLeaseMs(command.leaseMs, now);
3668
+ const status = leaseStatus(row2, command, now);
3669
+ if (status !== "valid") return result(status, row2);
3670
+ const next = snapshotActionProposal(row2);
3671
+ if (next.execution?.lease) next.execution.lease.expiresAt = Math.max(next.execution.lease.expiresAt, now + command.leaseMs);
3672
+ next.updatedAt = now;
3673
+ return result("applied", next);
3674
+ }
3675
+ __name(transitionActionProposalLease, "transitionActionProposalLease");
3676
+ function transitionActionProposalSettlement(row2, command, now) {
3677
+ checkTime(now);
3678
+ if (command.status !== "succeeded" && command.status !== "failed") throw new TypeError("Unsupported settlement status");
3679
+ 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");
3680
+ const status = leaseStatus(row2, command, now);
3681
+ if (status !== "valid") return result(status, row2);
3682
+ const next = snapshotActionProposal(row2);
3683
+ next.execution = command.status === "succeeded" ? {
3684
+ status: "succeeded",
3685
+ generation: command.generation,
3686
+ lease: null,
3687
+ ...command.result === void 0 ? {} : {
3688
+ result: command.result
3689
+ }
3690
+ } : {
3691
+ status: "failed",
3692
+ generation: command.generation,
3693
+ lease: null,
3694
+ error: command.error
3695
+ };
3696
+ if (command.ui !== void 0) canonicalActionProposalJson(command.ui);
3697
+ if (command.text !== void 0 && typeof command.text !== "string") throw new TypeError("Outcome text must be a string");
3698
+ next.updatedAt = now;
3699
+ return result("applied", attachActionProposalOutcome(next, now, command.ui, command.text));
3700
+ }
3701
+ __name(transitionActionProposalSettlement, "transitionActionProposalSettlement");
3702
+ function transitionActionProposalSupersession(row2, replacement, command, now) {
3703
+ checkTime(now);
3704
+ checkString(command.replacementProposalId);
3705
+ checkString(command.actorRef);
3706
+ checkString(command.via);
3707
+ if (replacement.id !== command.replacementProposalId || row2.id === replacement.id || !actionProposalScopeMatches(row2, replacement) || row2.toolName !== replacement.toolName || !row2.replacementKey || row2.replacementKey !== replacement.replacementKey) return result("conflict", row2);
3708
+ if (row2.decision === "superseded") return result(row2.supersededBy === replacement.id ? "unchanged" : "conflict", row2);
3709
+ if (row2.decision !== "pending") return result("conflict", row2);
3710
+ if (row2.expiresAt !== null && now >= row2.expiresAt) return {
3711
+ ...transitionActionProposalDecision(row2, {
3712
+ decision: "expired",
3713
+ actorRef: "system",
3714
+ via: "expiry"
3715
+ }, now),
3716
+ status: "expired"
3717
+ };
3718
+ const next = snapshotActionProposal({
3719
+ ...row2,
3720
+ decision: "superseded",
3721
+ supersededBy: replacement.id,
3722
+ decisionAudit: {
3723
+ actorRef: command.actorRef,
3724
+ via: command.via,
3725
+ replacementProposalId: replacement.id,
3726
+ at: now
3727
+ },
3728
+ updatedAt: now
3729
+ });
3730
+ return result("applied", attachActionProposalOutcome(next, now));
3731
+ }
3732
+ __name(transitionActionProposalSupersession, "transitionActionProposalSupersession");
3733
+
3734
+ // src/genui/builtins.ts
3735
+ var str = /* @__PURE__ */ __name((description, extra = {}) => ({
3736
+ type: "string",
3737
+ ...description !== void 0 ? {
3738
+ description
3739
+ } : {},
3740
+ ...extra
3741
+ }), "str");
3742
+ var obj = /* @__PURE__ */ __name((properties, required = []) => ({
3743
+ type: "object",
3744
+ properties,
3745
+ required,
3746
+ additionalProperties: false
3747
+ }), "obj");
3748
+ var arr = /* @__PURE__ */ __name((items, extra = {}) => ({
3749
+ type: "array",
3750
+ items,
3751
+ ...extra
3752
+ }), "arr");
3753
+ var cell = {
3754
+ type: [
3755
+ "string",
3756
+ "number",
3757
+ "boolean",
3758
+ "null"
3759
+ ]
3760
+ };
3761
+ var row = {
3762
+ type: "object",
3763
+ additionalProperties: cell
3764
+ };
3765
+ var s = /* @__PURE__ */ __name((value) => value === null || value === void 0 ? "" : String(value), "s");
3766
+ var heading = /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*
3767
+ ` : "", "heading");
3768
+ function textTable(columns, rows, max = 15) {
3769
+ const shown = rows.slice(0, max);
3770
+ const widths = columns.map((column) => Math.min(28, Math.max(column.label.length, ...shown.map((r) => s(r[column.key]).length))));
3771
+ const format = /* @__PURE__ */ __name((values) => values.map((value, index) => value.slice(0, widths[index]).padEnd(widths[index] ?? 0)).join(" "), "format");
3772
+ const lines = [
3773
+ format(columns.map((column) => column.label)),
3774
+ widths.map((width) => "-".repeat(width)).join(" "),
3775
+ ...shown.map((r) => format(columns.map((column) => s(r[column.key]))))
3776
+ ];
3777
+ if (rows.length > max) lines.push(`\u2026 ${rows.length - max} more rows`);
3778
+ return `\`\`\`
3779
+ ${lines.join("\n")}
3780
+ \`\`\``;
3781
+ }
3782
+ __name(textTable, "textTable");
3783
+ function lineDiff(before, after) {
3784
+ const a = before.split("\n");
3785
+ const b = after.split("\n");
3786
+ if (a.length * b.length > 25e4) {
3787
+ return [
3788
+ ...a.map((text) => ({
3789
+ op: "del",
3790
+ text
3791
+ })),
3792
+ ...b.map((text) => ({
3793
+ op: "add",
3794
+ text
3795
+ }))
3796
+ ];
3797
+ }
3798
+ const dp = Array.from({
3799
+ length: a.length + 1
3800
+ }, () => new Array(b.length + 1).fill(0));
3801
+ const at = /* @__PURE__ */ __name((i2, j2) => dp[i2]?.[j2] ?? 0, "at");
3802
+ for (let i2 = a.length - 1; i2 >= 0; i2--) {
3803
+ for (let j2 = b.length - 1; j2 >= 0; j2--) {
3804
+ dp[i2][j2] = a[i2] === b[j2] ? at(i2 + 1, j2 + 1) + 1 : Math.max(at(i2 + 1, j2), at(i2, j2 + 1));
3805
+ }
3806
+ }
3807
+ const out = [];
3808
+ let i = 0;
3809
+ let j = 0;
3810
+ while (i < a.length && j < b.length) {
3811
+ if (a[i] === b[j]) {
3812
+ out.push({
3813
+ op: "same",
3814
+ text: a[i]
3815
+ });
3816
+ i++;
3817
+ j++;
3818
+ } else if (at(i + 1, j) >= at(i, j + 1)) {
3819
+ out.push({
3820
+ op: "del",
3821
+ text: a[i++]
3822
+ });
3823
+ } else {
3824
+ out.push({
3825
+ op: "add",
3826
+ text: b[j++]
3827
+ });
3828
+ }
3829
+ }
3830
+ while (i < a.length) out.push({
3831
+ op: "del",
3832
+ text: a[i++]
3833
+ });
3834
+ while (j < b.length) out.push({
3835
+ op: "add",
3836
+ text: b[j++]
3837
+ });
3838
+ return out;
3839
+ }
3840
+ __name(lineDiff, "lineDiff");
3841
+ function bar(value, max, width = 20) {
3842
+ const n = max > 0 ? Math.round(Math.abs(value) / max * width) : 0;
3843
+ return "\u2588".repeat(n) || "\u258F";
3844
+ }
3845
+ __name(bar, "bar");
3846
+ var DataTable = defineComponent({
3847
+ name: "DataTable",
3848
+ title: "Data table",
3849
+ description: "Tabular data (records with the same fields) as a sortable table. Prefer it over markdown tables for more than 3 rows.",
3850
+ props: obj({
3851
+ title: str("Short caption"),
3852
+ columns: arr(obj({
3853
+ key: str("Field name in each row"),
3854
+ label: str("Column header"),
3855
+ align: str(void 0, {
3856
+ enum: [
3857
+ "left",
3858
+ "right",
3859
+ "center"
3860
+ ]
3861
+ }),
3862
+ format: str("How to format values", {
3863
+ enum: [
3864
+ "text",
3865
+ "number",
3866
+ "currency",
3867
+ "percent",
3868
+ "date",
3869
+ "link"
3870
+ ]
3871
+ })
3872
+ }, [
3873
+ "key",
3874
+ "label"
3875
+ ]), {
3876
+ minItems: 1,
3877
+ maxItems: 12
3878
+ }),
3879
+ rows: arr(row, {
3880
+ maxItems: 500
3881
+ })
3882
+ }, [
3883
+ "columns",
3884
+ "rows"
3885
+ ]),
3886
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${textTable(props.columns ?? [], props.rows ?? [])}`, "fallbackText")
3887
+ });
3888
+ var Chart = defineComponent({
3889
+ name: "Chart",
3890
+ title: "Chart",
3891
+ description: "`bar` to compare categories, `line` for a trend over time. `data` is a list of points; `xKey` names the category/time field and each series names a numeric field.",
3892
+ props: obj({
3893
+ type: str("Chart type", {
3894
+ enum: [
3895
+ "bar",
3896
+ "line"
3897
+ ]
3898
+ }),
3899
+ title: str(),
3900
+ xKey: str("Field of each point used for the x axis"),
3901
+ series: arr(obj({
3902
+ key: str("Numeric field"),
3903
+ label: str()
3904
+ }, [
3905
+ "key"
3906
+ ]), {
3907
+ minItems: 1,
3908
+ maxItems: 6
3909
+ }),
3910
+ data: arr(row, {
3911
+ minItems: 1,
3912
+ maxItems: 200
3913
+ }),
3914
+ unit: str("Unit shown after values, e.g. `KB` or `%`")
3915
+ }, [
3916
+ "type",
3917
+ "xKey",
3918
+ "series",
3919
+ "data"
3920
+ ]),
3921
+ fallbackText: /* @__PURE__ */ __name((props) => {
3922
+ const series = props.series ?? [];
3923
+ const data = props.data ?? [];
3924
+ const first = series[0]?.key ?? "";
3925
+ const max = Math.max(0, ...data.map((point) => Math.abs(Number(point[first]) || 0)));
3926
+ const unit = props.unit ? ` ${s(props.unit)}` : "";
3927
+ const lines = data.slice(0, 20).map((point) => `${s(point[props.xKey]).slice(0, 18).padEnd(18)} ${bar(Number(point[first]) || 0, max)} ${s(point[first])}${unit}`);
3928
+ return `${heading(props)}\`\`\`
3929
+ ${lines.join("\n")}
3930
+ \`\`\``;
3931
+ }, "fallbackText")
3932
+ });
3933
+ var KpiCards = defineComponent({
3934
+ name: "KpiCards",
3935
+ title: "KPI cards",
3936
+ description: "A few key numbers (metrics, totals, counts) as cards.",
3937
+ props: obj({
3938
+ title: str(),
3939
+ items: arr(obj({
3940
+ label: str(),
3941
+ value: {
3942
+ type: [
3943
+ "string",
3944
+ "number"
3945
+ ]
3946
+ },
3947
+ delta: str("Change, e.g. `+12%`"),
3948
+ trend: str(void 0, {
3949
+ enum: [
3950
+ "up",
3951
+ "down",
3952
+ "flat"
3953
+ ]
3954
+ }),
3955
+ hint: str()
3956
+ }, [
3957
+ "label",
3958
+ "value"
3959
+ ]), {
3960
+ minItems: 1,
3961
+ maxItems: 8
3962
+ })
3963
+ }, [
3964
+ "items"
3965
+ ]),
3966
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `\u2022 *${s(item.label)}:* ${s(item.value)}${item.delta ? ` (${s(item.delta)})` : ""}`).join("\n")}`, "fallbackText")
3967
+ });
3968
+ var SourceCards = defineComponent({
3969
+ name: "SourceCards",
3970
+ title: "Sources",
3971
+ description: "The documents, pages or links an answer is based on, as cards.",
3972
+ props: obj({
3973
+ title: str(),
3974
+ items: arr(obj({
3975
+ title: str(),
3976
+ url: str(),
3977
+ snippet: str(),
3978
+ source: str("e.g. Wiki, GitHub")
3979
+ }, [
3980
+ "title"
3981
+ ]), {
3982
+ minItems: 1,
3983
+ maxItems: 12
3984
+ })
3985
+ }, [
3986
+ "items"
3987
+ ]),
3988
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `\u2022 ${item.url ? `<${s(item.url)}|${s(item.title)}>` : s(item.title)}${item.source ? ` \u2014 ${s(item.source)}` : ""}`).join("\n")}`, "fallbackText")
3989
+ });
3990
+ var Checklist = defineComponent({
3991
+ name: "Checklist",
3992
+ title: "Checklist",
3993
+ description: "Steps or a to-do list with done/not-done state.",
3994
+ props: obj({
3995
+ title: str(),
3996
+ items: arr(obj({
3997
+ label: str(),
3998
+ done: {
3999
+ type: "boolean"
4000
+ },
4001
+ note: str()
4002
+ }, [
4003
+ "label"
4004
+ ]), {
4005
+ minItems: 1,
4006
+ maxItems: 50
4007
+ })
4008
+ }, [
4009
+ "items"
4010
+ ]),
4011
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `${item.done ? "\u2611" : "\u2610"} ${s(item.label)}${item.note ? ` \u2014 _${s(item.note)}_` : ""}`).join("\n")}`, "fallbackText")
4012
+ });
4013
+ var Timeline = defineComponent({
4014
+ name: "Timeline",
4015
+ title: "Timeline",
4016
+ description: "Events in chronological order (the history of a project, an incident, a deal).",
4017
+ props: obj({
4018
+ title: str(),
4019
+ events: arr(obj({
4020
+ date: str(),
4021
+ title: str(),
4022
+ description: str()
4023
+ }, [
4024
+ "title"
4025
+ ]), {
4026
+ minItems: 1,
4027
+ maxItems: 50
4028
+ })
4029
+ }, [
4030
+ "events"
4031
+ ]),
4032
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.events ?? []).map((event) => `\u2022 ${event.date ? `*${s(event.date)}* ` : ""}${s(event.title)}${event.description ? ` \u2014 ${s(event.description)}` : ""}`).join("\n")}`, "fallbackText")
4033
+ });
4034
+ var CodeBlock = defineComponent({
4035
+ name: "CodeBlock",
4036
+ title: "Code",
4037
+ description: "A code snippet or file excerpt with syntax highlighting.",
4038
+ props: obj({
4039
+ title: str(),
4040
+ language: str(),
4041
+ code: str()
4042
+ }, [
4043
+ "code"
4044
+ ]),
4045
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
4046
+ ${s(props.code)}
4047
+ \`\`\``, "fallbackText")
4048
+ });
4049
+ var Diff = defineComponent({
4050
+ name: "Diff",
4051
+ title: "Diff",
4052
+ description: "A before/after change to a text or code file, as a line diff.",
4053
+ props: obj({
4054
+ title: str(),
4055
+ language: str(),
4056
+ before: str(),
4057
+ after: str()
4058
+ }, [
4059
+ "before",
4060
+ "after"
4061
+ ]),
4062
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
4063
+ ${lineDiff(s(props.before), s(props.after)).map((line) => `${line.op === "add" ? "+" : line.op === "del" ? "-" : " "} ${line.text}`).join("\n")}
4064
+ \`\`\``, "fallbackText")
4065
+ });
4066
+ var Callout = defineComponent({
4067
+ name: "Callout",
4068
+ title: "Callout",
4069
+ description: "A short note, warning or success message, emphasized.",
4070
+ props: obj({
4071
+ tone: str(void 0, {
4072
+ enum: [
4073
+ "info",
4074
+ "success",
4075
+ "warning",
4076
+ "danger"
4077
+ ]
4078
+ }),
4079
+ title: str(),
4080
+ text: str()
4081
+ }, [
4082
+ "text"
4083
+ ]),
4084
+ fallbackText: /* @__PURE__ */ __name((props) => `${props.tone === "warning" || props.tone === "danger" ? ":warning: " : ":information_source: "}${heading(props)}${s(props.text)}`, "fallbackText")
4085
+ });
4086
+ var Stack = defineComponent({
4087
+ name: "Stack",
4088
+ title: "Stack",
4089
+ description: "Lays its children out vertically (or horizontally with direction=row).",
4090
+ props: obj({
4091
+ direction: str(void 0, {
4092
+ enum: [
4093
+ "column",
4094
+ "row"
4095
+ ]
4096
+ }),
4097
+ gap: {
4098
+ type: "number"
4099
+ }
4100
+ }),
4101
+ children: true,
4102
+ fallbackText: /* @__PURE__ */ __name(() => "", "fallbackText")
4103
+ });
4104
+ var Card = defineComponent({
4105
+ name: "Card",
4106
+ title: "Card",
4107
+ description: "A box with an optional title around its children.",
4108
+ props: obj({
4109
+ title: str(),
4110
+ subtitle: str()
4111
+ }),
4112
+ children: true,
4113
+ fallbackText: /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*${props.subtitle ? ` \u2014 ${s(props.subtitle)}` : ""}` : "", "fallbackText")
4114
+ });
4115
+ var Heading = defineComponent({
4116
+ name: "Heading",
4117
+ title: "Heading",
4118
+ description: "A heading.",
4119
+ props: obj({
4120
+ text: str()
4121
+ }, [
4122
+ "text"
4123
+ ]),
4124
+ fallbackText: /* @__PURE__ */ __name((props) => `*${s(props.text)}*`, "fallbackText")
4125
+ });
4126
+ var Text = defineComponent({
4127
+ name: "Text",
4128
+ title: "Text",
4129
+ description: "A paragraph of plain text (no markdown).",
4130
+ props: obj({
4131
+ text: str(),
4132
+ muted: {
4133
+ type: "boolean"
4134
+ }
4135
+ }, [
4136
+ "text"
4137
+ ]),
4138
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.text), "fallbackText")
4139
+ });
4140
+ var Badge = defineComponent({
4141
+ name: "Badge",
4142
+ title: "Badge",
4143
+ description: "A small label.",
4144
+ props: obj({
4145
+ text: str(),
4146
+ tone: str(void 0, {
4147
+ enum: [
4148
+ "neutral",
4149
+ "brand",
4150
+ "success",
4151
+ "warning",
4152
+ "danger"
4153
+ ]
4154
+ })
4155
+ }, [
4156
+ "text"
4157
+ ]),
4158
+ fallbackText: /* @__PURE__ */ __name((props) => `[${s(props.text)}]`, "fallbackText")
4159
+ });
4160
+ var Link = defineComponent({
4161
+ name: "Link",
4162
+ title: "Link",
4163
+ description: "A link to an http(s) URL.",
4164
+ props: obj({
4165
+ text: str(),
4166
+ url: str(void 0, {
4167
+ pattern: "^https?://"
4168
+ })
4169
+ }, [
4170
+ "text",
4171
+ "url"
4172
+ ]),
4173
+ fallbackText: /* @__PURE__ */ __name((props) => `<${s(props.url)}|${s(props.text || props.url)}>`, "fallbackText")
4174
+ });
4175
+ var Image = defineComponent({
4176
+ name: "Image",
4177
+ title: "Image",
4178
+ description: "An image from an https URL.",
4179
+ props: obj({
4180
+ url: str(void 0, {
4181
+ pattern: "^https://"
4182
+ }),
4183
+ alt: str()
4184
+ }, [
4185
+ "url"
4186
+ ]),
4187
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.url), "fallbackText")
4188
+ });
4189
+
4190
+ // src/genui/registry.ts
4191
+ function snapshotComponentPresentation(value) {
4192
+ const seen = /* @__PURE__ */ new Set();
4193
+ const check2 = /* @__PURE__ */ __name((entry) => {
4194
+ if (entry === null || typeof entry === "string" || typeof entry === "boolean") return;
4195
+ if (typeof entry === "number" && Number.isFinite(entry)) return;
4196
+ if (typeof entry !== "object" || entry === null) throw new TypeError("genui: presentation must contain only JSON values");
4197
+ if (seen.has(entry)) throw new TypeError("genui: presentation must contain acyclic JSON values");
4198
+ if (!Array.isArray(entry) && Object.getPrototypeOf(entry) !== Object.prototype && Object.getPrototypeOf(entry) !== null) throw new TypeError("genui: presentation must contain plain JSON objects");
4199
+ seen.add(entry);
4200
+ for (const item of Object.values(entry)) check2(item);
4201
+ seen.delete(entry);
4202
+ }, "check");
4203
+ check2(value);
4204
+ return JSON.parse(JSON.stringify(value));
4205
+ }
4206
+ __name(snapshotComponentPresentation, "snapshotComponentPresentation");
4207
+ function createComponent(definition, options = {}) {
4208
+ defineComponent(definition);
4209
+ const catalog = defineCatalog([
4210
+ definition
4211
+ ], options);
4212
+ const factory = /* @__PURE__ */ __name(async (props) => {
4213
+ const result2 = await validateProps(definition.props, props, catalog.validator);
4214
+ if (!result2.ok) throw new Error(`genui: invalid ${definition.name} props: ${formatIssues(result2.issues)}`);
4215
+ const normalized = snapshotComponentPresentation(result2.value);
4216
+ if (typeof normalized !== "object" || normalized === null || Array.isArray(normalized)) throw new TypeError("genui: component props must be a JSON object");
4217
+ const portable = await catalog.validate(definition.name, normalized);
4218
+ if (!portable.ok || canonicalJson(snapshotComponentPresentation(portable.value)) !== canonicalJson(normalized)) {
4219
+ throw new Error(`genui: ${definition.name} requires outputProps accepting stable normalized JSON props`);
4220
+ }
4221
+ return {
4222
+ component: definition.name,
4223
+ props: normalized,
4224
+ version: definition.version ?? 1,
4225
+ fallbackText: componentToText(catalog, definition.name, normalized)
4226
+ };
4227
+ }, "factory");
4228
+ return Object.assign(factory, {
4229
+ definition
4230
+ });
4231
+ }
4232
+ __name(createComponent, "createComponent");
4233
+ var table = createComponent(DataTable);
4234
+ var chart = createComponent(Chart);
4235
+ function validatePresentationBatch(presentations) {
4236
+ const batch = Array.isArray(presentations) ? presentations : [
4237
+ presentations
4238
+ ];
4239
+ return batch.map((item) => {
4240
+ const presentation = snapshotComponentPresentation(item);
4241
+ if (typeof presentation !== "object" || presentation === null || typeof presentation.component !== "string" || !/^[A-Za-z][A-Za-z0-9]{0,63}$/.test(presentation.component) || !Number.isSafeInteger(presentation.version) || presentation.version <= 0 || typeof presentation.fallbackText !== "string" || typeof presentation.props !== "object" || presentation.props === null || Array.isArray(presentation.props)) {
4242
+ throw new TypeError("genui: invalid component presentation");
4243
+ }
4244
+ return presentation;
4245
+ });
4246
+ }
4247
+ __name(validatePresentationBatch, "validatePresentationBatch");
4248
+ function canonicalJson(value) {
4249
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
4250
+ if (typeof value === "object" && value !== null) {
4251
+ const record2 = value;
4252
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
4253
+ }
4254
+ return JSON.stringify(value);
4255
+ }
4256
+ __name(canonicalJson, "canonicalJson");
4257
+
4258
+ // src/tool-presentations.ts
4259
+ async function emitToolPresentations(handler, output, ctx, toolName) {
4260
+ if (handler.present === void 0) return;
4261
+ try {
4262
+ const result2 = await handler.present(output, ctx);
4263
+ if (result2 === void 0) return;
4264
+ const batch = validatePresentationBatch(result2);
4265
+ for (const presentation of batch) {
4266
+ await ctx.emitUi(presentation.component, presentation.props, {
4267
+ version: presentation.version,
4268
+ fallbackText: presentation.fallbackText
4269
+ });
4270
+ }
4271
+ } catch (error) {
4272
+ if (ctx.onPresentationError !== void 0) {
4273
+ try {
4274
+ await ctx.onPresentationError(error, {
4275
+ toolName
4276
+ });
4277
+ return;
4278
+ } catch (reportError) {
4279
+ console.warn(`Tool "${toolName}" presentation error reporter failed`, reportError);
4280
+ }
4281
+ } else {
4282
+ console.warn(`Tool "${toolName}" presentation failed after successful execution`, error);
4283
+ }
4284
+ }
4285
+ }
4286
+ __name(emitToolPresentations, "emitToolPresentations");
4287
+
2591
4288
  // src/tool-registry.ts
2592
4289
  var ToolForbiddenError = class extends Error {
2593
4290
  static {
@@ -2630,6 +4327,35 @@ var ToolInputInvalidError = class extends Error {
2630
4327
  this.name = "ToolInputInvalidError";
2631
4328
  }
2632
4329
  };
4330
+ var ToolPreflightDeniedError = class extends Error {
4331
+ static {
4332
+ __name(this, "ToolPreflightDeniedError");
4333
+ }
4334
+ toolName;
4335
+ reason;
4336
+ constructor(toolName, reason) {
4337
+ super(reason), this.toolName = toolName, this.reason = reason;
4338
+ this.name = "ToolPreflightDeniedError";
4339
+ }
4340
+ };
4341
+ var ToolInputDriftError = class extends Error {
4342
+ static {
4343
+ __name(this, "ToolInputDriftError");
4344
+ }
4345
+ toolName;
4346
+ constructor(toolName) {
4347
+ super(`Tool "${toolName}" input differs from its trusted snapshot`), this.toolName = toolName;
4348
+ this.name = "ToolInputDriftError";
4349
+ }
4350
+ };
4351
+ function assertTrustedInput(name, value, approved) {
4352
+ try {
4353
+ if (canonicalActionProposalJson(value) === canonicalActionProposalJson(approved)) return;
4354
+ } catch {
4355
+ }
4356
+ throw new ToolInputDriftError(name);
4357
+ }
4358
+ __name(assertTrustedInput, "assertTrustedInput");
2633
4359
  var ToolRegistry = class {
2634
4360
  static {
2635
4361
  __name(this, "ToolRegistry");
@@ -2641,6 +4367,15 @@ var ToolRegistry = class {
2641
4367
  handler
2642
4368
  });
2643
4369
  }
4370
+ /** Whether a successful preflight shortcut has UI to journal. */
4371
+ hasPresentation(name) {
4372
+ return typeof this.entries.get(name)?.handler.present === "function";
4373
+ }
4374
+ /** Present an already authorized successful domain result without executing its action again. */
4375
+ async presentOutput(name, output, ctx) {
4376
+ const entry = this.entries.get(name);
4377
+ if (entry !== void 0) await emitToolPresentations(entry.handler, output, ctx, name);
4378
+ }
2644
4379
  has(name) {
2645
4380
  return this.entries.has(name);
2646
4381
  }
@@ -2677,11 +4412,12 @@ var ToolRegistry = class {
2677
4412
  */
2678
4413
  async definitionsFor(actor, policy, allowedTools, scope = {}) {
2679
4414
  const visible = await this.visibleEntries(actor, policy, allowedTools);
2680
- return Promise.all(visible.map(async ({ spec, handler }) => {
4415
+ const definitions = await Promise.all(visible.map(async ({ spec, handler }) => {
2681
4416
  const override = handler.describe === void 0 ? void 0 : await handler.describe({
2682
4417
  actor,
2683
4418
  ...scope
2684
4419
  });
4420
+ if (override?.available === false) return null;
2685
4421
  return {
2686
4422
  name: spec.name,
2687
4423
  kind: spec.kind,
@@ -2689,6 +4425,7 @@ var ToolRegistry = class {
2689
4425
  inputSchema: override?.inputSchema ?? spec.inputSchema
2690
4426
  };
2691
4427
  }));
4428
+ return definitions.filter((definition) => definition !== null);
2692
4429
  }
2693
4430
  /**
2694
4431
  * The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list,
@@ -2714,6 +4451,41 @@ var ToolRegistry = class {
2714
4451
  * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
2715
4452
  */
2716
4453
  async invoke(name, input, ctx, policy, options = {}) {
4454
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
4455
+ const trusted = Object.hasOwn(options, "approvedInput");
4456
+ if (trusted) assertTrustedInput(name, value, options.approvedInput);
4457
+ const executionInput = trusted ? snapshotActionProposal(value) : value;
4458
+ const hookInput = trusted ? snapshotActionProposal(executionInput) : value;
4459
+ const result2 = entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
4460
+ phase: "execute"
4461
+ }) : void 0;
4462
+ if (trusted) assertTrustedInput(name, hookInput, executionInput);
4463
+ if (result2?.status === "denied") throw new ToolPreflightDeniedError(name, result2.reason);
4464
+ const output = result2?.status === "completed" ? result2.output : await entry.handler.execute(executionInput, withEmit);
4465
+ await emitToolPresentations(entry.handler, output, withEmit, name);
4466
+ return output;
4467
+ }
4468
+ /** Authorize and validate without executing. The caller journals this result before approval. */
4469
+ async prepare(name, input, ctx, policy, options = {}) {
4470
+ return (await this.prepareValidated(name, input, ctx, policy, options)).preflight;
4471
+ }
4472
+ /** Expose the single schema parse; snapshotInput opts proposal callers into immutable JSON. */
4473
+ async prepareValidated(name, input, ctx, policy, options = {}) {
4474
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
4475
+ const normalized = options.snapshotInput === true ? snapshotActionProposal(value) : value;
4476
+ const hookInput = options.snapshotInput === true ? snapshotActionProposal(normalized) : value;
4477
+ const preflight = (entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
4478
+ phase: "prepare"
4479
+ }) : void 0) ?? {
4480
+ status: "ready"
4481
+ };
4482
+ if (options.snapshotInput === true) assertTrustedInput(name, hookInput, normalized);
4483
+ return {
4484
+ input: normalized,
4485
+ preflight
4486
+ };
4487
+ }
4488
+ async validated(name, input, ctx, policy, options) {
2717
4489
  const entry = this.entries.get(name);
2718
4490
  if (entry === void 0) {
2719
4491
  throw new ToolNotFoundError(name);
@@ -2738,7 +4510,11 @@ var ToolRegistry = class {
2738
4510
  ...ctx,
2739
4511
  emitUi: createNoopEmitUi(ctx.requestId)
2740
4512
  };
2741
- return entry.handler.execute(validation.value, withEmit);
4513
+ return {
4514
+ entry,
4515
+ value: validation.value,
4516
+ ctx: withEmit
4517
+ };
2742
4518
  }
2743
4519
  };
2744
4520
  var DefaultRolesPolicy = class {
@@ -2753,6 +4529,7 @@ var DefaultRolesPolicy = class {
2753
4529
  this.emptyRoles = options.emptyRoles ?? "allow";
2754
4530
  }
2755
4531
  can(actor, tool) {
4532
+ if (tool.ability !== void 0 && tool.roles === void 0) return false;
2756
4533
  const allowed = tool.roles ?? this.defaultRoles;
2757
4534
  if (allowed.length === 0) return this.emptyRoles === "allow";
2758
4535
  return (actor.roles ?? []).some((role) => allowed.includes(role));
@@ -2785,6 +4562,69 @@ __name(closeEmptyRoles, "closeEmptyRoles");
2785
4562
  import { createHash } from "crypto";
2786
4563
  import { trace } from "@dudousxd/nestjs-diagnostics";
2787
4564
 
4565
+ // src/action-proposal-approval.ts
4566
+ async function resolveActionProposalApproval(input) {
4567
+ const policy = input.policy ?? new DefaultApprovalPolicy();
4568
+ const requirement = await policy.requirementFor(input.tool, input.actor, input.thread);
4569
+ if (!requirement.required) return {
4570
+ mode: "auto"
4571
+ };
4572
+ const remembered = await input.store.rememberedApprovals?.(input.thread.threadId) ?? [];
4573
+ if (remembered.includes(input.tool.name)) return {
4574
+ mode: "remembered",
4575
+ approver: requirement.approver
4576
+ };
4577
+ const roundedTtl = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) ? Math.floor(requirement.ttlMs) : 0;
4578
+ if (roundedTtl <= 0) return {
4579
+ mode: "ask",
4580
+ approver: requirement.approver
4581
+ };
4582
+ const now = (input.clock ?? Date.now)();
4583
+ if (!Number.isSafeInteger(now) || !Number.isSafeInteger(roundedTtl) || !Number.isSafeInteger(now + roundedTtl)) throw new RangeError("Invalid action approval deadline");
4584
+ return {
4585
+ mode: "ask",
4586
+ approver: requirement.approver,
4587
+ ttlMs: roundedTtl,
4588
+ expiresAt: new Date(now + roundedTtl).toISOString()
4589
+ };
4590
+ }
4591
+ __name(resolveActionProposalApproval, "resolveActionProposalApproval");
4592
+
4593
+ // src/action-proposal-preparation.ts
4594
+ async function prepareActionProposal(registry, name, input, ctx, policy, options = {}) {
4595
+ const preparationInput = snapshotActionProposal(input);
4596
+ const prepared = await registry.prepareValidated(name, snapshotActionProposal(preparationInput), ctx, policy, {
4597
+ ...options,
4598
+ snapshotInput: true
4599
+ });
4600
+ let replacementKey;
4601
+ const replacement = registry.spec(name)?.replacementKey;
4602
+ if (prepared.preflight.status === "ready" && replacement !== void 0) {
4603
+ replacementKey = typeof replacement === "string" ? replacement : await replacement(snapshotActionProposal(prepared.input), ctx);
4604
+ 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");
4605
+ }
4606
+ return {
4607
+ ...replacementKey !== void 0 ? {
4608
+ replacementKey
4609
+ } : {},
4610
+ preparationInput,
4611
+ input: prepared.input,
4612
+ // Completed results keep their normal handler contract; they never create a proposal.
4613
+ preflight: prepared.preflight.status === "ready" ? snapshotActionProposal(prepared.preflight) : prepared.preflight
4614
+ };
4615
+ }
4616
+ __name(prepareActionProposal, "prepareActionProposal");
4617
+
4618
+ // src/action-proposal-receipt.ts
4619
+ function actionProposalReceipt(proposalId) {
4620
+ return {
4621
+ proposalId,
4622
+ status: "pending",
4623
+ executed: false
4624
+ };
4625
+ }
4626
+ __name(actionProposalReceipt, "actionProposalReceipt");
4627
+
2788
4628
  // src/diagnostics.ts
2789
4629
  import { emit } from "@dudousxd/nestjs-diagnostics";
2790
4630
  function publishAgentRunStarted(payload) {
@@ -2839,7 +4679,8 @@ var AGENT_SPAN_EVENTS = [
2839
4679
  "llm.turn",
2840
4680
  "tool.execution",
2841
4681
  "retrieval",
2842
- "follow-ups"
4682
+ "follow-ups",
4683
+ "structured-output"
2843
4684
  ];
2844
4685
  var AGENT_DIAGNOSTIC_EVENTS = [
2845
4686
  "run.started",
@@ -2860,6 +4701,67 @@ function agentDiagnosticKey(event) {
2860
4701
  }
2861
4702
  __name(agentDiagnosticKey, "agentDiagnosticKey");
2862
4703
 
4704
+ // src/negotiated-tool-ui.ts
4705
+ function createNegotiatedUiCollector(toolCallId, scope, resolveCatalog, write) {
4706
+ const collector = createUiCollector(toolCallId, write);
4707
+ const texts = /* @__PURE__ */ new Map();
4708
+ let next = 0;
4709
+ return {
4710
+ components: collector.components,
4711
+ text: /* @__PURE__ */ __name(() => [
4712
+ ...texts.values()
4713
+ ].join("\n"), "text"),
4714
+ restart: /* @__PURE__ */ __name(() => {
4715
+ next = 0;
4716
+ texts.clear();
4717
+ collector.restart();
4718
+ }, "restart"),
4719
+ emit: /* @__PURE__ */ __name(async (component, props, options = {}) => {
4720
+ if (!resolveCatalog) {
4721
+ const declared = scope.uiCapabilities === void 0 || scope.uiCapabilities.components.some((entry) => entry.name === component && entry.version === (options.version ?? 1));
4722
+ if (declared) return collector.emit(component, props, options);
4723
+ const id2 = options.id ?? `${toolCallId}:ui:${next++}`;
4724
+ if (options.fallbackText !== void 0 && options.fallbackText.length > 0) {
4725
+ texts.set(id2, options.fallbackText);
4726
+ await write?.({
4727
+ kind: "text",
4728
+ text: options.fallbackText
4729
+ });
4730
+ }
4731
+ return {
4732
+ id: id2
4733
+ };
4734
+ }
4735
+ const capturedProps = snapshotActionProposal(props);
4736
+ const capturedOptions = {
4737
+ ...options
4738
+ };
4739
+ const catalog = await resolveCatalog(scope);
4740
+ const prepared = await prepareUiEmission(catalog, scope.uiCapabilities, component, capturedProps, capturedOptions.version);
4741
+ const id = capturedOptions.id ?? `${toolCallId}:ui:${next++}`;
4742
+ if (prepared.kind === "text") {
4743
+ texts.set(id, prepared.text);
4744
+ await write?.({
4745
+ kind: "text",
4746
+ text: prepared.text
4747
+ });
4748
+ return {
4749
+ id
4750
+ };
4751
+ }
4752
+ return collector.emit(prepared.component, prepared.props, {
4753
+ id,
4754
+ version: prepared.version,
4755
+ fallbackText: prepared.fallbackText,
4756
+ ...prepared.componentVersions !== void 0 ? {
4757
+ componentVersions: prepared.componentVersions
4758
+ } : {}
4759
+ });
4760
+ }, "emit")
4761
+ };
4762
+ }
4763
+ __name(createNegotiatedUiCollector, "createNegotiatedUiCollector");
4764
+
2863
4765
  // src/tool-retry.ts
2864
4766
  function hasTransientShape(error) {
2865
4767
  if (typeof error !== "object" || error === null) {
@@ -2885,6 +4787,7 @@ function hasTransientShape(error) {
2885
4787
  }
2886
4788
  __name(hasTransientShape, "hasTransientShape");
2887
4789
  function isTransientToolError(error) {
4790
+ if (error instanceof ToolPreflightDeniedError) return false;
2888
4791
  if (hasTransientShape(error)) {
2889
4792
  return true;
2890
4793
  }
@@ -2927,7 +4830,7 @@ async function invokeWithTransientRetry(fn, setting, options) {
2927
4830
  try {
2928
4831
  return await fn();
2929
4832
  } catch (error) {
2930
- if (isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
4833
+ if (error instanceof ToolPreflightDeniedError || isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
2931
4834
  throw error;
2932
4835
  }
2933
4836
  const attemptsRemain = attempt < attempts;
@@ -3107,6 +5010,9 @@ function promptContext(input, persona) {
3107
5010
  ...input.pageContext !== void 0 ? {
3108
5011
  pageContext: input.pageContext
3109
5012
  } : {},
5013
+ ...input.uiCapabilities !== void 0 ? {
5014
+ uiCapabilities: input.uiCapabilities
5015
+ } : {},
3110
5016
  ...persona !== void 0 ? {
3111
5017
  persona: {
3112
5018
  id: persona.id,
@@ -3303,7 +5209,8 @@ async function loadSelectedHistory(deps, input, hooks) {
3303
5209
  messages: keep,
3304
5210
  dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
3305
5211
  title: thread.title,
3306
- hasAssistantMessage: thread.hasAssistantMessage
5212
+ hasAssistantMessage: thread.hasAssistantMessage,
5213
+ actionApprovalMode: deps.actionApprovalMode ?? "blocking"
3307
5214
  };
3308
5215
  });
3309
5216
  return {
@@ -3467,10 +5374,10 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3467
5374
  modelId: turn.modelId
3468
5375
  } : {}
3469
5376
  };
3470
- }, (result) => ({
3471
- inputTokens: result.usage.inputTokens,
3472
- outputTokens: result.usage.outputTokens,
3473
- textLength: result.text.length
5377
+ }, (result2) => ({
5378
+ inputTokens: result2.usage.inputTokens,
5379
+ outputTokens: result2.usage.outputTokens,
5380
+ textLength: result2.text.length
3474
5381
  })));
3475
5382
  await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => deps.store.recordUsage({
3476
5383
  threadId: input.threadId,
@@ -3500,13 +5407,16 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3500
5407
  throw new StructuredOutputError(issues, text, maxAttempts);
3501
5408
  }
3502
5409
  __name(structureAnswer, "structureAnswer");
3503
- function stampToolKinds(result, deps) {
5410
+ function stampToolKinds(result2, deps) {
3504
5411
  return {
3505
- ...result,
3506
- toolCalls: result.toolCalls.map((call) => call.kind === void 0 ? {
3507
- ...call,
3508
- kind: declaredKind(deps, call.name)
3509
- } : call)
5412
+ ...result2,
5413
+ toolCalls: result2.toolCalls.map((call) => {
5414
+ const { preflight: _untrusted, prepared: _untrustedPrepared, actionApproval: _untrustedApproval, ...trusted } = call;
5415
+ return trusted.kind === void 0 ? {
5416
+ ...trusted,
5417
+ kind: declaredKind(deps, trusted.name)
5418
+ } : trusted;
5419
+ })
3510
5420
  };
3511
5421
  }
3512
5422
  __name(stampToolKinds, "stampToolKinds");
@@ -3569,6 +5479,9 @@ function toolContext(deps, input, hooks) {
3569
5479
  ...input.pageContext !== void 0 ? {
3570
5480
  pageContext: input.pageContext
3571
5481
  } : {},
5482
+ ...input.uiCapabilities !== void 0 ? {
5483
+ uiCapabilities: input.uiCapabilities
5484
+ } : {},
3572
5485
  ...deps.host !== void 0 ? {
3573
5486
  host: deps.host
3574
5487
  } : {},
@@ -3644,16 +5557,16 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3644
5557
  }
3645
5558
  const messageId = asked.messageId;
3646
5559
  const reply = await awaitElicitation(hooks, request, toolContext(deps, input, hooks));
3647
- const result = settleElicitation(request, reply);
5560
+ const result2 = settleElicitation(request, reply);
3648
5561
  await hooks.step("intake:answers", async () => {
3649
5562
  await deps.store.updateToolCall({
3650
5563
  toolCallId: request.id,
3651
5564
  // A skip is not an answer. Both leave the agent holding the same values, but only one of them
3652
5565
  // is evidence the user chose them, and a reader auditing what the agent was told has to be
3653
5566
  // able to tell those apart.
3654
- status: result.skipped ? "rejected" : "executed",
3655
- output: result,
3656
- ...result.skipped ? {
5567
+ status: result2.skipped ? "rejected" : "executed",
5568
+ output: result2,
5569
+ ...result2.skipped ? {
3657
5570
  error: "skipped by the user"
3658
5571
  } : {},
3659
5572
  ...reply.answeredByRef !== void 0 ? {
@@ -3664,13 +5577,13 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3664
5577
  {
3665
5578
  id: request.id,
3666
5579
  name: ASK_TOOL_NAME,
3667
- output: result
5580
+ output: result2
3668
5581
  }
3669
5582
  ]);
3670
5583
  await writer.write(encodeStreamEvent({
3671
5584
  kind: "tool-output",
3672
5585
  id: request.id,
3673
- output: result
5586
+ output: result2
3674
5587
  }));
3675
5588
  });
3676
5589
  return {
@@ -3683,7 +5596,7 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3683
5596
  {
3684
5597
  id: request.id,
3685
5598
  name: ASK_TOOL_NAME,
3686
- output: result
5599
+ output: result2
3687
5600
  }
3688
5601
  ]
3689
5602
  };
@@ -3729,16 +5642,140 @@ async function claimToolCall(turn, call) {
3729
5642
  const spec = deps.registry.spec(call.name);
3730
5643
  const kind = call.kind ?? declaredKind(deps, call.name);
3731
5644
  const awaitsHuman = kind === "action" || kind === "ask";
3732
- const approval = kind === "action" ? await claimApproval(turn, call, spec) : void 0;
3733
- const parks = kind === "ask" || approval?.mode === "ask";
5645
+ const independentApproval = turn.actionApprovalMode === "independent" && kind === "action" ? hooks.dispatchLlm !== void 0 && call.actionApproval !== void 0 ? call.actionApproval : await resolveActionProposalApproval({
5646
+ actor: input.actor,
5647
+ store: deps.store,
5648
+ tool: {
5649
+ name: call.name,
5650
+ kind: "action",
5651
+ ...spec !== void 0 ? {
5652
+ spec
5653
+ } : {}
5654
+ },
5655
+ thread: {
5656
+ threadId: input.threadId,
5657
+ runId: hooks.runId,
5658
+ ...input.agentName !== void 0 ? {
5659
+ agentName: input.agentName
5660
+ } : {}
5661
+ },
5662
+ ...deps.approvalPolicy !== void 0 ? {
5663
+ policy: deps.approvalPolicy
5664
+ } : {}
5665
+ }) : void 0;
5666
+ let preflight;
5667
+ let prepared;
5668
+ if (turn.actionApprovalMode === "independent" && call.prepared !== void 0 && hooks.dispatchLlm !== void 0) prepared = call.prepared;
5669
+ if (kind === "action" && hooks.dispatchLlm !== void 0 && call.preflight !== void 0) {
5670
+ preflight = call.preflight;
5671
+ } else if (kind === "action" && hooks.dispatchLlm !== void 0 && spec === void 0) {
5672
+ preflight = {
5673
+ status: "ready"
5674
+ };
5675
+ } else if (kind === "action") {
5676
+ try {
5677
+ const allowedTools = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
5678
+ if (independentApproval?.mode === "ask") {
5679
+ prepared = await prepareActionProposal(deps.registry, call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5680
+ allowedTools
5681
+ });
5682
+ preflight = prepared.preflight;
5683
+ } else {
5684
+ preflight = await deps.registry.prepare(call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5685
+ allowedTools
5686
+ });
5687
+ }
5688
+ } catch (error) {
5689
+ if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) throw error;
5690
+ preflight = {
5691
+ status: "failed",
5692
+ error: error instanceof Error ? error.message : String(error)
5693
+ };
5694
+ }
5695
+ }
5696
+ let confirmation = preflight?.status === "ready" ? preflight.confirmation : void 0;
5697
+ const approval = kind === "action" && preflight?.status === "ready" ? independentApproval ?? await claimApproval(turn, call, spec) : void 0;
5698
+ let proposalId;
5699
+ if (turn.actionApprovalMode === "independent" && approval?.mode === "ask") {
5700
+ if (!prepared) throw new Error("Independent action lacks trusted prepared input");
5701
+ const proposalStore = deps.store;
5702
+ if (typeof proposalStore.createActionProposal !== "function") throw new Error("Independent action proposal store unavailable");
5703
+ proposalId = `proposal-${createHash("sha256").update(JSON.stringify([
5704
+ hooks.runId,
5705
+ call.id
5706
+ ])).digest("hex")}`;
5707
+ const display = confirmation ?? spec?.presentation?.confirm ?? {
5708
+ title: `Allow ${call.name}?`,
5709
+ verb: "Approve"
5710
+ };
5711
+ const fill = /* @__PURE__ */ __name((value) => value.replace(/\{([^}]+)\}/g, (_match, path) => {
5712
+ let current = prepared?.input;
5713
+ for (const part of path.split(".")) current = current !== null && typeof current === "object" ? current[part] : void 0;
5714
+ return current === void 0 ? "" : String(current);
5715
+ }), "fill");
5716
+ const create = prepared.replacementKey === void 0 ? proposalStore.createActionProposal.bind(proposalStore) : (() => {
5717
+ const store = proposalStore;
5718
+ if (typeof store.createReplacingActionProposal !== "function") throw new Error("Independent replacement requires atomic supersession store");
5719
+ return store.createReplacingActionProposal.bind(store);
5720
+ })();
5721
+ const created = await create({
5722
+ ...prepared.replacementKey !== void 0 ? {
5723
+ replacementKey: prepared.replacementKey
5724
+ } : {},
5725
+ id: proposalId,
5726
+ tenantRef: input.actor.tenantRef ?? null,
5727
+ actorRef: input.actor.id,
5728
+ threadId: input.threadId,
5729
+ originRunId: hooks.runId,
5730
+ originMessageId: messageId,
5731
+ originToolCallId: call.id,
5732
+ toolName: call.name,
5733
+ input: prepared.input,
5734
+ preparationInput: prepared.preparationInput,
5735
+ executionContext: {
5736
+ requestId: hooks.runId,
5737
+ ...input.agentName !== void 0 ? {
5738
+ agentName: input.agentName
5739
+ } : {},
5740
+ ...input.persona !== void 0 ? {
5741
+ persona: input.persona
5742
+ } : {},
5743
+ ...input.pageContext !== void 0 ? {
5744
+ pageContext: input.pageContext
5745
+ } : {},
5746
+ ...input.uiCapabilities !== void 0 ? {
5747
+ uiCapabilities: input.uiCapabilities
5748
+ } : {}
5749
+ },
5750
+ confirmation: confirmation ?? {
5751
+ title: fill(display.title),
5752
+ verb: fill(display.verb),
5753
+ ...display.detail !== void 0 ? {
5754
+ detail: fill(display.detail)
5755
+ } : {}
5756
+ },
5757
+ approver: approval.approver,
5758
+ expiresAt: approval.expiresAt === void 0 ? null : Date.parse(approval.expiresAt),
5759
+ idempotencyKey: toolCallContext(toolContext(deps, input, hooks), call.id).idempotencyKey
5760
+ });
5761
+ if (created.status === "conflict") throw new Error("Action proposal replay payload conflict");
5762
+ confirmation = created.proposal?.confirmation ?? confirmation;
5763
+ }
5764
+ const parks = kind === "ask" || approval?.mode === "ask" && proposalId === void 0;
3734
5765
  await deps.store.recordToolCall({
3735
5766
  toolCallId: call.id,
3736
5767
  messageId,
3737
5768
  toolName: call.name,
3738
5769
  toolType: awaitsHuman ? "action" : "read",
3739
5770
  input: call.input,
3740
- status: parks ? "pending_approval" : "auto_executed",
5771
+ status: proposalId !== void 0 ? "proposed" : parks ? "pending_approval" : "auto_executed",
5772
+ ...proposalId !== void 0 ? {
5773
+ proposalId
5774
+ } : {},
3741
5775
  runId: hooks.runId,
5776
+ ...confirmation !== void 0 ? {
5777
+ confirmation
5778
+ } : {},
3742
5779
  ...approval !== void 0 && approval.mode !== "auto" ? {
3743
5780
  approver: approval.approver
3744
5781
  } : {},
@@ -3750,7 +5787,16 @@ async function claimToolCall(turn, call) {
3750
5787
  await turn.writer.write(encodeStreamEvent({
3751
5788
  kind: "approval-requested",
3752
5789
  id: call.id,
5790
+ ...proposalId !== void 0 ? {
5791
+ target: {
5792
+ kind: "proposal",
5793
+ proposalId
5794
+ }
5795
+ } : {},
3753
5796
  approver: approval.approver,
5797
+ ...confirmation !== void 0 ? {
5798
+ confirmation
5799
+ } : {},
3754
5800
  ...approval.expiresAt !== void 0 ? {
3755
5801
  expiresAt: approval.expiresAt
3756
5802
  } : {}
@@ -3771,8 +5817,18 @@ async function claimToolCall(turn, call) {
3771
5817
  ...spec?.terminal === true ? {
3772
5818
  terminal: true
3773
5819
  } : {},
5820
+ // Journal this extra checkpoint's opt-in; historical claims without it keep their sequence.
5821
+ ...preflight?.status === "completed" && deps.registry.hasPresentation(call.name) ? {
5822
+ presentCompletedPreflight: true
5823
+ } : {},
3774
5824
  ...approval !== void 0 ? {
3775
5825
  approval
5826
+ } : {},
5827
+ ...preflight !== void 0 ? {
5828
+ preflight
5829
+ } : {},
5830
+ ...proposalId !== void 0 ? {
5831
+ proposalId
3776
5832
  } : {}
3777
5833
  };
3778
5834
  });
@@ -3795,9 +5851,18 @@ async function claimToolCall(turn, call) {
3795
5851
  ...persisted?.terminal === true ? {
3796
5852
  terminal: true
3797
5853
  } : {},
5854
+ ...persisted?.presentCompletedPreflight === true ? {
5855
+ presentCompletedPreflight: true
5856
+ } : {},
3798
5857
  ...toolType === "action" && persisted?.approval !== void 0 ? {
3799
5858
  approval: persisted.approval
3800
5859
  } : {},
5860
+ ...persisted?.preflight !== void 0 ? {
5861
+ preflight: persisted.preflight
5862
+ } : {},
5863
+ ...persisted?.proposalId !== void 0 ? {
5864
+ proposalId: persisted.proposalId
5865
+ } : {},
3801
5866
  ctx: toolContext(deps, input, hooks)
3802
5867
  };
3803
5868
  }
@@ -3945,6 +6010,9 @@ function skillContext(input) {
3945
6010
  } : {},
3946
6011
  ...input.pageContext !== void 0 ? {
3947
6012
  pageContext: input.pageContext
6013
+ } : {},
6014
+ ...input.uiCapabilities !== void 0 ? {
6015
+ uiCapabilities: input.uiCapabilities
3948
6016
  } : {}
3949
6017
  };
3950
6018
  }
@@ -3961,7 +6029,7 @@ async function invokeClaimedTool(turn, claimed) {
3961
6029
  if (claimed.toolType === "memory") {
3962
6030
  return await rememberIntoTurn(turn, claimed, startedAt);
3963
6031
  }
3964
- const invokeAllowList = turn.persona?.allowedTools === void 0 ? void 0 : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
6032
+ const invokeAllowList = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
3965
6033
  let raw;
3966
6034
  if (hooks.dispatchTool) {
3967
6035
  const stepCtx = {
@@ -3977,6 +6045,9 @@ async function invokeClaimedTool(turn, claimed) {
3977
6045
  } : {},
3978
6046
  ...input.pageContext !== void 0 ? {
3979
6047
  pageContext: input.pageContext
6048
+ } : {},
6049
+ ...input.uiCapabilities !== void 0 ? {
6050
+ uiCapabilities: input.uiCapabilities
3980
6051
  } : {}
3981
6052
  };
3982
6053
  const envelope = {
@@ -3998,37 +6069,49 @@ async function invokeClaimedTool(turn, claimed) {
3998
6069
  raw = await hooks.dispatchTool(resolvedCall, envelope);
3999
6070
  } else {
4000
6071
  const invocation = hooks.step(`tool:${call.id}`, async () => {
4001
- const ui2 = createUiCollector(call.id, (event) => turn.writer.write(encodeStreamEvent(event)));
4002
- const output2 = await traceToolExecution(hooks.runId, {
4003
- toolCallId: call.id,
4004
- toolName: call.name,
4005
- toolType
4006
- }, () => invokeWithTransientRetry(() => {
4007
- ui2.restart();
4008
- return deps.registry.invoke(call.name, call.input, {
4009
- ...toolCallContext(ctx, call.id),
4010
- emitUi: ui2.emit
4011
- }, deps.rolesPolicy, invokeAllowList !== void 0 ? {
4012
- allowedTools: invokeAllowList
4013
- } : {});
4014
- }, deps.toolTransientRetry ?? {}, {
4015
- ...hooks.isControlFlowError !== void 0 ? {
4016
- isControlFlowError: hooks.isControlFlowError
4017
- } : {},
4018
- onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
4019
- publishAgentToolRetry({
4020
- toolName: call.name,
4021
- toolCallId: call.id,
4022
- attempt,
4023
- message: retryError instanceof Error ? retryError.message : String(retryError)
4024
- });
4025
- }, "onRetry")
4026
- }));
4027
- return wrapToolStepOutput(output2, ui2.components());
6072
+ const ui2 = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
6073
+ let output2;
6074
+ try {
6075
+ output2 = await traceToolExecution(hooks.runId, {
6076
+ toolCallId: call.id,
6077
+ toolName: call.name,
6078
+ toolType
6079
+ }, () => invokeWithTransientRetry(() => {
6080
+ ui2.restart();
6081
+ return deps.registry.invoke(call.name, call.input, {
6082
+ ...toolCallContext(ctx, call.id),
6083
+ emitUi: ui2.emit
6084
+ }, deps.rolesPolicy, invokeAllowList !== void 0 ? {
6085
+ allowedTools: invokeAllowList
6086
+ } : {});
6087
+ }, deps.toolTransientRetry ?? {}, {
6088
+ ...hooks.isControlFlowError !== void 0 ? {
6089
+ isControlFlowError: hooks.isControlFlowError
6090
+ } : {},
6091
+ onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
6092
+ publishAgentToolRetry({
6093
+ toolName: call.name,
6094
+ toolCallId: call.id,
6095
+ attempt,
6096
+ message: retryError instanceof Error ? retryError.message : String(retryError)
6097
+ });
6098
+ }, "onRetry")
6099
+ }));
6100
+ } catch (error) {
6101
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") return wrapToolPreflightDenied(error.message);
6102
+ throw error;
6103
+ }
6104
+ return wrapToolStepOutput(output2, ui2.components(), ui2.text());
4028
6105
  });
4029
6106
  raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
4030
6107
  }
4031
- const { output, ui } = unwrapToolStepOutput(raw);
6108
+ const { output, ui, preflightDenied, text } = unwrapToolStepOutput(raw);
6109
+ if (text) turn.toolText.set(call.id, text);
6110
+ if (preflightDenied !== void 0) return {
6111
+ status: "denied",
6112
+ reason: preflightDenied,
6113
+ executionMs: Date.now() - startedAt
6114
+ };
4032
6115
  if (ui.length > 0) {
4033
6116
  turn.toolUi.set(call.id, ui);
4034
6117
  }
@@ -4044,6 +6127,13 @@ async function invokeClaimedTool(turn, claimed) {
4044
6127
  if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) {
4045
6128
  throw error;
4046
6129
  }
6130
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") {
6131
+ return {
6132
+ status: "denied",
6133
+ reason: error.message,
6134
+ executionMs: Date.now() - startedAt
6135
+ };
6136
+ }
4047
6137
  return {
4048
6138
  status: "failed",
4049
6139
  error: error instanceof Error ? error.message : String(error),
@@ -4085,6 +6175,27 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef, meta) {
4085
6175
  } : {}
4086
6176
  }));
4087
6177
  }, "settle");
6178
+ if (outcome.status === "denied") {
6179
+ await hooks.step(`persist:toolreject:${call.id}`, async () => {
6180
+ await deps.store.updateToolCall({
6181
+ toolCallId: call.id,
6182
+ status: meta === void 0 ? "rejected" : "failed",
6183
+ error: outcome.reason,
6184
+ ...decided
6185
+ });
6186
+ await settle();
6187
+ });
6188
+ return {
6189
+ id: call.id,
6190
+ name: call.name,
6191
+ output: {
6192
+ rejected: true,
6193
+ reason: outcome.reason
6194
+ },
6195
+ denied: true,
6196
+ error: `This action was denied by its preflight check: ${outcome.reason}. The requested mutation did not run.`
6197
+ };
6198
+ }
4088
6199
  if (outcome.status === "failed") {
4089
6200
  await hooks.step(`persist:toolfail:${call.id}`, async () => {
4090
6201
  await deps.store.updateToolCall({
@@ -4225,12 +6336,12 @@ async function elicitToolCall(turn, claimed) {
4225
6336
  }));
4226
6337
  });
4227
6338
  const reply = await awaitElicitation(hooks, request, ctx);
4228
- const result = settleElicitation(request, reply);
4229
- await hooks.step(result.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
6339
+ const result2 = settleElicitation(request, reply);
6340
+ await hooks.step(result2.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
4230
6341
  toolCallId: call.id,
4231
- status: result.skipped ? "rejected" : "executed",
4232
- output: result,
4233
- ...result.skipped ? {
6342
+ status: result2.skipped ? "rejected" : "executed",
6343
+ output: result2,
6344
+ ...result2.skipped ? {
4234
6345
  error: "skipped by the user"
4235
6346
  } : {},
4236
6347
  ...reply.answeredByRef !== void 0 ? {
@@ -4241,24 +6352,63 @@ async function elicitToolCall(turn, claimed) {
4241
6352
  runId: hooks.runId,
4242
6353
  toolName: call.name,
4243
6354
  toolType: "action",
4244
- status: result.skipped ? "rejected" : "executed"
6355
+ status: result2.skipped ? "rejected" : "executed"
4245
6356
  });
4246
6357
  return {
4247
6358
  id: call.id,
4248
6359
  name: call.name,
4249
- output: result
6360
+ output: result2
4250
6361
  };
4251
6362
  }
4252
6363
  __name(elicitToolCall, "elicitToolCall");
4253
6364
  async function runClaimedToolCall(turn, claimed) {
4254
6365
  const { deps, input, hooks } = turn;
4255
6366
  const { call, toolType, ctx } = claimed;
6367
+ if (claimed.proposalId !== void 0) {
6368
+ turn.halt.independentProposal = true;
6369
+ return {
6370
+ id: call.id,
6371
+ name: call.name,
6372
+ output: actionProposalReceipt(claimed.proposalId)
6373
+ };
6374
+ }
4256
6375
  if (toolType === "agent") {
4257
6376
  return delegateToolCall(turn, claimed);
4258
6377
  }
4259
6378
  if (toolType === "ask") {
4260
6379
  return elicitToolCall(turn, claimed);
4261
6380
  }
6381
+ if (claimed.preflight !== void 0 && claimed.preflight.status !== "ready") {
6382
+ const result2 = claimed.preflight;
6383
+ if (result2.status === "completed" && claimed.presentCompletedPreflight === true) {
6384
+ const raw = await hooks.step(`present:${call.id}`, async () => {
6385
+ const ui = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
6386
+ await deps.registry.presentOutput(call.name, result2.output, {
6387
+ ...toolCallContext(ctx, call.id),
6388
+ emitUi: ui.emit
6389
+ });
6390
+ return wrapToolStepOutput(result2.output, ui.components(), ui.text());
6391
+ });
6392
+ const presented = unwrapToolStepOutput(raw);
6393
+ if (presented.ui.length > 0) turn.toolUi.set(call.id, presented.ui);
6394
+ if (presented.text) turn.toolText.set(call.id, presented.text);
6395
+ }
6396
+ if (result2.status === "completed" && claimed.terminal === true) turn.halt.terminal = true;
6397
+ const outcome = result2.status === "completed" ? {
6398
+ status: "executed",
6399
+ output: result2.output,
6400
+ executionMs: 0
6401
+ } : result2.status === "denied" ? {
6402
+ status: "denied",
6403
+ reason: result2.reason,
6404
+ executionMs: 0
6405
+ } : {
6406
+ status: "failed",
6407
+ error: result2.error,
6408
+ executionMs: 0
6409
+ };
6410
+ return recordToolOutcome(turn, claimed, outcome, input.actor.id);
6411
+ }
4262
6412
  let deciderRef = input.actor.id;
4263
6413
  let meta;
4264
6414
  if (toolType === "action" && claimed.approval?.mode === "remembered") {
@@ -4383,30 +6533,30 @@ function expiryNarrative() {
4383
6533
  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.";
4384
6534
  }
4385
6535
  __name(expiryNarrative, "expiryNarrative");
4386
- function outputFrame(result) {
4387
- if (result.denied === true) {
4388
- const reason = refusalReason(result);
6536
+ function outputFrame(result2) {
6537
+ if (result2.denied === true) {
6538
+ const reason = refusalReason(result2);
4389
6539
  return {
4390
6540
  kind: "tool-output-denied",
4391
- id: result.id,
6541
+ id: result2.id,
4392
6542
  ...reason !== void 0 ? {
4393
6543
  reason
4394
6544
  } : {}
4395
6545
  };
4396
6546
  }
4397
- return result.error !== void 0 ? {
6547
+ return result2.error !== void 0 ? {
4398
6548
  kind: "tool-output-error",
4399
- id: result.id,
4400
- error: result.error
6549
+ id: result2.id,
6550
+ error: result2.error
4401
6551
  } : {
4402
6552
  kind: "tool-output",
4403
- id: result.id,
4404
- output: result.output
6553
+ id: result2.id,
6554
+ output: result2.output
4405
6555
  };
4406
6556
  }
4407
6557
  __name(outputFrame, "outputFrame");
4408
- function refusalReason(result) {
4409
- const { output } = result;
6558
+ function refusalReason(result2) {
6559
+ const { output } = result2;
4410
6560
  if (output !== null && typeof output === "object" && "reason" in output) {
4411
6561
  const reason = output.reason;
4412
6562
  return typeof reason === "string" && reason !== DEFAULT_REFUSAL_REASON ? reason : void 0;
@@ -4646,6 +6796,25 @@ ${block}`;
4646
6796
  messages: prompt.messages,
4647
6797
  actor: input.actor,
4648
6798
  threadId: input.threadId,
6799
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
6800
+ preflightContext: {
6801
+ actor: input.actor,
6802
+ threadId: input.threadId,
6803
+ runId: hooks.runId,
6804
+ requestId: hooks.runId,
6805
+ ...input.agentName !== void 0 ? {
6806
+ agentName: input.agentName
6807
+ } : {},
6808
+ ...input.persona !== void 0 ? {
6809
+ persona: input.persona
6810
+ } : {},
6811
+ ...input.pageContext !== void 0 ? {
6812
+ pageContext: input.pageContext
6813
+ } : {},
6814
+ ...input.uiCapabilities !== void 0 ? {
6815
+ uiCapabilities: input.uiCapabilities
6816
+ } : {}
6817
+ },
4649
6818
  ...gated ? {
4650
6819
  bufferOutput: true
4651
6820
  } : {},
@@ -4662,6 +6831,9 @@ ${block}`;
4662
6831
  tools: withAskTool({
4663
6832
  tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, intersectAllowLists(deps.toolAllowList, persona?.allowedTools), {
4664
6833
  threadId: input.threadId,
6834
+ ...input.uiCapabilities !== void 0 ? {
6835
+ uiCapabilities: input.uiCapabilities
6836
+ } : {},
4665
6837
  ...input.agentName !== void 0 ? {
4666
6838
  agentName: input.agentName
4667
6839
  } : {}
@@ -4681,7 +6853,7 @@ ${block}`;
4681
6853
  }) : void 0;
4682
6854
  const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
4683
6855
  const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
4684
- const result = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
6856
+ const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
4685
6857
  system: prompt.system,
4686
6858
  messages: prompt.messages,
4687
6859
  tools,
@@ -4691,15 +6863,15 @@ ${block}`;
4691
6863
  await incremental.settled();
4692
6864
  const refusal = incremental.rejection();
4693
6865
  return {
4694
- ...result,
6866
+ ...result2,
4695
6867
  releasedText: incremental.released(),
4696
6868
  ...refusal !== void 0 ? {
4697
6869
  gateRejection: refusal
4698
6870
  } : {}
4699
6871
  };
4700
6872
  }
4701
- return buffer === void 0 ? result : {
4702
- ...result,
6873
+ return buffer === void 0 ? result2 : {
6874
+ ...result2,
4703
6875
  bufferedFrames: buffer.frames()
4704
6876
  };
4705
6877
  });
@@ -4793,12 +6965,12 @@ ${block}`;
4793
6965
  role: "assistant",
4794
6966
  content: turn.text
4795
6967
  }
4796
- ], count), (result) => ({
4797
- followUps: result.followUps.length,
4798
- inputTokens: result.usage.inputTokens,
4799
- outputTokens: result.usage.outputTokens,
4800
- ...result.modelId !== void 0 ? {
4801
- modelId: result.modelId
6968
+ ], count), (result2) => ({
6969
+ followUps: result2.followUps.length,
6970
+ inputTokens: result2.usage.inputTokens,
6971
+ outputTokens: result2.usage.outputTokens,
6972
+ ...result2.modelId !== void 0 ? {
6973
+ modelId: result2.modelId
4802
6974
  } : {}
4803
6975
  })));
4804
6976
  let suggestions = generated.followUps;
@@ -4909,7 +7081,7 @@ ${block}`;
4909
7081
  };
4910
7082
  modelMessages.push(assistantMessage);
4911
7083
  for (const entry of synthetic) {
4912
- const { call, result } = entry;
7084
+ const { call, result: result2 } = entry;
4913
7085
  await hooks.step(`${entry.step}:${assistant.id}`, async () => {
4914
7086
  await deps.store.recordToolCall({
4915
7087
  toolCallId: call.id,
@@ -4923,7 +7095,7 @@ ${block}`;
4923
7095
  await deps.store.updateToolCall({
4924
7096
  toolCallId: call.id,
4925
7097
  status: "executed",
4926
- output: result.output
7098
+ output: result2.output
4927
7099
  });
4928
7100
  await writer.write(encodeStreamEvent({
4929
7101
  kind: "tool-input-available",
@@ -4935,7 +7107,7 @@ ${block}`;
4935
7107
  await writer.write(encodeStreamEvent({
4936
7108
  kind: "tool-output",
4937
7109
  id: call.id,
4938
- output: result.output
7110
+ output: result2.output
4939
7111
  }));
4940
7112
  });
4941
7113
  }
@@ -4964,9 +7136,11 @@ ${block}`;
4964
7136
  messageId: assistant.id,
4965
7137
  writer,
4966
7138
  toolUi: /* @__PURE__ */ new Map(),
7139
+ toolText: /* @__PURE__ */ new Map(),
4967
7140
  halt: {
4968
7141
  terminal: false
4969
7142
  },
7143
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
4970
7144
  ...skillOffer !== void 0 ? {
4971
7145
  skills: skillOffer
4972
7146
  } : {},
@@ -4999,17 +7173,35 @@ ${block}`;
4999
7173
  ...results,
5000
7174
  ...syntheticResults
5001
7175
  ];
7176
+ const toolText = escapeUnsafeToolUiText(toolCallsWithKind.map((call) => turnCalls.toolText.get(call.id)).filter((text) => text !== void 0).join("\n"));
5002
7177
  assistantMessage.toolResults = settledResults;
5003
7178
  await hooks.step(`stream:tool-outputs:${i}`, async () => {
5004
7179
  await deps.store.setMessageToolResults(assistant.id, settledResults);
7180
+ if (toolText) await deps.store.appendMessage({
7181
+ threadId: input.threadId,
7182
+ role: "assistant",
7183
+ content: toolText,
7184
+ runId: hooks.runId,
7185
+ ...input.agentName !== void 0 ? {
7186
+ agentName: input.agentName
7187
+ } : {}
7188
+ });
5005
7189
  const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
5006
7190
  if (toolUi.length > 0) {
5007
7191
  await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
5008
7192
  }
5009
- for (const result of results) {
5010
- await writer.write(encodeStreamEvent(outputFrame(result)));
7193
+ for (const result2 of results) {
7194
+ await writer.write(encodeStreamEvent(outputFrame(result2)));
5011
7195
  }
5012
7196
  });
7197
+ if (toolText) {
7198
+ modelMessages.push({
7199
+ role: "assistant",
7200
+ content: toolText
7201
+ });
7202
+ lastText += `
7203
+ ${toolText}`;
7204
+ }
5013
7205
  await hooks.step(`stream:step-finish:${i}`, async () => {
5014
7206
  await writer.write(encodeStreamEvent({
5015
7207
  kind: "step-finish",
@@ -5023,7 +7215,7 @@ ${block}`;
5023
7215
  } : {}
5024
7216
  }));
5025
7217
  });
5026
- if (turnCalls.halt.terminal) {
7218
+ if (turnCalls.halt.terminal || turnCalls.halt.independentProposal) {
5027
7219
  break;
5028
7220
  }
5029
7221
  }
@@ -5079,8 +7271,152 @@ ${block}`;
5079
7271
  }
5080
7272
  __name(runAgentLoop, "runAgentLoop");
5081
7273
 
7274
+ // src/in-memory-action-proposal-store.ts
7275
+ import { randomUUID } from "crypto";
7276
+ var InMemoryActionProposalStore = class {
7277
+ static {
7278
+ __name(this, "InMemoryActionProposalStore");
7279
+ }
7280
+ rows = /* @__PURE__ */ new Map();
7281
+ clock;
7282
+ constructor(options = {}) {
7283
+ this.clock = options.clock ?? Date.now;
7284
+ }
7285
+ async createActionProposal(input) {
7286
+ validateActionProposalCreation(input);
7287
+ const row2 = this.rows.get(input.id);
7288
+ if (row2) {
7289
+ if (!actionProposalScopeMatches(row2, input)) return {
7290
+ status: "conflict"
7291
+ };
7292
+ return {
7293
+ status: actionProposalCreationMatches(row2, input) ? "unchanged" : "conflict",
7294
+ proposal: snapshotActionProposal(row2)
7295
+ };
7296
+ }
7297
+ const proposal = initialActionProposal(input, this.clock());
7298
+ this.rows.set(input.id, proposal);
7299
+ return {
7300
+ status: "created",
7301
+ proposal: snapshotActionProposal(proposal)
7302
+ };
7303
+ }
7304
+ async getActionProposal(scope, id) {
7305
+ const row2 = this.rows.get(id);
7306
+ return row2 && actionProposalScopeMatches(row2, scope) ? snapshotActionProposal(row2) : null;
7307
+ }
7308
+ async listActionProposals(scope, query = {}) {
7309
+ const limit = validateActionProposalListQuery(query);
7310
+ return [
7311
+ ...this.rows.values()
7312
+ ].filter((row2) => actionProposalScopeMatches(row2, scope) && (query.decision === void 0 || row2.decision === query.decision) && (query.after === void 0 || row2.createdAt > query.after.createdAt || row2.createdAt === query.after.createdAt && row2.id > query.after.id)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, limit).map(snapshotActionProposal);
7313
+ }
7314
+ mutate(scope, id, transition) {
7315
+ const row2 = this.rows.get(id);
7316
+ if (!row2 || !actionProposalScopeMatches(row2, scope)) return {
7317
+ status: "not_found"
7318
+ };
7319
+ const result2 = transition(row2, this.clock());
7320
+ if (result2.proposal) this.rows.set(id, snapshotActionProposal(result2.proposal));
7321
+ return result2;
7322
+ }
7323
+ async decideActionProposal(scope, id, command) {
7324
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalDecision(row2, command, now));
7325
+ }
7326
+ async claimActionProposal(scope, id, command) {
7327
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalClaim(row2, command, now, randomUUID()));
7328
+ }
7329
+ async extendActionProposalLease(scope, id, command) {
7330
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalLease(row2, command, now));
7331
+ }
7332
+ async settleActionProposal(scope, id, command) {
7333
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalSettlement(row2, command, now));
7334
+ }
7335
+ async claimNextActionProposal(command) {
7336
+ const now = this.clock();
7337
+ validateActionProposalWorkerClaim(command, now);
7338
+ const candidates = [
7339
+ ...this.rows.values()
7340
+ ].filter((row2) => row2.decision === "approved" && (row2.execution?.status === "queued" || row2.execution?.status === "executing" && row2.execution.lease !== null && now >= row2.execution.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
7341
+ for (const candidate of candidates) {
7342
+ const claimed = await this.claimActionProposal(candidate, candidate.id, command);
7343
+ if (claimed.status === "applied" && claimed.proposal) return claimed.proposal;
7344
+ }
7345
+ return null;
7346
+ }
7347
+ async expireActionProposals(command) {
7348
+ const now = this.clock();
7349
+ validateActionProposalExpiryBatch(command, now);
7350
+ const candidates = [
7351
+ ...this.rows.values()
7352
+ ].filter((row2) => row2.decision === "pending" && row2.expiresAt !== null && now >= row2.expiresAt).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, command.limit);
7353
+ let expired = 0;
7354
+ for (const candidate of candidates) {
7355
+ const result2 = await this.decideActionProposal(candidate, candidate.id, {
7356
+ decision: "expired",
7357
+ actorRef: "system",
7358
+ via: "expiry"
7359
+ });
7360
+ if (result2.status === "applied") expired++;
7361
+ }
7362
+ return expired;
7363
+ }
7364
+ async claimNextActionProposalOutcome(command) {
7365
+ const now = this.clock();
7366
+ validateActionProposalWorkerClaim(command, now);
7367
+ const candidates = [
7368
+ ...this.rows.values()
7369
+ ].filter((row2) => row2.outcomeDelivery?.status === "pending" && (!row2.outcomeDelivery.lease || now >= row2.outcomeDelivery.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
7370
+ for (const candidate of candidates) {
7371
+ const row2 = this.rows.get(candidate.id);
7372
+ if (!row2) continue;
7373
+ const next = claimActionProposalOutcome(row2, command, this.clock(), randomUUID());
7374
+ if (!next?.outcome || !next.outcomeDelivery?.lease) continue;
7375
+ this.rows.set(next.id, next);
7376
+ return {
7377
+ outcome: snapshotActionProposal(next.outcome),
7378
+ lease: {
7379
+ outcomeId: next.outcome.id,
7380
+ token: next.outcomeDelivery.lease.token,
7381
+ generation: next.outcomeDelivery.generation
7382
+ }
7383
+ };
7384
+ }
7385
+ return null;
7386
+ }
7387
+ async createReplacingActionProposal(input) {
7388
+ validateActionProposalCreation(input);
7389
+ const existing = this.rows.get(input.id);
7390
+ if (existing) return this.createActionProposal(input);
7391
+ const proposal = initialActionProposal(input, this.clock());
7392
+ const replacements = [];
7393
+ if (input.replacementKey) for (const row2 of this.rows.values()) {
7394
+ if (row2.decision !== "pending" || !actionProposalScopeMatches(row2, input) || row2.toolName !== input.toolName || row2.replacementKey !== input.replacementKey) continue;
7395
+ const result2 = transitionActionProposalSupersession(row2, proposal, {
7396
+ replacementProposalId: proposal.id,
7397
+ actorRef: input.actorRef,
7398
+ via: "supersession"
7399
+ }, this.clock());
7400
+ if (result2.proposal) replacements.push(result2.proposal);
7401
+ }
7402
+ this.rows.set(proposal.id, proposal);
7403
+ for (const row2 of replacements) this.rows.set(row2.id, row2);
7404
+ return {
7405
+ status: "created",
7406
+ proposal: snapshotActionProposal(proposal)
7407
+ };
7408
+ }
7409
+ async supersedeActionProposal(scope, id, command) {
7410
+ const replacement = this.rows.get(command.replacementProposalId);
7411
+ if (!replacement || !actionProposalScopeMatches(replacement, scope)) return {
7412
+ status: "not_found"
7413
+ };
7414
+ return this.mutate(scope, id, (row2, now) => transitionActionProposalSupersession(row2, replacement, command, now));
7415
+ }
7416
+ };
7417
+
5082
7418
  // src/in-memory-store.ts
5083
- var InMemoryAgentStore = class {
7419
+ var InMemoryAgentStore = class extends InMemoryActionProposalStore {
5084
7420
  static {
5085
7421
  __name(this, "InMemoryAgentStore");
5086
7422
  }
@@ -5100,9 +7436,10 @@ var InMemoryAgentStore = class {
5100
7436
  throw new Error(`thread ${id} already exists`);
5101
7437
  }
5102
7438
  const ts = this.now();
5103
- const row = {
7439
+ const row2 = {
5104
7440
  id,
5105
7441
  actorRef: input.actor.id,
7442
+ tenantRef: input.actor.tenantRef ?? null,
5106
7443
  title: input.title ?? "New chat",
5107
7444
  transient: input.transient ?? false,
5108
7445
  createdAt: ts,
@@ -5112,20 +7449,20 @@ var InMemoryAgentStore = class {
5112
7449
  persona: input.persona
5113
7450
  } : {}
5114
7451
  };
5115
- this.threads.set(id, row);
5116
- return this.toSummary(row);
7452
+ this.threads.set(id, row2);
7453
+ return this.toSummary(row2);
5117
7454
  }
5118
7455
  async getThread(threadId) {
5119
- const row = this.threads.get(threadId);
5120
- if (row === void 0) {
7456
+ const row2 = this.threads.get(threadId);
7457
+ if (row2 === void 0) {
5121
7458
  return null;
5122
7459
  }
5123
7460
  return {
5124
- ...this.toSummary(row),
5125
- messages: row.messages.map((message) => this.withApprovals(message)),
7461
+ ...this.toSummary(row2),
7462
+ messages: row2.messages.map((message) => this.withApprovals(message)),
5126
7463
  // The run streaming right now — the one field the read-model reports it under.
5127
- ...row.activeStreamId !== void 0 ? {
5128
- activeRunId: row.activeStreamId
7464
+ ...row2.activeStreamId !== void 0 ? {
7465
+ activeRunId: row2.activeStreamId
5129
7466
  } : {}
5130
7467
  };
5131
7468
  }
@@ -5138,8 +7475,12 @@ var InMemoryAgentStore = class {
5138
7475
  }
5139
7476
  const approval = toolCallApprovalFromRow({
5140
7477
  toolCallId: call.toolCallId,
7478
+ ...call.proposalId !== void 0 ? {
7479
+ proposalId: call.proposalId
7480
+ } : {},
5141
7481
  status: call.status,
5142
7482
  approver: call.approver,
7483
+ confirmation: call.confirmation,
5143
7484
  expiresAt: call.expiresAt,
5144
7485
  remember: call.remember,
5145
7486
  executedByRef: call.executedByRef,
@@ -5162,6 +7503,10 @@ var InMemoryAgentStore = class {
5162
7503
  names.add(call.toolName);
5163
7504
  }
5164
7505
  }
7506
+ const thread = this.threads.get(threadId);
7507
+ if (thread) {
7508
+ 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);
7509
+ }
5165
7510
  return [
5166
7511
  ...names
5167
7512
  ];
@@ -5183,7 +7528,7 @@ var InMemoryAgentStore = class {
5183
7528
  async listThreads(actorRef, limit = 50) {
5184
7529
  return [
5185
7530
  ...this.threads.values()
5186
- ].filter((row) => row.actorRef === actorRef && !row.transient).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).slice(0, limit).map((row) => this.toSummary(row));
7531
+ ].filter((row2) => row2.actorRef === actorRef && !row2.transient).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).slice(0, limit).map((row2) => this.toSummary(row2));
5187
7532
  }
5188
7533
  async softDeleteThread(threadId) {
5189
7534
  this.threads.delete(threadId);
@@ -5199,9 +7544,10 @@ var InMemoryAgentStore = class {
5199
7544
  ];
5200
7545
  const id = crypto.randomUUID();
5201
7546
  const ts = this.now();
5202
- const row = {
7547
+ const row2 = {
5203
7548
  id,
5204
7549
  actorRef: source.actorRef,
7550
+ tenantRef: source.tenantRef,
5205
7551
  title: source.title,
5206
7552
  transient: false,
5207
7553
  createdAt: ts,
@@ -5220,8 +7566,8 @@ var InMemoryAgentStore = class {
5220
7566
  persona: source.persona
5221
7567
  } : {}
5222
7568
  };
5223
- this.threads.set(id, row);
5224
- return this.toSummary(row);
7569
+ this.threads.set(id, row2);
7570
+ return this.toSummary(row2);
5225
7571
  }
5226
7572
  async ownerOfThread(threadId) {
5227
7573
  return this.threads.get(threadId)?.actorRef ?? null;
@@ -5241,38 +7587,38 @@ var InMemoryAgentStore = class {
5241
7587
  return call.runId ?? this.threads.get(call.threadId)?.activeStreamId ?? null;
5242
7588
  }
5243
7589
  async ownerOfActiveStream(runId) {
5244
- for (const row of this.threads.values()) {
5245
- if (row.activeStreamId === runId) {
5246
- return row.actorRef;
7590
+ for (const row2 of this.threads.values()) {
7591
+ if (row2.activeStreamId === runId) {
7592
+ return row2.actorRef;
5247
7593
  }
5248
7594
  }
5249
7595
  return null;
5250
7596
  }
5251
7597
  async setTitle(threadId, title) {
5252
- const row = this.threads.get(threadId);
5253
- if (row !== void 0) {
5254
- row.title = title;
5255
- row.updatedAt = this.now();
7598
+ const row2 = this.threads.get(threadId);
7599
+ if (row2 !== void 0) {
7600
+ row2.title = title;
7601
+ row2.updatedAt = this.now();
5256
7602
  }
5257
7603
  }
5258
7604
  async updateThread(threadId, patch) {
5259
- const row = this.threads.get(threadId);
5260
- if (row === void 0) {
7605
+ const row2 = this.threads.get(threadId);
7606
+ if (row2 === void 0) {
5261
7607
  return;
5262
7608
  }
5263
7609
  if (patch.title !== void 0) {
5264
- row.title = patch.title;
7610
+ row2.title = patch.title;
5265
7611
  }
5266
7612
  if (patch.defaultAgent !== void 0) {
5267
- row.defaultAgent = patch.defaultAgent;
7613
+ row2.defaultAgent = patch.defaultAgent;
5268
7614
  }
5269
7615
  if (patch.model !== void 0) {
5270
- row.model = patch.model;
7616
+ row2.model = patch.model;
5271
7617
  }
5272
7618
  if (patch.persona !== void 0) {
5273
- row.persona = patch.persona;
7619
+ row2.persona = patch.persona;
5274
7620
  }
5275
- row.updatedAt = this.now();
7621
+ row2.updatedAt = this.now();
5276
7622
  }
5277
7623
  async activeRunForThread(threadId) {
5278
7624
  return this.threads.get(threadId)?.activeStreamId ?? null;
@@ -5320,64 +7666,64 @@ var InMemoryAgentStore = class {
5320
7666
  }
5321
7667
  /** Settle a run's outcome. A no-op when the run is unknown (mirrors `setTitle`/`updateThread`). */
5322
7668
  async recordRunEnd(end) {
5323
- const row = this.runs.get(end.runId);
5324
- if (row === void 0) {
7669
+ const row2 = this.runs.get(end.runId);
7670
+ if (row2 === void 0) {
5325
7671
  return;
5326
7672
  }
5327
- row.status = end.status;
5328
- row.settledAt = this.now();
7673
+ row2.status = end.status;
7674
+ row2.settledAt = this.now();
5329
7675
  if (end.durationMs !== void 0) {
5330
- row.durationMs = end.durationMs;
7676
+ row2.durationMs = end.durationMs;
5331
7677
  }
5332
7678
  if (end.errorCode !== void 0) {
5333
- row.errorCode = end.errorCode;
7679
+ row2.errorCode = end.errorCode;
5334
7680
  }
5335
7681
  if (end.errorMessage !== void 0) {
5336
- row.errorMessage = end.errorMessage;
7682
+ row2.errorMessage = end.errorMessage;
5337
7683
  }
5338
7684
  }
5339
7685
  /** Bump the run's llm-step retry counter. */
5340
7686
  async bumpRunRetries(runId) {
5341
- const row = this.runs.get(runId);
5342
- if (row !== void 0) {
5343
- row.retries += 1;
7687
+ const row2 = this.runs.get(runId);
7688
+ if (row2 !== void 0) {
7689
+ row2.retries += 1;
5344
7690
  }
5345
7691
  }
5346
7692
  async promoteThread(threadId) {
5347
- const row = this.threads.get(threadId);
5348
- if (row?.transient) {
5349
- row.transient = false;
5350
- row.updatedAt = this.now();
7693
+ const row2 = this.threads.get(threadId);
7694
+ if (row2?.transient) {
7695
+ row2.transient = false;
7696
+ row2.updatedAt = this.now();
5351
7697
  }
5352
7698
  }
5353
7699
  async setActiveStream(threadId, runId) {
5354
- const row = this.threads.get(threadId);
5355
- if (row !== void 0) {
7700
+ const row2 = this.threads.get(threadId);
7701
+ if (row2 !== void 0) {
5356
7702
  if (runId === null) {
5357
- delete row.activeStreamId;
7703
+ delete row2.activeStreamId;
5358
7704
  } else {
5359
- row.activeStreamId = runId;
7705
+ row2.activeStreamId = runId;
5360
7706
  }
5361
7707
  }
5362
7708
  }
5363
7709
  async claimActiveStream(threadId, runId, options = {}) {
5364
- const row = this.threads.get(threadId);
5365
- if (row === void 0) {
7710
+ const row2 = this.threads.get(threadId);
7711
+ if (row2 === void 0) {
5366
7712
  return false;
5367
7713
  }
5368
- const holder = row.activeStreamId;
7714
+ const holder = row2.activeStreamId;
5369
7715
  if (holder !== void 0 && holder !== runId && holder !== options.replacing) {
5370
7716
  return false;
5371
7717
  }
5372
- row.activeStreamId = runId;
7718
+ row2.activeStreamId = runId;
5373
7719
  return true;
5374
7720
  }
5375
7721
  async releaseActiveStream(threadId, runId) {
5376
- const row = this.threads.get(threadId);
5377
- if (row?.activeStreamId !== runId) {
7722
+ const row2 = this.threads.get(threadId);
7723
+ if (row2?.activeStreamId !== runId) {
5378
7724
  return false;
5379
7725
  }
5380
- delete row.activeStreamId;
7726
+ delete row2.activeStreamId;
5381
7727
  return true;
5382
7728
  }
5383
7729
  async enqueueMessage(input) {
@@ -5402,6 +7748,9 @@ var InMemoryAgentStore = class {
5402
7748
  ...input.pageContext !== void 0 ? {
5403
7749
  pageContext: input.pageContext
5404
7750
  } : {},
7751
+ ...input.uiCapabilities !== void 0 ? {
7752
+ uiCapabilities: input.uiCapabilities
7753
+ } : {},
5405
7754
  ...input.interrupt === true ? {
5406
7755
  interrupt: true
5407
7756
  } : {},
@@ -5506,9 +7855,81 @@ var InMemoryAgentStore = class {
5506
7855
  }
5507
7856
  return void 0;
5508
7857
  }
7858
+ async getThreadActionProposalScope(threadId) {
7859
+ const row2 = this.threads.get(threadId);
7860
+ return row2 ? {
7861
+ threadId,
7862
+ actorRef: row2.actorRef,
7863
+ tenantRef: row2.tenantRef
7864
+ } : null;
7865
+ }
7866
+ async admitActionProposalOutcome(command) {
7867
+ const row2 = [
7868
+ ...this.rows.values()
7869
+ ].find((row3) => row3.outcome?.id === command.outcomeId);
7870
+ if (!row2?.outcome || !row2.outcomeDelivery) return {
7871
+ status: "not_found"
7872
+ };
7873
+ if (row2.outcomeDelivery.status === "admitted") return {
7874
+ status: "unchanged",
7875
+ ...row2.outcomeDelivery.messageId !== void 0 ? {
7876
+ messageId: row2.outcomeDelivery.messageId
7877
+ } : {}
7878
+ };
7879
+ if (row2.outcomeDelivery.status === "discarded") return {
7880
+ status: "discarded"
7881
+ };
7882
+ if (!actionProposalOutcomeFenceValid(row2, command, this.clock())) return {
7883
+ status: "conflict"
7884
+ };
7885
+ const thread = this.threads.get(row2.threadId);
7886
+ if (!thread) {
7887
+ this.rows.set(row2.id, snapshotActionProposal({
7888
+ ...row2,
7889
+ outcomeDelivery: {
7890
+ ...row2.outcomeDelivery,
7891
+ status: "discarded",
7892
+ lease: null
7893
+ }
7894
+ }));
7895
+ return {
7896
+ status: "discarded"
7897
+ };
7898
+ }
7899
+ if (thread.actorRef !== row2.actorRef || thread.tenantRef !== row2.tenantRef) return {
7900
+ status: "conflict"
7901
+ };
7902
+ if (thread.activeStreamId) return {
7903
+ status: "busy"
7904
+ };
7905
+ const message = {
7906
+ id: crypto.randomUUID(),
7907
+ role: "assistant",
7908
+ content: actionProposalOutcomeText(row2.outcome),
7909
+ createdAt: this.now(),
7910
+ actionProposalOutcome: snapshotActionProposal(row2.outcome),
7911
+ ui: snapshotActionProposal(row2.outcome.ui)
7912
+ };
7913
+ const next = snapshotActionProposal({
7914
+ ...row2,
7915
+ outcomeDelivery: {
7916
+ ...row2.outcomeDelivery,
7917
+ status: "admitted",
7918
+ lease: null,
7919
+ messageId: message.id
7920
+ }
7921
+ });
7922
+ thread.messages.push(message);
7923
+ thread.updatedAt = message.createdAt;
7924
+ this.rows.set(row2.id, next);
7925
+ return {
7926
+ status: "applied",
7927
+ messageId: message.id
7928
+ };
7929
+ }
5509
7930
  async appendMessage(input) {
5510
- const row = this.threads.get(input.threadId);
5511
- if (row === void 0) {
7931
+ const row2 = this.threads.get(input.threadId);
7932
+ if (row2 === void 0) {
5512
7933
  throw new Error(`thread ${input.threadId} not found`);
5513
7934
  }
5514
7935
  const message = {
@@ -5548,15 +7969,18 @@ var InMemoryAgentStore = class {
5548
7969
  } : {},
5549
7970
  ...input.ui !== void 0 ? {
5550
7971
  ui: input.ui
7972
+ } : {},
7973
+ ...input.actionProposalOutcome !== void 0 ? {
7974
+ actionProposalOutcome: input.actionProposalOutcome
5551
7975
  } : {}
5552
7976
  };
5553
- row.messages.push(message);
5554
- row.updatedAt = message.createdAt;
7977
+ row2.messages.push(message);
7978
+ row2.updatedAt = message.createdAt;
5555
7979
  return message;
5556
7980
  }
5557
7981
  async setMessageToolResults(messageId, results) {
5558
- for (const row of this.threads.values()) {
5559
- const message = row.messages.find((candidate) => candidate.id === messageId);
7982
+ for (const row2 of this.threads.values()) {
7983
+ const message = row2.messages.find((candidate) => candidate.id === messageId);
5560
7984
  if (message !== void 0) {
5561
7985
  message.toolResults = results;
5562
7986
  return;
@@ -5564,12 +7988,12 @@ var InMemoryAgentStore = class {
5564
7988
  }
5565
7989
  }
5566
7990
  async setMessageUi(messageId, ui) {
5567
- for (const row of this.threads.values()) {
5568
- const index = row.messages.findIndex((candidate) => candidate.id === messageId);
5569
- const message = row.messages[index];
7991
+ for (const row2 of this.threads.values()) {
7992
+ const index = row2.messages.findIndex((candidate) => candidate.id === messageId);
7993
+ const message = row2.messages[index];
5570
7994
  if (message !== void 0) {
5571
7995
  const { ui: _previous, ...rest } = message;
5572
- row.messages[index] = ui.length > 0 ? {
7996
+ row2.messages[index] = ui.length > 0 ? {
5573
7997
  ...rest,
5574
7998
  ui
5575
7999
  } : rest;
@@ -5581,12 +8005,12 @@ var InMemoryAgentStore = class {
5581
8005
  return this.threadIdForMessage(messageId) ?? null;
5582
8006
  }
5583
8007
  async setMessageFeedback(messageId, feedback) {
5584
- for (const row of this.threads.values()) {
5585
- const index = row.messages.findIndex((candidate) => candidate.id === messageId);
5586
- const message = row.messages[index];
8008
+ for (const row2 of this.threads.values()) {
8009
+ const index = row2.messages.findIndex((candidate) => candidate.id === messageId);
8010
+ const message = row2.messages[index];
5587
8011
  if (message !== void 0) {
5588
8012
  const { feedback: _previous, ...rest } = message;
5589
- row.messages[index] = feedback !== null ? {
8013
+ row2.messages[index] = feedback !== null ? {
5590
8014
  ...rest,
5591
8015
  feedback
5592
8016
  } : rest;
@@ -5595,13 +8019,13 @@ var InMemoryAgentStore = class {
5595
8019
  }
5596
8020
  }
5597
8021
  async truncateFrom(threadId, messageId) {
5598
- const row = this.threads.get(threadId);
5599
- if (row === void 0) {
8022
+ const row2 = this.threads.get(threadId);
8023
+ if (row2 === void 0) {
5600
8024
  return;
5601
8025
  }
5602
- const cutoff = row.messages.findIndex((message) => message.id === messageId);
8026
+ const cutoff = row2.messages.findIndex((message) => message.id === messageId);
5603
8027
  if (cutoff >= 0) {
5604
- row.messages = row.messages.slice(0, cutoff);
8028
+ row2.messages = row2.messages.slice(0, cutoff);
5605
8029
  }
5606
8030
  }
5607
8031
  /**
@@ -5639,6 +8063,9 @@ var InMemoryAgentStore = class {
5639
8063
  async recordToolCall(input) {
5640
8064
  this.toolCalls.set(input.toolCallId, {
5641
8065
  toolCallId: input.toolCallId,
8066
+ ...input.proposalId !== void 0 ? {
8067
+ proposalId: input.proposalId
8068
+ } : {},
5642
8069
  messageId: input.messageId,
5643
8070
  threadId: this.threadIdForMessage(input.messageId) ?? "",
5644
8071
  toolName: input.toolName,
@@ -5652,6 +8079,9 @@ var InMemoryAgentStore = class {
5652
8079
  ...input.approver !== void 0 ? {
5653
8080
  approver: input.approver
5654
8081
  } : {},
8082
+ ...input.confirmation !== void 0 ? {
8083
+ confirmation: structuredClone(input.confirmation)
8084
+ } : {},
5655
8085
  ...input.expiresAt !== void 0 ? {
5656
8086
  expiresAt: input.expiresAt
5657
8087
  } : {}
@@ -5660,16 +8090,16 @@ var InMemoryAgentStore = class {
5660
8090
  async toolCallOutcomes(toolCallIds) {
5661
8091
  const outcomes = [];
5662
8092
  for (const id of toolCallIds) {
5663
- const row = this.toolCalls.get(id);
5664
- if (row !== void 0) {
8093
+ const row2 = this.toolCalls.get(id);
8094
+ if (row2 !== void 0) {
5665
8095
  outcomes.push({
5666
8096
  id,
5667
- status: row.status,
5668
- ...row.output !== void 0 ? {
5669
- output: row.output
8097
+ status: row2.status,
8098
+ ...row2.output !== void 0 ? {
8099
+ output: row2.output
5670
8100
  } : {},
5671
- ...row.error !== void 0 ? {
5672
- error: row.error
8101
+ ...row2.error !== void 0 ? {
8102
+ error: row2.error
5673
8103
  } : {}
5674
8104
  });
5675
8105
  }
@@ -5678,38 +8108,38 @@ var InMemoryAgentStore = class {
5678
8108
  }
5679
8109
  async failUnsettledToolCalls(runId, error) {
5680
8110
  let settled = 0;
5681
- for (const row of this.toolCalls.values()) {
5682
- if (row.runId === runId && row.status === "pending_approval") {
5683
- row.status = "failed";
5684
- row.error = error;
8111
+ for (const row2 of this.toolCalls.values()) {
8112
+ if (row2.runId === runId && row2.status === "pending_approval") {
8113
+ row2.status = "failed";
8114
+ row2.error = error;
5685
8115
  settled += 1;
5686
8116
  }
5687
8117
  }
5688
8118
  return settled;
5689
8119
  }
5690
8120
  async updateToolCall(input) {
5691
- const row = this.toolCalls.get(input.toolCallId);
5692
- if (row === void 0) {
8121
+ const row2 = this.toolCalls.get(input.toolCallId);
8122
+ if (row2 === void 0) {
5693
8123
  return;
5694
8124
  }
5695
- row.status = input.status;
8125
+ row2.status = input.status;
5696
8126
  if (input.output !== void 0) {
5697
- row.output = input.output;
8127
+ row2.output = input.output;
5698
8128
  }
5699
8129
  if (input.error !== void 0) {
5700
- row.error = input.error;
8130
+ row2.error = input.error;
5701
8131
  }
5702
8132
  if (input.executionMs !== void 0) {
5703
- row.executionMs = input.executionMs;
8133
+ row2.executionMs = input.executionMs;
5704
8134
  }
5705
8135
  if (input.executedByRef !== void 0) {
5706
- row.executedByRef = input.executedByRef;
8136
+ row2.executedByRef = input.executedByRef;
5707
8137
  }
5708
8138
  if (input.remember !== void 0) {
5709
- row.remember = input.remember;
8139
+ row2.remember = input.remember;
5710
8140
  }
5711
8141
  if (input.decidedVia !== void 0) {
5712
- row.decidedVia = input.decidedVia;
8142
+ row2.decidedVia = input.decidedVia;
5713
8143
  }
5714
8144
  }
5715
8145
  async recordUsage(input) {
@@ -5737,9 +8167,9 @@ var InMemoryAgentStore = class {
5737
8167
  return this.usageBetween(actorRef, day, day);
5738
8168
  }
5739
8169
  async usageBetween(actorRef, fromDay, toDay) {
5740
- const rows = this.usage.filter((row) => row.actorRef === actorRef && row.day >= fromDay && row.day <= toDay);
5741
- const usedTokens = rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);
5742
- const costUsd = rows.reduce((sum, row) => sum + (row.costUsd ?? 0), 0);
8170
+ const rows = this.usage.filter((row2) => row2.actorRef === actorRef && row2.day >= fromDay && row2.day <= toDay);
8171
+ const usedTokens = rows.reduce((sum, row2) => sum + row2.inputTokens + row2.outputTokens, 0);
8172
+ const costUsd = rows.reduce((sum, row2) => sum + (row2.costUsd ?? 0), 0);
5743
8173
  return {
5744
8174
  usedTokens,
5745
8175
  costUsd
@@ -5747,30 +8177,30 @@ var InMemoryAgentStore = class {
5747
8177
  }
5748
8178
  /** Test helper: read the recorded usage rows (modelId + token totals). */
5749
8179
  usageRows() {
5750
- return this.usage.map((row) => ({
5751
- actorRef: row.actorRef,
5752
- tokens: row.inputTokens + row.outputTokens,
5753
- modelId: row.modelId
8180
+ return this.usage.map((row2) => ({
8181
+ actorRef: row2.actorRef,
8182
+ tokens: row2.inputTokens + row2.outputTokens,
8183
+ modelId: row2.modelId
5754
8184
  }));
5755
8185
  }
5756
8186
  /** Governance read-model feed: recorded usage rows with the input/output split + thread/day. */
5757
8187
  governanceUsage() {
5758
- return this.usage.map((row) => ({
5759
- actorRef: row.actorRef,
5760
- threadId: row.threadId,
5761
- modelId: row.modelId,
5762
- inputTokens: row.inputTokens,
5763
- outputTokens: row.outputTokens,
5764
- day: row.day,
5765
- createdAt: row.createdAt,
5766
- ...row.cacheWriteTokens !== void 0 ? {
5767
- cacheWriteTokens: row.cacheWriteTokens
8188
+ return this.usage.map((row2) => ({
8189
+ actorRef: row2.actorRef,
8190
+ threadId: row2.threadId,
8191
+ modelId: row2.modelId,
8192
+ inputTokens: row2.inputTokens,
8193
+ outputTokens: row2.outputTokens,
8194
+ day: row2.day,
8195
+ createdAt: row2.createdAt,
8196
+ ...row2.cacheWriteTokens !== void 0 ? {
8197
+ cacheWriteTokens: row2.cacheWriteTokens
5768
8198
  } : {},
5769
- ...row.cacheReadTokens !== void 0 ? {
5770
- cacheReadTokens: row.cacheReadTokens
8199
+ ...row2.cacheReadTokens !== void 0 ? {
8200
+ cacheReadTokens: row2.cacheReadTokens
5771
8201
  } : {},
5772
- ...row.costUsd !== void 0 ? {
5773
- costUsd: row.costUsd
8202
+ ...row2.costUsd !== void 0 ? {
8203
+ costUsd: row2.costUsd
5774
8204
  } : {}
5775
8205
  }));
5776
8206
  }
@@ -5778,22 +8208,22 @@ var InMemoryAgentStore = class {
5778
8208
  governanceToolCalls() {
5779
8209
  return [
5780
8210
  ...this.toolCalls.values()
5781
- ].map((row) => ({
5782
- toolCallId: row.toolCallId,
5783
- toolName: row.toolName,
5784
- toolType: row.toolType,
5785
- status: row.status,
5786
- threadId: row.threadId,
5787
- messageId: row.messageId,
5788
- createdAt: row.createdAt,
5789
- ...row.executionMs !== void 0 ? {
5790
- executionMs: row.executionMs
8211
+ ].map((row2) => ({
8212
+ toolCallId: row2.toolCallId,
8213
+ toolName: row2.toolName,
8214
+ toolType: row2.toolType,
8215
+ status: row2.status,
8216
+ threadId: row2.threadId,
8217
+ messageId: row2.messageId,
8218
+ createdAt: row2.createdAt,
8219
+ ...row2.executionMs !== void 0 ? {
8220
+ executionMs: row2.executionMs
5791
8221
  } : {},
5792
- ...row.error !== void 0 ? {
5793
- error: row.error
8222
+ ...row2.error !== void 0 ? {
8223
+ error: row2.error
5794
8224
  } : {},
5795
- ...row.runId !== void 0 ? {
5796
- runId: row.runId
8225
+ ...row2.runId !== void 0 ? {
8226
+ runId: row2.runId
5797
8227
  } : {}
5798
8228
  }));
5799
8229
  }
@@ -5853,51 +8283,51 @@ var InMemoryAgentStore = class {
5853
8283
  governanceThreads() {
5854
8284
  return [
5855
8285
  ...this.threads.values()
5856
- ].map((row) => ({
5857
- threadId: row.id,
5858
- title: row.title,
5859
- actorRef: row.actorRef,
5860
- messageCount: row.messages.length,
5861
- updatedAt: row.updatedAt
8286
+ ].map((row2) => ({
8287
+ threadId: row2.id,
8288
+ title: row2.title,
8289
+ actorRef: row2.actorRef,
8290
+ messageCount: row2.messages.length,
8291
+ updatedAt: row2.updatedAt
5862
8292
  }));
5863
8293
  }
5864
8294
  /** Governance read-model feed: recorded run outcomes (the reliability surfaces). */
5865
8295
  governanceRuns() {
5866
8296
  return [
5867
8297
  ...this.runs.values()
5868
- ].map((row) => ({
5869
- runId: row.runId,
5870
- threadId: row.threadId,
5871
- actorRef: row.actorRef,
5872
- status: row.status,
5873
- retries: row.retries,
5874
- startedAt: row.startedAt,
5875
- ...row.agentName !== void 0 ? {
5876
- agentName: row.agentName
8298
+ ].map((row2) => ({
8299
+ runId: row2.runId,
8300
+ threadId: row2.threadId,
8301
+ actorRef: row2.actorRef,
8302
+ status: row2.status,
8303
+ retries: row2.retries,
8304
+ startedAt: row2.startedAt,
8305
+ ...row2.agentName !== void 0 ? {
8306
+ agentName: row2.agentName
5877
8307
  } : {},
5878
- ...row.parentRunId !== void 0 ? {
5879
- parentRunId: row.parentRunId
8308
+ ...row2.parentRunId !== void 0 ? {
8309
+ parentRunId: row2.parentRunId
5880
8310
  } : {},
5881
- ...row.durationMs !== void 0 ? {
5882
- durationMs: row.durationMs
8311
+ ...row2.durationMs !== void 0 ? {
8312
+ durationMs: row2.durationMs
5883
8313
  } : {},
5884
- ...row.errorCode !== void 0 ? {
5885
- errorCode: row.errorCode
8314
+ ...row2.errorCode !== void 0 ? {
8315
+ errorCode: row2.errorCode
5886
8316
  } : {},
5887
- ...row.errorMessage !== void 0 ? {
5888
- errorMessage: row.errorMessage
8317
+ ...row2.errorMessage !== void 0 ? {
8318
+ errorMessage: row2.errorMessage
5889
8319
  } : {},
5890
- ...row.settledAt !== void 0 ? {
5891
- settledAt: row.settledAt
8320
+ ...row2.settledAt !== void 0 ? {
8321
+ settledAt: row2.settledAt
5892
8322
  } : {},
5893
- ...row.promptHash !== void 0 ? {
5894
- promptHash: row.promptHash
8323
+ ...row2.promptHash !== void 0 ? {
8324
+ promptHash: row2.promptHash
5895
8325
  } : {}
5896
8326
  }));
5897
8327
  }
5898
8328
  threadIdForMessage(messageId) {
5899
- for (const [threadId, row] of this.threads) {
5900
- if (row.messages.some((message) => message.id === messageId)) {
8329
+ for (const [threadId, row2] of this.threads) {
8330
+ if (row2.messages.some((message) => message.id === messageId)) {
5901
8331
  return threadId;
5902
8332
  }
5903
8333
  }
@@ -5907,54 +8337,54 @@ var InMemoryAgentStore = class {
5907
8337
  toolCallRows() {
5908
8338
  return [
5909
8339
  ...this.toolCalls.values()
5910
- ].map((row) => ({
5911
- toolCallId: row.toolCallId,
5912
- toolName: row.toolName,
5913
- toolType: row.toolType,
5914
- status: row.status,
5915
- input: row.input,
5916
- ...row.output !== void 0 ? {
5917
- output: row.output
8340
+ ].map((row2) => ({
8341
+ toolCallId: row2.toolCallId,
8342
+ toolName: row2.toolName,
8343
+ toolType: row2.toolType,
8344
+ status: row2.status,
8345
+ input: row2.input,
8346
+ ...row2.output !== void 0 ? {
8347
+ output: row2.output
5918
8348
  } : {},
5919
- ...row.error !== void 0 ? {
5920
- error: row.error
8349
+ ...row2.error !== void 0 ? {
8350
+ error: row2.error
5921
8351
  } : {},
5922
- ...row.runId !== void 0 ? {
5923
- runId: row.runId
8352
+ ...row2.runId !== void 0 ? {
8353
+ runId: row2.runId
5924
8354
  } : {},
5925
- ...row.executedByRef !== void 0 ? {
5926
- executedByRef: row.executedByRef
8355
+ ...row2.executedByRef !== void 0 ? {
8356
+ executedByRef: row2.executedByRef
5927
8357
  } : {},
5928
- ...row.approver !== void 0 ? {
5929
- approver: row.approver
8358
+ ...row2.approver !== void 0 ? {
8359
+ approver: row2.approver
5930
8360
  } : {},
5931
- ...row.expiresAt !== void 0 ? {
5932
- expiresAt: row.expiresAt
8361
+ ...row2.expiresAt !== void 0 ? {
8362
+ expiresAt: row2.expiresAt
5933
8363
  } : {},
5934
- ...row.remember !== void 0 ? {
5935
- remember: row.remember
8364
+ ...row2.remember !== void 0 ? {
8365
+ remember: row2.remember
5936
8366
  } : {},
5937
- ...row.decidedVia !== void 0 ? {
5938
- decidedVia: row.decidedVia
8367
+ ...row2.decidedVia !== void 0 ? {
8368
+ decidedVia: row2.decidedVia
5939
8369
  } : {}
5940
8370
  }));
5941
8371
  }
5942
- toSummary(row) {
5943
- const last = row.messages[row.messages.length - 1];
8372
+ toSummary(row2) {
8373
+ const last = row2.messages[row2.messages.length - 1];
5944
8374
  return {
5945
- id: row.id,
5946
- title: row.title,
5947
- transient: row.transient,
5948
- createdAt: row.createdAt,
5949
- updatedAt: row.updatedAt,
8375
+ id: row2.id,
8376
+ title: row2.title,
8377
+ transient: row2.transient,
8378
+ createdAt: row2.createdAt,
8379
+ updatedAt: row2.updatedAt,
5950
8380
  ...last !== void 0 ? {
5951
8381
  lastMessagePreview: last.content.slice(0, 120)
5952
8382
  } : {},
5953
- defaultAgent: row.defaultAgent ?? null,
5954
- ...row.model != null ? {
5955
- model: row.model
8383
+ defaultAgent: row2.defaultAgent ?? null,
8384
+ ...row2.model != null ? {
8385
+ model: row2.model
5956
8386
  } : {},
5957
- persona: row.persona ?? null
8387
+ persona: row2.persona ?? null
5958
8388
  };
5959
8389
  }
5960
8390
  };
@@ -5962,22 +8392,22 @@ var InMemoryAgentStore = class {
5962
8392
  // src/confirm-token.ts
5963
8393
  import { createHash as createHash2, createHmac, timingSafeEqual } from "crypto";
5964
8394
  var DEFAULT_CONFIRM_TTL_MS = 15 * 60 * 1e3;
5965
- function canonicalJson(value) {
8395
+ function canonicalJson2(value) {
5966
8396
  if (value === void 0) return "null";
5967
8397
  if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
5968
- if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
8398
+ if (Array.isArray(value)) return `[${value.map(canonicalJson2).join(",")}]`;
5969
8399
  if (typeof value.toJSON === "function") {
5970
- return canonicalJson(value.toJSON());
8400
+ return canonicalJson2(value.toJSON());
5971
8401
  }
5972
8402
  const entries = Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
5973
- return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
8403
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson2(entry)}`).join(",")}}`;
5974
8404
  }
5975
- __name(canonicalJson, "canonicalJson");
8405
+ __name(canonicalJson2, "canonicalJson");
5976
8406
  function signature(subject, expiresAt, secret) {
5977
8407
  if (secret === "") {
5978
8408
  throw new Error("confirm token: the secret is empty");
5979
8409
  }
5980
- return createHmac("sha256", secret).update(canonicalJson([
8410
+ return createHmac("sha256", secret).update(canonicalJson2([
5981
8411
  "agent-confirm",
5982
8412
  subject.tool,
5983
8413
  subject.actorId,
@@ -6156,12 +8586,12 @@ function withConfirmFields(schema) {
6156
8586
  });
6157
8587
  }
6158
8588
  const { args, fields } = splitConfirm(value);
6159
- const result = await inner.validate(args);
6160
- if (result.issues !== void 0) {
8589
+ const result2 = await inner.validate(args);
8590
+ if (result2.issues !== void 0) {
6161
8591
  return {
6162
8592
  issues: [
6163
8593
  ...issues,
6164
- ...result.issues
8594
+ ...result2.issues
6165
8595
  ]
6166
8596
  };
6167
8597
  }
@@ -6171,10 +8601,10 @@ function withConfirmFields(schema) {
6171
8601
  };
6172
8602
  }
6173
8603
  return {
6174
- value: isRecord(result.value) ? {
6175
- ...result.value,
8604
+ value: isRecord(result2.value) ? {
8605
+ ...result2.value,
6176
8606
  ...fields
6177
- } : result.value
8607
+ } : result2.value
6178
8608
  };
6179
8609
  }, "validate"),
6180
8610
  jsonSchema: {
@@ -6305,8 +8735,8 @@ var SqlTokenStreamSink = class {
6305
8735
  autoPurge;
6306
8736
  writes = /* @__PURE__ */ new Map();
6307
8737
  lastAutoPurge = 0;
6308
- constructor(table, options = {}) {
6309
- this.table = table;
8738
+ constructor(table2, options = {}) {
8739
+ this.table = table2;
6310
8740
  this.pollIntervalMs = Math.max(1, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
6311
8741
  this.idlePollIntervalMs = Math.max(this.pollIntervalMs, options.idlePollIntervalMs ?? DEFAULT_IDLE_POLL_INTERVAL_MS);
6312
8742
  this.flushMs = Math.max(0, options.flushMs ?? DEFAULT_FLUSH_MS);
@@ -6448,15 +8878,15 @@ var SqlTokenStreamSink = class {
6448
8878
  let quietSince = Date.now();
6449
8879
  while (true) {
6450
8880
  const rows = await this.table.read(runId, cursor, READ_PAGE);
6451
- for (const row of rows) {
6452
- cursor = row.seq;
6453
- if (row.frame === null) {
6454
- if (row.error !== null) {
6455
- throw new AgentStreamError(JSON.parse(row.error));
8881
+ for (const row2 of rows) {
8882
+ cursor = row2.seq;
8883
+ if (row2.frame === null) {
8884
+ if (row2.error !== null) {
8885
+ throw new AgentStreamError(JSON.parse(row2.error));
6456
8886
  }
6457
8887
  return;
6458
8888
  }
6459
- yield encoder2.encode(row.frame);
8889
+ yield encoder2.encode(row2.frame);
6460
8890
  }
6461
8891
  if (rows.length > 0) {
6462
8892
  quietSince = Date.now();
@@ -6506,6 +8936,436 @@ var SqlTokenStreamSink = class {
6506
8936
  });
6507
8937
  }
6508
8938
  };
8939
+
8940
+ // src/action-proposal-text.ts
8941
+ function resolveTextActionProposalDecision(text, proposals, vocabulary) {
8942
+ const command = parseTextActionProposalCommand(text, vocabulary);
8943
+ if (command.status === "unmatched") return command;
8944
+ const { decision, remember } = command;
8945
+ const pending = proposals.filter((proposal) => proposal.decision === "pending");
8946
+ const explicitId = command.proposalId;
8947
+ if (explicitId !== void 0) {
8948
+ if (!pending.some((proposal) => proposal.id === explicitId)) return {
8949
+ status: "unmatched"
8950
+ };
8951
+ return {
8952
+ status: "decision",
8953
+ proposalId: explicitId,
8954
+ decision,
8955
+ remember
8956
+ };
8957
+ }
8958
+ if (pending.length === 0) return {
8959
+ status: "unmatched"
8960
+ };
8961
+ if (pending.length > 1) return {
8962
+ status: "ambiguous",
8963
+ proposalIds: pending.map(({ id }) => id)
8964
+ };
8965
+ const candidate = pending[0];
8966
+ if (!candidate) return {
8967
+ status: "unmatched"
8968
+ };
8969
+ return {
8970
+ status: "decision",
8971
+ proposalId: candidate.id,
8972
+ decision,
8973
+ remember
8974
+ };
8975
+ }
8976
+ __name(resolveTextActionProposalDecision, "resolveTextActionProposalDecision");
8977
+ var DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY = {
8978
+ approve: [
8979
+ "yes",
8980
+ "confirm",
8981
+ "approve",
8982
+ "approved",
8983
+ "ok"
8984
+ ],
8985
+ reject: [
8986
+ "no",
8987
+ "cancel",
8988
+ "reject",
8989
+ "deny"
8990
+ ],
8991
+ remember: [
8992
+ "always in this conversation"
8993
+ ]
8994
+ };
8995
+ var DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES = {
8996
+ approved: "Proposal approved and queued to run.",
8997
+ rejected: "Proposal rejected; nothing was run.",
8998
+ expired: "The proposal expired; nothing was run.",
8999
+ unchanged: "This proposal could not be changed; refresh the list to see where it stands.",
9000
+ ambiguous: /* @__PURE__ */ __name((ids) => `Which proposal? Reply confirm #ID or cancel #ID: ${ids.join(", ")}`, "ambiguous"),
9001
+ tooMany: "There are several proposals. Confirm or reject one with an explicit #ID."
9002
+ };
9003
+ function textActionProposalReply(result2, decision, replies = DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES) {
9004
+ return result2.status === "expired" || result2.proposal?.decision === "expired" ? replies.expired : result2.status === "applied" || result2.status === "unchanged" ? decision === "approved" ? replies.approved : replies.rejected : replies.unchanged;
9005
+ }
9006
+ __name(textActionProposalReply, "textActionProposalReply");
9007
+ var escapeRegExp = /* @__PURE__ */ __name((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "escapeRegExp");
9008
+ var alternatives = /* @__PURE__ */ __name((words) => words.map((word) => word.trim()).filter((word) => word.length > 0).map((word) => escapeRegExp(word).replace(/\s+/g, "\\s+")).join("|"), "alternatives");
9009
+ var fold = /* @__PURE__ */ __name((word) => word.trim().toLowerCase(), "fold");
9010
+ function parseTextActionProposalCommand(text, vocabulary = DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY) {
9011
+ if (/[\r\n]/u.test(text)) return {
9012
+ status: "unmatched"
9013
+ };
9014
+ const verbs = alternatives([
9015
+ ...vocabulary.approve,
9016
+ ...vocabulary.reject
9017
+ ]);
9018
+ if (verbs.length === 0) return {
9019
+ status: "unmatched"
9020
+ };
9021
+ const remember = alternatives(vocabulary.remember);
9022
+ const match = new RegExp(`^(${verbs})${remember.length > 0 ? `(?: (${remember}))?` : "()"}(?: #([^\\s]+?))?[.!]?$`, "iu").exec(text.trim());
9023
+ if (!match) return {
9024
+ status: "unmatched"
9025
+ };
9026
+ const verb = match[1] === void 0 ? void 0 : fold(match[1]);
9027
+ if (verb === void 0) return {
9028
+ status: "unmatched"
9029
+ };
9030
+ const decision = vocabulary.reject.some((word) => fold(word) === verb) ? "rejected" : "approved";
9031
+ const remembers = match[2] !== void 0 && match[2] !== "";
9032
+ if (remembers && decision !== "approved") return {
9033
+ status: "unmatched"
9034
+ };
9035
+ return {
9036
+ status: "command",
9037
+ decision,
9038
+ remember: remembers,
9039
+ ...match[3] !== void 0 ? {
9040
+ proposalId: match[3]
9041
+ } : {}
9042
+ };
9043
+ }
9044
+ __name(parseTextActionProposalCommand, "parseTextActionProposalCommand");
9045
+ var ptBrActionProposalText = {
9046
+ vocabulary: {
9047
+ approve: [
9048
+ "sim",
9049
+ "confirmo",
9050
+ "confirmar",
9051
+ "aprovar",
9052
+ "aprovo",
9053
+ "pode",
9054
+ ...DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY.approve
9055
+ ],
9056
+ reject: [
9057
+ "n\xE3o",
9058
+ "nao",
9059
+ "cancelar",
9060
+ "cancela",
9061
+ "rejeitar",
9062
+ "rejeito",
9063
+ ...DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY.reject
9064
+ ],
9065
+ remember: [
9066
+ "sempre nesta conversa",
9067
+ ...DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY.remember
9068
+ ]
9069
+ },
9070
+ replies: {
9071
+ approved: "Proposta aprovada e enfileirada para execu\xE7\xE3o.",
9072
+ rejected: "Proposta rejeitada; nenhuma a\xE7\xE3o foi executada.",
9073
+ expired: "A proposta expirou; nenhuma a\xE7\xE3o foi executada.",
9074
+ unchanged: "N\xE3o foi poss\xEDvel alterar esta proposta; atualize a lista para consultar seu estado.",
9075
+ ambiguous: /* @__PURE__ */ __name((ids) => `Qual proposta? Responda confirmar #ID ou cancelar #ID: ${ids.join(", ")}`, "ambiguous"),
9076
+ tooMany: "H\xE1 v\xE1rias propostas. Confirme ou rejeite usando #ID expl\xEDcito."
9077
+ }
9078
+ };
9079
+
9080
+ // src/action-proposal-view.ts
9081
+ function toActionProposalView(proposal) {
9082
+ const { execution, outcomeDelivery, idempotencyKey: _idempotencyKey, preparationInput: _preparationInput, executionContext: _executionContext, ...rest } = proposal;
9083
+ return {
9084
+ ...rest,
9085
+ execution: execution === null ? null : withoutLease(execution),
9086
+ ...outcomeDelivery !== void 0 ? {
9087
+ outcomeDelivery: withoutLease(outcomeDelivery)
9088
+ } : {}
9089
+ };
9090
+ }
9091
+ __name(toActionProposalView, "toActionProposalView");
9092
+ function toActionProposalMutationView(result2) {
9093
+ const { proposal, ...rest } = result2;
9094
+ return {
9095
+ ...rest,
9096
+ ...proposal !== void 0 ? {
9097
+ proposal: toActionProposalView(proposal)
9098
+ } : {}
9099
+ };
9100
+ }
9101
+ __name(toActionProposalMutationView, "toActionProposalMutationView");
9102
+ function withoutLease(value) {
9103
+ const { lease: _lease, ...rest } = value;
9104
+ return rest;
9105
+ }
9106
+ __name(withoutLease, "withoutLease");
9107
+
9108
+ // src/action-proposal-executor.ts
9109
+ var ActionProposalExecutor = class {
9110
+ static {
9111
+ __name(this, "ActionProposalExecutor");
9112
+ }
9113
+ options;
9114
+ constructor(options) {
9115
+ this.options = options;
9116
+ }
9117
+ async execute(proposal) {
9118
+ let ui = createNegotiatedUiCollector(proposal.originToolCallId, {
9119
+ actor: {
9120
+ id: proposal.actorRef
9121
+ },
9122
+ threadId: proposal.threadId
9123
+ });
9124
+ try {
9125
+ const actor = await this.options.resolver.resolve({
9126
+ actorRef: proposal.actorRef,
9127
+ tenantRef: proposal.tenantRef
9128
+ });
9129
+ if (!actor || actor.id !== proposal.actorRef || (actor.tenantRef ?? null) !== proposal.tenantRef) throw new Error("Action proposal requester no longer exists or scope changed");
9130
+ const deps = await this.options.resolveExecution(proposal, actor);
9131
+ if (deps.registry.spec(proposal.toolName)?.kind !== "action") throw new Error("Action proposal tool no longer exists as an action");
9132
+ const context = proposal.executionContext;
9133
+ ui = createNegotiatedUiCollector(proposal.originToolCallId, {
9134
+ actor,
9135
+ threadId: proposal.threadId,
9136
+ ...context?.agentName !== void 0 ? {
9137
+ agentName: context.agentName
9138
+ } : {},
9139
+ ...context?.uiCapabilities !== void 0 ? {
9140
+ uiCapabilities: context.uiCapabilities
9141
+ } : {}
9142
+ }, deps.resolveUiCatalog);
9143
+ const output = await deps.registry.invoke(proposal.toolName, snapshotActionProposal(Object.hasOwn(proposal, "preparationInput") ? proposal.preparationInput : proposal.input), {
9144
+ actor,
9145
+ threadId: proposal.threadId,
9146
+ runId: proposal.originRunId,
9147
+ requestId: context?.requestId ?? proposal.originRunId,
9148
+ toolCallId: proposal.originToolCallId,
9149
+ idempotencyKey: proposal.idempotencyKey,
9150
+ ...context?.agentName !== void 0 ? {
9151
+ agentName: context.agentName
9152
+ } : {},
9153
+ ...context?.persona !== void 0 ? {
9154
+ persona: context.persona
9155
+ } : {},
9156
+ ...context?.pageContext !== void 0 ? {
9157
+ pageContext: context.pageContext
9158
+ } : {},
9159
+ ...context?.uiCapabilities !== void 0 ? {
9160
+ uiCapabilities: context.uiCapabilities
9161
+ } : {},
9162
+ ...deps.host !== void 0 ? {
9163
+ host: deps.host
9164
+ } : {},
9165
+ emitUi: ui.emit
9166
+ }, deps.rolesPolicy, {
9167
+ approvedInput: proposal.input,
9168
+ ...deps.allowedTools !== void 0 ? {
9169
+ allowedTools: deps.allowedTools
9170
+ } : {}
9171
+ });
9172
+ return snapshotActionProposal({
9173
+ status: "succeeded",
9174
+ ...output !== void 0 ? {
9175
+ result: output
9176
+ } : {},
9177
+ ui: ui.components(),
9178
+ ...ui.text() ? {
9179
+ text: ui.text()
9180
+ } : {}
9181
+ });
9182
+ } catch (error) {
9183
+ return {
9184
+ status: "failed",
9185
+ error: error instanceof Error ? error.message : String(error),
9186
+ ui: ui.components(),
9187
+ ...ui.text() ? {
9188
+ text: ui.text()
9189
+ } : {}
9190
+ };
9191
+ }
9192
+ }
9193
+ };
9194
+
9195
+ // src/action-proposal-worker.ts
9196
+ var ActionProposalWorker = class {
9197
+ static {
9198
+ __name(this, "ActionProposalWorker");
9199
+ }
9200
+ options;
9201
+ leaseMs;
9202
+ pollIntervalMs;
9203
+ maxConcurrency;
9204
+ timer;
9205
+ stopped = false;
9206
+ renewals = /* @__PURE__ */ new Set();
9207
+ active;
9208
+ constructor(options) {
9209
+ this.options = options;
9210
+ this.leaseMs = options.leaseMs ?? 3e4;
9211
+ this.pollIntervalMs = options.pollIntervalMs ?? 1e3;
9212
+ this.maxConcurrency = options.maxConcurrency ?? 1;
9213
+ for (const value of [
9214
+ this.leaseMs,
9215
+ this.pollIntervalMs,
9216
+ this.maxConcurrency
9217
+ ]) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError("Worker configuration must be positive safe integers");
9218
+ if (this.pollIntervalMs >= this.leaseMs / 3) throw new RangeError("Worker poll interval must be below one third of lease");
9219
+ if (!options.workerId) throw new TypeError("workerId is required");
9220
+ }
9221
+ start() {
9222
+ if (this.timer !== void 0 || this.stopped) return;
9223
+ const tick = /* @__PURE__ */ __name(async () => {
9224
+ if (this.stopped) return;
9225
+ try {
9226
+ await this.runOnce();
9227
+ } catch (error) {
9228
+ this.options.onError?.(error);
9229
+ }
9230
+ if (!this.stopped) this.timer = setTimeout(tick, this.pollIntervalMs);
9231
+ }, "tick");
9232
+ this.timer = setTimeout(tick, 0);
9233
+ }
9234
+ async stop() {
9235
+ this.stopped = true;
9236
+ for (const timer of this.renewals) clearInterval(timer);
9237
+ this.renewals.clear();
9238
+ if (this.timer !== void 0) clearTimeout(this.timer);
9239
+ await this.active;
9240
+ }
9241
+ runOnce() {
9242
+ if (this.active) return this.active;
9243
+ if (this.stopped) return Promise.resolve();
9244
+ const task = this.tick();
9245
+ this.active = task;
9246
+ return task.finally(() => {
9247
+ if (this.active === task) this.active = void 0;
9248
+ });
9249
+ }
9250
+ async tick() {
9251
+ await this.options.store.expireActionProposals({
9252
+ limit: 100
9253
+ });
9254
+ const work = [];
9255
+ for (let index = 0; index < this.maxConcurrency && !this.stopped; index++) {
9256
+ const proposal = await this.options.store.claimNextActionProposal({
9257
+ workerId: this.options.workerId,
9258
+ leaseMs: this.leaseMs
9259
+ });
9260
+ if (!proposal) break;
9261
+ work.push(this.execute(proposal));
9262
+ }
9263
+ await Promise.all(work);
9264
+ for (let index = 0; index < 32 && !this.stopped; index++) {
9265
+ const delivery = await this.options.store.claimNextActionProposalOutcome({
9266
+ workerId: this.options.workerId,
9267
+ leaseMs: this.leaseMs
9268
+ });
9269
+ if (!delivery) break;
9270
+ await this.options.store.admitActionProposalOutcome(delivery.lease);
9271
+ }
9272
+ }
9273
+ async execute(proposal) {
9274
+ const lease = proposal.execution?.lease;
9275
+ if (!lease) throw new Error("Claimed action lacks execution lease");
9276
+ let fenced = true;
9277
+ let renewing;
9278
+ const renew = /* @__PURE__ */ __name(() => {
9279
+ if (renewing || !fenced) return;
9280
+ renewing = this.options.store.extendActionProposalLease(proposal, proposal.id, {
9281
+ token: lease.token,
9282
+ generation: lease.generation,
9283
+ leaseMs: this.leaseMs
9284
+ }).then((result2) => {
9285
+ if (result2.status !== "applied") fenced = false;
9286
+ }, (error) => {
9287
+ fenced = false;
9288
+ this.options.onError?.(error);
9289
+ }).finally(() => {
9290
+ renewing = void 0;
9291
+ });
9292
+ }, "renew");
9293
+ const timer = setInterval(renew, this.leaseMs / 3);
9294
+ this.renewals.add(timer);
9295
+ try {
9296
+ const result2 = await this.options.executor.execute(proposal);
9297
+ clearInterval(timer);
9298
+ await renewing;
9299
+ if (!fenced) return;
9300
+ const settled = await this.options.store.settleActionProposal(proposal, proposal.id, {
9301
+ ...result2,
9302
+ token: lease.token,
9303
+ generation: lease.generation
9304
+ });
9305
+ if (settled.status === "applied" && settled.proposal) await this.options.onSettled?.(settled.proposal);
9306
+ } finally {
9307
+ clearInterval(timer);
9308
+ this.renewals.delete(timer);
9309
+ await renewing;
9310
+ }
9311
+ }
9312
+ };
9313
+
9314
+ // src/action-proposal-capabilities.ts
9315
+ function assertIndependentActionCapabilities(store, resolver) {
9316
+ const candidate = store;
9317
+ if (!resolver || typeof resolver.resolve !== "function") throw new Error("Independent action mode requires a BackgroundActorResolver");
9318
+ if (!isChatQueueStore(store) || candidate.actionProposalAdmissionSupported === false) throw new Error("Independent action mode requires transactional thread admission");
9319
+ const required = [
9320
+ "createActionProposal",
9321
+ "getActionProposal",
9322
+ "listActionProposals",
9323
+ "decideActionProposal",
9324
+ "claimActionProposal",
9325
+ "extendActionProposalLease",
9326
+ "settleActionProposal",
9327
+ "claimNextActionProposal",
9328
+ "expireActionProposals",
9329
+ "getThreadActionProposalScope",
9330
+ "claimNextActionProposalOutcome",
9331
+ "admitActionProposalOutcome",
9332
+ "createReplacingActionProposal",
9333
+ "supersedeActionProposal"
9334
+ ];
9335
+ 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");
9336
+ }
9337
+ __name(assertIndependentActionCapabilities, "assertIndependentActionCapabilities");
9338
+
9339
+ // src/functional-tool.ts
9340
+ function createFunctionalTool(definition) {
9341
+ const { input, execute, present, preflight, isEnabled, canUse, describe, kind, ...metadata } = definition;
9342
+ return {
9343
+ spec: {
9344
+ ...metadata,
9345
+ kind: kind ?? "read",
9346
+ inputSchema: input
9347
+ },
9348
+ handler: {
9349
+ execute: execute.bind(definition),
9350
+ ...present === void 0 ? {} : {
9351
+ present: present.bind(definition)
9352
+ },
9353
+ ...preflight === void 0 ? {} : {
9354
+ preflight: preflight.bind(definition)
9355
+ },
9356
+ ...isEnabled === void 0 ? {} : {
9357
+ isEnabled: isEnabled.bind(definition)
9358
+ },
9359
+ ...canUse === void 0 ? {} : {
9360
+ canUse: canUse.bind(definition)
9361
+ },
9362
+ ...describe === void 0 ? {} : {
9363
+ describe: describe.bind(definition)
9364
+ }
9365
+ }
9366
+ };
9367
+ }
9368
+ __name(createFunctionalTool, "createFunctionalTool");
6509
9369
  export {
6510
9370
  AGENT_ACTOR_DIRECTORY,
6511
9371
  AGENT_ACTOR_RESOLVER,
@@ -6539,6 +9399,8 @@ export {
6539
9399
  APPROVAL_EXPIRED_REASON,
6540
9400
  ASK_TOOL_DESCRIPTION,
6541
9401
  ASK_TOOL_NAME,
9402
+ ActionProposalExecutor,
9403
+ ActionProposalWorker,
6542
9404
  AgentRegistry,
6543
9405
  AgentStreamError,
6544
9406
  CONFIRM_JSON_SCHEMA_PROPERTIES,
@@ -6553,12 +9415,15 @@ export {
6553
9415
  DEFAULT_MAX_SKILLS,
6554
9416
  DEFAULT_REFUSAL_REASON,
6555
9417
  DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION,
9418
+ DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES,
9419
+ DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY,
6556
9420
  DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS,
6557
9421
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
6558
9422
  DefaultApprovalPolicy,
6559
9423
  DefaultRolesPolicy,
6560
9424
  ELICITATION_INPUT_TYPES,
6561
9425
  GLOBAL_SCOPE,
9426
+ InMemoryActionProposalStore,
6562
9427
  InMemoryAgentStore,
6563
9428
  InMemoryConfirmTokenStore,
6564
9429
  MAX_ASK_QUESTIONS,
@@ -6582,15 +9447,25 @@ export {
6582
9447
  THREAD_DETAIL_CONTENT_CHARS,
6583
9448
  ToolDisabledError,
6584
9449
  ToolForbiddenError,
9450
+ ToolInputDriftError,
6585
9451
  ToolInputInvalidError,
6586
9452
  ToolNotFoundError,
9453
+ ToolPreflightDeniedError,
6587
9454
  ToolRegistry,
6588
9455
  UNFINISHED_TOOL_CALL,
9456
+ actionProposalCreationMatches,
9457
+ actionProposalOutcomeFenceValid,
9458
+ actionProposalOutcomeScope,
9459
+ actionProposalOutcomeText,
9460
+ actionProposalReceipt,
9461
+ actionProposalScopeMatches,
6589
9462
  actorScope,
6590
9463
  agentDiagnosticKey,
6591
9464
  agentFailureCode,
6592
9465
  askInputSchema,
6593
9466
  askToolDefinition,
9467
+ assertIndependentActionCapabilities,
9468
+ attachActionProposalOutcome,
6594
9469
  bucketByActor,
6595
9470
  bucketByModel,
6596
9471
  bucketByThread,
@@ -6598,12 +9473,16 @@ export {
6598
9473
  buildMemoryBlock,
6599
9474
  buildSkillsBlock,
6600
9475
  canActorUseTool,
6601
- canonicalJson,
9476
+ canonicalActionProposalJson,
9477
+ canonicalJson2 as canonicalJson,
9478
+ claimActionProposalOutcome,
6602
9479
  closeEmptyRoles,
6603
9480
  compositeSkillProvider,
6604
9481
  confirmTokenExpiry,
6605
9482
  createFrameBuffer,
9483
+ createFunctionalTool,
6606
9484
  createIncrementalGate,
9485
+ createNegotiatedUiCollector,
6607
9486
  createNoopEmitUi,
6608
9487
  createUiCollector,
6609
9488
  danglingToolCallIds,
@@ -6615,7 +9494,9 @@ export {
6615
9494
  detachedDelivered,
6616
9495
  detachedStarted,
6617
9496
  detachedUnsettled,
9497
+ emitToolPresentations,
6618
9498
  encodeStreamEvent,
9499
+ escapeUnsafeToolUiText,
6619
9500
  estimateCost,
6620
9501
  estimateMessageTokens,
6621
9502
  exhaustedWindow,
@@ -6630,6 +9511,7 @@ export {
6630
9511
  gateFollowUps,
6631
9512
  gateTail,
6632
9513
  hashConfirmToken,
9514
+ initialActionProposal,
6633
9515
  intersectAllowLists,
6634
9516
  invokeWithTransientRetry,
6635
9517
  isChatQueueStore,
@@ -6648,7 +9530,10 @@ export {
6648
9530
  observeTurnFrames,
6649
9531
  offerMemories,
6650
9532
  offerSkills,
9533
+ parseTextActionProposalCommand,
6651
9534
  personaCatalogEntry,
9535
+ prepareActionProposal,
9536
+ ptBrActionProposalText,
6652
9537
  publishAgentDelegated,
6653
9538
  publishAgentMemoryResolved,
6654
9539
  publishAgentMemoryWritten,
@@ -6674,12 +9559,14 @@ export {
6674
9559
  rememberToolDefinition,
6675
9560
  renderElicitationAnswers,
6676
9561
  repairInstruction,
9562
+ resolveActionProposalApproval,
6677
9563
  resolveElicitation,
6678
9564
  resolveGateLookback,
6679
9565
  resolveMemoryDigest,
6680
9566
  resolveOutputGateMode,
6681
9567
  resolvePersonaAlias,
6682
9568
  resolveSkillCatalog,
9569
+ resolveTextActionProposalDecision,
6683
9570
  resolveToolTransientRetryNumbers,
6684
9571
  rollupThreadUsage,
6685
9572
  runAgentLoop,
@@ -6696,18 +9583,32 @@ export {
6696
9583
  skillInputSchema,
6697
9584
  skillToolDefinition,
6698
9585
  skillWriteVerdict,
9586
+ snapshotActionProposal,
6699
9587
  stampToolKinds,
6700
9588
  staticModelCatalog,
6701
9589
  staticSkillProvider,
6702
9590
  streamFailure,
6703
9591
  summarizeWithModel,
6704
9592
  tenantScope,
9593
+ textActionProposalReply,
9594
+ toActionProposalMutationView,
9595
+ toActionProposalView,
6705
9596
  toolCallApprovalFromRow,
6706
9597
  toolCallContext,
6707
9598
  traceLlmTurn,
6708
9599
  traceToolExecution,
9600
+ transitionActionProposalClaim,
9601
+ transitionActionProposalDecision,
9602
+ transitionActionProposalLease,
9603
+ transitionActionProposalSettlement,
9604
+ transitionActionProposalSupersession,
6709
9605
  truncateDetailContent,
6710
9606
  unwrapToolStepOutput,
9607
+ validateActionProposalCreation,
9608
+ validateActionProposalDiscoveryIndexBatch,
9609
+ validateActionProposalExpiryBatch,
9610
+ validateActionProposalListQuery,
9611
+ validateActionProposalWorkerClaim,
6711
9612
  validateElicitationAnswer,
6712
9613
  validateElicitationValue,
6713
9614
  validateStructured,
@@ -6720,6 +9621,7 @@ export {
6720
9621
  withSkillTool,
6721
9622
  withToolTimeout,
6722
9623
  withTurnFrames,
9624
+ wrapToolPreflightDenied,
6723
9625
  wrapToolStepOutput,
6724
9626
  writeMemory
6725
9627
  };