@qduc/term2 0.32.2 → 0.33.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 (269) hide show
  1. package/dist/build-info.json +2 -2
  2. package/dist/components/menu/ModelSelectionMenu.d.ts.map +1 -1
  3. package/dist/components/menu/ModelSelectionMenu.js +12 -14
  4. package/dist/components/menu/ModelSelectionMenu.js.map +1 -1
  5. package/dist/lib/agent-chat-service.d.ts.map +1 -1
  6. package/dist/lib/agent-chat-service.js +2 -6
  7. package/dist/lib/agent-chat-service.js.map +1 -1
  8. package/dist/lib/agent-client.d.ts +2 -1
  9. package/dist/lib/agent-client.d.ts.map +1 -1
  10. package/dist/lib/agent-client.js +71 -84
  11. package/dist/lib/agent-client.js.map +1 -1
  12. package/dist/lib/agent-factory.d.ts.map +1 -1
  13. package/dist/lib/agent-factory.js +17 -19
  14. package/dist/lib/agent-factory.js.map +1 -1
  15. package/dist/prompts/tool-surface-guidance.js +1 -1
  16. package/dist/prompts/tool-surface-guidance.js.map +1 -1
  17. package/dist/prompts/tool-surface-guidance.ts +1 -1
  18. package/dist/providers/ai-sdk-anthropic.provider.d.ts.map +1 -1
  19. package/dist/providers/ai-sdk-anthropic.provider.js +16 -2
  20. package/dist/providers/ai-sdk-anthropic.provider.js.map +1 -1
  21. package/dist/providers/ai-sdk-google.provider.d.ts.map +1 -1
  22. package/dist/providers/ai-sdk-google.provider.js +16 -2
  23. package/dist/providers/ai-sdk-google.provider.js.map +1 -1
  24. package/dist/providers/ai-sdk-openrouter.provider.d.ts.map +1 -1
  25. package/dist/providers/ai-sdk-openrouter.provider.js +16 -1
  26. package/dist/providers/ai-sdk-openrouter.provider.js.map +1 -1
  27. package/dist/providers/ai-sdk-provider-settings.d.ts +2 -0
  28. package/dist/providers/ai-sdk-provider-settings.d.ts.map +1 -1
  29. package/dist/providers/ai-sdk-provider-settings.js +5 -0
  30. package/dist/providers/ai-sdk-provider-settings.js.map +1 -1
  31. package/dist/providers/ai-sdk-streamed-model.d.ts +3 -3
  32. package/dist/providers/ai-sdk-streamed-model.d.ts.map +1 -1
  33. package/dist/providers/ai-sdk-streamed-model.js +5 -69
  34. package/dist/providers/ai-sdk-streamed-model.js.map +1 -1
  35. package/dist/providers/anthropic-chat-caching.d.ts +5 -0
  36. package/dist/providers/anthropic-chat-caching.d.ts.map +1 -0
  37. package/dist/providers/{common/openai-compatible-messages.js → anthropic-chat-caching.js} +19 -1
  38. package/dist/providers/anthropic-chat-caching.js.map +1 -0
  39. package/dist/providers/codex.provider.js +2 -0
  40. package/dist/providers/codex.provider.js.map +1 -1
  41. package/dist/providers/custom-provider-runtime.d.ts +18 -0
  42. package/dist/providers/custom-provider-runtime.d.ts.map +1 -0
  43. package/dist/providers/custom-provider-runtime.js +26 -0
  44. package/dist/providers/custom-provider-runtime.js.map +1 -0
  45. package/dist/providers/llama-cpp.provider.d.ts +3 -0
  46. package/dist/providers/llama-cpp.provider.d.ts.map +1 -1
  47. package/dist/providers/llama-cpp.provider.js +17 -0
  48. package/dist/providers/llama-cpp.provider.js.map +1 -1
  49. package/dist/providers/neuralwatt.provider.d.ts +6 -0
  50. package/dist/providers/neuralwatt.provider.d.ts.map +1 -0
  51. package/dist/providers/neuralwatt.provider.js +8 -0
  52. package/dist/providers/neuralwatt.provider.js.map +1 -0
  53. package/dist/providers/openai-compatible-lazy.d.ts.map +1 -1
  54. package/dist/providers/openai-compatible-lazy.js +2 -0
  55. package/dist/providers/openai-compatible-lazy.js.map +1 -1
  56. package/dist/providers/openai-compatible-middleware.d.ts +4 -9
  57. package/dist/providers/openai-compatible-middleware.d.ts.map +1 -1
  58. package/dist/providers/openai-compatible-middleware.js +25 -48
  59. package/dist/providers/openai-compatible-middleware.js.map +1 -1
  60. package/dist/providers/openai-compatible-response-normalizer.d.ts +1 -1
  61. package/dist/providers/openai-compatible-response-normalizer.d.ts.map +1 -1
  62. package/dist/providers/openai-compatible-response-normalizer.js +6 -6
  63. package/dist/providers/openai-compatible-response-normalizer.js.map +1 -1
  64. package/dist/providers/openai-compatible.provider.d.ts +2 -32
  65. package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
  66. package/dist/providers/openai-compatible.provider.js +18 -144
  67. package/dist/providers/openai-compatible.provider.js.map +1 -1
  68. package/dist/providers/openai.provider.d.ts.map +1 -1
  69. package/dist/providers/openai.provider.js +1 -0
  70. package/dist/providers/openai.provider.js.map +1 -1
  71. package/dist/providers/opencode-middleware.d.ts +9 -0
  72. package/dist/providers/opencode-middleware.d.ts.map +1 -0
  73. package/dist/providers/opencode-middleware.js +15 -0
  74. package/dist/providers/opencode-middleware.js.map +1 -0
  75. package/dist/providers/opencode-model-provider.d.ts +19 -0
  76. package/dist/providers/opencode-model-provider.d.ts.map +1 -0
  77. package/dist/providers/opencode-model-provider.js +128 -0
  78. package/dist/providers/opencode-model-provider.js.map +1 -0
  79. package/dist/providers/opencode.provider.d.ts +2 -0
  80. package/dist/providers/opencode.provider.d.ts.map +1 -1
  81. package/dist/providers/opencode.provider.js +4 -0
  82. package/dist/providers/opencode.provider.js.map +1 -1
  83. package/dist/providers/openrouter-decision-models.d.ts +7 -0
  84. package/dist/providers/openrouter-decision-models.d.ts.map +1 -0
  85. package/dist/providers/openrouter-decision-models.js +31 -0
  86. package/dist/providers/openrouter-decision-models.js.map +1 -0
  87. package/dist/providers/openrouter.provider.js +2 -1
  88. package/dist/providers/openrouter.provider.js.map +1 -1
  89. package/dist/providers/orcarouter-models.d.ts +12 -0
  90. package/dist/providers/orcarouter-models.d.ts.map +1 -0
  91. package/dist/providers/orcarouter-models.js +34 -0
  92. package/dist/providers/orcarouter-models.js.map +1 -0
  93. package/dist/providers/registry.d.ts +7 -0
  94. package/dist/providers/registry.d.ts.map +1 -1
  95. package/dist/providers/registry.js.map +1 -1
  96. package/dist/services/agent-runtime/application-run-loop.d.ts +20 -1
  97. package/dist/services/agent-runtime/application-run-loop.d.ts.map +1 -1
  98. package/dist/services/agent-runtime/application-run-loop.js +871 -759
  99. package/dist/services/agent-runtime/application-run-loop.js.map +1 -1
  100. package/dist/services/agent-runtime/tool-call-execution.d.ts +1 -1
  101. package/dist/services/agent-runtime/tool-call-execution.d.ts.map +1 -1
  102. package/dist/services/agent-runtime/tool-call-execution.js +6 -3
  103. package/dist/services/agent-runtime/tool-call-execution.js.map +1 -1
  104. package/dist/services/approval/approval-flow-coordinator.d.ts +4 -2
  105. package/dist/services/approval/approval-flow-coordinator.d.ts.map +1 -1
  106. package/dist/services/approval/approval-flow-coordinator.js +2 -1
  107. package/dist/services/approval/approval-flow-coordinator.js.map +1 -1
  108. package/dist/services/approval/approval-state.d.ts +0 -13
  109. package/dist/services/approval/approval-state.d.ts.map +1 -1
  110. package/dist/services/approval/approval-state.js +0 -5
  111. package/dist/services/approval/approval-state.js.map +1 -1
  112. package/dist/services/approval/background-subagent-approval-controller.d.ts.map +1 -1
  113. package/dist/services/approval/background-subagent-approval-controller.js +0 -2
  114. package/dist/services/approval/background-subagent-approval-controller.js.map +1 -1
  115. package/dist/services/approval/nested-approval-owner.d.ts.map +1 -1
  116. package/dist/services/approval/nested-approval-owner.js +5 -1
  117. package/dist/services/approval/nested-approval-owner.js.map +1 -1
  118. package/dist/services/approval/shell-auto-approval-resolver.d.ts +2 -2
  119. package/dist/services/approval/shell-auto-approval-resolver.d.ts.map +1 -1
  120. package/dist/services/approval/shell-auto-approval-resolver.js +2 -2
  121. package/dist/services/approval/shell-auto-approval-resolver.js.map +1 -1
  122. package/dist/services/approval/tool-approval-batch-coordinator.d.ts +2 -2
  123. package/dist/services/approval/tool-approval-batch-coordinator.d.ts.map +1 -1
  124. package/dist/services/approval/tool-approval-batch-coordinator.js +4 -4
  125. package/dist/services/approval/tool-approval-batch-coordinator.js.map +1 -1
  126. package/dist/services/conversation/conversation-adapter.d.ts +17 -47
  127. package/dist/services/conversation/conversation-adapter.d.ts.map +1 -1
  128. package/dist/services/conversation/conversation-adapter.js +114 -88
  129. package/dist/services/conversation/conversation-adapter.js.map +1 -1
  130. package/dist/services/conversation/conversation-orchestrator.d.ts.map +1 -1
  131. package/dist/services/conversation/conversation-orchestrator.js +48 -121
  132. package/dist/services/conversation/conversation-orchestrator.js.map +1 -1
  133. package/dist/services/conversation/conversation-record-stream.d.ts +21 -0
  134. package/dist/services/conversation/conversation-record-stream.d.ts.map +1 -0
  135. package/dist/services/conversation/conversation-record-stream.js +186 -0
  136. package/dist/services/conversation/conversation-record-stream.js.map +1 -0
  137. package/dist/services/conversation/conversation-record.d.ts +160 -0
  138. package/dist/services/conversation/conversation-record.d.ts.map +1 -0
  139. package/dist/services/conversation/conversation-record.js +371 -0
  140. package/dist/services/conversation/conversation-record.js.map +1 -0
  141. package/dist/services/conversation/conversation-result-builder.d.ts +0 -4
  142. package/dist/services/conversation/conversation-result-builder.d.ts.map +1 -1
  143. package/dist/services/conversation/conversation-result-builder.js +0 -10
  144. package/dist/services/conversation/conversation-result-builder.js.map +1 -1
  145. package/dist/services/conversation/conversation-service.d.ts +2 -1
  146. package/dist/services/conversation/conversation-service.d.ts.map +1 -1
  147. package/dist/services/conversation/conversation-service.js +3 -0
  148. package/dist/services/conversation/conversation-service.js.map +1 -1
  149. package/dist/services/conversation-agent-client.d.ts +17 -7
  150. package/dist/services/conversation-agent-client.d.ts.map +1 -1
  151. package/dist/services/logging/conversation-logger.d.ts +1 -7
  152. package/dist/services/logging/conversation-logger.d.ts.map +1 -1
  153. package/dist/services/logging/conversation-logger.js +0 -18
  154. package/dist/services/logging/conversation-logger.js.map +1 -1
  155. package/dist/services/model-service.d.ts +2 -6
  156. package/dist/services/model-service.d.ts.map +1 -1
  157. package/dist/services/model-service.js +8 -21
  158. package/dist/services/model-service.js.map +1 -1
  159. package/dist/services/models/decision-model-listing.d.ts +2 -12
  160. package/dist/services/models/decision-model-listing.d.ts.map +1 -1
  161. package/dist/services/models/decision-model-listing.js +2 -29
  162. package/dist/services/models/decision-model-listing.js.map +1 -1
  163. package/dist/services/provider-continuity.d.ts +26 -0
  164. package/dist/services/provider-continuity.d.ts.map +1 -1
  165. package/dist/services/provider-continuity.js +47 -0
  166. package/dist/services/provider-continuity.js.map +1 -1
  167. package/dist/services/retry/recovery-executor.d.ts +3 -4
  168. package/dist/services/retry/recovery-executor.d.ts.map +1 -1
  169. package/dist/services/retry/recovery-executor.js +13 -75
  170. package/dist/services/retry/recovery-executor.js.map +1 -1
  171. package/dist/services/retry/retry-contracts.d.ts +2 -10
  172. package/dist/services/retry/retry-contracts.d.ts.map +1 -1
  173. package/dist/services/session/continuation-plan-applier.d.ts +5 -9
  174. package/dist/services/session/continuation-plan-applier.d.ts.map +1 -1
  175. package/dist/services/session/continuation-plan-applier.js +9 -43
  176. package/dist/services/session/continuation-plan-applier.js.map +1 -1
  177. package/dist/services/session/continuation-recovery-handler.d.ts +4 -6
  178. package/dist/services/session/continuation-recovery-handler.d.ts.map +1 -1
  179. package/dist/services/session/continuation-recovery-handler.js +3 -15
  180. package/dist/services/session/continuation-recovery-handler.js.map +1 -1
  181. package/dist/services/session/initial-input-preparer.d.ts +2 -2
  182. package/dist/services/session/initial-input-preparer.d.ts.map +1 -1
  183. package/dist/services/session/initial-input-preparer.js +3 -3
  184. package/dist/services/session/initial-input-preparer.js.map +1 -1
  185. package/dist/services/session/initial-turn-recovery-handler.d.ts +2 -4
  186. package/dist/services/session/initial-turn-recovery-handler.d.ts.map +1 -1
  187. package/dist/services/session/initial-turn-recovery-handler.js +4 -5
  188. package/dist/services/session/initial-turn-recovery-handler.js.map +1 -1
  189. package/dist/services/session/live-run.d.ts +9 -3
  190. package/dist/services/session/live-run.d.ts.map +1 -1
  191. package/dist/services/session/live-run.js +19 -4
  192. package/dist/services/session/live-run.js.map +1 -1
  193. package/dist/services/session/session-composition.d.ts +8 -12
  194. package/dist/services/session/session-composition.d.ts.map +1 -1
  195. package/dist/services/session/session-composition.js +41 -87
  196. package/dist/services/session/session-composition.js.map +1 -1
  197. package/dist/services/session/session-input-planner.d.ts +2 -2
  198. package/dist/services/session/session-input-planner.d.ts.map +1 -1
  199. package/dist/services/session/session-input-planner.js +6 -13
  200. package/dist/services/session/session-input-planner.js.map +1 -1
  201. package/dist/services/session/session-lifecycle.d.ts +73 -23
  202. package/dist/services/session/session-lifecycle.d.ts.map +1 -1
  203. package/dist/services/session/session-lifecycle.js +127 -45
  204. package/dist/services/session/session-lifecycle.js.map +1 -1
  205. package/dist/services/session/session-stream-processor.d.ts +4 -30
  206. package/dist/services/session/session-stream-processor.d.ts.map +1 -1
  207. package/dist/services/session/session-stream-processor.js +11 -300
  208. package/dist/services/session/session-stream-processor.js.map +1 -1
  209. package/dist/services/session/session-tool-tracker.d.ts +9 -144
  210. package/dist/services/session/session-tool-tracker.d.ts.map +1 -1
  211. package/dist/services/session/session-tool-tracker.js +17 -251
  212. package/dist/services/session/session-tool-tracker.js.map +1 -1
  213. package/dist/services/session/{turn-workflow.d.ts → session-turn-adapter.d.ts} +10 -43
  214. package/dist/services/session/session-turn-adapter.d.ts.map +1 -0
  215. package/dist/services/session/session-turn-adapter.js +734 -0
  216. package/dist/services/session/session-turn-adapter.js.map +1 -0
  217. package/dist/services/session/turn-attempt-factory.d.ts +2 -11
  218. package/dist/services/session/turn-attempt-factory.d.ts.map +1 -1
  219. package/dist/services/session/turn-attempt-factory.js +4 -12
  220. package/dist/services/session/turn-attempt-factory.js.map +1 -1
  221. package/dist/services/session/turn-attempt.d.ts +0 -3
  222. package/dist/services/session/turn-attempt.d.ts.map +1 -1
  223. package/dist/services/session/turn-attempt.js +0 -5
  224. package/dist/services/session/turn-attempt.js.map +1 -1
  225. package/dist/services/session/turn-coordinator.d.ts +3 -3
  226. package/dist/services/session/turn-coordinator.d.ts.map +1 -1
  227. package/dist/services/session/turn-coordinator.js +31 -26
  228. package/dist/services/session/turn-coordinator.js.map +1 -1
  229. package/dist/services/session/turn-projection-state.d.ts +45 -0
  230. package/dist/services/session/turn-projection-state.d.ts.map +1 -0
  231. package/dist/services/session/turn-projection-state.js +61 -0
  232. package/dist/services/session/turn-projection-state.js.map +1 -0
  233. package/dist/services/subagents/tool-policy.d.ts +5 -5
  234. package/dist/services/subagents/tool-policy.d.ts.map +1 -1
  235. package/dist/services/subagents/tool-policy.js.map +1 -1
  236. package/dist/tools/system/run-code/run-code-runtime.d.ts.map +1 -1
  237. package/dist/tools/system/run-code/run-code-runtime.js +90 -19
  238. package/dist/tools/system/run-code/run-code-runtime.js.map +1 -1
  239. package/dist/tools/system/shell.d.ts +2 -2
  240. package/dist/tools/system/shell.d.ts.map +1 -1
  241. package/dist/tools/system/shell.js +1 -0
  242. package/dist/tools/system/shell.js.map +1 -1
  243. package/dist/tools/tool-names.d.ts +1 -1
  244. package/dist/tools/tool-names.d.ts.map +1 -1
  245. package/dist/utils/ai/token-usage.js +9 -8
  246. package/dist/utils/ai/token-usage.js.map +1 -1
  247. package/package.json +1 -1
  248. package/dist/providers/anthropic-middleware.d.ts +0 -25
  249. package/dist/providers/anthropic-middleware.d.ts.map +0 -1
  250. package/dist/providers/anthropic-middleware.js +0 -32
  251. package/dist/providers/anthropic-middleware.js.map +0 -1
  252. package/dist/providers/common/openai-compatible-messages.d.ts +0 -2
  253. package/dist/providers/common/openai-compatible-messages.d.ts.map +0 -1
  254. package/dist/providers/common/openai-compatible-messages.js.map +0 -1
  255. package/dist/services/session/continuation-call-id-resolver.d.ts +0 -29
  256. package/dist/services/session/continuation-call-id-resolver.d.ts.map +0 -1
  257. package/dist/services/session/continuation-call-id-resolver.js +0 -69
  258. package/dist/services/session/continuation-call-id-resolver.js.map +0 -1
  259. package/dist/services/session/continuation-state.d.ts +0 -69
  260. package/dist/services/session/continuation-state.d.ts.map +0 -1
  261. package/dist/services/session/continuation-state.js +0 -78
  262. package/dist/services/session/continuation-state.js.map +0 -1
  263. package/dist/services/session/session-manager.d.ts +0 -87
  264. package/dist/services/session/session-manager.d.ts.map +0 -1
  265. package/dist/services/session/session-manager.js +0 -146
  266. package/dist/services/session/session-manager.js.map +0 -1
  267. package/dist/services/session/turn-workflow.d.ts.map +0 -1
  268. package/dist/services/session/turn-workflow.js +0 -1055
  269. package/dist/services/session/turn-workflow.js.map +0 -1
