@adhdev/daemon-core 0.9.19 → 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.mjs CHANGED
@@ -1958,6 +1958,7 @@ var init_provider_cli_runtime = __esm({
1958
1958
  var provider_cli_adapter_exports = {};
1959
1959
  __export(provider_cli_adapter_exports, {
1960
1960
  ProviderCliAdapter: () => ProviderCliAdapter,
1961
+ appendBoundedText: () => appendBoundedText,
1961
1962
  normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
1962
1963
  });
1963
1964
  import * as os10 from "os";
@@ -1991,6 +1992,14 @@ function parsedTranscriptIsRicherThanCommitted(parsedMessages, committedMessages
1991
1992
  }
1992
1993
  return false;
1993
1994
  }
1995
+ function appendBoundedText(current, chunk, maxChars) {
1996
+ if (!chunk) return current.length <= maxChars ? current : current.slice(-maxChars);
1997
+ if (maxChars <= 0) return "";
1998
+ if (chunk.length >= maxChars) return chunk.slice(-maxChars);
1999
+ const keepFromCurrent = maxChars - chunk.length;
2000
+ if (current.length <= keepFromCurrent) return current + chunk;
2001
+ return current.slice(-keepFromCurrent) + chunk;
2002
+ }
1994
2003
  var ProviderCliAdapter;
1995
2004
  var init_provider_cli_adapter = __esm({
1996
2005
  "src/cli-adapters/provider-cli-adapter.ts"() {
@@ -2067,15 +2076,17 @@ var init_provider_cli_adapter = __esm({
2067
2076
  startupFirstOutputAt = 0;
2068
2077
  // PTY I/O
2069
2078
  onPtyDataCallback = null;
2070
- pendingOutputParseBuffer = "";
2079
+ pendingOutputParseChunks = [];
2071
2080
  pendingOutputParseTimer = null;
2072
- ptyOutputBuffer = "";
2081
+ ptyOutputChunks = [];
2073
2082
  ptyOutputFlushTimer = null;
2074
2083
  pendingTerminalQueryTail = "";
2075
2084
  lastOutputAt = 0;
2076
2085
  lastNonEmptyOutputAt = 0;
2077
2086
  lastScreenChangeAt = 0;
2078
2087
  lastScreenSnapshot = "";
2088
+ lastScreenText = "";
2089
+ lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
2079
2090
  // Server log forwarding
2080
2091
  serverConn = null;
2081
2092
  logBuffer = [];
@@ -2121,6 +2132,9 @@ var init_provider_cli_adapter = __esm({
2121
2132
  traceSeq = 0;
2122
2133
  traceSessionId = "";
2123
2134
  parsedStatusCache = null;
2135
+ lastStatusHotPathParseAt = Number.NEGATIVE_INFINITY;
2136
+ static STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS = 1e3;
2137
+ static SCREEN_SNAPSHOT_MIN_INTERVAL_MS = 250;
2124
2138
  static MAX_TRACE_ENTRIES = 250;
2125
2139
  providerResolutionMeta;
2126
2140
  static FINISH_RETRY_DELAY_MS = 300;
@@ -2129,6 +2143,30 @@ var init_provider_cli_adapter = __esm({
2129
2143
  this.messages = [...this.committedMessages];
2130
2144
  this.structuredMessages = [...this.committedMessages];
2131
2145
  }
2146
+ readTerminalScreenText(now = Date.now()) {
2147
+ const screenText = this.terminalScreen.getText() || "";
2148
+ this.lastScreenText = screenText;
2149
+ this.lastScreenSnapshotReadAt = now;
2150
+ return screenText;
2151
+ }
2152
+ shouldReadTerminalScreenSnapshot(now) {
2153
+ if (!this.lastScreenText) return true;
2154
+ return now - this.lastScreenSnapshotReadAt >= _ProviderCliAdapter.SCREEN_SNAPSHOT_MIN_INTERVAL_MS;
2155
+ }
2156
+ resetTerminalScreen(rows, cols) {
2157
+ this.terminalScreen.reset(rows, cols);
2158
+ this.lastScreenText = "";
2159
+ this.lastScreenSnapshot = "";
2160
+ this.lastScreenChangeAt = 0;
2161
+ this.lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
2162
+ }
2163
+ getFreshParsedStatusCache() {
2164
+ const cached = this.parsedStatusCache;
2165
+ 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) {
2166
+ return cached.result;
2167
+ }
2168
+ return null;
2169
+ }
2132
2170
  getIdleFinishConfirmMs() {
2133
2171
  return this.timeouts.idleFinishConfirm;
2134
2172
  }
@@ -2243,9 +2281,9 @@ var init_provider_cli_adapter = __esm({
2243
2281
  clearTimeout(this.pendingOutputParseTimer);
2244
2282
  this.pendingOutputParseTimer = null;
2245
2283
  }
2246
- if (!this.pendingOutputParseBuffer) return;
2247
- const rawData = this.pendingOutputParseBuffer;
2248
- this.pendingOutputParseBuffer = "";
2284
+ if (this.pendingOutputParseChunks.length === 0) return;
2285
+ const rawData = this.pendingOutputParseChunks.join("");
2286
+ this.pendingOutputParseChunks = [];
2249
2287
  this.handleOutput(rawData);
2250
2288
  }
2251
2289
  async spawn() {
@@ -2300,7 +2338,7 @@ var init_provider_cli_adapter = __esm({
2300
2338
  terminalScreen: this.terminalScreen
2301
2339
  });
2302
2340
  }
2303
- this.pendingOutputParseBuffer += data;
2341
+ this.pendingOutputParseChunks.push(data);
2304
2342
  if (!this.pendingOutputParseTimer) {
2305
2343
  this.pendingOutputParseTimer = setTimeout(() => {
2306
2344
  this.pendingOutputParseTimer = null;
@@ -2308,13 +2346,13 @@ var init_provider_cli_adapter = __esm({
2308
2346
  }, this.timeouts.ptyFlush);
2309
2347
  }
2310
2348
  if (this.onPtyDataCallback) {
2311
- this.ptyOutputBuffer += data;
2349
+ this.ptyOutputChunks.push(data);
2312
2350
  if (!this.ptyOutputFlushTimer) {
2313
2351
  this.ptyOutputFlushTimer = setTimeout(() => {
2314
- if (this.ptyOutputBuffer && this.onPtyDataCallback) {
2315
- this.onPtyDataCallback(this.ptyOutputBuffer);
2352
+ if (this.ptyOutputChunks.length > 0 && this.onPtyDataCallback) {
2353
+ this.onPtyDataCallback(this.ptyOutputChunks.join(""));
2316
2354
  }
2317
- this.ptyOutputBuffer = "";
2355
+ this.ptyOutputChunks = [];
2318
2356
  this.ptyOutputFlushTimer = null;
2319
2357
  }, this.timeouts.ptyFlush);
2320
2358
  }
@@ -2339,7 +2377,7 @@ var init_provider_cli_adapter = __esm({
2339
2377
  clearTimeout(this.startupSettleTimer);
2340
2378
  this.startupSettleTimer = null;
2341
2379
  }
2342
- this.terminalScreen.reset(24, 80);
2380
+ this.resetTerminalScreen(24, 80);
2343
2381
  this.pendingTerminalQueryTail = "";
2344
2382
  this.currentTurnScope = null;
2345
2383
  this.finishRetryCount = 0;
@@ -2361,11 +2399,12 @@ var init_provider_cli_adapter = __esm({
2361
2399
  this.terminalScreen.write(rawData);
2362
2400
  const cleanData = sanitizeTerminalText(rawData);
2363
2401
  const now = Date.now();
2364
- const screenText = this.terminalScreen.getText();
2365
- const normalizedScreenSnapshot = normalizeScreenSnapshot(screenText);
2402
+ const shouldReadScreen = this.shouldReadTerminalScreenSnapshot(now);
2403
+ const screenText = shouldReadScreen ? this.readTerminalScreenText(now) : this.lastScreenText;
2404
+ const normalizedScreenSnapshot = shouldReadScreen ? normalizeScreenSnapshot(screenText) : this.lastScreenSnapshot;
2366
2405
  this.lastOutputAt = now;
2367
2406
  if (cleanData.trim()) this.lastNonEmptyOutputAt = now;
2368
- if (normalizedScreenSnapshot !== this.lastScreenSnapshot) {
2407
+ if (shouldReadScreen && normalizedScreenSnapshot !== this.lastScreenSnapshot) {
2369
2408
  this.lastScreenSnapshot = normalizedScreenSnapshot;
2370
2409
  this.lastScreenChangeAt = now;
2371
2410
  }
@@ -2387,7 +2426,7 @@ var init_provider_cli_adapter = __esm({
2387
2426
  this.scheduleStartupSettleCheck();
2388
2427
  }
2389
2428
  if (this.isWaitingForResponse && cleanData) {
2390
- this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
2429
+ this.responseBuffer = appendBoundedText(this.responseBuffer, cleanData, 8e3);
2391
2430
  }
2392
2431
  if (cleanData.trim()) {
2393
2432
  if (this.serverConn) {
@@ -2396,11 +2435,11 @@ var init_provider_cli_adapter = __esm({
2396
2435
  this.logBuffer.push({ message: cleanData.trim(), level: "info" });
2397
2436
  }
2398
2437
  }
2399
- this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
2400
2438
  const prevAccumulatedLen = this.accumulatedBuffer.length;
2401
2439
  const prevAccumulatedRawLen = this.accumulatedRawBuffer.length;
2402
- this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2403
- this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2440
+ this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData, 1e3);
2441
+ this.accumulatedBuffer = appendBoundedText(this.accumulatedBuffer, cleanData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2442
+ this.accumulatedRawBuffer = appendBoundedText(this.accumulatedRawBuffer, rawData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
2404
2443
  if (this.currentTurnScope) {
2405
2444
  const droppedClean = prevAccumulatedLen + cleanData.length - this.accumulatedBuffer.length;
2406
2445
  const droppedRaw = prevAccumulatedRawLen + rawData.length - this.accumulatedRawBuffer.length;
@@ -2411,14 +2450,14 @@ var init_provider_cli_adapter = __esm({
2411
2450
  this.currentTurnScope.rawBufferStart = Math.max(0, this.currentTurnScope.rawBufferStart - droppedRaw);
2412
2451
  }
2413
2452
  }
2414
- this.resolveStartupState("output");
2453
+ this.resolveStartupState("output", screenText, normalizedScreenSnapshot, now);
2415
2454
  this.scheduleSettle();
2416
2455
  }
2417
- resolveStartupState(trigger) {
2456
+ resolveStartupState(trigger, screenTextOverride, normalizedScreenOverride, nowOverride) {
2418
2457
  if (!this.startupParseGate) return;
2419
- const now = Date.now();
2420
- const screenText = this.terminalScreen.getText() || "";
2421
- const normalizedScreen = normalizeScreenSnapshot(screenText);
2458
+ const now = typeof nowOverride === "number" ? nowOverride : Date.now();
2459
+ const screenText = typeof screenTextOverride === "string" ? screenTextOverride : this.readTerminalScreenText();
2460
+ const normalizedScreen = typeof normalizedScreenOverride === "string" ? normalizedScreenOverride : normalizeScreenSnapshot(screenText);
2422
2461
  const hasStartupOutput = !!this.startupFirstOutputAt || !!normalizedScreen.trim();
2423
2462
  if (!hasStartupOutput) return;
2424
2463
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -2653,40 +2692,28 @@ var init_provider_cli_adapter = __esm({
2653
2692
  }, delayTime);
2654
2693
  return;
2655
2694
  }
2656
- const tail = this.settledBuffer;
2657
- const screenText = this.terminalScreen.getText() || "";
2658
2695
  this.resolveStartupState("settled");
2659
- if (this.startupParseGate) {
2660
- return;
2661
- }
2662
- const parsedTranscript = this.parseCurrentTranscript(
2663
- this.committedMessages,
2664
- this.responseBuffer,
2665
- this.currentTurnScope
2666
- );
2667
- const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
2668
- const modal = this.runParseApproval(tail) || parsedModal;
2669
- const rawScriptStatus = this.runDetectStatus(tail);
2670
- const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
2671
- const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
2696
+ if (this.startupParseGate) return;
2697
+ const session = this.runParseSession();
2698
+ if (!session) return;
2699
+ const { status, messages, modal, parsedStatus } = session;
2700
+ const parsedMessages = normalizeCliParsedMessages(messages, {
2672
2701
  committedMessages: this.committedMessages,
2673
2702
  scope: this.currentTurnScope,
2674
2703
  lastOutputAt: this.lastOutputAt
2675
- }) : [];
2676
- if (this.maybeCommitVisibleIdleTranscript(parsedTranscript)) {
2677
- return;
2678
- }
2679
- const lastParsedAssistant = [...parsedMessages].reverse().find((message) => message.role === "assistant");
2680
- const parsedShowsLiveAssistantProgress = parsedTranscript?.status === "generating" && !!lastParsedAssistant && parsedMessages.length > this.committedMessages.length;
2704
+ });
2705
+ if (this.maybeCommitVisibleIdleTranscript(session, parsedMessages)) return;
2706
+ const lastParsedAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant");
2681
2707
  const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet || this.currentTurnScope?.prompt || "");
2708
+ const screenText = this.terminalScreen.getText() || "";
2682
2709
  this.recordTrace("settled", {
2683
- tail: summarizeCliTraceText(tail, 500),
2710
+ tail: summarizeCliTraceText(this.settledBuffer, 500),
2684
2711
  screenText: summarizeCliTraceText(screenText, 1200),
2685
- detectStatus: scriptStatus,
2686
- parsedStatus: parsedTranscript?.status || null,
2712
+ detectStatus: status,
2713
+ parsedStatus: parsedStatus || null,
2687
2714
  parsedMessageCount: parsedMessages.length,
2688
2715
  parsedLastAssistant: lastParsedAssistant ? summarizeCliTraceText(lastParsedAssistant.content, 280) : "",
2689
- parsedActiveModal: parsedTranscript?.activeModal ?? null,
2716
+ parsedActiveModal: modal,
2690
2717
  approval: modal,
2691
2718
  ...buildCliTraceParseSnapshot({
2692
2719
  accumulatedBuffer: this.accumulatedBuffer,
@@ -2720,36 +2747,36 @@ var init_provider_cli_adapter = __esm({
2720
2747
  `[${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 || "-"}`
2721
2748
  );
2722
2749
  }
2723
- if (!scriptStatus) return;
2750
+ if (!status) return;
2724
2751
  const prevStatus = this.currentStatus;
2725
- const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
2752
+ const ctx = { now, modal, status, parsedMessages, lastParsedAssistant, parsedStatus: parsedStatus || null, prevStatus };
2726
2753
  if (!this.applyPendingScriptStatusDebounce(ctx)) return;
2727
2754
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
2728
2755
  LOG.info(
2729
2756
  "CLI",
2730
- `[${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)}`
2757
+ `[${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)}`
2731
2758
  );
2732
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
2759
+ const shouldHoldGenerating = status === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedStatus === "idle" && !!lastParsedAssistant);
2733
2760
  if (shouldHoldGenerating) {
2734
2761
  this.applyHoldGenerating(ctx, recentInteractiveActivity);
2735
2762
  return;
2736
2763
  }
2737
- if (scriptStatus === "waiting_approval") {
2764
+ if (status === "waiting_approval") {
2738
2765
  this.applyWaitingApproval(ctx);
2739
2766
  return;
2740
2767
  }
2741
- if (scriptStatus === "generating") {
2768
+ if (status === "generating") {
2742
2769
  this.applyGenerating(ctx);
2743
2770
  return;
2744
2771
  }
2745
- if (scriptStatus === "idle") {
2772
+ if (status === "idle") {
2746
2773
  this.applyIdle(ctx, now);
2747
2774
  }
2748
2775
  }
2749
2776
  // Returns false if the caller should bail out (debounce pending).
2750
2777
  applyPendingScriptStatusDebounce(ctx) {
2751
- const { now, scriptStatus, prevStatus } = ctx;
2752
- const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
2778
+ const { now, status, prevStatus } = ctx;
2779
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
2753
2780
  if (!shouldDebounce) {
2754
2781
  this.pendingScriptStatus = null;
2755
2782
  this.pendingScriptStatusSince = 0;
@@ -2767,8 +2794,8 @@ var init_provider_cli_adapter = __esm({
2767
2794
  this.evaluateSettled();
2768
2795
  }, delayMs);
2769
2796
  };
2770
- if (this.pendingScriptStatus !== scriptStatus) {
2771
- this.pendingScriptStatus = scriptStatus;
2797
+ if (this.pendingScriptStatus !== status) {
2798
+ this.pendingScriptStatus = status;
2772
2799
  this.pendingScriptStatusSince = now;
2773
2800
  armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
2774
2801
  return false;
@@ -2781,7 +2808,7 @@ var init_provider_cli_adapter = __esm({
2781
2808
  return true;
2782
2809
  }
2783
2810
  applyHoldGenerating(ctx, recentInteractiveActivity) {
2784
- const { scriptStatus } = ctx;
2811
+ const { status } = ctx;
2785
2812
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
2786
2813
  this.setStatus("generating", "recent_activity_hold");
2787
2814
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
@@ -2792,7 +2819,7 @@ var init_provider_cli_adapter = __esm({
2792
2819
  }
2793
2820
  }, this.timeouts.generatingIdle);
2794
2821
  this.recordTrace("hold_generating_recent_activity", {
2795
- scriptStatus,
2822
+ scriptStatus: status,
2796
2823
  recentInteractiveActivity,
2797
2824
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
2798
2825
  lastScreenChangeAt: this.lastScreenChangeAt,
@@ -2846,11 +2873,13 @@ var init_provider_cli_adapter = __esm({
2846
2873
  }
2847
2874
  }
2848
2875
  applyGenerating(ctx) {
2849
- const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
2876
+ const { modal, parsedMessages, lastParsedAssistant, parsedStatus, prevStatus } = ctx;
2850
2877
  this.clearIdleFinishCandidate("generating");
2878
+ const screenText = this.terminalScreen.getText() || "";
2851
2879
  const effectiveScreenText = screenText || this.accumulatedBuffer;
2852
2880
  const noActiveTurn = !this.currentTurnScope;
2853
2881
  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));
2882
+ const parsedShowsLiveAssistantProgress = parsedStatus === "generating" && !!lastParsedAssistant && parsedMessages.length > this.committedMessages.length;
2854
2883
  if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
2855
2884
  return;
2856
2885
  }
@@ -2877,7 +2906,7 @@ var init_provider_cli_adapter = __esm({
2877
2906
  this.onStatusChange?.();
2878
2907
  }
2879
2908
  applyIdle(ctx, now) {
2880
- const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
2909
+ const { modal, lastParsedAssistant, prevStatus } = ctx;
2881
2910
  if (prevStatus === "waiting_approval") {
2882
2911
  if (this.approvalExitTimeout) {
2883
2912
  clearTimeout(this.approvalExitTimeout);
@@ -2999,20 +3028,15 @@ var init_provider_cli_adapter = __esm({
2999
3028
  this.setStatus("idle", "response_finished");
3000
3029
  this.onStatusChange?.();
3001
3030
  }
3002
- maybeCommitVisibleIdleTranscript(parsed) {
3031
+ maybeCommitVisibleIdleTranscript(session, parsedMessages) {
3003
3032
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
3004
3033
  if (!allowImmediateScriptIdleCommit) return false;
3005
- if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
3034
+ if (!session || session.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || session.modal) {
3006
3035
  return false;
3007
3036
  }
3008
- const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
3009
- committedMessages: this.committedMessages,
3010
- scope: this.currentTurnScope,
3011
- lastOutputAt: this.lastOutputAt
3012
- });
3013
- const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
3037
+ const visibleAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant" && m.content.trim());
3014
3038
  if (!visibleAssistant) return false;
3015
- this.committedMessages = hydratedForIdleCommit;
3039
+ this.committedMessages = parsedMessages;
3016
3040
  this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
3017
3041
  this.clearAllTimers();
3018
3042
  this.syncMessageViews();
@@ -3090,6 +3114,59 @@ var init_provider_cli_adapter = __esm({
3090
3114
  };
3091
3115
  }
3092
3116
  // ─── Script Execution ──────────────────────────
3117
+ runParseSession() {
3118
+ if (typeof this.cliScripts?.parseSession === "function") {
3119
+ try {
3120
+ const screenText = this.terminalScreen.getText();
3121
+ const tail = this.recentOutputBuffer.slice(-500);
3122
+ const input = buildCliParseInput({
3123
+ accumulatedBuffer: this.accumulatedBuffer,
3124
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
3125
+ recentOutputBuffer: this.recentOutputBuffer,
3126
+ terminalScreenText: screenText,
3127
+ baseMessages: this.committedMessages,
3128
+ partialResponse: this.responseBuffer,
3129
+ isWaitingForResponse: this.isWaitingForResponse,
3130
+ scope: this.currentTurnScope,
3131
+ runtimeSettings: this.runtimeSettings
3132
+ });
3133
+ const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
3134
+ this.parseErrorMessage = null;
3135
+ return session && typeof session === "object" ? session : null;
3136
+ } catch (e) {
3137
+ const message = e?.message || String(e);
3138
+ this.parseErrorMessage = message;
3139
+ LOG.warn("CLI", `[${this.cliType}] parseSession error: ${message}`);
3140
+ return null;
3141
+ }
3142
+ }
3143
+ if (!this.cliScripts?.detectStatus && !this.cliScripts?.parseOutput) return null;
3144
+ try {
3145
+ const tail = this.settledBuffer;
3146
+ const parsedTranscript = this.parseCurrentTranscript(
3147
+ this.committedMessages,
3148
+ this.responseBuffer,
3149
+ this.currentTurnScope
3150
+ );
3151
+ const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((b) => typeof b === "string" && b.trim()) ? parsedTranscript.activeModal : null;
3152
+ const approval = this.runParseApproval(tail);
3153
+ const modal = approval || parsedModal;
3154
+ const rawStatus = this.runDetectStatus(tail);
3155
+ const parsedStatus = typeof parsedTranscript?.status === "string" ? parsedTranscript.status : null;
3156
+ const effectiveStatus = parsedStatus === "waiting_approval" && modal ? "waiting_approval" : rawStatus || parsedStatus || "idle";
3157
+ return {
3158
+ status: effectiveStatus,
3159
+ messages: Array.isArray(parsedTranscript?.messages) ? parsedTranscript.messages : [],
3160
+ modal,
3161
+ parsedStatus
3162
+ };
3163
+ } catch (e) {
3164
+ const message = e?.message || String(e);
3165
+ this.parseErrorMessage = message;
3166
+ LOG.warn("CLI", `[${this.cliType}] parseSession fallback error: ${message}`);
3167
+ return null;
3168
+ }
3169
+ }
3093
3170
  runDetectStatus(text) {
3094
3171
  if (!this.cliScripts?.detectStatus) return null;
3095
3172
  try {
@@ -3163,14 +3240,21 @@ var init_provider_cli_adapter = __esm({
3163
3240
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
3164
3241
  let effectiveModal = startupModal || this.activeModal;
3165
3242
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
3166
- try {
3167
- const parsed = this.getScriptParsedStatus();
3168
- const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons) && parsed.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsed.activeModal : null;
3169
- if (parsed?.status === "waiting_approval" && parsedModal) {
3170
- effectiveStatus = "waiting_approval";
3171
- effectiveModal = parsedModal;
3243
+ let parsed = this.getFreshParsedStatusCache();
3244
+ if (!parsed && effectiveStatus !== "idle") {
3245
+ const now = Date.now();
3246
+ if (now - this.lastStatusHotPathParseAt >= _ProviderCliAdapter.STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS) {
3247
+ this.lastStatusHotPathParseAt = now;
3248
+ try {
3249
+ parsed = this.getScriptParsedStatus();
3250
+ } catch {
3251
+ }
3172
3252
  }
3173
- } catch {
3253
+ }
3254
+ const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons) && parsed.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsed.activeModal : null;
3255
+ if (parsed?.status === "waiting_approval" && parsedModal) {
3256
+ effectiveStatus = "waiting_approval";
3257
+ effectiveModal = parsedModal;
3174
3258
  }
3175
3259
  }
3176
3260
  return {
@@ -3202,7 +3286,7 @@ var init_provider_cli_adapter = __esm({
3202
3286
  * Called by command handler / dashboard for rich content rendering.
3203
3287
  */
3204
3288
  getScriptParsedStatus() {
3205
- const screenText = this.terminalScreen.getText();
3289
+ const screenText = this.readTerminalScreenText();
3206
3290
  const cached = this.parsedStatusCache;
3207
3291
  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) {
3208
3292
  return cached.result;
@@ -3222,8 +3306,21 @@ var init_provider_cli_adapter = __esm({
3222
3306
  this.onStatusChange?.();
3223
3307
  }
3224
3308
  }
3225
- if (this.maybeCommitVisibleIdleTranscript(parsed)) {
3226
- return this.getScriptParsedStatus();
3309
+ if (parsed && Array.isArray(parsed.messages)) {
3310
+ const hydratedForCommit = normalizeCliParsedMessages(parsed.messages, {
3311
+ committedMessages: this.committedMessages,
3312
+ scope: this.currentTurnScope,
3313
+ lastOutputAt: this.lastOutputAt
3314
+ });
3315
+ const fakeSession = {
3316
+ status: parsed.status || "idle",
3317
+ messages: parsed.messages,
3318
+ modal: parsedModal,
3319
+ parsedStatus: parsed.status || null
3320
+ };
3321
+ if (this.maybeCommitVisibleIdleTranscript(fakeSession, hydratedForCommit)) {
3322
+ return this.getScriptParsedStatus();
3323
+ }
3227
3324
  }
3228
3325
  const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
3229
3326
  let result;
@@ -3405,6 +3502,17 @@ var init_provider_cli_adapter = __esm({
3405
3502
  }
3406
3503
  await this.sendMessage(promptText);
3407
3504
  }
3505
+ isSubmitStuck(normalizedPromptSnippet) {
3506
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return false;
3507
+ if (this.hasActionableApproval()) return false;
3508
+ if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return false;
3509
+ const screenText = this.terminalScreen.getText();
3510
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return false;
3511
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
3512
+ if (liveApproval) return false;
3513
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
3514
+ return liveStatus !== "generating" && liveStatus !== "waiting_approval";
3515
+ }
3408
3516
  async writeToPty(data) {
3409
3517
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
3410
3518
  await this.ptyProcess.write(data);
@@ -3428,6 +3536,121 @@ var init_provider_cli_adapter = __esm({
3428
3536
  this.finishRetryTimer = null;
3429
3537
  }
3430
3538
  }
3539
+ commitSendUserTurn(state) {
3540
+ if (state.didCommitUserTurn) return;
3541
+ state.didCommitUserTurn = true;
3542
+ this.committedMessages.push({ role: "user", content: state.text, timestamp: Date.now() });
3543
+ this.syncMessageViews();
3544
+ }
3545
+ armResponseTimeout() {
3546
+ if (this.responseTimeout) clearTimeout(this.responseTimeout);
3547
+ this.responseTimeout = setTimeout(() => {
3548
+ if (this.isWaitingForResponse) this.finishResponse();
3549
+ }, this.timeouts.maxResponse);
3550
+ }
3551
+ writeSubmitKeyForRetry(mode) {
3552
+ void this.writeToPty(this.sendKey).catch((error) => {
3553
+ LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error?.message || error}`);
3554
+ });
3555
+ }
3556
+ retrySubmitIfStuck(state, attempt) {
3557
+ this.submitRetryTimer = null;
3558
+ if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
3559
+ const screenText = this.terminalScreen.getText();
3560
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3561
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
3562
+ this.recordTrace("submit_write", {
3563
+ mode: "submit_retry",
3564
+ attempt,
3565
+ sendKey: this.sendKey,
3566
+ screenText: summarizeCliTraceText(screenText, 500)
3567
+ });
3568
+ this.writeSubmitKeyForRetry("submit_retry");
3569
+ if (attempt >= 3) {
3570
+ this.submitRetryUsed = true;
3571
+ return;
3572
+ }
3573
+ this.submitRetryTimer = setTimeout(() => this.retrySubmitIfStuck(state, attempt + 1), state.retryDelayMs);
3574
+ }
3575
+ retryImmediateSubmitIfStuck(state) {
3576
+ this.submitRetryTimer = null;
3577
+ if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
3578
+ const screenText = this.terminalScreen.getText();
3579
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3580
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
3581
+ this.recordTrace("submit_write", {
3582
+ mode: "immediate_retry",
3583
+ attempt: 1,
3584
+ sendKey: this.sendKey,
3585
+ screenText: summarizeCliTraceText(screenText, 500)
3586
+ });
3587
+ this.writeSubmitKeyForRetry("immediate_retry");
3588
+ this.submitRetryUsed = true;
3589
+ }
3590
+ submitSendKey(state, completion) {
3591
+ if (!this.ptyProcess) {
3592
+ completion.resolveOnce();
3593
+ return;
3594
+ }
3595
+ this.submitPendingUntil = 0;
3596
+ const screenText = this.terminalScreen.getText();
3597
+ this.recordTrace("submit_write", {
3598
+ mode: "submit_key",
3599
+ sendKey: this.sendKey,
3600
+ screenText: summarizeCliTraceText(screenText, 500)
3601
+ });
3602
+ void this.writeToPty(this.sendKey).then(() => {
3603
+ this.commitSendUserTurn(state);
3604
+ this.submitRetryTimer = setTimeout(() => this.retrySubmitIfStuck(state, 1), state.retryDelayMs);
3605
+ this.armResponseTimeout();
3606
+ completion.resolveOnce();
3607
+ }, completion.rejectOnce);
3608
+ }
3609
+ submitImmediatePrompt(state, completion) {
3610
+ this.submitPendingUntil = 0;
3611
+ this.recordTrace("submit_write", {
3612
+ mode: "immediate",
3613
+ text: summarizeCliTraceText(state.text, 500),
3614
+ sendKey: this.sendKey,
3615
+ screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
3616
+ });
3617
+ void this.writeToPty(state.text + this.sendKey).then(() => {
3618
+ this.commitSendUserTurn(state);
3619
+ this.submitRetryTimer = setTimeout(() => this.retryImmediateSubmitIfStuck(state), state.retryDelayMs);
3620
+ this.armResponseTimeout();
3621
+ completion.resolveOnce();
3622
+ }, completion.rejectOnce);
3623
+ }
3624
+ waitForEchoAndSubmit(state, completion, submitStartedAt, lastNormalizedScreen = "", lastScreenChangeAt = submitStartedAt) {
3625
+ if (!this.ptyProcess) {
3626
+ completion.resolveOnce();
3627
+ return;
3628
+ }
3629
+ const now = Date.now();
3630
+ const elapsed = now - submitStartedAt;
3631
+ const screenText = this.terminalScreen.getText();
3632
+ const normalizedScreen = normalizePromptText(screenText);
3633
+ const nextScreenChangeAt = normalizedScreen !== lastNormalizedScreen ? now : lastScreenChangeAt;
3634
+ const echoVisible = !state.normalizedPromptSnippet || promptLikelyVisible(screenText, state.normalizedPromptSnippet);
3635
+ if (echoVisible) {
3636
+ const screenSettled = now - nextScreenChangeAt >= 500;
3637
+ if (elapsed >= state.submitDelayMs && screenSettled) {
3638
+ this.submitSendKey(state, completion);
3639
+ return;
3640
+ }
3641
+ }
3642
+ if (elapsed >= state.maxEchoWaitMs) {
3643
+ this.submitSendKey(state, completion);
3644
+ return;
3645
+ }
3646
+ setTimeout(() => this.waitForEchoAndSubmit(
3647
+ state,
3648
+ completion,
3649
+ submitStartedAt,
3650
+ normalizedScreen,
3651
+ nextScreenChangeAt
3652
+ ), 50);
3653
+ }
3431
3654
  async sendMessage(text) {
3432
3655
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
3433
3656
  const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
@@ -3503,12 +3726,13 @@ var init_provider_cli_adapter = __esm({
3503
3726
  const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
3504
3727
  const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
3505
3728
  const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
3506
- let didCommitUserTurn = false;
3507
- const commitUserTurn = () => {
3508
- if (didCommitUserTurn) return;
3509
- didCommitUserTurn = true;
3510
- this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
3511
- this.syncMessageViews();
3729
+ const sendState = {
3730
+ text,
3731
+ normalizedPromptSnippet,
3732
+ submitDelayMs,
3733
+ maxEchoWaitMs,
3734
+ retryDelayMs,
3735
+ didCommitUserTurn: false
3512
3736
  };
3513
3737
  if (this.settleTimer) {
3514
3738
  clearTimeout(this.settleTimer);
@@ -3516,110 +3740,23 @@ var init_provider_cli_adapter = __esm({
3516
3740
  }
3517
3741
  this.responseEpoch += 1;
3518
3742
  this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
3519
- const startResponseTimeout = () => {
3520
- if (this.responseTimeout) clearTimeout(this.responseTimeout);
3521
- this.responseTimeout = setTimeout(() => {
3522
- if (this.isWaitingForResponse) this.finishResponse();
3523
- }, this.timeouts.maxResponse);
3524
- };
3525
3743
  await new Promise((resolve12, reject) => {
3526
3744
  let resolved = false;
3527
- const resolveOnce = () => {
3528
- if (resolved) return;
3529
- resolved = true;
3530
- resolve12();
3531
- };
3532
- const rejectOnce = (error) => {
3533
- if (resolved) return;
3534
- this.resetPendingSendState("send_write_failed");
3535
- resolved = true;
3536
- reject(error);
3537
- };
3538
- const writeRetryKey = (mode) => {
3539
- void this.writeToPty(this.sendKey).catch((error) => {
3540
- LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error?.message || error}`);
3541
- });
3542
- };
3543
- const submit = () => {
3544
- if (!this.ptyProcess) {
3545
- resolveOnce();
3546
- return;
3745
+ const completion = {
3746
+ resolveOnce: () => {
3747
+ if (resolved) return;
3748
+ resolved = true;
3749
+ resolve12();
3750
+ },
3751
+ rejectOnce: (error) => {
3752
+ if (resolved) return;
3753
+ this.resetPendingSendState("send_write_failed");
3754
+ resolved = true;
3755
+ reject(error);
3547
3756
  }
3548
- this.submitPendingUntil = 0;
3549
- const screenText = this.terminalScreen.getText();
3550
- this.recordTrace("submit_write", {
3551
- mode: "submit_key",
3552
- sendKey: this.sendKey,
3553
- screenText: summarizeCliTraceText(screenText, 500)
3554
- });
3555
- const retrySubmitIfStuck = (attempt) => {
3556
- this.submitRetryTimer = null;
3557
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
3558
- if (this.hasActionableApproval()) return;
3559
- if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
3560
- const screenText2 = this.terminalScreen.getText();
3561
- if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
3562
- const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
3563
- if (liveApproval) return;
3564
- const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
3565
- if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
3566
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3567
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
3568
- this.recordTrace("submit_write", {
3569
- mode: "submit_retry",
3570
- attempt,
3571
- sendKey: this.sendKey,
3572
- screenText: summarizeCliTraceText(screenText2, 500)
3573
- });
3574
- writeRetryKey("submit_retry");
3575
- if (attempt >= 3) {
3576
- this.submitRetryUsed = true;
3577
- return;
3578
- }
3579
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
3580
- };
3581
- void this.writeToPty(this.sendKey).then(() => {
3582
- commitUserTurn();
3583
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
3584
- startResponseTimeout();
3585
- resolveOnce();
3586
- }, rejectOnce);
3587
3757
  };
3588
3758
  if (this.submitStrategy === "immediate") {
3589
- this.submitPendingUntil = 0;
3590
- this.recordTrace("submit_write", {
3591
- mode: "immediate",
3592
- text: summarizeCliTraceText(text, 500),
3593
- sendKey: this.sendKey,
3594
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
3595
- });
3596
- void this.writeToPty(text + this.sendKey).then(() => {
3597
- commitUserTurn();
3598
- this.submitRetryTimer = setTimeout(() => {
3599
- this.submitRetryTimer = null;
3600
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
3601
- if (this.hasActionableApproval()) return;
3602
- if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
3603
- const screenText = this.terminalScreen.getText();
3604
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
3605
- const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
3606
- if (liveApproval) return;
3607
- const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
3608
- if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
3609
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
3610
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
3611
- this.recordTrace("submit_write", {
3612
- mode: "immediate_retry",
3613
- attempt: 1,
3614
- sendKey: this.sendKey,
3615
- screenText: summarizeCliTraceText(screenText, 500)
3616
- });
3617
- writeRetryKey("immediate_retry");
3618
- this.submitRetryUsed = true;
3619
- }, retryDelayMs);
3620
- startResponseTimeout();
3621
- resolveOnce();
3622
- }, rejectOnce);
3759
+ this.submitImmediatePrompt(sendState, completion);
3623
3760
  return;
3624
3761
  }
3625
3762
  if (submitDelayMs > 0) {
@@ -3632,36 +3769,10 @@ var init_provider_cli_adapter = __esm({
3632
3769
  screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
3633
3770
  });
3634
3771
  const submitStartedAt = Date.now();
3635
- let lastNormalizedScreen = "";
3636
- let lastScreenChangeAt = submitStartedAt;
3637
- const waitForEchoAndSubmit = () => {
3638
- if (!this.ptyProcess) {
3639
- resolveOnce();
3640
- return;
3641
- }
3642
- const now = Date.now();
3643
- const elapsed = now - submitStartedAt;
3644
- const screenText = this.terminalScreen.getText();
3645
- const normalizedScreen = normalizePromptText(screenText);
3646
- if (normalizedScreen !== lastNormalizedScreen) {
3647
- lastNormalizedScreen = normalizedScreen;
3648
- lastScreenChangeAt = now;
3649
- }
3650
- const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
3651
- if (echoVisible) {
3652
- const screenSettled = now - lastScreenChangeAt >= 500;
3653
- if (elapsed >= submitDelayMs && screenSettled) {
3654
- submit();
3655
- return;
3656
- }
3657
- }
3658
- if (elapsed >= maxEchoWaitMs) {
3659
- submit();
3660
- return;
3661
- }
3662
- setTimeout(waitForEchoAndSubmit, 50);
3663
- };
3664
- void this.writeToPty(text).then(() => waitForEchoAndSubmit(), rejectOnce);
3772
+ void this.writeToPty(text).then(
3773
+ () => this.waitForEchoAndSubmit(sendState, completion, submitStartedAt),
3774
+ completion.rejectOnce
3775
+ );
3665
3776
  });
3666
3777
  }
3667
3778
  getPartialResponse() {
@@ -3738,9 +3849,9 @@ var init_provider_cli_adapter = __esm({
3738
3849
  shutdown() {
3739
3850
  this.clearIdleFinishCandidate("shutdown");
3740
3851
  this.clearAllTimers();
3741
- this.pendingOutputParseBuffer = "";
3852
+ this.pendingOutputParseChunks = [];
3742
3853
  this.pendingTerminalQueryTail = "";
3743
- this.ptyOutputBuffer = "";
3854
+ this.ptyOutputChunks = [];
3744
3855
  this.finishRetryCount = 0;
3745
3856
  if (this.ptyProcess) {
3746
3857
  this.ptyProcess.write("");
@@ -3761,9 +3872,9 @@ var init_provider_cli_adapter = __esm({
3761
3872
  detach() {
3762
3873
  this.clearIdleFinishCandidate("detach");
3763
3874
  this.clearAllTimers();
3764
- this.pendingOutputParseBuffer = "";
3875
+ this.pendingOutputParseChunks = [];
3765
3876
  this.pendingTerminalQueryTail = "";
3766
- this.ptyOutputBuffer = "";
3877
+ this.ptyOutputChunks = [];
3767
3878
  this.finishRetryCount = 0;
3768
3879
  if (this.ptyProcess) {
3769
3880
  try {
@@ -3794,19 +3905,19 @@ var init_provider_cli_adapter = __esm({
3794
3905
  clearTimeout(this.pendingOutputParseTimer);
3795
3906
  this.pendingOutputParseTimer = null;
3796
3907
  }
3797
- this.pendingOutputParseBuffer = "";
3908
+ this.pendingOutputParseChunks = [];
3798
3909
  this.pendingTerminalQueryTail = "";
3799
3910
  if (this.ptyOutputFlushTimer) {
3800
3911
  clearTimeout(this.ptyOutputFlushTimer);
3801
3912
  this.ptyOutputFlushTimer = null;
3802
3913
  }
3803
- this.ptyOutputBuffer = "";
3914
+ this.ptyOutputChunks = [];
3804
3915
  if (this.finishRetryTimer) {
3805
3916
  clearTimeout(this.finishRetryTimer);
3806
3917
  this.finishRetryTimer = null;
3807
3918
  }
3808
3919
  this.finishRetryCount = 0;
3809
- this.terminalScreen.reset();
3920
+ this.resetTerminalScreen();
3810
3921
  this.ptyProcess?.clearBuffer?.();
3811
3922
  this.onStatusChange?.();
3812
3923
  }
@@ -3923,7 +4034,7 @@ var init_provider_cli_adapter = __esm({
3923
4034
  traceEntryCount: this.traceEntries.length,
3924
4035
  statusHistory: this.statusHistory.slice(-30),
3925
4036
  timeouts: this.timeouts,
3926
- pendingOutputParseBufferLength: this.pendingOutputParseBuffer.length,
4037
+ pendingOutputParseBufferLength: this.pendingOutputParseChunks.reduce((total, chunk) => total + chunk.length, 0),
3927
4038
  pendingOutputParseScheduled: !!this.pendingOutputParseTimer,
3928
4039
  ptyAlive: !!this.ptyProcess
3929
4040
  };
@@ -9514,14 +9625,14 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
9514
9625
  id: state.instanceId || state.type,
9515
9626
  parentId: null,
9516
9627
  providerType: state.type,
9517
- ...includeSessionMetadata && { providerName: state.name },
9628
+ providerName: state.name,
9518
9629
  kind: "workspace",
9519
9630
  transport: "cdp-page",
9520
9631
  status: normalizeManagedStatus(activeChat?.status || state.status, {
9521
9632
  activeModal: activeChat?.activeModal || null
9522
9633
  }),
9523
9634
  title,
9524
- ...includeSessionMetadata && { workspace: state.workspace || null },
9635
+ workspace: state.workspace || null,
9525
9636
  activeChat,
9526
9637
  ...summaryMetadata && { summaryMetadata },
9527
9638
  ...includeSessionMetadata && { capabilities: state.sessionCapabilities || IDE_SESSION_CAPABILITIES },
@@ -9546,7 +9657,7 @@ function buildExtensionAgentSession(parent, ext, options) {
9546
9657
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
9547
9658
  parentId: parent.instanceId || parent.type,
9548
9659
  providerType: ext.type,
9549
- ...includeSessionMetadata && { providerName: ext.name },
9660
+ providerName: ext.name,
9550
9661
  providerSessionId: ext.providerSessionId,
9551
9662
  kind: "agent",
9552
9663
  transport: "cdp-webview",
@@ -9554,7 +9665,7 @@ function buildExtensionAgentSession(parent, ext, options) {
9554
9665
  activeModal: activeChat?.activeModal || null
9555
9666
  }),
9556
9667
  title: activeChat?.title || ext.name,
9557
- ...includeSessionMetadata && { workspace: parent.workspace || null },
9668
+ workspace: parent.workspace || null,
9558
9669
  activeChat,
9559
9670
  ...summaryMetadata && { summaryMetadata },
9560
9671
  ...includeSessionMetadata && { capabilities: ext.sessionCapabilities || EXTENSION_SESSION_CAPABILITIES },
@@ -9594,7 +9705,7 @@ function buildCliSession(state, options) {
9594
9705
  id: state.instanceId,
9595
9706
  parentId: null,
9596
9707
  providerType: state.type,
9597
- ...includeSessionMetadata && { providerName: state.name },
9708
+ providerName: state.name,
9598
9709
  providerSessionId: state.providerSessionId,
9599
9710
  kind: "agent",
9600
9711
  transport: "pty",
@@ -9602,7 +9713,7 @@ function buildCliSession(state, options) {
9602
9713
  activeModal: activeChat?.activeModal || null
9603
9714
  }),
9604
9715
  title: activeChat?.title || state.name,
9605
- ...includeSessionMetadata && { workspace: state.workspace || null },
9716
+ workspace: state.workspace || null,
9606
9717
  ...includeRuntimeMetadata && {
9607
9718
  runtimeKey: state.runtime?.runtimeKey,
9608
9719
  runtimeDisplayName: state.runtime?.displayName,
@@ -9641,14 +9752,14 @@ function buildAcpSession(state, options) {
9641
9752
  id: state.instanceId,
9642
9753
  parentId: null,
9643
9754
  providerType: state.type,
9644
- ...includeSessionMetadata && { providerName: state.name },
9755
+ providerName: state.name,
9645
9756
  kind: "agent",
9646
9757
  transport: "acp",
9647
9758
  status: normalizeManagedStatus(activeChat?.status || state.status, {
9648
9759
  activeModal: activeChat?.activeModal || null
9649
9760
  }),
9650
9761
  title: activeChat?.title || state.name,
9651
- ...includeSessionMetadata && { workspace: state.workspace || null },
9762
+ workspace: state.workspace || null,
9652
9763
  activeChat,
9653
9764
  ...summaryMetadata && { summaryMetadata },
9654
9765
  ...includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES },
@@ -10095,26 +10206,26 @@ function buildReadChatReplayCollapseSignature(message) {
10095
10206
  function shouldCollapseReadChatReplayDuplicate(message) {
10096
10207
  if (!message) return false;
10097
10208
  const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
10098
- if (role !== "assistant" && role !== "system") return false;
10099
- const kind = typeof message.kind === "string" ? message.kind.trim().toLowerCase() : "standard";
10100
- return kind === "tool" || kind === "terminal" || kind === "thought" || kind === "system";
10209
+ return role === "assistant" || role === "system";
10101
10210
  }
10102
10211
  function collapseReplayDuplicatesFromReadChat(messages) {
10103
10212
  const collapsed = [];
10104
- let lastReplayTurnSignature = "";
10213
+ const replaySignaturesInCurrentTurn = /* @__PURE__ */ new Set();
10105
10214
  for (const message of messages) {
10215
+ const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
10216
+ if (role === "user") {
10217
+ replaySignaturesInCurrentTurn.clear();
10218
+ }
10106
10219
  const signature = buildReadChatReplayCollapseSignature(message);
10107
10220
  const previous = collapsed[collapsed.length - 1];
10108
10221
  const previousSignature = buildReadChatReplayCollapseSignature(previous);
10109
10222
  if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
10110
10223
  if (previousSignature === signature) continue;
10111
- if (lastReplayTurnSignature === signature) continue;
10224
+ if (replaySignaturesInCurrentTurn.has(signature)) continue;
10112
10225
  }
10113
10226
  collapsed.push(message);
10114
10227
  if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
10115
- lastReplayTurnSignature = signature;
10116
- } else if ((message.role || "").toLowerCase() === "user") {
10117
- lastReplayTurnSignature = "";
10228
+ replaySignaturesInCurrentTurn.add(signature);
10118
10229
  }
10119
10230
  }
10120
10231
  return collapsed;
@@ -17622,6 +17733,18 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
17622
17733
  const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
17623
17734
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
17624
17735
  }
17736
+ function projectLiveSessionFromFull(session) {
17737
+ const {
17738
+ capabilities: _capabilities,
17739
+ controlValues: _controlValues,
17740
+ providerControls: _providerControls,
17741
+ ...rest
17742
+ } = session;
17743
+ return {
17744
+ ...rest,
17745
+ activeChat: normalizeActiveChatData(session.activeChat, LIVE_STATUS_ACTIVE_CHAT_OPTIONS)
17746
+ };
17747
+ }
17625
17748
  function buildRecentLaunches(recentActivity) {
17626
17749
  return recentActivity.map((item) => ({
17627
17750
  id: item.id,
@@ -17646,7 +17769,7 @@ function buildStatusSnapshot(options) {
17646
17769
  options.cdpManagers,
17647
17770
  { profile: "full" }
17648
17771
  );
17649
- const sessions = profile === "full" ? unreadSourceSessions : buildSessionEntries(
17772
+ const sessions = profile === "full" ? unreadSourceSessions : profile === "live" ? unreadSourceSessions.map(projectLiveSessionFromFull) : buildSessionEntries(
17650
17773
  options.allStates,
17651
17774
  options.cdpManagers,
17652
17775
  { profile }
@@ -18932,10 +19055,6 @@ var DaemonStatusReporter = class {
18932
19055
  LOG.info("StatusReport", `\u2192${target} ${baseSummary}`);
18933
19056
  }
18934
19057
  }
18935
- const sessions = buildSessionEntries(
18936
- allStates,
18937
- this.deps.cdpManagers
18938
- );
18939
19058
  const payload = {
18940
19059
  ...buildStatusSnapshot({
18941
19060
  allStates,
@@ -18955,9 +19074,9 @@ var DaemonStatusReporter = class {
18955
19074
  }),
18956
19075
  screenshotUsage: this.deps.getScreenshotUsage?.() || null
18957
19076
  };
18958
- const payloadBytes = JSON.stringify(payload).length;
18959
19077
  const p2pSent = this.sendP2PPayload(payload);
18960
19078
  if (p2pSent) {
19079
+ const payloadBytes = JSON.stringify(payload).length;
18961
19080
  LOG.debug("P2P", `sent (${payloadBytes} bytes)`);
18962
19081
  if (payloadBytes > 256 * 1024) {
18963
19082
  LOG.warn(
@@ -18967,8 +19086,10 @@ var DaemonStatusReporter = class {
18967
19086
  }
18968
19087
  }
18969
19088
  if (opts?.p2pOnly) return;
19089
+ if (!serverConnected || !serverConn) return;
19090
+ const payloadSessions = Array.isArray(payload.sessions) ? payload.sessions : [];
18970
19091
  const wsPayload = {
18971
- sessions: sessions.map((session) => ({
19092
+ sessions: payloadSessions.map((session) => ({
18972
19093
  id: session.id,
18973
19094
  parentId: session.parentId,
18974
19095
  providerType: session.providerType,
@@ -18994,8 +19115,9 @@ var DaemonStatusReporter = class {
18994
19115
  return;
18995
19116
  }
18996
19117
  this.lastServerStatusHash = wsHash;
19118
+ const wsPayloadBytes = JSON.stringify(wsPayload).length;
18997
19119
  serverConn.sendMessage("status_report", wsPayload);
18998
- LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
19120
+ LOG.debug("Server", `sent status_report (${wsPayloadBytes} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
18999
19121
  }
19000
19122
  // ─── P2P ─────────────────────────────────────────
19001
19123
  sendP2PPayload(payload) {