@sayknow-cli/coding-agent 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/bin/skc.js +4 -0
  2. package/dist/types/cli/mcp-cli.d.ts +25 -0
  3. package/dist/types/cli/notify-cli.d.ts +2 -0
  4. package/dist/types/cli/plugin-cli.d.ts +2 -0
  5. package/dist/types/cli.d.ts +6 -0
  6. package/dist/types/commands/mcp.d.ts +70 -0
  7. package/dist/types/commands/plugin.d.ts +6 -0
  8. package/dist/types/commands/session.d.ts +6 -0
  9. package/dist/types/config/keybindings.d.ts +2 -2
  10. package/dist/types/config/model-profile-activation.d.ts +8 -1
  11. package/dist/types/config/settings-schema.d.ts +39 -2
  12. package/dist/types/deep-interview/plaintext-gate-guard.d.ts +11 -0
  13. package/dist/types/defaults/skc-defaults.d.ts +2 -13
  14. package/dist/types/extensibility/shared-events.d.ts +1 -0
  15. package/dist/types/extensibility/skc-plugins/compiler.d.ts +19 -0
  16. package/dist/types/extensibility/skc-plugins/constrained-hooks.d.ts +29 -0
  17. package/dist/types/extensibility/skc-plugins/index.d.ts +9 -0
  18. package/dist/types/extensibility/skc-plugins/injection.d.ts +9 -0
  19. package/dist/types/extensibility/skc-plugins/installer.d.ts +13 -0
  20. package/dist/types/extensibility/skc-plugins/mcp-policy.d.ts +26 -0
  21. package/dist/types/extensibility/skc-plugins/observability.d.ts +27 -0
  22. package/dist/types/extensibility/skc-plugins/prompt-appendix.d.ts +16 -0
  23. package/dist/types/extensibility/skc-plugins/registry.d.ts +32 -0
  24. package/dist/types/extensibility/skc-plugins/runtime-adapters.d.ts +64 -0
  25. package/dist/types/extensibility/skc-plugins/session-validation.d.ts +42 -0
  26. package/dist/types/extensibility/skc-plugins/types.d.ts +158 -2
  27. package/dist/types/extensibility/skc-plugins/validation.d.ts +8 -1
  28. package/dist/types/lsp/types.d.ts +2 -0
  29. package/dist/types/main.d.ts +2 -0
  30. package/dist/types/modes/components/custom-editor.d.ts +1 -1
  31. package/dist/types/modes/components/model-selector.d.ts +8 -0
  32. package/dist/types/modes/components/status-line/git-utils.d.ts +6 -0
  33. package/dist/types/modes/theme/defaults/index.d.ts +99 -0
  34. package/dist/types/notifications/attachment-registry.d.ts +17 -0
  35. package/dist/types/notifications/chat-adapters.d.ts +9 -0
  36. package/dist/types/notifications/config.d.ts +9 -1
  37. package/dist/types/notifications/engine.d.ts +59 -0
  38. package/dist/types/notifications/html-format.d.ts +11 -0
  39. package/dist/types/notifications/index.d.ts +149 -1
  40. package/dist/types/notifications/lifecycle-commands.d.ts +60 -0
  41. package/dist/types/notifications/lifecycle-control-runtime.d.ts +98 -0
  42. package/dist/types/notifications/lifecycle-orchestrator.d.ts +144 -0
  43. package/dist/types/notifications/managed-daemon.d.ts +48 -0
  44. package/dist/types/notifications/operator-runtime.d.ts +52 -0
  45. package/dist/types/notifications/rate-limit-pool.d.ts +2 -0
  46. package/dist/types/notifications/recent-activity.d.ts +35 -0
  47. package/dist/types/notifications/telegram-daemon.d.ts +133 -16
  48. package/dist/types/notifications/telegram-reference.d.ts +3 -1
  49. package/dist/types/notifications/threaded-inbound.d.ts +19 -0
  50. package/dist/types/notifications/threaded-render.d.ts +6 -1
  51. package/dist/types/notifications/topic-registry.d.ts +12 -9
  52. package/dist/types/runtime-mcp/types.d.ts +7 -0
  53. package/dist/types/sdk.d.ts +2 -0
  54. package/dist/types/session/agent-session.d.ts +16 -4
  55. package/dist/types/session/blob-store.d.ts +25 -0
  56. package/dist/types/session/session-manager.d.ts +57 -0
  57. package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
  58. package/dist/types/skc-runtime/psmux-detect.d.ts +78 -0
  59. package/dist/types/skc-runtime/ralplan-runtime.d.ts +1 -1
  60. package/dist/types/skc-runtime/team-runtime.d.ts +2 -0
  61. package/dist/types/skc-runtime/tmux-common.d.ts +30 -2
  62. package/dist/types/skc-runtime/tmux-sessions.d.ts +18 -0
  63. package/dist/types/slash-commands/helpers/fast-status-report.d.ts +6 -0
  64. package/dist/types/system-prompt.d.ts +2 -0
  65. package/dist/types/task/executor.d.ts +9 -1
  66. package/dist/types/tools/composer-bash-policy.d.ts +14 -0
  67. package/dist/types/tools/fetch.d.ts +23 -0
  68. package/dist/types/tools/index.d.ts +4 -1
  69. package/dist/types/tools/telegram-send.d.ts +32 -0
  70. package/dist/types/utils/changelog.d.ts +1 -0
  71. package/dist/types/web/insane/bridge.d.ts +103 -0
  72. package/dist/types/web/insane/url-guard.d.ts +25 -0
  73. package/dist/types/web/scrapers/types.d.ts +5 -0
  74. package/dist/types/web/scrapers/utils.d.ts +7 -1
  75. package/dist/types/web/search/provider.d.ts +18 -1
  76. package/dist/types/web/search/providers/insane.d.ts +53 -0
  77. package/dist/types/web/search/providers/text-citations.d.ts +23 -0
  78. package/dist/types/web/search/types.d.ts +12 -4
  79. package/package.json +14 -10
  80. package/scripts/g004-tmux-smoke.ts +100 -0
  81. package/scripts/g005-daemon-smoke.ts +180 -0
  82. package/scripts/g011-daemon-path-smoke.ts +153 -0
  83. package/scripts/verify-insane-vendor.ts +132 -0
  84. package/src/cli/args.ts +1 -1
  85. package/src/cli/fast-help.ts +30 -2
  86. package/src/cli/mcp-cli.ts +272 -0
  87. package/src/cli/notify-cli.ts +152 -5
  88. package/src/cli/plugin-cli.ts +66 -3
  89. package/src/cli.ts +30 -12
  90. package/src/commands/mcp.ts +117 -0
  91. package/src/commands/plugin.ts +4 -0
  92. package/src/commands/session.ts +18 -0
  93. package/src/commands/team.ts +1 -1
  94. package/src/config/keybindings.ts +2 -2
  95. package/src/config/model-profile-activation.ts +55 -7
  96. package/src/config/settings-schema.ts +30 -1
  97. package/src/deep-interview/plaintext-gate-guard.ts +94 -0
  98. package/src/defaults/skc/extensions/grok-cli-vendor/biome.json +1 -1
  99. package/src/defaults/skc/skills/deep-interview/SKILL.md +7 -6
  100. package/src/defaults/skc/skills/ralplan/SKILL.md +11 -4
  101. package/src/defaults/skc/skills/team/SKILL.md +5 -3
  102. package/src/defaults/skc/skills/ultragoal/SKILL.md +41 -13
  103. package/src/defaults/skc-defaults.ts +2 -27
  104. package/src/export/html/index.ts +2 -2
  105. package/src/extensibility/extensions/runner.ts +1 -0
  106. package/src/extensibility/shared-events.ts +1 -0
  107. package/src/extensibility/skc-plugins/compiler.ts +351 -0
  108. package/src/extensibility/skc-plugins/constrained-hooks.ts +170 -0
  109. package/src/extensibility/skc-plugins/index.ts +9 -0
  110. package/src/extensibility/skc-plugins/injection.ts +109 -0
  111. package/src/extensibility/skc-plugins/installer.ts +434 -0
  112. package/src/extensibility/skc-plugins/loader.ts +3 -1
  113. package/src/extensibility/skc-plugins/mcp-policy.ts +239 -0
  114. package/src/extensibility/skc-plugins/observability.ts +84 -0
  115. package/src/extensibility/skc-plugins/paths.ts +1 -1
  116. package/src/extensibility/skc-plugins/prompt-appendix.ts +109 -0
  117. package/src/extensibility/skc-plugins/registry.ts +180 -0
  118. package/src/extensibility/skc-plugins/runtime-adapters.ts +234 -0
  119. package/src/extensibility/skc-plugins/schema.ts +250 -20
  120. package/src/extensibility/skc-plugins/session-validation.ts +147 -0
  121. package/src/extensibility/skc-plugins/types.ts +199 -3
  122. package/src/extensibility/skc-plugins/validation.ts +80 -0
  123. package/src/extensibility/skills.ts +15 -0
  124. package/src/hooks/skill-state.ts +57 -0
  125. package/src/internal-urls/docs-index.generated.ts +17 -13
  126. package/src/lsp/config.ts +16 -3
  127. package/src/lsp/defaults.json +7 -0
  128. package/src/lsp/types.ts +2 -0
  129. package/src/main.ts +14 -3
  130. package/src/modes/bridge/bridge-mode.ts +11 -0
  131. package/src/modes/components/assistant-message.ts +49 -1
  132. package/src/modes/components/custom-editor.ts +2 -0
  133. package/src/modes/components/footer.ts +2 -3
  134. package/src/modes/components/hook-editor.ts +1 -1
  135. package/src/modes/components/hook-selector.ts +67 -43
  136. package/src/modes/components/model-selector.ts +56 -11
  137. package/src/modes/components/status-line/git-utils.ts +25 -0
  138. package/src/modes/components/status-line.ts +10 -11
  139. package/src/modes/components/welcome.ts +2 -3
  140. package/src/modes/controllers/event-controller.ts +15 -0
  141. package/src/modes/controllers/extension-ui-controller.ts +0 -27
  142. package/src/modes/controllers/selector-controller.ts +53 -11
  143. package/src/modes/interactive-mode.ts +50 -3
  144. package/src/modes/shared/agent-wire/scopes.ts +1 -1
  145. package/src/modes/theme/defaults/gruvbox-dark.json +99 -0
  146. package/src/modes/theme/defaults/index.ts +2 -0
  147. package/src/modes/utils/context-usage.ts +2 -2
  148. package/src/modes/utils/hotkeys-markdown.ts +1 -1
  149. package/src/notifications/attachment-registry.ts +23 -0
  150. package/src/notifications/chat-adapters.ts +147 -0
  151. package/src/notifications/config.ts +23 -2
  152. package/src/notifications/engine.ts +100 -0
  153. package/src/notifications/html-format.ts +38 -0
  154. package/src/notifications/index.ts +417 -45
  155. package/src/notifications/lifecycle-commands.ts +238 -0
  156. package/src/notifications/lifecycle-control-runtime.ts +405 -0
  157. package/src/notifications/lifecycle-orchestrator.ts +358 -0
  158. package/src/notifications/managed-daemon.ts +163 -0
  159. package/src/notifications/operator-runtime.ts +171 -0
  160. package/src/notifications/rate-limit-pool.ts +19 -0
  161. package/src/notifications/recent-activity.ts +132 -0
  162. package/src/notifications/telegram-daemon.ts +984 -242
  163. package/src/notifications/telegram-reference.ts +25 -7
  164. package/src/notifications/threaded-inbound.ts +60 -4
  165. package/src/notifications/threaded-render.ts +20 -2
  166. package/src/notifications/topic-registry.ts +23 -9
  167. package/src/prompts/agents/executor.md +2 -2
  168. package/src/runtime-mcp/transports/stdio.ts +38 -4
  169. package/src/runtime-mcp/types.ts +7 -0
  170. package/src/sdk.ts +157 -10
  171. package/src/session/agent-session.ts +248 -125
  172. package/src/session/blob-store.ts +196 -8
  173. package/src/session/session-manager.ts +762 -12
  174. package/src/skc-runtime/launch-tmux.ts +85 -22
  175. package/src/skc-runtime/ledger-event-renderer.ts +1 -0
  176. package/src/skc-runtime/psmux-detect.ts +239 -0
  177. package/src/skc-runtime/ralplan-runtime.ts +2 -2
  178. package/src/skc-runtime/team-runtime.ts +56 -23
  179. package/src/skc-runtime/tmux-common.ts +88 -4
  180. package/src/skc-runtime/tmux-sessions.ts +111 -9
  181. package/src/skc-runtime/ultragoal-guard.ts +25 -8
  182. package/src/skc-runtime/ultragoal-runtime.ts +75 -15
  183. package/src/skc-runtime/workflow-manifest.generated.json +29 -0
  184. package/src/skc-runtime/workflow-manifest.ts +7 -2
  185. package/src/slash-commands/builtin-registry.ts +23 -3
  186. package/src/slash-commands/helpers/fast-status-report.ts +13 -3
  187. package/src/slash-commands/helpers/parse.ts +2 -1
  188. package/src/system-prompt.ts +9 -0
  189. package/src/task/executor.ts +31 -7
  190. package/src/task/index.ts +2 -0
  191. package/src/tools/ask.ts +5 -1
  192. package/src/tools/bash.ts +9 -0
  193. package/src/tools/composer-bash-policy.ts +96 -0
  194. package/src/tools/fetch.ts +94 -1
  195. package/src/tools/index.ts +6 -1
  196. package/src/tools/telegram-send.ts +137 -0
  197. package/src/utils/changelog.ts +8 -0
  198. package/src/web/insane/bridge.ts +350 -0
  199. package/src/web/insane/url-guard.ts +159 -0
  200. package/src/web/scrapers/types.ts +143 -45
  201. package/src/web/scrapers/utils.ts +70 -19
  202. package/src/web/search/provider.ts +77 -18
  203. package/src/web/search/providers/anthropic.ts +70 -3
  204. package/src/web/search/providers/codex.ts +1 -119
  205. package/src/web/search/providers/gemini.ts +99 -0
  206. package/src/web/search/providers/insane.ts +551 -0
  207. package/src/web/search/providers/openai-compatible.ts +66 -32
  208. package/src/web/search/providers/text-citations.ts +111 -0
  209. package/src/web/search/types.ts +13 -2
  210. package/vendor/insane-search/LICENSE +21 -0
  211. package/vendor/insane-search/MANIFEST.json +24 -0
  212. package/vendor/insane-search/engine/__init__.py +23 -0
  213. package/vendor/insane-search/engine/__main__.py +128 -0
  214. package/vendor/insane-search/engine/bias_check.py +183 -0
  215. package/vendor/insane-search/engine/executor.py +254 -0
  216. package/vendor/insane-search/engine/fetch_chain.py +725 -0
  217. package/vendor/insane-search/engine/learning.py +175 -0
  218. package/vendor/insane-search/engine/phase0.py +214 -0
  219. package/vendor/insane-search/engine/safety.py +91 -0
  220. package/vendor/insane-search/engine/templates/package.json +11 -0
  221. package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
  222. package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
  223. package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
  224. package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
  225. package/vendor/insane-search/engine/tests/test_u1.py +200 -0
  226. package/vendor/insane-search/engine/tests/test_u4.py +131 -0
  227. package/vendor/insane-search/engine/tests/test_u5.py +163 -0
  228. package/vendor/insane-search/engine/tests/test_u7.py +124 -0
  229. package/vendor/insane-search/engine/transport.py +211 -0
  230. package/vendor/insane-search/engine/url_transforms.py +98 -0
  231. package/vendor/insane-search/engine/validators.py +331 -0
  232. package/vendor/insane-search/engine/waf_detector.py +214 -0
  233. package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