@@ -0,0 +1,734 @@
1
+ import { TurnAttempt } from './turn-attempt.js';
2
+ import { getCallIdFromObject, getToolInfoFromInterruption } from '../interruption-info.js';
3
+ import { ShellAutoApprovalDecisionPolicy } from '../approval/approval-decision-policy.js';
4
+ import { isClassifiedCancellation } from '../retry/provider-failure-classification.js';
5
+ import { extractUsage } from '../../utils/ai/token-usage.js';
6
+ import { TurnProjectionState } from './turn-projection-state.js';
7
+ import { ToolApprovalBatchCoordinator } from '../approval/tool-approval-batch-coordinator.js';
8
+ import { createApprovalRequiredTerminal, buildConversationResult, } from '../conversation/conversation-result-builder.js';
9
+ import { ToolCallMarkerStore } from '../../utils/streaming/extract-command-messages.js';
10
+ import { LiveRun } from './live-run.js';
11
+ import { extractFinalizationSnapshot } from '../stream-snapshot.js';
12
+ import { lastOpenAICompaction } from '../conversation/conversation-record-stream.js';
13
+ import { contextCompactionFailureCategory } from '../../providers/openai-responses-model.js';
14
+ import { resolvePromptCacheKey, resolveSessionId } from './session-identity.js';
15
+ /** Session preparation, projection and decisions. The application runtime owns execution. */
16
+ export class SessionTurnAdapter {
17
+ deps;
18
+ #activePolicyRegistry;
19
+ #toolCallMarkers;
20
+ #batchCoordinator;
21
+ #liveRun = null;
22
+ #nextLiveRunId = 0;
23
+ #hookTurnId;
24
+ #compactionFailureNotified = false;
25
+ #activeRecoveryBudget;
26
+ #attempt;
27
+ #turnSignal;
28
+ #input = '';
29
+ #options = {};
30
+ #state;
31
+ #projection;
32
+ #outcome = { kind: 'failed' };
33
+ #emit = () => undefined;
34
+ #pendingDecision;
35
+ #policy;
36
+ #syncPolicyRegistry() {
37
+ const current = this.deps.agentClient?.getApprovalPolicyRegistry?.();
38
+ if (current && current !== this.#activePolicyRegistry) {
39
+ this.#activePolicyRegistry = current;
40
+ this.#batchCoordinator.updatePolicyRegistry(current);
41
+ }
42
+ }
43
+ constructor(deps) {
44
+ this.deps = deps;
45
+ this.#activePolicyRegistry = deps.approvalPolicyRegistry;
46
+ this.#toolCallMarkers = deps.toolCallMarkers ?? new ToolCallMarkerStore();
47
+ this.#batchCoordinator =
48
+ deps.batchCoordinator ??
49
+ new ToolApprovalBatchCoordinator({
50
+ approvalFlow: deps.approvalFlow,
51
+ planApplier: deps.planApplier,
52
+ shellAutoApproval: deps.shellAutoApproval,
53
+ logger: deps.logger,
54
+ sessionId: deps.sessionId,
55
+ sessionAccess: deps.sessionAccess,
56
+ policyRegistry: deps.approvalPolicyRegistry,
57
+ isCurrent: (token) => deps.generationGuard.isCurrent(token),
58
+ hookLifecycle: deps.hookLifecycle,
59
+ hookEvents: deps.hookEvents,
60
+ });
61
+ }
62
+ /**
63
+ * Tell the run loop where this turn begins and ends, so a message steered
64
+ * into it survives the gaps between its streams — the preparation before the
65
+ * first request, and the backoff before a retry restarts one.
66
+ */
67
+ openTurn() {
68
+ this.#compactionFailureNotified = false;
69
+ this.deps.agentClient.openTurn?.();
70
+ }
71
+ closeTurn() {
72
+ this.deps.agentClient.closeTurn?.();
73
+ }
74
+ /**
75
+ * Yields at most one `context_compaction_failed` per failure episode. Returns
76
+ * undefined (and emits nothing) when the same episode already notified, so
77
+ * recovery replays of one request-boundary failure render a single UI message.
78
+ */
79
+ #compactionFailureEvent(category) {
80
+ if (this.#compactionFailureNotified)
81
+ return undefined;
82
+ this.#compactionFailureNotified = true;
83
+ return {
84
+ type: 'context_compaction_failed',
85
+ provider: 'openai',
86
+ sessionId: resolveSessionId(this.deps.sessionId),
87
+ errorCategory: category,
88
+ durationMs: 0,
89
+ };
90
+ }
91
+ steer(items, options) {
92
+ return this.deps.agentClient.steer?.(items, options) ?? Promise.resolve('released');
93
+ }
94
+ /** Drop a still-waiting steer. False when it was already admitted. */
95
+ retractSteer(id) {
96
+ return this.deps.agentClient.retractSteer?.(id) ?? false;
97
+ }
98
+ /** Replace a waiting steer's items in place, keeping its position. */
99
+ editSteer(id, items) {
100
+ return this.deps.agentClient.editSteer?.(id, items) ?? false;
101
+ }
102
+ /** Internal composition seam for correlating physical tool calls to a turn. */
103
+ setHookTurnId(turnId) {
104
+ this.#hookTurnId = turnId;
105
+ }
106
+ async *executeInitial(input, options = {}) {
107
+ if (this.#liveRun)
108
+ throw new Error('A conversation turn is already active');
109
+ this.#syncPolicyRegistry();
110
+ this.#input = input;
111
+ this.#options = options;
112
+ this.#attempt = undefined;
113
+ this.#state = undefined;
114
+ this.#policy = undefined;
115
+ this.#activeRecoveryBudget = undefined;
116
+ this.#outcome = { kind: 'failed' };
117
+ const runId = `${resolveSessionId(this.deps.sessionId)}:live:${++this.#nextLiveRunId}`;
118
+ this.deps.setActivePostExecuteRunId?.(runId);
119
+ this.#liveRun = new LiveRun(runId, this.deps.postExecutePending, async (emit) => {
120
+ this.#emit = emit;
121
+ try {
122
+ await this.deps.agentClient.runTurn({
123
+ prepare: (signal) => this.#prepare(signal),
124
+ beginSegment: () => this.deps.record.beginSegment(),
125
+ acceptEvent: (event) => this.#acceptEvent(event),
126
+ acceptToolDispatch: (callId) => this.deps.record.markDispatched(callId),
127
+ acceptSegment: (stream) => this.#acceptSegment(stream),
128
+ consume: (stream) => this.#consume(stream),
129
+ interrupted: (stream) => this.#resolveInteraction(stream),
130
+ completed: (stream) => this.#completed(stream),
131
+ recover: (error) => this.#recover(error),
132
+ });
133
+ return this.#outcome;
134
+ }
135
+ catch (error) {
136
+ const failure = contextCompactionFailureCategory(error);
137
+ if (failure) {
138
+ const event = this.#compactionFailureEvent(failure);
139
+ if (event)
140
+ emit(event);
141
+ }
142
+ throw error;
143
+ }
144
+ finally {
145
+ this.#attempt?.close();
146
+ this.deps.setActivePostExecuteRunId?.(null);
147
+ }
148
+ });
149
+ return yield* this.#observe();
150
+ }
151
+ /** An answer resolves the owned wait; it never starts a continuation run. */
152
+ async *executeContinuation(init, policy) {
153
+ const pending = this.#pendingDecision;
154
+ if (!pending)
155
+ throw new Error('No conversation decision is waiting');
156
+ const address = init.kind === 'approval_decision' ? init.decisionAddress : init.abortedContext.interruption;
157
+ if (address !== pending.address ||
158
+ init.generation !== pending.generation ||
159
+ !this.deps.generationGuard.isCurrent(pending.generation)) {
160
+ throw new Error('Conversation decision is stale');
161
+ }
162
+ this.#pendingDecision = undefined;
163
+ this.#liveRun?.resume();
164
+ pending.resolve({ init, policy });
165
+ return yield* this.#observe();
166
+ }
167
+ async *continuePostExecute() {
168
+ return yield* this.#observe();
169
+ }
170
+ async *#observe() {
171
+ const live = this.#liveRun;
172
+ if (!live)
173
+ throw new Error('No active conversation turn');
174
+ let waiting = false;
175
+ let completed = false;
176
+ try {
177
+ while (true) {
178
+ const next = await live.next();
179
+ if (next.kind === 'event') {
180
+ yield next.event;
181
+ continue;
182
+ }
183
+ if (next.kind === 'approval_required') {
184
+ waiting = true;
185
+ this.#compactionFailureNotified = false;
186
+ return { kind: 'approval_required', terminal: next.terminal };
187
+ }
188
+ if (next.kind === 'post_execute_approval_required') {
189
+ waiting = true;
190
+ return { kind: 'approval_required', terminal: this.#postExecuteApprovalTerminal(next.entries) };
191
+ }
192
+ if (next.kind === 'cancelled')
193
+ return { kind: 'stale' };
194
+ this.#compactionFailureNotified = false;
195
+ completed = true;
196
+ return next.result;
197
+ }
198
+ }
199
+ finally {
200
+ if (!waiting && this.#liveRun === live) {
201
+ if (!completed)
202
+ await this.abortLiveRun();
203
+ else {
204
+ this.#liveRun = null;
205
+ await live.completion.catch(() => undefined);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ async #project(events, onEvent) {
211
+ let next = await events.next();
212
+ while (!next.done) {
213
+ onEvent?.(next.value);
214
+ this.#emit(next.value);
215
+ next = await events.next();
216
+ }
217
+ return next.value;
218
+ }
219
+ async #prepare(signal) {
220
+ this.#turnSignal = signal;
221
+ if (!this.#attempt) {
222
+ const created = this.#input instanceof TurnAttempt
223
+ ? { kind: 'created', attempt: this.#input }
224
+ : this.deps.attemptFactory.create(this.#input, {
225
+ ...this.#options,
226
+ signal: this.#options.signal ? AbortSignal.any([signal, this.#options.signal]) : signal,
227
+ recoveryBudget: this.#activeRecoveryBudget ?? this.#options.recoveryBudget,
228
+ });
229
+ if (created.kind === 'stale') {
230
+ this.#outcome = { kind: 'stale' };
231
+ return;
232
+ }
233
+ this.#attempt = created.attempt;
234
+ this.#activeRecoveryBudget = this.#attempt.recoveryBudget;
235
+ const counts = this.#attempt.retryCounts;
236
+ if (this.#options.replayFromHistory ||
237
+ !this.#options.skipUserMessage ||
238
+ counts.modelRetryCount > 0 ||
239
+ counts.serviceTierFallbackCount > 0 ||
240
+ counts.transientRetryCount > 0 ||
241
+ counts.transportDowngradeCount > 0) {
242
+ this.deps.turnAccumulator.resetPersistedTurnState();
243
+ }
244
+ this.deps.record.beginTurn();
245
+ }
246
+ const attempt = this.#attempt;
247
+ this.#state ??= new TurnProjectionState(attempt.token, attempt.recoveryBudget);
248
+ this.#state.resetForAttempt(attempt.inputMode ?? 'delta');
249
+ this.#policy = undefined;
250
+ this.#projection = undefined;
251
+ if (this.#options.delayMs)
252
+ await this.#waitBeforeRetry(attempt, this.#options.delayMs);
253
+ if (!this.deps.generationGuard.isCurrent(attempt.token)) {
254
+ this.#outcome = { kind: 'stale' };
255
+ return;
256
+ }
257
+ if (this.#options.useStandardServiceTier)
258
+ this.deps.agentClient.useStandardServiceTierForNextRequest?.();
259
+ const preparation = this.deps.inputPreparer.prepare(attempt, this.#options.skipUserMessage ?? false, this.#options);
260
+ if (preparation.kind === 'blocked') {
261
+ this.#emit(preparation.event);
262
+ this.#outcome = { kind: 'failed' };
263
+ return;
264
+ }
265
+ this.#state.inputMode = attempt.inputMode;
266
+ return {
267
+ input: attempt.streamInput,
268
+ options: this.#initialOptions(attempt),
269
+ };
270
+ }
271
+ #initialOptions(attempt) {
272
+ const sessionId = resolveSessionId(this.deps.sessionId);
273
+ const promptCacheKey = resolvePromptCacheKey(this.deps.sessionId);
274
+ const options = {
275
+ recoveryBudget: attempt.recoveryBudget,
276
+ sessionId,
277
+ ...(promptCacheKey !== sessionId ? { promptCacheKey } : {}),
278
+ providerHistorySnapshot: attempt.providerHistorySnapshot,
279
+ hookTurnId: this.#hookTurnId,
280
+ ...(this.#options.disableChainingForAttempt ? { disableChainingForAttempt: true } : {}),
281
+ };
282
+ return this.deps.providerContinuity.prepareRequest(options, {
283
+ kind: 'initial',
284
+ inputMode: attempt.inputMode,
285
+ disableChaining: this.#options.disableChainingForAttempt,
286
+ });
287
+ }
288
+ #acceptEvent(event) {
289
+ const state = this.#state;
290
+ if (!state || !this.deps.generationGuard.isCurrent(state.token))
291
+ return;
292
+ if (event.type === 'text_delta')
293
+ this.deps.record.recordTextDelta(event.text);
294
+ if (event.type === 'reasoning_delta')
295
+ this.deps.record.recordReasoningDelta(event.text);
296
+ if (event.type === 'model_attempt_rollback')
297
+ this.deps.record.rollbackReasoning(event.reasoningCharacters);
298
+ if (event.type !== 'item')
299
+ return;
300
+ this.deps.record.acceptObservedItem(event.item);
301
+ if (state.source === 'startStream')
302
+ this.#attempt?.markModelEventSeen();
303
+ }
304
+ #acceptSegment(stream) {
305
+ const state = this.#state;
306
+ if (!this.deps.generationGuard.isCurrent(state.token))
307
+ return;
308
+ this.deps.record.acceptSegment(stream, state.inputMode, {
309
+ logger: this.deps.logger,
310
+ sessionId: this.deps.sessionId,
311
+ });
312
+ }
313
+ /** Presentation only: no history acceptance or execution decision is produced here. */
314
+ async #consume(stream) {
315
+ const attempt = this.#attempt;
316
+ attempt.attachStream(stream);
317
+ const state = this.#state;
318
+ await this.#project(this.#projectStream(state, stream));
319
+ }
320
+ async #completed(stream) {
321
+ const state = this.#state;
322
+ if (!this.deps.generationGuard.isCurrent(state.token)) {
323
+ this.#outcome = { kind: 'stale' };
324
+ return;
325
+ }
326
+ const outcome = await this.#buildResult(stream);
327
+ if (outcome.kind !== 'response')
328
+ throw new Error('Completed turn still requires a decision');
329
+ this.#recordSuccess(state);
330
+ this.#outcome = { kind: 'response', terminal: outcome.result };
331
+ }
332
+ async #waitForApproval(terminal) {
333
+ if (this.#pendingDecision)
334
+ throw new Error('A decision is already waiting');
335
+ const signal = this.#turnSignal;
336
+ return await new Promise((resolve, reject) => {
337
+ const abort = () => {
338
+ this.#pendingDecision = undefined;
339
+ reject(Object.assign(new Error('Operation aborted'), { name: 'AbortError' }));
340
+ };
341
+ this.#pendingDecision = {
342
+ address: this.deps.approvalFlow.getPendingInterruption(),
343
+ generation: this.#state.token,
344
+ resolve: (value) => {
345
+ signal?.removeEventListener('abort', abort);
346
+ resolve(value);
347
+ },
348
+ reject: (error) => {
349
+ signal?.removeEventListener('abort', abort);
350
+ reject(error);
351
+ },
352
+ };
353
+ signal?.addEventListener('abort', abort, { once: true });
354
+ if (signal?.aborted) {
355
+ abort();
356
+ return;
357
+ }
358
+ this.#liveRun.pause(terminal);
359
+ });
360
+ }
361
+ async #applyDecision(init, policy) {
362
+ const prepared = this.deps.planApplier.prepareInit(init);
363
+ const state = this.#state;
364
+ state.bindDecision(prepared);
365
+ this.#policy = policy ?? new ShellAutoApprovalDecisionPolicy(this.deps.shellAutoApproval);
366
+ await this.#project(this.deps.planApplier.applyInitialSetup(prepared));
367
+ }
368
+ async #resolveInteraction(stream) {
369
+ const outcome = await this.#buildResult(stream);
370
+ if (this.#state.source === 'startStream')
371
+ this.#recordSuccess(this.#state);
372
+ if (!outcome || outcome.kind === 'response')
373
+ throw new Error('Expected an interrupted stream');
374
+ let init;
375
+ const state = this.#state;
376
+ if (state.source === 'startStream') {
377
+ if (outcome.kind === 'auto_approve') {
378
+ if (outcome.advisory?.source === 'llm')
379
+ this.#toolCallMarkers.markToolCallAsLlmAutoApproved(outcome.callId);
380
+ init = this.deps.approvalFlow.buildApprovalDecision('y');
381
+ await this.#applyDecision(init);
382
+ }
383
+ else {
384
+ this.deps.planApplier.recordPendingApproval(outcome.result.approval);
385
+ await this.#emitApprovalRequested(outcome.result.approval);
386
+ const answer = await this.#waitForApproval(outcome.result);
387
+ init = answer.init;
388
+ await this.#applyDecision(init, answer.policy);
389
+ }
390
+ }
391
+ else {
392
+ const result = await this.#handleApprovalOutcome(outcome, state, this.#policy, state.cumulativeUsage);
393
+ if (result.action === 'loop') {
394
+ await this.#project(this.deps.planApplier.applyNextPlan(result.nextPlan, state, result.isApproved));
395
+ }
396
+ else {
397
+ if (result.result.kind !== 'approval_required')
398
+ throw Object.assign(new Error('Operation aborted'), { name: 'AbortError' });
399
+ const answer = await this.#waitForApproval(result.result.terminal);
400
+ init = answer.init;
401
+ await this.#applyDecision(init, answer.policy);
402
+ }
403
+ }
404
+ const stopAfterResolution = await this.#resolveBatch(init?.kind === 'approval_decision' && init.stopAfterApprovalResolution === true);
405
+ return this.#continuationOptions(stopAfterResolution);
406
+ }
407
+ async #resolveBatch(stopAfterResolution) {
408
+ const result = await this.#project(this.#stagePendingParallelApprovals(this.#state, this.#policy));
409
+ if (result.kind === 'stale')
410
+ throw Object.assign(new Error('Operation aborted'), { name: 'AbortError' });
411
+ if (result.kind === 'approval_required') {
412
+ const answer = await this.#waitForApproval(result.terminal);
413
+ await this.#applyDecision(answer.init, answer.policy);
414
+ return this.#resolveBatch(stopAfterResolution ||
415
+ (answer.init.kind === 'approval_decision' && answer.init.stopAfterApprovalResolution === true));
416
+ }
417
+ return stopAfterResolution;
418
+ }
419
+ #continuationOptions(stopAfterApprovalResolution) {
420
+ const state = this.#state;
421
+ const sessionId = resolveSessionId(this.deps.sessionId);
422
+ const promptCacheKey = resolvePromptCacheKey(this.deps.sessionId);
423
+ const options = {
424
+ recoveryBudget: state.recoveryBudget,
425
+ sessionId,
426
+ ...(promptCacheKey !== sessionId ? { promptCacheKey } : {}),
427
+ providerHistorySnapshot: this.deps.record.getProviderHistorySnapshot(),
428
+ hookTurnId: this.#hookTurnId,
429
+ ...(stopAfterApprovalResolution ? { stopAfterApprovalResolution: true } : {}),
430
+ };
431
+ return this.deps.providerContinuity.prepareRequest(options, { kind: 'continuation' });
432
+ }
433
+ async #recover(error) {
434
+ if (isClassifiedCancellation(error))
435
+ throw error;
436
+ if (!this.#state || this.#state.source === 'startStream') {
437
+ const handled = await this.#project(this.deps.recoveryHandler.handle({ error, attempt: this.#attempt, stream: this.#attempt?.stream ?? null }));
438
+ if (handled.kind === 'run') {
439
+ this.#options = {
440
+ ...this.#options,
441
+ ...handled.instruction,
442
+ delayMs: handled.delayMs,
443
+ useStandardServiceTier: handled.useStandardServiceTier,
444
+ disableChainingForAttempt: handled.instruction.disableChainingForAttempt === true,
445
+ };
446
+ return true;
447
+ }
448
+ if (handled.kind === 'stale') {
449
+ this.#outcome = { kind: 'stale' };
450
+ return false;
451
+ }
452
+ throw error;
453
+ }
454
+ const recovered = await this.#project(this.deps.continuationRecoveryHandler.handle({ error, state: this.#state }));
455
+ if (recovered.kind === 'stale') {
456
+ this.#outcome = { kind: 'stale' };
457
+ return false;
458
+ }
459
+ if (recovered.kind === 'terminated')
460
+ throw error;
461
+ const token = this.#state.token;
462
+ this.#attempt?.close();
463
+ this.#attempt = undefined;
464
+ this.#input = { text: '' };
465
+ this.#options = {
466
+ skipUserMessage: true,
467
+ token,
468
+ retries: recovered.retryCounts,
469
+ delayMs: recovered.delayMs,
470
+ useStandardServiceTier: recovered.useStandardServiceTier,
471
+ disableChainingForAttempt: recovered.disableChainingForAttempt,
472
+ replayFromHistory: true,
473
+ recoveryBudget: this.#activeRecoveryBudget,
474
+ };
475
+ this.#syncPolicyRegistry();
476
+ return true;
477
+ }
478
+ abortLiveRun() {
479
+ const live = this.#liveRun;
480
+ this.#liveRun = null;
481
+ this.#pendingDecision?.reject(Object.assign(new Error('Operation aborted'), { name: 'AbortError' }));
482
+ this.#pendingDecision = undefined;
483
+ live?.cancel();
484
+ this.deps.agentClient.abort();
485
+ return (live?.completion.then(() => undefined, () => undefined) ?? Promise.resolve());
486
+ }
487
+ get activePostExecuteRunId() {
488
+ return this.#liveRun?.runId ?? null;
489
+ }
490
+ #contextCompactionCompletedEvent(args) {
491
+ const compaction = lastOpenAICompaction(extractFinalizationSnapshot(args.stream).output);
492
+ if (!compaction)
493
+ return [];
494
+ return [
495
+ {
496
+ type: 'context_compaction_completed',
497
+ provider: compaction.provider,
498
+ sessionId: resolveSessionId(this.deps.sessionId),
499
+ ...(args.inputTokensBefore !== undefined ? { inputTokensBefore: args.inputTokensBefore } : {}),
500
+ // The provider reports no post-compaction size: usage.input_tokens is the
501
+ // compaction pass's own input, i.e. the pre-compaction total.
502
+ inputTokensAfter: undefined,
503
+ durationMs: args.durationMs ?? 0,
504
+ },
505
+ ];
506
+ }
507
+ #postExecuteApprovalTerminal(entries) {
508
+ const entry = entries[0];
509
+ const snapshot = this.deps.postExecutePending.snapshot();
510
+ return createApprovalRequiredTerminal({
511
+ agentName: 'Agent',
512
+ toolName: entry.toolName,
513
+ argumentsText: entry.argumentsText,
514
+ deniedRead: entry.deniedRead,
515
+ rawInterruption: { type: 'post_execute', runId: entry.runId, id: entry.id },
516
+ callId: entry.toolCallId,
517
+ postExecute: {
518
+ kind: 'post_execute',
519
+ sessionId: snapshot.sessionId,
520
+ epoch: snapshot.epoch,
521
+ revision: snapshot.revision,
522
+ ids: [entry.id],
523
+ },
524
+ });
525
+ }
526
+ async #waitBeforeRetry(attempt, delayMs) {
527
+ const signal = this.#turnSignal && attempt.signal
528
+ ? AbortSignal.any([this.#turnSignal, attempt.signal])
529
+ : this.#turnSignal ?? attempt.signal;
530
+ if (signal?.aborted)
531
+ throw Object.assign(new Error('Operation aborted'), { name: 'AbortError' });
532
+ if (!signal) {
533
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
534
+ return;
535
+ }
536
+ await new Promise((resolve, reject) => {
537
+ const onAbort = () => {
538
+ clearTimeout(timer);
539
+ signal?.removeEventListener('abort', onAbort);
540
+ reject(Object.assign(new Error('Operation aborted'), { name: 'AbortError' }));
541
+ };
542
+ const timer = setTimeout(() => {
543
+ signal?.removeEventListener('abort', onAbort);
544
+ resolve();
545
+ }, delayMs);
546
+ signal.addEventListener('abort', onAbort, { once: true });
547
+ });
548
+ }
549
+ async *#stagePendingParallelApprovals(state, activePolicy) {
550
+ const result = yield* this.#batchCoordinator.stageBatch({
551
+ state,
552
+ policy: activePolicy,
553
+ token: state.token,
554
+ });
555
+ if (result.kind === 'stale') {
556
+ return { kind: 'stale' };
557
+ }
558
+ return result;
559
+ }
560
+ async #handleApprovalOutcome(outcome, state, activePolicy, nextCumulativeUsage, previousInputForSurge) {
561
+ const { kind } = outcome;
562
+ if (kind === 'auto_approve') {
563
+ if (outcome.advisory?.source === 'llm') {
564
+ this.#toolCallMarkers.markToolCallAsLlmAutoApproved(outcome.callId);
565
+ }
566
+ this.deps.logger.debug('Shell command auto-approved by LLM', {
567
+ eventType: 'approval.auto_approved',
568
+ category: 'approval',
569
+ phase: 'approval',
570
+ sessionId: resolveSessionId(this.deps.sessionId),
571
+ traceId: this.deps.logger.getCorrelationId(),
572
+ callId: outcome.callId,
573
+ command: outcome.argumentsText,
574
+ model: outcome.advisory?.model,
575
+ reasoning: outcome.advisory?.reasoning,
576
+ });
577
+ const nextPlan = this.deps.approvalFlow.prepareContinuation('y', undefined, 'policy');
578
+ if (!nextPlan) {
579
+ const approvalFallback = this.#createApprovalRequiredFromAutoApprove(outcome, nextCumulativeUsage);
580
+ return { action: 'return', result: { kind: 'approval_required', terminal: approvalFallback } };
581
+ }
582
+ return { action: 'loop', nextPlan, isApproved: true };
583
+ }
584
+ const approvalContext = {
585
+ toolName: outcome.result.approval.toolName,
586
+ argumentsText: outcome.result.approval.argumentsText,
587
+ callId: outcome.result.approval.callId,
588
+ llmAdvisory: outcome.result.approval.llmAdvisory,
589
+ };
590
+ const decision = await activePolicy.decide(approvalContext);
591
+ if (decision === 'prompt') {
592
+ this.deps.planApplier.recordPendingApproval(approvalContext);
593
+ if (outcome.result.approval.callId) {
594
+ this.deps.logger.debug('Tool approval required', {
595
+ eventType: 'approval.required',
596
+ category: 'approval',
597
+ phase: 'approval',
598
+ sessionId: resolveSessionId(this.deps.sessionId),
599
+ traceId: this.deps.logger.getCorrelationId(),
600
+ toolName: outcome.result.approval.toolName,
601
+ });
602
+ }
603
+ this.#recordSuccess(state, previousInputForSurge);
604
+ const resultWithUsage = {
605
+ ...outcome.result,
606
+ ...(nextCumulativeUsage && Object.keys(nextCumulativeUsage).length > 0 ? { usage: nextCumulativeUsage } : {}),
607
+ };
608
+ return { action: 'return', result: { kind: 'approval_required', terminal: resultWithUsage } };
609
+ }
610
+ if (decision === 'approve') {
611
+ this.deps.logger.debug('Shell command auto-approved by policy', {
612
+ eventType: 'approval.auto_approved',
613
+ category: 'approval',
614
+ phase: 'approval',
615
+ sessionId: resolveSessionId(this.deps.sessionId),
616
+ traceId: this.deps.logger.getCorrelationId(),
617
+ callId: approvalContext.callId,
618
+ command: approvalContext.argumentsText,
619
+ });
620
+ }
621
+ const answer = decision === 'approve' ? 'y' : 'n';
622
+ const nextPlan = this.deps.approvalFlow.prepareContinuation(answer, undefined, decision === 'approve' || decision === 'reject' ? 'policy' : 'user');
623
+ if (!nextPlan) {
624
+ return { action: 'return', result: { kind: 'approval_required', terminal: outcome.result } };
625
+ }
626
+ return { action: 'loop', nextPlan, isApproved: answer === 'y' };
627
+ }
628
+ /** Every stream segment projects through this path into the same turn state. */
629
+ async *#projectStream(state, stream) {
630
+ state.setLastStream(stream);
631
+ const previousIds = state.previouslyEmittedIds;
632
+ const acc = yield* this.deps.streamProcessor.process(stream, {
633
+ gen: state.token,
634
+ source: state.source,
635
+ preserveExistingToolArgs: state.source !== 'startStream',
636
+ previouslyEmittedCommandIds: previousIds,
637
+ });
638
+ if (!this.deps.generationGuard.isCurrent(state.token))
639
+ return;
640
+ yield* this.#contextCompactionCompletedEvent({
641
+ stream,
642
+ inputTokensBefore: acc.latestUsage?.prompt_tokens,
643
+ durationMs: acc.lastContextCompactionDurationMs,
644
+ });
645
+ state.previouslyEmittedIds = new Set([...previousIds, ...acc.emittedCommandIds]);
646
+ state.setCumulativeUsage(acc.latestUsage ?? state.cumulativeUsage ?? extractUsage(stream));
647
+ this.#projection = acc;
648
+ }
649
+ /** Called by the execution owner at a decision/completion boundary, never by projection. */
650
+ async #buildResult(stream) {
651
+ const state = this.#state;
652
+ const acc = this.#projection;
653
+ if (state.source === 'startStream') {
654
+ this.#attempt.advanceRetry({ ...this.#attempt.retryCounts, transientRetryCount: 0 });
655
+ }
656
+ else
657
+ state.setRetryCounts({ ...state.retryCounts, transientRetryCount: 0 });
658
+ return await buildConversationResult({
659
+ result: stream,
660
+ finalOutputOverride: acc?.finalOutput || undefined,
661
+ reasoningOutputOverride: acc?.reasoningOutput || undefined,
662
+ emittedCommandIds: state.previouslyEmittedIds,
663
+ usage: acc?.latestUsage,
664
+ toolCallArgumentsById: this.deps.toolTracker.snapshotArguments(),
665
+ turnItems: this.deps.turnAccumulator.getTurnItems(),
666
+ token: state.token,
667
+ inputMode: state.inputMode,
668
+ }, {
669
+ approvalFlow: this.deps.approvalFlow,
670
+ shellAutoApproval: this.deps.shellAutoApproval,
671
+ logger: this.deps.logger,
672
+ sessionId: resolveSessionId(this.deps.sessionId),
673
+ sessionAccess: this.deps.sessionAccess,
674
+ approvalPolicyRegistry: this.#activePolicyRegistry,
675
+ toolCallMarkers: this.#toolCallMarkers,
676
+ });
677
+ }
678
+ #recordSuccess(state, previousInputForSurge) {
679
+ const previousInput = state.source === 'startStream' ? this.#attempt.streamInput : previousInputForSurge;
680
+ const input = state.source === 'startStream' ? this.#attempt.streamInput : state.lastStream;
681
+ this.deps.inputPlanner.recordSuccess(state.inputMode === 'delta' ? input : this.deps.record.getHistory(), state.inputMode === 'delta' ? { kind: state.inputMode } : { kind: state.inputMode, previousInput });
682
+ }
683
+ #createApprovalRequiredFromAutoApprove(outcome, usage) {
684
+ const pending = this.deps.approvalFlow.getPending();
685
+ if (pending) {
686
+ const { toolName, argumentsText } = getToolInfoFromInterruption(pending.interruption);
687
+ const agent = pending.interruption && typeof pending.interruption === 'object'
688
+ ? pending.interruption.agent
689
+ : undefined;
690
+ const agentName = agent && typeof agent === 'object' ? agent.name : 'Agent';
691
+ const callId = getCallIdFromObject(pending.interruption);
692
+ return createApprovalRequiredTerminal({
693
+ agentName: typeof agentName === 'string' ? agentName : 'Agent',
694
+ toolName: toolName ?? 'Unknown Tool',
695
+ argumentsText,
696
+ rawInterruption: pending.interruption,
697
+ callId: callId ? String(callId) : undefined,
698
+ llmAdvisory: outcome.advisory,
699
+ usage,
700
+ });
701
+ }
702
+ return createApprovalRequiredTerminal({
703
+ agentName: 'Agent',
704
+ toolName: 'Unknown Tool',
705
+ argumentsText: outcome.argumentsText,
706
+ rawInterruption: undefined,
707
+ callId: outcome.callId,
708
+ llmAdvisory: outcome.advisory,
709
+ usage,
710
+ });
711
+ }
712
+ async #emitApprovalRequested(approval) {
713
+ if (!this.deps.hookLifecycle || !this.deps.hookEvents)
714
+ return;
715
+ let normalizedArguments = approval.argumentsText;
716
+ try {
717
+ normalizedArguments = JSON.parse(approval.argumentsText);
718
+ }
719
+ catch {
720
+ // Preserve a bounded opaque representation for malformed model input.
721
+ }
722
+ await this.deps.hookLifecycle.emit(this.deps.hookEvents.create('approval.requested', {
723
+ toolName: approval.toolName,
724
+ normalizedArguments: this.deps.hookEvents.includeToolArguments
725
+ ? normalizedArguments
726
+ : typeof normalizedArguments === 'string'
727
+ ? normalizedArguments.slice(0, 500)
728
+ : JSON.stringify(normalizedArguments).slice(0, 500),
729
+ approvalKind: approval.toolName === 'ask_user' ? 'ask_user' : 'tool',
730
+ proposedDecision: 'approve',
731
+ }, { toolCallId: approval.callId }));
732
+ }
733
+ }
734
+ //# sourceMappingURL=session-turn-adapter.js.map