@dudousxd/nestjs-agent-core 0.38.1 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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 = "*";
@@ -331,6 +363,9 @@ function queuedMessageView(message) {
331
363
  ...message.agentName !== void 0 ? {
332
364
  agentName: message.agentName
333
365
  } : {},
366
+ ...message.persona !== void 0 ? {
367
+ persona: message.persona
368
+ } : {},
334
369
  ...message.model !== void 0 ? {
335
370
  model: message.model
336
371
  } : {},
@@ -497,12 +532,18 @@ function toolCallApprovalFromRow(row) {
497
532
  if (row.approver === null || row.approver === void 0) {
498
533
  return null;
499
534
  }
500
- const status = row.status === "pending_approval" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
535
+ const status = row.status === "pending_approval" || row.status === "proposed" ? "pending" : row.status === "rejected" ? "rejected" : row.status === "expired" ? "expired" : "approved";
501
536
  const expiresAt = row.expiresAt instanceof Date ? row.expiresAt.toISOString() : typeof row.expiresAt === "string" ? row.expiresAt : void 0;
502
537
  const decided = status === "approved" || status === "rejected";
503
538
  return {
504
539
  toolCallId: row.toolCallId,
540
+ ...row.proposalId != null ? {
541
+ proposalId: row.proposalId
542
+ } : {},
505
543
  approver: row.approver,
544
+ ...row.confirmation != null ? {
545
+ confirmation: row.confirmation
546
+ } : {},
506
547
  status,
507
548
  ...expiresAt !== void 0 ? {
508
549
  expiresAt
@@ -553,9 +594,9 @@ function bucketByModel(rows, prices) {
553
594
  bucket.costUsd += rowCost(row, prices);
554
595
  byModel.set(row.modelId, bucket);
555
596
  }
556
- const result = [];
597
+ const result2 = [];
557
598
  for (const [modelId, bucket] of byModel) {
558
- result.push({
599
+ result2.push({
559
600
  modelId,
560
601
  requests: bucket.requests,
561
602
  inputTokens: bucket.inputTokens,
@@ -563,8 +604,8 @@ function bucketByModel(rows, prices) {
563
604
  costUsd: bucket.costUsd
564
605
  });
565
606
  }
566
- result.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
567
- return result;
607
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
608
+ return result2;
568
609
  }
569
610
  __name(bucketByModel, "bucketByModel");
570
611
  function bucketByActor(rows, prices) {
@@ -582,9 +623,9 @@ function bucketByActor(rows, prices) {
582
623
  bucket.threadIds.add(row.threadId);
583
624
  byActor.set(row.actorRef, bucket);
584
625
  }
585
- const result = [];
626
+ const result2 = [];
586
627
  for (const [actorRef, bucket] of byActor) {
587
- result.push({
628
+ result2.push({
588
629
  actorRef,
589
630
  requests: bucket.requests,
590
631
  totalTokens: bucket.totalTokens,
@@ -592,8 +633,8 @@ function bucketByActor(rows, prices) {
592
633
  threadCount: bucket.threadIds.size
593
634
  });
594
635
  }
595
- result.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
596
- return result;
636
+ result2.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
637
+ return result2;
597
638
  }
598
639
  __name(bucketByActor, "bucketByActor");
599
640
  function bucketByThread(rows, prices, threads, options) {
@@ -609,13 +650,13 @@ function bucketByThread(rows, prices, threads, options) {
609
650
  bucket.costUsd += rowCost(row, prices);
610
651
  byThread.set(row.threadId, bucket);
611
652
  }
612
- const result = [];
653
+ const result2 = [];
613
654
  for (const [threadId, bucket] of byThread) {
614
655
  const thread = threads.get(threadId);
615
656
  if (thread === void 0 && !options.includeUnknownThreads) {
616
657
  continue;
617
658
  }
618
- result.push({
659
+ result2.push({
619
660
  threadId,
620
661
  title: thread?.title ?? "",
621
662
  actorRef: thread?.actorRef ?? "",
@@ -624,8 +665,8 @@ function bucketByThread(rows, prices, threads, options) {
624
665
  costUsd: bucket.costUsd
625
666
  });
626
667
  }
627
- result.sort((left, right) => right.costUsd - left.costUsd || left.threadId.localeCompare(right.threadId));
628
- 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);
629
670
  }
630
671
  __name(bucketByThread, "bucketByThread");
631
672
  function rollupThreadUsage(rows, prices) {
@@ -657,16 +698,16 @@ function bucketUsageTrend(rows, prices) {
657
698
  bucket.costUsd += rowCost(row, prices);
658
699
  byDay.set(row.day, bucket);
659
700
  }
660
- const result = [];
701
+ const result2 = [];
661
702
  for (const [day, bucket] of byDay) {
662
- result.push({
703
+ result2.push({
663
704
  day,
664
705
  totalTokens: bucket.totalTokens,
665
706
  costUsd: bucket.costUsd
666
707
  });
667
708
  }
668
- result.sort((left, right) => left.day.localeCompare(right.day));
669
- return result;
709
+ result2.sort((left, right) => left.day.localeCompare(right.day));
710
+ return result2;
670
711
  }
671
712
  __name(bucketUsageTrend, "bucketUsageTrend");
672
713
  function dayBoundsUtc(range) {
@@ -723,6 +764,54 @@ function filterToolsByAllowList(tools, allowedTools) {
723
764
  }
724
765
  __name(filterToolsByAllowList, "filterToolsByAllowList");
725
766
 
767
+ // src/personas.ts
768
+ function intersectAllowLists(first, second) {
769
+ if (first === void 0) {
770
+ return second === void 0 ? void 0 : [
771
+ ...second
772
+ ];
773
+ }
774
+ if (second === void 0) {
775
+ return [
776
+ ...first
777
+ ];
778
+ }
779
+ const allowed = new Set(second);
780
+ return first.filter((name) => allowed.has(name));
781
+ }
782
+ __name(intersectAllowLists, "intersectAllowLists");
783
+ function findPersona(definition, id) {
784
+ if (id === void 0) {
785
+ return void 0;
786
+ }
787
+ return definition?.personas?.find((persona) => persona.id === id);
788
+ }
789
+ __name(findPersona, "findPersona");
790
+ function personaCatalogEntry(persona) {
791
+ return {
792
+ id: persona.id,
793
+ label: persona.label,
794
+ ...persona.description !== void 0 ? {
795
+ description: persona.description
796
+ } : {}
797
+ };
798
+ }
799
+ __name(personaCatalogEntry, "personaCatalogEntry");
800
+ function resolvePersonaAlias(definitions, name) {
801
+ for (const definition of definitions) {
802
+ for (const persona of definition.personas ?? []) {
803
+ if (persona.aliases?.includes(name) === true) {
804
+ return {
805
+ agent: definition.name,
806
+ persona: persona.id
807
+ };
808
+ }
809
+ }
810
+ }
811
+ return void 0;
812
+ }
813
+ __name(resolvePersonaAlias, "resolvePersonaAlias");
814
+
726
815
  // src/history.ts
727
816
  function estimateMessageTokens(message) {
728
817
  const extras = (message.toolCalls !== void 0 ? JSON.stringify(message.toolCalls).length : 0) + (message.toolResults !== void 0 ? JSON.stringify(message.toolResults).length : 0);
@@ -1062,13 +1151,13 @@ async function validateStructured(schema, text, reported) {
1062
1151
  issues: []
1063
1152
  };
1064
1153
  }
1065
- const result = await schema["~standard"].validate(candidate);
1066
- return result.issues !== void 0 ? {
1154
+ const result2 = await schema["~standard"].validate(candidate);
1155
+ return result2.issues !== void 0 ? {
1067
1156
  ok: false,
1068
- issues: result.issues
1157
+ issues: result2.issues
1069
1158
  } : {
1070
1159
  ok: true,
1071
- value: result.value
1160
+ value: result2.value
1072
1161
  };
1073
1162
  }
1074
1163
  __name(validateStructured, "validateStructured");
@@ -2247,12 +2336,12 @@ function memoryWriteVerdict(request) {
2247
2336
  };
2248
2337
  }
2249
2338
  __name(memoryWriteVerdict, "memoryWriteVerdict");
2250
- function memoryForgetVerdict({ record, actor }) {
2251
- return record.scope === actorScope(actor) ? {
2339
+ function memoryForgetVerdict({ record: record2, actor }) {
2340
+ return record2.scope === actorScope(actor) ? {
2252
2341
  allowed: true
2253
2342
  } : {
2254
2343
  allowed: false,
2255
- 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`
2256
2345
  };
2257
2346
  }
2258
2347
  __name(memoryForgetVerdict, "memoryForgetVerdict");
@@ -2295,7 +2384,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2295
2384
  error: verdict.reason
2296
2385
  };
2297
2386
  }
2298
- const record = await write({
2387
+ const record2 = await write({
2299
2388
  key,
2300
2389
  text,
2301
2390
  scope,
@@ -2309,7 +2398,7 @@ async function writeMemory({ config, digest, call, ctx, runId }) {
2309
2398
  });
2310
2399
  return {
2311
2400
  ok: true,
2312
- record
2401
+ record: record2
2313
2402
  };
2314
2403
  }
2315
2404
  __name(writeMemory, "writeMemory");
@@ -2361,7 +2450,7 @@ function danglingToolCallIds(messages) {
2361
2450
  if (message.role !== "assistant" || calls.length === 0) {
2362
2451
  continue;
2363
2452
  }
2364
- const settled = new Set((message.toolResults ?? []).map((result) => result.id));
2453
+ const settled = new Set((message.toolResults ?? []).map((result2) => result2.id));
2365
2454
  for (const call of calls) {
2366
2455
  if (!settled.has(call.id)) {
2367
2456
  ids.push(call.id);
@@ -2429,7 +2518,7 @@ function settleDanglingToolCalls(messages, outcomes = []) {
2429
2518
  return message;
2430
2519
  }
2431
2520
  const results = message.toolResults ?? [];
2432
- const settled = new Set(results.map((result) => result.id));
2521
+ const settled = new Set(results.map((result2) => result2.id));
2433
2522
  const missing = calls.filter((call) => !settled.has(call.id));
2434
2523
  if (missing.length === 0) {
2435
2524
  return message;
@@ -2537,6 +2626,1111 @@ async function settleUnsettledDelegation(args) {
2537
2626
  }
2538
2627
  __name(settleUnsettledDelegation, "settleUnsettledDelegation");
2539
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(row, now, ui = [], text) {
2648
+ if (row.outcome) return row;
2649
+ const execution = row.execution;
2650
+ const outcome = {
2651
+ id: `${row.id}:outcome:1`,
2652
+ proposalId: row.id,
2653
+ outcomeVersion: 1,
2654
+ tenantRef: row.tenantRef,
2655
+ actorRef: row.actorRef,
2656
+ threadId: row.threadId,
2657
+ originRunId: row.originRunId,
2658
+ originToolCallId: row.originToolCallId,
2659
+ toolName: row.toolName,
2660
+ decision: row.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
+ } : row.decisionAudit?.reason !== void 0 ? {
2675
+ error: row.decisionAudit.reason
2676
+ } : {}
2677
+ };
2678
+ return snapshotActionProposal({
2679
+ ...row,
2680
+ outcome,
2681
+ outcomeDelivery: {
2682
+ status: "pending",
2683
+ generation: 0,
2684
+ lease: null
2685
+ }
2686
+ });
2687
+ }
2688
+ __name(attachActionProposalOutcome, "attachActionProposalOutcome");
2689
+ function claimActionProposalOutcome(row, command, now, token) {
2690
+ validateActionProposalWorkerClaim(command, now);
2691
+ const delivery = row.outcomeDelivery;
2692
+ if (!row.outcome || !delivery || delivery.status !== "pending" || delivery.lease && now < delivery.lease.expiresAt) return null;
2693
+ const generation = delivery.generation + 1;
2694
+ return snapshotActionProposal({
2695
+ ...row,
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(row, 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 = row.outcomeDelivery;
2713
+ return row.outcome?.id === command.outcomeId && delivery?.status === "pending" && delivery.generation === command.generation && delivery.lease?.token === command.token && delivery.lease.generation === command.generation && now < delivery.lease.expiresAt;
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 s = schema;
2889
+ if (typeof s.$ref === "string") {
2890
+ const target = resolveRef(root, s.$ref);
2891
+ if (target === void 0) {
2892
+ issues.push({
2893
+ path,
2894
+ message: `references unknown schema ${s.$ref}`
2895
+ });
2896
+ return;
2897
+ }
2898
+ check(target, value, path, root, issues, depth + 1);
2899
+ }
2900
+ if (s.type !== void 0) {
2901
+ const types = Array.isArray(s.type) ? s.type : [
2902
+ s.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(s.enum) && !s.enum.some((option) => deepEqual(option, value))) {
2913
+ issues.push({
2914
+ path,
2915
+ message: `must be one of ${s.enum.map((option) => JSON.stringify(option)).join(", ")}`
2916
+ });
2917
+ }
2918
+ if ("const" in s && !deepEqual(s.const, value)) {
2919
+ issues.push({
2920
+ path,
2921
+ message: `must be ${JSON.stringify(s.const)}`
2922
+ });
2923
+ }
2924
+ if (typeof value === "string") {
2925
+ if (typeof s.minLength === "number" && value.length < s.minLength) {
2926
+ issues.push({
2927
+ path,
2928
+ message: `must be at least ${s.minLength} characters`
2929
+ });
2930
+ }
2931
+ if (typeof s.maxLength === "number" && value.length > s.maxLength) {
2932
+ issues.push({
2933
+ path,
2934
+ message: `must be at most ${s.maxLength} characters`
2935
+ });
2936
+ }
2937
+ if (typeof s.pattern === "string") {
2938
+ let pattern;
2939
+ try {
2940
+ pattern = new RegExp(s.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 ${s.pattern}`
2948
+ });
2949
+ }
2950
+ }
2951
+ }
2952
+ if (typeof value === "number") {
2953
+ if (typeof s.minimum === "number" && value < s.minimum) {
2954
+ issues.push({
2955
+ path,
2956
+ message: `must be >= ${s.minimum}`
2957
+ });
2958
+ }
2959
+ if (typeof s.maximum === "number" && value > s.maximum) {
2960
+ issues.push({
2961
+ path,
2962
+ message: `must be <= ${s.maximum}`
2963
+ });
2964
+ }
2965
+ }
2966
+ if (Array.isArray(value)) {
2967
+ if (typeof s.minItems === "number" && value.length < s.minItems) {
2968
+ issues.push({
2969
+ path,
2970
+ message: `must have at least ${s.minItems} items`
2971
+ });
2972
+ }
2973
+ if (typeof s.maxItems === "number" && value.length > s.maxItems) {
2974
+ issues.push({
2975
+ path,
2976
+ message: `must have at most ${s.maxItems} items`
2977
+ });
2978
+ }
2979
+ if (s.items !== void 0 && !Array.isArray(s.items)) {
2980
+ value.forEach((item, index) => {
2981
+ check(s.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 s.properties === "object" && s.properties !== null ? s.properties : {};
2991
+ if (Array.isArray(s.required)) {
2992
+ for (const key of s.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 (s.additionalProperties === false) {
3013
+ issues.push({
3014
+ path: [
3015
+ ...path,
3016
+ key
3017
+ ],
3018
+ message: "is not a known property"
3019
+ });
3020
+ } else if (typeof s.additionalProperties === "object" && s.additionalProperties !== null) {
3021
+ check(s.additionalProperties, entry, [
3022
+ ...path,
3023
+ key
3024
+ ], root, issues, depth + 1);
3025
+ }
3026
+ }
3027
+ }
3028
+ if (Array.isArray(s.allOf)) {
3029
+ for (const sub of s.allOf) check(sub, value, path, root, issues, depth + 1);
3030
+ }
3031
+ if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
3032
+ const options = Array.isArray(s.anyOf) ? s.anyOf : s.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(s.anyOf) ? passing === 0 : passing !== 1) {
3039
+ issues.push({
3040
+ path,
3041
+ message: Array.isArray(s.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
3042
+ });
3043
+ }
3044
+ }
3045
+ if (s.not !== void 0) {
3046
+ const nested = [];
3047
+ check(s.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.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.props, props, validator);
3121
+ },
3122
+ jsonSchemaFor(component) {
3123
+ const definition = byName.get(component);
3124
+ return definition === void 0 ? void 0 : toJsonSchema(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 = {}) {
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 = 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(row, scope) {
3553
+ return row.tenantRef === scope.tenantRef && row.actorRef === scope.actorRef && row.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(row, input) {
3570
+ validateActionProposalCreation(input);
3571
+ const { decision: _decision, decisionAudit: _audit, execution: _execution, createdAt: _created, updatedAt: _updated, supersededBy: _supersededBy, outcome: _outcome, outcomeDelivery: _delivery, ...original } = row;
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(row, 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 (row.decision !== "pending") {
3594
+ return result(row.decision === command.decision ? "unchanged" : row.decision === "expired" ? "expired" : "conflict", row);
3595
+ }
3596
+ const next = snapshotActionProposal(row);
3597
+ if (row.expiresAt !== null && now >= row.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", row);
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(row, command, now, token) {
3636
+ checkLeaseMs(command.leaseMs, now);
3637
+ checkString(command.workerId);
3638
+ checkString(token);
3639
+ const execution = row.execution;
3640
+ if (row.decision !== "approved" || !execution || execution.status !== "queued" && !(execution.status === "executing" && execution.lease && now >= execution.lease.expiresAt)) return result("conflict", row);
3641
+ const next = snapshotActionProposal(row);
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(row, 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 = row.execution;
3662
+ if (row.decision !== "approved" || work?.status !== "executing" || !work.lease || work.generation !== command.generation || work.lease.generation !== command.generation || work.lease.token !== command.token) return "conflict";
3663
+ return now >= work.lease.expiresAt ? "expired" : "valid";
3664
+ }
3665
+ __name(leaseStatus, "leaseStatus");
3666
+ function transitionActionProposalLease(row, command, now) {
3667
+ checkLeaseMs(command.leaseMs, now);
3668
+ const status = leaseStatus(row, command, now);
3669
+ if (status !== "valid") return result(status, row);
3670
+ const next = snapshotActionProposal(row);
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(row, 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(row, command, now);
3681
+ if (status !== "valid") return result(status, row);
3682
+ const next = snapshotActionProposal(row);
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(row, replacement, command, now) {
3703
+ checkTime(now);
3704
+ checkString(command.replacementProposalId);
3705
+ checkString(command.actorRef);
3706
+ checkString(command.via);
3707
+ if (replacement.id !== command.replacementProposalId || row.id === replacement.id || !actionProposalScopeMatches(row, replacement) || row.toolName !== replacement.toolName || !row.replacementKey || row.replacementKey !== replacement.replacementKey) return result("conflict", row);
3708
+ if (row.decision === "superseded") return result(row.supersededBy === replacement.id ? "unchanged" : "conflict", row);
3709
+ if (row.decision !== "pending") return result("conflict", row);
3710
+ if (row.expiresAt !== null && now >= row.expiresAt) return {
3711
+ ...transitionActionProposalDecision(row, {
3712
+ decision: "expired",
3713
+ actorRef: "system",
3714
+ via: "expiry"
3715
+ }, now),
3716
+ status: "expired"
3717
+ };
3718
+ const next = snapshotActionProposal({
3719
+ ...row,
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
+
2540
3734
  // src/tool-registry.ts
2541
3735
  var ToolForbiddenError = class extends Error {
2542
3736
  static {
@@ -2579,6 +3773,35 @@ var ToolInputInvalidError = class extends Error {
2579
3773
  this.name = "ToolInputInvalidError";
2580
3774
  }
2581
3775
  };
3776
+ var ToolPreflightDeniedError = class extends Error {
3777
+ static {
3778
+ __name(this, "ToolPreflightDeniedError");
3779
+ }
3780
+ toolName;
3781
+ reason;
3782
+ constructor(toolName, reason) {
3783
+ super(reason), this.toolName = toolName, this.reason = reason;
3784
+ this.name = "ToolPreflightDeniedError";
3785
+ }
3786
+ };
3787
+ var ToolInputDriftError = class extends Error {
3788
+ static {
3789
+ __name(this, "ToolInputDriftError");
3790
+ }
3791
+ toolName;
3792
+ constructor(toolName) {
3793
+ super(`Tool "${toolName}" input differs from its trusted snapshot`), this.toolName = toolName;
3794
+ this.name = "ToolInputDriftError";
3795
+ }
3796
+ };
3797
+ function assertTrustedInput(name, value, approved) {
3798
+ try {
3799
+ if (canonicalActionProposalJson(value) === canonicalActionProposalJson(approved)) return;
3800
+ } catch {
3801
+ }
3802
+ throw new ToolInputDriftError(name);
3803
+ }
3804
+ __name(assertTrustedInput, "assertTrustedInput");
2582
3805
  var ToolRegistry = class {
2583
3806
  static {
2584
3807
  __name(this, "ToolRegistry");
@@ -2626,11 +3849,12 @@ var ToolRegistry = class {
2626
3849
  */
2627
3850
  async definitionsFor(actor, policy, allowedTools, scope = {}) {
2628
3851
  const visible = await this.visibleEntries(actor, policy, allowedTools);
2629
- return Promise.all(visible.map(async ({ spec, handler }) => {
3852
+ const definitions = await Promise.all(visible.map(async ({ spec, handler }) => {
2630
3853
  const override = handler.describe === void 0 ? void 0 : await handler.describe({
2631
3854
  actor,
2632
3855
  ...scope
2633
3856
  });
3857
+ if (override?.available === false) return null;
2634
3858
  return {
2635
3859
  name: spec.name,
2636
3860
  kind: spec.kind,
@@ -2638,6 +3862,7 @@ var ToolRegistry = class {
2638
3862
  inputSchema: override?.inputSchema ?? spec.inputSchema
2639
3863
  };
2640
3864
  }));
3865
+ return definitions.filter((definition) => definition !== null);
2641
3866
  }
2642
3867
  /**
2643
3868
  * The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list,
@@ -2662,7 +3887,41 @@ var ToolRegistry = class {
2662
3887
  * a call can reach here from a replayed durable step or an approval granted before the flag
2663
3888
  * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
2664
3889
  */
2665
- async invoke(name, input, ctx, policy) {
3890
+ async invoke(name, input, ctx, policy, options = {}) {
3891
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
3892
+ const trusted = Object.hasOwn(options, "approvedInput");
3893
+ if (trusted) assertTrustedInput(name, value, options.approvedInput);
3894
+ const executionInput = trusted ? snapshotActionProposal(value) : value;
3895
+ const hookInput = trusted ? snapshotActionProposal(executionInput) : value;
3896
+ const result2 = entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
3897
+ phase: "execute"
3898
+ }) : void 0;
3899
+ if (trusted) assertTrustedInput(name, hookInput, executionInput);
3900
+ if (result2?.status === "denied") throw new ToolPreflightDeniedError(name, result2.reason);
3901
+ if (result2?.status === "completed") return result2.output;
3902
+ return entry.handler.execute(executionInput, withEmit);
3903
+ }
3904
+ /** Authorize and validate without executing. The caller journals this result before approval. */
3905
+ async prepare(name, input, ctx, policy, options = {}) {
3906
+ return (await this.prepareValidated(name, input, ctx, policy, options)).preflight;
3907
+ }
3908
+ /** Expose the single schema parse; snapshotInput opts proposal callers into immutable JSON. */
3909
+ async prepareValidated(name, input, ctx, policy, options = {}) {
3910
+ const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
3911
+ const normalized = options.snapshotInput === true ? snapshotActionProposal(value) : value;
3912
+ const hookInput = options.snapshotInput === true ? snapshotActionProposal(normalized) : value;
3913
+ const preflight = (entry.spec.kind === "action" ? await entry.handler.preflight?.(hookInput, withEmit, {
3914
+ phase: "prepare"
3915
+ }) : void 0) ?? {
3916
+ status: "ready"
3917
+ };
3918
+ if (options.snapshotInput === true) assertTrustedInput(name, hookInput, normalized);
3919
+ return {
3920
+ input: normalized,
3921
+ preflight
3922
+ };
3923
+ }
3924
+ async validated(name, input, ctx, policy, options) {
2666
3925
  const entry = this.entries.get(name);
2667
3926
  if (entry === void 0) {
2668
3927
  throw new ToolNotFoundError(name);
@@ -2676,6 +3935,9 @@ var ToolRegistry = class {
2676
3935
  if (!await canActorUseTool(ctx.actor, entry.handler)) {
2677
3936
  throw new ToolForbiddenError(name);
2678
3937
  }
3938
+ if (options.allowedTools !== void 0 && !options.allowedTools.includes(name)) {
3939
+ throw new ToolForbiddenError(name);
3940
+ }
2679
3941
  const validation = await entry.spec.inputSchema["~standard"].validate(input);
2680
3942
  if (validation.issues !== void 0) {
2681
3943
  throw new ToolInputInvalidError(name, validation.issues);
@@ -2684,7 +3946,11 @@ var ToolRegistry = class {
2684
3946
  ...ctx,
2685
3947
  emitUi: createNoopEmitUi(ctx.requestId)
2686
3948
  };
2687
- return entry.handler.execute(validation.value, withEmit);
3949
+ return {
3950
+ entry,
3951
+ value: validation.value,
3952
+ ctx: withEmit
3953
+ };
2688
3954
  }
2689
3955
  };
2690
3956
  var DefaultRolesPolicy = class {
@@ -2731,6 +3997,69 @@ __name(closeEmptyRoles, "closeEmptyRoles");
2731
3997
  import { createHash } from "crypto";
2732
3998
  import { trace } from "@dudousxd/nestjs-diagnostics";
2733
3999
 
4000
+ // src/action-proposal-approval.ts
4001
+ async function resolveActionProposalApproval(input) {
4002
+ const policy = input.policy ?? new DefaultApprovalPolicy();
4003
+ const requirement = await policy.requirementFor(input.tool, input.actor, input.thread);
4004
+ if (!requirement.required) return {
4005
+ mode: "auto"
4006
+ };
4007
+ const remembered = await input.store.rememberedApprovals?.(input.thread.threadId) ?? [];
4008
+ if (remembered.includes(input.tool.name)) return {
4009
+ mode: "remembered",
4010
+ approver: requirement.approver
4011
+ };
4012
+ const roundedTtl = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) ? Math.floor(requirement.ttlMs) : 0;
4013
+ if (roundedTtl <= 0) return {
4014
+ mode: "ask",
4015
+ approver: requirement.approver
4016
+ };
4017
+ const now = (input.clock ?? Date.now)();
4018
+ if (!Number.isSafeInteger(now) || !Number.isSafeInteger(roundedTtl) || !Number.isSafeInteger(now + roundedTtl)) throw new RangeError("Invalid action approval deadline");
4019
+ return {
4020
+ mode: "ask",
4021
+ approver: requirement.approver,
4022
+ ttlMs: roundedTtl,
4023
+ expiresAt: new Date(now + roundedTtl).toISOString()
4024
+ };
4025
+ }
4026
+ __name(resolveActionProposalApproval, "resolveActionProposalApproval");
4027
+
4028
+ // src/action-proposal-preparation.ts
4029
+ async function prepareActionProposal(registry, name, input, ctx, policy, options = {}) {
4030
+ const preparationInput = snapshotActionProposal(input);
4031
+ const prepared = await registry.prepareValidated(name, snapshotActionProposal(preparationInput), ctx, policy, {
4032
+ ...options,
4033
+ snapshotInput: true
4034
+ });
4035
+ let replacementKey;
4036
+ const replacement = registry.spec(name)?.replacementKey;
4037
+ if (prepared.preflight.status === "ready" && replacement !== void 0) {
4038
+ replacementKey = typeof replacement === "string" ? replacement : await replacement(snapshotActionProposal(prepared.input), ctx);
4039
+ 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");
4040
+ }
4041
+ return {
4042
+ ...replacementKey !== void 0 ? {
4043
+ replacementKey
4044
+ } : {},
4045
+ preparationInput,
4046
+ input: prepared.input,
4047
+ // Completed results keep their normal handler contract; they never create a proposal.
4048
+ preflight: prepared.preflight.status === "ready" ? snapshotActionProposal(prepared.preflight) : prepared.preflight
4049
+ };
4050
+ }
4051
+ __name(prepareActionProposal, "prepareActionProposal");
4052
+
4053
+ // src/action-proposal-receipt.ts
4054
+ function actionProposalReceipt(proposalId) {
4055
+ return {
4056
+ proposalId,
4057
+ status: "pending",
4058
+ executed: false
4059
+ };
4060
+ }
4061
+ __name(actionProposalReceipt, "actionProposalReceipt");
4062
+
2734
4063
  // src/diagnostics.ts
2735
4064
  import { emit } from "@dudousxd/nestjs-diagnostics";
2736
4065
  function publishAgentRunStarted(payload) {
@@ -2806,6 +4135,56 @@ function agentDiagnosticKey(event) {
2806
4135
  }
2807
4136
  __name(agentDiagnosticKey, "agentDiagnosticKey");
2808
4137
 
4138
+ // src/negotiated-tool-ui.ts
4139
+ function createNegotiatedUiCollector(toolCallId, scope, resolveCatalog, write) {
4140
+ const collector = createUiCollector(toolCallId, write);
4141
+ const texts = /* @__PURE__ */ new Map();
4142
+ let next = 0;
4143
+ return {
4144
+ components: collector.components,
4145
+ text: /* @__PURE__ */ __name(() => [
4146
+ ...texts.values()
4147
+ ].join("\n"), "text"),
4148
+ restart: /* @__PURE__ */ __name(() => {
4149
+ next = 0;
4150
+ texts.clear();
4151
+ collector.restart();
4152
+ }, "restart"),
4153
+ emit: /* @__PURE__ */ __name(async (component, props, options = {}) => {
4154
+ if (!resolveCatalog) {
4155
+ if (scope.uiCapabilities !== void 0) throw new Error("UI capabilities require an authorized server catalog");
4156
+ return collector.emit(component, props, options);
4157
+ }
4158
+ const capturedProps = snapshotActionProposal(props);
4159
+ const capturedOptions = {
4160
+ ...options
4161
+ };
4162
+ const catalog = await resolveCatalog(scope);
4163
+ const prepared = await prepareUiEmission(catalog, scope.uiCapabilities, component, capturedProps, capturedOptions.version);
4164
+ const id = capturedOptions.id ?? `${toolCallId}:ui:${next++}`;
4165
+ if (prepared.kind === "text") {
4166
+ texts.set(id, prepared.text);
4167
+ await write?.({
4168
+ kind: "text",
4169
+ text: prepared.text
4170
+ });
4171
+ return {
4172
+ id
4173
+ };
4174
+ }
4175
+ return collector.emit(prepared.component, prepared.props, {
4176
+ id,
4177
+ version: prepared.version,
4178
+ fallbackText: prepared.fallbackText,
4179
+ ...prepared.componentVersions !== void 0 ? {
4180
+ componentVersions: prepared.componentVersions
4181
+ } : {}
4182
+ });
4183
+ }, "emit")
4184
+ };
4185
+ }
4186
+ __name(createNegotiatedUiCollector, "createNegotiatedUiCollector");
4187
+
2809
4188
  // src/tool-retry.ts
2810
4189
  function hasTransientShape(error) {
2811
4190
  if (typeof error !== "object" || error === null) {
@@ -2831,6 +4210,7 @@ function hasTransientShape(error) {
2831
4210
  }
2832
4211
  __name(hasTransientShape, "hasTransientShape");
2833
4212
  function isTransientToolError(error) {
4213
+ if (error instanceof ToolPreflightDeniedError) return false;
2834
4214
  if (hasTransientShape(error)) {
2835
4215
  return true;
2836
4216
  }
@@ -2873,7 +4253,7 @@ async function invokeWithTransientRetry(fn, setting, options) {
2873
4253
  try {
2874
4254
  return await fn();
2875
4255
  } catch (error) {
2876
- if (isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
4256
+ if (error instanceof ToolPreflightDeniedError || isControlFlowSignal(error) || options?.isControlFlowError?.(error) === true) {
2877
4257
  throw error;
2878
4258
  }
2879
4259
  const attemptsRemain = attempt < attempts;
@@ -3027,16 +4407,10 @@ async function resolvePrompt(prompt, ctx) {
3027
4407
  return typeof prompt === "function" ? prompt(ctx) : prompt;
3028
4408
  }
3029
4409
  __name(resolvePrompt, "resolvePrompt");
3030
- async function resolveSystemPrompt(deps, input) {
3031
- const ctx = {
3032
- actor: input.actor,
3033
- agentName: input.agentName ?? "default",
3034
- ...input.pageContext !== void 0 ? {
3035
- pageContext: input.pageContext
3036
- } : {}
3037
- };
4410
+ async function resolveSystemPrompt(deps, input, persona) {
4411
+ const ctx = promptContext(input, persona);
3038
4412
  const sections = [
3039
- await resolvePrompt(deps.systemPrompt, ctx)
4413
+ persona?.prompt ?? await resolvePrompt(deps.systemPrompt, ctx)
3040
4414
  ];
3041
4415
  for (const contribute of deps.promptContributors ?? []) {
3042
4416
  const section = await contribute(ctx);
@@ -3047,6 +4421,70 @@ async function resolveSystemPrompt(deps, input) {
3047
4421
  return sections.join("\n\n");
3048
4422
  }
3049
4423
  __name(resolveSystemPrompt, "resolveSystemPrompt");
4424
+ function withoutPersona(input) {
4425
+ const { persona: _dropped, ...rest } = input;
4426
+ return rest;
4427
+ }
4428
+ __name(withoutPersona, "withoutPersona");
4429
+ function promptContext(input, persona) {
4430
+ return {
4431
+ actor: input.actor,
4432
+ agentName: input.agentName ?? "default",
4433
+ ...input.pageContext !== void 0 ? {
4434
+ pageContext: input.pageContext
4435
+ } : {},
4436
+ ...input.uiCapabilities !== void 0 ? {
4437
+ uiCapabilities: input.uiCapabilities
4438
+ } : {},
4439
+ ...persona !== void 0 ? {
4440
+ persona: {
4441
+ id: persona.id,
4442
+ label: persona.label
4443
+ }
4444
+ } : {}
4445
+ };
4446
+ }
4447
+ __name(promptContext, "promptContext");
4448
+ async function resolveTurnPersona(deps, input) {
4449
+ const persona = findPersona(deps, input.persona);
4450
+ if (persona === void 0) {
4451
+ return null;
4452
+ }
4453
+ const ref = {
4454
+ id: persona.id,
4455
+ label: persona.label
4456
+ };
4457
+ let prompt;
4458
+ if (persona.systemPrompt !== void 0) {
4459
+ const ctx = promptContext(input, ref);
4460
+ const basePrompt = typeof persona.systemPrompt === "function" ? await resolvePrompt(deps.systemPrompt, ctx) : void 0;
4461
+ prompt = await resolvePrompt(persona.systemPrompt, {
4462
+ ...ctx,
4463
+ ...basePrompt !== void 0 ? {
4464
+ basePrompt
4465
+ } : {}
4466
+ });
4467
+ }
4468
+ return {
4469
+ ...ref,
4470
+ ...persona.allowedTools !== void 0 ? {
4471
+ allowedTools: [
4472
+ ...persona.allowedTools
4473
+ ]
4474
+ } : {},
4475
+ ...prompt !== void 0 ? {
4476
+ prompt
4477
+ } : {}
4478
+ };
4479
+ }
4480
+ __name(resolveTurnPersona, "resolveTurnPersona");
4481
+ function personaRefusal(persona, toolName) {
4482
+ if (persona?.allowedTools === void 0 || persona.allowedTools.includes(toolName)) {
4483
+ return null;
4484
+ }
4485
+ return `(tool "${toolName}" is not available to the "${persona.id}" persona)`;
4486
+ }
4487
+ __name(personaRefusal, "personaRefusal");
3050
4488
  function extractTask(input) {
3051
4489
  if (typeof input === "object" && input !== null && "task" in input) {
3052
4490
  const task = input.task;
@@ -3194,7 +4632,8 @@ async function loadSelectedHistory(deps, input, hooks) {
3194
4632
  messages: keep,
3195
4633
  dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
3196
4634
  title: thread.title,
3197
- hasAssistantMessage: thread.hasAssistantMessage
4635
+ hasAssistantMessage: thread.hasAssistantMessage,
4636
+ actionApprovalMode: deps.actionApprovalMode ?? "blocking"
3198
4637
  };
3199
4638
  });
3200
4639
  return {
@@ -3288,6 +4727,9 @@ function processorContext(input, step) {
3288
4727
  step,
3289
4728
  ...input.agentName !== void 0 ? {
3290
4729
  agentName: input.agentName
4730
+ } : {},
4731
+ ...input.persona !== void 0 ? {
4732
+ persona: input.persona
3291
4733
  } : {}
3292
4734
  };
3293
4735
  }
@@ -3355,10 +4797,10 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3355
4797
  modelId: turn.modelId
3356
4798
  } : {}
3357
4799
  };
3358
- }, (result) => ({
3359
- inputTokens: result.usage.inputTokens,
3360
- outputTokens: result.usage.outputTokens,
3361
- textLength: result.text.length
4800
+ }, (result2) => ({
4801
+ inputTokens: result2.usage.inputTokens,
4802
+ outputTokens: result2.usage.outputTokens,
4803
+ textLength: result2.text.length
3362
4804
  })));
3363
4805
  await hooks.step(`persist:usage:structured:${step}:${attempt}`, () => deps.store.recordUsage({
3364
4806
  threadId: input.threadId,
@@ -3388,13 +4830,16 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
3388
4830
  throw new StructuredOutputError(issues, text, maxAttempts);
3389
4831
  }
3390
4832
  __name(structureAnswer, "structureAnswer");
3391
- function stampToolKinds(result, deps) {
4833
+ function stampToolKinds(result2, deps) {
3392
4834
  return {
3393
- ...result,
3394
- toolCalls: result.toolCalls.map((call) => call.kind === void 0 ? {
3395
- ...call,
3396
- kind: declaredKind(deps, call.name)
3397
- } : call)
4835
+ ...result2,
4836
+ toolCalls: result2.toolCalls.map((call) => {
4837
+ const { preflight: _untrusted, prepared: _untrustedPrepared, actionApproval: _untrustedApproval, ...trusted } = call;
4838
+ return trusted.kind === void 0 ? {
4839
+ ...trusted,
4840
+ kind: declaredKind(deps, trusted.name)
4841
+ } : trusted;
4842
+ })
3398
4843
  };
3399
4844
  }
3400
4845
  __name(stampToolKinds, "stampToolKinds");
@@ -3451,9 +4896,15 @@ function toolContext(deps, input, hooks) {
3451
4896
  ...input.agentName !== void 0 ? {
3452
4897
  agentName: input.agentName
3453
4898
  } : {},
4899
+ ...input.persona !== void 0 ? {
4900
+ persona: input.persona
4901
+ } : {},
3454
4902
  ...input.pageContext !== void 0 ? {
3455
4903
  pageContext: input.pageContext
3456
4904
  } : {},
4905
+ ...input.uiCapabilities !== void 0 ? {
4906
+ uiCapabilities: input.uiCapabilities
4907
+ } : {},
3457
4908
  ...deps.host !== void 0 ? {
3458
4909
  host: deps.host
3459
4910
  } : {},
@@ -3498,6 +4949,9 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3498
4949
  ],
3499
4950
  ...input.agentName !== void 0 ? {
3500
4951
  agentName: input.agentName
4952
+ } : {},
4953
+ ...input.persona !== void 0 ? {
4954
+ persona: input.persona
3501
4955
  } : {}
3502
4956
  });
3503
4957
  await deps.store.recordToolCall({
@@ -3526,16 +4980,16 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3526
4980
  }
3527
4981
  const messageId = asked.messageId;
3528
4982
  const reply = await awaitElicitation(hooks, request, toolContext(deps, input, hooks));
3529
- const result = settleElicitation(request, reply);
4983
+ const result2 = settleElicitation(request, reply);
3530
4984
  await hooks.step("intake:answers", async () => {
3531
4985
  await deps.store.updateToolCall({
3532
4986
  toolCallId: request.id,
3533
4987
  // A skip is not an answer. Both leave the agent holding the same values, but only one of them
3534
4988
  // is evidence the user chose them, and a reader auditing what the agent was told has to be
3535
4989
  // able to tell those apart.
3536
- status: result.skipped ? "rejected" : "executed",
3537
- output: result,
3538
- ...result.skipped ? {
4990
+ status: result2.skipped ? "rejected" : "executed",
4991
+ output: result2,
4992
+ ...result2.skipped ? {
3539
4993
  error: "skipped by the user"
3540
4994
  } : {},
3541
4995
  ...reply.answeredByRef !== void 0 ? {
@@ -3546,13 +5000,13 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3546
5000
  {
3547
5001
  id: request.id,
3548
5002
  name: ASK_TOOL_NAME,
3549
- output: result
5003
+ output: result2
3550
5004
  }
3551
5005
  ]);
3552
5006
  await writer.write(encodeStreamEvent({
3553
5007
  kind: "tool-output",
3554
5008
  id: request.id,
3555
- output: result
5009
+ output: result2
3556
5010
  }));
3557
5011
  });
3558
5012
  return {
@@ -3565,7 +5019,7 @@ async function runIntake(intake, deps, input, hooks, writer, threadHasAssistant)
3565
5019
  {
3566
5020
  id: request.id,
3567
5021
  name: ASK_TOOL_NAME,
3568
- output: result
5022
+ output: result2
3569
5023
  }
3570
5024
  ]
3571
5025
  };
@@ -3611,16 +5065,140 @@ async function claimToolCall(turn, call) {
3611
5065
  const spec = deps.registry.spec(call.name);
3612
5066
  const kind = call.kind ?? declaredKind(deps, call.name);
3613
5067
  const awaitsHuman = kind === "action" || kind === "ask";
3614
- const approval = kind === "action" ? await claimApproval(turn, call, spec) : void 0;
3615
- const parks = kind === "ask" || approval?.mode === "ask";
5068
+ const independentApproval = turn.actionApprovalMode === "independent" && kind === "action" ? hooks.dispatchLlm !== void 0 && call.actionApproval !== void 0 ? call.actionApproval : await resolveActionProposalApproval({
5069
+ actor: input.actor,
5070
+ store: deps.store,
5071
+ tool: {
5072
+ name: call.name,
5073
+ kind: "action",
5074
+ ...spec !== void 0 ? {
5075
+ spec
5076
+ } : {}
5077
+ },
5078
+ thread: {
5079
+ threadId: input.threadId,
5080
+ runId: hooks.runId,
5081
+ ...input.agentName !== void 0 ? {
5082
+ agentName: input.agentName
5083
+ } : {}
5084
+ },
5085
+ ...deps.approvalPolicy !== void 0 ? {
5086
+ policy: deps.approvalPolicy
5087
+ } : {}
5088
+ }) : void 0;
5089
+ let preflight;
5090
+ let prepared;
5091
+ if (turn.actionApprovalMode === "independent" && call.prepared !== void 0 && hooks.dispatchLlm !== void 0) prepared = call.prepared;
5092
+ if (kind === "action" && hooks.dispatchLlm !== void 0 && call.preflight !== void 0) {
5093
+ preflight = call.preflight;
5094
+ } else if (kind === "action" && hooks.dispatchLlm !== void 0 && spec === void 0) {
5095
+ preflight = {
5096
+ status: "ready"
5097
+ };
5098
+ } else if (kind === "action") {
5099
+ try {
5100
+ const allowedTools = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
5101
+ if (independentApproval?.mode === "ask") {
5102
+ prepared = await prepareActionProposal(deps.registry, call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5103
+ allowedTools
5104
+ });
5105
+ preflight = prepared.preflight;
5106
+ } else {
5107
+ preflight = await deps.registry.prepare(call.name, call.input, toolCallContext(toolContext(deps, input, hooks), call.id), deps.rolesPolicy, allowedTools === void 0 ? {} : {
5108
+ allowedTools
5109
+ });
5110
+ }
5111
+ } catch (error) {
5112
+ if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) throw error;
5113
+ preflight = {
5114
+ status: "failed",
5115
+ error: error instanceof Error ? error.message : String(error)
5116
+ };
5117
+ }
5118
+ }
5119
+ let confirmation = preflight?.status === "ready" ? preflight.confirmation : void 0;
5120
+ const approval = kind === "action" && preflight?.status === "ready" ? independentApproval ?? await claimApproval(turn, call, spec) : void 0;
5121
+ let proposalId;
5122
+ if (turn.actionApprovalMode === "independent" && approval?.mode === "ask") {
5123
+ if (!prepared) throw new Error("Independent action lacks trusted prepared input");
5124
+ const proposalStore = deps.store;
5125
+ if (typeof proposalStore.createActionProposal !== "function") throw new Error("Independent action proposal store unavailable");
5126
+ proposalId = `proposal-${createHash("sha256").update(JSON.stringify([
5127
+ hooks.runId,
5128
+ call.id
5129
+ ])).digest("hex")}`;
5130
+ const display = confirmation ?? spec?.presentation?.confirm ?? {
5131
+ title: `Allow ${call.name}?`,
5132
+ verb: "Approve"
5133
+ };
5134
+ const fill = /* @__PURE__ */ __name((value) => value.replace(/\{([^}]+)\}/g, (_match, path) => {
5135
+ let current = prepared?.input;
5136
+ for (const part of path.split(".")) current = current !== null && typeof current === "object" ? current[part] : void 0;
5137
+ return current === void 0 ? "" : String(current);
5138
+ }), "fill");
5139
+ const create = prepared.replacementKey === void 0 ? proposalStore.createActionProposal.bind(proposalStore) : (() => {
5140
+ const store = proposalStore;
5141
+ if (typeof store.createReplacingActionProposal !== "function") throw new Error("Independent replacement requires atomic supersession store");
5142
+ return store.createReplacingActionProposal.bind(store);
5143
+ })();
5144
+ const created = await create({
5145
+ ...prepared.replacementKey !== void 0 ? {
5146
+ replacementKey: prepared.replacementKey
5147
+ } : {},
5148
+ id: proposalId,
5149
+ tenantRef: input.actor.tenantRef ?? null,
5150
+ actorRef: input.actor.id,
5151
+ threadId: input.threadId,
5152
+ originRunId: hooks.runId,
5153
+ originMessageId: messageId,
5154
+ originToolCallId: call.id,
5155
+ toolName: call.name,
5156
+ input: prepared.input,
5157
+ preparationInput: prepared.preparationInput,
5158
+ executionContext: {
5159
+ requestId: hooks.runId,
5160
+ ...input.agentName !== void 0 ? {
5161
+ agentName: input.agentName
5162
+ } : {},
5163
+ ...input.persona !== void 0 ? {
5164
+ persona: input.persona
5165
+ } : {},
5166
+ ...input.pageContext !== void 0 ? {
5167
+ pageContext: input.pageContext
5168
+ } : {},
5169
+ ...input.uiCapabilities !== void 0 ? {
5170
+ uiCapabilities: input.uiCapabilities
5171
+ } : {}
5172
+ },
5173
+ confirmation: confirmation ?? {
5174
+ title: fill(display.title),
5175
+ verb: fill(display.verb),
5176
+ ...display.detail !== void 0 ? {
5177
+ detail: fill(display.detail)
5178
+ } : {}
5179
+ },
5180
+ approver: approval.approver,
5181
+ expiresAt: approval.expiresAt === void 0 ? null : Date.parse(approval.expiresAt),
5182
+ idempotencyKey: toolCallContext(toolContext(deps, input, hooks), call.id).idempotencyKey
5183
+ });
5184
+ if (created.status === "conflict") throw new Error("Action proposal replay payload conflict");
5185
+ confirmation = created.proposal?.confirmation ?? confirmation;
5186
+ }
5187
+ const parks = kind === "ask" || approval?.mode === "ask" && proposalId === void 0;
3616
5188
  await deps.store.recordToolCall({
3617
5189
  toolCallId: call.id,
3618
5190
  messageId,
3619
5191
  toolName: call.name,
3620
5192
  toolType: awaitsHuman ? "action" : "read",
3621
5193
  input: call.input,
3622
- status: parks ? "pending_approval" : "auto_executed",
5194
+ status: proposalId !== void 0 ? "proposed" : parks ? "pending_approval" : "auto_executed",
5195
+ ...proposalId !== void 0 ? {
5196
+ proposalId
5197
+ } : {},
3623
5198
  runId: hooks.runId,
5199
+ ...confirmation !== void 0 ? {
5200
+ confirmation
5201
+ } : {},
3624
5202
  ...approval !== void 0 && approval.mode !== "auto" ? {
3625
5203
  approver: approval.approver
3626
5204
  } : {},
@@ -3632,7 +5210,16 @@ async function claimToolCall(turn, call) {
3632
5210
  await turn.writer.write(encodeStreamEvent({
3633
5211
  kind: "approval-requested",
3634
5212
  id: call.id,
5213
+ ...proposalId !== void 0 ? {
5214
+ target: {
5215
+ kind: "proposal",
5216
+ proposalId
5217
+ }
5218
+ } : {},
3635
5219
  approver: approval.approver,
5220
+ ...confirmation !== void 0 ? {
5221
+ confirmation
5222
+ } : {},
3636
5223
  ...approval.expiresAt !== void 0 ? {
3637
5224
  expiresAt: approval.expiresAt
3638
5225
  } : {}
@@ -3655,6 +5242,12 @@ async function claimToolCall(turn, call) {
3655
5242
  } : {},
3656
5243
  ...approval !== void 0 ? {
3657
5244
  approval
5245
+ } : {},
5246
+ ...preflight !== void 0 ? {
5247
+ preflight
5248
+ } : {},
5249
+ ...proposalId !== void 0 ? {
5250
+ proposalId
3658
5251
  } : {}
3659
5252
  };
3660
5253
  });
@@ -3680,6 +5273,12 @@ async function claimToolCall(turn, call) {
3680
5273
  ...toolType === "action" && persisted?.approval !== void 0 ? {
3681
5274
  approval: persisted.approval
3682
5275
  } : {},
5276
+ ...persisted?.preflight !== void 0 ? {
5277
+ preflight: persisted.preflight
5278
+ } : {},
5279
+ ...persisted?.proposalId !== void 0 ? {
5280
+ proposalId: persisted.proposalId
5281
+ } : {},
3683
5282
  ctx: toolContext(deps, input, hooks)
3684
5283
  };
3685
5284
  }
@@ -3827,6 +5426,9 @@ function skillContext(input) {
3827
5426
  } : {},
3828
5427
  ...input.pageContext !== void 0 ? {
3829
5428
  pageContext: input.pageContext
5429
+ } : {},
5430
+ ...input.uiCapabilities !== void 0 ? {
5431
+ uiCapabilities: input.uiCapabilities
3830
5432
  } : {}
3831
5433
  };
3832
5434
  }
@@ -3843,6 +5445,7 @@ async function invokeClaimedTool(turn, claimed) {
3843
5445
  if (claimed.toolType === "memory") {
3844
5446
  return await rememberIntoTurn(turn, claimed, startedAt);
3845
5447
  }
5448
+ const invokeAllowList = turn.persona?.allowedTools === void 0 ? deps.toolAllowList : intersectAllowLists(deps.toolAllowList, turn.persona.allowedTools);
3846
5449
  let raw;
3847
5450
  if (hooks.dispatchTool) {
3848
5451
  const stepCtx = {
@@ -3853,14 +5456,23 @@ async function invokeClaimedTool(turn, claimed) {
3853
5456
  ...input.agentName !== void 0 ? {
3854
5457
  agentName: input.agentName
3855
5458
  } : {},
5459
+ ...input.persona !== void 0 ? {
5460
+ persona: input.persona
5461
+ } : {},
3856
5462
  ...input.pageContext !== void 0 ? {
3857
5463
  pageContext: input.pageContext
5464
+ } : {},
5465
+ ...input.uiCapabilities !== void 0 ? {
5466
+ uiCapabilities: input.uiCapabilities
3858
5467
  } : {}
3859
5468
  };
3860
5469
  const envelope = {
3861
5470
  toolName: call.name,
3862
5471
  input: call.input,
3863
5472
  ctx: stepCtx,
5473
+ ...invokeAllowList !== void 0 ? {
5474
+ allowedTools: invokeAllowList
5475
+ } : {},
3864
5476
  ...deps.toolTimeoutMs !== void 0 ? {
3865
5477
  timeoutMs: deps.toolTimeoutMs
3866
5478
  } : {},
@@ -3873,35 +5485,49 @@ async function invokeClaimedTool(turn, claimed) {
3873
5485
  raw = await hooks.dispatchTool(resolvedCall, envelope);
3874
5486
  } else {
3875
5487
  const invocation = hooks.step(`tool:${call.id}`, async () => {
3876
- const ui2 = createUiCollector(call.id, (event) => turn.writer.write(encodeStreamEvent(event)));
3877
- const output2 = await traceToolExecution(hooks.runId, {
3878
- toolCallId: call.id,
3879
- toolName: call.name,
3880
- toolType
3881
- }, () => invokeWithTransientRetry(() => {
3882
- ui2.restart();
3883
- return deps.registry.invoke(call.name, call.input, {
3884
- ...toolCallContext(ctx, call.id),
3885
- emitUi: ui2.emit
3886
- }, deps.rolesPolicy);
3887
- }, deps.toolTransientRetry ?? {}, {
3888
- ...hooks.isControlFlowError !== void 0 ? {
3889
- isControlFlowError: hooks.isControlFlowError
3890
- } : {},
3891
- onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
3892
- publishAgentToolRetry({
3893
- toolName: call.name,
3894
- toolCallId: call.id,
3895
- attempt,
3896
- message: retryError instanceof Error ? retryError.message : String(retryError)
3897
- });
3898
- }, "onRetry")
3899
- }));
3900
- return wrapToolStepOutput(output2, ui2.components());
5488
+ const ui2 = createNegotiatedUiCollector(call.id, ctx, deps.resolveUiCatalog, (event) => turn.writer.write(encodeStreamEvent(event)));
5489
+ let output2;
5490
+ try {
5491
+ output2 = await traceToolExecution(hooks.runId, {
5492
+ toolCallId: call.id,
5493
+ toolName: call.name,
5494
+ toolType
5495
+ }, () => invokeWithTransientRetry(() => {
5496
+ ui2.restart();
5497
+ return deps.registry.invoke(call.name, call.input, {
5498
+ ...toolCallContext(ctx, call.id),
5499
+ emitUi: ui2.emit
5500
+ }, deps.rolesPolicy, invokeAllowList !== void 0 ? {
5501
+ allowedTools: invokeAllowList
5502
+ } : {});
5503
+ }, deps.toolTransientRetry ?? {}, {
5504
+ ...hooks.isControlFlowError !== void 0 ? {
5505
+ isControlFlowError: hooks.isControlFlowError
5506
+ } : {},
5507
+ onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
5508
+ publishAgentToolRetry({
5509
+ toolName: call.name,
5510
+ toolCallId: call.id,
5511
+ attempt,
5512
+ message: retryError instanceof Error ? retryError.message : String(retryError)
5513
+ });
5514
+ }, "onRetry")
5515
+ }));
5516
+ } catch (error) {
5517
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") return wrapToolPreflightDenied(error.message);
5518
+ throw error;
5519
+ }
5520
+ return wrapToolStepOutput(output2, ui2.components(), ui2.text());
3901
5521
  });
3902
5522
  raw = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
3903
5523
  }
3904
- const { output, ui } = unwrapToolStepOutput(raw);
5524
+ const { output, ui, preflightDenied, text } = unwrapToolStepOutput(raw);
5525
+ if (text) turn.toolText.set(call.id, text);
5526
+ if (preflightDenied !== void 0) return {
5527
+ status: "denied",
5528
+ reason: preflightDenied,
5529
+ executionMs: Date.now() - startedAt
5530
+ };
3905
5531
  if (ui.length > 0) {
3906
5532
  turn.toolUi.set(call.id, ui);
3907
5533
  }
@@ -3917,6 +5543,13 @@ async function invokeClaimedTool(turn, claimed) {
3917
5543
  if (hooks.isControlFlowError?.(error) === true || isControlFlowSignal(error) || isReplayIntegrityError(error)) {
3918
5544
  throw error;
3919
5545
  }
5546
+ if (error instanceof Error && error.name === "ToolPreflightDeniedError") {
5547
+ return {
5548
+ status: "denied",
5549
+ reason: error.message,
5550
+ executionMs: Date.now() - startedAt
5551
+ };
5552
+ }
3920
5553
  return {
3921
5554
  status: "failed",
3922
5555
  error: error instanceof Error ? error.message : String(error),
@@ -3958,6 +5591,27 @@ async function recordToolOutcome(turn, claimed, outcome, deciderRef, meta) {
3958
5591
  } : {}
3959
5592
  }));
3960
5593
  }, "settle");
5594
+ if (outcome.status === "denied") {
5595
+ await hooks.step(`persist:toolreject:${call.id}`, async () => {
5596
+ await deps.store.updateToolCall({
5597
+ toolCallId: call.id,
5598
+ status: meta === void 0 ? "rejected" : "failed",
5599
+ error: outcome.reason,
5600
+ ...decided
5601
+ });
5602
+ await settle();
5603
+ });
5604
+ return {
5605
+ id: call.id,
5606
+ name: call.name,
5607
+ output: {
5608
+ rejected: true,
5609
+ reason: outcome.reason
5610
+ },
5611
+ denied: true,
5612
+ error: `This action was denied by its preflight check: ${outcome.reason}. The requested mutation did not run.`
5613
+ };
5614
+ }
3961
5615
  if (outcome.status === "failed") {
3962
5616
  await hooks.step(`persist:toolfail:${call.id}`, async () => {
3963
5617
  await deps.store.updateToolCall({
@@ -4014,7 +5668,7 @@ async function delegateToolCall(turn, claimed) {
4014
5668
  const { deps, input, hooks } = turn;
4015
5669
  const { call, targetAgent = call.name } = claimed;
4016
5670
  const task = extractTask(call.input);
4017
- const refusal = delegationRefusal({
5671
+ const refusal = personaRefusal(turn.persona, call.name) ?? delegationRefusal({
4018
5672
  deps,
4019
5673
  input,
4020
5674
  targetAgent
@@ -4098,12 +5752,12 @@ async function elicitToolCall(turn, claimed) {
4098
5752
  }));
4099
5753
  });
4100
5754
  const reply = await awaitElicitation(hooks, request, ctx);
4101
- const result = settleElicitation(request, reply);
4102
- await hooks.step(result.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
5755
+ const result2 = settleElicitation(request, reply);
5756
+ await hooks.step(result2.skipped ? `persist:toolreject:${call.id}` : `persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
4103
5757
  toolCallId: call.id,
4104
- status: result.skipped ? "rejected" : "executed",
4105
- output: result,
4106
- ...result.skipped ? {
5758
+ status: result2.skipped ? "rejected" : "executed",
5759
+ output: result2,
5760
+ ...result2.skipped ? {
4107
5761
  error: "skipped by the user"
4108
5762
  } : {},
4109
5763
  ...reply.answeredByRef !== void 0 ? {
@@ -4114,24 +5768,50 @@ async function elicitToolCall(turn, claimed) {
4114
5768
  runId: hooks.runId,
4115
5769
  toolName: call.name,
4116
5770
  toolType: "action",
4117
- status: result.skipped ? "rejected" : "executed"
5771
+ status: result2.skipped ? "rejected" : "executed"
4118
5772
  });
4119
5773
  return {
4120
5774
  id: call.id,
4121
5775
  name: call.name,
4122
- output: result
5776
+ output: result2
4123
5777
  };
4124
5778
  }
4125
5779
  __name(elicitToolCall, "elicitToolCall");
4126
5780
  async function runClaimedToolCall(turn, claimed) {
4127
5781
  const { deps, input, hooks } = turn;
4128
5782
  const { call, toolType, ctx } = claimed;
5783
+ if (claimed.proposalId !== void 0) {
5784
+ turn.halt.independentProposal = true;
5785
+ return {
5786
+ id: call.id,
5787
+ name: call.name,
5788
+ output: actionProposalReceipt(claimed.proposalId)
5789
+ };
5790
+ }
4129
5791
  if (toolType === "agent") {
4130
5792
  return delegateToolCall(turn, claimed);
4131
5793
  }
4132
5794
  if (toolType === "ask") {
4133
5795
  return elicitToolCall(turn, claimed);
4134
5796
  }
5797
+ if (claimed.preflight !== void 0 && claimed.preflight.status !== "ready") {
5798
+ const result2 = claimed.preflight;
5799
+ if (result2.status === "completed" && claimed.terminal === true) turn.halt.terminal = true;
5800
+ const outcome = result2.status === "completed" ? {
5801
+ status: "executed",
5802
+ output: result2.output,
5803
+ executionMs: 0
5804
+ } : result2.status === "denied" ? {
5805
+ status: "denied",
5806
+ reason: result2.reason,
5807
+ executionMs: 0
5808
+ } : {
5809
+ status: "failed",
5810
+ error: result2.error,
5811
+ executionMs: 0
5812
+ };
5813
+ return recordToolOutcome(turn, claimed, outcome, input.actor.id);
5814
+ }
4135
5815
  let deciderRef = input.actor.id;
4136
5816
  let meta;
4137
5817
  if (toolType === "action" && claimed.approval?.mode === "remembered") {
@@ -4256,30 +5936,30 @@ function expiryNarrative() {
4256
5936
  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.";
4257
5937
  }
4258
5938
  __name(expiryNarrative, "expiryNarrative");
4259
- function outputFrame(result) {
4260
- if (result.denied === true) {
4261
- const reason = refusalReason(result);
5939
+ function outputFrame(result2) {
5940
+ if (result2.denied === true) {
5941
+ const reason = refusalReason(result2);
4262
5942
  return {
4263
5943
  kind: "tool-output-denied",
4264
- id: result.id,
5944
+ id: result2.id,
4265
5945
  ...reason !== void 0 ? {
4266
5946
  reason
4267
5947
  } : {}
4268
5948
  };
4269
5949
  }
4270
- return result.error !== void 0 ? {
5950
+ return result2.error !== void 0 ? {
4271
5951
  kind: "tool-output-error",
4272
- id: result.id,
4273
- error: result.error
5952
+ id: result2.id,
5953
+ error: result2.error
4274
5954
  } : {
4275
5955
  kind: "tool-output",
4276
- id: result.id,
4277
- output: result.output
5956
+ id: result2.id,
5957
+ output: result2.output
4278
5958
  };
4279
5959
  }
4280
5960
  __name(outputFrame, "outputFrame");
4281
- function refusalReason(result) {
4282
- const { output } = result;
5961
+ function refusalReason(result2) {
5962
+ const { output } = result2;
4283
5963
  if (output !== null && typeof output === "object" && "reason" in output) {
4284
5964
  const reason = output.reason;
4285
5965
  return typeof reason === "string" && reason !== DEFAULT_REFUSAL_REASON ? reason : void 0;
@@ -4310,14 +5990,16 @@ async function invokeClaimedToolsTogether(turn, parallel, claimed) {
4310
5990
  return results;
4311
5991
  }
4312
5992
  __name(invokeClaimedToolsTogether, "invokeClaimedToolsTogether");
4313
- async function runAgentLoop(boundDeps, input, hooks) {
5993
+ async function runAgentLoop(boundDeps, requested, hooks) {
5994
+ const persona = requested.persona === void 0 ? void 0 : await hooks.step("persona:resolve", () => resolveTurnPersona(boundDeps, requested)) ?? void 0;
5995
+ const input = persona === void 0 || persona.id !== requested.persona ? withoutPersona(requested) : requested;
4314
5996
  const deps = input.model === void 0 ? boundDeps : {
4315
5997
  ...boundDeps,
4316
5998
  model: withSelectedModel(boundDeps.model, input.model),
4317
5999
  modelId: input.model
4318
6000
  };
4319
6001
  const maxSteps = deps.maxSteps ?? 8;
4320
- let system = await resolveSystemPrompt(deps, input);
6002
+ let system = await resolveSystemPrompt(deps, input, persona);
4321
6003
  const inputProcessors = deps.inputProcessors ?? [];
4322
6004
  const outputProcessors = deps.outputProcessors ?? [];
4323
6005
  const gateMode = resolveOutputGateMode(outputProcessors);
@@ -4358,6 +6040,9 @@ async function runAgentLoop(boundDeps, input, hooks) {
4358
6040
  role: "user",
4359
6041
  content: input.userText,
4360
6042
  runId: hooks.runId,
6043
+ ...input.persona !== void 0 ? {
6044
+ persona: input.persona
6045
+ } : {},
4361
6046
  ...input.attachments !== void 0 ? {
4362
6047
  attachments: input.attachments
4363
6048
  } : {}
@@ -4380,6 +6065,9 @@ async function runAgentLoop(boundDeps, input, hooks) {
4380
6065
  actorId: input.actor.id,
4381
6066
  ...input.agentName !== void 0 ? {
4382
6067
  agentName: input.agentName
6068
+ } : {},
6069
+ ...input.persona !== void 0 ? {
6070
+ persona: input.persona
4383
6071
  } : {}
4384
6072
  });
4385
6073
  const stages = await resolvePromptStages(deps, hooks);
@@ -4511,19 +6199,44 @@ ${block}`;
4511
6199
  messages: prompt.messages,
4512
6200
  actor: input.actor,
4513
6201
  threadId: input.threadId,
6202
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
6203
+ preflightContext: {
6204
+ actor: input.actor,
6205
+ threadId: input.threadId,
6206
+ runId: hooks.runId,
6207
+ requestId: hooks.runId,
6208
+ ...input.agentName !== void 0 ? {
6209
+ agentName: input.agentName
6210
+ } : {},
6211
+ ...input.persona !== void 0 ? {
6212
+ persona: input.persona
6213
+ } : {},
6214
+ ...input.pageContext !== void 0 ? {
6215
+ pageContext: input.pageContext
6216
+ } : {},
6217
+ ...input.uiCapabilities !== void 0 ? {
6218
+ uiCapabilities: input.uiCapabilities
6219
+ } : {}
6220
+ },
4514
6221
  ...gated ? {
4515
6222
  bufferOutput: true
4516
6223
  } : {},
4517
6224
  ...input.model !== void 0 ? {
4518
6225
  model: input.model
6226
+ } : {},
6227
+ ...persona?.allowedTools !== void 0 ? {
6228
+ personaAllowedTools: persona.allowedTools
4519
6229
  } : {}
4520
6230
  });
4521
6231
  } else {
4522
6232
  const tools = withMemoryTool({
4523
6233
  tools: withSkillTool({
4524
6234
  tools: withAskTool({
4525
- tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList, {
6235
+ tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, intersectAllowLists(deps.toolAllowList, persona?.allowedTools), {
4526
6236
  threadId: input.threadId,
6237
+ ...input.uiCapabilities !== void 0 ? {
6238
+ uiCapabilities: input.uiCapabilities
6239
+ } : {},
4527
6240
  ...input.agentName !== void 0 ? {
4528
6241
  agentName: input.agentName
4529
6242
  } : {}
@@ -4543,7 +6256,7 @@ ${block}`;
4543
6256
  }) : void 0;
4544
6257
  const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
4545
6258
  const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
4546
- const result = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
6259
+ const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
4547
6260
  system: prompt.system,
4548
6261
  messages: prompt.messages,
4549
6262
  tools,
@@ -4553,15 +6266,15 @@ ${block}`;
4553
6266
  await incremental.settled();
4554
6267
  const refusal = incremental.rejection();
4555
6268
  return {
4556
- ...result,
6269
+ ...result2,
4557
6270
  releasedText: incremental.released(),
4558
6271
  ...refusal !== void 0 ? {
4559
6272
  gateRejection: refusal
4560
6273
  } : {}
4561
6274
  };
4562
6275
  }
4563
- return buffer === void 0 ? result : {
4564
- ...result,
6276
+ return buffer === void 0 ? result2 : {
6277
+ ...result2,
4565
6278
  bufferedFrames: buffer.frames()
4566
6279
  };
4567
6280
  });
@@ -4655,12 +6368,12 @@ ${block}`;
4655
6368
  role: "assistant",
4656
6369
  content: turn.text
4657
6370
  }
4658
- ], count), (result) => ({
4659
- followUps: result.followUps.length,
4660
- inputTokens: result.usage.inputTokens,
4661
- outputTokens: result.usage.outputTokens,
4662
- ...result.modelId !== void 0 ? {
4663
- modelId: result.modelId
6371
+ ], count), (result2) => ({
6372
+ followUps: result2.followUps.length,
6373
+ inputTokens: result2.usage.inputTokens,
6374
+ outputTokens: result2.usage.outputTokens,
6375
+ ...result2.modelId !== void 0 ? {
6376
+ modelId: result2.modelId
4664
6377
  } : {}
4665
6378
  })));
4666
6379
  let suggestions = generated.followUps;
@@ -4737,6 +6450,9 @@ ${block}`;
4737
6450
  ...input.agentName !== void 0 ? {
4738
6451
  agentName: input.agentName
4739
6452
  } : {},
6453
+ ...input.persona !== void 0 ? {
6454
+ persona: input.persona
6455
+ } : {},
4740
6456
  ...turn.reasoning !== void 0 ? {
4741
6457
  reasoning: turn.reasoning
4742
6458
  } : {},
@@ -4768,7 +6484,7 @@ ${block}`;
4768
6484
  };
4769
6485
  modelMessages.push(assistantMessage);
4770
6486
  for (const entry of synthetic) {
4771
- const { call, result } = entry;
6487
+ const { call, result: result2 } = entry;
4772
6488
  await hooks.step(`${entry.step}:${assistant.id}`, async () => {
4773
6489
  await deps.store.recordToolCall({
4774
6490
  toolCallId: call.id,
@@ -4782,7 +6498,7 @@ ${block}`;
4782
6498
  await deps.store.updateToolCall({
4783
6499
  toolCallId: call.id,
4784
6500
  status: "executed",
4785
- output: result.output
6501
+ output: result2.output
4786
6502
  });
4787
6503
  await writer.write(encodeStreamEvent({
4788
6504
  kind: "tool-input-available",
@@ -4794,7 +6510,7 @@ ${block}`;
4794
6510
  await writer.write(encodeStreamEvent({
4795
6511
  kind: "tool-output",
4796
6512
  id: call.id,
4797
- output: result.output
6513
+ output: result2.output
4798
6514
  }));
4799
6515
  });
4800
6516
  }
@@ -4823,14 +6539,19 @@ ${block}`;
4823
6539
  messageId: assistant.id,
4824
6540
  writer,
4825
6541
  toolUi: /* @__PURE__ */ new Map(),
6542
+ toolText: /* @__PURE__ */ new Map(),
4826
6543
  halt: {
4827
6544
  terminal: false
4828
6545
  },
6546
+ actionApprovalMode: history.actionApprovalMode ?? "blocking",
4829
6547
  ...skillOffer !== void 0 ? {
4830
6548
  skills: skillOffer
4831
6549
  } : {},
4832
6550
  ...memoryDigest !== void 0 ? {
4833
6551
  memory: memoryDigest
6552
+ } : {},
6553
+ ...persona !== void 0 ? {
6554
+ persona
4834
6555
  } : {}
4835
6556
  };
4836
6557
  const parallel = hooks.parallel;
@@ -4855,17 +6576,35 @@ ${block}`;
4855
6576
  ...results,
4856
6577
  ...syntheticResults
4857
6578
  ];
6579
+ const toolText = escapeUnsafeToolUiText(toolCallsWithKind.map((call) => turnCalls.toolText.get(call.id)).filter((text) => text !== void 0).join("\n"));
4858
6580
  assistantMessage.toolResults = settledResults;
4859
6581
  await hooks.step(`stream:tool-outputs:${i}`, async () => {
4860
6582
  await deps.store.setMessageToolResults(assistant.id, settledResults);
6583
+ if (toolText) await deps.store.appendMessage({
6584
+ threadId: input.threadId,
6585
+ role: "assistant",
6586
+ content: toolText,
6587
+ runId: hooks.runId,
6588
+ ...input.agentName !== void 0 ? {
6589
+ agentName: input.agentName
6590
+ } : {}
6591
+ });
4861
6592
  const toolUi = toolCallsWithKind.flatMap((call) => turnCalls.toolUi.get(call.id) ?? []);
4862
6593
  if (toolUi.length > 0) {
4863
6594
  await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
4864
6595
  }
4865
- for (const result of results) {
4866
- await writer.write(encodeStreamEvent(outputFrame(result)));
6596
+ for (const result2 of results) {
6597
+ await writer.write(encodeStreamEvent(outputFrame(result2)));
4867
6598
  }
4868
6599
  });
6600
+ if (toolText) {
6601
+ modelMessages.push({
6602
+ role: "assistant",
6603
+ content: toolText
6604
+ });
6605
+ lastText += `
6606
+ ${toolText}`;
6607
+ }
4869
6608
  await hooks.step(`stream:step-finish:${i}`, async () => {
4870
6609
  await writer.write(encodeStreamEvent({
4871
6610
  kind: "step-finish",
@@ -4879,7 +6618,7 @@ ${block}`;
4879
6618
  } : {}
4880
6619
  }));
4881
6620
  });
4882
- if (turnCalls.halt.terminal) {
6621
+ if (turnCalls.halt.terminal || turnCalls.halt.independentProposal) {
4883
6622
  break;
4884
6623
  }
4885
6624
  }
@@ -4935,8 +6674,152 @@ ${block}`;
4935
6674
  }
4936
6675
  __name(runAgentLoop, "runAgentLoop");
4937
6676
 
6677
+ // src/in-memory-action-proposal-store.ts
6678
+ import { randomUUID } from "crypto";
6679
+ var InMemoryActionProposalStore = class {
6680
+ static {
6681
+ __name(this, "InMemoryActionProposalStore");
6682
+ }
6683
+ rows = /* @__PURE__ */ new Map();
6684
+ clock;
6685
+ constructor(options = {}) {
6686
+ this.clock = options.clock ?? Date.now;
6687
+ }
6688
+ async createActionProposal(input) {
6689
+ validateActionProposalCreation(input);
6690
+ const row = this.rows.get(input.id);
6691
+ if (row) {
6692
+ if (!actionProposalScopeMatches(row, input)) return {
6693
+ status: "conflict"
6694
+ };
6695
+ return {
6696
+ status: actionProposalCreationMatches(row, input) ? "unchanged" : "conflict",
6697
+ proposal: snapshotActionProposal(row)
6698
+ };
6699
+ }
6700
+ const proposal = initialActionProposal(input, this.clock());
6701
+ this.rows.set(input.id, proposal);
6702
+ return {
6703
+ status: "created",
6704
+ proposal: snapshotActionProposal(proposal)
6705
+ };
6706
+ }
6707
+ async getActionProposal(scope, id) {
6708
+ const row = this.rows.get(id);
6709
+ return row && actionProposalScopeMatches(row, scope) ? snapshotActionProposal(row) : null;
6710
+ }
6711
+ async listActionProposals(scope, query = {}) {
6712
+ const limit = validateActionProposalListQuery(query);
6713
+ return [
6714
+ ...this.rows.values()
6715
+ ].filter((row) => actionProposalScopeMatches(row, scope) && (query.decision === void 0 || row.decision === query.decision) && (query.after === void 0 || row.createdAt > query.after.createdAt || row.createdAt === query.after.createdAt && row.id > query.after.id)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, limit).map(snapshotActionProposal);
6716
+ }
6717
+ mutate(scope, id, transition) {
6718
+ const row = this.rows.get(id);
6719
+ if (!row || !actionProposalScopeMatches(row, scope)) return {
6720
+ status: "not_found"
6721
+ };
6722
+ const result2 = transition(row, this.clock());
6723
+ if (result2.proposal) this.rows.set(id, snapshotActionProposal(result2.proposal));
6724
+ return result2;
6725
+ }
6726
+ async decideActionProposal(scope, id, command) {
6727
+ return this.mutate(scope, id, (row, now) => transitionActionProposalDecision(row, command, now));
6728
+ }
6729
+ async claimActionProposal(scope, id, command) {
6730
+ return this.mutate(scope, id, (row, now) => transitionActionProposalClaim(row, command, now, randomUUID()));
6731
+ }
6732
+ async extendActionProposalLease(scope, id, command) {
6733
+ return this.mutate(scope, id, (row, now) => transitionActionProposalLease(row, command, now));
6734
+ }
6735
+ async settleActionProposal(scope, id, command) {
6736
+ return this.mutate(scope, id, (row, now) => transitionActionProposalSettlement(row, command, now));
6737
+ }
6738
+ async claimNextActionProposal(command) {
6739
+ const now = this.clock();
6740
+ validateActionProposalWorkerClaim(command, now);
6741
+ const candidates = [
6742
+ ...this.rows.values()
6743
+ ].filter((row) => row.decision === "approved" && (row.execution?.status === "queued" || row.execution?.status === "executing" && row.execution.lease !== null && now >= row.execution.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
6744
+ for (const candidate of candidates) {
6745
+ const claimed = await this.claimActionProposal(candidate, candidate.id, command);
6746
+ if (claimed.status === "applied" && claimed.proposal) return claimed.proposal;
6747
+ }
6748
+ return null;
6749
+ }
6750
+ async expireActionProposals(command) {
6751
+ const now = this.clock();
6752
+ validateActionProposalExpiryBatch(command, now);
6753
+ const candidates = [
6754
+ ...this.rows.values()
6755
+ ].filter((row) => row.decision === "pending" && row.expiresAt !== null && now >= row.expiresAt).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, command.limit);
6756
+ let expired = 0;
6757
+ for (const candidate of candidates) {
6758
+ const result2 = await this.decideActionProposal(candidate, candidate.id, {
6759
+ decision: "expired",
6760
+ actorRef: "system",
6761
+ via: "expiry"
6762
+ });
6763
+ if (result2.status === "applied") expired++;
6764
+ }
6765
+ return expired;
6766
+ }
6767
+ async claimNextActionProposalOutcome(command) {
6768
+ const now = this.clock();
6769
+ validateActionProposalWorkerClaim(command, now);
6770
+ const candidates = [
6771
+ ...this.rows.values()
6772
+ ].filter((row) => row.outcomeDelivery?.status === "pending" && (!row.outcomeDelivery.lease || now >= row.outcomeDelivery.lease.expiresAt)).sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).slice(0, 32);
6773
+ for (const candidate of candidates) {
6774
+ const row = this.rows.get(candidate.id);
6775
+ if (!row) continue;
6776
+ const next = claimActionProposalOutcome(row, command, this.clock(), randomUUID());
6777
+ if (!next?.outcome || !next.outcomeDelivery?.lease) continue;
6778
+ this.rows.set(next.id, next);
6779
+ return {
6780
+ outcome: snapshotActionProposal(next.outcome),
6781
+ lease: {
6782
+ outcomeId: next.outcome.id,
6783
+ token: next.outcomeDelivery.lease.token,
6784
+ generation: next.outcomeDelivery.generation
6785
+ }
6786
+ };
6787
+ }
6788
+ return null;
6789
+ }
6790
+ async createReplacingActionProposal(input) {
6791
+ validateActionProposalCreation(input);
6792
+ const existing = this.rows.get(input.id);
6793
+ if (existing) return this.createActionProposal(input);
6794
+ const proposal = initialActionProposal(input, this.clock());
6795
+ const replacements = [];
6796
+ if (input.replacementKey) for (const row of this.rows.values()) {
6797
+ if (row.decision !== "pending" || !actionProposalScopeMatches(row, input) || row.toolName !== input.toolName || row.replacementKey !== input.replacementKey) continue;
6798
+ const result2 = transitionActionProposalSupersession(row, proposal, {
6799
+ replacementProposalId: proposal.id,
6800
+ actorRef: input.actorRef,
6801
+ via: "supersession"
6802
+ }, this.clock());
6803
+ if (result2.proposal) replacements.push(result2.proposal);
6804
+ }
6805
+ this.rows.set(proposal.id, proposal);
6806
+ for (const row of replacements) this.rows.set(row.id, row);
6807
+ return {
6808
+ status: "created",
6809
+ proposal: snapshotActionProposal(proposal)
6810
+ };
6811
+ }
6812
+ async supersedeActionProposal(scope, id, command) {
6813
+ const replacement = this.rows.get(command.replacementProposalId);
6814
+ if (!replacement || !actionProposalScopeMatches(replacement, scope)) return {
6815
+ status: "not_found"
6816
+ };
6817
+ return this.mutate(scope, id, (row, now) => transitionActionProposalSupersession(row, replacement, command, now));
6818
+ }
6819
+ };
6820
+
4938
6821
  // src/in-memory-store.ts
4939
- var InMemoryAgentStore = class {
6822
+ var InMemoryAgentStore = class extends InMemoryActionProposalStore {
4940
6823
  static {
4941
6824
  __name(this, "InMemoryAgentStore");
4942
6825
  }
@@ -4959,11 +6842,15 @@ var InMemoryAgentStore = class {
4959
6842
  const row = {
4960
6843
  id,
4961
6844
  actorRef: input.actor.id,
6845
+ tenantRef: input.actor.tenantRef ?? null,
4962
6846
  title: input.title ?? "New chat",
4963
6847
  transient: input.transient ?? false,
4964
6848
  createdAt: ts,
4965
6849
  updatedAt: ts,
4966
- messages: []
6850
+ messages: [],
6851
+ ...input.persona !== void 0 ? {
6852
+ persona: input.persona
6853
+ } : {}
4967
6854
  };
4968
6855
  this.threads.set(id, row);
4969
6856
  return this.toSummary(row);
@@ -4991,8 +6878,12 @@ var InMemoryAgentStore = class {
4991
6878
  }
4992
6879
  const approval = toolCallApprovalFromRow({
4993
6880
  toolCallId: call.toolCallId,
6881
+ ...call.proposalId !== void 0 ? {
6882
+ proposalId: call.proposalId
6883
+ } : {},
4994
6884
  status: call.status,
4995
6885
  approver: call.approver,
6886
+ confirmation: call.confirmation,
4996
6887
  expiresAt: call.expiresAt,
4997
6888
  remember: call.remember,
4998
6889
  executedByRef: call.executedByRef,
@@ -5015,6 +6906,10 @@ var InMemoryAgentStore = class {
5015
6906
  names.add(call.toolName);
5016
6907
  }
5017
6908
  }
6909
+ const thread = this.threads.get(threadId);
6910
+ if (thread) {
6911
+ 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);
6912
+ }
5018
6913
  return [
5019
6914
  ...names
5020
6915
  ];
@@ -5055,6 +6950,7 @@ var InMemoryAgentStore = class {
5055
6950
  const row = {
5056
6951
  id,
5057
6952
  actorRef: source.actorRef,
6953
+ tenantRef: source.tenantRef,
5058
6954
  title: source.title,
5059
6955
  transient: false,
5060
6956
  createdAt: ts,
@@ -5068,6 +6964,9 @@ var InMemoryAgentStore = class {
5068
6964
  } : {},
5069
6965
  ...source.model != null ? {
5070
6966
  model: source.model
6967
+ } : {},
6968
+ ...source.persona != null ? {
6969
+ persona: source.persona
5071
6970
  } : {}
5072
6971
  };
5073
6972
  this.threads.set(id, row);
@@ -5119,6 +7018,9 @@ var InMemoryAgentStore = class {
5119
7018
  if (patch.model !== void 0) {
5120
7019
  row.model = patch.model;
5121
7020
  }
7021
+ if (patch.persona !== void 0) {
7022
+ row.persona = patch.persona;
7023
+ }
5122
7024
  row.updatedAt = this.now();
5123
7025
  }
5124
7026
  async activeRunForThread(threadId) {
@@ -5132,6 +7034,10 @@ var InMemoryAgentStore = class {
5132
7034
  async defaultAgentForThread(threadId) {
5133
7035
  return this.threads.get(threadId)?.defaultAgent ?? null;
5134
7036
  }
7037
+ /** The thread's persona, projected like {@link defaultAgentForThread}. */
7038
+ async personaForThread(threadId) {
7039
+ return this.threads.get(threadId)?.persona ?? null;
7040
+ }
5135
7041
  /** The thread's pinned model, projected like {@link defaultAgentForThread}. */
5136
7042
  async modelForThread(threadId) {
5137
7043
  return this.threads.get(threadId)?.model ?? null;
@@ -5236,12 +7142,18 @@ var InMemoryAgentStore = class {
5236
7142
  ...input.agentName !== void 0 ? {
5237
7143
  agentName: input.agentName
5238
7144
  } : {},
7145
+ ...input.persona !== void 0 ? {
7146
+ persona: input.persona
7147
+ } : {},
5239
7148
  ...input.model !== void 0 ? {
5240
7149
  model: input.model
5241
7150
  } : {},
5242
7151
  ...input.pageContext !== void 0 ? {
5243
7152
  pageContext: input.pageContext
5244
7153
  } : {},
7154
+ ...input.uiCapabilities !== void 0 ? {
7155
+ uiCapabilities: input.uiCapabilities
7156
+ } : {},
5245
7157
  ...input.interrupt === true ? {
5246
7158
  interrupt: true
5247
7159
  } : {},
@@ -5346,6 +7258,78 @@ var InMemoryAgentStore = class {
5346
7258
  }
5347
7259
  return void 0;
5348
7260
  }
7261
+ async getThreadActionProposalScope(threadId) {
7262
+ const row = this.threads.get(threadId);
7263
+ return row ? {
7264
+ threadId,
7265
+ actorRef: row.actorRef,
7266
+ tenantRef: row.tenantRef
7267
+ } : null;
7268
+ }
7269
+ async admitActionProposalOutcome(command) {
7270
+ const row = [
7271
+ ...this.rows.values()
7272
+ ].find((row2) => row2.outcome?.id === command.outcomeId);
7273
+ if (!row?.outcome || !row.outcomeDelivery) return {
7274
+ status: "not_found"
7275
+ };
7276
+ if (row.outcomeDelivery.status === "admitted") return {
7277
+ status: "unchanged",
7278
+ ...row.outcomeDelivery.messageId !== void 0 ? {
7279
+ messageId: row.outcomeDelivery.messageId
7280
+ } : {}
7281
+ };
7282
+ if (row.outcomeDelivery.status === "discarded") return {
7283
+ status: "discarded"
7284
+ };
7285
+ if (!actionProposalOutcomeFenceValid(row, command, this.clock())) return {
7286
+ status: "conflict"
7287
+ };
7288
+ const thread = this.threads.get(row.threadId);
7289
+ if (!thread) {
7290
+ this.rows.set(row.id, snapshotActionProposal({
7291
+ ...row,
7292
+ outcomeDelivery: {
7293
+ ...row.outcomeDelivery,
7294
+ status: "discarded",
7295
+ lease: null
7296
+ }
7297
+ }));
7298
+ return {
7299
+ status: "discarded"
7300
+ };
7301
+ }
7302
+ if (thread.actorRef !== row.actorRef || thread.tenantRef !== row.tenantRef) return {
7303
+ status: "conflict"
7304
+ };
7305
+ if (thread.activeStreamId) return {
7306
+ status: "busy"
7307
+ };
7308
+ const message = {
7309
+ id: crypto.randomUUID(),
7310
+ role: "assistant",
7311
+ content: actionProposalOutcomeText(row.outcome),
7312
+ createdAt: this.now(),
7313
+ actionProposalOutcome: snapshotActionProposal(row.outcome),
7314
+ ui: snapshotActionProposal(row.outcome.ui)
7315
+ };
7316
+ const next = snapshotActionProposal({
7317
+ ...row,
7318
+ outcomeDelivery: {
7319
+ ...row.outcomeDelivery,
7320
+ status: "admitted",
7321
+ lease: null,
7322
+ messageId: message.id
7323
+ }
7324
+ });
7325
+ thread.messages.push(message);
7326
+ thread.updatedAt = message.createdAt;
7327
+ this.rows.set(row.id, next);
7328
+ return {
7329
+ status: "applied",
7330
+ messageId: message.id
7331
+ };
7332
+ }
5349
7333
  async appendMessage(input) {
5350
7334
  const row = this.threads.get(input.threadId);
5351
7335
  if (row === void 0) {
@@ -5374,6 +7358,9 @@ var InMemoryAgentStore = class {
5374
7358
  ...input.agentName !== void 0 ? {
5375
7359
  agentName: input.agentName
5376
7360
  } : {},
7361
+ ...input.persona !== void 0 ? {
7362
+ persona: input.persona
7363
+ } : {},
5377
7364
  ...input.runId !== void 0 ? {
5378
7365
  runId: input.runId
5379
7366
  } : {},
@@ -5385,6 +7372,9 @@ var InMemoryAgentStore = class {
5385
7372
  } : {},
5386
7373
  ...input.ui !== void 0 ? {
5387
7374
  ui: input.ui
7375
+ } : {},
7376
+ ...input.actionProposalOutcome !== void 0 ? {
7377
+ actionProposalOutcome: input.actionProposalOutcome
5388
7378
  } : {}
5389
7379
  };
5390
7380
  row.messages.push(message);
@@ -5476,6 +7466,9 @@ var InMemoryAgentStore = class {
5476
7466
  async recordToolCall(input) {
5477
7467
  this.toolCalls.set(input.toolCallId, {
5478
7468
  toolCallId: input.toolCallId,
7469
+ ...input.proposalId !== void 0 ? {
7470
+ proposalId: input.proposalId
7471
+ } : {},
5479
7472
  messageId: input.messageId,
5480
7473
  threadId: this.threadIdForMessage(input.messageId) ?? "",
5481
7474
  toolName: input.toolName,
@@ -5489,6 +7482,9 @@ var InMemoryAgentStore = class {
5489
7482
  ...input.approver !== void 0 ? {
5490
7483
  approver: input.approver
5491
7484
  } : {},
7485
+ ...input.confirmation !== void 0 ? {
7486
+ confirmation: structuredClone(input.confirmation)
7487
+ } : {},
5492
7488
  ...input.expiresAt !== void 0 ? {
5493
7489
  expiresAt: input.expiresAt
5494
7490
  } : {}
@@ -5790,7 +7786,8 @@ var InMemoryAgentStore = class {
5790
7786
  defaultAgent: row.defaultAgent ?? null,
5791
7787
  ...row.model != null ? {
5792
7788
  model: row.model
5793
- } : {}
7789
+ } : {},
7790
+ persona: row.persona ?? null
5794
7791
  };
5795
7792
  }
5796
7793
  };
@@ -5992,12 +7989,12 @@ function withConfirmFields(schema) {
5992
7989
  });
5993
7990
  }
5994
7991
  const { args, fields } = splitConfirm(value);
5995
- const result = await inner.validate(args);
5996
- if (result.issues !== void 0) {
7992
+ const result2 = await inner.validate(args);
7993
+ if (result2.issues !== void 0) {
5997
7994
  return {
5998
7995
  issues: [
5999
7996
  ...issues,
6000
- ...result.issues
7997
+ ...result2.issues
6001
7998
  ]
6002
7999
  };
6003
8000
  }
@@ -6007,10 +8004,10 @@ function withConfirmFields(schema) {
6007
8004
  };
6008
8005
  }
6009
8006
  return {
6010
- value: isRecord(result.value) ? {
6011
- ...result.value,
8007
+ value: isRecord(result2.value) ? {
8008
+ ...result2.value,
6012
8009
  ...fields
6013
- } : result.value
8010
+ } : result2.value
6014
8011
  };
6015
8012
  }, "validate"),
6016
8013
  jsonSchema: {
@@ -6342,6 +8339,309 @@ var SqlTokenStreamSink = class {
6342
8339
  });
6343
8340
  }
6344
8341
  };
8342
+
8343
+ // src/action-proposal-text.ts
8344
+ function resolveTextActionProposalDecision(text, proposals) {
8345
+ const command = parseTextActionProposalCommand(text);
8346
+ if (command.status === "unmatched") return command;
8347
+ const { decision, remember } = command;
8348
+ const pending = proposals.filter((proposal) => proposal.decision === "pending");
8349
+ const explicitId = command.proposalId;
8350
+ if (explicitId !== void 0) {
8351
+ if (!pending.some((proposal) => proposal.id === explicitId)) return {
8352
+ status: "unmatched"
8353
+ };
8354
+ return {
8355
+ status: "decision",
8356
+ proposalId: explicitId,
8357
+ decision,
8358
+ remember
8359
+ };
8360
+ }
8361
+ if (pending.length === 0) return {
8362
+ status: "unmatched"
8363
+ };
8364
+ if (pending.length > 1) return {
8365
+ status: "ambiguous",
8366
+ proposalIds: pending.map(({ id }) => id)
8367
+ };
8368
+ const candidate = pending[0];
8369
+ if (!candidate) return {
8370
+ status: "unmatched"
8371
+ };
8372
+ return {
8373
+ status: "decision",
8374
+ proposalId: candidate.id,
8375
+ decision,
8376
+ remember
8377
+ };
8378
+ }
8379
+ __name(resolveTextActionProposalDecision, "resolveTextActionProposalDecision");
8380
+ function parseTextActionProposalCommand(text) {
8381
+ if (/[\r\n]/u.test(text)) return {
8382
+ status: "unmatched"
8383
+ };
8384
+ const normalized = text.trim();
8385
+ const match = /^(sim|confirmo|confirmar|aprovar|aprovo|cancelar|rejeitar|rejeito|não|nao)(?: (sempre nesta conversa))?(?: #([^\s]+?))?[.!]?$/iu.exec(normalized);
8386
+ if (!match) return {
8387
+ status: "unmatched"
8388
+ };
8389
+ const verb = match[1]?.toLocaleLowerCase("pt-BR");
8390
+ if (verb === void 0) return {
8391
+ status: "unmatched"
8392
+ };
8393
+ const decision = [
8394
+ "cancelar",
8395
+ "rejeitar",
8396
+ "rejeito",
8397
+ "n\xE3o",
8398
+ "nao"
8399
+ ].includes(verb) ? "rejected" : "approved";
8400
+ const remember = match[2] !== void 0;
8401
+ if (remember && decision !== "approved") return {
8402
+ status: "unmatched"
8403
+ };
8404
+ return {
8405
+ status: "command",
8406
+ decision,
8407
+ remember,
8408
+ ...match[3] !== void 0 ? {
8409
+ proposalId: match[3]
8410
+ } : {}
8411
+ };
8412
+ }
8413
+ __name(parseTextActionProposalCommand, "parseTextActionProposalCommand");
8414
+
8415
+ // src/action-proposal-executor.ts
8416
+ var ActionProposalExecutor = class {
8417
+ static {
8418
+ __name(this, "ActionProposalExecutor");
8419
+ }
8420
+ options;
8421
+ constructor(options) {
8422
+ this.options = options;
8423
+ }
8424
+ async execute(proposal) {
8425
+ let ui = createNegotiatedUiCollector(proposal.originToolCallId, {
8426
+ actor: {
8427
+ id: proposal.actorRef
8428
+ },
8429
+ threadId: proposal.threadId
8430
+ });
8431
+ try {
8432
+ const actor = await this.options.resolver.resolve({
8433
+ actorRef: proposal.actorRef,
8434
+ tenantRef: proposal.tenantRef
8435
+ });
8436
+ if (!actor || actor.id !== proposal.actorRef || (actor.tenantRef ?? null) !== proposal.tenantRef) throw new Error("Action proposal requester no longer exists or scope changed");
8437
+ const deps = await this.options.resolveExecution(proposal, actor);
8438
+ if (deps.registry.spec(proposal.toolName)?.kind !== "action") throw new Error("Action proposal tool no longer exists as an action");
8439
+ const context = proposal.executionContext;
8440
+ ui = createNegotiatedUiCollector(proposal.originToolCallId, {
8441
+ actor,
8442
+ threadId: proposal.threadId,
8443
+ ...context?.agentName !== void 0 ? {
8444
+ agentName: context.agentName
8445
+ } : {},
8446
+ ...context?.uiCapabilities !== void 0 ? {
8447
+ uiCapabilities: context.uiCapabilities
8448
+ } : {}
8449
+ }, deps.resolveUiCatalog);
8450
+ const output = await deps.registry.invoke(proposal.toolName, snapshotActionProposal(Object.hasOwn(proposal, "preparationInput") ? proposal.preparationInput : proposal.input), {
8451
+ actor,
8452
+ threadId: proposal.threadId,
8453
+ runId: proposal.originRunId,
8454
+ requestId: context?.requestId ?? proposal.originRunId,
8455
+ toolCallId: proposal.originToolCallId,
8456
+ idempotencyKey: proposal.idempotencyKey,
8457
+ ...context?.agentName !== void 0 ? {
8458
+ agentName: context.agentName
8459
+ } : {},
8460
+ ...context?.persona !== void 0 ? {
8461
+ persona: context.persona
8462
+ } : {},
8463
+ ...context?.pageContext !== void 0 ? {
8464
+ pageContext: context.pageContext
8465
+ } : {},
8466
+ ...context?.uiCapabilities !== void 0 ? {
8467
+ uiCapabilities: context.uiCapabilities
8468
+ } : {},
8469
+ ...deps.host !== void 0 ? {
8470
+ host: deps.host
8471
+ } : {},
8472
+ emitUi: ui.emit
8473
+ }, deps.rolesPolicy, {
8474
+ approvedInput: proposal.input,
8475
+ ...deps.allowedTools !== void 0 ? {
8476
+ allowedTools: deps.allowedTools
8477
+ } : {}
8478
+ });
8479
+ return snapshotActionProposal({
8480
+ status: "succeeded",
8481
+ ...output !== void 0 ? {
8482
+ result: output
8483
+ } : {},
8484
+ ui: ui.components(),
8485
+ ...ui.text() ? {
8486
+ text: ui.text()
8487
+ } : {}
8488
+ });
8489
+ } catch (error) {
8490
+ return {
8491
+ status: "failed",
8492
+ error: error instanceof Error ? error.message : String(error),
8493
+ ui: ui.components(),
8494
+ ...ui.text() ? {
8495
+ text: ui.text()
8496
+ } : {}
8497
+ };
8498
+ }
8499
+ }
8500
+ };
8501
+
8502
+ // src/action-proposal-worker.ts
8503
+ var ActionProposalWorker = class {
8504
+ static {
8505
+ __name(this, "ActionProposalWorker");
8506
+ }
8507
+ options;
8508
+ leaseMs;
8509
+ pollIntervalMs;
8510
+ maxConcurrency;
8511
+ timer;
8512
+ stopped = false;
8513
+ renewals = /* @__PURE__ */ new Set();
8514
+ active;
8515
+ constructor(options) {
8516
+ this.options = options;
8517
+ this.leaseMs = options.leaseMs ?? 3e4;
8518
+ this.pollIntervalMs = options.pollIntervalMs ?? 1e3;
8519
+ this.maxConcurrency = options.maxConcurrency ?? 1;
8520
+ for (const value of [
8521
+ this.leaseMs,
8522
+ this.pollIntervalMs,
8523
+ this.maxConcurrency
8524
+ ]) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError("Worker configuration must be positive safe integers");
8525
+ if (this.pollIntervalMs >= this.leaseMs / 3) throw new RangeError("Worker poll interval must be below one third of lease");
8526
+ if (!options.workerId) throw new TypeError("workerId is required");
8527
+ }
8528
+ start() {
8529
+ if (this.timer !== void 0 || this.stopped) return;
8530
+ const tick = /* @__PURE__ */ __name(async () => {
8531
+ if (this.stopped) return;
8532
+ try {
8533
+ await this.runOnce();
8534
+ } catch (error) {
8535
+ this.options.onError?.(error);
8536
+ }
8537
+ if (!this.stopped) this.timer = setTimeout(tick, this.pollIntervalMs);
8538
+ }, "tick");
8539
+ this.timer = setTimeout(tick, 0);
8540
+ }
8541
+ async stop() {
8542
+ this.stopped = true;
8543
+ for (const timer of this.renewals) clearInterval(timer);
8544
+ this.renewals.clear();
8545
+ if (this.timer !== void 0) clearTimeout(this.timer);
8546
+ await this.active;
8547
+ }
8548
+ runOnce() {
8549
+ if (this.active) return this.active;
8550
+ if (this.stopped) return Promise.resolve();
8551
+ const task = this.tick();
8552
+ this.active = task;
8553
+ return task.finally(() => {
8554
+ if (this.active === task) this.active = void 0;
8555
+ });
8556
+ }
8557
+ async tick() {
8558
+ await this.options.store.expireActionProposals({
8559
+ limit: 100
8560
+ });
8561
+ const work = [];
8562
+ for (let index = 0; index < this.maxConcurrency && !this.stopped; index++) {
8563
+ const proposal = await this.options.store.claimNextActionProposal({
8564
+ workerId: this.options.workerId,
8565
+ leaseMs: this.leaseMs
8566
+ });
8567
+ if (!proposal) break;
8568
+ work.push(this.execute(proposal));
8569
+ }
8570
+ await Promise.all(work);
8571
+ for (let index = 0; index < 32 && !this.stopped; index++) {
8572
+ const delivery = await this.options.store.claimNextActionProposalOutcome({
8573
+ workerId: this.options.workerId,
8574
+ leaseMs: this.leaseMs
8575
+ });
8576
+ if (!delivery) break;
8577
+ await this.options.store.admitActionProposalOutcome(delivery.lease);
8578
+ }
8579
+ }
8580
+ async execute(proposal) {
8581
+ const lease = proposal.execution?.lease;
8582
+ if (!lease) throw new Error("Claimed action lacks execution lease");
8583
+ let fenced = true;
8584
+ let renewing;
8585
+ const renew = /* @__PURE__ */ __name(() => {
8586
+ if (renewing || !fenced) return;
8587
+ renewing = this.options.store.extendActionProposalLease(proposal, proposal.id, {
8588
+ token: lease.token,
8589
+ generation: lease.generation,
8590
+ leaseMs: this.leaseMs
8591
+ }).then((result2) => {
8592
+ if (result2.status !== "applied") fenced = false;
8593
+ }, (error) => {
8594
+ fenced = false;
8595
+ this.options.onError?.(error);
8596
+ }).finally(() => {
8597
+ renewing = void 0;
8598
+ });
8599
+ }, "renew");
8600
+ const timer = setInterval(renew, this.leaseMs / 3);
8601
+ this.renewals.add(timer);
8602
+ try {
8603
+ const result2 = await this.options.executor.execute(proposal);
8604
+ clearInterval(timer);
8605
+ await renewing;
8606
+ if (!fenced) return;
8607
+ const settled = await this.options.store.settleActionProposal(proposal, proposal.id, {
8608
+ ...result2,
8609
+ token: lease.token,
8610
+ generation: lease.generation
8611
+ });
8612
+ if (settled.status === "applied" && settled.proposal) await this.options.onSettled?.(settled.proposal);
8613
+ } finally {
8614
+ clearInterval(timer);
8615
+ this.renewals.delete(timer);
8616
+ await renewing;
8617
+ }
8618
+ }
8619
+ };
8620
+
8621
+ // src/action-proposal-capabilities.ts
8622
+ function assertIndependentActionCapabilities(store, resolver) {
8623
+ const candidate = store;
8624
+ if (!resolver || typeof resolver.resolve !== "function") throw new Error("Independent action mode requires a BackgroundActorResolver");
8625
+ if (!isChatQueueStore(store) || candidate.actionProposalAdmissionSupported === false) throw new Error("Independent action mode requires transactional thread admission");
8626
+ const required = [
8627
+ "createActionProposal",
8628
+ "getActionProposal",
8629
+ "listActionProposals",
8630
+ "decideActionProposal",
8631
+ "claimActionProposal",
8632
+ "extendActionProposalLease",
8633
+ "settleActionProposal",
8634
+ "claimNextActionProposal",
8635
+ "expireActionProposals",
8636
+ "getThreadActionProposalScope",
8637
+ "claimNextActionProposalOutcome",
8638
+ "admitActionProposalOutcome",
8639
+ "createReplacingActionProposal",
8640
+ "supersedeActionProposal"
8641
+ ];
8642
+ 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");
8643
+ }
8644
+ __name(assertIndependentActionCapabilities, "assertIndependentActionCapabilities");
6345
8645
  export {
6346
8646
  AGENT_ACTOR_DIRECTORY,
6347
8647
  AGENT_ACTOR_RESOLVER,
@@ -6375,6 +8675,8 @@ export {
6375
8675
  APPROVAL_EXPIRED_REASON,
6376
8676
  ASK_TOOL_DESCRIPTION,
6377
8677
  ASK_TOOL_NAME,
8678
+ ActionProposalExecutor,
8679
+ ActionProposalWorker,
6378
8680
  AgentRegistry,
6379
8681
  AgentStreamError,
6380
8682
  CONFIRM_JSON_SCHEMA_PROPERTIES,
@@ -6395,6 +8697,7 @@ export {
6395
8697
  DefaultRolesPolicy,
6396
8698
  ELICITATION_INPUT_TYPES,
6397
8699
  GLOBAL_SCOPE,
8700
+ InMemoryActionProposalStore,
6398
8701
  InMemoryAgentStore,
6399
8702
  InMemoryConfirmTokenStore,
6400
8703
  MAX_ASK_QUESTIONS,
@@ -6418,15 +8721,25 @@ export {
6418
8721
  THREAD_DETAIL_CONTENT_CHARS,
6419
8722
  ToolDisabledError,
6420
8723
  ToolForbiddenError,
8724
+ ToolInputDriftError,
6421
8725
  ToolInputInvalidError,
6422
8726
  ToolNotFoundError,
8727
+ ToolPreflightDeniedError,
6423
8728
  ToolRegistry,
6424
8729
  UNFINISHED_TOOL_CALL,
8730
+ actionProposalCreationMatches,
8731
+ actionProposalOutcomeFenceValid,
8732
+ actionProposalOutcomeScope,
8733
+ actionProposalOutcomeText,
8734
+ actionProposalReceipt,
8735
+ actionProposalScopeMatches,
6425
8736
  actorScope,
6426
8737
  agentDiagnosticKey,
6427
8738
  agentFailureCode,
6428
8739
  askInputSchema,
6429
8740
  askToolDefinition,
8741
+ assertIndependentActionCapabilities,
8742
+ attachActionProposalOutcome,
6430
8743
  bucketByActor,
6431
8744
  bucketByModel,
6432
8745
  bucketByThread,
@@ -6434,12 +8747,15 @@ export {
6434
8747
  buildMemoryBlock,
6435
8748
  buildSkillsBlock,
6436
8749
  canActorUseTool,
8750
+ canonicalActionProposalJson,
6437
8751
  canonicalJson,
8752
+ claimActionProposalOutcome,
6438
8753
  closeEmptyRoles,
6439
8754
  compositeSkillProvider,
6440
8755
  confirmTokenExpiry,
6441
8756
  createFrameBuffer,
6442
8757
  createIncrementalGate,
8758
+ createNegotiatedUiCollector,
6443
8759
  createNoopEmitUi,
6444
8760
  createUiCollector,
6445
8761
  danglingToolCallIds,
@@ -6452,6 +8768,7 @@ export {
6452
8768
  detachedStarted,
6453
8769
  detachedUnsettled,
6454
8770
  encodeStreamEvent,
8771
+ escapeUnsafeToolUiText,
6455
8772
  estimateCost,
6456
8773
  estimateMessageTokens,
6457
8774
  exhaustedWindow,
@@ -6462,9 +8779,12 @@ export {
6462
8779
  filterToolsByEnabled,
6463
8780
  filterToolsByRole,
6464
8781
  findCatalogModel,
8782
+ findPersona,
6465
8783
  gateFollowUps,
6466
8784
  gateTail,
6467
8785
  hashConfirmToken,
8786
+ initialActionProposal,
8787
+ intersectAllowLists,
6468
8788
  invokeWithTransientRetry,
6469
8789
  isChatQueueStore,
6470
8790
  isControlFlowSignal,
@@ -6482,6 +8802,9 @@ export {
6482
8802
  observeTurnFrames,
6483
8803
  offerMemories,
6484
8804
  offerSkills,
8805
+ parseTextActionProposalCommand,
8806
+ personaCatalogEntry,
8807
+ prepareActionProposal,
6485
8808
  publishAgentDelegated,
6486
8809
  publishAgentMemoryResolved,
6487
8810
  publishAgentMemoryWritten,
@@ -6507,11 +8830,14 @@ export {
6507
8830
  rememberToolDefinition,
6508
8831
  renderElicitationAnswers,
6509
8832
  repairInstruction,
8833
+ resolveActionProposalApproval,
6510
8834
  resolveElicitation,
6511
8835
  resolveGateLookback,
6512
8836
  resolveMemoryDigest,
6513
8837
  resolveOutputGateMode,
8838
+ resolvePersonaAlias,
6514
8839
  resolveSkillCatalog,
8840
+ resolveTextActionProposalDecision,
6515
8841
  resolveToolTransientRetryNumbers,
6516
8842
  rollupThreadUsage,
6517
8843
  runAgentLoop,
@@ -6528,6 +8854,7 @@ export {
6528
8854
  skillInputSchema,
6529
8855
  skillToolDefinition,
6530
8856
  skillWriteVerdict,
8857
+ snapshotActionProposal,
6531
8858
  stampToolKinds,
6532
8859
  staticModelCatalog,
6533
8860
  staticSkillProvider,
@@ -6538,8 +8865,18 @@ export {
6538
8865
  toolCallContext,
6539
8866
  traceLlmTurn,
6540
8867
  traceToolExecution,
8868
+ transitionActionProposalClaim,
8869
+ transitionActionProposalDecision,
8870
+ transitionActionProposalLease,
8871
+ transitionActionProposalSettlement,
8872
+ transitionActionProposalSupersession,
6541
8873
  truncateDetailContent,
6542
8874
  unwrapToolStepOutput,
8875
+ validateActionProposalCreation,
8876
+ validateActionProposalDiscoveryIndexBatch,
8877
+ validateActionProposalExpiryBatch,
8878
+ validateActionProposalListQuery,
8879
+ validateActionProposalWorkerClaim,
6543
8880
  validateElicitationAnswer,
6544
8881
  validateElicitationValue,
6545
8882
  validateStructured,
@@ -6552,6 +8889,7 @@ export {
6552
8889
  withSkillTool,
6553
8890
  withToolTimeout,
6554
8891
  withTurnFrames,
8892
+ wrapToolPreflightDenied,
6555
8893
  wrapToolStepOutput,
6556
8894
  writeMemory
6557
8895
  };