@@ -50,7 +50,11 @@ import {
50
50
  type SummaryOptions,
51
51
  shouldCompact,
52
52
  } from "@sayknow-cli/agent-core/compaction";
53
- import { DEFAULT_PRUNE_CONFIG, pruneToolOutputs } from "@sayknow-cli/agent-core/compaction/pruning";
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,
@@ -286,6 +290,8 @@ import { ToolChoiceQueue } from "./tool-choice-queue";
286
290
  import { YieldQueue } from "./yield-queue";
287
291
 
288
292
  /** Session-specific events that extend the core AgentEvent */
293
+ export type AutoCompactionContinuationSkipReason = "auto_continue_disabled_non_resumable_tail";
294
+
289
295
  export type AgentSessionEvent =
290
296
  | AgentEvent
291
297
  | { type: "auto_compaction_start"; reason: "threshold" | "overflow" | "idle"; action: "context-full" | "handoff" }
@@ -298,6 +304,7 @@ export type AgentSessionEvent =
298
304
  errorMessage?: string;
299
305
  /** True when compaction was skipped for a benign reason (no model, no candidates, nothing to compact). */
300
306
  skipped?: boolean;
307
+ continuationSkipReason?: AutoCompactionContinuationSkipReason;
301
308
  }
302
309
  | {
303
310
  type: "auto_retry_start";
@@ -418,6 +425,13 @@ export interface AgentSessionConfig {
418
425
  * **MUST NOT** dispose it on their own teardown.
419
426
  */
420
427
  ownedAsyncJobManager?: AsyncJobManager;
428
+ /**
429
+ * MCPManager whose lifecycle this session owns (top-level sessions that
430
+ * connected plugin-bundle MCP servers). Only the owned manager is
431
+ * disconnected on dispose; subagents and callers that merely observe the
432
+ * process-global manager **MUST NOT** dispose it on their own teardown.
433
+ */
434
+ ownedMcpManager?: MCPManager;
421
435
  /** Optional fork-context seed used to initialize a child session before its first prompt. */
422
436
  forkContextSeed?: ForkContextSeed;
423
437
  /** Optional provider state override. Fork-context children should omit this by default. */
@@ -955,6 +969,7 @@ export class AgentSession {
955
969
  * this undefined and **MUST NOT** dispose the global instance on teardown.
956
970
  */
957
971
  readonly #ownedAsyncJobManager: AsyncJobManager | undefined;
972
+ readonly #ownedMcpManager: MCPManager | undefined;
958
973
  #pendingPythonMessages: PythonExecutionMessage[] = [];
959
974
  #activeEvalExecutions = new Set<Promise<unknown>>();
960
975
  #evalExecutionDisposing = false;
@@ -1084,6 +1099,16 @@ export class AgentSession {
1084
1099
  #lastSuccessfulYieldToolCallId: string | undefined = undefined;
1085
1100
  #promptGeneration = 0;
1086
1101
  #providerSessionState = new Map<string, ProviderSessionState>();
1102
+ /**
1103
+ * Provider keys for which the Anthropic fast-mode auto-fallback fired this
1104
+ * session (the provider rejected `speed:"fast"` and we retried without it).
1105
+ * Provider/API-session scoped — matching the provider's own per-session
1106
+ * `fastModeDisabled` flag — NOT model-keyed. Transient (never persisted): it
1107
+ * suppresses the current-model fast indicator and dedups the one-time warning
1108
+ * WITHOUT mutating the user's intended `serviceTier`, so task subagents still
1109
+ * inherit the intended tier and a different provider still shows fast.
1110
+ */
1111
+ #fastModeAutoDisabledProviderKeys = new Set<string>();
1087
1112
  #hindsightSessionState: HindsightSessionState | undefined = undefined;
1088
1113
  readonly rawSseDebugBuffer: RawSseDebugBuffer;
1089
1114
 
@@ -1158,6 +1183,7 @@ export class AgentSession {
1158
1183
  // Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
1159
1184
  this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
1160
1185
  this.#ownedAsyncJobManager = config.ownedAsyncJobManager;
1186
+ this.#ownedMcpManager = config.ownedMcpManager;
1161
1187
  this.#scopedModels = config.scopedModels ?? [];
1162
1188
  this.#thinkingLevel = config.thinkingLevel;
1163
1189
  this.#promptTemplates = config.promptTemplates ?? [];
@@ -1869,7 +1895,6 @@ export class AgentSession {
1869
1895
  this.#resolveTtsrResume();
1870
1896
  return;
1871
1897
  }
1872
- this.#ttsrAbortPending = false;
1873
1898
  this.#perToolTtsrInjections.clear();
1874
1899
  const ttsrSettings = this.#ttsrManager?.getSettings();
1875
1900
  if (ttsrSettings?.contextMode === "discard" && targetAssistantIndex !== -1) {
@@ -1898,11 +1923,22 @@ export class AgentSession {
1898
1923
  );
1899
1924
  this.#markTtsrInjected(details.rules);
1900
1925
  }
1901
- try {
1902
- await this.agent.continue();
1903
- } catch {
1904
- this.#resolveTtsrResume();
1905
- }
1926
+ await this.#scheduleAgentContinue({
1927
+ delayMs: 0,
1928
+ generation,
1929
+ shouldContinue: () => {
1930
+ this.#ttsrAbortPending = false;
1931
+ return true;
1932
+ },
1933
+ onSkip: () => {
1934
+ this.#ttsrAbortPending = false;
1935
+ this.#resolveTtsrResume();
1936
+ },
1937
+ onError: () => {
1938
+ this.#ttsrAbortPending = false;
1939
+ this.#resolveTtsrResume();
1940
+ },
1941
+ });
1906
1942
  },
1907
1943
  { delayMs: 50 },
1908
1944
  );
@@ -1963,12 +1999,18 @@ export class AgentSession {
1963
1999
  const currentGrantsAnthropicPriority =
1964
2000
  this.serviceTier === "priority" || this.serviceTier === "claude-only";
1965
2001
  if (assistantMsg.disabledFeatures?.includes("priority") && currentGrantsAnthropicPriority) {
1966
- this.setServiceTier(undefined);
1967
- this.emitNotice(
1968
- "warning",
1969
- "Priority/fast mode rejected for this model; retried without it. Fast mode is now off.",
1970
- "priority",
1971
- );
2002
+ // The provider auto-dropped `speed:"fast"` for the current model's
2003
+ // provider this turn. Record a transient, provider-scoped marker
2004
+ // instead of clearing the user's intended tier, so task subagents
2005
+ // still inherit it and a different provider still gets fast mode.
2006
+ // Warn once per provider until the user re-arms with `/fast on`.
2007
+ if (this.#markFastModeAutoDisabledForCurrentModel()) {
2008
+ this.emitNotice(
2009
+ "warning",
2010
+ "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.",
2011
+ "priority",
2012
+ );
2013
+ }
1972
2014
  }
1973
2015
  // Resolve TTSR resume gate before checking for new deferred injections.
1974
2016
  // Gate on #ttsrAbortPending, not stopReason: a non-TTSR abort (e.g. streaming
@@ -2214,7 +2256,7 @@ export class AgentSession {
2214
2256
  #schedulePostPromptTask(
2215
2257
  task: (signal: AbortSignal) => Promise<void>,
2216
2258
  options?: { delayMs?: number; generation?: number; onSkip?: () => void },
2217
- ): void {
2259
+ ): Promise<void> {
2218
2260
  const delayMs = options?.delayMs ?? 0;
2219
2261
  const signal = this.#postPromptTasksAbortController.signal;
2220
2262
  const scheduled = (async () => {
@@ -2236,18 +2278,20 @@ export class AgentSession {
2236
2278
  await task(signal);
2237
2279
  })();
2238
2280
  this.#trackPostPromptTask(scheduled);
2281
+ return scheduled;
2239
2282
  }
2240
2283
 
2241
2284
  #scheduleAgentContinue(options?: {
2242
2285
  delayMs?: number;
2243
2286
  generation?: number;
2287
+ skipCompactionCheck?: boolean;
2244
2288
  shouldContinue?: () => boolean;
2245
2289
  onSkip?: (reason: "generation_changed" | "aborted_signal" | "queue_drained") => void;
2246
2290
  onError?: (error: unknown) => void;
2247
- }): void {
2291
+ }): Promise<void> {
2248
2292
  const scheduledGeneration = options?.generation;
2249
2293
  const signal = this.#postPromptTasksAbortController.signal;
2250
- this.#schedulePostPromptTask(
2294
+ return this.#schedulePostPromptTask(
2251
2295
  async () => {
2252
2296
  if (signal.aborted) {
2253
2297
  options?.onSkip?.("aborted_signal");
@@ -2263,6 +2307,9 @@ export class AgentSession {
2263
2307
  }
2264
2308
  try {
2265
2309
  await this.#maybeRestoreRetryFallbackPrimary();
2310
+ if (!options?.skipCompactionCheck) {
2311
+ await this.#checkEstimatedContextBeforePrompt();
2312
+ }
2266
2313
  await this.agent.continue();
2267
2314
  } catch (error) {
2268
2315
  logger.warn("agent.continue failed after scheduling", {
@@ -2307,6 +2354,34 @@ export class AgentSession {
2307
2354
  }
2308
2355
  }
2309
2356
 
2357
+ #detectOverflowRetryContinuationSkip(): AutoCompactionContinuationSkipReason | undefined {
2358
+ this.#stripOverflowFailedTurnForRetry();
2359
+ if (this.#isResumableAgentTail()) return undefined;
2360
+ const compactionSettings = this.settings.getGroup("compaction");
2361
+ return compactionSettings.autoContinue === false ? "auto_continue_disabled_non_resumable_tail" : undefined;
2362
+ }
2363
+
2364
+ #scheduleOverflowRetryContinuation(generation: number): void {
2365
+ this.#stripOverflowFailedTurnForRetry();
2366
+ if (this.#isResumableAgentTail()) {
2367
+ this.#scheduleAgentContinue({
2368
+ delayMs: 100,
2369
+ generation,
2370
+ onSkip: reason => this.#logCompactionContinuationSkipped("overflow_retry", reason),
2371
+ onError: error => this.#logCompactionContinuationError("overflow_retry", error),
2372
+ });
2373
+ return;
2374
+ }
2375
+
2376
+ const compactionSettings = this.settings.getGroup("compaction");
2377
+ if (compactionSettings.autoContinue !== false) {
2378
+ this.#scheduleAutoContinuePrompt(generation);
2379
+ return;
2380
+ }
2381
+
2382
+ this.#logCompactionContinuationSkipped("overflow_retry", "auto_continue_disabled_non_resumable_tail");
2383
+ }
2384
+
2310
2385
  #scheduleAutoContinuePrompt(generation: number): void {
2311
2386
  const continuePrompt = async () => {
2312
2387
  await this.#promptWithMessage(
@@ -3052,6 +3127,7 @@ export class AgentSession {
3052
3127
  willRetry: event.willRetry,
3053
3128
  errorMessage: event.errorMessage,
3054
3129
  skipped: event.skipped,
3130
+ continuationSkipReason: event.continuationSkipReason,
3055
3131
  });
3056
3132
  } else if (event.type === "auto_retry_start") {
3057
3133
  await this.#extensionRunner.emit({
@@ -3193,10 +3269,14 @@ export class AgentSession {
3193
3269
  AsyncJobManager.setInstance(undefined);
3194
3270
  }
3195
3271
  }
3196
- const mcpManager = MCPManager.instance();
3197
- if (mcpManager) {
3198
- await mcpManager.disconnectAll();
3199
- if (MCPManager.instance() === mcpManager) {
3272
+ // Only disconnect the MCP manager THIS session owns (top-level sessions that
3273
+ // connected plugin-bundle MCP servers). Subagents and callers that merely
3274
+ // observe the process-global manager must never tear down a manager they do
3275
+ // not own. Mirrors the ownedAsyncJobManager rule above.
3276
+ const ownedMcpManager = this.#ownedMcpManager;
3277
+ if (ownedMcpManager) {
3278
+ await ownedMcpManager.disconnectAll();
3279
+ if (MCPManager.instance() === ownedMcpManager) {
3200
3280
  MCPManager.setInstance(undefined);
3201
3281
  }
3202
3282
  }
@@ -5557,7 +5637,7 @@ export class AgentSession {
5557
5637
  }
5558
5638
 
5559
5639
  #syncTodoPhasesFromBranch(): void {
5560
- const phases = getLatestTodoPhasesFromEntries(this.sessionManager.getBranch());
5640
+ const phases = getLatestTodoPhasesFromEntries(this.sessionManager.getActivePathEntriesCanonical());
5561
5641
  // Strip completed/abandoned tasks — they were done in a previous run,
5562
5642
  // so they have no bearing on progress tracking for the new turn.
5563
5643
  for (const phase of phases) {
@@ -5566,6 +5646,42 @@ export class AgentSession {
5566
5646
  this.setTodoPhases(phases.filter(p => p.tasks.length > 0));
5567
5647
  }
5568
5648
 
5649
+ async #applyCompactionPostAppend(
5650
+ compactionEntryId: string,
5651
+ firstKeptEntryId: string,
5652
+ fromExtension?: boolean,
5653
+ ): Promise<CompactionEntry | undefined> {
5654
+ const eviction = this.sessionManager.evictCompactedContent(firstKeptEntryId, compactionEntryId);
5655
+ if (eviction.evictedEntries > 0) await this.sessionManager.rewriteEntries();
5656
+ const sessionContext = this.buildDisplaySessionContext();
5657
+ this.agent.replaceMessages(sessionContext.messages);
5658
+ this.#syncTodoPhasesFromBranch();
5659
+ this.#closeCodexProviderSessionsForHistoryRewrite();
5660
+
5661
+ // Get the saved compaction entry for the hook without materializing all entries.
5662
+ const savedCompactionEntry = this.sessionManager.getEntryForFidelity(compactionEntryId) as
5663
+ | CompactionEntry
5664
+ | undefined;
5665
+
5666
+ if (this.#extensionRunner && savedCompactionEntry) {
5667
+ await this.#extensionRunner.emit({
5668
+ type: "session_compact",
5669
+ compactionEntry: savedCompactionEntry,
5670
+ fromExtension: fromExtension ?? false,
5671
+ });
5672
+ }
5673
+
5674
+ return savedCompactionEntry;
5675
+ }
5676
+
5677
+ async applyCompactionPostAppendForTests(
5678
+ compactionEntryId: string,
5679
+ firstKeptEntryId: string,
5680
+ fromExtension?: boolean,
5681
+ ): Promise<CompactionEntry | undefined> {
5682
+ return this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
5683
+ }
5684
+
5569
5685
  #cloneTodoPhases(phases: TodoPhase[]): TodoPhase[] {
5570
5686
  return phases.map(phase => ({
5571
5687
  name: phase.name,
@@ -6195,33 +6311,81 @@ export class AgentSession {
6195
6311
  return resolveServiceTier(this.#subagentServiceTier(), provider) === "priority";
6196
6312
  }
6197
6313
 
6314
+ /**
6315
+ * Provider/API-session key used to scope the fast-mode auto-disable marker.
6316
+ * Mirrors the provider's own per-session `fastModeDisabled` scope (the key in
6317
+ * {@link #providerSessionState} cleared by `clearAnthropicFastModeFallback`),
6318
+ * so the marker is provider-scoped, never model-keyed. Returns `undefined`
6319
+ * when no model/provider is selected.
6320
+ */
6321
+ #fastModeProviderKey(provider: string | undefined = this.model?.provider): string | undefined {
6322
+ return provider;
6323
+ }
6324
+
6325
+ /**
6326
+ * Record that the current model's provider had fast mode auto-dropped this
6327
+ * session. Returns `true` only when the provider key is newly marked, so the
6328
+ * caller emits the one-time warning exactly once per provider until re-arm.
6329
+ */
6330
+ #markFastModeAutoDisabledForCurrentModel(): boolean {
6331
+ const key = this.#fastModeProviderKey();
6332
+ if (key === undefined) return false;
6333
+ if (this.#fastModeAutoDisabledProviderKeys.has(key)) return false;
6334
+ this.#fastModeAutoDisabledProviderKeys.add(key);
6335
+ return true;
6336
+ }
6337
+
6338
+ /** True when `provider`'s fast mode was auto-disabled this session. */
6339
+ #isFastModeAutoDisabledForProvider(provider?: string): boolean {
6340
+ const key = this.#fastModeProviderKey(provider);
6341
+ return key !== undefined && this.#fastModeAutoDisabledProviderKeys.has(key);
6342
+ }
6343
+
6344
+ /**
6345
+ * Re-arm fast mode after an auto-disable: clear the provider's sticky
6346
+ * `fastModeDisabled` fallback flag and the session auto-disable markers so the
6347
+ * next request carries `speed:"fast"` again and a future rejection can warn
6348
+ * once more. Called on explicit re-enable (`/fast on`, re-arming tier change),
6349
+ * never by the transient Q1 auto-disable path.
6350
+ */
6351
+ #rearmFastMode(): void {
6352
+ clearAnthropicFastModeFallback(this.#providerSessionState);
6353
+ this.#fastModeAutoDisabledProviderKeys.clear();
6354
+ }
6355
+
6198
6356
  /**
6199
6357
  * True when the configured `serviceTier` resolves to `"priority"` for the
6200
- * *currently selected model's provider*. Returns false for scoped tiers
6201
- * that don't match (e.g. `"openai-only"` on an anthropic model) and when
6202
- * no model is selected.
6358
+ * *currently selected model's provider* AND fast mode was not auto-disabled
6359
+ * for that provider this session. This is the current-model EFFECTIVE
6360
+ * predicate (what the next request actually does); use {@link isFastForProvider}
6361
+ * for pure configured intent (e.g. subagent/`modelRoles` display rows).
6203
6362
  */
6204
6363
  isFastModeActive(): boolean {
6205
- return this.isFastForProvider(this.model?.provider);
6364
+ const provider = this.model?.provider;
6365
+ return this.isFastForProvider(provider) && !this.#isFastModeAutoDisabledForProvider(provider);
6206
6366
  }
6207
6367
 
6208
6368
  setServiceTier(serviceTier: ServiceTier | undefined): void {
6209
- if (this.serviceTier === serviceTier) return;
6210
- // Re-arming priority on Anthropic? Clear the per-session auto-fallback
6211
- // sticky disable so the next request actually carries `speed: "fast"`
6212
- // again. Without this, `/fast on` (or user switching to a tier that
6213
- // grants anthropic priority) after an auto-disable is a silent no-op
6214
- // and the warning notice fires every turn.
6369
+ // Re-arming a priority-granting tier always clears the per-session
6370
+ // auto-fallback sticky disable AND the auto-disable markers so the next
6371
+ // request carries `speed: "fast"` again — even when the tier is unchanged
6372
+ // (re-selecting the same tier is a deliberate re-arm), and before the
6373
+ // no-op early-return below.
6215
6374
  if (serviceTier === "priority" || serviceTier === "claude-only") {
6216
- clearAnthropicFastModeFallback(this.#providerSessionState);
6375
+ this.#rearmFastMode();
6217
6376
  }
6377
+ if (this.serviceTier === serviceTier) return;
6218
6378
  this.agent.serviceTier = serviceTier;
6219
6379
  this.sessionManager.appendServiceTierChange(serviceTier ?? null);
6220
6380
  }
6221
6381
 
6222
6382
  setFastMode(enabled: boolean): void {
6223
6383
  if (enabled && this.isFastModeEnabled()) {
6224
- // Already on under any scope — keep the user's scoped value.
6384
+ // Intent already grants fast mode under some scope — keep the user's
6385
+ // scoped value but still re-arm, so an explicit `/fast on` after a
6386
+ // provider auto-disable actually clears the sticky fallback + markers
6387
+ // (otherwise it is a silent no-op). No history append: intent is unchanged.
6388
+ this.#rearmFastMode();
6225
6389
  return;
6226
6390
  }
6227
6391
  this.setServiceTier(enabled ? "priority" : undefined);
@@ -6279,19 +6443,23 @@ export class AgentSession {
6279
6443
  async #pruneToolOutputs(): Promise<{ prunedCount: number; tokensSaved: number } | undefined> {
6280
6444
  const branchEntries = this.sessionManager.getBranch();
6281
6445
  const result = pruneToolOutputs(branchEntries, DEFAULT_PRUNE_CONFIG);
6282
- if (result.prunedCount === 0) {
6446
+ const argumentResult = pruneAssistantToolArguments(branchEntries, DEFAULT_PRUNE_CONFIG);
6447
+ const tokensSaved = result.tokensSaved + argumentResult.argumentTokensSaved;
6448
+ const prunedCount = result.prunedCount + argumentResult.argumentPrunedCount;
6449
+ if (prunedCount === 0) {
6283
6450
  return undefined;
6284
6451
  }
6285
6452
 
6286
6453
  // getBranch() returns materialized copies for blob-externalized entries, so
6287
6454
  // the pruning mutations must be written back into the canonical store.
6288
- this.sessionManager.applyEntryMessageUpdates(result.prunedEntries);
6455
+ const combined = [...result.prunedEntries, ...argumentResult.prunedEntries];
6456
+ this.sessionManager.applyEntryMessageUpdates(combined);
6289
6457
  await this.sessionManager.rewriteEntries();
6290
6458
  const sessionContext = this.buildDisplaySessionContext();
6291
6459
  this.agent.replaceMessages(sessionContext.messages);
6292
6460
  this.#syncTodoPhasesFromBranch();
6293
6461
  this.#closeCodexProviderSessionsForHistoryRewrite();
6294
- return result;
6462
+ return { prunedCount, tokensSaved };
6295
6463
  }
6296
6464
 
6297
6465
  /**
@@ -6409,7 +6577,7 @@ export class AgentSession {
6409
6577
  throw new CompactionCancelledError();
6410
6578
  }
6411
6579
 
6412
- this.sessionManager.appendCompaction(
6580
+ const compactionEntryId = this.sessionManager.appendCompaction(
6413
6581
  summary,
6414
6582
  shortSummary,
6415
6583
  firstKeptEntryId,
@@ -6418,24 +6586,7 @@ export class AgentSession {
6418
6586
  fromExtension,
6419
6587
  preserveData,
6420
6588
  );
6421
- const newEntries = this.sessionManager.getEntries();
6422
- const sessionContext = this.buildDisplaySessionContext();
6423
- this.agent.replaceMessages(sessionContext.messages);
6424
- this.#syncTodoPhasesFromBranch();
6425
- this.#closeCodexProviderSessionsForHistoryRewrite();
6426
-
6427
- // Get the saved compaction entry for the hook
6428
- const savedCompactionEntry = newEntries.find(e => e.type === "compaction" && e.summary === summary) as
6429
- | CompactionEntry
6430
- | undefined;
6431
-
6432
- if (this.#extensionRunner && savedCompactionEntry) {
6433
- await this.#extensionRunner.emit({
6434
- type: "session_compact",
6435
- compactionEntry: savedCompactionEntry,
6436
- fromExtension,
6437
- });
6438
- }
6589
+ await this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
6439
6590
 
6440
6591
  const compactionResult: CompactionResult = {
6441
6592
  summary,
@@ -6722,6 +6873,8 @@ export class AgentSession {
6722
6873
  const compactionSettings = this.settings.getGroup("compaction");
6723
6874
  if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
6724
6875
  await this.#runAutoCompaction("overflow", true);
6876
+ } else {
6877
+ this.#scheduleOverflowRetryContinuation(generation);
6725
6878
  }
6726
6879
  return;
6727
6880
  }
@@ -6732,17 +6885,19 @@ export class AgentSession {
6732
6885
  // Skip if this was an error (non-overflow errors don't have usage data)
6733
6886
  if (assistantMessage.stopReason === "error") return;
6734
6887
  let contextTokens = calculateContextTokens(assistantMessage.usage);
6735
- const maxOutputTokens = this.model?.maxTokens ?? 0;
6888
+ // Model maxTokens is a capability ceiling, not a per-turn reservation.
6889
+ // Auto maintenance should track actual context fullness.
6890
+ const autoCompactionOutputReserveTokens = 0;
6736
6891
  // Cache-epoch invariant: pruning rewrites already-sent toolResult history,
6737
6892
  // which breaks the provider prompt-cache prefix mid-epoch. Only prune at a
6738
6893
  // sanctioned maintenance boundary, i.e. when the un-pruned context already
6739
6894
  // crosses the compaction threshold. Pruning may then avert full compaction.
6740
- if (!shouldCompact(contextTokens, contextWindow, compactionSettings, maxOutputTokens)) return;
6895
+ if (!shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) return;
6741
6896
  const pruneResult = await this.#pruneToolOutputs();
6742
6897
  if (pruneResult) {
6743
6898
  contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
6744
6899
  }
6745
- if (shouldCompact(contextTokens, contextWindow, compactionSettings, maxOutputTokens)) {
6900
+ if (shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) {
6746
6901
  // Try promotion first — if a larger model is available, switch instead of compacting
6747
6902
  const promoted = await this.#tryContextPromotion(assistantMessage);
6748
6903
  if (!promoted) {
@@ -6820,14 +6975,16 @@ export class AgentSession {
6820
6975
  if (!compactionSettings.enabled || compactionSettings.strategy === "off") return;
6821
6976
 
6822
6977
  let contextTokens = this.#estimateContextTokensForCompaction(pendingMessages).tokens;
6823
- const maxOutputTokens = model.maxTokens ?? 0;
6824
- if (!shouldCompact(contextTokens, contextWindow, compactionSettings, maxOutputTokens)) return;
6978
+ // Model maxTokens is a capability ceiling, not a per-turn reservation.
6979
+ // Auto maintenance should track actual context fullness.
6980
+ const autoCompactionOutputReserveTokens = 0;
6981
+ if (!shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) return;
6825
6982
 
6826
6983
  const pruneResult = await this.#pruneToolOutputs();
6827
6984
  if (pruneResult) {
6828
6985
  contextTokens = Math.max(0, contextTokens - pruneResult.tokensSaved);
6829
6986
  }
6830
- if (shouldCompact(contextTokens, contextWindow, compactionSettings, maxOutputTokens)) {
6987
+ if (shouldCompact(contextTokens, contextWindow, compactionSettings, autoCompactionOutputReserveTokens)) {
6831
6988
  await this.#runAutoCompaction("threshold", false, false, {
6832
6989
  continueAfterMaintenance: false,
6833
6990
  deferHandoffMaintenance: false,
@@ -7427,11 +7584,14 @@ export class AgentSession {
7427
7584
  availableModels: Model[],
7428
7585
  currentModel: Model | undefined,
7429
7586
  ): ResolvedModelRoleValue {
7587
+ const target = SKC_MODEL_ASSIGNMENT_TARGETS[role as keyof typeof SKC_MODEL_ASSIGNMENT_TARGETS];
7430
7588
  const roleModelStr =
7431
- role === "default"
7432
- ? (this.settings.getModelRole("default") ??
7433
- (currentModel ? `${currentModel.provider}/${currentModel.id}` : undefined))
7434
- : this.settings.getModelRole(role);
7589
+ target?.settingsPath === "task.agentModelOverrides"
7590
+ ? this.settings.get("task.agentModelOverrides")[role]
7591
+ : role === "default"
7592
+ ? (this.settings.getModelRole("default") ??
7593
+ (currentModel ? `${currentModel.provider}/${currentModel.id}` : undefined))
7594
+ : this.settings.getModelRole(role);
7435
7595
 
7436
7596
  if (!roleModelStr) {
7437
7597
  return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
@@ -7730,32 +7890,18 @@ export class AgentSession {
7730
7890
 
7731
7891
  const preparation = prepareCompaction(pathEntries, compactionSettings);
7732
7892
  if (!preparation) {
7893
+ const continuationSkipReason = willRetry ? this.#detectOverflowRetryContinuationSkip() : undefined;
7733
7894
  await this.#emitSessionEvent({
7734
7895
  type: "auto_compaction_end",
7735
7896
  action,
7736
7897
  result: undefined,
7737
7898
  aborted: false,
7738
- willRetry: false,
7899
+ willRetry: willRetry && !continuationSkipReason,
7739
7900
  skipped: true,
7901
+ continuationSkipReason,
7740
7902
  });
7741
7903
  if (willRetry) {
7742
- this.#stripOverflowFailedTurnForRetry();
7743
- if (this.#isResumableAgentTail()) {
7744
- this.#scheduleAgentContinue({
7745
- delayMs: 100,
7746
- generation,
7747
- onSkip: skipReason => this.#logCompactionContinuationSkipped("overflow_retry", skipReason),
7748
- onError: error => this.#logCompactionContinuationError("overflow_retry", error),
7749
- });
7750
- } else {
7751
- const tail = this.agent.state.messages.at(-1) as AssistantMessage | undefined;
7752
- logger.warn("Auto-compaction continuation skipped", {
7753
- source: "overflow_retry",
7754
- reason: "not_resumable_tail",
7755
- role: tail?.role,
7756
- stopReason: tail?.stopReason,
7757
- });
7758
- }
7904
+ this.#scheduleOverflowRetryContinuation(generation);
7759
7905
  } else if (continueAfterMaintenance && reason !== "idle" && this.agent.hasQueuedMessages()) {
7760
7906
  this.#scheduleAgentContinue({
7761
7907
  delayMs: 100,
@@ -7930,7 +8076,7 @@ export class AgentSession {
7930
8076
  return;
7931
8077
  }
7932
8078
 
7933
- this.sessionManager.appendCompaction(
8079
+ const compactionEntryId = this.sessionManager.appendCompaction(
7934
8080
  summary,
7935
8081
  shortSummary,
7936
8082
  firstKeptEntryId,
@@ -7939,24 +8085,7 @@ export class AgentSession {
7939
8085
  fromExtension,
7940
8086
  preserveData,
7941
8087
  );
7942
- const newEntries = this.sessionManager.getEntries();
7943
- const sessionContext = this.buildDisplaySessionContext();
7944
- this.agent.replaceMessages(sessionContext.messages);
7945
- this.#syncTodoPhasesFromBranch();
7946
- this.#closeCodexProviderSessionsForHistoryRewrite();
7947
-
7948
- // Get the saved compaction entry for the hook
7949
- const savedCompactionEntry = newEntries.find(e => e.type === "compaction" && e.summary === summary) as
7950
- | CompactionEntry
7951
- | undefined;
7952
-
7953
- if (this.#extensionRunner && savedCompactionEntry) {
7954
- await this.#extensionRunner.emit({
7955
- type: "session_compact",
7956
- compactionEntry: savedCompactionEntry,
7957
- fromExtension,
7958
- });
7959
- }
8088
+ await this.#applyCompactionPostAppend(compactionEntryId, firstKeptEntryId, fromExtension);
7960
8089
 
7961
8090
  const result: CompactionResult = {
7962
8091
  summary,
@@ -7966,26 +8095,18 @@ export class AgentSession {
7966
8095
  details,
7967
8096
  preserveData,
7968
8097
  };
7969
- await this.#emitSessionEvent({ type: "auto_compaction_end", action, result, aborted: false, willRetry });
8098
+ const continuationSkipReason = willRetry ? this.#detectOverflowRetryContinuationSkip() : undefined;
8099
+ await this.#emitSessionEvent({
8100
+ type: "auto_compaction_end",
8101
+ action,
8102
+ result,
8103
+ aborted: false,
8104
+ willRetry: willRetry && !continuationSkipReason,
8105
+ continuationSkipReason,
8106
+ });
7970
8107
 
7971
8108
  if (willRetry) {
7972
- this.#stripOverflowFailedTurnForRetry();
7973
- if (!this.#isResumableAgentTail()) {
7974
- const tail = this.agent.state.messages.at(-1) as AssistantMessage | undefined;
7975
- logger.warn("Auto-compaction continuation skipped", {
7976
- source: "overflow_retry",
7977
- reason: "not_resumable_tail",
7978
- role: tail?.role,
7979
- stopReason: tail?.stopReason,
7980
- });
7981
- } else {
7982
- this.#scheduleAgentContinue({
7983
- delayMs: 100,
7984
- generation,
7985
- onSkip: reason => this.#logCompactionContinuationSkipped("overflow_retry", reason),
7986
- onError: error => this.#logCompactionContinuationError("overflow_retry", error),
7987
- });
7988
- }
8109
+ this.#scheduleOverflowRetryContinuation(generation);
7989
8110
  } else if (continueAfterMaintenance && reason !== "idle" && this.agent.hasQueuedMessages()) {
7990
8111
  // Auto-compaction can complete while follow-up/steering/custom messages are waiting.
7991
8112
  // Kick the loop so queued messages are actually delivered.
@@ -9582,7 +9703,7 @@ export class AgentSession {
9582
9703
  cancelled: boolean;
9583
9704
  }> {
9584
9705
  const previousSessionFile = this.sessionFile;
9585
- const selectedEntry = this.sessionManager.getEntry(entryId);
9706
+ const selectedEntry = this.sessionManager.getEntryForFidelity(entryId);
9586
9707
 
9587
9708
  if (selectedEntry?.type !== "message" || selectedEntry.message.role !== "user") {
9588
9709
  throw new Error("Invalid entry ID for branching");
@@ -9680,7 +9801,7 @@ export class AgentSession {
9680
9801
  throw new Error("No model available for summarization");
9681
9802
  }
9682
9803
 
9683
- const targetEntry = this.sessionManager.getEntry(targetId);
9804
+ const targetEntry = this.sessionManager.getEntryForFidelity(targetId);
9684
9805
  if (!targetEntry) {
9685
9806
  throw new Error(`Entry ${targetId} not found`);
9686
9807
  }
@@ -9836,9 +9957,11 @@ export class AgentSession {
9836
9957
 
9837
9958
  for (const entry of entries) {
9838
9959
  if (entry.type !== "message") continue;
9839
- if (entry.message.role !== "user") continue;
9960
+ const fidelityEntry = this.sessionManager.getEntryForFidelity(entry.id);
9961
+ if (fidelityEntry?.type !== "message") continue;
9962
+ if (fidelityEntry.message.role !== "user") continue;
9840
9963
 
9841
- const text = this.#extractUserMessageText(entry.message.content);
9964
+ const text = this.#extractUserMessageText(fidelityEntry.message.content);
9842
9965
  if (text) {
9843
9966
  result.push({ entryId: entry.id, text });
9844
9967
  }