@adhdev/daemon-core 0.8.84 → 0.8.86

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.
@@ -138,8 +138,11 @@ export declare class ProviderCliAdapter implements CliAdapter {
138
138
  private getStartupConfirmationModal;
139
139
  private shouldResolveModalWithEnter;
140
140
  private waitForInteractivePrompt;
141
+ private clearStaleIdleResponseGuard;
142
+ private hasMeaningfulResponseBuffer;
141
143
  private evaluateSettled;
142
144
  private finishResponse;
145
+ private maybeCommitVisibleIdleTranscript;
143
146
  private commitCurrentTranscript;
144
147
  private runDetectStatus;
145
148
  private runParseApproval;
@@ -1,4 +1,4 @@
1
- import { type SessionHostEndpoint, type SessionHostCategory } from '@adhdev/session-host-core';
1
+ import { type SessionHostEndpoint, type SessionHostCategory, type SessionHostRecord } from '@adhdev/session-host-core';
2
2
  import type { PtyRuntimeTransport, PtySpawnOptions, PtyTransportFactory } from './pty-transport.js';
3
3
  interface SessionHostPtyTransportFactoryOptions {
4
4
  endpoint?: SessionHostEndpoint;
@@ -12,6 +12,7 @@ interface SessionHostPtyTransportFactoryOptions {
12
12
  meta?: Record<string, unknown>;
13
13
  attachExisting?: boolean;
14
14
  }
15
+ export declare function shouldResumeAttachedSession(record: SessionHostRecord | null | undefined): boolean;
15
16
  export declare class SessionHostPtyTransportFactory implements PtyTransportFactory {
16
17
  private readonly options;
17
18
  constructor(options: SessionHostPtyTransportFactoryOptions);
package/dist/index.js CHANGED
@@ -2501,6 +2501,58 @@ var init_provider_cli_adapter = __esm({
2501
2501
  `[${this.cliType}] Interactive prompt wait timed out after ${maxWaitMs}ms; proceeding with screen=${JSON.stringify(summarizeCliTraceText(finalScreenText, 240)).slice(0, 280)}`
2502
2502
  );
2503
2503
  }
2504
+ clearStaleIdleResponseGuard(reason) {
2505
+ const screenText = this.terminalScreen.getText() || "";
2506
+ const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
2507
+ const blockingModal = this.activeModal || this.getStartupConfirmationModal(screenText);
2508
+ if (!this.isWaitingForResponse || this.currentStatus !== "idle" || !visibleIdlePrompt || !!blockingModal) {
2509
+ return false;
2510
+ }
2511
+ if (this.responseTimeout) {
2512
+ clearTimeout(this.responseTimeout);
2513
+ this.responseTimeout = null;
2514
+ }
2515
+ if (this.idleTimeout) {
2516
+ clearTimeout(this.idleTimeout);
2517
+ this.idleTimeout = null;
2518
+ }
2519
+ if (this.approvalExitTimeout) {
2520
+ clearTimeout(this.approvalExitTimeout);
2521
+ this.approvalExitTimeout = null;
2522
+ }
2523
+ if (this.finishRetryTimer) {
2524
+ clearTimeout(this.finishRetryTimer);
2525
+ this.finishRetryTimer = null;
2526
+ }
2527
+ this.clearIdleFinishCandidate(reason);
2528
+ this.responseBuffer = "";
2529
+ this.isWaitingForResponse = false;
2530
+ this.responseSettleIgnoreUntil = 0;
2531
+ this.submitRetryUsed = false;
2532
+ this.submitRetryPromptSnippet = "";
2533
+ this.finishRetryCount = 0;
2534
+ this.currentTurnScope = null;
2535
+ this.activeModal = null;
2536
+ this.recordTrace("stale_idle_response_cleared", {
2537
+ reason,
2538
+ screenText: summarizeCliTraceText(screenText, 240)
2539
+ });
2540
+ return true;
2541
+ }
2542
+ hasMeaningfulResponseBuffer(promptSnippet) {
2543
+ const raw = String(this.responseBuffer || "").trim();
2544
+ if (!raw) return false;
2545
+ const normalizedPrompt = compactPromptText(promptSnippet);
2546
+ if (!normalizedPrompt) return true;
2547
+ const normalizedBuffer = compactPromptText(raw);
2548
+ if (!normalizedBuffer) return false;
2549
+ if (normalizedBuffer === normalizedPrompt) return false;
2550
+ if (normalizedBuffer.startsWith(normalizedPrompt)) {
2551
+ const remainder = normalizedBuffer.slice(normalizedPrompt.length).replace(/[─═\-]+/g, "").replace(/⏵⏵accepteditson\([^)]*\)/gi, "").replace(/accepteditson\([^)]*\)/gi, "").replace(/(?:◐|◑|◒|◓|◔|◕|◉|●|·)?(?:x?high|medium|low|max)·?\/effort/gi, "").replace(/updateavailable!run:[a-z0-9:._\-/]+/gi, "").replace(/esctointerrupt/gi, "").replace(/❯/g, "").replace(/^[\s\-–—:;,.!/?]+/, "").trim();
2552
+ return remainder.length > 0;
2553
+ }
2554
+ return true;
2555
+ }
2504
2556
  evaluateSettled() {
2505
2557
  const now = Date.now();
2506
2558
  if (this.submitPendingUntil > now || this.responseSettleIgnoreUntil > now) {
@@ -2533,7 +2585,11 @@ var init_provider_cli_adapter = __esm({
2533
2585
  scope: this.currentTurnScope,
2534
2586
  lastOutputAt: this.lastOutputAt
2535
2587
  }) : [];
2588
+ if (this.maybeCommitVisibleIdleTranscript(parsedTranscript)) {
2589
+ return;
2590
+ }
2536
2591
  const lastParsedAssistant = [...parsedMessages].reverse().find((message) => message.role === "assistant");
2592
+ const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet || this.currentTurnScope?.prompt || "");
2537
2593
  this.recordTrace("settled", {
2538
2594
  tail: summarizeCliTraceText(tail, 500),
2539
2595
  screenText: summarizeCliTraceText(screenText, 1200),
@@ -2551,6 +2607,24 @@ var init_provider_cli_adapter = __esm({
2551
2607
  scope: this.currentTurnScope
2552
2608
  })
2553
2609
  });
2610
+ if (this.currentTurnScope && !lastParsedAssistant && !this.submitRetryUsed && this.ptyProcess && this.currentStatus !== "waiting_approval" && promptLikelyVisible(screenText, normalizedPromptSnippet) && !this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) {
2611
+ this.submitRetryUsed = true;
2612
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
2613
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key from settled parser (no assistant yet)`);
2614
+ this.recordTrace("submit_write", {
2615
+ mode: "settled_retry",
2616
+ sendKey: this.sendKey,
2617
+ screenText: summarizeCliTraceText(screenText, 500)
2618
+ });
2619
+ this.ptyProcess.write(this.sendKey);
2620
+ if (this.settleTimer) clearTimeout(this.settleTimer);
2621
+ this.settleTimer = setTimeout(() => {
2622
+ this.settleTimer = null;
2623
+ this.settledBuffer = this.recentOutputBuffer;
2624
+ this.evaluateSettled();
2625
+ }, this.timeouts.outputSettle + 150);
2626
+ return;
2627
+ }
2554
2628
  if (this.currentTurnScope && !lastParsedAssistant) {
2555
2629
  LOG.info(
2556
2630
  "CLI",
@@ -2593,7 +2667,15 @@ var init_provider_cli_adapter = __esm({
2593
2667
  }
2594
2668
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
2595
2669
  const statusActivityHoldMs = this.getStatusActivityHoldMs();
2596
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
2670
+ const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
2671
+ const visibleAssistantCandidate = this.looksLikeVisibleAssistantCandidate(screenText);
2672
+ if (this.currentTurnScope && this.cliType === "claude-cli") {
2673
+ LOG.info(
2674
+ "CLI",
2675
+ `[${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)} visibleIdlePrompt=${String(visibleIdlePrompt)} visibleAssistantCandidate=${String(visibleAssistantCandidate)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 160)).slice(0, 220)} screen=${JSON.stringify(summarizeCliTraceText(screenText, 160)).slice(0, 220)}`
2676
+ );
2677
+ }
2678
+ const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(visibleIdlePrompt && visibleAssistantCandidate);
2597
2679
  if (shouldHoldGenerating) {
2598
2680
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
2599
2681
  this.setStatus("generating", "recent_activity_hold");
@@ -2624,8 +2706,8 @@ var init_provider_cli_adapter = __esm({
2624
2706
  if (scriptStatus === "waiting_approval") {
2625
2707
  this.clearIdleFinishCandidate("waiting_approval");
2626
2708
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
2627
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
2628
- if ((inCooldown || visibleIdlePrompt) && !modal) {
2709
+ const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
2710
+ if ((inCooldown || visibleIdlePrompt2) && !modal) {
2629
2711
  if (this.approvalExitTimeout) {
2630
2712
  clearTimeout(this.approvalExitTimeout);
2631
2713
  this.approvalExitTimeout = null;
@@ -2697,7 +2779,7 @@ var init_provider_cli_adapter = __esm({
2697
2779
  this.lastApprovalResolvedAt = Date.now();
2698
2780
  }
2699
2781
  if (this.isWaitingForResponse) {
2700
- const visibleIdlePrompt = this.looksLikeVisibleIdlePrompt(screenText);
2782
+ const visibleIdlePrompt2 = this.looksLikeVisibleIdlePrompt(screenText);
2701
2783
  const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
2702
2784
  const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
2703
2785
  const hasAssistantTurn = !!lastParsedAssistant;
@@ -2705,12 +2787,12 @@ var init_provider_cli_adapter = __esm({
2705
2787
  const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
2706
2788
  const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
2707
2789
  const idleStableThresholdMs = idleFinishConfirmMs;
2708
- const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
2790
+ const idleReady = visibleIdlePrompt2 && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
2709
2791
  const candidate = this.idleFinishCandidate;
2710
2792
  const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= idleFinishConfirmMs;
2711
2793
  const canFinishImmediately = idleReady && candidateQuiet;
2712
2794
  this.recordTrace("idle_decision", {
2713
- visibleIdlePrompt,
2795
+ visibleIdlePrompt: visibleIdlePrompt2,
2714
2796
  quietForMs,
2715
2797
  screenStableMs,
2716
2798
  hasAssistantTurn,
@@ -2830,6 +2912,70 @@ var init_provider_cli_adapter = __esm({
2830
2912
  this.setStatus("idle", "response_finished");
2831
2913
  this.onStatusChange?.();
2832
2914
  }
2915
+ maybeCommitVisibleIdleTranscript(parsed, options) {
2916
+ const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
2917
+ if (!allowImmediateScriptIdleCommit) return false;
2918
+ if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
2919
+ return false;
2920
+ }
2921
+ if (options?.requireVisibleAssistantCandidate) {
2922
+ const candidateText = options.screenText || this.terminalScreen.getText() || "";
2923
+ if (!this.looksLikeVisibleAssistantCandidate(candidateText)) {
2924
+ return false;
2925
+ }
2926
+ }
2927
+ const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
2928
+ committedMessages: this.committedMessages,
2929
+ scope: this.currentTurnScope,
2930
+ lastOutputAt: this.lastOutputAt
2931
+ });
2932
+ const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
2933
+ if (!visibleAssistant) return false;
2934
+ this.committedMessages = hydratedForIdleCommit;
2935
+ const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
2936
+ if (promptForTrim) {
2937
+ const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
2938
+ if (lastAssistantForTrim) {
2939
+ lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
2940
+ }
2941
+ }
2942
+ if (this.responseTimeout) {
2943
+ clearTimeout(this.responseTimeout);
2944
+ this.responseTimeout = null;
2945
+ }
2946
+ if (this.idleTimeout) {
2947
+ clearTimeout(this.idleTimeout);
2948
+ this.idleTimeout = null;
2949
+ }
2950
+ if (this.approvalExitTimeout) {
2951
+ clearTimeout(this.approvalExitTimeout);
2952
+ this.approvalExitTimeout = null;
2953
+ }
2954
+ if (this.submitRetryTimer) {
2955
+ clearTimeout(this.submitRetryTimer);
2956
+ this.submitRetryTimer = null;
2957
+ }
2958
+ if (this.finishRetryTimer) {
2959
+ clearTimeout(this.finishRetryTimer);
2960
+ this.finishRetryTimer = null;
2961
+ }
2962
+ this.syncMessageViews();
2963
+ this.responseBuffer = "";
2964
+ this.isWaitingForResponse = false;
2965
+ this.responseSettleIgnoreUntil = 0;
2966
+ this.submitRetryUsed = false;
2967
+ this.submitRetryPromptSnippet = "";
2968
+ this.finishRetryCount = 0;
2969
+ this.currentTurnScope = null;
2970
+ this.activeModal = null;
2971
+ this.setStatus("idle", "script_idle_commit");
2972
+ this.onStatusChange?.();
2973
+ this.recordTrace("script_idle_commit", {
2974
+ messageCount: this.committedMessages.length,
2975
+ lastAssistant: summarizeCliTraceText(visibleAssistant.content, 320)
2976
+ });
2977
+ return true;
2978
+ }
2833
2979
  commitCurrentTranscript() {
2834
2980
  const parsed = this.parseCurrentTranscript(
2835
2981
  this.committedMessages,
@@ -2851,6 +2997,12 @@ var init_provider_cli_adapter = __esm({
2851
2997
  }
2852
2998
  this.syncMessageViews();
2853
2999
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
3000
+ if (this.currentTurnScope) {
3001
+ LOG.info(
3002
+ "CLI",
3003
+ `[${this.cliType}] commitCurrentTranscript committedMessages=${this.committedMessages.length} finalLastAssistant=${JSON.stringify(summarizeCliTraceText(lastAssistant?.content || "", 220)).slice(0, 260)}`
3004
+ );
3005
+ }
2854
3006
  this.recordTrace("commit_transcript", {
2855
3007
  parsedStatus: parsed.status || null,
2856
3008
  messageCount: this.committedMessages.length,
@@ -2870,11 +3022,18 @@ var init_provider_cli_adapter = __esm({
2870
3022
  `[${this.cliType}] Commit without assistant turn: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(summarizeCliTraceText(this.responseBuffer, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
2871
3023
  );
2872
3024
  }
3025
+ const hasAssistant = !!lastAssistant;
2873
3026
  return {
2874
- hasAssistant: !!lastAssistant,
3027
+ hasAssistant,
2875
3028
  assistantContent: lastAssistant?.content || ""
2876
3029
  };
2877
3030
  }
