@sema-agent/core 7.11.2 → 7.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +86 -13
  2. package/dist/core/ask-origin.d.ts +55 -0
  3. package/dist/core/ask-origin.js +21 -0
  4. package/dist/core/auto-mode-arming.d.ts +10 -14
  5. package/dist/core/auto-mode-arming.js +3 -9
  6. package/dist/core/auto-mode-defaults.d.ts +0 -2
  7. package/dist/core/auto-mode-defaults.js +0 -1
  8. package/dist/core/auto-mode-rebuild.d.ts +6 -13
  9. package/dist/core/auto-mode-rebuild.js +0 -2
  10. package/dist/core/auto-mode.d.ts +30 -89
  11. package/dist/core/auto-mode.js +12 -59
  12. package/dist/core/checkpoint-store.d.ts +1 -3
  13. package/dist/core/engine-notice.d.ts +18 -0
  14. package/dist/core/gate-fold.js +1 -9
  15. package/dist/core/gate-lanes.d.ts +0 -40
  16. package/dist/core/gate-lanes.js +18 -26
  17. package/dist/core/governance-codes.d.ts +1 -1
  18. package/dist/core/governance-codes.js +2 -0
  19. package/dist/core/hooks.d.ts +7 -1
  20. package/dist/core/hooks.js +1 -1
  21. package/dist/core/runner/assemble-result.d.ts +45 -1
  22. package/dist/core/runner/assemble-result.js +4 -1
  23. package/dist/core/runner/compaction-seams.d.ts +42 -0
  24. package/dist/core/runner/compaction-seams.js +80 -0
  25. package/dist/core/runner/contracts.d.ts +85 -15
  26. package/dist/core/runner/denial-limit-arms.d.ts +10 -13
  27. package/dist/core/runner/denial-limit-arms.js +12 -9
  28. package/dist/core/runner/gate-exit.js +9 -1
  29. package/dist/core/runner/permission-rule-lanes.d.ts +2 -1
  30. package/dist/core/runner/permission-rule-lanes.js +2 -1
  31. package/dist/core/runner/prepare-caps-and-workflow.d.ts +1 -1
  32. package/dist/core/runner/prepare-caps-and-workflow.js +0 -5
  33. package/dist/core/runner/prepare-policy-chain.js +3 -2
  34. package/dist/core/runner/prepare-protocol-tools.js +5 -0
  35. package/dist/core/runner/prepare-safety-scan.js +11 -16
  36. package/dist/core/runner/prepare-suspend-saga.d.ts +0 -2
  37. package/dist/core/runner/prepare-suspend-saga.js +2 -10
  38. package/dist/core/runner/prepare-task.d.ts +9 -1
  39. package/dist/core/runner/prepare-task.js +7 -2
  40. package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -1
  41. package/dist/core/runner/prepare-wiring-manifest.js +1 -8
  42. package/dist/core/runner/resume-claim.d.ts +2 -2
  43. package/dist/core/runner/resume-preflight.d.ts +2 -2
  44. package/dist/core/runner/run-attachment-seats.d.ts +6 -4
  45. package/dist/core/runner/run-attachment-seats.js +2 -2
  46. package/dist/core/runner/run-git-lane.d.ts +1 -1
  47. package/dist/core/runner/run-harness-handlers.js +2 -0
  48. package/dist/core/runner/run-identity-wiring.d.ts +21 -33
  49. package/dist/core/runner/run-identity-wiring.js +6 -3
  50. package/dist/core/runner/run-leg.d.ts +108 -0
  51. package/dist/core/runner/run-leg.js +713 -0
  52. package/dist/core/runner/run-notification-lane.d.ts +56 -0
  53. package/dist/core/runner/run-notification-lane.js +128 -0
  54. package/dist/core/runner/run-reasoning-seat.d.ts +5 -5
  55. package/dist/core/runner/run-reasoning-seat.js +7 -7
  56. package/dist/core/runner/run-recovery-lanes.d.ts +4 -17
  57. package/dist/core/runner/run-recovery-lanes.js +5 -4
  58. package/dist/core/runner/run-settle-and-teardown.d.ts +113 -0
  59. package/dist/core/runner/run-settle-and-teardown.js +432 -0
  60. package/dist/core/runner/run-stop-and-final-verify.d.ts +2 -2
  61. package/dist/core/runner/run-terminal-adoption.d.ts +99 -0
  62. package/dist/core/runner/run-terminal-adoption.js +200 -0
  63. package/dist/core/runner/run-turn-boundary.js +5 -4
  64. package/dist/core/runner/runtask.d.ts +10 -79
  65. package/dist/core/runner/runtask.js +83 -1557
  66. package/dist/core/runner/stream-lifecycle-verbs.js +10 -1
  67. package/dist/core/runner-deps.d.ts +4 -14
  68. package/dist/core/store-contracts/workflow-journal-store-contract.d.ts +7 -0
  69. package/dist/core/store-contracts/workflow-journal-store-contract.js +85 -0
  70. package/dist/core/task-stream.d.ts +10 -1
  71. package/dist/core/tool-face.d.ts +8 -0
  72. package/dist/core/tool-face.js +1 -0
  73. package/dist/core/tool-policy.d.ts +42 -96
  74. package/dist/core/tool-policy.js +1 -11
  75. package/dist/core/tool-registry.d.ts +11 -3
  76. package/dist/core/tool-registry.js +7 -1
  77. package/dist/core/tool-roster.d.ts +26 -0
  78. package/dist/core/tool-roster.js +38 -7
  79. package/dist/core/trace.d.ts +6 -7
  80. package/dist/core/wiring-manifest.d.ts +5 -22
  81. package/dist/core/wiring-manifest.js +3 -11
  82. package/dist/core/workflow-journal-store.d.ts +35 -4
  83. package/dist/core/workflow-journal-store.js +19 -2
  84. package/dist/index.d.ts +6 -5
  85. package/dist/index.js +6 -5
  86. package/dist/orchestration/workflow.js +2 -0
  87. package/dist/server/http.js +2 -1
  88. package/dist/stores/file/workflow-journal-store.d.ts +7 -10
  89. package/dist/stores/file/workflow-journal-store.js +2 -4
  90. package/package.json +1 -1
  91. package/test/export-surface.snapshot.json +21 -11
