@adhdev/daemon-core 0.9.18 → 0.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1961,6 +1961,7 @@ var init_provider_cli_runtime = __esm({
1961
1961
  var provider_cli_adapter_exports = {};
1962
1962
  __export(provider_cli_adapter_exports, {
1963
1963
  ProviderCliAdapter: () => ProviderCliAdapter,
1964
+ appendBoundedText: () => appendBoundedText,
1964
1965
  normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
1965
1966
  });
1966
1967
  function normalizeComparableTranscriptText(value) {
@@ -1993,6 +1994,14 @@ function parsedTranscriptIsRicherThanCommitted(parsedMessages, committedMessages
1993
1994
  }
1994
1995
  return false;
1995
1996
  }
1997
+ function appendBoundedText(current, chunk, maxChars) {
1998
+ if (!chunk) return current.length <= maxChars ? current : current.slice(-maxChars);
1999
+ if (maxChars <= 0) return "";
2000
+ if (chunk.length >= maxChars) return chunk.slice(-maxChars);
2001
+ const keepFromCurrent = maxChars - chunk.length;
2002
+ if (current.length <= keepFromCurrent) return current + chunk;
2003
+ return current.slice(-keepFromCurrent) + chunk;
2004
+ }
1996
2005
  var os10, ProviderCliAdapter;
1997
2006
  var init_provider_cli_adapter = __esm({
1998
2007
  "src/cli-adapters/provider-cli-adapter.ts"() {
@@ -2070,15 +2079,17 @@ var init_provider_cli_adapter = __esm({
2070
2079
  startupFirstOutputAt = 0;
2071
2080
  // PTY I/O
2072
2081
  onPtyDataCallback = null;
2073
- pendingOutputParseBuffer = "";
2082
+ pendingOutputParseChunks = [];
2074
2083
  pendingOutputParseTimer = null;
2075
- ptyOutputBuffer = "";
2084
+ ptyOutputChunks = [];
2076
2085
  ptyOutputFlushTimer = null;
2077
2086
  pendingTerminalQueryTail = "";
2078
2087
  lastOutputAt = 0;
2079
2088
  lastNonEmptyOutputAt = 0;
2080
2089
  lastScreenChangeAt = 0;
2081
2090
  lastScreenSnapshot = "";
2091
+ lastScreenText = "";
2092
+ lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
2082
2093
  // Server log forwarding
2083
2094
  serverConn = null;
2084
2095
  logBuffer = [];
@@ -2124,6 +2135,9 @@ var init_provider_cli_adapter = __esm({
2124
2135
  traceSeq = 0;
2125
2136
  traceSessionId = "";
2126
2137
  parsedStatusCache = null;
2138
+ lastStatusHotPathParseAt = Number.NEGATIVE_INFINITY;
2139
+ static STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS = 1e3;
2140
+ static SCREEN_SNAPSHOT_MIN_INTERVAL_MS = 250;
2127
2141
  static MAX_TRACE_ENTRIES = 250;
2128
2142
  providerResolutionMeta;
2129
2143
  static FINISH_RETRY_DELAY_MS = 300;
@@ -2132,6 +2146,30 @@ var init_provider_cli_adapter = __esm({
2132
2146
  this.messages = [...this.committedMessages];
2133
2147
  this.structuredMessages = [...this.committedMessages];
2134
2148
  }
2149
+ readTerminalScreenText(now = Date.now()) {
2150
+ const screenText = this.terminalScreen.getText() || "";
2151
+ this.lastScreenText = screenText;
2152
+ this.lastScreenSnapshotReadAt = now;
2153
+ return screenText;
2154
+ }
2155
+ shouldReadTerminalScreenSnapshot(now) {
2156
+ if (!this.lastScreenText) return true;
2157
+ return now - this.lastScreenSnapshotReadAt >= _ProviderCliAdapter.SCREEN_SNAPSHOT_MIN_INTERVAL_MS;
2158
+ }
2159
+ resetTerminalScreen(rows, cols) {
2160
+ this.terminalScreen.reset(rows, cols);
2161
+ this.lastScreenText = "";
2162
+ this.lastScreenSnapshot = "";
2163
+ this.lastScreenChangeAt = 0;
2164
+ this.lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
2165
+ }
2166
+ getFreshParsedStatusCache() {
2167
+ const cached = this.parsedStatusCache;
2168
+ if (cached && cached.committedMessagesRef === this.committedMessages && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBuffer === this.accumulatedRawBuffer && cached.screenText === this.lastScreenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName && cached.lastOutputAt === this.lastOutputAt) {
2169
+ return cached.result;
2170
+ }
2171
+ return null;
2172
+ }
2135
2173
  getIdleFinishConfirmMs() {
2136
2174
  return this.timeouts.idleFinishConfirm;
2137
2175
  }
@@ -2246,9 +2284,9 @@ var init_provider_cli_adapter = __esm({
2246
2284
  clearTimeout(this.pendingOutputParseTimer);
2247
2285
  this.pendingOutputParseTimer = null;
2248
2286
  }
2249
- if (!this.pendingOutputParseBuffer) return;
2250
- const rawData = this.pendingOutputParseBuffer;
2251
- this.pendingOutputParseBuffer = "";
2287
+ if (this.pendingOutputParseChunks.length === 0) return;
2288
+ const rawData = this.pendingOutputParseChunks.join("");
2289
+ this.pendingOutputParseChunks = [];
2252
2290
  this.handleOutput(rawData);
2253
2291
  }
2254
2292
  async spawn() {
@@ -2303,7 +2341,7 @@ var init_provider_cli_adapter = __esm({
2303
2341
  terminalScreen: this.terminalScreen
2304
2342
  });
2305
2343
  }
2306
- this.pendingOutputParseBuffer += data;
2344
+ this.pendingOutputParseChunks.push(data);
2307
2345
  if (!this.pendingOutputParseTimer) {
2308
2346
  this.pendingOutputParseTimer = setTimeout(() => {
2309
2347
  this.pendingOutputParseTimer = null;
@@ -2311,13 +2349,13 @@ var init_provider_cli_adapter = __esm({
2311
2349
  }, this.timeouts.ptyFlush);
2312
2350
  }
2313
2351
  if (this.onPtyDataCallback) {
2314
- this.ptyOutputBuffer += data;
2352
+ this.ptyOutputChunks.push(data);
2315
2353
  if (!this.ptyOutputFlushTimer) {
2316
2354
  this.ptyOutputFlushTimer = setTimeout(() => {
2317
- if (this.ptyOutputBuffer && this.onPtyDataCallback) {
2318
- this.onPtyDataCallback(this.ptyOutputBuffer);
2355
+ if (this.ptyOutputChunks.length > 0 && this.onPtyDataCallback) {
2356
+ this.onPtyDataCallback(this.ptyOutputChunks.join(""));
2319
2357
  }
2320
- this.ptyOutputBuffer = "";
2358
+ this.ptyOutputChunks = [];
2321
2359
  this.ptyOutputFlushTimer = null;
2322
2360
  }, this.timeouts.ptyFlush);
2323
2361
  }
@@ -2342,7 +2380,7 @@ var init_provider_cli_adapter = __esm({
2342
2380
  clearTimeout(this.startupSettleTimer);
2343
2381
  this.startupSettleTimer = null;
2344
2382
  }
2345
- this.terminalScreen.reset(24, 80);
2383
+ this.resetTerminalScreen(24, 80);
2346
2384
  this.pendingTerminalQueryTail = "";
2347
2385
  this.currentTurnScope = null;
2348
2386
  this.finishRetryCount = 0;
@@ -2364,11 +2402,12 @@ var init_provider_cli_adapter = __esm({
2364
2402
  this.terminalScreen.write(rawData);
2365
2403
  const cleanData = sanitizeTerminalText(rawData);
2366
2404
  const now = Date.now();
2367
- const screenText = this.terminalScreen.getText();
2368
- const normalizedScreenSnapshot = normalizeScreenSnapshot(screenText);
2405
+ const shouldReadScreen = this.shouldReadTerminalScreenSnapshot(now);
2406
+ const screenText = shouldReadScreen ? this.readTerminalScreenText(now) : this.lastScreenText;
2407
+ const normalizedScreenSnapshot = shouldReadScreen ? normalizeScreenSnapshot(screenText) : this.lastScreenSnapshot;
2369
2408
  this.lastOutputAt = now;
2370
2409
  if (cleanData.trim()) this.lastNonEmptyOutputAt = now;
2371
- if (normalizedScreenSnapshot !== this.lastScreenSnapshot) {
2410
+ if (shouldReadScreen && normalizedScreenSnapshot !== this.lastScreenSnapshot) {
2372
2411
  this.lastScreenSnapshot = normalizedScreenSnapshot;
2373
2412
  this.lastScreenChangeAt = now;
2374
2413
  }
@@ -2390,7 +2429,7 @@ var init_provider_cli_adapter = __esm({
2390
2429
  this.scheduleStartupSettleCheck();
2391
2430
  }
2392
2431
  if (this.isWaitingForResponse && cleanData) {
2393
- this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
2432
+ this.responseBuffer = appendBoundedText(this.responseBuffer, cleanData, 8e3);
2394
2433
  }
2395
2434
  if (cleanData.trim()) {
2396
2435
  if (this.serverConn) {
@@ -2399,11 +2438,11 @@ var init_provider_cli_adapter = __esm({
2399
2438
  this.logBuffer.push({ message: cleanData.trim(), level: "info" });
2400
2439
  }
2401
2440
  }
2402
- this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
2403
2441
  const prevAccumulatedLen = this.accumulatedBuffer.length;
2404
2442
  const prevAccumulatedRawLen = this.accumulatedRawBuffer.length;
2405
- this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2406
- this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2443
+ this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData, 1e3);
2444
+ this.accumulatedBuffer = appendBoundedText(this.accumulatedBuffer, cleanData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2445
+ this.accumulatedRawBuffer = appendBoundedText(this.accumulatedRawBuffer, rawData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2407
2446
  if (this.currentTurnScope) {
2408
2447
  const droppedClean = prevAccumulatedLen + cleanData.length - this.accumulatedBuffer.length;
2409
2448
  const droppedRaw = prevAccumulatedRawLen + rawData.length - this.accumulatedRawBuffer.length;
@@ -2414,14 +2453,14 @@ var init_provider_cli_adapter = __esm({
2414
2453
  this.currentTurnScope.rawBufferStart = Math.max(0, this.currentTurnScope.rawBufferStart - droppedRaw);
2415
2454
  }
2416
2455
  }
2417
- this.resolveStartupState("output");
2456
+ this.resolveStartupState("output", screenText, normalizedScreenSnapshot, now);
2418
2457
  this.scheduleSettle();
2419
2458
  }
2420
- resolveStartupState(trigger) {
2459
+ resolveStartupState(trigger, screenTextOverride, normalizedScreenOverride, nowOverride) {
2421
2460
  if (!this.startupParseGate) return;
2422
- const now = Date.now();
2423
- const screenText = this.terminalScreen.getText() || "";
2424
- const normalizedScreen = normalizeScreenSnapshot(screenText);
2461
+ const now = typeof nowOverride === "number" ? nowOverride : Date.now();
2462
+ const screenText = typeof screenTextOverride === "string" ? screenTextOverride : this.readTerminalScreenText();
2463
+ const normalizedScreen = typeof normalizedScreenOverride === "string" ? normalizedScreenOverride : normalizeScreenSnapshot(screenText);
2425
2464
  const hasStartupOutput = !!this.startupFirstOutputAt || !!normalizedScreen.trim();
2426
2465
  if (!hasStartupOutput) return;
2427
2466
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -2656,40 +2695,28 @@ var init_provider_cli_adapter = __esm({
2656
2695
  }, delayTime);
2657
2696
  return;
2658
2697
  }
2659
- const tail = this.settledBuffer;
2660
- const screenText = this.terminalScreen.getText() || "";
2661
2698
  this.resolveStartupState("settled");
2662
- if (this.startupParseGate) {
2663
- return;
2664
- }
2665
- const parsedTranscript = this.parseCurrentTranscript(
2666
- this.committedMessages,
2667
- this.responseBuffer,
2668
- this.currentTurnScope
2669
- );
2670
- const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
2671
- const modal = this.runParseApproval(tail) || parsedModal;
2672
- const rawScriptStatus = this.runDetectStatus(tail);
2673
- const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
2674
- const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
2699
+ if (this.startupParseGate) return;
2700
+ const session = this.runParseSession();
2701
+ if (!session) return;
2702
+ const { status, messages, modal, parsedStatus } = session;
2703
+ const parsedMessages = normalizeCliParsedMessages(messages, {
2675
2704
  committedMessages: this.committedMessages,
2676
2705
  scope: this.currentTurnScope,
2677
2706
  lastOutputAt: this.lastOutputAt
2678
- }) : [];
2679
- if (this.maybeCommitVisibleIdleTranscript(parsedTranscript)) {
2680
- return;
2681
- }
2682
- const lastParsedAssistant = [...parsedMessages].reverse().find((message) => message.role === "assistant");
2683
- const parsedShowsLiveAssistantProgress = parsedTranscript?.status === "generating" && !!lastParsedAssistant && parsedMessages.length > this.committedMessages.length;
2707
+ });
2708
+ if (this.maybeCommitVisibleIdleTranscript(session, parsedMessages)) return;
2709
+ const lastParsedAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant");
2684
2710
  const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet || this.currentTurnScope?.prompt || "");
2711
+ const screenText = this.terminalScreen.getText() || "";
2685
2712
  this.recordTrace("settled", {
2686
- tail: summarizeCliTraceText(tail, 500),
2713
+ tail: summarizeCliTraceText(this.settledBuffer, 500),
2687
2714
  screenText: summarizeCliTraceText(screenText, 1200),
2688
- detectStatus: scriptStatus,
2689
- parsedStatus: parsedTranscript?.status || null,
2715
+ detectStatus: status,
2716
+ parsedStatus: parsedStatus || null,
2690
2717
  parsedMessageCount: parsedMessages.length,
2691
2718
  parsedLastAssistant: lastParsedAssistant ? summarizeCliTraceText(lastParsedAssistant.content, 280) : "",
2692
- parsedActiveModal: parsedTranscript?.activeModal ?? null,
2719
+ parsedActiveModal: modal,
2693
2720
  approval: modal,
2694
2721
  ...buildCliTraceParseSnapshot({
2695
2722
  accumulatedBuffer: this.accumulatedBuffer,
@@ -2723,36 +2750,36 @@ var init_provider_cli_adapter = __esm({
2723
2750
  `[${this.cliType}] Settled without assistant: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 220)).slice(0, 260)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"}`
2724
2751
  );
2725
2752
  }
2726
- if (!scriptStatus) return;
2753
+ if (!status) return;
2727
2754
  const prevStatus = this.currentStatus;
2728
- const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
2755
+ const ctx = { now, modal, status, parsedMessages, lastParsedAssistant, parsedStatus: parsedStatus || null, prevStatus };
2729
2756
  if (!this.applyPendingScriptStatusDebounce(ctx)) return;
2730
2757
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
2731
2758
  LOG.info(
2732
2759
  "CLI",
2733
- `[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 140)} scriptStatus=${String(scriptStatus || "")} parsedStatus=${String(parsedTranscript?.status || "")} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || "", 120)).slice(0, 160)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
2760
+ `[${this.cliType}] settled diagnostics prompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 140)} status=${String(status || "")} parsedStatus=${String(parsedStatus || "")} parsedMsgCount=${parsedMessages.length} lastParsedAssistant=${JSON.stringify(summarizeCliTraceText(lastParsedAssistant?.content || "", 120)).slice(0, 160)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
2734
2761
  );
2735
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
2762
+ const shouldHoldGenerating = status === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedStatus === "idle" && !!lastParsedAssistant);
2736
2763
  if (shouldHoldGenerating) {
2737
2764
  this.applyHoldGenerating(ctx, recentInteractiveActivity);
2738
2765
  return;
2739
2766
  }
2740
- if (scriptStatus === "waiting_approval") {
2767
+ if (status === "waiting_approval") {
2741
2768
  this.applyWaitingApproval(ctx);
2742
2769
  return;
2743
2770
  }
2744
- if (scriptStatus === "generating") {
2771
+ if (status === "generating") {
2745
2772
  this.applyGenerating(ctx);
2746
2773
  return;
2747
2774
  }
2748
- if (scriptStatus === "idle") {
2775
+ if (status === "idle") {
2749
2776
  this.applyIdle(ctx, now);
2750
2777
  }
2751
2778
  }
2752
2779
  // Returns false if the caller should bail out (debounce pending).
2753
2780
  applyPendingScriptStatusDebounce(ctx) {
2754
- const { now, scriptStatus, prevStatus } = ctx;
2755
- const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
2781
+ const { now, status, prevStatus } = ctx;
2782
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
2756
2783
  if (!shouldDebounce) {
2757
2784
  this.pendingScriptStatus = null;
2758
2785
  this.pendingScriptStatusSince = 0;
@@ -2770,8 +2797,8 @@ var init_provider_cli_adapter = __esm({
2770
2797
  this.evaluateSettled();
2771
2798
  }, delayMs);
2772
2799
  };
2773
- if (this.pendingScriptStatus !== scriptStatus) {
2774
- this.pendingScriptStatus = scriptStatus;
2800
+ if (this.pendingScriptStatus !== status) {
2801
+ this.pendingScriptStatus = status;
2775
2802
  this.pendingScriptStatusSince = now;
2776
2803
  armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
2777
2804
  return false;
@@ -2784,7 +2811,7 @@ var init_provider_cli_adapter = __esm({
2784
2811
  return true;
2785
2812
  }
2786
2813
  applyHoldGenerating(ctx, recentInteractiveActivity) {
2787
- const { scriptStatus } = ctx;
2814
+ const { status } = ctx;
2788
2815
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
2789
2816
  this.setStatus("generating", "recent_activity_hold");
2790
2817
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
@@ -2795,7 +2822,7 @@ var init_provider_cli_adapter = __esm({
2795
2822
  }
2796
2823
  }, this.timeouts.generatingIdle);
2797
2824
  this.recordTrace("hold_generating_recent_activity", {
2798
- scriptStatus,
2825
+ scriptStatus: status,
2799
2826
  recentInteractiveActivity,
2800
2827
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
2801
2828
  lastScreenChangeAt: this.lastScreenChangeAt,
@@ -2849,11 +2876,13 @@ var init_provider_cli_adapter = __esm({
2849
2876
  }
2850
2877
  }
2851
2878
  applyGenerating(ctx) {
2852
- const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
2879
+ const { modal, parsedMessages, lastParsedAssistant, parsedStatus, prevStatus } = ctx;
2853
2880
  this.clearIdleFinishCandidate("generating");
2881
+ const screenText = this.terminalScreen.getText() || "";
2854
2882
  const effectiveScreenText = screenText || this.accumulatedBuffer;
2855
2883
  const noActiveTurn = !this.currentTurnScope;
2856
2884
  const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(effectiveScreenText) || /accept edits on/i.test(effectiveScreenText) && (/Update available!/i.test(screenText) || /\/effort/i.test(screenText) || /^.*➜\s+\S+/m.test(effectiveScreenText));
2885
+ const parsedShowsLiveAssistantProgress = parsedStatus === "generating" && !!lastParsedAssistant && parsedMessages.length > this.committedMessages.length;
2857
2886
  if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
2858
2887
  return;
2859
2888
  }
@@ -2880,7 +2909,7 @@ var init_provider_cli_adapter = __esm({
2880
2909
  this.onStatusChange?.();
2881
2910
  }
2882
2911
  applyIdle(ctx, now) {
2883
- const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
2912
+ const { modal, lastParsedAssistant, prevStatus } = ctx;
2884
2913
  if (prevStatus === "waiting_approval") {
2885
2914
  if (this.approvalExitTimeout) {
2886
2915
  clearTimeout(this.approvalExitTimeout);
@@ -3002,20 +3031,15 @@ var init_provider_cli_adapter = __esm({
3002
3031
  this.setStatus("idle", "response_finished");
3003
3032
  this.onStatusChange?.();
3004
3033
  }
3005
- maybeCommitVisibleIdleTranscript(parsed) {
3034
+ maybeCommitVisibleIdleTranscript(session, parsedMessages) {
3006
3035
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
3007
3036
  if (!allowImmediateScriptIdleCommit) return false;
3008
- if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
3037
+ if (!session || session.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || session.modal) {
3009
3038
  return false;
3010
3039
  }
3011
- const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
3012
- committedMessages: this.committedMessages,
3013
- scope: this.currentTurnScope,
3014
- lastOutputAt: this.lastOutputAt
3015
- });
3016
- const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
3040
+ const visibleAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant" && m.content.trim());
3017
3041
  if (!visibleAssistant) return false;
3018
- this.committedMessages = hydratedForIdleCommit;
3042
+ this.committedMessages = parsedMessages;
3019
3043
  this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
3020
3044
  this.clearAllTimers();
3021
3045
  this.syncMessageViews();
@@ -3093,6 +3117,59 @@ var init_provider_cli_adapter = __esm({
3093
3117
  };
3094
3118
  }
3095
3119
  // ─── Script Execution ──────────────────────────
3120
+ runParseSession() {
3121
+ if (typeof this.cliScripts?.parseSession === "function") {
3122
+ try {
3123
+ const screenText = this.terminalScreen.getText();
3124
+ const tail = this.recentOutputBuffer.slice(-500);
3125
+ const input = buildCliParseInput({
3126
+ accumulatedBuffer: this.accumulatedBuffer,
3127
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3128
+ recentOutputBuffer: this.recentOutputBuffer,
3129
+ terminalScreenText: screenText,
3130
+ baseMessages: this.committedMessages,
3131
+ partialResponse: this.responseBuffer,
3132
+ isWaitingForResponse: this.isWaitingForResponse,
3133
+ scope: this.currentTurnScope,
3134
+ runtimeSettings: this.runtimeSettings
3135
+ });
3136
+ const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3137
+ this.parseErrorMessage = null;
3138
+ return session && typeof session === "object" ? session : null;
3139
+ } catch (e) {
3140
+ const message = e?.message || String(e);
3141
+ this.parseErrorMessage = message;
3142
+ LOG.warn("CLI", `[${this.cliType}] parseSession error: ${message}`);
3143
+ return null;
3144
+ }
3145
+ }
3146
+ if (!this.cliScripts?.detectStatus && !this.cliScripts?.parseOutput) return null;
3147
+ try {
3148
+ const tail = this.settledBuffer;
3149
+ const parsedTranscript = this.parseCurrentTranscript(
3150
+ this.committedMessages,
3151
+ this.responseBuffer,
3152
+ this.currentTurnScope
3153
+ );
3154
+ const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((b) => typeof b === "string" && b.trim()) ? parsedTranscript.activeModal : null;
3155
+ const approval = this.runParseApproval(tail);
3156
+ const modal = approval || parsedModal;
3157
+ const rawStatus = this.runDetectStatus(tail);
3158
+ const parsedStatus = typeof parsedTranscript?.status === "string" ? parsedTranscript.status : null;
3159
+ const effectiveStatus = parsedStatus === "waiting_approval" && modal ? "waiting_approval" : rawStatus || parsedStatus || "idle";
3160
+ return {
3161
+ status: effectiveStatus,
3162
+ messages: Array.isArray(parsedTranscript?.messages) ? parsedTranscript.messages : [],
3163
+ modal,
3164
+ parsedStatus
3165
+ };
3166
+ } catch (e) {
3167
+ const message = e?.message || String(e);
3168
+ this.parseErrorMessage = message;
3169
+ LOG.warn("CLI", `[${this.cliType}] parseSession fallback error: ${message}`);
3170
+ return null;
3171
+ }
3172
+ }
3096
3173
  runDetectStatus(text) {
3097
3174
  if (!this.cliScripts?.detectStatus) return null;
3098
3175
  try {
@@ -3166,14 +3243,21 @@ var init_provider_cli_adapter = __esm({
3166
3243
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
3167
3244
  let effectiveModal = startupModal || this.activeModal;
3168
3245
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
3169
- try {
3170
- const parsed = this.getScriptParsedStatus();
3171
- const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons) && parsed.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsed.activeModal : null;
3172
- if (parsed?.status === "waiting_approval" && parsedModal) {
3173
- effectiveStatus = "waiting_approval";
3174
- effectiveModal = parsedModal;
3246
+ let parsed = this.getFreshParsedStatusCache();
3247
+ if (!parsed && effectiveStatus !== "idle") {
3248
+ const now = Date.now();
3249
+ if (now - this.lastStatusHotPathParseAt >= _ProviderCliAdapter.STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS) {
3250
+ this.lastStatusHotPathParseAt = now;
3251
+ try {
3252
+ parsed = this.getScriptParsedStatus();
3253
+ } catch {
3254
+ }
3175
3255
  }
3176
- } catch {
3256
+ }
3257
+ const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons) && parsed.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsed.activeModal : null;
3258
+ if (parsed?.status === "waiting_approval" && parsedModal) {
3259
+ effectiveStatus = "waiting_approval";
3260
+ effectiveModal = parsedModal;
3177
3261
  }
3178
3262
  }
3179
3263
  return {
@@ -3205,7 +3289,7 @@ var init_provider_cli_adapter = __esm({
3205
3289
  * Called by command handler / dashboard for rich content rendering.
3206
3290
  */
3207
3291
  getScriptParsedStatus() {
3208
- const screenText = this.terminalScreen.getText();
3292
+ const screenText = this.readTerminalScreenText();
3209
3293
  const cached = this.parsedStatusCache;
3210
3294
  if (cached && cached.committedMessagesRef === this.committedMessages && cached.responseBuffer === this.responseBuffer && cached.currentTurnScope === this.currentTurnScope && cached.recentOutputBuffer === this.recentOutputBuffer && cached.accumulatedBuffer === this.accumulatedBuffer && cached.accumulatedRawBuffer === this.accumulatedRawBuffer && cached.screenText === screenText && cached.currentStatus === this.currentStatus && cached.activeModal === this.activeModal && cached.cliName === this.cliName && cached.lastOutputAt === this.lastOutputAt) {
3211
3295
  return cached.result;
@@ -3225,8 +3309,21 @@ var init_provider_cli_adapter = __esm({
3225
3309
  this.onStatusChange?.();
3226
3310
  }
3227
3311
  }
3228
- if (this.maybeCommitVisibleIdleTranscript(parsed)) {
3229
- return this.getScriptParsedStatus();
3312
+ if (parsed && Array.isArray(parsed.messages)) {
3313
+ const hydratedForCommit = normalizeCliParsedMessages(parsed.messages, {
3314
+ committedMessages: this.committedMessages,
3315
+ scope: this.currentTurnScope,
3316
+ lastOutputAt: this.lastOutputAt
3317
+ });
3318
+ const fakeSession = {
3319
+ status: parsed.status || "idle",
3320
+ messages: parsed.messages,
3321
+ modal: parsedModal,
3322
+ parsedStatus: parsed.status || null
3323
+ };
3324
+ if (this.maybeCommitVisibleIdleTranscript(fakeSession, hydratedForCommit)) {
3325
+ return this.getScriptParsedStatus();
3326
+ }
3230
3327
  }
3231
3328
  const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
3232
3329
  let result;
@@ -3408,6 +3505,17 @@ var init_provider_cli_adapter = __esm({
3408
3505
  }
3409
3506
  await this.sendMessage(promptText);
3410
3507
  }
3508
+ isSubmitStuck(normalizedPromptSnippet) {
3509
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return false;
3510
+ if (this.hasActionableApproval()) return false;
3511
+ if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return false;
3512
+ const screenText = this.terminalScreen.getText();
3513
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return false;
3514
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
3515
+ if (liveApproval) return false;
3516
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
3517
+ return liveStatus !== "generating" && liveStatus !== "waiting_approval";
3518
+ }
3411
3519
  async writeToPty(data) {
3412
3520
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
3413
3521
  await this.ptyProcess.write(data);
@@ -3431,6 +3539,121 @@ var init_provider_cli_adapter = __esm({
3431
3539
  this.finishRetryTimer = null;
3432
3540
  }
3433
3541
  }
3542
+ commitSendUserTurn(state) {
3543
+ if (state.didCommitUserTurn) return;
3544
+ state.didCommitUserTurn = true;
3545
+ this.committedMessages.push({ role: "user", content: state.text, timestamp: Date.now() });
3546
+ this.syncMessageViews();
3547
+ }
3548
+ armResponseTimeout() {
3549
+ if (this.responseTimeout) clearTimeout(this.responseTimeout);
3550
+ this.responseTimeout = setTimeout(() => {
3551
+ if (this.isWaitingForResponse) this.finishResponse();
3552
+ }, this.timeouts.maxResponse);
3553
+ }
3554
+ writeSubmitKeyForRetry(mode) {
3555
+ void this.writeToPty(this.sendKey).catch((error) => {
3556
+ LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error?.message || error}`);
3557
+ });
3558
+ }
3559
+ retrySubmitIfStuck(state, attempt) {
3560
+ this.submitRetryTimer = null;
3561
+ if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
3562
+ const screenText = this.terminalScreen.getText();
3563
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3564
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
3565
+ this.recordTrace("submit_write", {
3566
+ mode: "submit_retry",
3567
+ attempt,
3568
+ sendKey: this.sendKey,
3569
+ screenText: summarizeCliTraceText(screenText, 500)
3570
+ });
3571
+ this.writeSubmitKeyForRetry("submit_retry");
3572
+ if (attempt >= 3) {
3573
+ this.submitRetryUsed = true;
3574
+ return;
3575
+ }
3576
+ this.submitRetryTimer = setTimeout(() => this.retrySubmitIfStuck(state, attempt + 1), state.retryDelayMs);
3577
+ }
3578
+ retryImmediateSubmitIfStuck(state) {
3579
+ this.submitRetryTimer = null;
3580
+ if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
3581
+ const screenText = this.terminalScreen.getText();
3582
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3583
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
3584
+ this.recordTrace("submit_write", {
3585
+ mode: "immediate_retry",
3586
+ attempt: 1,
3587
+ sendKey: this.sendKey,
3588
+ screenText: summarizeCliTraceText(screenText, 500)
3589
+ });
3590
+ this.writeSubmitKeyForRetry("immediate_retry");
3591
+ this.submitRetryUsed = true;
3592
+ }
3593
+ submitSendKey(state, completion) {
3594
+ if (!this.ptyProcess) {
3595
+ completion.resolveOnce();
3596
+ return;
3597
+ }
3598
+ this.submitPendingUntil = 0;
3599
+ const screenText = this.terminalScreen.getText();
3600
+ this.recordTrace("submit_write", {
3601
+ mode: "submit_key",
3602
+ sendKey: this.sendKey,
3603
+ screenText: summarizeCliTraceText(screenText, 500)
3604
+ });
3605
+ void this.writeToPty(this.sendKey).then(() => {
3606
+ this.commitSendUserTurn(state);
3607
+ this.submitRetryTimer = setTimeout(() => this.retrySubmitIfStuck(state, 1), state.retryDelayMs);
3608
+ this.armResponseTimeout();
3609
+ completion.resolveOnce();
3610
+ }, completion.rejectOnce);
3611
+ }
3612
+ submitImmediatePrompt(state, completion) {
3613
+ this.submitPendingUntil = 0;
3614
+ this.recordTrace("submit_write", {
3615
+ mode: "immediate",
3616
+ text: summarizeCliTraceText(state.text, 500),
3617
+ sendKey: this.sendKey,
3618
+ screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
3619
+ });
3620
+ void this.writeToPty(state.text + this.sendKey).then(() => {
3621
+ this.commitSendUserTurn(state);
3622
+ this.submitRetryTimer = setTimeout(() => this.retryImmediateSubmitIfStuck(state), state.retryDelayMs);
3623
+ this.armResponseTimeout();
3624
+ completion.resolveOnce();
3625
+ }, completion.rejectOnce);
3626
+ }
3627
+ waitForEchoAndSubmit(state, completion, submitStartedAt, lastNormalizedScreen = "", lastScreenChangeAt = submitStartedAt) {
3628
+ if (!this.ptyProcess) {
3629
+ completion.resolveOnce();
3630
+ return;
3631
+ }
3632
+ const now = Date.now();
3633
+ const elapsed = now - submitStartedAt;
3634
+ const screenText = this.terminalScreen.getText();
3635
+ const normalizedScreen = normalizePromptText(screenText);
3636
+ const nextScreenChangeAt = normalizedScreen !== lastNormalizedScreen ? now : lastScreenChangeAt;
3637
+ const echoVisible = !state.normalizedPromptSnippet || promptLikelyVisible(screenText, state.normalizedPromptSnippet);
3638
+ if (echoVisible) {
3639
+ const screenSettled = now - nextScreenChangeAt >= 500;
3640
+ if (elapsed >= state.submitDelayMs && screenSettled) {
3641
+ this.submitSendKey(state, completion);
3642
+ return;
3643
+ }
3644
+ }
3645
+ if (elapsed >= state.maxEchoWaitMs) {
3646
+ this.submitSendKey(state, completion);
3647
+ return;
3648
+ }
3649
+ setTimeout(() => this.waitForEchoAndSubmit(
3650
+ state,
3651
+ completion,
3652
+ submitStartedAt,
3653
+ normalizedScreen,
3654
+ nextScreenChangeAt
3655
+ ), 50);
3656
+ }
3434
3657
  async sendMessage(text) {
3435
3658
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
3436
3659
  const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
@@ -3506,12 +3729,13 @@ var init_provider_cli_adapter = __esm({
3506
3729
  const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
3507
3730
  const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
3508
3731
  const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
3509
- let didCommitUserTurn = false;
3510
- const commitUserTurn = () => {
3511
- if (didCommitUserTurn) return;
3512
- didCommitUserTurn = true;
3513
- this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
3514
- this.syncMessageViews();
3732
+ const sendState = {
3733
+ text,
3734
+ normalizedPromptSnippet,
3735
+ submitDelayMs,
3736
+ maxEchoWaitMs,
3737
+ retryDelayMs,
3738
+ didCommitUserTurn: false
3515
3739
  };
3516
3740
  if (this.settleTimer) {
3517
3741
  clearTimeout(this.settleTimer);
@@ -3519,110 +3743,23 @@ var init_provider_cli_adapter = __esm({
3519
3743
  }
3520
3744
  this.responseEpoch += 1;
3521
3745
  this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
3522
- const startResponseTimeout = () => {
3523
- if (this.responseTimeout) clearTimeout(this.responseTimeout);
3524
- this.responseTimeout = setTimeout(() => {
3525
- if (this.isWaitingForResponse) this.finishResponse();
3526
- }, this.timeouts.maxResponse);
3527
- };
3528
3746
  await new Promise((resolve12, reject) => {
3529
3747
  let resolved = false;
3530
- const resolveOnce = () => {
3531
- if (resolved) return;
3532
- resolved = true;
3533
- resolve12();
3534
- };
3535
- const rejectOnce = (error) => {
3536
- if (resolved) return;
3537
- this.resetPendingSendState("send_write_failed");
3538
- resolved = true;
3539
- reject(error);
3540
- };
3541
- const writeRetryKey = (mode) => {
3542
- void this.writeToPty(this.sendKey).catch((error) => {
3543
- LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error?.message || error}`);
3544
- });
3545
- };
3546
- const submit = () => {
3547
- if (!this.ptyProcess) {
3548
- resolveOnce();
3549
- return;
3748
+ const completion = {
3749
+ resolveOnce: () => {
3750
+ if (resolved) return;
3751
+ resolved = true;
3752
+ resolve12();
3753
+ },
3754
+ rejectOnce: (error) => {
3755
+ if (resolved) return;
3756
+ this.resetPendingSendState("send_write_failed");
3757
+ resolved = true;
3758
+ reject(error);
3550
3759
  }
3551
- this.submitPendingUntil = 0;
3552
- const screenText = this.terminalScreen.getText();
3553
- this.recordTrace("submit_write", {
3554
- mode: "submit_key",
3555
- sendKey: this.sendKey,
3556
- screenText: summarizeCliTraceText(screenText, 500)
3557
- });
3558
- const retrySubmitIfStuck = (attempt) => {
3559
- this.submitRetryTimer = null;
3560
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
3561
- if (this.hasActionableApproval()) return;
3562
- if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
3563
- const screenText2 = this.terminalScreen.getText();
3564
- if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
3565
- const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
3566
- if (liveApproval) return;
3567
- const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
3568
- if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
3569
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3570
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
3571
- this.recordTrace("submit_write", {
3572
- mode: "submit_retry",
3573
- attempt,
3574
- sendKey: this.sendKey,
3575
- screenText: summarizeCliTraceText(screenText2, 500)
3576
- });
3577
- writeRetryKey("submit_retry");
3578
- if (attempt >= 3) {
3579
- this.submitRetryUsed = true;
3580
- return;
3581
- }
3582
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
3583
- };
3584
- void this.writeToPty(this.sendKey).then(() => {
3585
- commitUserTurn();
3586
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
3587
- startResponseTimeout();
3588
- resolveOnce();
3589
- }, rejectOnce);
3590
3760
  };
3591
3761
  if (this.submitStrategy === "immediate") {
3592
- this.submitPendingUntil = 0;
3593
- this.recordTrace("submit_write", {
3594
- mode: "immediate",
3595
- text: summarizeCliTraceText(text, 500),
3596
- sendKey: this.sendKey,
3597
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
3598
- });
3599
- void this.writeToPty(text + this.sendKey).then(() => {
3600
- commitUserTurn();
3601
- this.submitRetryTimer = setTimeout(() => {
3602
- this.submitRetryTimer = null;
3603
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
3604
- if (this.hasActionableApproval()) return;
3605
- if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
3606
- const screenText = this.terminalScreen.getText();
3607
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
3608
- const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
3609
- if (liveApproval) return;
3610
- const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
3611
- if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
3612
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
3613
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3614
- this.recordTrace("submit_write", {
3615
- mode: "immediate_retry",
3616
- attempt: 1,
3617
- sendKey: this.sendKey,
3618
- screenText: summarizeCliTraceText(screenText, 500)
3619
- });
3620
- writeRetryKey("immediate_retry");
3621
- this.submitRetryUsed = true;
3622
- }, retryDelayMs);
3623
- startResponseTimeout();
3624
- resolveOnce();
3625
- }, rejectOnce);
3762
+ this.submitImmediatePrompt(sendState, completion);
3626
3763
  return;
3627
3764
  }
3628
3765
  if (submitDelayMs > 0) {
@@ -3635,36 +3772,10 @@ var init_provider_cli_adapter = __esm({
3635
3772
  screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
3636
3773
  });
3637
3774
  const submitStartedAt = Date.now();
3638
- let lastNormalizedScreen = "";
3639
- let lastScreenChangeAt = submitStartedAt;
3640
- const waitForEchoAndSubmit = () => {
3641
- if (!this.ptyProcess) {
3642
- resolveOnce();
3643
- return;
3644
- }
3645
- const now = Date.now();
3646
- const elapsed = now - submitStartedAt;
3647
- const screenText = this.terminalScreen.getText();
3648
- const normalizedScreen = normalizePromptText(screenText);
3649
- if (normalizedScreen !== lastNormalizedScreen) {
3650
- lastNormalizedScreen = normalizedScreen;
3651
- lastScreenChangeAt = now;
3652
- }
3653
- const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
3654
- if (echoVisible) {
3655
- const screenSettled = now - lastScreenChangeAt >= 500;
3656
- if (elapsed >= submitDelayMs && screenSettled) {
3657
- submit();
3658
- return;
3659
- }
3660
- }
3661
- if (elapsed >= maxEchoWaitMs) {
3662
- submit();
3663
- return;
3664
- }
3665
- setTimeout(waitForEchoAndSubmit, 50);
3666
- };
3667
- void this.writeToPty(text).then(() => waitForEchoAndSubmit(), rejectOnce);
3775
+ void this.writeToPty(text).then(
3776
+ () => this.waitForEchoAndSubmit(sendState, completion, submitStartedAt),
3777
+ completion.rejectOnce
3778
+ );
3668
3779
  });
3669
3780
  }
3670
3781
  getPartialResponse() {
@@ -3741,9 +3852,9 @@ var init_provider_cli_adapter = __esm({
3741
3852
  shutdown() {
3742
3853
  this.clearIdleFinishCandidate("shutdown");
3743
3854
  this.clearAllTimers();
3744
- this.pendingOutputParseBuffer = "";
3855
+ this.pendingOutputParseChunks = [];
3745
3856
  this.pendingTerminalQueryTail = "";
3746
- this.ptyOutputBuffer = "";
3857
+ this.ptyOutputChunks = [];
3747
3858
  this.finishRetryCount = 0;
3748
3859
  if (this.ptyProcess) {
3749
3860
  this.ptyProcess.write("");
@@ -3764,9 +3875,9 @@ var init_provider_cli_adapter = __esm({
3764
3875
  detach() {
3765
3876
  this.clearIdleFinishCandidate("detach");
3766
3877
  this.clearAllTimers();
3767
- this.pendingOutputParseBuffer = "";
3878
+ this.pendingOutputParseChunks = [];
3768
3879
  this.pendingTerminalQueryTail = "";
3769
- this.ptyOutputBuffer = "";
3880
+ this.ptyOutputChunks = [];
3770
3881
  this.finishRetryCount = 0;
3771
3882
  if (this.ptyProcess) {
3772
3883
  try {
@@ -3797,19 +3908,19 @@ var init_provider_cli_adapter = __esm({
3797
3908
  clearTimeout(this.pendingOutputParseTimer);
3798
3909
  this.pendingOutputParseTimer = null;
3799
3910
  }
3800
- this.pendingOutputParseBuffer = "";
3911
+ this.pendingOutputParseChunks = [];
3801
3912
  this.pendingTerminalQueryTail = "";
3802
3913
  if (this.ptyOutputFlushTimer) {
3803
3914
  clearTimeout(this.ptyOutputFlushTimer);
3804
3915
  this.ptyOutputFlushTimer = null;
3805
3916
  }
3806
- this.ptyOutputBuffer = "";
3917
+ this.ptyOutputChunks = [];
3807
3918
  if (this.finishRetryTimer) {
3808
3919
  clearTimeout(this.finishRetryTimer);
3809
3920
  this.finishRetryTimer = null;
3810
3921
  }
3811
3922
  this.finishRetryCount = 0;
3812
- this.terminalScreen.reset();
3923
+ this.resetTerminalScreen();
3813
3924
  this.ptyProcess?.clearBuffer?.();
3814
3925
  this.onStatusChange?.();
3815
3926
  }
@@ -3926,7 +4037,7 @@ var init_provider_cli_adapter = __esm({
3926
4037
  traceEntryCount: this.traceEntries.length,
3927
4038
  statusHistory: this.statusHistory.slice(-30),
3928
4039
  timeouts: this.timeouts,
3929
- pendingOutputParseBufferLength: this.pendingOutputParseBuffer.length,
4040
+ pendingOutputParseBufferLength: this.pendingOutputParseChunks.reduce((total, chunk) => total + chunk.length, 0),
3930
4041
  pendingOutputParseScheduled: !!this.pendingOutputParseTimer,
3931
4042
  ptyAlive: !!this.ptyProcess
3932
4043
  };
@@ -9667,14 +9778,14 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
9667
9778
  id: state.instanceId || state.type,
9668
9779
  parentId: null,
9669
9780
  providerType: state.type,
9670
- ...includeSessionMetadata && { providerName: state.name },
9781
+ providerName: state.name,
9671
9782
  kind: "workspace",
9672
9783
  transport: "cdp-page",
9673
9784
  status: normalizeManagedStatus(activeChat?.status || state.status, {
9674
9785
  activeModal: activeChat?.activeModal || null
9675
9786
  }),
9676
9787
  title,
9677
- ...includeSessionMetadata && { workspace: state.workspace || null },
9788
+ workspace: state.workspace || null,
9678
9789
  activeChat,
9679
9790
  ...summaryMetadata && { summaryMetadata },
9680
9791
  ...includeSessionMetadata && { capabilities: state.sessionCapabilities || IDE_SESSION_CAPABILITIES },
@@ -9699,7 +9810,7 @@ function buildExtensionAgentSession(parent, ext, options) {
9699
9810
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
9700
9811
  parentId: parent.instanceId || parent.type,
9701
9812
  providerType: ext.type,
9702
- ...includeSessionMetadata && { providerName: ext.name },
9813
+ providerName: ext.name,
9703
9814
  providerSessionId: ext.providerSessionId,
9704
9815
  kind: "agent",
9705
9816
  transport: "cdp-webview",
@@ -9707,7 +9818,7 @@ function buildExtensionAgentSession(parent, ext, options) {
9707
9818
  activeModal: activeChat?.activeModal || null
9708
9819
  }),
9709
9820
  title: activeChat?.title || ext.name,
9710
- ...includeSessionMetadata && { workspace: parent.workspace || null },
9821
+ workspace: parent.workspace || null,
9711
9822
  activeChat,
9712
9823
  ...summaryMetadata && { summaryMetadata },
9713
9824
  ...includeSessionMetadata && { capabilities: ext.sessionCapabilities || EXTENSION_SESSION_CAPABILITIES },
@@ -9747,7 +9858,7 @@ function buildCliSession(state, options) {
9747
9858
  id: state.instanceId,
9748
9859
  parentId: null,
9749
9860
  providerType: state.type,
9750
- ...includeSessionMetadata && { providerName: state.name },
9861
+ providerName: state.name,
9751
9862
  providerSessionId: state.providerSessionId,
9752
9863
  kind: "agent",
9753
9864
  transport: "pty",
@@ -9755,7 +9866,7 @@ function buildCliSession(state, options) {
9755
9866
  activeModal: activeChat?.activeModal || null
9756
9867
  }),
9757
9868
  title: activeChat?.title || state.name,
9758
- ...includeSessionMetadata && { workspace: state.workspace || null },
9869
+ workspace: state.workspace || null,
9759
9870
  ...includeRuntimeMetadata && {
9760
9871
  runtimeKey: state.runtime?.runtimeKey,
9761
9872
  runtimeDisplayName: state.runtime?.displayName,
@@ -9794,14 +9905,14 @@ function buildAcpSession(state, options) {
9794
9905
  id: state.instanceId,
9795
9906
  parentId: null,
9796
9907
  providerType: state.type,
9797
- ...includeSessionMetadata && { providerName: state.name },
9908
+ providerName: state.name,
9798
9909
  kind: "agent",
9799
9910
  transport: "acp",
9800
9911
  status: normalizeManagedStatus(activeChat?.status || state.status, {
9801
9912
  activeModal: activeChat?.activeModal || null
9802
9913
  }),
9803
9914
  title: activeChat?.title || state.name,
9804
- ...includeSessionMetadata && { workspace: state.workspace || null },
9915
+ workspace: state.workspace || null,
9805
9916
  activeChat,
9806
9917
  ...summaryMetadata && { summaryMetadata },
9807
9918
  ...includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES },
@@ -10248,26 +10359,26 @@ function buildReadChatReplayCollapseSignature(message) {
10248
10359
  function shouldCollapseReadChatReplayDuplicate(message) {
10249
10360
  if (!message) return false;
10250
10361
  const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
10251
- if (role !== "assistant" && role !== "system") return false;
10252
- const kind = typeof message.kind === "string" ? message.kind.trim().toLowerCase() : "standard";
10253
- return kind === "tool" || kind === "terminal" || kind === "thought" || kind === "system";
10362
+ return role === "assistant" || role === "system";
10254
10363
  }
10255
10364
  function collapseReplayDuplicatesFromReadChat(messages) {
10256
10365
  const collapsed = [];
10257
- let lastReplayTurnSignature = "";
10366
+ const replaySignaturesInCurrentTurn = /* @__PURE__ */ new Set();
10258
10367
  for (const message of messages) {
10368
+ const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
10369
+ if (role === "user") {
10370
+ replaySignaturesInCurrentTurn.clear();
10371
+ }
10259
10372
  const signature = buildReadChatReplayCollapseSignature(message);
10260
10373
  const previous = collapsed[collapsed.length - 1];
10261
10374
  const previousSignature = buildReadChatReplayCollapseSignature(previous);
10262
10375
  if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
10263
10376
  if (previousSignature === signature) continue;
10264
- if (lastReplayTurnSignature === signature) continue;
10377
+ if (replaySignaturesInCurrentTurn.has(signature)) continue;
10265
10378
  }
10266
10379
  collapsed.push(message);
10267
10380
  if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
10268
- lastReplayTurnSignature = signature;
10269
- } else if ((message.role || "").toLowerCase() === "user") {
10270
- lastReplayTurnSignature = "";
10381
+ replaySignaturesInCurrentTurn.add(signature);
10271
10382
  }
10272
10383
  }
10273
10384
  return collapsed;
@@ -17770,6 +17881,18 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
17770
17881
  const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
17771
17882
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
17772
17883
  }
17884
+ function projectLiveSessionFromFull(session) {
17885
+ const {
17886
+ capabilities: _capabilities,
17887
+ controlValues: _controlValues,
17888
+ providerControls: _providerControls,
17889
+ ...rest
17890
+ } = session;
17891
+ return {
17892
+ ...rest,
17893
+ activeChat: normalizeActiveChatData(session.activeChat, LIVE_STATUS_ACTIVE_CHAT_OPTIONS)
17894
+ };
17895
+ }
17773
17896
  function buildRecentLaunches(recentActivity) {
17774
17897
  return recentActivity.map((item) => ({
17775
17898
  id: item.id,
@@ -17794,7 +17917,7 @@ function buildStatusSnapshot(options) {
17794
17917
  options.cdpManagers,
17795
17918
  { profile: "full" }
17796
17919
  );
17797
- const sessions = profile === "full" ? unreadSourceSessions : buildSessionEntries(
17920
+ const sessions = profile === "full" ? unreadSourceSessions : profile === "live" ? unreadSourceSessions.map(projectLiveSessionFromFull) : buildSessionEntries(
17798
17921
  options.allStates,
17799
17922
  options.cdpManagers,
17800
17923
  { profile }
@@ -19080,10 +19203,6 @@ var DaemonStatusReporter = class {
19080
19203
  LOG.info("StatusReport", `\u2192${target} ${baseSummary}`);
19081
19204
  }
19082
19205
  }
19083
- const sessions = buildSessionEntries(
19084
- allStates,
19085
- this.deps.cdpManagers
19086
- );
19087
19206
  const payload = {
19088
19207
  ...buildStatusSnapshot({
19089
19208
  allStates,
@@ -19103,9 +19222,9 @@ var DaemonStatusReporter = class {
19103
19222
  }),
19104
19223
  screenshotUsage: this.deps.getScreenshotUsage?.() || null
19105
19224
  };
19106
- const payloadBytes = JSON.stringify(payload).length;
19107
19225
  const p2pSent = this.sendP2PPayload(payload);
19108
19226
  if (p2pSent) {
19227
+ const payloadBytes = JSON.stringify(payload).length;
19109
19228
  LOG.debug("P2P", `sent (${payloadBytes} bytes)`);
19110
19229
  if (payloadBytes > 256 * 1024) {
19111
19230
  LOG.warn(
@@ -19115,8 +19234,10 @@ var DaemonStatusReporter = class {
19115
19234
  }
19116
19235
  }
19117
19236
  if (opts?.p2pOnly) return;
19237
+ if (!serverConnected || !serverConn) return;
19238
+ const payloadSessions = Array.isArray(payload.sessions) ? payload.sessions : [];
19118
19239
  const wsPayload = {
19119
- sessions: sessions.map((session) => ({
19240
+ sessions: payloadSessions.map((session) => ({
19120
19241
  id: session.id,
19121
19242
  parentId: session.parentId,
19122
19243
  providerType: session.providerType,
@@ -19142,8 +19263,9 @@ var DaemonStatusReporter = class {
19142
19263
  return;
19143
19264
  }
19144
19265
  this.lastServerStatusHash = wsHash;
19266
+ const wsPayloadBytes = JSON.stringify(wsPayload).length;
19145
19267
  serverConn.sendMessage("status_report", wsPayload);
19146
- LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
19268
+ LOG.debug("Server", `sent status_report (${wsPayloadBytes} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
19147
19269
  }
19148
19270
  // ─── P2P ─────────────────────────────────────────
19149
19271
  sendP2PPayload(payload) {