@prestyj/cli 5.3.0 → 5.3.1

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 (168) hide show
  1. package/dist/app-sidecar.js +581 -55
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/core/agent-session-compaction.test.js +89 -0
  4. package/dist/core/agent-session-compaction.test.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +71 -3
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +261 -47
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/autopilot-cycle.d.ts +69 -17
  10. package/dist/core/autopilot-cycle.d.ts.map +1 -1
  11. package/dist/core/autopilot-cycle.js +82 -13
  12. package/dist/core/autopilot-cycle.js.map +1 -1
  13. package/dist/core/autopilot-cycle.test.js +206 -10
  14. package/dist/core/autopilot-cycle.test.js.map +1 -1
  15. package/dist/core/autopilot-gate.d.ts +48 -22
  16. package/dist/core/autopilot-gate.d.ts.map +1 -1
  17. package/dist/core/autopilot-gate.js +110 -1
  18. package/dist/core/autopilot-gate.js.map +1 -1
  19. package/dist/core/autopilot-gate.test.js +139 -3
  20. package/dist/core/autopilot-gate.test.js.map +1 -1
  21. package/dist/core/autopilot-verdict.d.ts +6 -0
  22. package/dist/core/autopilot-verdict.d.ts.map +1 -1
  23. package/dist/core/autopilot-verdict.js +75 -1
  24. package/dist/core/autopilot-verdict.js.map +1 -1
  25. package/dist/core/autopilot-verdict.test.js +63 -0
  26. package/dist/core/autopilot-verdict.test.js.map +1 -1
  27. package/dist/core/bash-spawn-benchmark.d.ts +2 -0
  28. package/dist/core/bash-spawn-benchmark.d.ts.map +1 -0
  29. package/dist/core/bash-spawn-benchmark.js +139 -0
  30. package/dist/core/bash-spawn-benchmark.js.map +1 -0
  31. package/dist/core/hashline-edit-benchmark.d.ts +33 -1
  32. package/dist/core/hashline-edit-benchmark.d.ts.map +1 -1
  33. package/dist/core/hashline-edit-benchmark.js +193 -21
  34. package/dist/core/hashline-edit-benchmark.js.map +1 -1
  35. package/dist/core/hashline-edit-benchmark.test.js +51 -2
  36. package/dist/core/hashline-edit-benchmark.test.js.map +1 -1
  37. package/dist/core/mcp/deferred-catalog.d.ts +28 -0
  38. package/dist/core/mcp/deferred-catalog.d.ts.map +1 -0
  39. package/dist/core/mcp/deferred-catalog.js +72 -0
  40. package/dist/core/mcp/deferred-catalog.js.map +1 -0
  41. package/dist/core/mcp/resolve-stdio.d.ts +19 -3
  42. package/dist/core/mcp/resolve-stdio.d.ts.map +1 -1
  43. package/dist/core/mcp/resolve-stdio.js +136 -5
  44. package/dist/core/mcp/resolve-stdio.js.map +1 -1
  45. package/dist/core/mcp/resolve-stdio.test.js +82 -1
  46. package/dist/core/mcp/resolve-stdio.test.js.map +1 -1
  47. package/dist/core/nolan-context.d.ts +26 -2
  48. package/dist/core/nolan-context.d.ts.map +1 -1
  49. package/dist/core/nolan-context.js +46 -4
  50. package/dist/core/nolan-context.js.map +1 -1
  51. package/dist/core/nolan-context.test.js +53 -12
  52. package/dist/core/nolan-context.test.js.map +1 -1
  53. package/dist/core/nolan-prompt.d.ts +2 -15
  54. package/dist/core/nolan-prompt.d.ts.map +1 -1
  55. package/dist/core/nolan-prompt.js +96 -15
  56. package/dist/core/nolan-prompt.js.map +1 -1
  57. package/dist/core/nolan-prompt.test.js +102 -7
  58. package/dist/core/nolan-prompt.test.js.map +1 -1
  59. package/dist/core/persistent-shell.d.ts +27 -0
  60. package/dist/core/persistent-shell.d.ts.map +1 -0
  61. package/dist/core/persistent-shell.js +150 -0
  62. package/dist/core/persistent-shell.js.map +1 -0
  63. package/dist/core/persistent-shell.test.d.ts +2 -0
  64. package/dist/core/persistent-shell.test.d.ts.map +1 -0
  65. package/dist/core/persistent-shell.test.js +84 -0
  66. package/dist/core/persistent-shell.test.js.map +1 -0
  67. package/dist/core/process-manager-dev-server-repro.test.js +34 -6
  68. package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
  69. package/dist/core/progress/engine.d.ts +23 -0
  70. package/dist/core/progress/engine.d.ts.map +1 -0
  71. package/dist/core/progress/engine.js +131 -0
  72. package/dist/core/progress/engine.js.map +1 -0
  73. package/dist/core/progress/engine.test.d.ts +2 -0
  74. package/dist/core/progress/engine.test.d.ts.map +1 -0
  75. package/dist/core/progress/engine.test.js +136 -0
  76. package/dist/core/progress/engine.test.js.map +1 -0
  77. package/dist/core/progress/git-xp.d.ts +16 -0
  78. package/dist/core/progress/git-xp.d.ts.map +1 -0
  79. package/dist/core/progress/git-xp.js +106 -0
  80. package/dist/core/progress/git-xp.js.map +1 -0
  81. package/dist/core/progress/git-xp.test.d.ts +2 -0
  82. package/dist/core/progress/git-xp.test.d.ts.map +1 -0
  83. package/dist/core/progress/git-xp.test.js +88 -0
  84. package/dist/core/progress/git-xp.test.js.map +1 -0
  85. package/dist/core/progress/ranks.d.ts +21 -0
  86. package/dist/core/progress/ranks.d.ts.map +1 -0
  87. package/dist/core/progress/ranks.js +141 -0
  88. package/dist/core/progress/ranks.js.map +1 -0
  89. package/dist/core/progress/ranks.test.d.ts +2 -0
  90. package/dist/core/progress/ranks.test.d.ts.map +1 -0
  91. package/dist/core/progress/ranks.test.js +59 -0
  92. package/dist/core/progress/ranks.test.js.map +1 -0
  93. package/dist/core/progress/rebuild.d.ts +7 -0
  94. package/dist/core/progress/rebuild.d.ts.map +1 -0
  95. package/dist/core/progress/rebuild.js +106 -0
  96. package/dist/core/progress/rebuild.js.map +1 -0
  97. package/dist/core/progress/rebuild.test.d.ts +2 -0
  98. package/dist/core/progress/rebuild.test.d.ts.map +1 -0
  99. package/dist/core/progress/rebuild.test.js +72 -0
  100. package/dist/core/progress/rebuild.test.js.map +1 -0
  101. package/dist/core/progress/store.d.ts +35 -0
  102. package/dist/core/progress/store.d.ts.map +1 -0
  103. package/dist/core/progress/store.js +200 -0
  104. package/dist/core/progress/store.js.map +1 -0
  105. package/dist/core/progress/store.test.d.ts +2 -0
  106. package/dist/core/progress/store.test.d.ts.map +1 -0
  107. package/dist/core/progress/store.test.js +108 -0
  108. package/dist/core/progress/store.test.js.map +1 -0
  109. package/dist/core/progress/types.d.ts +108 -0
  110. package/dist/core/progress/types.d.ts.map +1 -0
  111. package/dist/core/progress/types.js +3 -0
  112. package/dist/core/progress/types.js.map +1 -0
  113. package/dist/core/project-discovery.d.ts.map +1 -1
  114. package/dist/core/project-discovery.js +40 -9
  115. package/dist/core/project-discovery.js.map +1 -1
  116. package/dist/core/project-discovery.test.d.ts +2 -0
  117. package/dist/core/project-discovery.test.d.ts.map +1 -0
  118. package/dist/core/project-discovery.test.js +104 -0
  119. package/dist/core/project-discovery.test.js.map +1 -0
  120. package/dist/core/session-history.d.ts +51 -0
  121. package/dist/core/session-history.d.ts.map +1 -0
  122. package/dist/core/session-history.js +156 -0
  123. package/dist/core/session-history.js.map +1 -0
  124. package/dist/core/session-history.test.d.ts +2 -0
  125. package/dist/core/session-history.test.d.ts.map +1 -0
  126. package/dist/core/session-history.test.js +101 -0
  127. package/dist/core/session-history.test.js.map +1 -0
  128. package/dist/core/session-manager.d.ts +34 -0
  129. package/dist/core/session-manager.d.ts.map +1 -1
  130. package/dist/core/session-manager.js +67 -0
  131. package/dist/core/session-manager.js.map +1 -1
  132. package/dist/core/session-manager.test.js +140 -1
  133. package/dist/core/session-manager.test.js.map +1 -1
  134. package/dist/core/settings-manager.d.ts +1 -0
  135. package/dist/core/settings-manager.d.ts.map +1 -1
  136. package/dist/core/settings-manager.js +4 -0
  137. package/dist/core/settings-manager.js.map +1 -1
  138. package/dist/system-prompt.d.ts.map +1 -1
  139. package/dist/system-prompt.js +12 -8
  140. package/dist/system-prompt.js.map +1 -1
  141. package/dist/system-prompt.test.js +15 -20
  142. package/dist/system-prompt.test.js.map +1 -1
  143. package/dist/tools/bash.d.ts +1 -0
  144. package/dist/tools/bash.d.ts.map +1 -1
  145. package/dist/tools/bash.js +27 -2
  146. package/dist/tools/bash.js.map +1 -1
  147. package/dist/tools/edit.d.ts +9 -2
  148. package/dist/tools/edit.d.ts.map +1 -1
  149. package/dist/tools/edit.js +91 -10
  150. package/dist/tools/edit.js.map +1 -1
  151. package/dist/tools/edit.test.js +97 -0
  152. package/dist/tools/edit.test.js.map +1 -1
  153. package/dist/tools/plan-mode.test.js +25 -0
  154. package/dist/tools/plan-mode.test.js.map +1 -1
  155. package/dist/tools/prompt-hints.d.ts.map +1 -1
  156. package/dist/tools/prompt-hints.js +3 -0
  157. package/dist/tools/prompt-hints.js.map +1 -1
  158. package/dist/tools/subagent.d.ts.map +1 -1
  159. package/dist/tools/subagent.js +24 -1
  160. package/dist/tools/subagent.js.map +1 -1
  161. package/dist/tools/tool-search.d.ts +15 -0
  162. package/dist/tools/tool-search.d.ts.map +1 -0
  163. package/dist/tools/tool-search.js +35 -0
  164. package/dist/tools/tool-search.js.map +1 -0
  165. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  166. package/dist/ui/hooks/useAgentLoop.js +22 -9
  167. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  168. package/package.json +4 -4