3031
+ if (this.currentTurnScope) {
3032
+ LOG.info(
3033
+ "CLI",
3034
+ `[${this.cliType}] commitCurrentTranscript parsed.messages=none responseBufferLen=${this.responseBuffer.length} accumulatedBufferLen=${this.accumulatedBuffer.length} parsedStatus=${parsed?.status || "-"} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"}`
3035
+ );
3036
+ }
2878
3037
  return {
2879
3038
  hasAssistant: false,
2880
3039
  assistantContent: ""
@@ -2960,19 +3119,25 @@ var init_provider_cli_adapter = __esm({
2960
3119
  this.currentTurnScope,
2961
3120
  screenText
2962
3121
  );
2963
- const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
3122
+ if (this.maybeCommitVisibleIdleTranscript(parsed)) {
3123
+ return this.getScriptParsedStatus();
3124
+ }
3125
+ const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
2964
3126
  let result;
2965
3127
  if (parsed && Array.isArray(parsed.messages)) {
2966
- const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => buildChatMessage({
2967
- ...message,
2968
- id: message.id || `msg_${index}`,
2969
- index: typeof message.index === "number" ? message.index : index,
2970
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
2971
- })) : hydrateCliParsedMessages(parsed.messages, {
3128
+ const parsedHydratedMessages = hydrateCliParsedMessages(parsed.messages, {
2972
3129
  committedMessages: this.committedMessages,
2973
3130
  scope: this.currentTurnScope,
2974
3131
  lastOutputAt: this.lastOutputAt
2975
3132
  });
3133
+ const committedHydratedMessages = this.committedMessages.map((message, index) => buildChatMessage({
3134
+ ...message,
3135
+ id: message.id || `msg_${index}`,
3136
+ index: typeof message.index === "number" ? message.index : index,
3137
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3138
+ }));
3139
+ const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && committedHydratedMessages.length > parsedHydratedMessages.length;
3140
+ const hydratedMessages = shouldPreferCommittedMessages || shouldPreferCommittedHistoryReplay ? committedHydratedMessages : parsedHydratedMessages;
2976
3141
  result = {
2977
3142
  id: parsed.id || "cli_session",
2978
3143
  status: parsed.status || this.currentStatus,
@@ -2996,6 +3161,23 @@ var init_provider_cli_adapter = __esm({
2996
3161
  activeModal: this.activeModal
2997
3162
  };
2998
3163
  }
3164
+ const hasVisibleAssistantMessage = Array.isArray(result?.messages) && result.messages.some((message) => message?.role === "assistant" && typeof message?.content === "string" && message.content.trim());
3165
+ const shouldClampStaleGeneratingToIdle = result?.status === "generating" && this.currentStatus === "idle" && !this.currentTurnScope && !result?.activeModal && hasVisibleAssistantMessage;
3166
+ if (shouldClampStaleGeneratingToIdle) {
3167
+ result = {
3168
+ ...result,
3169
+ status: "idle",
3170
+ messages: Array.isArray(result.messages) ? result.messages.map((message) => {
3171
+ if (message?.role !== "assistant" || !message?.meta?.streaming) return message;
3172
+ const nextMeta = { ...message.meta || {} };
3173
+ delete nextMeta.streaming;
3174
+ return {
3175
+ ...message,
3176
+ ...Object.keys(nextMeta).length > 0 ? { meta: nextMeta } : { meta: void 0 }
3177
+ };
3178
+ }) : result.messages
3179
+ };
3180
+ }
2999
3181
  this.parsedStatusCache = {
3000
3182
  committedMessagesRef: this.committedMessages,
3001
3183
  responseBuffer: this.responseBuffer,
@@ -3128,9 +3310,27 @@ ${data.message || ""}`.trim();
3128
3310
  }
3129
3311
  }
3130
3312
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
3131
- if (this.isWaitingForResponse && !allowInputDuringGeneration) {
3313
+ const parsedStatusBeforeSend = !allowInputDuringGeneration ? (() => {
3314
+ try {
3315
+ return this.getScriptParsedStatus?.() || null;
3316
+ } catch {
3317
+ return null;
3318
+ }
3319
+ })() : null;
3320
+ const parsedSessionStatus = typeof parsedStatusBeforeSend?.status === "string" ? String(parsedStatusBeforeSend.status) : "";
3321
+ const parsedMessagesBeforeSend = Array.isArray(parsedStatusBeforeSend?.messages) ? parsedStatusBeforeSend.messages.filter((message) => message && (message.role === "user" || message.role === "assistant")) : [];
3322
+ const shouldCommitParsedIdleBeforeSend = !allowInputDuringGeneration && parsedSessionStatus === "idle" && parsedMessagesBeforeSend.length > this.committedMessages.length && parsedMessagesBeforeSend.some((message) => message?.role === "assistant" && typeof message?.content === "string" && message.content.trim());
3323
+ if (shouldCommitParsedIdleBeforeSend) {
3324
+ this.commitCurrentTranscript();
3325
+ }
3326
+ if (!allowInputDuringGeneration && (parsedSessionStatus === "generating" || parsedSessionStatus === "long_generating")) {
3132
3327
  throw new Error(`${this.cliName} is still processing the previous prompt`);
3133
3328
  }
3329
+ if (this.isWaitingForResponse && !allowInputDuringGeneration) {
3330
+ if (!this.clearStaleIdleResponseGuard("send_message_guard")) {
3331
+ throw new Error(`${this.cliName} is still processing the previous prompt`);
3332
+ }
3333
+ }
3134
3334
  const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
3135
3335
  if (blockingModal || this.currentStatus === "waiting_approval") {
3136
3336
  throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
@@ -3210,7 +3410,7 @@ ${data.message || ""}`.trim();
3210
3410
  this.submitRetryTimer = null;
3211
3411
  if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
3212
3412
  if (this.currentStatus === "waiting_approval") return;
3213
- if ((this.responseBuffer || "").trim()) return;
3413
+ if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
3214
3414
  const screenText2 = this.terminalScreen.getText();
3215
3415
  if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
3216
3416
  if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText2)) return;
@@ -3247,7 +3447,7 @@ ${data.message || ""}`.trim();
3247
3447
  this.submitRetryTimer = null;
3248
3448
  if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
3249
3449
  if (this.currentStatus === "waiting_approval") return;
3250
- if ((this.responseBuffer || "").trim()) return;
3450
+ if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
3251
3451
  const screenText = this.terminalScreen.getText();
3252
3452
  if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
3253
3453
  LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
@@ -9748,6 +9948,7 @@ function normalizeReadChatCommandStatus(status, activeModal) {
9748
9948
  }
9749
9949
  function buildReadChatCommandResult(payload, args) {
9750
9950
  let validatedPayload;
9951
+ const debugReadChat = payload?.debugReadChat && typeof payload.debugReadChat === "object" ? payload.debugReadChat : void 0;
9751
9952
  try {
9752
9953
  validatedPayload = validateReadChatResultPayload({
9753
9954
  ...payload,
@@ -9768,7 +9969,8 @@ function buildReadChatCommandResult(payload, args) {
9768
9969
  syncMode: "full",
9769
9970
  replaceFrom: 0,
9770
9971
  totalMessages: messages.length,
9771
- lastMessageSignature
9972
+ lastMessageSignature,
9973
+ ...debugReadChat ? { debugReadChat } : {}
9772
9974
  };
9773
9975
  }
9774
9976
  const sync = computeReadChatSync(messages, cursor);
@@ -9779,7 +9981,8 @@ function buildReadChatCommandResult(payload, args) {
9779
9981
  syncMode: sync.syncMode,
9780
9982
  replaceFrom: sync.replaceFrom,
9781
9983
  totalMessages: sync.totalMessages,
9782
- lastMessageSignature: sync.lastMessageSignature
9984
+ lastMessageSignature: sync.lastMessageSignature,
9985
+ ...debugReadChat ? { debugReadChat } : {}
9783
9986
  };
9784
9987
  }
9785
9988
  function didProviderConfirmSend(result) {
@@ -9870,14 +10073,33 @@ async function handleReadChat(h, args) {
9870
10073
  }
9871
10074
  }
9872
10075
  const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
9873
- const status = parsedRecord || adapter.getStatus();
10076
+ const adapterStatus = adapter.getStatus();
10077
+ const shouldPreferAdapterMessages = Array.isArray(adapterStatus.messages) && adapterStatus.messages.length > 0 && Array.isArray(parsedRecord?.messages) && adapterStatus.messages.length > parsedRecord.messages.length;
10078
+ const status = parsedRecord ? {
10079
+ ...parsedRecord,
10080
+ messages: shouldPreferAdapterMessages ? adapterStatus.messages : parsedRecord.messages,
10081
+ status: adapterStatus.status !== "idle" ? adapterStatus.status : parsedRecord.status || adapterStatus.status,
10082
+ activeModal: parsedRecord.activeModal || adapterStatus.activeModal
10083
+ } : adapterStatus;
9874
10084
  const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
9875
10085
  const providerSessionId = typeof parsedRecord?.providerSessionId === "string" ? parsedRecord.providerSessionId : void 0;
9876
10086
  if (status) {
10087
+ LOG.info("Command", `[read_chat] cli-like resolved provider=${adapter.cliType} target=${String(args?.targetSessionId || "")} adapterStatus=${String(adapterStatus.status || "")} parsedStatus=${String(parsedRecord?.status || "")} shouldPreferAdapterMessages=${String(shouldPreferAdapterMessages)} adapterMsgCount=${Array.isArray(adapterStatus.messages) ? adapterStatus.messages.length : 0} parsedMsgCount=${Array.isArray(parsedRecord?.messages) ? parsedRecord.messages.length : 0} returnedMsgCount=${Array.isArray(status.messages) ? status.messages.length : 0}`);
9877
10088
  return buildReadChatCommandResult({
9878
10089
  messages: status.messages || [],
9879
10090
  status: status.status,
9880
10091
  activeModal: status.activeModal,
10092
+ debugReadChat: {
10093
+ provider: adapter.cliType,
10094
+ targetSessionId: String(args?.targetSessionId || ""),
10095
+ adapterStatus: String(adapterStatus.status || ""),
10096
+ parsedStatus: String(parsedRecord?.status || ""),
10097
+ returnedStatus: String(status.status || ""),
10098
+ shouldPreferAdapterMessages,
10099
+ adapterMsgCount: Array.isArray(adapterStatus.messages) ? adapterStatus.messages.length : 0,
10100
+ parsedMsgCount: Array.isArray(parsedRecord?.messages) ? parsedRecord.messages.length : 0,
10101
+ returnedMsgCount: Array.isArray(status.messages) ? status.messages.length : 0
10102
+ },
9881
10103
  ...title ? { title } : {},
9882
10104
  ...providerSessionId ? { providerSessionId } : {}
9883
10105
  }, args);
@@ -12436,7 +12658,7 @@ var CliProviderInstance = class {
12436
12658
  activeChat: {
12437
12659
  id: `${this.type}_${this.workingDir}`,
12438
12660
  title: parsedStatus?.title || dirName,
12439
- status: parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : parsedStatus?.status || visibleStatus,
12661
+ status: parseErrorMessage ? "error" : autoApproveActive && parsedStatus?.status === "waiting_approval" ? "generating" : adapterStatus.status !== "idle" ? visibleStatus : parsedStatus?.status || visibleStatus,
12440
12662
  messages: mergedMessages,
12441
12663
  activeModal: autoApproveActive ? null : parsedStatus?.activeModal ?? adapterStatus.activeModal,
12442
12664
  inputContent: ""
@@ -24876,6 +25098,13 @@ init_pty_transport();
24876
25098
  // src/cli-adapters/session-host-transport.ts
24877
25099
  var import_session_host_core2 = require("@adhdev/session-host-core");
24878
25100
  init_logger();
25101
+ function shouldResumeAttachedSession(record) {
25102
+ if (!record) return false;
25103
+ if (record.lifecycle === "interrupted") return true;
25104
+ if (record.lifecycle !== "stopped") return false;
25105
+ if (record.meta?.restoredFromStorage === true) return true;
25106
+ return typeof record.meta?.runtimeRecoveryState === "string" && String(record.meta.runtimeRecoveryState).trim().length > 0;
25107
+ }
24879
25108
  var SessionHostRuntimeTransport = class {
24880
25109
  constructor(options) {
24881
25110
  this.options = options;
@@ -25049,7 +25278,7 @@ var SessionHostRuntimeTransport = class {
25049
25278
  payload: {}
25050
25279
  });
25051
25280
  const existingRecord = existingRecords.success && existingRecords.result ? existingRecords.result.find((item) => item.sessionId === this.options.runtimeId) || null : null;
25052
- if (existingRecord?.lifecycle === "interrupted") {
25281
+ if (shouldResumeAttachedSession(existingRecord)) {
25053
25282
  const resumeResponse = await this.client.request({
25054
25283
  type: "resume_session",
25055
25284
  payload: {
@@ -25345,6 +25574,8 @@ async function listHostedCliRuntimes(endpoint) {
25345
25574
  // src/session-host/startup-restore-policy.js
25346
25575
  function shouldAutoRestoreHostedSessionsOnStartup(env = process.env) {
25347
25576
  const raw = typeof env.ADHDEV_RESTORE_HOSTED_SESSIONS_ON_STARTUP === "string" ? env.ADHDEV_RESTORE_HOSTED_SESSIONS_ON_STARTUP.trim().toLowerCase() : "";
25577
+ if (!raw) return true;
25578
+ if (raw === "0" || raw === "false" || raw === "no") return false;
25348
25579
  return raw === "1" || raw === "true" || raw === "yes";
25349
25580
  }
25350
25581