@sayknow-cli/coding-agent 0.3.1 → 0.3.4
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/bin/skc.js +4 -0
- package/dist/types/cli/mcp-cli.d.ts +25 -0
- package/dist/types/cli/plugin-cli.d.ts +2 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/mcp.d.ts +70 -0
- package/dist/types/commands/plugin.d.ts +6 -0
- package/dist/types/commands/session.d.ts +6 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +8 -1
- package/dist/types/config/model-profiles.d.ts +2 -2
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/models-config-schema.d.ts +0 -5
- package/dist/types/config/settings-schema.d.ts +79 -68
- package/dist/types/deep-interview/plaintext-gate-guard.d.ts +11 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/skc-plugins/compiler.d.ts +19 -0
- package/dist/types/extensibility/skc-plugins/constrained-hooks.d.ts +29 -0
- package/dist/types/extensibility/skc-plugins/index.d.ts +9 -0
- package/dist/types/extensibility/skc-plugins/injection.d.ts +9 -0
- package/dist/types/extensibility/skc-plugins/installer.d.ts +13 -0
- package/dist/types/extensibility/skc-plugins/mcp-policy.d.ts +26 -0
- package/dist/types/extensibility/skc-plugins/observability.d.ts +27 -0
- package/dist/types/extensibility/skc-plugins/prompt-appendix.d.ts +16 -0
- package/dist/types/extensibility/skc-plugins/registry.d.ts +32 -0
- package/dist/types/extensibility/skc-plugins/runtime-adapters.d.ts +64 -0
- package/dist/types/extensibility/skc-plugins/session-validation.d.ts +42 -0
- package/dist/types/extensibility/skc-plugins/types.d.ts +158 -2
- package/dist/types/extensibility/skc-plugins/validation.d.ts +8 -1
- package/dist/types/i18n/messages/en.d.ts +0 -1
- package/dist/types/main.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +8 -0
- package/dist/types/modes/components/settings-defs.d.ts +0 -1
- package/dist/types/modes/components/status-line/git-utils.d.ts +6 -0
- package/dist/types/modes/theme/defaults/index.d.ts +99 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/notifications/html-format.d.ts +11 -0
- package/dist/types/notifications/index.d.ts +149 -1
- package/dist/types/notifications/lifecycle-commands.d.ts +60 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +98 -0
- package/dist/types/notifications/lifecycle-orchestrator.d.ts +144 -0
- package/dist/types/notifications/operator-runtime.d.ts +52 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +35 -0
- package/dist/types/notifications/telegram-daemon.d.ts +114 -16
- package/dist/types/notifications/telegram-reference.d.ts +3 -1
- package/dist/types/notifications/topic-registry.d.ts +12 -9
- package/dist/types/runtime-mcp/types.d.ts +7 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +14 -4
- package/dist/types/session/blob-store.d.ts +25 -0
- package/dist/types/session/session-manager.d.ts +57 -0
- package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/skc-runtime/psmux-detect.d.ts +78 -0
- package/dist/types/skc-runtime/state-renderer.d.ts +5 -0
- package/dist/types/skc-runtime/team-runtime.d.ts +2 -0
- package/dist/types/skc-runtime/tmux-common.d.ts +30 -2
- package/dist/types/skc-runtime/tmux-sessions.d.ts +18 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +37 -1
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +80 -0
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +9 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +31 -0
- package/dist/types/tools/composer-bash-policy.d.ts +14 -0
- package/dist/types/tools/computer-gc.d.ts +23 -0
- package/dist/types/tools/cron.d.ts +36 -61
- package/dist/types/tools/index.d.ts +3 -2
- package/dist/types/tools/resource-gc.d.ts +54 -0
- package/dist/types/utils/changelog.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +6 -3
- package/dist/types/web/scrapers/types.d.ts +5 -0
- package/dist/types/web/scrapers/utils.d.ts +7 -1
- package/dist/types/web/search/index.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +11 -4
- package/package.json +11 -9
- package/scripts/g004-tmux-smoke.ts +100 -0
- package/scripts/g005-daemon-smoke.ts +180 -0
- package/scripts/g011-daemon-path-smoke.ts +153 -0
- package/src/cli/args.ts +0 -1
- package/src/cli/fast-help.ts +29 -2
- package/src/cli/mcp-cli.ts +272 -0
- package/src/cli/plugin-cli.ts +66 -3
- package/src/cli/web-search-cli.ts +5 -0
- package/src/cli.ts +30 -12
- package/src/commands/mcp.ts +117 -0
- package/src/commands/plugin.ts +4 -0
- package/src/commands/session.ts +18 -0
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +62 -8
- package/src/config/model-profiles.ts +3 -4
- package/src/config/model-registry.ts +3 -6
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +88 -103
- package/src/deep-interview/plaintext-gate-guard.ts +94 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/biome.json +1 -1
- package/src/defaults/skc/skills/deep-interview/SKILL.md +7 -6
- package/src/defaults/skc/skills/team/SKILL.md +5 -3
- package/src/defaults/skc/skills/ultragoal/SKILL.md +41 -13
- package/src/export/html/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -12
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/skc-plugins/compiler.ts +351 -0
- package/src/extensibility/skc-plugins/constrained-hooks.ts +170 -0
- package/src/extensibility/skc-plugins/index.ts +9 -0
- package/src/extensibility/skc-plugins/injection.ts +109 -0
- package/src/extensibility/skc-plugins/installer.ts +434 -0
- package/src/extensibility/skc-plugins/loader.ts +3 -1
- package/src/extensibility/skc-plugins/mcp-policy.ts +239 -0
- package/src/extensibility/skc-plugins/observability.ts +84 -0
- package/src/extensibility/skc-plugins/paths.ts +1 -1
- package/src/extensibility/skc-plugins/prompt-appendix.ts +109 -0
- package/src/extensibility/skc-plugins/registry.ts +180 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +234 -0
- package/src/extensibility/skc-plugins/schema.ts +250 -20
- package/src/extensibility/skc-plugins/session-validation.ts +147 -0
- package/src/extensibility/skc-plugins/types.ts +199 -3
- package/src/extensibility/skc-plugins/validation.ts +80 -0
- package/src/extensibility/skills.ts +15 -0
- package/src/goals/tools/goal-tool.ts +14 -1
- package/src/hooks/skill-state.ts +57 -0
- package/src/i18n/messages/de.ts +0 -1
- package/src/i18n/messages/en.ts +0 -1
- package/src/i18n/messages/es.ts +0 -1
- package/src/i18n/messages/fr.ts +0 -1
- package/src/i18n/messages/ja.ts +0 -1
- package/src/i18n/messages/ko.ts +0 -1
- package/src/i18n/messages/zh.ts +0 -1
- package/src/internal-urls/docs-index.generated.ts +14 -11
- package/src/main.ts +14 -3
- package/src/modes/bridge/bridge-mode.ts +11 -0
- package/src/modes/components/assistant-message.ts +49 -1
- package/src/modes/components/custom-editor.ts +2 -0
- package/src/modes/components/footer.ts +2 -3
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +67 -43
- package/src/modes/components/model-selector.ts +65 -12
- package/src/modes/components/settings-defs.ts +1 -2
- package/src/modes/components/settings-selector.ts +2 -7
- package/src/modes/components/status-line/git-utils.ts +25 -0
- package/src/modes/components/status-line.ts +10 -11
- package/src/modes/components/welcome.ts +2 -3
- package/src/modes/controllers/extension-ui-controller.ts +0 -27
- package/src/modes/controllers/selector-controller.ts +59 -11
- package/src/modes/interactive-mode.ts +4 -1
- package/src/modes/shared/agent-wire/scopes.ts +1 -1
- package/src/modes/theme/defaults/gruvbox-dark.json +99 -0
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/theme/theme.ts +0 -4
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/notifications/html-format.ts +38 -0
- package/src/notifications/index.ts +242 -12
- package/src/notifications/lifecycle-commands.ts +238 -0
- package/src/notifications/lifecycle-control-runtime.ts +405 -0
- package/src/notifications/lifecycle-orchestrator.ts +358 -0
- package/src/notifications/operator-runtime.ts +171 -0
- package/src/notifications/rate-limit-pool.ts +19 -0
- package/src/notifications/recent-activity.ts +132 -0
- package/src/notifications/telegram-daemon.ts +778 -257
- package/src/notifications/telegram-reference.ts +25 -7
- package/src/notifications/topic-registry.ts +23 -9
- package/src/prompts/agents/executor.md +2 -2
- package/src/prompts/system/system-prompt.md +2 -2
- package/src/prompts/tools/cron.md +5 -3
- package/src/prompts/tools/read.md +1 -1
- package/src/runtime-mcp/transports/stdio.ts +38 -4
- package/src/runtime-mcp/types.ts +7 -0
- package/src/sdk.ts +162 -10
- package/src/session/agent-session.ts +210 -74
- package/src/session/blob-store.ts +196 -8
- package/src/session/session-manager.ts +762 -12
- package/src/skc-runtime/launch-tmux.ts +68 -19
- package/src/skc-runtime/psmux-detect.ts +239 -0
- package/src/skc-runtime/state-renderer.ts +13 -0
- package/src/skc-runtime/team-runtime.ts +56 -23
- package/src/skc-runtime/tmux-common.ts +88 -4
- package/src/skc-runtime/tmux-sessions.ts +111 -9
- package/src/skc-runtime/ultragoal-guard.ts +192 -8
- package/src/skc-runtime/ultragoal-runtime.ts +296 -16
- package/src/slash-commands/builtin-registry.ts +23 -3
- package/src/slash-commands/helpers/fast-status-report.ts +13 -3
- package/src/slash-commands/helpers/parse.ts +2 -1
- package/src/system-prompt.ts +9 -0
- package/src/task/executor.ts +31 -7
- package/src/task/index.ts +2 -0
- package/src/tools/ask.ts +5 -1
- package/src/tools/bash.ts +9 -0
- package/src/tools/browser/tab-supervisor.ts +86 -2
- package/src/tools/composer-bash-policy.ts +96 -0
- package/src/tools/computer-gc.ts +66 -0
- package/src/tools/computer.ts +2 -0
- package/src/tools/cron.ts +75 -112
- package/src/tools/fetch.ts +18 -2
- package/src/tools/index.ts +5 -9
- package/src/tools/read.ts +25 -55
- package/src/tools/renderers.ts +0 -2
- package/src/tools/resource-gc.ts +291 -0
- package/src/tools/ultragoal-ask-guard.ts +7 -1
- package/src/utils/changelog.ts +8 -0
- package/src/web/insane/url-guard.ts +18 -14
- package/src/web/scrapers/types.ts +143 -45
- package/src/web/scrapers/utils.ts +70 -19
- package/src/web/search/index.ts +1 -0
- package/src/web/search/providers/utils.ts +22 -5
- package/vendor/insane-search/MANIFEST.json +3 -1
- package/vendor/insane-search/engine/__init__.py +14 -0
- package/vendor/insane-search/engine/content_safety.py +151 -0
- package/vendor/insane-search/engine/fetch_chain.py +32 -0
- package/vendor/insane-search/engine/tests/test_u8.py +216 -0
- package/dist/types/tools/inspect-image-renderer.d.ts +0 -26
- package/dist/types/tools/inspect-image.d.ts +0 -31
- package/src/prompts/tools/inspect-image-system.md +0 -20
- package/src/prompts/tools/inspect-image.md +0 -32
- package/src/tools/inspect-image-renderer.ts +0 -103
- package/src/tools/inspect-image.ts +0 -172
|
@@ -50,7 +50,11 @@ import {
|
|
|
50
50
|
type SummaryOptions,
|
|
51
51
|
shouldCompact,
|
|
52
52
|
} from "@sayknow-cli/agent-core/compaction";
|
|
53
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
DEFAULT_PRUNE_CONFIG,
|
|
55
|
+
pruneAssistantToolArguments,
|
|
56
|
+
pruneToolOutputs,
|
|
57
|
+
} from "@sayknow-cli/agent-core/compaction/pruning";
|
|
54
58
|
import type {
|
|
55
59
|
AssistantMessage,
|
|
56
60
|
Context,
|
|
@@ -127,7 +131,7 @@ import {
|
|
|
127
131
|
import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async";
|
|
128
132
|
import { reset as resetCapabilities } from "../capability";
|
|
129
133
|
import type { Rule } from "../capability/rule";
|
|
130
|
-
import { MODEL_ROLE_IDS, type ModelRegistry } from "../config/model-registry";
|
|
134
|
+
import { MODEL_ROLE_IDS, type ModelRegistry, SKC_MODEL_ASSIGNMENT_TARGETS } from "../config/model-registry";
|
|
131
135
|
import {
|
|
132
136
|
extractExplicitThinkingSelector,
|
|
133
137
|
formatModelSelectorValue,
|
|
@@ -246,6 +250,7 @@ import { releaseTabsForOwner } from "../tools/browser/tab-supervisor";
|
|
|
246
250
|
import type { CheckpointState } from "../tools/checkpoint";
|
|
247
251
|
import { outputMeta, wrapToolWithMetaNotice } from "../tools/output-meta";
|
|
248
252
|
import { normalizeLocalScheme, resolveToCwd } from "../tools/path-utils";
|
|
253
|
+
import { registerResourceGcSession } from "../tools/resource-gc";
|
|
249
254
|
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo-write";
|
|
250
255
|
import { ToolAbortError, ToolError } from "../tools/tool-errors";
|
|
251
256
|
import { clampTimeout } from "../tools/tool-timeouts";
|
|
@@ -421,6 +426,13 @@ export interface AgentSessionConfig {
|
|
|
421
426
|
* **MUST NOT** dispose it on their own teardown.
|
|
422
427
|
*/
|
|
423
428
|
ownedAsyncJobManager?: AsyncJobManager;
|
|
429
|
+
/**
|
|
430
|
+
* MCPManager whose lifecycle this session owns (top-level sessions that
|
|
431
|
+
* connected plugin-bundle MCP servers). Only the owned manager is
|
|
432
|
+
* disconnected on dispose; subagents and callers that merely observe the
|
|
433
|
+
* process-global manager **MUST NOT** dispose it on their own teardown.
|
|
434
|
+
*/
|
|
435
|
+
ownedMcpManager?: MCPManager;
|
|
424
436
|
/** Optional fork-context seed used to initialize a child session before its first prompt. */
|
|
425
437
|
forkContextSeed?: ForkContextSeed;
|
|
426
438
|
/** Optional provider state override. Fork-context children should omit this by default. */
|
|
@@ -953,11 +965,14 @@ export class AgentSession {
|
|
|
953
965
|
// Python execution state
|
|
954
966
|
#evalAbortControllers = new Set<AbortController>();
|
|
955
967
|
#evalKernelOwnerId: string;
|
|
968
|
+
/** Idempotent unregister handle for this session's resource-GC registration. */
|
|
969
|
+
#unregisterResourceGc?: () => void;
|
|
956
970
|
/**
|
|
957
971
|
* AsyncJobManager owned by this session (top-level only). Subagents leave
|
|
958
972
|
* this undefined and **MUST NOT** dispose the global instance on teardown.
|
|
959
973
|
*/
|
|
960
974
|
readonly #ownedAsyncJobManager: AsyncJobManager | undefined;
|
|
975
|
+
readonly #ownedMcpManager: MCPManager | undefined;
|
|
961
976
|
#pendingPythonMessages: PythonExecutionMessage[] = [];
|
|
962
977
|
#activeEvalExecutions = new Set<Promise<unknown>>();
|
|
963
978
|
#evalExecutionDisposing = false;
|
|
@@ -1087,6 +1102,16 @@ export class AgentSession {
|
|
|
1087
1102
|
#lastSuccessfulYieldToolCallId: string | undefined = undefined;
|
|
1088
1103
|
#promptGeneration = 0;
|
|
1089
1104
|
#providerSessionState = new Map<string, ProviderSessionState>();
|
|
1105
|
+
/**
|
|
1106
|
+
* Provider keys for which the Anthropic fast-mode auto-fallback fired this
|
|
1107
|
+
* session (the provider rejected `speed:"fast"` and we retried without it).
|
|
1108
|
+
* Provider/API-session scoped — matching the provider's own per-session
|
|
1109
|
+
* `fastModeDisabled` flag — NOT model-keyed. Transient (never persisted): it
|
|
1110
|
+
* suppresses the current-model fast indicator and dedups the one-time warning
|
|
1111
|
+
* WITHOUT mutating the user's intended `serviceTier`, so task subagents still
|
|
1112
|
+
* inherit the intended tier and a different provider still shows fast.
|
|
1113
|
+
*/
|
|
1114
|
+
#fastModeAutoDisabledProviderKeys = new Set<string>();
|
|
1090
1115
|
#hindsightSessionState: HindsightSessionState | undefined = undefined;
|
|
1091
1116
|
readonly rawSseDebugBuffer: RawSseDebugBuffer;
|
|
1092
1117
|
|
|
@@ -1158,9 +1183,19 @@ export class AgentSession {
|
|
|
1158
1183
|
this.sessionManager = config.sessionManager;
|
|
1159
1184
|
this.settings = config.settings;
|
|
1160
1185
|
this.taskDepth = config.taskDepth ?? 0;
|
|
1186
|
+
// Register this session with the process-wide resource GC (idle/RSS browser-tab eviction
|
|
1187
|
+
// + stale screenshot cleanup). Session-keyed so concurrent sessions share one timer safely.
|
|
1188
|
+
const resourceGcSessionId = this.sessionManager.getSessionId();
|
|
1189
|
+
if (resourceGcSessionId) {
|
|
1190
|
+
this.#unregisterResourceGc = registerResourceGcSession({
|
|
1191
|
+
sessionId: resourceGcSessionId,
|
|
1192
|
+
settings: this.settings,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1161
1195
|
// Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
|
|
1162
1196
|
this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
|
|
1163
1197
|
this.#ownedAsyncJobManager = config.ownedAsyncJobManager;
|
|
1198
|
+
this.#ownedMcpManager = config.ownedMcpManager;
|
|
1164
1199
|
this.#scopedModels = config.scopedModels ?? [];
|
|
1165
1200
|
this.#thinkingLevel = config.thinkingLevel;
|
|
1166
1201
|
this.#promptTemplates = config.promptTemplates ?? [];
|
|
@@ -1976,12 +2011,18 @@ export class AgentSession {
|
|
|
1976
2011
|
const currentGrantsAnthropicPriority =
|
|
1977
2012
|
this.serviceTier === "priority" || this.serviceTier === "claude-only";
|
|
1978
2013
|
if (assistantMsg.disabledFeatures?.includes("priority") && currentGrantsAnthropicPriority) {
|
|
1979
|
-
|
|
1980
|
-
this.
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
)
|
|
2014
|
+
// The provider auto-dropped `speed:"fast"` for the current model's
|
|
2015
|
+
// provider this turn. Record a transient, provider-scoped marker
|
|
2016
|
+
// instead of clearing the user's intended tier, so task subagents
|
|
2017
|
+
// still inherit it and a different provider still gets fast mode.
|
|
2018
|
+
// Warn once per provider until the user re-arms with `/fast on`.
|
|
2019
|
+
if (this.#markFastModeAutoDisabledForCurrentModel()) {
|
|
2020
|
+
this.emitNotice(
|
|
2021
|
+
"warning",
|
|
2022
|
+
"Priority/fast mode rejected for this model; retried without it. Fast mode is off for this model until you re-enable it with /fast on.",
|
|
2023
|
+
"priority",
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
1985
2026
|
}
|
|
1986
2027
|
// Resolve TTSR resume gate before checking for new deferred injections.
|
|
1987
2028
|
// Gate on #ttsrAbortPending, not stopReason: a non-TTSR abort (e.g. streaming
|
|
@@ -3240,10 +3281,14 @@ export class AgentSession {
|
|
|
3240
3281
|
AsyncJobManager.setInstance(undefined);
|
|
3241
3282
|
}
|
|
3242
3283
|
}
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3284
|
+
// Only disconnect the MCP manager THIS session owns (top-level sessions that
|
|
3285
|
+
// connected plugin-bundle MCP servers). Subagents and callers that merely
|
|
3286
|
+
// observe the process-global manager must never tear down a manager they do
|
|
3287
|
+
// not own. Mirrors the ownedAsyncJobManager rule above.
|
|
3288
|
+
const ownedMcpManager = this.#ownedMcpManager;
|
|
3289
|
+
if (ownedMcpManager) {
|
|
3290
|
+
await ownedMcpManager.disconnectAll();
|
|
3291
|
+
if (MCPManager.instance() === ownedMcpManager) {
|
|
3247
3292
|
MCPManager.setInstance(undefined);
|
|
3248
3293
|
}
|
|
3249
3294
|
}
|
|
@@ -3252,6 +3297,8 @@ export class AgentSession {
|
|
|
3252
3297
|
// browsers disconnect, headless close gracefully). Scoped by the session id the
|
|
3253
3298
|
// browser tool tagged tabs with, so other live sessions' tabs are untouched.
|
|
3254
3299
|
// No-op when this session opened no tabs. Failure is logged, not thrown.
|
|
3300
|
+
this.#unregisterResourceGc?.();
|
|
3301
|
+
this.#unregisterResourceGc = undefined;
|
|
3255
3302
|
await releaseTabsForOwner(this.sessionManager.getSessionId()).catch((error: unknown) =>
|
|
3256
3303
|
logger.warn("session dispose: releaseTabsForOwner failed", { error }),
|
|
3257
3304
|
);
|
|
@@ -3291,6 +3338,8 @@ export class AgentSession {
|
|
|
3291
3338
|
async disposeChildSubprocesses(timeoutMs = SIGNAL_TEARDOWN_TIMEOUT_MS): Promise<void> {
|
|
3292
3339
|
const sessionId = this.sessionManager.getSessionId();
|
|
3293
3340
|
const kernelOwnerId = this.#evalKernelOwnerId;
|
|
3341
|
+
this.#unregisterResourceGc?.();
|
|
3342
|
+
this.#unregisterResourceGc = undefined;
|
|
3294
3343
|
const work = Promise.allSettled([
|
|
3295
3344
|
// kill:true so a forced exit also reaps spawned-app Chrome we own (headless
|
|
3296
3345
|
// always closes; connected/attached browsers only disconnect — never killed).
|
|
@@ -5198,6 +5247,19 @@ export class AgentSession {
|
|
|
5198
5247
|
attribution: "user",
|
|
5199
5248
|
timestamp: Date.now(),
|
|
5200
5249
|
});
|
|
5250
|
+
// A live agent loop polls the steering queue at every tool/turn boundary
|
|
5251
|
+
// and consumes this message on its own. But when a steer is queued while no
|
|
5252
|
+
// loop is actively running — e.g. the session still reports busy only
|
|
5253
|
+
// because a finished prompt is unwinding (deferred agent_end / post-prompt
|
|
5254
|
+
// work) — nothing delivers it until the next explicit prompt or a
|
|
5255
|
+
// user-interrupt abort, so it stalls until the user presses Esc. Schedule a
|
|
5256
|
+
// continue so the steer is delivered promptly. A live loop (or an
|
|
5257
|
+
// already-drained queue) makes the scheduled continue a no-op.
|
|
5258
|
+
if (this.#canAutoContinueForSteer()) {
|
|
5259
|
+
this.#scheduleAgentContinue({
|
|
5260
|
+
shouldContinue: () => this.#canAutoContinueForSteer() && this.agent.hasQueuedSteering(),
|
|
5261
|
+
});
|
|
5262
|
+
}
|
|
5201
5263
|
}
|
|
5202
5264
|
|
|
5203
5265
|
/**
|
|
@@ -5242,6 +5304,21 @@ export class AgentSession {
|
|
|
5242
5304
|
return last?.role === "assistant";
|
|
5243
5305
|
}
|
|
5244
5306
|
|
|
5307
|
+
/**
|
|
5308
|
+
* Gate for idle / winding-down steer auto-continue. Unlike the follow-up gate
|
|
5309
|
+
* this checks `agent.state.isStreaming` (a live agent loop) rather than the
|
|
5310
|
+
* public `isStreaming` (which stays true while a finished prompt unwinds), so a
|
|
5311
|
+
* steer queued during the unwind window is still delivered. A live loop returns
|
|
5312
|
+
* false here because it polls the steering queue itself.
|
|
5313
|
+
*/
|
|
5314
|
+
#canAutoContinueForSteer(): boolean {
|
|
5315
|
+
if (this.agent.state.isStreaming) return false;
|
|
5316
|
+
if (this.isRetrying) return false;
|
|
5317
|
+
const messages = this.agent.state.messages;
|
|
5318
|
+
const last = messages[messages.length - 1];
|
|
5319
|
+
return last?.role === "assistant";
|
|
5320
|
+
}
|
|
5321
|
+
|
|
5245
5322
|
queueDeferredMessage(message: CustomMessage): void {
|
|
5246
5323
|
this.#queueHiddenNextTurnMessage(message, true);
|
|
5247
5324
|
}
|
|
@@ -5604,7 +5681,7 @@ export class AgentSession {
|
|
|
5604
5681
|
}
|
|
5605
5682
|
|
|
5606
5683
|
#syncTodoPhasesFromBranch(): void {
|
|
5607
|
-
const phases = getLatestTodoPhasesFromEntries(this.sessionManager.
|
|
5684
|
+
const phases = getLatestTodoPhasesFromEntries(this.sessionManager.getActivePathEntriesCanonical());
|
|
5608
5685
|
// Strip completed/abandoned tasks — they were done in a previous run,
|
|
5609
5686
|
// so they have no bearing on progress tracking for the new turn.
|
|
5610
5687
|
for (const phase of phases) {
|
|
@@ -5613,6 +5690,42 @@ export class AgentSession {
|
|
|
5613
5690
|
this.setTodoPhases(phases.filter(p => p.tasks.length > 0));
|
|
5614
5691
|
}
|
|
5615
5692
|
|
|
5693
|
+
async #applyCompactionPostAppend(
|
|
5694
|
+
compactionEntryId: string,
|
|
5695
|
+
firstKeptEntryId: string,
|
|
5696
|
+
fromExtension?: boolean,
|
|
5697
|
+
): Promise<CompactionEntry | undefined> {
|
|
5698
|
+
const eviction = this.sessionManager.evictCompactedContent(firstKeptEntryId, compactionEntryId);
|
|
5699
|
+
if (eviction.evictedEntries > 0) await this.sessionManager.rewriteEntries();
|
|
5700
|
+
const sessionContext = this.buildDisplaySessionContext();
|
|
5701
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
5702
|
+
this.#syncTodoPhasesFromBranch();
|
|
5703
|
+
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
5704
|
+
|
|
5705
|
+
// Get the saved compaction entry for the hook without materializing all entries.
|
|
5706
|
+
const savedCompactionEntry = this.sessionManager.getEntryForFidelity(compactionEntryId) as
|
|
5707
|
+
| CompactionEntry
|
|
5708
|
+
| undefined;
|
|
5709
|
+
|
|
5710
|
+
if (this.#extensionRunner && savedCompactionEntry) {
|
|
5711
|
+
await this.#extensionRunner.emit({
|
|
5712
|
+
type: "session_compact",
|
|
5713
|
+
compactionEntry: savedCompactionEntry,
|
|
5714
|
+
fromExtension: fromExtension ?? false,
|
|
5715
|
+
});
|
|
5716
|
+
}
|
|
5717
|
+
|
|
5718
|
+
return savedCompactionEntry;
|
|
5719
|
+
}
|
|
5720
|
+
|
|
5721
|
+
async applyCompactionPostAppendForTests(
|
|
5722
|
+
compactionEntryId: string,
|
|
5723
|
+
firstKeptEntryId: string,
|
|
5724
|
+
fromExtension?: boolean,
|
|
5725
|
+
): Promise<CompactionEntry | undefined> {
|
|
5726
|
+
return this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
|
|
5727
|
+
}
|
|
5728
|
+
|
|
5616
5729
|
#cloneTodoPhases(phases: TodoPhase[]): TodoPhase[] {
|
|
5617
5730
|
return phases.map(phase => ({
|
|
5618
5731
|
name: phase.name,
|
|
@@ -6242,33 +6355,81 @@ export class AgentSession {
|
|
|
6242
6355
|
return resolveServiceTier(this.#subagentServiceTier(), provider) === "priority";
|
|
6243
6356
|
}
|
|
6244
6357
|
|
|
6358
|
+
/**
|
|
6359
|
+
* Provider/API-session key used to scope the fast-mode auto-disable marker.
|
|
6360
|
+
* Mirrors the provider's own per-session `fastModeDisabled` scope (the key in
|
|
6361
|
+
* {@link #providerSessionState} cleared by `clearAnthropicFastModeFallback`),
|
|
6362
|
+
* so the marker is provider-scoped, never model-keyed. Returns `undefined`
|
|
6363
|
+
* when no model/provider is selected.
|
|
6364
|
+
*/
|
|
6365
|
+
#fastModeProviderKey(provider: string | undefined = this.model?.provider): string | undefined {
|
|
6366
|
+
return provider;
|
|
6367
|
+
}
|
|
6368
|
+
|
|
6369
|
+
/**
|
|
6370
|
+
* Record that the current model's provider had fast mode auto-dropped this
|
|
6371
|
+
* session. Returns `true` only when the provider key is newly marked, so the
|
|
6372
|
+
* caller emits the one-time warning exactly once per provider until re-arm.
|
|
6373
|
+
*/
|
|
6374
|
+
#markFastModeAutoDisabledForCurrentModel(): boolean {
|
|
6375
|
+
const key = this.#fastModeProviderKey();
|
|
6376
|
+
if (key === undefined) return false;
|
|
6377
|
+
if (this.#fastModeAutoDisabledProviderKeys.has(key)) return false;
|
|
6378
|
+
this.#fastModeAutoDisabledProviderKeys.add(key);
|
|
6379
|
+
return true;
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6382
|
+
/** True when `provider`'s fast mode was auto-disabled this session. */
|
|
6383
|
+
#isFastModeAutoDisabledForProvider(provider?: string): boolean {
|
|
6384
|
+
const key = this.#fastModeProviderKey(provider);
|
|
6385
|
+
return key !== undefined && this.#fastModeAutoDisabledProviderKeys.has(key);
|
|
6386
|
+
}
|
|
6387
|
+
|
|
6388
|
+
/**
|
|
6389
|
+
* Re-arm fast mode after an auto-disable: clear the provider's sticky
|
|
6390
|
+
* `fastModeDisabled` fallback flag and the session auto-disable markers so the
|
|
6391
|
+
* next request carries `speed:"fast"` again and a future rejection can warn
|
|
6392
|
+
* once more. Called on explicit re-enable (`/fast on`, re-arming tier change),
|
|
6393
|
+
* never by the transient Q1 auto-disable path.
|
|
6394
|
+
*/
|
|
6395
|
+
#rearmFastMode(): void {
|
|
6396
|
+
clearAnthropicFastModeFallback(this.#providerSessionState);
|
|
6397
|
+
this.#fastModeAutoDisabledProviderKeys.clear();
|
|
6398
|
+
}
|
|
6399
|
+
|
|
6245
6400
|
/**
|
|
6246
6401
|
* True when the configured `serviceTier` resolves to `"priority"` for the
|
|
6247
|
-
* *currently selected model's provider
|
|
6248
|
-
* that
|
|
6249
|
-
*
|
|
6402
|
+
* *currently selected model's provider* AND fast mode was not auto-disabled
|
|
6403
|
+
* for that provider this session. This is the current-model EFFECTIVE
|
|
6404
|
+
* predicate (what the next request actually does); use {@link isFastForProvider}
|
|
6405
|
+
* for pure configured intent (e.g. subagent/`modelRoles` display rows).
|
|
6250
6406
|
*/
|
|
6251
6407
|
isFastModeActive(): boolean {
|
|
6252
|
-
|
|
6408
|
+
const provider = this.model?.provider;
|
|
6409
|
+
return this.isFastForProvider(provider) && !this.#isFastModeAutoDisabledForProvider(provider);
|
|
6253
6410
|
}
|
|
6254
6411
|
|
|
6255
6412
|
setServiceTier(serviceTier: ServiceTier | undefined): void {
|
|
6256
|
-
|
|
6257
|
-
//
|
|
6258
|
-
//
|
|
6259
|
-
//
|
|
6260
|
-
//
|
|
6261
|
-
// and the warning notice fires every turn.
|
|
6413
|
+
// Re-arming a priority-granting tier always clears the per-session
|
|
6414
|
+
// auto-fallback sticky disable AND the auto-disable markers so the next
|
|
6415
|
+
// request carries `speed: "fast"` again — even when the tier is unchanged
|
|
6416
|
+
// (re-selecting the same tier is a deliberate re-arm), and before the
|
|
6417
|
+
// no-op early-return below.
|
|
6262
6418
|
if (serviceTier === "priority" || serviceTier === "claude-only") {
|
|
6263
|
-
|
|
6419
|
+
this.#rearmFastMode();
|
|
6264
6420
|
}
|
|
6421
|
+
if (this.serviceTier === serviceTier) return;
|
|
6265
6422
|
this.agent.serviceTier = serviceTier;
|
|
6266
6423
|
this.sessionManager.appendServiceTierChange(serviceTier ?? null);
|
|
6267
6424
|
}
|
|
6268
6425
|
|
|
6269
6426
|
setFastMode(enabled: boolean): void {
|
|
6270
6427
|
if (enabled && this.isFastModeEnabled()) {
|
|
6271
|
-
//
|
|
6428
|
+
// Intent already grants fast mode under some scope — keep the user's
|
|
6429
|
+
// scoped value but still re-arm, so an explicit `/fast on` after a
|
|
6430
|
+
// provider auto-disable actually clears the sticky fallback + markers
|
|
6431
|
+
// (otherwise it is a silent no-op). No history append: intent is unchanged.
|
|
6432
|
+
this.#rearmFastMode();
|
|
6272
6433
|
return;
|
|
6273
6434
|
}
|
|
6274
6435
|
this.setServiceTier(enabled ? "priority" : undefined);
|
|
@@ -6326,19 +6487,23 @@ export class AgentSession {
|
|
|
6326
6487
|
async #pruneToolOutputs(): Promise<{ prunedCount: number; tokensSaved: number } | undefined> {
|
|
6327
6488
|
const branchEntries = this.sessionManager.getBranch();
|
|
6328
6489
|
const result = pruneToolOutputs(branchEntries, DEFAULT_PRUNE_CONFIG);
|
|
6329
|
-
|
|
6490
|
+
const argumentResult = pruneAssistantToolArguments(branchEntries, DEFAULT_PRUNE_CONFIG);
|
|
6491
|
+
const tokensSaved = result.tokensSaved + argumentResult.argumentTokensSaved;
|
|
6492
|
+
const prunedCount = result.prunedCount + argumentResult.argumentPrunedCount;
|
|
6493
|
+
if (prunedCount === 0) {
|
|
6330
6494
|
return undefined;
|
|
6331
6495
|
}
|
|
6332
6496
|
|
|
6333
6497
|
// getBranch() returns materialized copies for blob-externalized entries, so
|
|
6334
6498
|
// the pruning mutations must be written back into the canonical store.
|
|
6335
|
-
|
|
6499
|
+
const combined = [...result.prunedEntries, ...argumentResult.prunedEntries];
|
|
6500
|
+
this.sessionManager.applyEntryMessageUpdates(combined);
|
|
6336
6501
|
await this.sessionManager.rewriteEntries();
|
|
6337
6502
|
const sessionContext = this.buildDisplaySessionContext();
|
|
6338
6503
|
this.agent.replaceMessages(sessionContext.messages);
|
|
6339
6504
|
this.#syncTodoPhasesFromBranch();
|
|
6340
6505
|
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
6341
|
-
return
|
|
6506
|
+
return { prunedCount, tokensSaved };
|
|
6342
6507
|
}
|
|
6343
6508
|
|
|
6344
6509
|
/**
|
|
@@ -6456,7 +6621,7 @@ export class AgentSession {
|
|
|
6456
6621
|
throw new CompactionCancelledError();
|
|
6457
6622
|
}
|
|
6458
6623
|
|
|
6459
|
-
this.sessionManager.appendCompaction(
|
|
6624
|
+
const compactionEntryId = this.sessionManager.appendCompaction(
|
|
6460
6625
|
summary,
|
|
6461
6626
|
shortSummary,
|
|
6462
6627
|
firstKeptEntryId,
|
|
@@ -6465,24 +6630,7 @@ export class AgentSession {
|
|
|
6465
6630
|
fromExtension,
|
|
6466
6631
|
preserveData,
|
|
6467
6632
|
);
|
|
6468
|
-
|
|
6469
|
-
const sessionContext = this.buildDisplaySessionContext();
|
|
6470
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
6471
|
-
this.#syncTodoPhasesFromBranch();
|
|
6472
|
-
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
6473
|
-
|
|
6474
|
-
// Get the saved compaction entry for the hook
|
|
6475
|
-
const savedCompactionEntry = newEntries.find(e => e.type === "compaction" && e.summary === summary) as
|
|
6476
|
-
| CompactionEntry
|
|
6477
|
-
| undefined;
|
|
6478
|
-
|
|
6479
|
-
if (this.#extensionRunner && savedCompactionEntry) {
|
|
6480
|
-
await this.#extensionRunner.emit({
|
|
6481
|
-
type: "session_compact",
|
|
6482
|
-
compactionEntry: savedCompactionEntry,
|
|
6483
|
-
fromExtension,
|
|
6484
|
-
});
|
|
6485
|
-
}
|
|
6633
|
+
await this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
|
|
6486
6634
|
|
|
6487
6635
|
const compactionResult: CompactionResult = {
|
|
6488
6636
|
summary,
|
|
@@ -7480,11 +7628,14 @@ export class AgentSession {
|
|
|
7480
7628
|
availableModels: Model[],
|
|
7481
7629
|
currentModel: Model | undefined,
|
|
7482
7630
|
): ResolvedModelRoleValue {
|
|
7631
|
+
const target = SKC_MODEL_ASSIGNMENT_TARGETS[role as keyof typeof SKC_MODEL_ASSIGNMENT_TARGETS];
|
|
7483
7632
|
const roleModelStr =
|
|
7484
|
-
|
|
7485
|
-
?
|
|
7486
|
-
|
|
7487
|
-
|
|
7633
|
+
target?.settingsPath === "task.agentModelOverrides"
|
|
7634
|
+
? this.settings.get("task.agentModelOverrides")[role]
|
|
7635
|
+
: role === "default"
|
|
7636
|
+
? (this.settings.getModelRole("default") ??
|
|
7637
|
+
(currentModel ? `${currentModel.provider}/${currentModel.id}` : undefined))
|
|
7638
|
+
: this.settings.getModelRole(role);
|
|
7488
7639
|
|
|
7489
7640
|
if (!roleModelStr) {
|
|
7490
7641
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
@@ -7969,7 +8120,7 @@ export class AgentSession {
|
|
|
7969
8120
|
return;
|
|
7970
8121
|
}
|
|
7971
8122
|
|
|
7972
|
-
this.sessionManager.appendCompaction(
|
|
8123
|
+
const compactionEntryId = this.sessionManager.appendCompaction(
|
|
7973
8124
|
summary,
|
|
7974
8125
|
shortSummary,
|
|
7975
8126
|
firstKeptEntryId,
|
|
@@ -7978,24 +8129,7 @@ export class AgentSession {
|
|
|
7978
8129
|
fromExtension,
|
|
7979
8130
|
preserveData,
|
|
7980
8131
|
);
|
|
7981
|
-
|
|
7982
|
-
const sessionContext = this.buildDisplaySessionContext();
|
|
7983
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
7984
|
-
this.#syncTodoPhasesFromBranch();
|
|
7985
|
-
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
7986
|
-
|
|
7987
|
-
// Get the saved compaction entry for the hook
|
|
7988
|
-
const savedCompactionEntry = newEntries.find(e => e.type === "compaction" && e.summary === summary) as
|
|
7989
|
-
| CompactionEntry
|
|
7990
|
-
| undefined;
|
|
7991
|
-
|
|
7992
|
-
if (this.#extensionRunner && savedCompactionEntry) {
|
|
7993
|
-
await this.#extensionRunner.emit({
|
|
7994
|
-
type: "session_compact",
|
|
7995
|
-
compactionEntry: savedCompactionEntry,
|
|
7996
|
-
fromExtension,
|
|
7997
|
-
});
|
|
7998
|
-
}
|
|
8132
|
+
await this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
|
|
7999
8133
|
|
|
8000
8134
|
const result: CompactionResult = {
|
|
8001
8135
|
summary,
|
|
@@ -9613,7 +9747,7 @@ export class AgentSession {
|
|
|
9613
9747
|
cancelled: boolean;
|
|
9614
9748
|
}> {
|
|
9615
9749
|
const previousSessionFile = this.sessionFile;
|
|
9616
|
-
const selectedEntry = this.sessionManager.
|
|
9750
|
+
const selectedEntry = this.sessionManager.getEntryForFidelity(entryId);
|
|
9617
9751
|
|
|
9618
9752
|
if (selectedEntry?.type !== "message" || selectedEntry.message.role !== "user") {
|
|
9619
9753
|
throw new Error("Invalid entry ID for branching");
|
|
@@ -9711,7 +9845,7 @@ export class AgentSession {
|
|
|
9711
9845
|
throw new Error("No model available for summarization");
|
|
9712
9846
|
}
|
|
9713
9847
|
|
|
9714
|
-
const targetEntry = this.sessionManager.
|
|
9848
|
+
const targetEntry = this.sessionManager.getEntryForFidelity(targetId);
|
|
9715
9849
|
if (!targetEntry) {
|
|
9716
9850
|
throw new Error(`Entry ${targetId} not found`);
|
|
9717
9851
|
}
|
|
@@ -9867,9 +10001,11 @@ export class AgentSession {
|
|
|
9867
10001
|
|
|
9868
10002
|
for (const entry of entries) {
|
|
9869
10003
|
if (entry.type !== "message") continue;
|
|
9870
|
-
|
|
10004
|
+
const fidelityEntry = this.sessionManager.getEntryForFidelity(entry.id);
|
|
10005
|
+
if (fidelityEntry?.type !== "message") continue;
|
|
10006
|
+
if (fidelityEntry.message.role !== "user") continue;
|
|
9871
10007
|
|
|
9872
|
-
const text = this.#extractUserMessageText(
|
|
10008
|
+
const text = this.#extractUserMessageText(fidelityEntry.message.content);
|
|
9873
10009
|
if (text) {
|
|
9874
10010
|
result.push({ entryId: entry.id, text });
|
|
9875
10011
|
}
|