@@ -1,7 +1,7 @@
1
1
  import { type Message, type Provider, type ThinkingLevel } from "@prestyj/ai";
2
2
  import { EventBus } from "./event-bus.js";
3
3
  import { SlashCommandRegistry } from "./slash-commands.js";
4
- import { type BranchInfo, type NolanTurnPayload } from "./session-manager.js";
4
+ import { type BranchInfo, type NolanTurnPayload, type AutopilotMarkerPayload, type AppMarkerPayload } from "./session-manager.js";
5
5
  import type { BackgroundProcess } from "./process-manager.js";
6
6
  import { type EnhanceResult } from "../utils/prompt-enhancer.js";
7
7
  /** A chat attachment (image / video / other file) prepared for the model. The
@@ -58,7 +58,12 @@ export interface AgentSessionOptions {
58
58
  * rebuilds. Hosts (e.g. the ezcoder-app sidecar) use these to surface plan-mode
59
59
  * UI. Omitted by callers that don't want plan mode (CLI wires its own).
60
60
  */
61
- onEnterPlan?: (reason?: string) => void | Promise<void>;
61
+ /**
62
+ * Return `false` to decline plan mode (e.g. an unattended task run where no
63
+ * human is present to approve a plan). The `enter_plan` tool then tells the
64
+ * agent to skip planning and implement directly. Any other return accepts.
65
+ */
66
+ onEnterPlan?: (reason?: string) => boolean | void | Promise<boolean | void>;
62
67
  onExitPlan?: (planPath: string) => Promise<string>;