@@ -0,0 +1,713 @@
1
+ import { snapshotActorAssertion } from "../../internal/llm.js";
2
+ import { redactThenCut } from "../../agents/subagent-steps.js";
3
+ import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
4
+ import { CheckpointError, readPendingSteerQueue, realApprovalOrgFact, remainingBudgetMicroUsd } from "../checkpoint-store.js";
5
+ import { normalizePersistedRuleHit } from "../gate-lanes.js";
6
+ import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
7
+ import { buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
8
+ import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
9
+ import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
10
+ import { computeCostMicroUsd } from "../pricing.js";
11
+ import { mintSystemReminder } from "../reminder-mint.js";
12
+ import { hasDestroy } from "../remote-env.js";
13
+ import { refuseOutOfContractDecision } from "../tool-policy.js";
14
+ import { callFaceSeat, judgeParkedToolIdentity, rosterEntryNamed, toolCallFaceOf } from "../tool-roster.js";
15
+ import { deliverEngineNotice } from "../types.js";
16
+ import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
17
+ import { errorCodeOf } from "./assemble-result.js";
18
+ import { DEFAULT_PRECALL_OUTPUT_TOKENS, platformLimitTerminal } from "./clock-and-limits.js";
19
+ import { deepJsonEqual, DEFERRED_REISSUE, resumeContinuation } from "./decide-continuation.js";
20
+ import { settleExecutionRecord } from "./execution-record.js";
21
+ import { mintGateOutcome } from "./gate-exit.js";
22
+ import { resolveGitLegDelivery, wrapGitFrame } from "./git-leg-delivery.js";
23
+ import { GIT_STATUS_ECHO_PREVIEW } from "./git-status-frame.js";
24
+ import { toImageContent } from "./image.js";
25
+ import { nextHumanInputSeq } from "./steer-admission.js";
26
+ import { settleTeardownLeg } from "./teardown-bounded.js";
27
+ import { toolEndBodyFrom, toolResultMsg } from "./tool-end-body.js";
28
+ import { attachmentEnvelopeTags, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingDelta, renderOrphanedBackgroundTasks, renderSkillsListingDelta, stampWriteAnchor } from "./turn-attachments.js";
29
+ export async function runLeg(input) {
30
+ const { spec, queue, prepared, resume, rs, entryActor, effectiveTimeoutMs, timeout, pushContent, ident, emitCommitted, writeFamilyOf, startedToolCallIds, announceWorkspaceMove, withBrainSinks, flushGitMirror, loopLatch, notificationLane, unsubscribeTaskNotifications, unsubGitRetry, unsubBoundary, unsub, runner, next } = input;
31
+ let final;
32
+ let threw;
33
+ let abortedLive = false;
34
+ let userInterruptedLive = false;
35
+ let strandedHumanAnswers = [];
36
+ try {
37
+ if (prepared.abortController.signal.aborted) {
38
+ const e = new Error("run aborted");
39
+ e.name = "AbortError";
40
+ throw e;
41
+ }
42
+ if (resume) {
43
+ const walltimeExhaustedResume = effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0;
44
+ const resumeWindowRetryAfterMs = !walltimeExhaustedResume && prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
45
+ if (resumeWindowRetryAfterMs !== undefined) {
46
+ const owesNothing = resume.outcome.gate === "resource_limit" &&
47
+ readPendingSteerQueue(resume.cp.state).length === 0 &&
48
+ (resume.cp.state.runningBackgroundTasks?.length ?? 0) === 0;
49
+ const committed = owesNothing &&
50
+ prepared.suspendForPlatformLimit !== undefined &&
51
+ (await prepared.suspendForPlatformLimit("usage_window", { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) }, { resumeAfterMs: resumeWindowRetryAfterMs }));
52
+ if (committed) {
53
+ rs.limits.turnsExceeded = false;
54
+ rs.limits.budgetHit = undefined;
55
+ }
56
+ else {
57
+ const terminal = platformLimitTerminal("usage_window", resumeWindowRetryAfterMs, "resume");
58
+ rs.limits.platformTerminal = terminal;
59
+ try {
60
+ runner.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
61
+ }
62
+ catch {
63
+ }
64
+ }
65
+ }
66
+ if (resumeWindowRetryAfterMs === undefined && !walltimeExhaustedResume && resume.outcome.gate !== "wake") {
67
+ await applyResumeDecision(runner, prepared, resume, (e) => pushContent({ ...e, ...ident() }), emitCommitted, (toolName, details) => {
68
+ if (rs.attach.attachState === undefined)
69
+ return;
70
+ const family = writeFamilyOf(toolName);
71
+ if (family !== undefined)
72
+ stampWriteAnchor(rs.attach.attachState, family, rs.counters.cadenceTurns);
73
+ if (details !== undefined)
74
+ reduceToolEnd(rs.attach.attachState, details);
75
+ }, (toolCallId) => {
76
+ startedToolCallIds.add(toolCallId);
77
+ if (resume !== undefined && resume.cp.pendingAction.kind === "tool_approval" && toolCallId === resume.cp.pendingAction.toolCallId) {
78
+ resume.pendingActionStarted = true;
79
+ }
80
+ });
81
+ if (resume.outcome.gate === "policy_ask")
82
+ resume.decisionDelivered = true;
83
+ announceWorkspaceMove();
84
+ }
85
+ if (resumeWindowRetryAfterMs !== undefined) {
86
+ }
87
+ else if (walltimeExhaustedResume) {
88
+ timeout.fired = true;
89
+ timeout.clear();
90
+ void prepared.harness.abort();
91
+ prepared.abortController.abort();
92
+ }
93
+ else if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow <= 0) {
94
+ rs.limits.budgetHit = "exceeded";
95
+ rs.limits.budgetAxis = "tokens";
96
+ }
97
+ else if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd <= 0) {
98
+ rs.limits.budgetHit = "exceeded";
99
+ rs.limits.budgetAxis = "cost";
100
+ }
101
+ else {
102
+ let continuation = resumeContinuation(resume, prepared.reminderMark);
103
+ const bgSnapshot = resume.cp.state.runningBackgroundTasks;
104
+ if (bgSnapshot !== undefined && bgSnapshot.length > 0) {
105
+ const alive = new Set(prepared.listBackgroundTasks().map((t) => t.id));
106
+ const orphans = bgSnapshot.filter((t) => !alive.has(t.id));
107
+ if (orphans.length > 0) {
108
+ continuation += "\n\n" + formatHookFeedback(renderOrphanedBackgroundTasks(orphans), prepared.reminderMark);
109
+ }
110
+ }
111
+ let gitResumeDelivered;
112
+ {
113
+ const gitBody = await resolveGitLegDelivery(prepared, resume.cp.state.gitAnnouncement, (err) => {
114
+ try {
115
+ runner.deps.onError?.(new Error(`git announcement ladder unreadable (conservative re-announce): ${err.message}`), { phase: "degraded", sessionId: prepared.sessionId });
116
+ }
117
+ catch {
118
+ }
119
+ });
120
+ const gitFrame = prepared.gitStatusRef.frame;
121
+ if (gitBody !== undefined && gitFrame !== undefined) {
122
+ const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
123
+ continuation += "\n\n" + wrappedGit;
124
+ gitResumeDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, wrapped: wrappedGit };
125
+ if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
126
+ prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
127
+ }
128
+ }
129
+ }
130
+ const engineSegments = continuation.length > 0 ? [{ start: 0, end: continuation.length }] : [];
131
+ const resumeFrames = [
132
+ ...readPendingSteerQueue(resume.cp.state).map((entry) => ({ entry, source: "steer" })),
133
+ ...(resume.wakeMessage !== undefined ? [{ entry: resume.wakeMessage, source: "wake" }] : []),
134
+ ];
135
+ for (const { entry: steer, source } of resumeFrames) {
136
+ if (source === "wake" && resume.wakeMessageHookContext !== undefined) {
137
+ const hookStart = continuation.length;
138
+ continuation += "\n\n" + formatHookFeedback(resume.wakeMessageHookContext, prepared.reminderMark);
139
+ engineSegments.push({ start: hookStart, end: continuation.length });
140
+ }
141
+ if (source === "steer") {
142
+ const parkedScreen = (spec.hooks ?? runner.deps.hooks)?.userPromptSubmit;
143
+ if (parkedScreen !== undefined) {
144
+ let outcome;
145
+ try {
146
+ const seat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => parkedScreen(steer.text, {
147
+ identity: prepared.hookIdentity,
148
+ signal: sig,
149
+ source: "parked_redelivery",
150
+ ...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
151
+ ...(steer.actor !== undefined ? { actor: snapshotActorAssertion(steer.actor) } : {}),
152
+ }));
153
+ outcome = seat.expired
154
+ ? { blocked: seat.cause === "timeout" ? `the screen did not answer within its ${prepared.hookTimeoutMs}ms bound (fail-closed)` : "the run was cancelled while the screen was still deciding (fail-closed)" }
155
+ : seat.value?.block !== undefined && seat.value.block !== ""
156
+ ? { blocked: inlineUntrusted(seat.value.block) }
157
+ : { ...(seat.value?.additionalContext !== undefined && seat.value.additionalContext !== "" ? { context: seat.value.additionalContext } : {}) };
158
+ }
159
+ catch (hookErr) {
160
+ const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
161
+ try {
162
+ runner.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
163
+ }
164
+ catch {
165
+ }
166
+ outcome = { blocked: `the screen crashed (${inlineUntrusted(err.message)}; fail-closed)` };
167
+ }
168
+ if ("blocked" in outcome) {
169
+ deliverEngineNotice(runner.deps.onNotice, {
170
+ code: "steering.parked_input_blocked",
171
+ message: "a parked steering input was withheld by the deployment's userPromptSubmit screen when this resume redelivered it: " +
172
+ `${outcome.blocked}. The parked row was consumed with the checkpoint; the instruction never reached the model — ` +
173
+ "re-issue it if it still applies.",
174
+ detail: {
175
+ ...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
176
+ sessionId: prepared.sessionId,
177
+ runId: prepared.runId,
178
+ ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
179
+ },
180
+ });
181
+ queue.push({
182
+ ...buildHumanInputEvent({
183
+ carrier: source,
184
+ source,
185
+ delivery: "blocked",
186
+ sessionSeq: nextHumanInputSeq(prepared.harness),
187
+ ...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
188
+ ...(steer.actor !== undefined ? { actor: steer.actor } : {}),
189
+ ...(steer.actor?.issuer !== undefined ? { issuer: steer.actor.issuer } : {}),
190
+ ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
191
+ }),
192
+ ...ident(),
193
+ });
194
+ continue;
195
+ }
196
+ if ("context" in outcome && outcome.context !== undefined) {
197
+ const hookStart = continuation.length;
198
+ continuation += "\n\n" + formatHookFeedback(outcome.context, prepared.reminderMark);
199
+ engineSegments.push({ start: hookStart, end: continuation.length });
200
+ }
201
+ }
202
+ }
203
+ const start = continuation.length;
204
+ const projected = projectHumanInput({ text: steer.text, actor: steer.actor, source });
205
+ continuation +=
206
+ "\n\n" +
207
+ (steer.trusted ? formatHookFeedback(projected, prepared.reminderMark) : delimitUntrusted("supervisor steering message", projected));
208
+ if (steer.trusted)
209
+ engineSegments.push({ start, end: continuation.length });
210
+ queue.push({
211
+ ...buildHumanInputEvent({
212
+ carrier: source,
213
+ source,
214
+ delivery: "applied",
215
+ sessionSeq: nextHumanInputSeq(prepared.harness),
216
+ inputId: steer.inputId,
217
+ ...(steer.actor !== undefined ? { actor: steer.actor } : {}),
218
+ ...(steer.actor?.issuer !== undefined ? { issuer: steer.actor.issuer } : {}),
219
+ ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
220
+ }),
221
+ ...ident(),
222
+ });
223
+ }
224
+ if (resume !== undefined)
225
+ resume.decisionDelivered = true;
226
+ if (gitResumeDelivered !== undefined) {
227
+ const delivered = gitResumeDelivered;
228
+ prepared.gitStatusRef.protectedText = delivered.wrapped;
229
+ prepared.gitStatusRef.pendingReceipt = {
230
+ text: continuation,
231
+ commit: (entryId) => {
232
+ prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
233
+ queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
234
+ rs.attach.attachmentsInjected += 1;
235
+ prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
236
+ },
237
+ };
238
+ }
239
+ final = await withBrainSinks(() => prepared.harness.prompt(continuation, { engineSegments }));
240
+ await flushGitMirror();
241
+ if (prepared.gitStatusRef.announced?.pending === true)
242
+ await prepared.gitStatusRef.reassert?.();
243
+ }
244
+ }
245
+ else {
246
+ const objectiveActor = entryActor;
247
+ const projectedObjective = projectHumanInput({ text: spec.objective, actor: objectiveActor, source: "objective" });
248
+ let effectiveObjective = projectedObjective;
249
+ let promptBlocked = false;
250
+ const userPromptSubmit = (spec.hooks ?? runner.deps.hooks)?.userPromptSubmit;
251
+ if (userPromptSubmit) {
252
+ try {
253
+ const promptSeat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => userPromptSubmit(spec.objective, { identity: prepared.hookIdentity, signal: sig, source: "objective", ...(objectiveActor !== undefined ? { actor: snapshotActorAssertion(objectiveActor) } : {}) }));
254
+ if (promptSeat.expired) {
255
+ if (promptSeat.cause === "timeout") {
256
+ try {
257
+ runner.deps.onError?.(hookSeatExpiredError("userPromptSubmit", prepared.hookTimeoutMs, promptSeat.cause, "the prompt was NOT submitted (fail-closed) and the task ends blocked"), { phase: "hook", sessionId: prepared.sessionId });
258
+ }
259
+ catch {
260
+ }
261
+ }
262
+ prepared.blockedRef.reason = formatHookFeedback(promptSeat.cause === "timeout"
263
+ ? `the deployment's userPromptSubmit hook did not answer within its ${prepared.hookTimeoutMs}ms bound while screening this prompt; ` +
264
+ `the prompt was NOT submitted (fail-closed)`
265
+ : `the task was cancelled while the deployment's userPromptSubmit hook was still screening this prompt; ` +
266
+ `the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
267
+ promptBlocked = true;
268
+ }
269
+ const decision = promptSeat.expired ? undefined : promptSeat.value;
270
+ if (decision?.block) {
271
+ prepared.blockedRef.reason = formatHookFeedback(decision.block, prepared.reminderMark);
272
+ promptBlocked = true;
273
+ }
274
+ else if (decision?.additionalContext) {
275
+ effectiveObjective = `${formatHookFeedback(decision.additionalContext, prepared.reminderMark)}\n\n${projectedObjective}`;
276
+ }
277
+ }
278
+ catch (hookErr) {
279
+ const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
280
+ try {
281
+ runner.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
282
+ }
283
+ catch {
284
+ }
285
+ prepared.blockedRef.reason = formatHookFeedback(`the deployment's userPromptSubmit hook crashed while screening this prompt (${err.message}); ` +
286
+ `the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
287
+ promptBlocked = true;
288
+ }
289
+ }
290
+ let gitLegDelivered;
291
+ if (!promptBlocked) {
292
+ const gitBody = await resolveGitLegDelivery(prepared, undefined, (err) => {
293
+ try {
294
+ runner.deps.onError?.(new Error(`git announcement ladder unreadable (conservative re-announce): ${err.message}`), { phase: "degraded", sessionId: prepared.sessionId });
295
+ }
296
+ catch {
297
+ }
298
+ });
299
+ const gitFrame = prepared.gitStatusRef.frame;
300
+ if (gitBody !== undefined && gitFrame !== undefined) {
301
+ const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
302
+ const standalone = spec.images !== undefined && spec.images.length > 0;
303
+ if (!standalone)
304
+ effectiveObjective = `${wrappedGit}\n${effectiveObjective}`;
305
+ gitLegDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, standalone, wrapped: wrappedGit };
306
+ if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
307
+ prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
308
+ }
309
+ }
310
+ }
311
+ const firstFrames = [];
312
+ if (rs.attach.attachState !== undefined) {
313
+ const attach = rs.attach.attachState;
314
+ if (rs.attach.agentListingOn && prepared.agentListing !== undefined) {
315
+ const listing = prepared.agentListing;
316
+ const body = renderAgentListingDelta(attach, listing.entries, listing.toolName, listing.models);
317
+ if (body !== undefined) {
318
+ firstFrames.push({
319
+ source: "agent_listing",
320
+ body,
321
+ commit: () => {
322
+ commitAgentListing(attach, listing.entries, listing.models);
323
+ prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
324
+ if (listing.models !== undefined)
325
+ prepared.announcedListingsRef.models = [...listing.models];
326
+ },
327
+ });
328
+ }
329
+ }
330
+ if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined) {
331
+ const listing = prepared.skillsListing;
332
+ const body = renderSkillsListingDelta(attach, listing.entries);
333
+ if (body !== undefined) {
334
+ firstFrames.push({
335
+ source: "skills_listing",
336
+ body,
337
+ commit: () => {
338
+ commitSkillsListing(attach, listing.entries);
339
+ prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
340
+ },
341
+ });
342
+ }
343
+ }
344
+ if (firstFrames.length > 0) {
345
+ effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body, attachmentEnvelopeTags(f.source)), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
346
+ }
347
+ }
348
+ const gitQueuedChars = gitLegDelivered !== undefined && gitLegDelivered.standalone ? gitLegDelivered.wrapped.length : 0;
349
+ const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
350
+ ? 0
351
+ : computeCostMicroUsd({
352
+ totalInputTokens: Math.ceil((effectiveObjective.length + gitQueuedChars) / 4),
353
+ cacheReadTokens: 0,
354
+ cacheWriteTokens: 0,
355
+ cacheWriteTokensLong: 0,
356
+ outputTokens: prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS,
357
+ }, rs.telemetry.pricing);
358
+ const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? rs.budget.remainingMicroUsd : rs.budget.maxCostMicroUsd;
359
+ const precallTokens = Math.ceil((effectiveObjective.length + gitQueuedChars) / 4) + (prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS);
360
+ const precallCeilingTokens = prepared.suspendForResource !== undefined ? rs.budget.remainingTokens : rs.budget.maxTokensWindow;
361
+ const entryUsageRetryAfterMs = prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
362
+ if (promptBlocked) {
363
+ }
364
+ else if (entryUsageRetryAfterMs !== undefined) {
365
+ const terminal = platformLimitTerminal("usage_window", entryUsageRetryAfterMs, "entry");
366
+ rs.limits.platformTerminal = terminal;
367
+ try {
368
+ runner.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
369
+ }
370
+ catch {
371
+ }
372
+ }
373
+ else if (precallCeilingTokens !== undefined && precallTokens > precallCeilingTokens) {
374
+ rs.limits.budgetHit = "precall";
375
+ rs.limits.budgetAxis = "tokens";
376
+ }
377
+ else if (precallCeilingMicroUsd !== undefined && precallMicroUsd > precallCeilingMicroUsd) {
378
+ rs.limits.budgetHit = "precall";
379
+ rs.limits.budgetAxis = "cost";
380
+ }
381
+ else if (effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0) {
382
+ timeout.fired = true;
383
+ timeout.clear();
384
+ void prepared.harness.abort();
385
+ prepared.abortController.abort();
386
+ }
387
+ else {
388
+ const images = spec.images
389
+ ? await Promise.all(spec.images.map((img) => toImageContent(img, runner.deps.allowImageUrl)))
390
+ : undefined;
391
+ if (gitLegDelivered !== undefined) {
392
+ const delivered = gitLegDelivered;
393
+ if (delivered.standalone)
394
+ await prepared.harness.nextTurn(delivered.wrapped, { engineMinted: true });
395
+ prepared.gitStatusRef.protectedText = delivered.wrapped;
396
+ prepared.gitStatusRef.pendingReceipt = {
397
+ text: delivered.standalone ? delivered.wrapped : effectiveObjective,
398
+ commit: (entryId) => {
399
+ prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
400
+ queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
401
+ rs.attach.attachmentsInjected += 1;
402
+ prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
403
+ },
404
+ };
405
+ }
406
+ for (const f of firstFrames) {
407
+ f.commit();
408
+ queue.push({ type: "steering_injected", source: f.source, preview: redactThenCut(f.body, 220), ...ident() });
409
+ }
410
+ rs.attach.attachmentsInjected += firstFrames.length;
411
+ if (firstFrames.length > 0 && prepared.announcedSnapshotRecovered)
412
+ await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
413
+ const enginePrefixChars = effectiveObjective.length - projectedObjective.length;
414
+ queue.push({
415
+ ...buildHumanInputEvent({
416
+ carrier: "objective",
417
+ source: "objective",
418
+ delivery: "applied",
419
+ sessionSeq: nextHumanInputSeq(prepared.harness),
420
+ ...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
421
+ ...(objectiveActor?.issuer !== undefined ? { issuer: objectiveActor.issuer } : {}),
422
+ ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
423
+ }),
424
+ ...ident(),
425
+ });
426
+ final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, {
427
+ ...(images && images.length > 0 ? { images } : {}),
428
+ ...(enginePrefixChars > 0 ? { enginePrefixChars } : {}),
429
+ ...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
430
+ }));
431
+ await flushGitMirror();
432
+ if (prepared.gitStatusRef.announced?.pending === true)
433
+ await prepared.gitStatusRef.reassert?.();
434
+ }
435
+ }
436
+ loopLatch.ended = true;
437
+ abortedLive = prepared.abortController.signal.aborted;
438
+ userInterruptedLive = loopLatch.userInterrupted;
439
+ }
440
+ catch (err) {
441
+ loopLatch.ended = true;
442
+ if (errorCodeOf(err) === "resume.tool_unavailable" || errorCodeOf(err) === "resume.tool_contract_mismatch") {
443
+ await settleTeardownLeg(() => prepared.mcp.dispose(), "mcp.dispose (tool_unavailable leg)", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
444
+ await settleTeardownLeg(() => prepared.a2a?.dispose(), "a2a.dispose (tool_unavailable leg)", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
445
+ await settleTeardownLeg(() => (prepared.ownedEnv && hasDestroy(prepared.ownedEnv) ? prepared.ownedEnv.destroy() : undefined), "ownedEnv.destroy (tool_unavailable leg)", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
446
+ throw err;
447
+ }
448
+ threw = err;
449
+ abortedLive = prepared.abortController.signal.aborted;
450
+ userInterruptedLive = loopLatch.userInterrupted;
451
+ }
452
+ finally {
453
+ timeout.clear();
454
+ prepared.lspDiagnostics?.registry.releaseRun(prepared.lspDiagnostics.runIdent);
455
+ prepared.abortController.abort();
456
+ prepared.releaseSignal();
457
+ strandedHumanAnswers = prepared.settleContentAskBindings();
458
+ try {
459
+ prepared.harness.recoverUndrainedEngineNotes();
460
+ }
461
+ catch {
462
+ }
463
+ notificationLane.live = false;
464
+ unsubscribeTaskNotifications();
465
+ prepared.toolRosterDeltas.unsubscribe();
466
+ unsubGitRetry();
467
+ unsubBoundary();
468
+ unsub();
469
+ }
470
+ return await next({ final, threw, abortedLive, userInterruptedLive, strandedHumanAnswers });
471
+ }
472
+ const ORG_DISCLOSURE_MAX_CHARS = 600;
473
+ async function applyResumeDecision(runner, prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
474
+ const { pendingAction } = resume.cp;
475
+ if (resume.outcome.gate === "dry_run_review")
476
+ return;
477
+ if (resume.outcome.gate === "plan_review")
478
+ return;
479
+ if (pendingAction.kind !== "tool_approval")
480
+ return;
481
+ if (resume.outcome.gate === "policy_ask" &&
482
+ resume.outcome.decision === "allow" &&
483
+ !prepared.tools.some((t) => t.name === pendingAction.toolName)) {
484
+ const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume — refusing to continue as if it ran; the checkpoint is reopened for a retry with the tool present`);
485
+ e.code = "resume.tool_unavailable";
486
+ throw e;
487
+ }
488
+ const liveRoster = prepared.toolRosterDeltas.current;
489
+ if (pendingAction.kind === "tool_approval" && liveRoster !== undefined && judgeParkedToolIdentity(pendingAction, rosterEntryNamed(liveRoster, pendingAction.toolName)) === "mismatch") {
490
+ const e = new Error(`the approved tool "${pendingAction.toolName}" on this leg is not the tool the approval was minted against (its contract / shape / revision differ) — refusing to execute a replacement wearing the name; the checkpoint is reopened for a retry with the original tool present`);
491
+ e.code = "resume.tool_contract_mismatch";
492
+ throw e;
493
+ }
494
+ const completed = new Set(pendingAction.completedCallIds);
495
+ const deferredIds = pendingAction.batchToolCallIds.filter((id) => id !== pendingAction.toolCallId && !completed.has(id));
496
+ const names = new Map();
497
+ const deferredArgs = new Map();
498
+ if (deferredIds.length > 0) {
499
+ const { messages } = await prepared.session.buildContext();
500
+ for (const m of messages) {
501
+ if (m.role === "assistant") {
502
+ for (const c of m.content) {
503
+ if (c.type === "toolCall") {
504
+ names.set(c.id, c.name);
505
+ deferredArgs.set(c.id, c.arguments);
506
+ }
507
+ }
508
+ }
509
+ }
510
+ }
511
+ for (const id of pendingAction.batchToolCallIds) {
512
+ if (id === pendingAction.toolCallId) {
513
+ await resolvePendingCall(runner, prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart);
514
+ }
515
+ else if (!completed.has(id)) {
516
+ const name = names.get(id) ?? "unknown";
517
+ const displayLabel = (() => {
518
+ const l = prepared.tools.find((t) => t.name === name)?.label;
519
+ return l !== undefined && l !== name ? { label: l } : {};
520
+ })();
521
+ emit({ type: "tool_start", toolCallId: id, toolName: name, ...displayLabel, args: deferredArgs.get(id) ?? {} });
522
+ emit({ type: "tool_end", toolCallId: id, toolName: name, ...displayLabel, isError: true, ...toolEndBodyFrom({ content: formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark) }, true, prepared.structuredProjector) });
523
+ const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark), true));
524
+ emitCommitted(eid, "toolResult", id);
525
+ }
526
+ }
527
+ }
528
+ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
529
+ const { pendingAction } = resume.cp;
530
+ const { outcome } = resume;
531
+ if (pendingAction.kind !== "tool_approval" || outcome.gate !== "policy_ask")
532
+ return;
533
+ const decidedGate = outcome.gateOutcome;
534
+ if (decidedGate === undefined) {
535
+ throw new CheckpointError("checkpoint.invalid_outcome", "pending action reached the resolver with no settlement record on its decision (gateOutcome) — refusing to execute or settle a decision that carries no record");
536
+ }
537
+ const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
538
+ const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
539
+ emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs });
540
+ const settleEnd = async (isError, result, gate = decidedGate) => {
541
+ emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError, prepared.structuredProjector, gate) });
542
+ const word = await resume.recordExecutionOutcome(gate);
543
+ settleExecutionRecord(runner.deps.onNotice, word, { toolName: pendingAction.toolName, gate, sessionId: prepared.sessionId, runId: prepared.runId, scope: resume.cp.scope, ...(resume.cp.checkpointId !== undefined ? { checkpointId: resume.cp.checkpointId } : {}) });
544
+ };
545
+ const vetoed = (layer) => mintGateOutcome({
546
+ deniedBy: layer,
547
+ ...(outcome.gateOutcome?.settlement !== undefined && outcome.gateOutcome.origin !== undefined ? { settled: { settlement: outcome.gateOutcome.settlement, origin: outcome.gateOutcome.origin } } : {}),
548
+ });
549
+ if (outcome.decision === "deny") {
550
+ const defaultDenial = outcome.gateOutcome?.settlement?.kind === "park_sla_expired"
551
+ ? `No one answered the approval request for the pending tool call "${pendingAction.toolName}" — the approval window elapsed with no answer, so it was not executed.`
552
+ : `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
553
+ const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason, REVIEWER_NOTE_MAX_BODY) : defaultDenial;
554
+ await settleEnd(true, { content: formatHookFeedback(reason, prepared.reminderMark) });
555
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason, prepared.reminderMark), true));
556
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
557
+ return;
558
+ }
559
+ if (outcome.decision !== "allow") {
560
+ throw new CheckpointError("checkpoint.invalid_outcome", `pending action reached the resolver with decision "${String(outcome.decision)}" — only "allow" executes and only "deny" injects a denial; refusing to execute`);
561
+ }
562
+ const beltBudget = {
563
+ resourceRemainingMicroUsd: remainingBudgetMicroUsd(prepared.resourceLedger),
564
+ resourceSpentMicroUsd: prepared.resourceLedger?.spentMicroUsd ?? 0,
565
+ suspendCount: resume.cp.suspendCount ?? 0,
566
+ };
567
+ if (outcome.decision === "allow" && outcome.updatedInput !== undefined && prepared.basePolicyForResumeEdit) {
568
+ const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check({
569
+ toolName: pendingAction.toolName,
570
+ args: resolvedArgs,
571
+ toolCallId: pendingAction.toolCallId,
572
+ budget: beltBudget,
573
+ ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
574
+ ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
575
+ }, prepared.abortController.signal));
576
+ const beltUnsafe = rechecked.action === "deny" ||
577
+ (rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs));
578
+ if (beltUnsafe) {
579
+ const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is refused by the deployment's tool policy and was not executed${rechecked.action === "deny" && rechecked.message ? `: ${rechecked.message}` : " (the policy rewrote the edit to a value that was not approved)"}.`, prepared.reminderMark);
580
+ await settleEnd(true, { content: editedDenial }, vetoed("policy"));
581
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
582
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
583
+ return;
584
+ }
585
+ }
586
+ const gateRealApproval = resume.cp.gate.kind === "irreversible_ask" ? resume.cp.gate.realApproval : undefined;
587
+ const gateOrgGoverned = realApprovalOrgFact(gateRealApproval) !== undefined;
588
+ if (gateOrgGoverned && prepared.permissionRuleOrg === undefined) {
589
+ const unwiredDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its approval was minted under organization governance (${gateRealApproval.origin}), and this worker has no org adjudication wiring — a governed approval may only be redeemed where governance can be enforced. This approval is spent; re-issue the call on an org-wired worker.`, prepared.reminderMark);
590
+ await settleEnd(true, { content: unwiredDenial }, vetoed("org"));
591
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, unwiredDenial, true));
592
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
593
+ return;
594
+ }
595
+ if (prepared.permissionRuleOrg !== undefined) {
596
+ const orgVerdict = prepared.permissionRuleOrg
597
+ .adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId })
598
+ .catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
599
+ let orgWaitCancelled = false;
600
+ const org = await settleOrgVerdictWithin(orgVerdict, { status: "unavailable", disclosures: [`the org adjudication face did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms (or the task ended first)`] }, {
601
+ signal: prepared.abortController.signal,
602
+ timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS,
603
+ onFallback: (cause) => {
604
+ orgWaitCancelled = cause === "aborted";
605
+ },
606
+ });
607
+ const blocked = org.status === "unavailable"
608
+ ? realApprovalOrgFact(gateRealApproval) === "unavailable"
609
+ ? undefined
610
+ : orgWaitCancelled
611
+ ? "this deployment is org-governed and the task was cancelled before the organization policy snapshot could be adjudicated"
612
+ : "this deployment is org-governed and cannot currently adjudicate against an organization policy snapshot"
613
+ : org.verdict?.behavior === "deny"
614
+ ? `an organization policy rule (${org.verdict.rule}) denies it`
615
+ : undefined;
616
+ if (blocked !== undefined) {
617
+ const orgDisclosures = org.status === "unavailable" && org.disclosures.length > 0
618
+ ? ` Governance disclosures: ${inlineUntrusted(org.disclosures.join("; "), ORG_DISCLOSURE_MAX_CHARS)}.`
619
+ : "";
620
+ const orgDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${blocked}.${orgDisclosures} This approval is spent — the call has to be re-issued and approved again once organization policy permits it.`, prepared.reminderMark);
621
+ await settleEnd(true, { content: orgDenial }, vetoed("org"));
622
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, orgDenial, true));
623
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
624
+ return;
625
+ }
626
+ }
627
+ if (prepared.denyNarrowingPolicy) {
628
+ const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check({
629
+ toolName: pendingAction.toolName,
630
+ args: resolvedArgs,
631
+ toolCallId: pendingAction.toolCallId,
632
+ ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
633
+ budget: beltBudget,
634
+ ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
635
+ }, prepared.abortController.signal));
636
+ if (narrowed.action === "deny") {
637
+ const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
638
+ await settleEnd(true, { content: narrowedDenial }, vetoed("policy"));
639
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
640
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
641
+ return;
642
+ }
643
+ }
644
+ if (prepared.persistedRuleLane !== undefined) {
645
+ const unreadable = { unreadable: true };
646
+ const read = await settleOrgVerdictWithin(Promise.resolve()
647
+ .then(() => prepared.persistedRuleLane.adjudicate({
648
+ toolName: pendingAction.toolName,
649
+ args: resolvedArgs,
650
+ toolCallId: pendingAction.toolCallId,
651
+ ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
652
+ budget: beltBudget,
653
+ ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
654
+ }))
655
+ .then((answer) => normalizePersistedRuleHit(answer)), unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
656
+ const tightened = applyPersistedTightening({ action: "allow" }, read);
657
+ if (tightened.tightened === "deny" || tightened.tightened === "unreadable") {
658
+ const why = tightened.tightened === "deny"
659
+ ? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
660
+ : "your persisted permission rules could not be read on this leg";
661
+ const ruleDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${why}. This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
662
+ await settleEnd(true, { content: ruleDenial }, vetoed("persisted_rule"));
663
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, ruleDenial, true));
664
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
665
+ return;
666
+ }
667
+ }
668
+ const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
669
+ if (!tool) {
670
+ const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
671
+ e.code = "resume.tool_unavailable";
672
+ throw e;
673
+ }
674
+ const args = resolvedArgs;
675
+ onExecuteStart?.(pendingAction.toolCallId);
676
+ prepared.suspendProgressRef.executedApproved = true;
677
+ let res;
678
+ try {
679
+ res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
680
+ }
681
+ catch (err) {
682
+ const execError = `Error: ${err instanceof Error ? err.message : String(err)}`;
683
+ const marks = (() => {
684
+ if (err === null || typeof err !== "object")
685
+ return undefined;
686
+ const src = err;
687
+ let d;
688
+ if (src.details !== null && typeof src.details === "object" && !Array.isArray(src.details)) {
689
+ d = { ...src.details };
690
+ }
691
+ if (typeof src.errorKind === "string")
692
+ d = { ...(d ?? {}), errorKind: src.errorKind };
693
+ return d;
694
+ })();
695
+ await settleEnd(true, { content: execError, ...(marks !== undefined ? { details: marks } : {}) });
696
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, execError, true));
697
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
698
+ return;
699
+ }
700
+ const executedIsError = res.isError === true;
701
+ await settleEnd(executedIsError, res);
702
+ onResolvedToolSuccess?.(pendingAction.toolName, executedIsError ? undefined : res.details);
703
+ const eid = await prepared.session.appendMessage({
704
+ role: "toolResult",
705
+ toolCallId: pendingAction.toolCallId,
706
+ toolName: pendingAction.toolName,
707
+ content: res.content,
708
+ details: res.details,
709
+ isError: executedIsError,
710
+ timestamp: Date.now(),
711
+ });
712
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
713
+ }