@oh-my-pi/pi-coding-agent 17.2.8 → 17.2.10
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/CHANGELOG.md +94 -1
- package/dist/{CHANGELOG-0b0w17se.md → CHANGELOG-rhwzpexa.md} +94 -1
- package/dist/cli.js +13216 -15614
- package/dist/types/async/job-manager.d.ts +12 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +13 -0
- package/dist/types/discovery/agents.d.ts +11 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/loader.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +34 -8
- package/dist/types/extensibility/hooks/types.d.ts +5 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +10 -0
- package/dist/types/hindsight/state.d.ts +0 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/lsp/utils.d.ts +8 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
- package/dist/types/modes/acp/acp-mode.d.ts +1 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
- package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
- package/dist/types/modes/components/agent-hub.d.ts +12 -5
- package/dist/types/modes/components/model-browser.d.ts +9 -1
- package/dist/types/modes/interactive-mode.d.ts +8 -1
- package/dist/types/modes/session-observer-registry.d.ts +2 -0
- package/dist/types/registry/agent-lifecycle.d.ts +1 -1
- package/dist/types/registry/agent-registry.d.ts +47 -0
- package/dist/types/registry/persisted-agents.d.ts +3 -1
- package/dist/types/session/agent-session-types.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +8 -4
- package/dist/types/session/session-entries.d.ts +10 -0
- package/dist/types/session/session-loader.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +9 -1
- package/dist/types/session/session-tools.d.ts +10 -1
- package/dist/types/session/turn-recovery.d.ts +6 -2
- package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
- package/dist/types/slash-commands/available-commands.d.ts +1 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +2 -3
- package/dist/types/task/read-only-policy.d.ts +3 -0
- package/dist/types/task/structured-subagent.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/launch.d.ts +3 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -2
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/read.d.ts +3 -5
- package/dist/types/tools/run-scope.d.ts +27 -1
- package/dist/types/tools/shell-tokenize.d.ts +21 -3
- package/dist/types/tools/terminal-output.d.ts +1 -1
- package/dist/types/utils/changelog.d.ts +3 -2
- package/dist/types/utils/late-cleanup.d.ts +2 -0
- package/dist/types/utils/turndown.d.ts +1 -1
- package/dist/types/vibe/runtime.d.ts +4 -3
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
- package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/api-demo.ts +5 -6
- package/examples/extensions/chalk-logger.ts +1 -1
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +15 -31
- package/scripts/bundle-dist.ts +1 -11
- package/scripts/omp +11 -2
- package/src/advisor/runtime.ts +54 -0
- package/src/async/job-manager.ts +26 -0
- package/src/cli/agents-cli.ts +1 -1
- package/src/cli/args.ts +24 -2
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/flag-tables.ts +4 -0
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/help-extra.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +4 -4
- package/src/cli/tiny-models-cli.ts +1 -1
- package/src/cli/ttsr-cli.ts +1 -1
- package/src/cli/update-cli.ts +3 -57
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +1 -1
- package/src/commands/say.ts +1 -1
- package/src/commands/token.ts +1 -1
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/cli.ts +1 -1
- package/src/config/config-file.ts +2 -2
- package/src/config/keybindings.ts +3 -3
- package/src/config/model-discovery.ts +36 -2
- package/src/config/model-registry.ts +4 -1
- package/src/config/model-resolver.ts +54 -7
- package/src/discovery/agents.ts +73 -3
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
- package/src/discovery/claude.ts +8 -5
- package/src/discovery/cursor.ts +8 -5
- package/src/discovery/gemini.ts +11 -8
- package/src/discovery/vscode.ts +1 -0
- package/src/discovery/windsurf.ts +4 -2
- package/src/edit/hashline/filesystem.ts +7 -7
- package/src/eval/agent-bridge.ts +1 -3
- package/src/eval/jl/prelude.jl +4 -4
- package/src/eval/js/shared/prelude.txt +2 -2
- package/src/eval/py/prelude.py +0 -3
- package/src/eval/py/runner.py +38 -1
- package/src/eval/rb/prelude.rb +1 -2
- package/src/extensibility/custom-commands/loader.ts +4 -4
- package/src/extensibility/custom-commands/types.ts +5 -5
- package/src/extensibility/custom-tools/loader.ts +4 -4
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +7 -6
- package/src/extensibility/extensions/types.ts +39 -8
- package/src/extensibility/hooks/loader.ts +4 -5
- package/src/extensibility/hooks/types.ts +5 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +23 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
- package/src/hindsight/state.ts +2 -22
- package/src/index.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +33 -0
- package/src/launch/terminal-output.ts +1 -1
- package/src/lsp/client.ts +2 -2
- package/src/lsp/utils.ts +29 -0
- package/src/main.ts +85 -21
- package/src/markit/converters/docx.ts +1 -1
- package/src/markit/converters/epub.ts +1 -1
- package/src/markit/converters/pptx.ts +1 -1
- package/src/markit/converters/xlsx.ts +1 -1
- package/src/modes/acp/acp-agent.ts +7 -6
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/acp/acp-mode.ts +1 -1
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +670 -192
- package/src/modes/components/agent-transcript-viewer.ts +1 -3
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/custom-editor.ts +2 -2
- package/src/modes/components/extensions/inspector-panel.ts +11 -6
- package/src/modes/components/footer.ts +1 -3
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-browser.ts +11 -3
- package/src/modes/components/model-hub.ts +4 -1
- package/src/modes/components/session-selector.ts +2 -2
- package/src/modes/components/status-line/segments.ts +4 -3
- package/src/modes/controllers/event-controller.ts +188 -7
- package/src/modes/controllers/extension-ui-controller.ts +4 -3
- package/src/modes/controllers/mcp-command-controller.ts +81 -20
- package/src/modes/controllers/selector-controller.ts +40 -49
- package/src/modes/controllers/streaming-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/print-mode.ts +29 -12
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/session-observer-registry.ts +5 -0
- package/src/modes/theme/theme.ts +2 -2
- package/src/prompts/tools/computer.md +1 -1
- package/src/registry/agent-lifecycle.ts +35 -10
- package/src/registry/agent-registry.ts +65 -2
- package/src/registry/persisted-agents.ts +341 -16
- package/src/sdk.ts +11 -5
- package/src/session/agent-session-types.ts +8 -0
- package/src/session/agent-session.ts +177 -188
- package/src/session/session-entries.ts +10 -0
- package/src/session/session-listing.ts +1 -1
- package/src/session/session-loader.ts +74 -8
- package/src/session/session-manager.ts +22 -2
- package/src/session/session-paths.ts +105 -110
- package/src/session/session-tools.ts +28 -1
- package/src/session/settings-stream-fn.ts +7 -3
- package/src/session/turn-recovery.ts +236 -18
- package/src/session/unexpected-stop-classifier.ts +12 -3
- package/src/slash-commands/acp-builtins.ts +1 -1
- package/src/slash-commands/available-commands.ts +1 -1
- package/src/slash-commands/helpers/stats-dashboard.ts +2 -1
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +173 -47
- package/src/task/index.ts +9 -28
- package/src/task/isolation-runner.ts +43 -12
- package/src/task/persisted-revive.ts +12 -5
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/structured-subagent.ts +31 -6
- package/src/task/types.ts +4 -0
- package/src/tiny/worker.ts +1 -1
- package/src/tools/auto-generated-guard.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash-interceptor.ts +8 -4
- package/src/tools/bash.ts +6 -6
- package/src/tools/browser/cmux/cmux-tab.ts +66 -18
- package/src/tools/browser/launch.ts +76 -19
- package/src/tools/browser/readable.ts +9 -9
- package/src/tools/browser/tab-supervisor.ts +12 -3
- package/src/tools/browser/tab-worker-entry.ts +1 -1
- package/src/tools/browser/tab-worker.ts +101 -12
- package/src/tools/fetch.ts +1 -1
- package/src/tools/glob.ts +4 -2
- package/src/tools/path-utils.ts +22 -5
- package/src/tools/read.ts +22 -5
- package/src/tools/run-scope.ts +290 -4
- package/src/tools/shell-tokenize.ts +38 -9
- package/src/tools/terminal-output.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/utils/changelog.ts +5 -4
- package/src/utils/clipboard.ts +10 -2
- package/src/utils/external-editor.ts +4 -2
- package/src/utils/jj.ts +1 -1
- package/src/utils/late-cleanup.ts +17 -0
- package/src/utils/turndown.ts +1 -2
- package/src/vibe/runtime.ts +202 -58
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/hackage.ts +1 -12
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +2 -1
- package/src/web/scrapers/twitter.ts +2 -2
- package/src/web/scrapers/types.ts +1 -1
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/search/providers/browser-headers.ts +12 -39
- package/src/web/search/providers/codex.ts +3 -26
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/ecosia.ts +2 -2
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/firecrawl.ts +1 -1
- package/src/web/search/providers/google.ts +1 -1
- package/src/web/search/providers/mojeek.ts +2 -2
- package/src/web/search/providers/perplexity.ts +1 -1
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/startpage.ts +2 -2
- package/src/markit/converters/mammoth.d.ts +0 -24
|
@@ -58,7 +58,6 @@ import type {
|
|
|
58
58
|
ImageContent,
|
|
59
59
|
Message,
|
|
60
60
|
Model,
|
|
61
|
-
ModelUsageHealth,
|
|
62
61
|
OAuthAccountIdentity,
|
|
63
62
|
ProviderSessionState,
|
|
64
63
|
ResetCreditAccountStatus,
|
|
@@ -83,6 +82,7 @@ import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
|
83
82
|
import { MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
|
|
84
83
|
import {
|
|
85
84
|
$env,
|
|
85
|
+
APP_NAME,
|
|
86
86
|
escapeXmlText,
|
|
87
87
|
formatDuration,
|
|
88
88
|
getAgentDbPath,
|
|
@@ -101,7 +101,7 @@ import { type AdvisorConfig, type AdvisorRuntimeStatus, loadAdvisorTranscriptCos
|
|
|
101
101
|
import { type AsyncJob, AsyncJobManager } from "../async";
|
|
102
102
|
import { shouldEnableAppendOnlyContext } from "../config/append-only-context-mode";
|
|
103
103
|
import type { ModelRegistry } from "../config/model-registry";
|
|
104
|
-
import {
|
|
104
|
+
import type { ResolvedModelRoleValue } from "../config/model-resolver";
|
|
105
105
|
import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates";
|
|
106
106
|
import { buildServiceTierByFamily } from "../config/service-tier";
|
|
107
107
|
import type { Settings, SkillsSettings } from "../config/settings";
|
|
@@ -127,6 +127,7 @@ import type {
|
|
|
127
127
|
ToolExecutionEndEvent,
|
|
128
128
|
ToolExecutionStartEvent,
|
|
129
129
|
ToolExecutionUpdateEvent,
|
|
130
|
+
ToolInfo,
|
|
130
131
|
TreePreparation,
|
|
131
132
|
TurnEndEvent,
|
|
132
133
|
TurnStartEvent,
|
|
@@ -230,7 +231,7 @@ import type {
|
|
|
230
231
|
SessionHandoffOptions,
|
|
231
232
|
SessionOAuthAccountList,
|
|
232
233
|
SessionStats,
|
|
233
|
-
|
|
234
|
+
UsageFallbackConfirmer,
|
|
234
235
|
} from "./agent-session-types";
|
|
235
236
|
import {
|
|
236
237
|
ASYNC_INLINE_RESULT_MAX_CHARS,
|
|
@@ -312,7 +313,6 @@ import {
|
|
|
312
313
|
queueChipText,
|
|
313
314
|
toRestoredQueuedMessage,
|
|
314
315
|
} from "./queued-messages";
|
|
315
|
-
import { formatRetryFallbackSelector, type RetryFallbackSelector } from "./retry-fallback-chains";
|
|
316
316
|
import { type AdvisorStats, SessionAdvisors, type SessionAdvisorsHost } from "./session-advisors";
|
|
317
317
|
import type { BuildSessionContextOptions, SessionContext } from "./session-context";
|
|
318
318
|
import { getRestorableSessionModels } from "./session-context";
|
|
@@ -447,6 +447,7 @@ export class AgentSession {
|
|
|
447
447
|
// Event subscription state
|
|
448
448
|
#unsubscribeAgent?: () => void;
|
|
449
449
|
#cancelExitRecorder?: () => void;
|
|
450
|
+
#cancelFatalRecoveryHint?: () => void;
|
|
450
451
|
#exitRecorded = false;
|
|
451
452
|
#unsubscribeAppendOnly?: () => void;
|
|
452
453
|
#unsubscribeModelRoles?: () => void;
|
|
@@ -485,6 +486,7 @@ export class AgentSession {
|
|
|
485
486
|
|
|
486
487
|
// Retry state
|
|
487
488
|
readonly #recovery: TurnRecovery;
|
|
489
|
+
#textOutputCommitted = true;
|
|
488
490
|
#planModeReminderCount = 0;
|
|
489
491
|
#planModeReminderAwaitingProgress = false;
|
|
490
492
|
readonly #todo: TodoTracker;
|
|
@@ -558,9 +560,13 @@ export class AgentSession {
|
|
|
558
560
|
|
|
559
561
|
// Model registry for API key resolution
|
|
560
562
|
#modelRegistry: ModelRegistry;
|
|
561
|
-
#usageFallbackConfirmer:
|
|
562
|
-
#usageReserveApprovedSelector: string | undefined;
|
|
563
|
+
#usageFallbackConfirmer: UsageFallbackConfirmer | undefined;
|
|
563
564
|
#usagePreflightAbortControllers = new Set<AbortController>();
|
|
565
|
+
#queuedMessageDrainBlocked = false;
|
|
566
|
+
#usagePreflightReadyForNextModelCall = false;
|
|
567
|
+
#usagePreflightReadyModel: Model | undefined;
|
|
568
|
+
#detachUsageBeforeQueueDequeue: (() => void) | undefined;
|
|
569
|
+
#detachUsageBeforeModelCall: (() => void) | undefined;
|
|
564
570
|
|
|
565
571
|
#transformContext: (messages: AgentMessage[], signal?: AbortSignal) => AgentMessage[] | Promise<AgentMessage[]>;
|
|
566
572
|
#onPayload: SimpleStreamOptions["onPayload"] | undefined;
|
|
@@ -768,8 +774,10 @@ export class AgentSession {
|
|
|
768
774
|
!this.#canAutoContinueForFollowUp()
|
|
769
775
|
? [...this.agent.peekFollowUpQueue()]
|
|
770
776
|
: [];
|
|
777
|
+
const parkedQueueDrainBlocked = parkedFollowUps.length > 0 && this.#queuedMessageDrainBlocked;
|
|
771
778
|
if (parkedFollowUps.length > 0) {
|
|
772
779
|
this.agent.replaceQueues([...this.agent.peekSteeringQueue()], []);
|
|
780
|
+
if (parkedQueueDrainBlocked) this.#queuedMessageDrainBlocked = false;
|
|
773
781
|
}
|
|
774
782
|
let finishObservation: ((error?: unknown) => void | Promise<void>) | undefined;
|
|
775
783
|
try {
|
|
@@ -805,6 +813,7 @@ export class AgentSession {
|
|
|
805
813
|
[...this.agent.peekSteeringQueue()],
|
|
806
814
|
[...parkedFollowUps, ...this.agent.peekFollowUpQueue()],
|
|
807
815
|
);
|
|
816
|
+
this.#queuedMessageDrainBlocked ||= parkedQueueDrainBlocked;
|
|
808
817
|
}
|
|
809
818
|
this.#endInFlight(async () => {
|
|
810
819
|
try {
|
|
@@ -830,6 +839,7 @@ export class AgentSession {
|
|
|
830
839
|
steering.filter(m => !isAdvisorCard(m)),
|
|
831
840
|
followUp.filter(m => !isAdvisorCard(m)),
|
|
832
841
|
);
|
|
842
|
+
this.#reconcileQueuedMessageDrain();
|
|
833
843
|
return cards;
|
|
834
844
|
}
|
|
835
845
|
|
|
@@ -1025,6 +1035,7 @@ export class AgentSession {
|
|
|
1025
1035
|
modelRegistry: this.#modelRegistry,
|
|
1026
1036
|
configWarnings: this.configWarnings,
|
|
1027
1037
|
model: () => this.model,
|
|
1038
|
+
textOutputCommitted: () => this.#textOutputCommitted,
|
|
1028
1039
|
thinkingLevel: () => this.thinkingLevel,
|
|
1029
1040
|
configuredThinkingLevel: () => this.configuredThinkingLevel(),
|
|
1030
1041
|
setThinkingLevel: level => this.setThinkingLevel(level),
|
|
@@ -1050,6 +1061,31 @@ export class AgentSession {
|
|
|
1050
1061
|
withBashBranchTransition: operation => this.#bash.withBranchTransition(operation),
|
|
1051
1062
|
};
|
|
1052
1063
|
this.#recovery = new TurnRecovery(recoveryHost, { initialRetryFallback: config.initialRetryFallback });
|
|
1064
|
+
this.#detachUsageBeforeQueueDequeue = this.agent.addBeforeQueuedMessageDequeueHook(async signal => {
|
|
1065
|
+
if (
|
|
1066
|
+
!this.settings.get("retry.usageAwareFallback") ||
|
|
1067
|
+
(this.#usagePreflightReadyForNextModelCall && this.#usagePreflightReadyModel === this.model)
|
|
1068
|
+
) {
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (!(await this.#runQueuedUsageAwarePreflight(signal))) {
|
|
1072
|
+
signal?.throwIfAborted();
|
|
1073
|
+
throw new DOMException("Usage preflight cancelled", "AbortError");
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
this.#detachUsageBeforeModelCall = this.agent.addBeforeModelCallHook(async signal => {
|
|
1077
|
+
if (!this.settings.get("retry.usageAwareFallback")) return;
|
|
1078
|
+
if (this.#usagePreflightReadyForNextModelCall) {
|
|
1079
|
+
const checkedModel = this.#usagePreflightReadyModel;
|
|
1080
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
1081
|
+
this.#usagePreflightReadyModel = undefined;
|
|
1082
|
+
if (checkedModel === this.model) return;
|
|
1083
|
+
}
|
|
1084
|
+
if (!(await this.#runUsageAwarePreflight(signal))) {
|
|
1085
|
+
signal?.throwIfAborted();
|
|
1086
|
+
throw new DOMException("Usage preflight cancelled", "AbortError");
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1053
1089
|
const statsHost: SessionStatsTrackerHost = {
|
|
1054
1090
|
session: this,
|
|
1055
1091
|
agent: this.agent,
|
|
@@ -1343,6 +1379,14 @@ export class AgentSession {
|
|
|
1343
1379
|
this.#cancelExitRecorder = postmortem.register(`agent-session:${this.sessionManager.getSessionId()}`, reason => {
|
|
1344
1380
|
this.#recordSessionExit(reason);
|
|
1345
1381
|
});
|
|
1382
|
+
this.#cancelFatalRecoveryHint = postmortem.registerFatalRecoveryHint(() => {
|
|
1383
|
+
const sessionId = this.sessionManager.getSessionId();
|
|
1384
|
+
if (!sessionId || !this.sessionManager.getSessionFile()) return undefined;
|
|
1385
|
+
return {
|
|
1386
|
+
label: this.#agentId ?? (this.#agentKind === "main" ? "Main" : "Agent"),
|
|
1387
|
+
command: `${APP_NAME} --resume ${sessionId}`,
|
|
1388
|
+
};
|
|
1389
|
+
});
|
|
1346
1390
|
|
|
1347
1391
|
const advisorsHost: SessionAdvisorsHost = {
|
|
1348
1392
|
agent: this.agent,
|
|
@@ -2658,14 +2702,21 @@ export class AgentSession {
|
|
|
2658
2702
|
// outside the session transcript (issue #6177).
|
|
2659
2703
|
logProviderTurnError(msg);
|
|
2660
2704
|
|
|
2661
|
-
// Invalidate GitHub Copilot credentials on auth failure
|
|
2662
|
-
// aren't reused on the next request
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2705
|
+
// Invalidate GitHub Copilot credentials on a hard auth failure (401, or an
|
|
2706
|
+
// expired/revoked token) so stale tokens aren't reused on the next request.
|
|
2707
|
+
// Account usage caps and concurrency caps leave the credential valid: the
|
|
2708
|
+
// former rotates until its reset window, while the latter is retried after
|
|
2709
|
+
// a short backoff without touching the credential pool.
|
|
2710
|
+
if (msg.stopReason === "error" && msg.provider === "github-copilot") {
|
|
2711
|
+
const errorId = AIError.classifyMessage(msg);
|
|
2712
|
+
const isConcurrencyCap = AIError.parseRateLimitReason(msg.errorMessage ?? "") === "CONCURRENT_LIMIT";
|
|
2713
|
+
if (
|
|
2714
|
+
AIError.is(errorId, AIError.Flag.AuthFailed) &&
|
|
2715
|
+
!AIError.is(errorId, AIError.Flag.UsageLimit) &&
|
|
2716
|
+
!isConcurrencyCap
|
|
2717
|
+
) {
|
|
2718
|
+
await this.#modelRegistry.authStorage.remove("github-copilot");
|
|
2719
|
+
}
|
|
2669
2720
|
}
|
|
2670
2721
|
|
|
2671
2722
|
if (this.#maintenance.skipPostTurnMaintenanceAssistantTimestamp === msg.timestamp) {
|
|
@@ -2967,19 +3018,17 @@ export class AgentSession {
|
|
|
2967
3018
|
this.#beginInFlight();
|
|
2968
3019
|
try {
|
|
2969
3020
|
await this.#recovery.maybeRestoreRetryFallbackPrimary();
|
|
2970
|
-
if (
|
|
2971
|
-
this.settings.get("retry.modelFallback") &&
|
|
2972
|
-
this.settings.get("retry.usageAwareFallback") &&
|
|
2973
|
-
!(await this.#runUsageAwarePreflight())
|
|
2974
|
-
) {
|
|
2975
|
-
this.#skipAgentContinue("session-unavailable", options);
|
|
2976
|
-
return;
|
|
2977
|
-
}
|
|
2978
3021
|
if (signal.aborted || this.#isDisposed) {
|
|
2979
3022
|
this.#skipAgentContinue("post-restore-unavailable", options);
|
|
2980
3023
|
return;
|
|
2981
3024
|
}
|
|
2982
|
-
|
|
3025
|
+
if (this.settings.get("retry.usageAwareFallback")) {
|
|
3026
|
+
if (!(await this.#runQueuedUsageAwarePreflight(signal))) {
|
|
3027
|
+
this.#skipAgentContinue("session-unavailable", options);
|
|
3028
|
+
return;
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
await this.agent.continue(signal);
|
|
2983
3032
|
} catch (error) {
|
|
2984
3033
|
logger.warn("agent.continue failed after scheduling", {
|
|
2985
3034
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -2987,6 +3036,7 @@ export class AgentSession {
|
|
|
2987
3036
|
});
|
|
2988
3037
|
options?.onError?.(error);
|
|
2989
3038
|
} finally {
|
|
3039
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
2990
3040
|
this.#endInFlight();
|
|
2991
3041
|
}
|
|
2992
3042
|
},
|
|
@@ -3605,6 +3655,12 @@ export class AgentSession {
|
|
|
3605
3655
|
*/
|
|
3606
3656
|
beginDispose(): void {
|
|
3607
3657
|
this.#isDisposed = true;
|
|
3658
|
+
this.#queuedMessageDrainBlocked = false;
|
|
3659
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
3660
|
+
this.#detachUsageBeforeQueueDequeue?.();
|
|
3661
|
+
this.#detachUsageBeforeQueueDequeue = undefined;
|
|
3662
|
+
this.#detachUsageBeforeModelCall?.();
|
|
3663
|
+
this.#detachUsageBeforeModelCall = undefined;
|
|
3608
3664
|
this.#memory.cancelLocalMemoryStartup();
|
|
3609
3665
|
this.#titleGenerationAbortController.abort();
|
|
3610
3666
|
this.#abortAutolearnCapture();
|
|
@@ -3713,6 +3769,8 @@ export class AgentSession {
|
|
|
3713
3769
|
this.#recordSessionExit(options.reason ?? "dispose");
|
|
3714
3770
|
this.#cancelExitRecorder?.();
|
|
3715
3771
|
this.#cancelExitRecorder = undefined;
|
|
3772
|
+
this.#cancelFatalRecoveryHint?.();
|
|
3773
|
+
this.#cancelFatalRecoveryHint = undefined;
|
|
3716
3774
|
try {
|
|
3717
3775
|
await emitSessionShutdownEvent(this.#extensionRunner);
|
|
3718
3776
|
} catch (error) {
|
|
@@ -3926,174 +3984,69 @@ export class AgentSession {
|
|
|
3926
3984
|
}
|
|
3927
3985
|
|
|
3928
3986
|
/** Install the interactive decision surface for reserve-triggered model changes. */
|
|
3929
|
-
setUsageFallbackConfirmer(
|
|
3930
|
-
confirmer: ((confirmation: UsageFallbackConfirmation) => Promise<boolean>) | undefined,
|
|
3931
|
-
): void {
|
|
3987
|
+
setUsageFallbackConfirmer(confirmer: UsageFallbackConfirmer | undefined): void {
|
|
3932
3988
|
this.#usageFallbackConfirmer = confirmer;
|
|
3933
3989
|
}
|
|
3934
3990
|
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
const controller = new AbortController();
|
|
3938
|
-
this.#usagePreflightAbortControllers.add(controller);
|
|
3939
|
-
try {
|
|
3940
|
-
await this.#maybeApplyUsageAwareFallback(controller.signal);
|
|
3941
|
-
return !controller.signal.aborted && this.#promptGeneration === generation;
|
|
3942
|
-
} catch (error) {
|
|
3943
|
-
if (controller.signal.aborted || this.#promptGeneration !== generation) return false;
|
|
3944
|
-
throw error;
|
|
3945
|
-
} finally {
|
|
3946
|
-
this.#usagePreflightAbortControllers.delete(controller);
|
|
3947
|
-
}
|
|
3991
|
+
#allowQueuedMessageDrainRetry(): void {
|
|
3992
|
+
this.#queuedMessageDrainBlocked = false;
|
|
3948
3993
|
}
|
|
3949
3994
|
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
const aborted = Promise.withResolvers<boolean>();
|
|
3954
|
-
const onAbort = () => aborted.resolve(false);
|
|
3955
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
3956
|
-
try {
|
|
3957
|
-
return await Promise.race([confirmer(confirmation), aborted.promise]);
|
|
3958
|
-
} finally {
|
|
3959
|
-
signal.removeEventListener("abort", onAbort);
|
|
3995
|
+
#reconcileQueuedMessageDrain(): void {
|
|
3996
|
+
if (!this.agent.hasQueuedMessages()) {
|
|
3997
|
+
this.#queuedMessageDrainBlocked = false;
|
|
3960
3998
|
}
|
|
3961
3999
|
}
|
|
3962
4000
|
|
|
3963
|
-
async #
|
|
3964
|
-
if (!this.settings.get("retry.modelFallback") || !this.settings.get("retry.usageAwareFallback")) return;
|
|
3965
|
-
const currentModel = this.model;
|
|
3966
|
-
if (!currentModel) return;
|
|
3967
|
-
const currentSelector = formatRetryFallbackSelector(currentModel, this.thinkingLevel);
|
|
3968
|
-
let health: ModelUsageHealth;
|
|
4001
|
+
async #runQueuedUsageAwarePreflight(signal?: AbortSignal): Promise<boolean> {
|
|
3969
4002
|
try {
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
signal,
|
|
3976
|
-
});
|
|
4003
|
+
const allowed = await this.#runUsageAwarePreflight(signal);
|
|
4004
|
+
this.#usagePreflightReadyForNextModelCall = allowed;
|
|
4005
|
+
this.#usagePreflightReadyModel = allowed ? this.model : undefined;
|
|
4006
|
+
this.#queuedMessageDrainBlocked = !allowed && this.agent.hasQueuedMessages();
|
|
4007
|
+
return allowed;
|
|
3977
4008
|
} catch (error) {
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
model: currentModel.id,
|
|
3981
|
-
error: String(error),
|
|
3982
|
-
});
|
|
3983
|
-
return;
|
|
4009
|
+
this.#queuedMessageDrainBlocked = this.agent.hasQueuedMessages();
|
|
4010
|
+
throw error;
|
|
3984
4011
|
}
|
|
3985
|
-
|
|
4012
|
+
}
|
|
3986
4013
|
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
if (!role) return;
|
|
4010
|
-
let fallback: { selector: RetryFallbackSelector; apiKey: string } | undefined;
|
|
4011
|
-
for (const candidate of this.#recovery.findRetryFallbackCandidates(role, currentSelector, currentModel)) {
|
|
4012
|
-
if (this.#recovery.isRetryFallbackSelectorSuppressed(candidate)) continue;
|
|
4013
|
-
const resolved = resolveModelOverride([candidate.raw], this.#modelRegistry, this.settings);
|
|
4014
|
-
const candidateModel = resolved.model ?? this.#modelRegistry.find(candidate.provider, candidate.id);
|
|
4015
|
-
if (!candidateModel) continue;
|
|
4016
|
-
if (!this.#modelRegistry.hasConfiguredAuth(candidateModel)) continue;
|
|
4017
|
-
try {
|
|
4018
|
-
const candidateHealth = await this.#modelRegistry.authStorage.getModelUsageHealth(candidateModel.provider, {
|
|
4019
|
-
modelId: candidateModel.id,
|
|
4020
|
-
sessionId: this.sessionId,
|
|
4021
|
-
baseUrl: candidateModel.baseUrl,
|
|
4022
|
-
reserveFraction: this.settings.get("retry.usageReservePct") / 100,
|
|
4023
|
-
signal,
|
|
4024
|
-
});
|
|
4025
|
-
if (signal.aborted) return;
|
|
4026
|
-
if (candidateHealth.state === "depleted" || candidateHealth.state === "reserve") continue;
|
|
4027
|
-
if (candidateHealth.state === "healthy") {
|
|
4028
|
-
const selected = candidateHealth.accounts.find(account => account.selected);
|
|
4029
|
-
if (
|
|
4030
|
-
selected &&
|
|
4031
|
-
selected.state !== "healthy" &&
|
|
4032
|
-
candidateHealth.accounts.some(account => account.state === "healthy")
|
|
4033
|
-
) {
|
|
4034
|
-
this.#modelRegistry.authStorage.releaseSessionCredentialForReselection(
|
|
4035
|
-
candidateModel.provider,
|
|
4036
|
-
this.sessionId,
|
|
4037
|
-
);
|
|
4038
|
-
}
|
|
4039
|
-
}
|
|
4040
|
-
} catch {
|
|
4041
|
-
if (signal.aborted) return;
|
|
4042
|
-
// Unknown usage fails open for an otherwise valid fallback.
|
|
4043
|
-
}
|
|
4044
|
-
if (signal.aborted) return;
|
|
4045
|
-
let apiKey: string | undefined;
|
|
4046
|
-
try {
|
|
4047
|
-
apiKey = await this.#modelRegistry.getApiKey(candidateModel, this.sessionId, { signal });
|
|
4048
|
-
} catch {
|
|
4049
|
-
if (signal.aborted) return;
|
|
4050
|
-
continue;
|
|
4051
|
-
}
|
|
4052
|
-
if (signal.aborted) return;
|
|
4053
|
-
if (!apiKey) continue;
|
|
4054
|
-
fallback = { selector: candidate, apiKey };
|
|
4055
|
-
break;
|
|
4056
|
-
}
|
|
4057
|
-
if (!fallback) return;
|
|
4058
|
-
|
|
4059
|
-
if (health.state === "reserve") {
|
|
4060
|
-
if (reservePolicy === "confirm" && this.#usageFallbackConfirmer) {
|
|
4061
|
-
if (this.#usageReserveApprovedSelector === currentSelector) return;
|
|
4062
|
-
const selected = health.accounts.find(account => account.selected);
|
|
4063
|
-
const remainingFraction =
|
|
4064
|
-
selected?.remainingFraction ??
|
|
4065
|
-
health.accounts.reduce<number | undefined>(
|
|
4066
|
-
(minimum, account) =>
|
|
4067
|
-
account.remainingFraction === undefined
|
|
4068
|
-
? minimum
|
|
4069
|
-
: minimum === undefined
|
|
4070
|
-
? account.remainingFraction
|
|
4071
|
-
: Math.min(minimum, account.remainingFraction),
|
|
4072
|
-
undefined,
|
|
4014
|
+
async #runUsageAwarePreflightForNextModelCall(signal?: AbortSignal): Promise<boolean> {
|
|
4015
|
+
const allowed = await this.#runUsageAwarePreflight(signal);
|
|
4016
|
+
this.#usagePreflightReadyForNextModelCall = allowed;
|
|
4017
|
+
this.#usagePreflightReadyModel = allowed ? this.model : undefined;
|
|
4018
|
+
return allowed;
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
async #runUsageAwarePreflight(signal?: AbortSignal): Promise<boolean> {
|
|
4022
|
+
if (signal?.aborted) return false;
|
|
4023
|
+
const generation = this.#promptGeneration;
|
|
4024
|
+
|
|
4025
|
+
const controller = new AbortController();
|
|
4026
|
+
const onAbort = () => controller.abort(signal?.reason);
|
|
4027
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4028
|
+
this.#usagePreflightAbortControllers.add(controller);
|
|
4029
|
+
try {
|
|
4030
|
+
while (true) {
|
|
4031
|
+
const model = this.model;
|
|
4032
|
+
try {
|
|
4033
|
+
const fallbackCommitted = await this.#recovery.maybeApplyUsageAwareFallback(
|
|
4034
|
+
controller.signal,
|
|
4035
|
+
this.#usageFallbackConfirmer,
|
|
4073
4036
|
);
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
);
|
|
4082
|
-
if (signal.aborted) return;
|
|
4083
|
-
if (!shouldFallback) {
|
|
4084
|
-
this.#usageReserveApprovedSelector = currentSelector;
|
|
4085
|
-
return;
|
|
4037
|
+
if (fallbackCommitted) return true;
|
|
4038
|
+
if (controller.signal.aborted || this.#promptGeneration !== generation) return false;
|
|
4039
|
+
if (this.model === model || modelsAreEqual(this.model, model)) return true;
|
|
4040
|
+
} catch (error) {
|
|
4041
|
+
if (controller.signal.aborted || this.#promptGeneration !== generation) return false;
|
|
4042
|
+
if (this.model !== model && !modelsAreEqual(this.model, model)) continue;
|
|
4043
|
+
throw error;
|
|
4086
4044
|
}
|
|
4087
4045
|
}
|
|
4046
|
+
} finally {
|
|
4047
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4048
|
+
this.#usagePreflightAbortControllers.delete(controller);
|
|
4088
4049
|
}
|
|
4089
|
-
|
|
4090
|
-
if (signal.aborted) return;
|
|
4091
|
-
this.#usageReserveApprovedSelector = undefined;
|
|
4092
|
-
await this.#recovery.applyRetryFallbackCandidate(role, fallback.selector, currentSelector, {
|
|
4093
|
-
pinFallback: true,
|
|
4094
|
-
apiKey: fallback.apiKey,
|
|
4095
|
-
signal,
|
|
4096
|
-
});
|
|
4097
4050
|
}
|
|
4098
4051
|
|
|
4099
4052
|
/** Effective thinking level applied to the agent (the resolved level when `auto`). */
|
|
@@ -4184,6 +4137,11 @@ export class AgentSession {
|
|
|
4184
4137
|
return this.agent.state.systemPrompt;
|
|
4185
4138
|
}
|
|
4186
4139
|
|
|
4140
|
+
/** Marks streamed text as committed or buffered for turn-recovery replay decisions. */
|
|
4141
|
+
setTextOutputCommitted(committed: boolean): void {
|
|
4142
|
+
this.#textOutputCommitted = committed;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4187
4145
|
/** Current retry attempt (0 if not retrying) */
|
|
4188
4146
|
get retryAttempt(): number {
|
|
4189
4147
|
return this.#recovery.attempt;
|
|
@@ -4224,6 +4182,11 @@ export class AgentSession {
|
|
|
4224
4182
|
return this.#tools.getAllToolNames();
|
|
4225
4183
|
}
|
|
4226
4184
|
|
|
4185
|
+
/** Full metadata for every registered tool, including source provenance (backs `getAllTools()`). */
|
|
4186
|
+
getAllToolInfos(): ToolInfo[] {
|
|
4187
|
+
return this.#tools.getAllToolInfos();
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4227
4190
|
/** Installs and activates the ephemeral vibe tool set. */
|
|
4228
4191
|
activateVibeTools(baseToolNames: string[]): Promise<void> {
|
|
4229
4192
|
return this.#tools.activateVibeTools(baseToolNames);
|
|
@@ -5026,7 +4989,7 @@ export class AgentSession {
|
|
|
5026
4989
|
if (this.isStreaming) {
|
|
5027
4990
|
const streamingBehavior = options?.streamingBehavior;
|
|
5028
4991
|
if (!streamingBehavior) throw new AgentBusyError();
|
|
5029
|
-
|
|
4992
|
+
|
|
5030
4993
|
// Steer/follow-up the keyword notices BEFORE the queued user message so the
|
|
5031
4994
|
// model reads the steering notice ahead of the prompt it modifies.
|
|
5032
4995
|
for (const notice of keywordNotices) {
|
|
@@ -5121,7 +5084,7 @@ export class AgentSession {
|
|
|
5121
5084
|
if (options?.queueOnly) {
|
|
5122
5085
|
const streamingBehavior = options?.streamingBehavior;
|
|
5123
5086
|
if (!streamingBehavior) throw new AgentBusyError();
|
|
5124
|
-
|
|
5087
|
+
|
|
5125
5088
|
for (const notice of keywordNotices) {
|
|
5126
5089
|
await this.#queueCustomMessage(notice, streamingBehavior);
|
|
5127
5090
|
}
|
|
@@ -5131,7 +5094,7 @@ export class AgentSession {
|
|
|
5131
5094
|
if (this.isStreaming) {
|
|
5132
5095
|
const streamingBehavior = options?.streamingBehavior;
|
|
5133
5096
|
if (!streamingBehavior) throw new AgentBusyError();
|
|
5134
|
-
|
|
5097
|
+
|
|
5135
5098
|
for (const notice of keywordNotices) {
|
|
5136
5099
|
await this.#queueCustomMessage(notice, streamingBehavior);
|
|
5137
5100
|
}
|
|
@@ -5168,7 +5131,7 @@ export class AgentSession {
|
|
|
5168
5131
|
const generation = this.#promptGeneration;
|
|
5169
5132
|
try {
|
|
5170
5133
|
await this.#recovery.maybeRestoreRetryFallbackPrimary();
|
|
5171
|
-
if (!(await this.#
|
|
5134
|
+
if (!(await this.#runUsageAwarePreflightForNextModelCall())) return;
|
|
5172
5135
|
// Flush any pending bash messages before the new prompt
|
|
5173
5136
|
await this.#bash.flushPending();
|
|
5174
5137
|
this.#eval.flushPending();
|
|
@@ -5379,6 +5342,7 @@ export class AgentSession {
|
|
|
5379
5342
|
await this.#waitForPostPromptRecovery(generation);
|
|
5380
5343
|
}
|
|
5381
5344
|
} finally {
|
|
5345
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
5382
5346
|
this.#endInFlight();
|
|
5383
5347
|
}
|
|
5384
5348
|
}
|
|
@@ -5544,7 +5508,6 @@ export class AgentSession {
|
|
|
5544
5508
|
}
|
|
5545
5509
|
|
|
5546
5510
|
const expandedText = expandPromptTemplate(text, [...this.#promptTemplates]);
|
|
5547
|
-
if (!(await this.#runUsageAwarePreflight())) return;
|
|
5548
5511
|
await this.#queueUserMessage(expandedText, images, "steer");
|
|
5549
5512
|
}
|
|
5550
5513
|
|
|
@@ -5562,7 +5525,6 @@ export class AgentSession {
|
|
|
5562
5525
|
|
|
5563
5526
|
const expandedText =
|
|
5564
5527
|
options?.expandPromptTemplates === false ? text : expandPromptTemplate(text, [...this.#promptTemplates]);
|
|
5565
|
-
if (!(await this.#runUsageAwarePreflight())) return;
|
|
5566
5528
|
if (!options?.synthetic) {
|
|
5567
5529
|
await this.#queueUserMessage(expandedText, images, "followUp");
|
|
5568
5530
|
return;
|
|
@@ -5579,6 +5541,7 @@ export class AgentSession {
|
|
|
5579
5541
|
const imageDescriptionNotice = normalizedImages?.length
|
|
5580
5542
|
? await this.#buildImageDescriptionNotice(normalizedImages)
|
|
5581
5543
|
: undefined;
|
|
5544
|
+
this.#allowQueuedMessageDrainRetry();
|
|
5582
5545
|
if (imageDescriptionNotice) this.agent.followUp(imageDescriptionNotice);
|
|
5583
5546
|
this.agent.followUp({
|
|
5584
5547
|
role: "developer",
|
|
@@ -5608,6 +5571,7 @@ export class AgentSession {
|
|
|
5608
5571
|
const imageDescriptionNotice = normalizedImages?.length
|
|
5609
5572
|
? await this.#buildImageDescriptionNotice(normalizedImages)
|
|
5610
5573
|
: undefined;
|
|
5574
|
+
this.#allowQueuedMessageDrainRetry();
|
|
5611
5575
|
if (mode === "followUp") {
|
|
5612
5576
|
if (imageDescriptionNotice) this.agent.followUp(imageDescriptionNotice);
|
|
5613
5577
|
this.agent.followUp({
|
|
@@ -5634,7 +5598,12 @@ export class AgentSession {
|
|
|
5634
5598
|
}
|
|
5635
5599
|
|
|
5636
5600
|
#scheduleQueuedMessageDrain(): void {
|
|
5637
|
-
if (
|
|
5601
|
+
if (
|
|
5602
|
+
this.#queuedMessageDrainScheduled ||
|
|
5603
|
+
this.#queuedMessageDrainBlocked ||
|
|
5604
|
+
!this.#canAutoContinueForFollowUp() ||
|
|
5605
|
+
!this.agent.hasQueuedMessages()
|
|
5606
|
+
) {
|
|
5638
5607
|
return;
|
|
5639
5608
|
}
|
|
5640
5609
|
this.#queuedMessageDrainScheduled = true;
|
|
@@ -5648,6 +5617,7 @@ export class AgentSession {
|
|
|
5648
5617
|
},
|
|
5649
5618
|
onError: () => {
|
|
5650
5619
|
this.#queuedMessageDrainScheduled = false;
|
|
5620
|
+
this.#queuedMessageDrainBlocked = this.agent.hasQueuedMessages();
|
|
5651
5621
|
},
|
|
5652
5622
|
});
|
|
5653
5623
|
}
|
|
@@ -5784,7 +5754,7 @@ export class AgentSession {
|
|
|
5784
5754
|
): Promise<void> {
|
|
5785
5755
|
this.#beginInFlight();
|
|
5786
5756
|
try {
|
|
5787
|
-
if (!(await this.#
|
|
5757
|
+
if (!(await this.#runUsageAwarePreflightForNextModelCall())) return;
|
|
5788
5758
|
const acceptTerminalEmptyStop = options?.acceptTerminalEmptyStop === true;
|
|
5789
5759
|
if (acceptTerminalEmptyStop) {
|
|
5790
5760
|
this.#resetPromptMaintenanceState();
|
|
@@ -5793,6 +5763,7 @@ export class AgentSession {
|
|
|
5793
5763
|
await this.agent.prompt(message);
|
|
5794
5764
|
await this.#waitForPostPromptRecovery();
|
|
5795
5765
|
} finally {
|
|
5766
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
5796
5767
|
this.#recovery.setAcceptTerminalEmptyStop(false);
|
|
5797
5768
|
this.#endInFlight();
|
|
5798
5769
|
}
|
|
@@ -5824,6 +5795,7 @@ export class AgentSession {
|
|
|
5824
5795
|
timestamp: Date.now(),
|
|
5825
5796
|
};
|
|
5826
5797
|
const normalizedAppMessage = await this.#normalizeAgentMessageImages(appMessage);
|
|
5798
|
+
this.#allowQueuedMessageDrainRetry();
|
|
5827
5799
|
if (deliverAs === "followUp") {
|
|
5828
5800
|
this.agent.followUp(normalizedAppMessage);
|
|
5829
5801
|
} else {
|
|
@@ -5880,7 +5852,7 @@ export class AgentSession {
|
|
|
5880
5852
|
this.#queueHiddenNextTurnMessage(normalizedAppMessage, options?.triggerTurn ?? false);
|
|
5881
5853
|
return false;
|
|
5882
5854
|
}
|
|
5883
|
-
|
|
5855
|
+
this.#allowQueuedMessageDrainRetry();
|
|
5884
5856
|
|
|
5885
5857
|
if (options?.deliverAs === "followUp") {
|
|
5886
5858
|
this.agent.followUp(normalizedAppMessage);
|
|
@@ -5963,8 +5935,6 @@ export class AgentSession {
|
|
|
5963
5935
|
if (images.length === 0) images = undefined;
|
|
5964
5936
|
}
|
|
5965
5937
|
|
|
5966
|
-
if (options?.deliverAs && !(await this.#runUsageAwarePreflight())) return;
|
|
5967
|
-
|
|
5968
5938
|
if (options?.deliverAs === "followUp") {
|
|
5969
5939
|
await this.#queueUserMessage(text, images, "followUp");
|
|
5970
5940
|
return;
|
|
@@ -6004,6 +5974,7 @@ export class AgentSession {
|
|
|
6004
5974
|
? isAdvisorCard
|
|
6005
5975
|
: m => !isUserQueuedMessage(m) && !isHiddenUserCompanion(m);
|
|
6006
5976
|
this.agent.replaceQueues(steeringAll.filter(keep), followUpAll.filter(keep));
|
|
5977
|
+
this.#reconcileQueuedMessageDrain();
|
|
6007
5978
|
return { steering, followUp };
|
|
6008
5979
|
}
|
|
6009
5980
|
|
|
@@ -6053,12 +6024,14 @@ export class AgentSession {
|
|
|
6053
6024
|
if (fromSteer >= 0) {
|
|
6054
6025
|
const removed = steering[fromSteer];
|
|
6055
6026
|
this.agent.replaceQueues(removeWithCompanions(steering, fromSteer), followUp.slice());
|
|
6027
|
+
this.#reconcileQueuedMessageDrain();
|
|
6056
6028
|
return toRestoredQueuedMessage(removed);
|
|
6057
6029
|
}
|
|
6058
6030
|
const fromFollowUp = lastUserIndex(followUp);
|
|
6059
6031
|
if (fromFollowUp >= 0) {
|
|
6060
6032
|
const removed = followUp[fromFollowUp];
|
|
6061
6033
|
this.agent.replaceQueues(steering.slice(), removeWithCompanions(followUp, fromFollowUp));
|
|
6034
|
+
this.#reconcileQueuedMessageDrain();
|
|
6062
6035
|
return toRestoredQueuedMessage(removed);
|
|
6063
6036
|
}
|
|
6064
6037
|
return undefined;
|
|
@@ -6348,6 +6321,8 @@ export class AgentSession {
|
|
|
6348
6321
|
await this.#memory.resetContextForNewTranscript();
|
|
6349
6322
|
this.#pendingNextTurnMessages = [];
|
|
6350
6323
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
6324
|
+
this.#queuedMessageDrainBlocked = false;
|
|
6325
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
6351
6326
|
|
|
6352
6327
|
this.sessionManager.appendThinkingLevelChange(this.thinkingLevel, this.configuredThinkingLevel());
|
|
6353
6328
|
this.sessionManager.appendServiceTierChange(this.#models.serviceTierEntry());
|
|
@@ -6712,9 +6687,10 @@ export class AgentSession {
|
|
|
6712
6687
|
}
|
|
6713
6688
|
|
|
6714
6689
|
#extractRewindReport(messages: AgentMessage[]): string | undefined {
|
|
6715
|
-
|
|
6690
|
+
const checkpointState = this.#checkpointState;
|
|
6691
|
+
if (!checkpointState) return undefined;
|
|
6716
6692
|
if (this.#pendingRewindReport) return this.#pendingRewindReport;
|
|
6717
|
-
for (let i = messages.length - 1; i >=
|
|
6693
|
+
for (let i = messages.length - 1; i >= checkpointState.checkpointMessageCount; i--) {
|
|
6718
6694
|
const message = messages[i];
|
|
6719
6695
|
if (message?.role !== "toolResult" || message.isError) continue;
|
|
6720
6696
|
const semanticResult = semanticToolResult(message.toolName, message);
|
|
@@ -7372,6 +7348,9 @@ export class AgentSession {
|
|
|
7372
7348
|
const previousFollowUpMessages = [...this.agent.peekFollowUpQueue()];
|
|
7373
7349
|
const previousPendingNextTurnMessages = [...this.#pendingNextTurnMessages];
|
|
7374
7350
|
const previousScheduledHiddenNextTurnGeneration = this.#scheduledHiddenNextTurnGeneration;
|
|
7351
|
+
const previousQueuedMessageDrainBlocked = this.#queuedMessageDrainBlocked;
|
|
7352
|
+
const previousUsagePreflightReadyForNextModelCall = this.#usagePreflightReadyForNextModelCall;
|
|
7353
|
+
const previousUsagePreflightReadyModel = this.#usagePreflightReadyModel;
|
|
7375
7354
|
const previousModel = this.model;
|
|
7376
7355
|
const previousThinkingLevel = this.thinkingLevel;
|
|
7377
7356
|
const previousAutoThinking = this.isAutoThinking;
|
|
@@ -7396,6 +7375,9 @@ export class AgentSession {
|
|
|
7396
7375
|
this.agent.clearAllQueues();
|
|
7397
7376
|
this.#pendingNextTurnMessages = [];
|
|
7398
7377
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
7378
|
+
this.#queuedMessageDrainBlocked = false;
|
|
7379
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
7380
|
+
this.#usagePreflightReadyModel = undefined;
|
|
7399
7381
|
|
|
7400
7382
|
try {
|
|
7401
7383
|
if (switchingToDifferentSession) {
|
|
@@ -7564,6 +7546,9 @@ export class AgentSession {
|
|
|
7564
7546
|
this.agent.replaceQueues(previousSteeringMessages, previousFollowUpMessages);
|
|
7565
7547
|
this.#pendingNextTurnMessages = previousPendingNextTurnMessages;
|
|
7566
7548
|
this.#scheduledHiddenNextTurnGeneration = previousScheduledHiddenNextTurnGeneration;
|
|
7549
|
+
this.#queuedMessageDrainBlocked = previousQueuedMessageDrainBlocked;
|
|
7550
|
+
this.#usagePreflightReadyForNextModelCall = previousUsagePreflightReadyForNextModelCall;
|
|
7551
|
+
this.#usagePreflightReadyModel = previousUsagePreflightReadyModel;
|
|
7567
7552
|
this.#inheritedProviderPromptCacheKey = previousInheritedProviderPromptCacheKey;
|
|
7568
7553
|
this.#checkpointState = previousCheckpointState;
|
|
7569
7554
|
this.#pendingRewindReport = previousPendingRewindReport;
|
|
@@ -7652,6 +7637,8 @@ export class AgentSession {
|
|
|
7652
7637
|
// Clear pending messages (bound to old session state)
|
|
7653
7638
|
this.#pendingNextTurnMessages = [];
|
|
7654
7639
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
7640
|
+
this.#queuedMessageDrainBlocked = false;
|
|
7641
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
7655
7642
|
|
|
7656
7643
|
await this.#bash.flushPending();
|
|
7657
7644
|
// Flush pending writes before branching
|
|
@@ -7779,6 +7766,8 @@ export class AgentSession {
|
|
|
7779
7766
|
this.#pendingNextTurnMessages = [];
|
|
7780
7767
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
7781
7768
|
this.agent.replaceQueues([], []);
|
|
7769
|
+
this.#queuedMessageDrainBlocked = false;
|
|
7770
|
+
this.#usagePreflightReadyForNextModelCall = false;
|
|
7782
7771
|
await this.#bash.flushPending();
|
|
7783
7772
|
await this.sessionManager.flush();
|
|
7784
7773
|
const bashTransition = this.#bash.beginSessionTransition();
|