63
68
  /**
64
69
  * If provided, the session's tool set is filtered to ONLY these tool names
@@ -79,6 +84,16 @@ export interface AgentSessionOptions {
79
84
  * MCP entirely (its dynamic tool names could never match a fixed allow-list).
80
85
  */
81
86
  allowedMcpServers?: string[];
87
+ /**
88
+ * Force 1-h prompt-cache TTL + pre-warm regardless of the user's global
89
+ * `speedProfile` setting. Bursty read-only advisory sessions (the Nolan
90
+ * mentor + autopilot reviewer) call the same static system prompt on a
91
+ * schedule that routinely exceeds the default 5-min cache window — a
92
+ * dropped cache there resends the whole cached prefix at full price right
93
+ * when it matters most, independent of whatever the user picked for the
94
+ * main build session. Default (undefined) = follow `speedProfile`.
95
+ */
96
+ forceLongCacheRetention?: boolean;
82
97
  }
83
98
  export interface AgentSessionState {
84
99
  provider: Provider;
@@ -102,6 +117,8 @@ export declare class AgentSession {
102
117
  private extensionLoader;
103
118
  private messages;
104
119
  private nolanTurns;
120
+ private autopilotMarkers;
121
+ private appMarkers;
105
122
  private tools;
106
123
  /** Rebuilds the read tool for a new model (video byte cap is baked in at
107
124
  * creation). Called from switchModel so video-capable models get the
@@ -129,6 +146,8 @@ export declare class AgentSession {
129
146
  private processManager?;
130
147
  private lspManager?;
131
148
  private mcpManager?;
149
+ /** Deferred MCP tools awaiting discovery via tool_search (bench A win). */
150
+ private mcpCatalog?;
132
151
  private provider;
133
152
  private model;
134
153
  private cwd;
@@ -183,6 +202,16 @@ export declare class AgentSession {
183
202
  * turn, so background-connected servers become available on the next prompt.
184
203
  */
185
204
  private connectMcpServers;
205
+ /**
206
+ * Route freshly connected MCP tools: deferred into the tool_search catalog
207
+ * (default — keeps ~8k tokens of schema out of every cache-miss turn, see
208
+ * bench/RESULTS.md bench A) or pushed eagerly when the user opted out.
209
+ * Allow-listed sessions (Nolan) always get the eager path — their fixed tool
210
+ * expectations predate the catalog, and tool_search isn't allow-listed.
211
+ * Promotion pushes onto the live `this.tools` array the running agent loop
212
+ * re-reads every turn, so promoted tools are callable on the next step.
213
+ */
214
+ private addMcpTools;
186
215
  /**
187
216
  * Process user input. Handles slash commands or runs agent loop.
188
217
  */
@@ -295,6 +324,10 @@ export declare class AgentSession {
295
324
  * by the host to interleave Nolan's advisory exchanges back into the transcript
296
325
  * on resume. Never part of the LLM message history. */
297
326
  getNolanTurns(): NolanTurnPayload[];
327
+ /** Autopilot verdict markers recorded against this session, in record order.
328
+ * Used by the host to interleave the auto-review loop's markers back into
329
+ * the transcript on resume, mirroring `getNolanTurns`. */
330
+ getAutopilotMarkers(): AutopilotMarkerPayload[];
298
331
  /**
299
332
  * Record one Nolan Grout (mentor agent) turn against this build session: the
300
333
  * user's question + Nolan's reply. Kept in memory for the live transcript and
@@ -311,6 +344,40 @@ export declare class AgentSession {
311
344
  * lost when the session is rewritten (those rewrites only re-persist
312
345
  * messages). Each turn keeps its original `afterMessageCount` anchor. */
313
346
  private rePersistNolanTurns;
347
+ /**
348
+ * Record one autopilot verdict marker (prompted / done / human / capped)
349
+ * against this build session. Kept in memory for the live transcript and
350
+ * persisted as a `custom` entry (parentId null, same as Nolan turns) so a
351
+ * resumed session renders the exact same Nolan bubble the live run showed
352
+ * instead of dropping the marker or falling back to a raw verdict string.
353
+ * No-op persistence for transient sessions (kept in memory only).
354
+ */
355
+ persistAutopilotMarker(phase: AutopilotMarkerPayload["phase"], extra?: {
356
+ reason?: string;
357
+ body?: string;
358
+ }): Promise<void>;
359
+ /** Re-append the in-memory autopilot markers to the current session file.
360
+ * Mirrors `rePersistNolanTurns` — called after a continuation/compaction file
361
+ * is created so the auto-review history survives the rewrite. */
362
+ private rePersistAutopilotMarkers;
363
+ /** App transcript markers recorded against this session, in record order.
364
+ * Used by the host to interleave display-only rows (plan banner, task
365
+ * header, errors, user-bubble hints) back into the transcript on resume. */
366
+ getAppMarkers(): AppMarkerPayload[];
367
+ /**
368
+ * Record one app transcript marker (display-only row) against this session.
369
+ * Same treatment as autopilot markers: kept in memory for the live
370
+ * transcript, persisted as a `custom` entry (parentId null, never on the
371
+ * message DAG) so a resumed session shows the identical row. `anchorOffset`
372
+ * shifts the recorded `afterMessageCount` — pass +1 for a marker that should
373
+ * attach to the user message about to be pushed by the imminent prompt.
374
+ * No-op persistence for transient sessions.
375
+ */
376
+ persistAppMarker(kind: AppMarkerPayload["kind"], data: Record<string, unknown>, anchorOffset?: number): Promise<void>;
377
+ /** Re-append the in-memory app markers to the current session file. Mirrors
378
+ * `rePersistNolanTurns` — called after a continuation/compaction file is
379
+ * created so display-only rows survive the rewrite. */
380
+ private rePersistAppMarkers;
314
381
  /**
315
382
  * Generate a short LLM session title from the conversation so far (first user
316
383
  * message + first assistant reply). Best-effort; returns null on failure or
@@ -332,7 +399,8 @@ export declare class AgentSession {
332
399
  setThinkingLevel(level: ThinkingLevel | undefined): void;
333
400
  /** Replace the abort signal (e.g. after cancellation). */
334
401
  setSignal(signal: AbortSignal): void;
335
- /** True when speedProfile is "optimized" (1-h cache TTL + pre-warm). */
402
+ /** True when speedProfile is "optimized" (1-h cache TTL + pre-warm), or the
403
+ * session was constructed with `forceLongCacheRetention` (Nolan sessions). */
336
404
  private isSpeedOptimized;
337
405
  /**
338
406
  * Ordered auth-storage keys the current (provider, model) pair tries, first
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session.d.ts","sourceRoot":"","sources":["../../src/core/agent-session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-session.d.ts","sourceRoot":"","sources":["../../src/core/agent-session.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ import { SettingsManager } from "./settings-manager.js";
8
8
  import { AuthStorage } from "./auth-storage.js";
9
9
  import { getClaudeCliUserAgent } from "./claude-code-version.js";
10
10
  import { kimiCodingHeaders, isKimiCodingEndpoint } from "./oauth/kimi.js";
11
- import { SessionManager, NOLAN_TURN_CUSTOM_KIND, } from "./session-manager.js";
11
+ import { SessionManager, NOLAN_TURN_CUSTOM_KIND, AUTOPILOT_MARKER_CUSTOM_KIND, APP_MARKER_CUSTOM_KIND, } from "./session-manager.js";
12
12
  import { ExtensionLoader } from "./extensions/loader.js";
13
13
  import { shouldCompact, compact, getCompactionReserveTokens } from "./compaction/compactor.js";
14
14
  import { getAuthStorageKeys, getContextWindow, getModel, MODELS } from "./model-registry.js";
@@ -17,6 +17,8 @@ import { ensureAppDirs } from "../config.js";
17
17
  import { buildSystemPrompt } from "../system-prompt.js";
18
18
  import { createTools, createWebSearchTool, } from "../tools/index.js";
19
19
  import { MCPClientManager, getAllMcpServers } from "./mcp/index.js";
20
+ import { DeferredToolCatalog } from "./mcp/deferred-catalog.js";
21
+ import { createToolSearchTool } from "../tools/tool-search.js";
20
22
  import { log } from "./logger.js";
21
23
  import { setEstimatorModel } from "./compaction/token-estimator.js";
22
24
  import { discoverAgents } from "./agents.js";
@@ -45,6 +47,17 @@ export class AgentSession {
45
47
  // transcript. Each carries the non-system message count at record time so the
46
48
  // webview can interleave them chronologically.
47
49
  nolanTurns = [];
50
+ // Autopilot Nolan (auto-reviewer) markers recorded against this build session:
51
+ // the review verdict shown in the transcript (prompted / done / human /
52
+ // capped). Same not-on-the-DAG treatment as nolanTurns — advisory only,
53
+ // persisted + reloaded so a resumed session shows the identical Nolan bubble
54
+ // the live run showed instead of dropping it or replaying a raw verdict.
55
+ autopilotMarkers = [];
56
+ // Generic app transcript markers (plan-mode banner, task header, error rows,
57
+ // user-bubble display hints). Same not-on-the-DAG treatment as nolanTurns —
58
+ // display only, persisted + reloaded so a resumed session shows the same
59
+ // transcript rows the live run showed.
60
+ appMarkers = [];
48
61
  tools = [];
49
62
  /** Rebuilds the read tool for a new model (video byte cap is baked in at
50
63
  * creation). Called from switchModel so video-capable models get the
@@ -87,6 +100,8 @@ export class AgentSession {
87
100
  processManager;
88
101
  lspManager;
89
102
  mcpManager;
103
+ /** Deferred MCP tools awaiting discovery via tool_search (bench A win). */
104
+ mcpCatalog;
90
105
  provider;
91
106
  model;
92
107
  cwd;
@@ -352,7 +367,7 @@ export class AgentSession {
352
367
  const mcpTools = this.opts.allowedTools
353
368
  ? connected.filter((t) => this.isToolAllowed(t.name))
354
369
  : connected;
355
- this.tools.push(...mcpTools);
370
+ this.addMcpTools(mcpTools);
356
371
  // Background connect resolves AFTER initialize() has already built the
357
372
  // system prompt (the default path awaits this before buildSystemPrompt,
358
373
  // so its prompt already lists the tools). Refresh messages[0] so the
@@ -369,6 +384,31 @@ export class AgentSession {
369
384
  log("WARN", "mcp", `MCP initialization failed: ${err instanceof Error ? err.message : String(err)}`);
370
385
  }
371
386
  }
387
+ /**
388
+ * Route freshly connected MCP tools: deferred into the tool_search catalog
389
+ * (default — keeps ~8k tokens of schema out of every cache-miss turn, see
390
+ * bench/RESULTS.md bench A) or pushed eagerly when the user opted out.
391
+ * Allow-listed sessions (Nolan) always get the eager path — their fixed tool
392
+ * expectations predate the catalog, and tool_search isn't allow-listed.
393
+ * Promotion pushes onto the live `this.tools` array the running agent loop
394
+ * re-reads every turn, so promoted tools are callable on the next step.
395
+ */
396
+ addMcpTools(mcpTools) {
397
+ if (mcpTools.length === 0)
398
+ return;
399
+ const defer = !this.opts.allowedTools && this.settingsManager.get("deferredMcpTools");
400
+ if (!defer) {
401
+ this.tools.push(...mcpTools);
402
+ return;
403
+ }
404
+ this.mcpCatalog ??= new DeferredToolCatalog();
405
+ this.mcpCatalog.add(mcpTools);
406
+ if (!this.tools.some((t) => t.name === "tool_search")) {
407
+ this.tools.push(createToolSearchTool(this.mcpCatalog, (promoted) => {
408
+ this.tools.push(...promoted);
409
+ }));
410
+ }
411
+ }
372
412
  /**
373
413
  * Process user input. Handles slash commands or runs agent loop.
374
414
  */
@@ -579,22 +619,23 @@ export class AgentSession {
579
619
  // agent sees them mid-loop instead of after it stops.
580
620
  if (this.userQueue.length > 0) {
581
621
  const queued = this.userQueue.splice(0);
582
- // Frame the queued text as concurrent steering — without this wrapper the
583
- // model treats a mid-run message as a fresh request that supersedes the
584
- // original task and silently drops it.
585
- // Plain-text-only queue: keep the simple merged-string message.
586
- if (queued.every((m) => m.attachments.length === 0)) {
587
- const merged = queued.map((m) => m.text).join("\n\n");
588
- return [{ role: "user", content: wrapSteeringText(merged) }];
589
- }
590
- // Any queued attachments → deliver one user message with text + media
591
- // blocks built the same way as a non-queued attachment prompt.
592
- const parts = [
593
- { type: "text", text: STEERING_PREFIX },
594
- ];
595
- for (const m of queued)
596
- parts.push(...this.buildAttachmentParts(m.text, m.attachments));
597
- return [{ role: "user", content: parts }];
622
+ // Frame each queued item as concurrent steering — without this wrapper
623
+ // the model treats a mid-run message as a fresh request that supersedes
624
+ // the original task and silently drops it. ONE message per queued item
625
+ // (not merged): each persists as its own user message, so a resumed
626
+ // session shows the same number of bubbles the live run did.
627
+ return queued.map((m) => {
628
+ if (m.attachments.length === 0) {
629
+ return { role: "user", content: wrapSteeringText(m.text) };
630
+ }
631
+ // Queued attachments ride the same native-block path as a non-queued
632
+ // attachment prompt, prefixed with the steering framing.
633
+ const parts = [
634
+ { type: "text", text: STEERING_PREFIX },
635
+ ...this.buildAttachmentParts(m.text, m.attachments),
636
+ ];
637
+ return { role: "user", content: parts };
638
+ });
598
639
  }
599
640
  if (!this.settingsManager.get("idealReviewEnabled"))
600
641
  return null;
@@ -840,7 +881,11 @@ export class AgentSession {
840
881
  // Use getAllMcpServers so user-configured servers survive the reconnect.
841
882
  const servers = await getAllMcpServers(this.provider, apiKey, this.cwd);
842
883
  const mcpTools = await this.mcpManager.connectAll(servers);
843
- this.tools.push(...mcpTools);
884
+ // Drop stale MCP tools from both the live set and deferred catalog before
885
+ // re-adding. Some tools may already have been promoted out of the catalog.
886
+ this.tools = this.tools.filter((t) => !t.name.startsWith("mcp__"));
887
+ this.mcpCatalog?.removeWhere((name) => name.startsWith("mcp__"));
888
+ this.addMcpTools(mcpTools);
844
889
  }
845
890
  catch (err) {
846
891
  log("WARN", "mcp", `MCP reconnection failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -869,20 +914,39 @@ export class AgentSession {
869
914
  signal: this.opts.signal,
870
915
  });
871
916
  this.messages = result.messages;
872
- // Persist compacted messages to a new session file so `ezcoder continue`
873
- // picks up the compacted state instead of the full original history.
874
- const session = await this.sessionManager.create(this.cwd, this.provider, this.model);
875
- this.sessionId = session.id;
876
- this.sessionPath = session.path;
877
- // Write compacted messages (skip system it's rebuilt on load)
878
- for (const msg of this.messages) {
879
- if (msg.role === "system")
880
- continue;
881
- await this.persistMessage(msg);
917
+ // Transient sessions (Nolan chat/autopilot, subagent spawns) must NEVER touch
918
+ // the session store: without this guard, the first auto-compaction called
919
+ // sessionManager.create() and assigned a real sessionPath, silently turning
920
+ // the "in-memory only" session into a persisted one — every later turn (and
921
+ // every further compaction) then leaked a Nolan transcript file into the
922
+ // project's session list. Compact in memory only and keep sessionPath empty.
923
+ if (this.opts.transient) {
924
+ this.lastPersistedIndex = this.messages.length;
925
+ }
926
+ else {
927
+ // Persist compacted messages to a new session file so `ezcoder continue`
928
+ // picks up the compacted state instead of the full original history.
929
+ const session = await this.sessionManager.create(this.cwd, this.provider, this.model);
930
+ this.sessionId = session.id;
931
+ this.sessionPath = session.path;
932
+ // Write compacted messages (skip system — it's rebuilt on load)
933
+ for (const msg of this.messages) {
934
+ if (msg.role === "system")
935
+ continue;
936
+ await this.persistMessage(msg);
937
+ }
938
+ this.lastPersistedIndex = this.messages.length;
939
+ // Carry Nolan's advisory turns into the new file so they survive compaction.
940
+ await this.rePersistNolanTurns();
941
+ await this.rePersistAutopilotMarkers();
942
+ await this.rePersistAppMarkers();
943
+ // Persist the compaction counts so a resumed session's quiet notice can
944
+ // show the same "N → M messages" summary the live run did.
945
+ await this.persistAppMarker("compaction", {
946
+ originalCount: result.result.originalCount,
947
+ newCount: result.result.newCount,
948
+ });
882
949
  }
883
- this.lastPersistedIndex = this.messages.length;
884
- // Carry Nolan's advisory turns into the new file so they survive compaction.
885
- await this.rePersistNolanTurns();
886
950
  this.eventBus.emit("compaction_end", {
887
951
  originalCount: result.result.originalCount,
888
952
  newCount: result.result.newCount,
@@ -892,10 +956,31 @@ export class AgentSession {
892
956
  // A fresh session drops any in-flight plan state so its prompt is clean.
893
957
  this.planModeRef.current = false;
894
958
  this.approvedPlanPath = undefined;
959
+ // Display-only history belongs to the OLD session. Without this, stale Nolan
960
+ // turns / autopilot verdicts / app markers linger in memory, show up in the
961
+ // new session's /history, and get re-persisted into the new file by the
962
+ // next compaction — the cross-session duplicate-marker propagation bug.
963
+ this.nolanTurns = [];
964
+ this.autopilotMarkers = [];
965
+ this.appMarkers = [];
895
966
  const basePrompt = this.customSystemPrompt ??
896
967
  (await buildSystemPrompt(this.cwd, this.skills, false, undefined, this.tools.map((tool) => tool.name), undefined, this.provider));
897
968
  this.messages = [{ role: "system", content: basePrompt }];
898
- await this.createNewSession();
969
+ // Fresh conversation — new entries must not chain onto the old DAG's leaf.
970
+ this.currentLeafId = null;
971
+ // Transient sessions (Nolan chat/autopilot, subagent spawns) never touch the
972
+ // session store. Without this guard, autopilot's per-cycle resetReviewer
973
+ // (nolanAutoSession.newSession()) created a real session file EVERY review
974
+ // cycle — the stream of 3-line "## Who you are … Nolan Grout" sessions that
975
+ // polluted the project's session list.
976
+ if (this.opts.transient) {
977
+ this.sessionId = "";
978
+ this.sessionPath = "";
979
+ this.lastPersistedIndex = this.messages.length;
980
+ }
981
+ else {
982
+ await this.createNewSession();
983
+ }
899
984
  this.eventBus.emit("session_start", { sessionId: this.sessionId });
900
985
  }
901
986
  async loadSession(sessionPath) {
@@ -1040,6 +1125,12 @@ export class AgentSession {
1040
1125
  getNolanTurns() {
1041
1126
  return this.nolanTurns;
1042
1127
  }
1128
+ /** Autopilot verdict markers recorded against this session, in record order.
1129
+ * Used by the host to interleave the auto-review loop's markers back into
1130
+ * the transcript on resume, mirroring `getNolanTurns`. */
1131
+ getAutopilotMarkers() {
1132
+ return this.autopilotMarkers;
1133
+ }
1043
1134
  /**
1044
1135
  * Record one Nolan Grout (mentor agent) turn against this build session: the
1045
1136
  * user's question + Nolan's reply. Kept in memory for the live transcript and
@@ -1085,6 +1176,103 @@ export class AgentSession {
1085
1176
  await this.sessionManager.appendEntry(this.sessionPath, entry);
1086
1177
  }
1087
1178
  }
1179
+ /**
1180
+ * Record one autopilot verdict marker (prompted / done / human / capped)
1181
+ * against this build session. Kept in memory for the live transcript and
1182
+ * persisted as a `custom` entry (parentId null, same as Nolan turns) so a
1183
+ * resumed session renders the exact same Nolan bubble the live run showed
1184
+ * instead of dropping the marker or falling back to a raw verdict string.
1185
+ * No-op persistence for transient sessions (kept in memory only).
1186
+ */
1187
+ async persistAutopilotMarker(phase, extra) {
1188
+ const afterMessageCount = this.messages.filter((m) => m.role !== "system").length;
1189
+ const payload = {
1190
+ version: 1,
1191
+ phase,
1192
+ afterMessageCount,
1193
+ ...(extra?.reason !== undefined ? { reason: extra.reason } : {}),
1194
+ ...(extra?.body !== undefined ? { body: extra.body } : {}),
1195
+ };
1196
+ this.autopilotMarkers.push(payload);
1197
+ if (!this.sessionPath)
1198
+ return;
1199
+ const entry = {
1200
+ type: "custom",
1201
+ kind: AUTOPILOT_MARKER_CUSTOM_KIND,
1202
+ id: crypto.randomUUID(),
1203
+ parentId: null,
1204
+ timestamp: new Date().toISOString(),
1205
+ data: payload,
1206
+ };
1207
+ await this.sessionManager.appendEntry(this.sessionPath, entry);
1208
+ }
1209
+ /** Re-append the in-memory autopilot markers to the current session file.
1210
+ * Mirrors `rePersistNolanTurns` — called after a continuation/compaction file
1211
+ * is created so the auto-review history survives the rewrite. */
1212
+ async rePersistAutopilotMarkers() {
1213
+ if (!this.sessionPath)
1214
+ return;
1215
+ for (const payload of this.autopilotMarkers) {
1216
+ const entry = {
1217
+ type: "custom",
1218
+ kind: AUTOPILOT_MARKER_CUSTOM_KIND,
1219
+ id: crypto.randomUUID(),
1220
+ parentId: null,
1221
+ timestamp: new Date().toISOString(),
1222
+ data: payload,
1223
+ };
1224
+ await this.sessionManager.appendEntry(this.sessionPath, entry);
1225
+ }
1226
+ }
1227
+ /** App transcript markers recorded against this session, in record order.
1228
+ * Used by the host to interleave display-only rows (plan banner, task
1229
+ * header, errors, user-bubble hints) back into the transcript on resume. */
1230
+ getAppMarkers() {
1231
+ return this.appMarkers;
1232
+ }
1233
+ /**
1234
+ * Record one app transcript marker (display-only row) against this session.
1235
+ * Same treatment as autopilot markers: kept in memory for the live
1236
+ * transcript, persisted as a `custom` entry (parentId null, never on the
1237
+ * message DAG) so a resumed session shows the identical row. `anchorOffset`
1238
+ * shifts the recorded `afterMessageCount` — pass +1 for a marker that should
1239
+ * attach to the user message about to be pushed by the imminent prompt.
1240
+ * No-op persistence for transient sessions.
1241
+ */
1242
+ async persistAppMarker(kind, data, anchorOffset = 0) {
1243
+ const afterMessageCount = this.messages.filter((m) => m.role !== "system").length + anchorOffset;
1244
+ const payload = { version: 1, kind, afterMessageCount, data };
1245
+ this.appMarkers.push(payload);
1246
+ if (!this.sessionPath)
1247
+ return;
1248
+ const entry = {
1249
+ type: "custom",
1250
+ kind: APP_MARKER_CUSTOM_KIND,
1251
+ id: crypto.randomUUID(),
1252
+ parentId: null,
1253
+ timestamp: new Date().toISOString(),
1254
+ data: payload,
1255
+ };
1256
+ await this.sessionManager.appendEntry(this.sessionPath, entry);
1257
+ }
1258
+ /** Re-append the in-memory app markers to the current session file. Mirrors
1259
+ * `rePersistNolanTurns` — called after a continuation/compaction file is
1260
+ * created so display-only rows survive the rewrite. */
1261
+ async rePersistAppMarkers() {
1262
+ if (!this.sessionPath)
1263
+ return;
1264
+ for (const payload of this.appMarkers) {
1265
+ const entry = {
1266
+ type: "custom",
1267
+ kind: APP_MARKER_CUSTOM_KIND,
1268
+ id: crypto.randomUUID(),
1269
+ parentId: null,
1270
+ timestamp: new Date().toISOString(),
1271
+ data: payload,
1272
+ };
1273
+ await this.sessionManager.appendEntry(this.sessionPath, entry);
1274
+ }
1275
+ }
1088
1276
  /**
1089
1277
  * Generate a short LLM session title from the conversation so far (first user
1090
1278
  * message + first assistant reply). Best-effort; returns null on failure or
@@ -1165,9 +1353,11 @@ export class AgentSession {
1165
1353
  setSignal(signal) {
1166
1354
  this.opts = { ...this.opts, signal };
1167
1355
  }
1168
- /** True when speedProfile is "optimized" (1-h cache TTL + pre-warm). */
1356
+ /** True when speedProfile is "optimized" (1-h cache TTL + pre-warm), or the
1357
+ * session was constructed with `forceLongCacheRetention` (Nolan sessions). */
1169
1358
  isSpeedOptimized() {
1170
- return this.settingsManager?.get("speedProfile") === "optimized";
1359
+ return (this.opts.forceLongCacheRetention === true ||
1360
+ this.settingsManager?.get("speedProfile") === "optimized");
1171
1361
  }
1172
1362
  /**
1173
1363
  * Ordered auth-storage keys the current (provider, model) pair tries, first
@@ -1247,6 +1437,10 @@ export class AgentSession {
1247
1437
  // Restore Nolan's advisory turns (custom entries, not on the message branch) so
1248
1438
  // they reappear in the transcript and survive into the continuation file.
1249
1439
  this.nolanTurns = this.sessionManager.getNolanTurns(loaded.entries);
1440
+ // Restore autopilot verdict markers the same way (not on the message DAG).
1441
+ this.autopilotMarkers = this.sessionManager.getAutopilotMarkers(loaded.entries);
1442
+ // Restore app transcript markers (plan banner / task header / errors / hints).
1443
+ this.appMarkers = this.sessionManager.getAppMarkers(loaded.entries);
1250
1444
  // Track the current leaf for subsequent entries
1251
1445
  this.currentLeafId = loaded.header.leafId;
1252
1446
  // Rebuild messages: keep system, add loaded
@@ -1282,20 +1476,40 @@ export class AgentSession {
1282
1476
  before: String(compacted.result.originalCount),
1283
1477
  after: String(compacted.result.newCount),
1284
1478
  });
1479
+ // Compaction rewrote history, so the on-disk file no longer reflects
1480
+ // what's in memory — fork a fresh session file for the compacted state
1481
+ // (mirrors compact()'s own persistence) so `ezcoder continue` picks up
1482
+ // the summary instead of the full original transcript.
1483
+ const session = await this.sessionManager.create(this.cwd, this.provider, this.model);
1484
+ this.sessionId = session.id;
1485
+ this.sessionPath = session.path;
1486
+ this.currentLeafId = null;
1487
+ // Re-persist (compacted) messages — skip system, it's rebuilt on load
1488
+ for (const msg of this.messages) {
1489
+ if (msg.role === "system")
1490
+ continue;
1491
+ await this.persistMessage(msg);
1492
+ }
1493
+ this.lastPersistedIndex = this.messages.length;
1494
+ // Carry Nolan's restored turns into the continuation file.
1495
+ await this.rePersistNolanTurns();
1496
+ await this.rePersistAutopilotMarkers();
1497
+ await this.rePersistAppMarkers();
1498
+ // Record this load-time auto-compaction's counts for the resumed notice.
1499
+ await this.persistAppMarker("compaction", {
1500
+ originalCount: compacted.result.originalCount,
1501
+ newCount: compacted.result.newCount,
1502
+ });
1503
+ return;
1285
1504
  }
1286
- // Create new session file for continuation
1287
- const session = await this.sessionManager.create(this.cwd, this.provider, this.model);
1288
- this.sessionId = session.id;
1289
- this.sessionPath = session.path;
1290
- // Re-persist (compacted) messagesskip system, it's rebuilt on load
1291
- for (const msg of this.messages) {
1292
- if (msg.role === "system")
1293
- continue;
1294
- await this.persistMessage(msg);
1295
- }
1505
+ // Plain resume (no compaction needed): keep using the original session
1506
+ // file/id and append future turns to it in place. Forking a new file here
1507
+ // unconditionally used to create a byte-identical duplicate every time a
1508
+ // session was merely reopened (e.g. app/window restart) with zero new
1509
+ // messages in betweenthe duplicate entries seen in the session list.
1510
+ this.sessionId = loaded.header.id;
1511
+ this.sessionPath = sessionPath;
1296
1512
  this.lastPersistedIndex = this.messages.length;
1297
- // Carry Nolan's restored turns into the continuation file.
1298
- await this.rePersistNolanTurns();
1299
1513
  }
1300
1514
  async prepareDynamicContext(_latestUserPrompt) {
1301
1515
  return this.messages;