@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.
- package/dist/build-info.json +2 -2
- package/dist/components/menu/ModelSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/ModelSelectionMenu.js +12 -14
- package/dist/components/menu/ModelSelectionMenu.js.map +1 -1
- package/dist/lib/agent-chat-service.d.ts.map +1 -1
- package/dist/lib/agent-chat-service.js +2 -6
- package/dist/lib/agent-chat-service.js.map +1 -1
- package/dist/lib/agent-client.d.ts +2 -1
- package/dist/lib/agent-client.d.ts.map +1 -1
- package/dist/lib/agent-client.js +71 -84
- package/dist/lib/agent-client.js.map +1 -1
- package/dist/lib/agent-factory.d.ts.map +1 -1
- package/dist/lib/agent-factory.js +17 -19
- package/dist/lib/agent-factory.js.map +1 -1
- package/dist/prompts/tool-surface-guidance.js +1 -1
- package/dist/prompts/tool-surface-guidance.js.map +1 -1
- package/dist/prompts/tool-surface-guidance.ts +1 -1
- package/dist/providers/ai-sdk-anthropic.provider.d.ts.map +1 -1
- package/dist/providers/ai-sdk-anthropic.provider.js +16 -2
- package/dist/providers/ai-sdk-anthropic.provider.js.map +1 -1
- package/dist/providers/ai-sdk-google.provider.d.ts.map +1 -1
- package/dist/providers/ai-sdk-google.provider.js +16 -2
- package/dist/providers/ai-sdk-google.provider.js.map +1 -1
- package/dist/providers/ai-sdk-openrouter.provider.d.ts.map +1 -1
- package/dist/providers/ai-sdk-openrouter.provider.js +16 -1
- package/dist/providers/ai-sdk-openrouter.provider.js.map +1 -1
- package/dist/providers/ai-sdk-provider-settings.d.ts +2 -0
- package/dist/providers/ai-sdk-provider-settings.d.ts.map +1 -1
- package/dist/providers/ai-sdk-provider-settings.js +5 -0
- package/dist/providers/ai-sdk-provider-settings.js.map +1 -1
- package/dist/providers/ai-sdk-streamed-model.d.ts +3 -3
- package/dist/providers/ai-sdk-streamed-model.d.ts.map +1 -1
- package/dist/providers/ai-sdk-streamed-model.js +5 -69
- package/dist/providers/ai-sdk-streamed-model.js.map +1 -1
- package/dist/providers/anthropic-chat-caching.d.ts +5 -0
- package/dist/providers/anthropic-chat-caching.d.ts.map +1 -0
- package/dist/providers/{common/openai-compatible-messages.js → anthropic-chat-caching.js} +19 -1
- package/dist/providers/anthropic-chat-caching.js.map +1 -0
- package/dist/providers/codex.provider.js +2 -0
- package/dist/providers/codex.provider.js.map +1 -1
- package/dist/providers/custom-provider-runtime.d.ts +18 -0
- package/dist/providers/custom-provider-runtime.d.ts.map +1 -0
- package/dist/providers/custom-provider-runtime.js +26 -0
- package/dist/providers/custom-provider-runtime.js.map +1 -0
- package/dist/providers/llama-cpp.provider.d.ts +3 -0
- package/dist/providers/llama-cpp.provider.d.ts.map +1 -1
- package/dist/providers/llama-cpp.provider.js +17 -0
- package/dist/providers/llama-cpp.provider.js.map +1 -1
- package/dist/providers/neuralwatt.provider.d.ts +6 -0
- package/dist/providers/neuralwatt.provider.d.ts.map +1 -0
- package/dist/providers/neuralwatt.provider.js +8 -0
- package/dist/providers/neuralwatt.provider.js.map +1 -0
- package/dist/providers/openai-compatible-lazy.d.ts.map +1 -1
- package/dist/providers/openai-compatible-lazy.js +2 -0
- package/dist/providers/openai-compatible-lazy.js.map +1 -1
- package/dist/providers/openai-compatible-middleware.d.ts +4 -9
- package/dist/providers/openai-compatible-middleware.d.ts.map +1 -1
- package/dist/providers/openai-compatible-middleware.js +25 -48
- package/dist/providers/openai-compatible-middleware.js.map +1 -1
- package/dist/providers/openai-compatible-response-normalizer.d.ts +1 -1
- package/dist/providers/openai-compatible-response-normalizer.d.ts.map +1 -1
- package/dist/providers/openai-compatible-response-normalizer.js +6 -6
- package/dist/providers/openai-compatible-response-normalizer.js.map +1 -1
- package/dist/providers/openai-compatible.provider.d.ts +2 -32
- package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
- package/dist/providers/openai-compatible.provider.js +18 -144
- package/dist/providers/openai-compatible.provider.js.map +1 -1
- package/dist/providers/openai.provider.d.ts.map +1 -1
- package/dist/providers/openai.provider.js +1 -0
- package/dist/providers/openai.provider.js.map +1 -1
- package/dist/providers/opencode-middleware.d.ts +9 -0
- package/dist/providers/opencode-middleware.d.ts.map +1 -0
- package/dist/providers/opencode-middleware.js +15 -0
- package/dist/providers/opencode-middleware.js.map +1 -0
- package/dist/providers/opencode-model-provider.d.ts +19 -0
- package/dist/providers/opencode-model-provider.d.ts.map +1 -0
- package/dist/providers/opencode-model-provider.js +128 -0
- package/dist/providers/opencode-model-provider.js.map +1 -0
- package/dist/providers/opencode.provider.d.ts +2 -0
- package/dist/providers/opencode.provider.d.ts.map +1 -1
- package/dist/providers/opencode.provider.js +4 -0
- package/dist/providers/opencode.provider.js.map +1 -1
- package/dist/providers/openrouter-decision-models.d.ts +7 -0
- package/dist/providers/openrouter-decision-models.d.ts.map +1 -0
- package/dist/providers/openrouter-decision-models.js +31 -0
- package/dist/providers/openrouter-decision-models.js.map +1 -0
- package/dist/providers/openrouter.provider.js +2 -1
- package/dist/providers/openrouter.provider.js.map +1 -1
- package/dist/providers/orcarouter-models.d.ts +12 -0
- package/dist/providers/orcarouter-models.d.ts.map +1 -0
- package/dist/providers/orcarouter-models.js +34 -0
- package/dist/providers/orcarouter-models.js.map +1 -0
- package/dist/providers/registry.d.ts +7 -0
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js.map +1 -1
- package/dist/services/agent-runtime/application-run-loop.d.ts +20 -1
- package/dist/services/agent-runtime/application-run-loop.d.ts.map +1 -1
- package/dist/services/agent-runtime/application-run-loop.js +871 -759
- package/dist/services/agent-runtime/application-run-loop.js.map +1 -1
- package/dist/services/agent-runtime/tool-call-execution.d.ts +1 -1
- package/dist/services/agent-runtime/tool-call-execution.d.ts.map +1 -1
- package/dist/services/agent-runtime/tool-call-execution.js +6 -3
- package/dist/services/agent-runtime/tool-call-execution.js.map +1 -1
- package/dist/services/approval/approval-flow-coordinator.d.ts +4 -2
- package/dist/services/approval/approval-flow-coordinator.d.ts.map +1 -1
- package/dist/services/approval/approval-flow-coordinator.js +2 -1
- package/dist/services/approval/approval-flow-coordinator.js.map +1 -1
- package/dist/services/approval/approval-state.d.ts +0 -13
- package/dist/services/approval/approval-state.d.ts.map +1 -1
- package/dist/services/approval/approval-state.js +0 -5
- package/dist/services/approval/approval-state.js.map +1 -1
- package/dist/services/approval/background-subagent-approval-controller.d.ts.map +1 -1
- package/dist/services/approval/background-subagent-approval-controller.js +0 -2
- package/dist/services/approval/background-subagent-approval-controller.js.map +1 -1
- package/dist/services/approval/nested-approval-owner.d.ts.map +1 -1
- package/dist/services/approval/nested-approval-owner.js +5 -1
- package/dist/services/approval/nested-approval-owner.js.map +1 -1
- package/dist/services/approval/shell-auto-approval-resolver.d.ts +2 -2
- package/dist/services/approval/shell-auto-approval-resolver.d.ts.map +1 -1
- package/dist/services/approval/shell-auto-approval-resolver.js +2 -2
- package/dist/services/approval/shell-auto-approval-resolver.js.map +1 -1
- package/dist/services/approval/tool-approval-batch-coordinator.d.ts +2 -2
- package/dist/services/approval/tool-approval-batch-coordinator.d.ts.map +1 -1
- package/dist/services/approval/tool-approval-batch-coordinator.js +4 -4
- package/dist/services/approval/tool-approval-batch-coordinator.js.map +1 -1
- package/dist/services/conversation/conversation-adapter.d.ts +17 -47
- package/dist/services/conversation/conversation-adapter.d.ts.map +1 -1
- package/dist/services/conversation/conversation-adapter.js +114 -88
- package/dist/services/conversation/conversation-adapter.js.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.d.ts.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.js +48 -121
- package/dist/services/conversation/conversation-orchestrator.js.map +1 -1
- package/dist/services/conversation/conversation-record-stream.d.ts +21 -0
- package/dist/services/conversation/conversation-record-stream.d.ts.map +1 -0
- package/dist/services/conversation/conversation-record-stream.js +186 -0
- package/dist/services/conversation/conversation-record-stream.js.map +1 -0
- package/dist/services/conversation/conversation-record.d.ts +160 -0
- package/dist/services/conversation/conversation-record.d.ts.map +1 -0
- package/dist/services/conversation/conversation-record.js +371 -0
- package/dist/services/conversation/conversation-record.js.map +1 -0
- package/dist/services/conversation/conversation-result-builder.d.ts +0 -4
- package/dist/services/conversation/conversation-result-builder.d.ts.map +1 -1
- package/dist/services/conversation/conversation-result-builder.js +0 -10
- package/dist/services/conversation/conversation-result-builder.js.map +1 -1
- package/dist/services/conversation/conversation-service.d.ts +2 -1
- package/dist/services/conversation/conversation-service.d.ts.map +1 -1
- package/dist/services/conversation/conversation-service.js +3 -0
- package/dist/services/conversation/conversation-service.js.map +1 -1
- package/dist/services/conversation-agent-client.d.ts +17 -7
- package/dist/services/conversation-agent-client.d.ts.map +1 -1
- package/dist/services/logging/conversation-logger.d.ts +1 -7
- package/dist/services/logging/conversation-logger.d.ts.map +1 -1
- package/dist/services/logging/conversation-logger.js +0 -18
- package/dist/services/logging/conversation-logger.js.map +1 -1
- package/dist/services/model-service.d.ts +2 -6
- package/dist/services/model-service.d.ts.map +1 -1
- package/dist/services/model-service.js +8 -21
- package/dist/services/model-service.js.map +1 -1
- package/dist/services/models/decision-model-listing.d.ts +2 -12
- package/dist/services/models/decision-model-listing.d.ts.map +1 -1
- package/dist/services/models/decision-model-listing.js +2 -29
- package/dist/services/models/decision-model-listing.js.map +1 -1
- package/dist/services/provider-continuity.d.ts +26 -0
- package/dist/services/provider-continuity.d.ts.map +1 -1
- package/dist/services/provider-continuity.js +47 -0
- package/dist/services/provider-continuity.js.map +1 -1
- package/dist/services/retry/recovery-executor.d.ts +3 -4
- package/dist/services/retry/recovery-executor.d.ts.map +1 -1
- package/dist/services/retry/recovery-executor.js +13 -75
- package/dist/services/retry/recovery-executor.js.map +1 -1
- package/dist/services/retry/retry-contracts.d.ts +2 -10
- package/dist/services/retry/retry-contracts.d.ts.map +1 -1
- package/dist/services/session/continuation-plan-applier.d.ts +5 -9
- package/dist/services/session/continuation-plan-applier.d.ts.map +1 -1
- package/dist/services/session/continuation-plan-applier.js +9 -43
- package/dist/services/session/continuation-plan-applier.js.map +1 -1
- package/dist/services/session/continuation-recovery-handler.d.ts +4 -6
- package/dist/services/session/continuation-recovery-handler.d.ts.map +1 -1
- package/dist/services/session/continuation-recovery-handler.js +3 -15
- package/dist/services/session/continuation-recovery-handler.js.map +1 -1
- package/dist/services/session/initial-input-preparer.d.ts +2 -2
- package/dist/services/session/initial-input-preparer.d.ts.map +1 -1
- package/dist/services/session/initial-input-preparer.js +3 -3
- package/dist/services/session/initial-input-preparer.js.map +1 -1
- package/dist/services/session/initial-turn-recovery-handler.d.ts +2 -4
- package/dist/services/session/initial-turn-recovery-handler.d.ts.map +1 -1
- package/dist/services/session/initial-turn-recovery-handler.js +4 -5
- package/dist/services/session/initial-turn-recovery-handler.js.map +1 -1
- package/dist/services/session/live-run.d.ts +9 -3
- package/dist/services/session/live-run.d.ts.map +1 -1
- package/dist/services/session/live-run.js +19 -4
- package/dist/services/session/live-run.js.map +1 -1
- package/dist/services/session/session-composition.d.ts +8 -12
- package/dist/services/session/session-composition.d.ts.map +1 -1
- package/dist/services/session/session-composition.js +41 -87
- package/dist/services/session/session-composition.js.map +1 -1
- package/dist/services/session/session-input-planner.d.ts +2 -2
- package/dist/services/session/session-input-planner.d.ts.map +1 -1
- package/dist/services/session/session-input-planner.js +6 -13
- package/dist/services/session/session-input-planner.js.map +1 -1
- package/dist/services/session/session-lifecycle.d.ts +73 -23
- package/dist/services/session/session-lifecycle.d.ts.map +1 -1
- package/dist/services/session/session-lifecycle.js +127 -45
- package/dist/services/session/session-lifecycle.js.map +1 -1
- package/dist/services/session/session-stream-processor.d.ts +4 -30
- package/dist/services/session/session-stream-processor.d.ts.map +1 -1
- package/dist/services/session/session-stream-processor.js +11 -300
- package/dist/services/session/session-stream-processor.js.map +1 -1
- package/dist/services/session/session-tool-tracker.d.ts +9 -144
- package/dist/services/session/session-tool-tracker.d.ts.map +1 -1
- package/dist/services/session/session-tool-tracker.js +17 -251
- package/dist/services/session/session-tool-tracker.js.map +1 -1
- package/dist/services/session/{turn-workflow.d.ts → session-turn-adapter.d.ts} +10 -43
- package/dist/services/session/session-turn-adapter.d.ts.map +1 -0
- package/dist/services/session/session-turn-adapter.js +734 -0
- package/dist/services/session/session-turn-adapter.js.map +1 -0
- package/dist/services/session/turn-attempt-factory.d.ts +2 -11
- package/dist/services/session/turn-attempt-factory.d.ts.map +1 -1
- package/dist/services/session/turn-attempt-factory.js +4 -12
- package/dist/services/session/turn-attempt-factory.js.map +1 -1
- package/dist/services/session/turn-attempt.d.ts +0 -3
- package/dist/services/session/turn-attempt.d.ts.map +1 -1
- package/dist/services/session/turn-attempt.js +0 -5
- package/dist/services/session/turn-attempt.js.map +1 -1
- package/dist/services/session/turn-coordinator.d.ts +3 -3
- package/dist/services/session/turn-coordinator.d.ts.map +1 -1
- package/dist/services/session/turn-coordinator.js +31 -26
- package/dist/services/session/turn-coordinator.js.map +1 -1
- package/dist/services/session/turn-projection-state.d.ts +45 -0
- package/dist/services/session/turn-projection-state.d.ts.map +1 -0
- package/dist/services/session/turn-projection-state.js +61 -0
- package/dist/services/session/turn-projection-state.js.map +1 -0
- package/dist/services/subagents/tool-policy.d.ts +5 -5
- package/dist/services/subagents/tool-policy.d.ts.map +1 -1
- package/dist/services/subagents/tool-policy.js.map +1 -1
- package/dist/tools/system/run-code/run-code-runtime.d.ts.map +1 -1
- package/dist/tools/system/run-code/run-code-runtime.js +90 -19
- package/dist/tools/system/run-code/run-code-runtime.js.map +1 -1
- package/dist/tools/system/shell.d.ts +2 -2
- package/dist/tools/system/shell.d.ts.map +1 -1
- package/dist/tools/system/shell.js +1 -0
- package/dist/tools/system/shell.js.map +1 -1
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/tools/tool-names.d.ts.map +1 -1
- package/dist/utils/ai/token-usage.js +9 -8
- package/dist/utils/ai/token-usage.js.map +1 -1
- package/package.json +1 -1
- package/dist/providers/anthropic-middleware.d.ts +0 -25
- package/dist/providers/anthropic-middleware.d.ts.map +0 -1
- package/dist/providers/anthropic-middleware.js +0 -32
- package/dist/providers/anthropic-middleware.js.map +0 -1
- package/dist/providers/common/openai-compatible-messages.d.ts +0 -2
- package/dist/providers/common/openai-compatible-messages.d.ts.map +0 -1
- package/dist/providers/common/openai-compatible-messages.js.map +0 -1
- package/dist/services/session/continuation-call-id-resolver.d.ts +0 -29
- package/dist/services/session/continuation-call-id-resolver.d.ts.map +0 -1
- package/dist/services/session/continuation-call-id-resolver.js +0 -69
- package/dist/services/session/continuation-call-id-resolver.js.map +0 -1
- package/dist/services/session/continuation-state.d.ts +0 -69
- package/dist/services/session/continuation-state.d.ts.map +0 -1
- package/dist/services/session/continuation-state.js +0 -78
- package/dist/services/session/continuation-state.js.map +0 -1
- package/dist/services/session/session-manager.d.ts +0 -87
- package/dist/services/session/session-manager.d.ts.map +0 -1
- package/dist/services/session/session-manager.js +0 -146
- package/dist/services/session/session-manager.js.map +0 -1
- package/dist/services/session/turn-workflow.d.ts.map +0 -1
- package/dist/services/session/turn-workflow.js +0 -1055
- package/dist/services/session/turn-workflow.js.map +0 -1
|
@@ -1,1055 +0,0 @@
|
|
|
1
|
-
import { isCommittedOutputEvent } from '../conversation/conversation-events.js';
|
|
2
|
-
import { TurnAttempt } from './turn-attempt.js';
|
|
3
|
-
import { getMethod, getCallIdFromObject, getToolInfoFromInterruption } from '../interruption-info.js';
|
|
4
|
-
import { issueInputSurgeApproval } from '../input-surge-approval.js';
|
|
5
|
-
import { ShellAutoApprovalDecisionPolicy } from '../approval/approval-decision-policy.js';
|
|
6
|
-
import { describeError } from '../../utils/error-helpers.js';
|
|
7
|
-
import { classifyProviderFailure, isClassifiedCancellation } from '../retry/provider-failure-classification.js';
|
|
8
|
-
import { ContinuationState } from './continuation-state.js';
|
|
9
|
-
import { ToolApprovalBatchCoordinator } from '../approval/tool-approval-batch-coordinator.js';
|
|
10
|
-
import { createApprovalRequiredTerminal, buildConversationResult, } from '../conversation/conversation-result-builder.js';
|
|
11
|
-
import { selectAgentStreamItems } from '../agent-stream.js';
|
|
12
|
-
import { extractCommandMessages, ToolCallMarkerStore } from '../../utils/streaming/extract-command-messages.js';
|
|
13
|
-
import { collectKnownToolCallIds, resolveAbortedApprovalCallIds, resolveResponseCycleCallIds, } from './continuation-call-id-resolver.js';
|
|
14
|
-
import { LiveRun } from './live-run.js';
|
|
15
|
-
import { extractFinalizationSnapshot } from '../stream-snapshot.js';
|
|
16
|
-
import { lastOpenAICompaction } from './session-stream-processor.js';
|
|
17
|
-
import { contextCompactionFailureCategory } from '../../providers/openai-responses-model.js';
|
|
18
|
-
import { resolvePromptCacheKey, resolveSessionId } from './session-identity.js';
|
|
19
|
-
export class TurnWorkflow {
|
|
20
|
-
deps;
|
|
21
|
-
/**
|
|
22
|
-
* Graph registry pinned for the active turn. Re-resolved from the client at
|
|
23
|
-
* each fresh-turn boundary so post-rebuild turns follow the rebuilt graph,
|
|
24
|
-
* while in-flight turns and pending-approval continuations keep the graph
|
|
25
|
-
* that produced their interruptions and tools.
|
|
26
|
-
*/
|
|
27
|
-
#activePolicyRegistry;
|
|
28
|
-
#batchCoordinator;
|
|
29
|
-
#toolCallMarkers;
|
|
30
|
-
#liveAttemptOwners = new WeakSet();
|
|
31
|
-
#liveRun = null;
|
|
32
|
-
#nextLiveRunId = 0;
|
|
33
|
-
#hookTurnId;
|
|
34
|
-
/**
|
|
35
|
-
* The recovery envelope of the initial attempt that started the current
|
|
36
|
-
* logical turn, if one is active. Continuation attempts (tool-call
|
|
37
|
-
* follow-ups, and standalone approval-resolution continuations) reuse it so
|
|
38
|
-
* the whole turn shares one 90s/3-attempt/1-replay budget rather than each
|
|
39
|
-
* continuation getting its own unbounded allowance.
|
|
40
|
-
*/
|
|
41
|
-
#activeRecoveryBudget;
|
|
42
|
-
/**
|
|
43
|
-
* Set once `context_compaction_failed` has been yielded for the current failure
|
|
44
|
-
* episode. Recovery replays the same request-boundary failure through both the
|
|
45
|
-
* initial and continuation catch sites, and without this each replay appends
|
|
46
|
-
* another identical "Context compaction failed" system message. Cleared at the
|
|
47
|
-
* next turn boundary (openTurn) and whenever a turn returns an outcome, so a
|
|
48
|
-
* genuinely separate episode still surfaces.
|
|
49
|
-
*/
|
|
50
|
-
#compactionFailureNotified = false;
|
|
51
|
-
/** Pull the client's current graph registry when it has rotated (rebuild). */
|
|
52
|
-
#syncPolicyRegistry() {
|
|
53
|
-
const current = this.deps.agentClient?.getApprovalPolicyRegistry?.();
|
|
54
|
-
if (current && current !== this.#activePolicyRegistry) {
|
|
55
|
-
this.#activePolicyRegistry = current;
|
|
56
|
-
this.#batchCoordinator.updatePolicyRegistry(current);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
constructor(deps) {
|
|
60
|
-
this.deps = deps;
|
|
61
|
-
this.#activePolicyRegistry = deps.approvalPolicyRegistry;
|
|
62
|
-
this.#toolCallMarkers = deps.toolCallMarkers ?? new ToolCallMarkerStore();
|
|
63
|
-
this.#batchCoordinator =
|
|
64
|
-
deps.batchCoordinator ??
|
|
65
|
-
new ToolApprovalBatchCoordinator({
|
|
66
|
-
approvalFlow: deps.approvalFlow,
|
|
67
|
-
planApplier: deps.planApplier,
|
|
68
|
-
shellAutoApproval: deps.shellAutoApproval,
|
|
69
|
-
logger: deps.logger,
|
|
70
|
-
sessionId: deps.sessionId,
|
|
71
|
-
sessionAccess: deps.sessionAccess,
|
|
72
|
-
policyRegistry: deps.approvalPolicyRegistry,
|
|
73
|
-
isCurrent: (token) => deps.generationGuard.isCurrent(token),
|
|
74
|
-
hookLifecycle: deps.hookLifecycle,
|
|
75
|
-
hookEvents: deps.hookEvents,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Tell the run loop where this turn begins and ends, so a message steered
|
|
80
|
-
* into it survives the gaps between its streams — the preparation before the
|
|
81
|
-
* first request, and the backoff before a retry restarts one.
|
|
82
|
-
*/
|
|
83
|
-
openTurn() {
|
|
84
|
-
this.#compactionFailureNotified = false;
|
|
85
|
-
this.deps.agentClient.openTurn?.();
|
|
86
|
-
}
|
|
87
|
-
closeTurn() {
|
|
88
|
-
this.deps.agentClient.closeTurn?.();
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Yields at most one `context_compaction_failed` per failure episode. Returns
|
|
92
|
-
* undefined (and emits nothing) when the same episode already notified, so
|
|
93
|
-
* recovery replays of one request-boundary failure render a single UI message.
|
|
94
|
-
*/
|
|
95
|
-
#compactionFailureEvent(category) {
|
|
96
|
-
if (this.#compactionFailureNotified)
|
|
97
|
-
return undefined;
|
|
98
|
-
this.#compactionFailureNotified = true;
|
|
99
|
-
return {
|
|
100
|
-
type: 'context_compaction_failed',
|
|
101
|
-
provider: 'openai',
|
|
102
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
103
|
-
errorCategory: category,
|
|
104
|
-
durationMs: 0,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
steer(items, options) {
|
|
108
|
-
return this.deps.agentClient.steer?.(items, options) ?? Promise.resolve('released');
|
|
109
|
-
}
|
|
110
|
-
/** Drop a still-waiting steer. False when it was already admitted. */
|
|
111
|
-
retractSteer(id) {
|
|
112
|
-
return this.deps.agentClient.retractSteer?.(id) ?? false;
|
|
113
|
-
}
|
|
114
|
-
/** Replace a waiting steer's items in place, keeping its position. */
|
|
115
|
-
editSteer(id, items) {
|
|
116
|
-
return this.deps.agentClient.editSteer?.(id, items) ?? false;
|
|
117
|
-
}
|
|
118
|
-
/** Internal composition seam for correlating physical tool calls to a turn. */
|
|
119
|
-
setHookTurnId(turnId) {
|
|
120
|
-
this.#hookTurnId = turnId;
|
|
121
|
-
}
|
|
122
|
-
async *executeInitial(attemptOrInput, options = {}) {
|
|
123
|
-
// Fresh-turn boundary: follow a mid-session rebuild. Continuations
|
|
124
|
-
// deliberately do not re-sync — a pending approval resolves against the
|
|
125
|
-
// graph that produced its interruption.
|
|
126
|
-
this.#syncPolicyRegistry();
|
|
127
|
-
try {
|
|
128
|
-
const outcome = yield* this.#executeInitialBody(attemptOrInput, options);
|
|
129
|
-
// A returned outcome ends the failure episode even when the turn itself
|
|
130
|
-
// failed; only a thrown error keeps the episode (and the suppression) alive.
|
|
131
|
-
this.#compactionFailureNotified = false;
|
|
132
|
-
return outcome;
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
const failure = contextCompactionFailureCategory(error);
|
|
136
|
-
if (failure) {
|
|
137
|
-
const event = this.#compactionFailureEvent(failure);
|
|
138
|
-
if (event)
|
|
139
|
-
yield event;
|
|
140
|
-
}
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
async *#executeInitialBody(attemptOrInput, options = {}) {
|
|
145
|
-
let currentInput = attemptOrInput;
|
|
146
|
-
let currentOptions = options;
|
|
147
|
-
while (true) {
|
|
148
|
-
const initialOutcome = yield* this.executeInitialAttempt(currentInput, currentOptions);
|
|
149
|
-
if (initialOutcome.kind !== 'abort_resolution_required' && initialOutcome.kind !== 'auto_approval_required') {
|
|
150
|
-
return initialOutcome;
|
|
151
|
-
}
|
|
152
|
-
const generation = initialOutcome.generation;
|
|
153
|
-
let driveResult;
|
|
154
|
-
if (initialOutcome.kind === 'abort_resolution_required') {
|
|
155
|
-
driveResult = yield* this.executeContinuationAttempt({
|
|
156
|
-
kind: 'abort_resolution',
|
|
157
|
-
abortedContext: initialOutcome.abortedContext,
|
|
158
|
-
userText: initialOutcome.userText,
|
|
159
|
-
generation,
|
|
160
|
-
}, new ShellAutoApprovalDecisionPolicy(this.deps.shellAutoApproval));
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
driveResult = yield* this.executeContinuationAttempt({
|
|
164
|
-
kind: 'approval_decision',
|
|
165
|
-
answer: 'y',
|
|
166
|
-
generation,
|
|
167
|
-
}, new ShellAutoApprovalDecisionPolicy(this.deps.shellAutoApproval));
|
|
168
|
-
}
|
|
169
|
-
if (driveResult.kind !== 'fresh_start_required') {
|
|
170
|
-
return driveResult;
|
|
171
|
-
}
|
|
172
|
-
currentInput = { text: '' };
|
|
173
|
-
currentOptions = {
|
|
174
|
-
skipUserMessage: true,
|
|
175
|
-
retries: driveResult.retryCounts,
|
|
176
|
-
delayMs: driveResult.delayMs,
|
|
177
|
-
useStandardServiceTier: driveResult.useStandardServiceTier,
|
|
178
|
-
disableChainingForAttempt: driveResult.disableChainingForAttempt,
|
|
179
|
-
token: generation,
|
|
180
|
-
replayFromHistory: true,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
async *executeContinuation(init, policy) {
|
|
185
|
-
try {
|
|
186
|
-
const outcome = yield* this.#executeContinuationBodyImpl(init, policy);
|
|
187
|
-
// A returned outcome ends the failure episode (see executeInitial).
|
|
188
|
-
this.#compactionFailureNotified = false;
|
|
189
|
-
return outcome;
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
const failure = contextCompactionFailureCategory(error);
|
|
193
|
-
if (failure) {
|
|
194
|
-
const event = this.#compactionFailureEvent(failure);
|
|
195
|
-
if (event)
|
|
196
|
-
yield event;
|
|
197
|
-
}
|
|
198
|
-
throw error;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
async *#executeContinuationBodyImpl(init, policy) {
|
|
202
|
-
if (this.#liveRun) {
|
|
203
|
-
return yield* this.#continuePostExecuteRun();
|
|
204
|
-
}
|
|
205
|
-
const driveResult = yield* this.executeContinuationAttempt(init, policy);
|
|
206
|
-
if (driveResult.kind !== 'fresh_start_required') {
|
|
207
|
-
return driveResult;
|
|
208
|
-
}
|
|
209
|
-
return yield* this.#replayFromFreshStart(init.generation, driveResult);
|
|
210
|
-
}
|
|
211
|
-
/** Re-observe an already-running post-execute stream without involving SDK approvals. */
|
|
212
|
-
async *continuePostExecute() {
|
|
213
|
-
return yield* this.#continuePostExecuteRun();
|
|
214
|
-
}
|
|
215
|
-
#freshStartReplayOptions(generation, result) {
|
|
216
|
-
return {
|
|
217
|
-
skipUserMessage: true,
|
|
218
|
-
retries: result.retryCounts,
|
|
219
|
-
delayMs: result.delayMs,
|
|
220
|
-
useStandardServiceTier: result.useStandardServiceTier,
|
|
221
|
-
disableChainingForAttempt: result.disableChainingForAttempt,
|
|
222
|
-
token: generation,
|
|
223
|
-
replayFromHistory: true,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
async *#replayFromFreshStart(generation, result) {
|
|
227
|
-
return yield* this.executeInitial({ text: '' }, this.#freshStartReplayOptions(generation, result));
|
|
228
|
-
}
|
|
229
|
-
async *executeInitialAttempt(attemptOrInput, options = {}) {
|
|
230
|
-
let attempt;
|
|
231
|
-
if (attemptOrInput instanceof TurnAttempt) {
|
|
232
|
-
attempt = attemptOrInput;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
const creation = this.deps.attemptFactory.create(attemptOrInput, options);
|
|
236
|
-
if (creation.kind === 'stale') {
|
|
237
|
-
return { kind: 'stale' };
|
|
238
|
-
}
|
|
239
|
-
attempt = creation.attempt;
|
|
240
|
-
}
|
|
241
|
-
// Continuation attempts driven from within this same logical turn (tool
|
|
242
|
-
// approvals, abort resolution) reuse this budget instead of getting their
|
|
243
|
-
// own unbounded one; see #activeRecoveryBudget.
|
|
244
|
-
this.#activeRecoveryBudget = attempt.recoveryBudget;
|
|
245
|
-
let skipUser = options.skipUserMessage ?? false;
|
|
246
|
-
let currentResumeState = options.resumeState;
|
|
247
|
-
let currentResumePreviousResponseId = options.resumePreviousResponseId;
|
|
248
|
-
let currentDisableChainingForAttempt = options.disableChainingForAttempt === true;
|
|
249
|
-
let currentAbortedContext = options.abortedContext ?? null;
|
|
250
|
-
const initialCounts = attempt.retryCounts;
|
|
251
|
-
if (options.replayFromHistory ||
|
|
252
|
-
!skipUser ||
|
|
253
|
-
initialCounts.modelRetryCount > 0 ||
|
|
254
|
-
initialCounts.serviceTierFallbackCount > 0 ||
|
|
255
|
-
initialCounts.transientRetryCount > 0 ||
|
|
256
|
-
initialCounts.transportDowngradeCount > 0) {
|
|
257
|
-
this.deps.turnAccumulator.resetPersistedTurnState();
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
if (options.delayMs && options.delayMs > 0) {
|
|
261
|
-
await this.#waitBeforeRetry(attempt, options.delayMs);
|
|
262
|
-
}
|
|
263
|
-
if (!this.deps.generationGuard.isCurrent(attempt.token)) {
|
|
264
|
-
return { kind: 'stale' };
|
|
265
|
-
}
|
|
266
|
-
if (options.useStandardServiceTier) {
|
|
267
|
-
getMethod(this.deps.agentClient, 'useStandardServiceTierForNextRequest')?.call(this.deps.agentClient);
|
|
268
|
-
}
|
|
269
|
-
this.deps.toolTracker.ledger.beginTurn();
|
|
270
|
-
this.deps.journal.resetForNewTurn();
|
|
271
|
-
while (true) {
|
|
272
|
-
// 1. Check generation token validity
|
|
273
|
-
if (currentAbortedContext) {
|
|
274
|
-
const tokenVal = currentAbortedContext.token ?? 0;
|
|
275
|
-
if (!this.deps.generationGuard.isCurrent(tokenVal)) {
|
|
276
|
-
return { kind: 'stale' };
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
if (!this.deps.generationGuard.isCurrent(attempt.token)) {
|
|
281
|
-
return { kind: 'stale' };
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// 2. Handle aborted-approval resolution
|
|
285
|
-
if (currentAbortedContext) {
|
|
286
|
-
// Preserve the follow-up prompt as a normal user turn so the next
|
|
287
|
-
// turn can steer the conversation naturally after an ESC abort.
|
|
288
|
-
const preparation = this.deps.inputPreparer.prepare(attempt, skipUser, {
|
|
289
|
-
inputSurgeApproval: issueInputSurgeApproval(attempt.submittedTurn),
|
|
290
|
-
replayFromHistory: options.replayFromHistory,
|
|
291
|
-
});
|
|
292
|
-
if (preparation.kind === 'blocked') {
|
|
293
|
-
yield preparation.event;
|
|
294
|
-
return { kind: 'failed' };
|
|
295
|
-
}
|
|
296
|
-
this.deps.logger.debug('Resolving aborted approval with fake execution', {
|
|
297
|
-
message: attempt.turn.text,
|
|
298
|
-
});
|
|
299
|
-
return {
|
|
300
|
-
kind: 'abort_resolution_required',
|
|
301
|
-
abortedContext: currentAbortedContext,
|
|
302
|
-
userText: attempt.turn.text,
|
|
303
|
-
generation: attempt.token,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
const preparation = this.deps.inputPreparer.prepare(attempt, skipUser, options);
|
|
307
|
-
if (preparation.kind === 'blocked') {
|
|
308
|
-
yield preparation.event;
|
|
309
|
-
return { kind: 'failed' };
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
const cycleResult = yield* this.#executeInitialStreamCycle(attempt, {
|
|
313
|
-
resumeState: currentResumeState,
|
|
314
|
-
resumePreviousResponseId: currentResumePreviousResponseId,
|
|
315
|
-
disableChainingForAttempt: currentDisableChainingForAttempt,
|
|
316
|
-
});
|
|
317
|
-
currentDisableChainingForAttempt = false;
|
|
318
|
-
if (cycleResult.kind === 'stale') {
|
|
319
|
-
return { kind: 'stale' };
|
|
320
|
-
}
|
|
321
|
-
if (cycleResult.kind === 'post_execute_approval_required') {
|
|
322
|
-
return { kind: 'approval_required', terminal: this.#postExecuteApprovalTerminal(cycleResult.entries) };
|
|
323
|
-
}
|
|
324
|
-
const { outcome } = cycleResult;
|
|
325
|
-
attempt.advanceRetry({ ...attempt.retryCounts, transientRetryCount: 0 });
|
|
326
|
-
if (outcome.kind === 'response') {
|
|
327
|
-
return { kind: 'response', terminal: outcome.result };
|
|
328
|
-
}
|
|
329
|
-
if (outcome.kind === 'auto_approve') {
|
|
330
|
-
if (outcome.advisory?.source === 'llm') {
|
|
331
|
-
this.#toolCallMarkers.markToolCallAsLlmAutoApproved(outcome.callId);
|
|
332
|
-
}
|
|
333
|
-
this.deps.logger.debug('Shell command auto-approved by LLM', {
|
|
334
|
-
eventType: 'approval.auto_approved',
|
|
335
|
-
category: 'approval',
|
|
336
|
-
phase: 'approval',
|
|
337
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
338
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
339
|
-
callId: outcome.callId,
|
|
340
|
-
command: outcome.argumentsText,
|
|
341
|
-
model: outcome.advisory?.model,
|
|
342
|
-
reasoning: outcome.advisory?.reasoning,
|
|
343
|
-
});
|
|
344
|
-
return {
|
|
345
|
-
kind: 'auto_approval_required',
|
|
346
|
-
generation: attempt.token,
|
|
347
|
-
callId: outcome.callId,
|
|
348
|
-
command: outcome.argumentsText,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
if (outcome.result.approval.callId) {
|
|
352
|
-
this.deps.toolTracker.recordFunctionCall({
|
|
353
|
-
type: 'function_call',
|
|
354
|
-
callId: outcome.result.approval.callId,
|
|
355
|
-
name: outcome.result.approval.toolName,
|
|
356
|
-
arguments: outcome.result.approval.argumentsText,
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
await this.#emitApprovalRequested(outcome.result.approval);
|
|
360
|
-
this.deps.logger.debug('Tool approval required', {
|
|
361
|
-
eventType: 'approval.required',
|
|
362
|
-
category: 'approval',
|
|
363
|
-
phase: 'approval',
|
|
364
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
365
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
366
|
-
toolName: outcome.result.approval.toolName,
|
|
367
|
-
});
|
|
368
|
-
return { kind: 'approval_required', terminal: outcome.result };
|
|
369
|
-
}
|
|
370
|
-
catch (error) {
|
|
371
|
-
const handled = yield* this.deps.recoveryHandler.handle({
|
|
372
|
-
error,
|
|
373
|
-
attempt,
|
|
374
|
-
stream: attempt.stream,
|
|
375
|
-
});
|
|
376
|
-
if (handled.kind === 'run') {
|
|
377
|
-
skipUser = handled.instruction.skipUserMessage;
|
|
378
|
-
currentResumeState = handled.instruction.resumeState;
|
|
379
|
-
currentResumePreviousResponseId = handled.instruction.resumePreviousResponseId;
|
|
380
|
-
currentDisableChainingForAttempt = handled.instruction.disableChainingForAttempt === true;
|
|
381
|
-
currentAbortedContext = null;
|
|
382
|
-
if (handled.delayMs && handled.delayMs > 0) {
|
|
383
|
-
await this.#waitBeforeRetry(attempt, handled.delayMs);
|
|
384
|
-
}
|
|
385
|
-
if (handled.useStandardServiceTier) {
|
|
386
|
-
getMethod(this.deps.agentClient, 'useStandardServiceTierForNextRequest')?.call(this.deps.agentClient);
|
|
387
|
-
}
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
else if (handled.kind === 'stale') {
|
|
391
|
-
return { kind: 'stale' };
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
throw error;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
finally {
|
|
400
|
-
// A post-execute gate hands this attempt to its live consumer. That task
|
|
401
|
-
// owns terminal cleanup; closing here would detach abort handling while
|
|
402
|
-
// the SDK is still waiting at the tool boundary.
|
|
403
|
-
if (!this.#liveAttemptOwners.has(attempt))
|
|
404
|
-
attempt.close();
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
async *#executeInitialStreamCycle(attempt, options) {
|
|
408
|
-
// Establish ownership before asking the client for a stream. Some clients
|
|
409
|
-
// can start consuming synchronously while constructing their stream.
|
|
410
|
-
const runId = `${this.deps.sessionId}:live:${++this.#nextLiveRunId}`;
|
|
411
|
-
this.deps.setActivePostExecuteRunId?.(runId);
|
|
412
|
-
let stream;
|
|
413
|
-
try {
|
|
414
|
-
stream = await this.#startInitialStream(attempt, options);
|
|
415
|
-
}
|
|
416
|
-
catch (error) {
|
|
417
|
-
this.deps.setActivePostExecuteRunId?.(null);
|
|
418
|
-
throw error;
|
|
419
|
-
}
|
|
420
|
-
attempt.attachStream(stream);
|
|
421
|
-
const liveRun = new LiveRun(runId, this.deps.postExecutePending, async (emit) => {
|
|
422
|
-
try {
|
|
423
|
-
return await this.#consumeInitialStream(stream, attempt, emit);
|
|
424
|
-
}
|
|
425
|
-
finally {
|
|
426
|
-
this.#liveAttemptOwners.delete(attempt);
|
|
427
|
-
attempt.close();
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
this.#liveAttemptOwners.add(attempt);
|
|
431
|
-
this.#liveRun = liveRun;
|
|
432
|
-
return yield* this.#drainLiveRun(liveRun);
|
|
433
|
-
}
|
|
434
|
-
async *#drainLiveRun(liveRun) {
|
|
435
|
-
while (true) {
|
|
436
|
-
let state;
|
|
437
|
-
try {
|
|
438
|
-
state = await liveRun.next();
|
|
439
|
-
}
|
|
440
|
-
catch (error) {
|
|
441
|
-
if (this.#liveRun === liveRun) {
|
|
442
|
-
this.#liveRun = null;
|
|
443
|
-
this.deps.setActivePostExecuteRunId?.(null);
|
|
444
|
-
}
|
|
445
|
-
throw error;
|
|
446
|
-
}
|
|
447
|
-
if (state.kind === 'event') {
|
|
448
|
-
yield state.event;
|
|
449
|
-
continue;
|
|
450
|
-
}
|
|
451
|
-
if (state.kind === 'post_execute_approval_required')
|
|
452
|
-
return state;
|
|
453
|
-
if (state.kind === 'cancelled') {
|
|
454
|
-
if (this.#liveRun === liveRun) {
|
|
455
|
-
this.#liveRun = null;
|
|
456
|
-
this.deps.setActivePostExecuteRunId?.(null);
|
|
457
|
-
}
|
|
458
|
-
return { kind: 'stale' };
|
|
459
|
-
}
|
|
460
|
-
if (this.#liveRun === liveRun) {
|
|
461
|
-
this.#liveRun = null;
|
|
462
|
-
this.deps.setActivePostExecuteRunId?.(null);
|
|
463
|
-
}
|
|
464
|
-
return state.result;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
async #consumeInitialStream(stream, attempt, emit) {
|
|
468
|
-
const processor = this.deps.streamProcessor.process(stream, {
|
|
469
|
-
gen: attempt.token,
|
|
470
|
-
source: 'startStream',
|
|
471
|
-
preserveExistingToolArgs: false,
|
|
472
|
-
});
|
|
473
|
-
let next = await processor.next();
|
|
474
|
-
while (!next.done) {
|
|
475
|
-
if (isCommittedOutputEvent(next.value))
|
|
476
|
-
attempt.markModelEventSeen();
|
|
477
|
-
emit(next.value);
|
|
478
|
-
next = await processor.next();
|
|
479
|
-
}
|
|
480
|
-
const accumulated = next.value;
|
|
481
|
-
const finalized = this.deps.streamProcessor.finalize(stream, attempt.token, attempt.inputMode, 'startStream');
|
|
482
|
-
if (finalized.kind === 'stale') {
|
|
483
|
-
return { kind: 'stale' };
|
|
484
|
-
}
|
|
485
|
-
for (const event of this.#contextCompactionCompletedEvent({
|
|
486
|
-
stream,
|
|
487
|
-
inputTokensBefore: accumulated.latestUsage?.prompt_tokens,
|
|
488
|
-
durationMs: accumulated.lastContextCompactionDurationMs,
|
|
489
|
-
})) {
|
|
490
|
-
emit(event);
|
|
491
|
-
}
|
|
492
|
-
const outcome = await buildConversationResult({
|
|
493
|
-
result: stream,
|
|
494
|
-
finalOutputOverride: accumulated.finalOutput || undefined,
|
|
495
|
-
reasoningOutputOverride: accumulated.reasoningOutput || undefined,
|
|
496
|
-
emittedCommandIds: accumulated.emittedCommandIds,
|
|
497
|
-
usage: accumulated.latestUsage,
|
|
498
|
-
toolCallArgumentsById: this.deps.toolTracker.argumentsById,
|
|
499
|
-
turnItems: this.deps.turnAccumulator.getTurnItems(),
|
|
500
|
-
token: attempt.token,
|
|
501
|
-
inputMode: attempt.inputMode,
|
|
502
|
-
}, {
|
|
503
|
-
approvalFlow: this.deps.approvalFlow,
|
|
504
|
-
shellAutoApproval: this.deps.shellAutoApproval,
|
|
505
|
-
logger: this.deps.logger,
|
|
506
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
507
|
-
sessionAccess: this.deps.sessionAccess,
|
|
508
|
-
approvalPolicyRegistry: this.#activePolicyRegistry,
|
|
509
|
-
toolCallMarkers: this.#toolCallMarkers,
|
|
510
|
-
});
|
|
511
|
-
this.deps.inputPlanner.recordSuccess(attempt.inputMode === 'delta' ? attempt.streamInput : this.deps.conversationStore.getHistory(), attempt.inputMode === 'delta'
|
|
512
|
-
? { kind: attempt.inputMode }
|
|
513
|
-
: { kind: attempt.inputMode, previousInput: attempt.streamInput });
|
|
514
|
-
return { kind: 'completed', outcome, generation: attempt.token };
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* The compaction completion notice.
|
|
518
|
-
*
|
|
519
|
-
* `started` is *not* emitted here: it is surfaced live by the stream processor from the
|
|
520
|
-
* provider's own `output_item.added` frame, which is the only moment that corresponds to
|
|
521
|
-
* compaction actually beginning. This site owns only the completion, because the token
|
|
522
|
-
* count it reports comes from `usage`, which the provider does not send until the response
|
|
523
|
-
* completes — after the compaction frames have already gone by.
|
|
524
|
-
*
|
|
525
|
-
* `durationMs` likewise comes from the frames (parked on the accumulator), not from a
|
|
526
|
-
* clock read here: a `Date.now()` captured at this call site is taken after the stream has
|
|
527
|
-
* finished, so it would always yield ~0.
|
|
528
|
-
*/
|
|
529
|
-
#contextCompactionCompletedEvent(args) {
|
|
530
|
-
const compaction = lastOpenAICompaction(extractFinalizationSnapshot(args.stream).output);
|
|
531
|
-
if (!compaction)
|
|
532
|
-
return [];
|
|
533
|
-
return [
|
|
534
|
-
{
|
|
535
|
-
type: 'context_compaction_completed',
|
|
536
|
-
provider: compaction.provider,
|
|
537
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
538
|
-
...(args.inputTokensBefore !== undefined ? { inputTokensBefore: args.inputTokensBefore } : {}),
|
|
539
|
-
// The provider reports no post-compaction size: usage.input_tokens is the
|
|
540
|
-
// compaction pass's own input, i.e. the pre-compaction total.
|
|
541
|
-
inputTokensAfter: undefined,
|
|
542
|
-
durationMs: args.durationMs ?? 0,
|
|
543
|
-
},
|
|
544
|
-
];
|
|
545
|
-
}
|
|
546
|
-
async *#continuePostExecuteRun() {
|
|
547
|
-
const liveRun = this.#liveRun;
|
|
548
|
-
if (!liveRun) {
|
|
549
|
-
throw new Error('No live post-execute run active to continue.');
|
|
550
|
-
}
|
|
551
|
-
try {
|
|
552
|
-
const result = yield* this.#drainLiveRun(liveRun);
|
|
553
|
-
if (result.kind === 'stale')
|
|
554
|
-
return { kind: 'stale' };
|
|
555
|
-
if (result.kind === 'post_execute_approval_required') {
|
|
556
|
-
return { kind: 'approval_required', terminal: this.#postExecuteApprovalTerminal(result.entries) };
|
|
557
|
-
}
|
|
558
|
-
const { outcome, generation } = result;
|
|
559
|
-
if (outcome.kind === 'response')
|
|
560
|
-
return { kind: 'response', terminal: outcome.result };
|
|
561
|
-
if (outcome.kind === 'approval_required')
|
|
562
|
-
return { kind: 'approval_required', terminal: outcome.result };
|
|
563
|
-
if (outcome.kind === 'auto_approve') {
|
|
564
|
-
// Initial-path parity (`turn-workflow.test.ts:685`): the resumed live
|
|
565
|
-
// run ended on a shell interruption the auto-approval policy already
|
|
566
|
-
// approved, so settle exactly as the initial path settles an
|
|
567
|
-
// `auto_approve` stream result — drive the auto-approval continuation
|
|
568
|
-
// instead of re-prompting a decision the policy has made. The
|
|
569
|
-
// generation travels with the live run so the init-less
|
|
570
|
-
// `continuePostExecute` observer path uses the same token.
|
|
571
|
-
if (outcome.advisory?.source === 'llm') {
|
|
572
|
-
this.#toolCallMarkers.markToolCallAsLlmAutoApproved(outcome.callId);
|
|
573
|
-
}
|
|
574
|
-
this.deps.logger.debug('Shell command auto-approved by LLM', {
|
|
575
|
-
eventType: 'approval.auto_approved',
|
|
576
|
-
category: 'approval',
|
|
577
|
-
phase: 'approval',
|
|
578
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
579
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
580
|
-
callId: outcome.callId,
|
|
581
|
-
command: outcome.argumentsText,
|
|
582
|
-
model: outcome.advisory?.model,
|
|
583
|
-
reasoning: outcome.advisory?.reasoning,
|
|
584
|
-
});
|
|
585
|
-
const driveResult = yield* this.executeContinuationAttempt({ kind: 'approval_decision', answer: 'y', generation }, new ShellAutoApprovalDecisionPolicy(this.deps.shellAutoApproval));
|
|
586
|
-
if (driveResult.kind !== 'fresh_start_required') {
|
|
587
|
-
return driveResult;
|
|
588
|
-
}
|
|
589
|
-
return yield* this.#replayFromFreshStart(generation, driveResult);
|
|
590
|
-
}
|
|
591
|
-
throw new Error('Post-execute live run finished without a terminal outcome.');
|
|
592
|
-
}
|
|
593
|
-
catch (error) {
|
|
594
|
-
this.#liveRun = null;
|
|
595
|
-
this.deps.setActivePostExecuteRunId?.(null);
|
|
596
|
-
throw error;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
#postExecuteApprovalTerminal(entries) {
|
|
600
|
-
const entry = entries[0];
|
|
601
|
-
const snapshot = this.deps.postExecutePending.snapshot();
|
|
602
|
-
return createApprovalRequiredTerminal({
|
|
603
|
-
agentName: 'Agent',
|
|
604
|
-
toolName: entry.toolName,
|
|
605
|
-
argumentsText: entry.argumentsText,
|
|
606
|
-
deniedRead: entry.deniedRead,
|
|
607
|
-
rawInterruption: { type: 'post_execute', runId: entry.runId, id: entry.id },
|
|
608
|
-
callId: entry.toolCallId,
|
|
609
|
-
postExecute: {
|
|
610
|
-
kind: 'post_execute',
|
|
611
|
-
sessionId: snapshot.sessionId,
|
|
612
|
-
epoch: snapshot.epoch,
|
|
613
|
-
revision: snapshot.revision,
|
|
614
|
-
ids: [entry.id],
|
|
615
|
-
},
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
async #startInitialStream(attempt, options) {
|
|
619
|
-
if (options.resumeState && typeof this.deps.agentClient.continueRunStream === 'function') {
|
|
620
|
-
const resumeOptions = {
|
|
621
|
-
recoveryBudget: attempt.recoveryBudget,
|
|
622
|
-
previousResponseId: options.resumePreviousResponseId ?? this.deps.providerContinuity.previousResponseId,
|
|
623
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
624
|
-
providerHistorySnapshot: this.deps.conversationStore.getProviderHistorySnapshot(),
|
|
625
|
-
hookTurnId: this.#hookTurnId,
|
|
626
|
-
};
|
|
627
|
-
Object.defineProperty(resumeOptions, 'providerContinuityLineage', {
|
|
628
|
-
value: this.deps.providerContinuity.lineage,
|
|
629
|
-
enumerable: this.deps.providerContinuity.lineage !== 0,
|
|
630
|
-
});
|
|
631
|
-
return (await this.deps.agentClient.continueRunStream(options.resumeState, resumeOptions));
|
|
632
|
-
}
|
|
633
|
-
const legacyPreviousResponseId = attempt.inputMode === 'delta' ? this.deps.providerContinuity.previousResponseId : null;
|
|
634
|
-
const selectedPreviousResponseId = legacyPreviousResponseId;
|
|
635
|
-
const sessionId = resolveSessionId(this.deps.sessionId);
|
|
636
|
-
const promptCacheKey = resolvePromptCacheKey(this.deps.sessionId);
|
|
637
|
-
const startOptions = {
|
|
638
|
-
recoveryBudget: attempt.recoveryBudget,
|
|
639
|
-
previousResponseId: options.disableChainingForAttempt ? undefined : selectedPreviousResponseId,
|
|
640
|
-
sessionId,
|
|
641
|
-
...(promptCacheKey !== sessionId ? { promptCacheKey } : {}),
|
|
642
|
-
providerHistorySnapshot: attempt.providerHistorySnapshot,
|
|
643
|
-
...(this.#hookTurnId ? { hookTurnId: this.#hookTurnId } : {}),
|
|
644
|
-
...(options.disableChainingForAttempt ? { disableChainingForAttempt: true } : {}),
|
|
645
|
-
};
|
|
646
|
-
Object.defineProperty(startOptions, 'providerContinuityLineage', {
|
|
647
|
-
value: this.deps.providerContinuity.lineage,
|
|
648
|
-
enumerable: this.deps.providerContinuity.lineage !== 0,
|
|
649
|
-
});
|
|
650
|
-
return (await this.deps.agentClient.startStream(attempt.streamInput, startOptions));
|
|
651
|
-
}
|
|
652
|
-
async #waitBeforeRetry(attempt, delayMs) {
|
|
653
|
-
if (attempt.signal?.aborted)
|
|
654
|
-
throw Object.assign(new Error('Operation aborted'), { name: 'AbortError' });
|
|
655
|
-
if (!attempt.signal) {
|
|
656
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
await new Promise((resolve, reject) => {
|
|
660
|
-
const onAbort = () => {
|
|
661
|
-
clearTimeout(timer);
|
|
662
|
-
attempt.signal?.removeEventListener('abort', onAbort);
|
|
663
|
-
reject(Object.assign(new Error('Operation aborted'), { name: 'AbortError' }));
|
|
664
|
-
};
|
|
665
|
-
const timer = setTimeout(() => {
|
|
666
|
-
attempt.signal?.removeEventListener('abort', onAbort);
|
|
667
|
-
resolve();
|
|
668
|
-
}, delayMs);
|
|
669
|
-
attempt.signal.addEventListener('abort', onAbort, { once: true });
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
async *executeContinuationAttempt(init, policy) {
|
|
673
|
-
const activePolicy = policy ?? new ShellAutoApprovalDecisionPolicy(this.deps.shellAutoApproval);
|
|
674
|
-
if (!this.deps.generationGuard.isCurrent(init.generation)) {
|
|
675
|
-
return { kind: 'stale' };
|
|
676
|
-
}
|
|
677
|
-
const prepared = this.deps.planApplier.prepareInit(init);
|
|
678
|
-
const state = new ContinuationState(init.generation, this.#activeRecoveryBudget);
|
|
679
|
-
state.initializeFrom(prepared, this.#activeCallIdsForInit(init, prepared));
|
|
680
|
-
try {
|
|
681
|
-
yield* this.deps.planApplier.applyInitialSetup(prepared, state);
|
|
682
|
-
while (true) {
|
|
683
|
-
if (!this.deps.generationGuard.isCurrent(state.token)) {
|
|
684
|
-
return { kind: 'stale' };
|
|
685
|
-
}
|
|
686
|
-
try {
|
|
687
|
-
const batchDecision = yield* this.#stagePendingParallelApprovals(state, activePolicy);
|
|
688
|
-
if (batchDecision.kind === 'stale') {
|
|
689
|
-
return { kind: 'stale' };
|
|
690
|
-
}
|
|
691
|
-
if (batchDecision.kind === 'approval_required') {
|
|
692
|
-
return { kind: 'approval_required', terminal: batchDecision.terminal };
|
|
693
|
-
}
|
|
694
|
-
const previousInputForSurge = state.inputMode === 'full_history' ? this.deps.conversationStore.getHistory() : undefined;
|
|
695
|
-
const cycleResult = yield* this.#executeContinuationStreamCycle(state, init.kind === 'approval_decision' && init.stopAfterApprovalResolution === true);
|
|
696
|
-
if (cycleResult.kind === 'stale') {
|
|
697
|
-
return { kind: 'stale' };
|
|
698
|
-
}
|
|
699
|
-
const { outcome, nextCumulativeMessages, nextCumulativeUsage, nextCumulativeTurnItems, mergedEmittedIds } = cycleResult;
|
|
700
|
-
state.setRetryCounts({ ...state.retryCounts, transientRetryCount: 0 });
|
|
701
|
-
state.setCumulativeUsage(nextCumulativeUsage);
|
|
702
|
-
state.setCumulativeCommandMessages(nextCumulativeMessages);
|
|
703
|
-
state.setCumulativeTurnItems(nextCumulativeTurnItems);
|
|
704
|
-
if (outcome.kind === 'response') {
|
|
705
|
-
this.#recordSuccess(state, previousInputForSurge);
|
|
706
|
-
return { kind: 'response', terminal: this.#buildResponse(outcome.result, nextCumulativeUsage) };
|
|
707
|
-
}
|
|
708
|
-
const approvalResult = await this.#handleApprovalOutcome(outcome, state, activePolicy, nextCumulativeUsage, previousInputForSurge);
|
|
709
|
-
if (approvalResult.action === 'return') {
|
|
710
|
-
return approvalResult.result;
|
|
711
|
-
}
|
|
712
|
-
if (approvalResult.action === 'loop') {
|
|
713
|
-
yield* this.deps.planApplier.applyNextPlan(approvalResult.nextPlan, state, mergedEmittedIds, approvalResult.isApproved);
|
|
714
|
-
state.currentCallIds = resolveResponseCycleCallIds({
|
|
715
|
-
interruptionCallIds: this.#interruptionCallIds(approvalResult.nextPlan.pendingApprovalContext.state, approvalResult.nextPlan.pendingApprovalContext.interruption),
|
|
716
|
-
completedResultCallIds: this.deps.toolTracker.completedResultCallIdsForCurrentTurn(),
|
|
717
|
-
fallbackCallIds: state.currentCallIds,
|
|
718
|
-
conversationHistory: this.deps.conversationStore.getHistory(),
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
continue;
|
|
722
|
-
}
|
|
723
|
-
catch (error) {
|
|
724
|
-
const recovery = yield* this.#handleContinuationRecovery(error, state);
|
|
725
|
-
if (recovery.kind === 'terminated') {
|
|
726
|
-
throw error;
|
|
727
|
-
}
|
|
728
|
-
if (recovery.kind === 'stale') {
|
|
729
|
-
return { kind: 'stale' };
|
|
730
|
-
}
|
|
731
|
-
if (recovery.kind === 'fresh_start') {
|
|
732
|
-
return {
|
|
733
|
-
kind: 'fresh_start_required',
|
|
734
|
-
retryCounts: recovery.retryCounts,
|
|
735
|
-
...(recovery.delayMs !== undefined ? { delayMs: recovery.delayMs } : {}),
|
|
736
|
-
...(recovery.useStandardServiceTier ? { useStandardServiceTier: true } : {}),
|
|
737
|
-
...(recovery.disableChainingForAttempt ? { disableChainingForAttempt: true } : {}),
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
// recovery.kind === 'resume' -> continue loop
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
catch (error) {
|
|
745
|
-
const classification = classifyProviderFailure(error);
|
|
746
|
-
const cancelled = isClassifiedCancellation(error);
|
|
747
|
-
const log = cancelled ? this.deps.logger.debug : this.deps.logger.error;
|
|
748
|
-
log.call(this.deps.logger, cancelled ? 'Conversation stream aborted during continuation' : 'Conversation stream error during continuation', {
|
|
749
|
-
eventType: cancelled ? 'stream.aborted' : 'stream.failed',
|
|
750
|
-
category: 'stream',
|
|
751
|
-
phase: 'abort',
|
|
752
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
753
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
754
|
-
errorMessage: describeError(error),
|
|
755
|
-
errorKind: classification.errorKind,
|
|
756
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
757
|
-
});
|
|
758
|
-
if (!cancelled) {
|
|
759
|
-
yield {
|
|
760
|
-
type: 'error',
|
|
761
|
-
message: describeError(error),
|
|
762
|
-
...(error && typeof error === 'object' && 'code' in error && typeof error.code === 'string'
|
|
763
|
-
? { code: error.code }
|
|
764
|
-
: {}),
|
|
765
|
-
...(error instanceof Error && error.stack ? { stack: error.stack } : {}),
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
throw error;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
async *#stagePendingParallelApprovals(state, activePolicy) {
|
|
772
|
-
const result = yield* this.#batchCoordinator.stageBatch({
|
|
773
|
-
state,
|
|
774
|
-
policy: activePolicy,
|
|
775
|
-
token: state.token,
|
|
776
|
-
});
|
|
777
|
-
if (result.kind === 'stale') {
|
|
778
|
-
return { kind: 'stale' };
|
|
779
|
-
}
|
|
780
|
-
if (result.kind === 'ready') {
|
|
781
|
-
const pending = this.deps.approvalFlow.getPending?.();
|
|
782
|
-
state.currentCallIds = resolveResponseCycleCallIds({
|
|
783
|
-
interruptionCallIds: this.#interruptionCallIds(state.currentState, pending?.interruption),
|
|
784
|
-
completedResultCallIds: this.deps.toolTracker.completedResultCallIdsForCurrentTurn(),
|
|
785
|
-
fallbackCallIds: state.currentCallIds,
|
|
786
|
-
conversationHistory: this.deps.conversationStore.getHistory(),
|
|
787
|
-
preserveFallback: true,
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
return result;
|
|
791
|
-
}
|
|
792
|
-
async *#handleContinuationRecovery(error, state) {
|
|
793
|
-
const recoveryIterator = this.deps.continuationRecoveryHandler.handle({ error, state });
|
|
794
|
-
let recoveryNext = await recoveryIterator.next();
|
|
795
|
-
while (!recoveryNext.done) {
|
|
796
|
-
yield recoveryNext.value;
|
|
797
|
-
recoveryNext = await recoveryIterator.next();
|
|
798
|
-
}
|
|
799
|
-
return recoveryNext.value;
|
|
800
|
-
}
|
|
801
|
-
#buildResponse(result, usage) {
|
|
802
|
-
return {
|
|
803
|
-
type: 'response',
|
|
804
|
-
commandMessages: result.commandMessages ?? [],
|
|
805
|
-
finalText: result.finalText,
|
|
806
|
-
...(result.reasoningText ? { reasoningText: result.reasoningText } : {}),
|
|
807
|
-
...(usage && Object.keys(usage).length > 0 ? { usage } : {}),
|
|
808
|
-
// Cost records are run-cumulative, so the continuation cycle must carry
|
|
809
|
-
// forward what the collector observed; dropping them here silently
|
|
810
|
-
// unprices every turn that used a tool or paused for approval.
|
|
811
|
-
...(result.costRecords?.length ? { costRecords: result.costRecords } : {}),
|
|
812
|
-
turnItems: result.turnItems,
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
async #handleApprovalOutcome(outcome, state, activePolicy, nextCumulativeUsage, previousInputForSurge) {
|
|
816
|
-
const { kind } = outcome;
|
|
817
|
-
if (kind === 'auto_approve') {
|
|
818
|
-
if (outcome.advisory?.source === 'llm') {
|
|
819
|
-
this.#toolCallMarkers.markToolCallAsLlmAutoApproved(outcome.callId);
|
|
820
|
-
}
|
|
821
|
-
this.deps.logger.debug('Shell command auto-approved by LLM', {
|
|
822
|
-
eventType: 'approval.auto_approved',
|
|
823
|
-
category: 'approval',
|
|
824
|
-
phase: 'approval',
|
|
825
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
826
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
827
|
-
callId: outcome.callId,
|
|
828
|
-
command: outcome.argumentsText,
|
|
829
|
-
model: outcome.advisory?.model,
|
|
830
|
-
reasoning: outcome.advisory?.reasoning,
|
|
831
|
-
});
|
|
832
|
-
const nextPlan = this.deps.approvalFlow.prepareContinuation('y', undefined, 'policy');
|
|
833
|
-
if (!nextPlan) {
|
|
834
|
-
const approvalFallback = this.#createApprovalRequiredFromAutoApprove(outcome, nextCumulativeUsage);
|
|
835
|
-
return { action: 'return', result: { kind: 'approval_required', terminal: approvalFallback } };
|
|
836
|
-
}
|
|
837
|
-
return { action: 'loop', nextPlan, isApproved: true };
|
|
838
|
-
}
|
|
839
|
-
const approvalContext = {
|
|
840
|
-
toolName: outcome.result.approval.toolName,
|
|
841
|
-
argumentsText: outcome.result.approval.argumentsText,
|
|
842
|
-
callId: outcome.result.approval.callId,
|
|
843
|
-
llmAdvisory: outcome.result.approval.llmAdvisory,
|
|
844
|
-
};
|
|
845
|
-
const decision = await activePolicy.decide(approvalContext);
|
|
846
|
-
if (decision === 'prompt') {
|
|
847
|
-
this.deps.planApplier.recordPendingApproval(approvalContext);
|
|
848
|
-
if (outcome.result.approval.callId) {
|
|
849
|
-
this.deps.logger.debug('Tool approval required', {
|
|
850
|
-
eventType: 'approval.required',
|
|
851
|
-
category: 'approval',
|
|
852
|
-
phase: 'approval',
|
|
853
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
854
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
855
|
-
toolName: outcome.result.approval.toolName,
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
this.#recordSuccess(state, previousInputForSurge);
|
|
859
|
-
const resultWithUsage = {
|
|
860
|
-
...outcome.result,
|
|
861
|
-
...(nextCumulativeUsage && Object.keys(nextCumulativeUsage).length > 0 ? { usage: nextCumulativeUsage } : {}),
|
|
862
|
-
};
|
|
863
|
-
return { action: 'return', result: { kind: 'approval_required', terminal: resultWithUsage } };
|
|
864
|
-
}
|
|
865
|
-
if (decision === 'approve') {
|
|
866
|
-
this.deps.logger.debug('Shell command auto-approved by policy', {
|
|
867
|
-
eventType: 'approval.auto_approved',
|
|
868
|
-
category: 'approval',
|
|
869
|
-
phase: 'approval',
|
|
870
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
871
|
-
traceId: this.deps.logger.getCorrelationId(),
|
|
872
|
-
callId: approvalContext.callId,
|
|
873
|
-
command: approvalContext.argumentsText,
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
const answer = decision === 'approve' ? 'y' : 'n';
|
|
877
|
-
const nextPlan = this.deps.approvalFlow.prepareContinuation(answer, undefined, decision === 'approve' || decision === 'reject' ? 'policy' : 'user');
|
|
878
|
-
if (!nextPlan) {
|
|
879
|
-
return { action: 'return', result: { kind: 'approval_required', terminal: outcome.result } };
|
|
880
|
-
}
|
|
881
|
-
return { action: 'loop', nextPlan, isApproved: answer === 'y' };
|
|
882
|
-
}
|
|
883
|
-
async *#executeContinuationStreamCycle(state, stopAfterApprovalResolution = false) {
|
|
884
|
-
const sessionId = resolveSessionId(this.deps.sessionId);
|
|
885
|
-
const promptCacheKey = resolvePromptCacheKey(this.deps.sessionId);
|
|
886
|
-
const continuationOptions = {
|
|
887
|
-
recoveryBudget: state.recoveryBudget,
|
|
888
|
-
previousResponseId: state.currentResumePreviousResponseId ?? this.deps.providerContinuity.previousResponseId,
|
|
889
|
-
sessionId,
|
|
890
|
-
...(promptCacheKey !== sessionId ? { promptCacheKey } : {}),
|
|
891
|
-
toolResultCallIds: state.currentCallIds,
|
|
892
|
-
knownToolCallIds: collectKnownToolCallIds(this.deps.conversationStore.getHistory(), this.deps.toolTracker.activeCallIdsForCurrentTurn()),
|
|
893
|
-
providerHistorySnapshot: this.deps.conversationStore.getProviderHistorySnapshot(),
|
|
894
|
-
hookTurnId: this.#hookTurnId,
|
|
895
|
-
...(stopAfterApprovalResolution ? { stopAfterApprovalResolution: true } : {}),
|
|
896
|
-
};
|
|
897
|
-
Object.defineProperty(continuationOptions, 'providerContinuityLineage', {
|
|
898
|
-
value: this.deps.providerContinuity.lineage,
|
|
899
|
-
enumerable: this.deps.providerContinuity.lineage !== 0,
|
|
900
|
-
});
|
|
901
|
-
const stream = (await this.deps.agentClient.continueRunStream(state.currentState, continuationOptions));
|
|
902
|
-
state.setLastStream(stream);
|
|
903
|
-
const allEmittedIds = new Set([...state.previouslyEmittedIds]);
|
|
904
|
-
const acc = yield* this.deps.streamProcessor.process(stream, {
|
|
905
|
-
gen: state.token,
|
|
906
|
-
source: state.source,
|
|
907
|
-
preserveExistingToolArgs: true,
|
|
908
|
-
previouslyEmittedCommandIds: allEmittedIds,
|
|
909
|
-
});
|
|
910
|
-
const finalizeResult = this.deps.streamProcessor.finalize(stream, state.token, state.inputMode, state.source);
|
|
911
|
-
if (finalizeResult.kind === 'stale') {
|
|
912
|
-
return { kind: 'stale' };
|
|
913
|
-
}
|
|
914
|
-
for (const event of this.#contextCompactionCompletedEvent({
|
|
915
|
-
stream,
|
|
916
|
-
inputTokensBefore: acc.latestUsage?.prompt_tokens,
|
|
917
|
-
durationMs: acc.lastContextCompactionDurationMs,
|
|
918
|
-
})) {
|
|
919
|
-
yield event;
|
|
920
|
-
}
|
|
921
|
-
const mergedEmittedIds = new Set([...allEmittedIds, ...acc.emittedCommandIds]);
|
|
922
|
-
const streamMessages = extractCommandMessages(selectAgentStreamItems(stream), this.#toolCallMarkers);
|
|
923
|
-
const filteredMessages = streamMessages.filter((msg) => !state.previouslyEmittedIds.has(msg.id));
|
|
924
|
-
const nextCumulativeMessages = [...state.cumulativeCommandMessages, ...filteredMessages];
|
|
925
|
-
const nextCumulativeUsage = acc.latestUsage ?? state.cumulativeUsage;
|
|
926
|
-
const nextCumulativeTurnItems = this.deps.turnAccumulator.getTurnItems();
|
|
927
|
-
const outcome = await buildConversationResult({
|
|
928
|
-
result: stream,
|
|
929
|
-
finalOutputOverride: acc.finalOutput || undefined,
|
|
930
|
-
reasoningOutputOverride: acc.reasoningOutput || undefined,
|
|
931
|
-
emittedCommandIds: mergedEmittedIds,
|
|
932
|
-
usage: acc.latestUsage,
|
|
933
|
-
toolCallArgumentsById: this.deps.toolTracker.argumentsById,
|
|
934
|
-
turnItems: nextCumulativeTurnItems,
|
|
935
|
-
token: state.token,
|
|
936
|
-
inputMode: state.inputMode,
|
|
937
|
-
cumulativeUsage: nextCumulativeUsage,
|
|
938
|
-
cumulativeCommandMessages: nextCumulativeMessages,
|
|
939
|
-
cumulativeTurnItems: nextCumulativeTurnItems,
|
|
940
|
-
}, {
|
|
941
|
-
approvalFlow: this.deps.approvalFlow,
|
|
942
|
-
shellAutoApproval: this.deps.shellAutoApproval,
|
|
943
|
-
logger: this.deps.logger,
|
|
944
|
-
sessionId: resolveSessionId(this.deps.sessionId),
|
|
945
|
-
sessionAccess: this.deps.sessionAccess,
|
|
946
|
-
approvalPolicyRegistry: this.#activePolicyRegistry,
|
|
947
|
-
toolCallMarkers: this.#toolCallMarkers,
|
|
948
|
-
});
|
|
949
|
-
return {
|
|
950
|
-
kind: 'completed',
|
|
951
|
-
outcome,
|
|
952
|
-
nextCumulativeMessages,
|
|
953
|
-
nextCumulativeUsage,
|
|
954
|
-
nextCumulativeTurnItems,
|
|
955
|
-
mergedEmittedIds,
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
#activeCallIdsForInit(init, prepared) {
|
|
959
|
-
if (init.kind === 'abort_resolution') {
|
|
960
|
-
return resolveAbortedApprovalCallIds({
|
|
961
|
-
interruptionCallIds: this.#interruptionCallIds(init.abortedContext.state, init.abortedContext.interruption),
|
|
962
|
-
completedResultCallIds: this.deps.toolTracker.completedResultCallIdsForCurrentTurn(),
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
return resolveResponseCycleCallIds({
|
|
966
|
-
interruptionCallIds: this.#interruptionCallIds(prepared.state, prepared.interruption),
|
|
967
|
-
completedResultCallIds: this.deps.toolTracker.completedResultCallIdsForCurrentTurn(),
|
|
968
|
-
fallbackCallIds: this.deps.toolTracker.activeCallIdsForCurrentTurn(),
|
|
969
|
-
conversationHistory: this.deps.conversationStore.getHistory(),
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
#interruptionCallIds(state, primaryInterruption) {
|
|
973
|
-
const callIds = new Set();
|
|
974
|
-
const interruptions = getMethod(state, 'getInterruptions')?.();
|
|
975
|
-
if (Array.isArray(interruptions)) {
|
|
976
|
-
for (const interruption of interruptions) {
|
|
977
|
-
const callId = getCallIdFromObject(interruption);
|
|
978
|
-
if (callId) {
|
|
979
|
-
callIds.add(callId);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
const primaryCallId = getCallIdFromObject(primaryInterruption);
|
|
984
|
-
if (primaryCallId) {
|
|
985
|
-
callIds.add(primaryCallId);
|
|
986
|
-
}
|
|
987
|
-
return [...callIds];
|
|
988
|
-
}
|
|
989
|
-
#recordSuccess(state, previousInputForSurge) {
|
|
990
|
-
this.deps.inputPlanner.recordSuccess(state.inputMode === 'delta' ? state.lastStream : this.deps.conversationStore.getHistory(), state.inputMode === 'delta'
|
|
991
|
-
? { kind: state.inputMode }
|
|
992
|
-
: { kind: state.inputMode, previousInput: previousInputForSurge });
|
|
993
|
-
}
|
|
994
|
-
abortLiveRun() {
|
|
995
|
-
const liveRun = this.#liveRun;
|
|
996
|
-
this.#liveRun = null;
|
|
997
|
-
liveRun?.cancel();
|
|
998
|
-
this.deps.setActivePostExecuteRunId?.(null);
|
|
999
|
-
return (liveRun?.completion.then(() => undefined, () => undefined) ?? Promise.resolve());
|
|
1000
|
-
}
|
|
1001
|
-
get activePostExecuteRunId() {
|
|
1002
|
-
return this.#liveRun?.runId ?? null;
|
|
1003
|
-
}
|
|
1004
|
-
#createApprovalRequiredFromAutoApprove(outcome, usage) {
|
|
1005
|
-
const pending = this.deps.approvalFlow.getPending();
|
|
1006
|
-
if (pending) {
|
|
1007
|
-
const { toolName, argumentsText } = getToolInfoFromInterruption(pending.interruption);
|
|
1008
|
-
const agent = pending.interruption && typeof pending.interruption === 'object'
|
|
1009
|
-
? pending.interruption.agent
|
|
1010
|
-
: undefined;
|
|
1011
|
-
const agentName = agent && typeof agent === 'object' ? agent.name : 'Agent';
|
|
1012
|
-
const callId = getCallIdFromObject(pending.interruption);
|
|
1013
|
-
return createApprovalRequiredTerminal({
|
|
1014
|
-
agentName: typeof agentName === 'string' ? agentName : 'Agent',
|
|
1015
|
-
toolName: toolName ?? 'Unknown Tool',
|
|
1016
|
-
argumentsText,
|
|
1017
|
-
rawInterruption: pending.interruption,
|
|
1018
|
-
callId: callId ? String(callId) : undefined,
|
|
1019
|
-
llmAdvisory: outcome.advisory,
|
|
1020
|
-
usage,
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
return createApprovalRequiredTerminal({
|
|
1024
|
-
agentName: 'Agent',
|
|
1025
|
-
toolName: 'Unknown Tool',
|
|
1026
|
-
argumentsText: outcome.argumentsText,
|
|
1027
|
-
rawInterruption: undefined,
|
|
1028
|
-
callId: outcome.callId,
|
|
1029
|
-
llmAdvisory: outcome.advisory,
|
|
1030
|
-
usage,
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
async #emitApprovalRequested(approval) {
|
|
1034
|
-
if (!this.deps.hookLifecycle || !this.deps.hookEvents)
|
|
1035
|
-
return;
|
|
1036
|
-
let normalizedArguments = approval.argumentsText;
|
|
1037
|
-
try {
|
|
1038
|
-
normalizedArguments = JSON.parse(approval.argumentsText);
|
|
1039
|
-
}
|
|
1040
|
-
catch {
|
|
1041
|
-
// Preserve a bounded opaque representation for malformed model input.
|
|
1042
|
-
}
|
|
1043
|
-
await this.deps.hookLifecycle.emit(this.deps.hookEvents.create('approval.requested', {
|
|
1044
|
-
toolName: approval.toolName,
|
|
1045
|
-
normalizedArguments: this.deps.hookEvents.includeToolArguments
|
|
1046
|
-
? normalizedArguments
|
|
1047
|
-
: typeof normalizedArguments === 'string'
|
|
1048
|
-
? normalizedArguments.slice(0, 500)
|
|
1049
|
-
: JSON.stringify(normalizedArguments).slice(0, 500),
|
|
1050
|
-
approvalKind: approval.toolName === 'ask_user' ? 'ask_user' : 'tool',
|
|
1051
|
-
proposedDecision: 'approve',
|
|
1052
|
-
}, { toolCallId: approval.callId }));
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
//# sourceMappingURL=turn-workflow.js.map
|