adhdev 0.9.19 → 0.9.21

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/cli/index.js CHANGED
@@ -7195,14 +7195,14 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
7195
7195
  id: state.instanceId || state.type,
7196
7196
  parentId: null,
7197
7197
  providerType: state.type,
7198
- ...includeSessionMetadata && { providerName: state.name },
7198
+ providerName: state.name,
7199
7199
  kind: "workspace",
7200
7200
  transport: "cdp-page",
7201
7201
  status: normalizeManagedStatus(activeChat?.status || state.status, {
7202
7202
  activeModal: activeChat?.activeModal || null
7203
7203
  }),
7204
7204
  title,
7205
- ...includeSessionMetadata && { workspace: state.workspace || null },
7205
+ workspace: state.workspace || null,
7206
7206
  activeChat,
7207
7207
  ...summaryMetadata && { summaryMetadata },
7208
7208
  ...includeSessionMetadata && { capabilities: state.sessionCapabilities || IDE_SESSION_CAPABILITIES },
@@ -7227,7 +7227,7 @@ function buildExtensionAgentSession(parent, ext, options) {
7227
7227
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
7228
7228
  parentId: parent.instanceId || parent.type,
7229
7229
  providerType: ext.type,
7230
- ...includeSessionMetadata && { providerName: ext.name },
7230
+ providerName: ext.name,
7231
7231
  providerSessionId: ext.providerSessionId,
7232
7232
  kind: "agent",
7233
7233
  transport: "cdp-webview",
@@ -7235,7 +7235,7 @@ function buildExtensionAgentSession(parent, ext, options) {
7235
7235
  activeModal: activeChat?.activeModal || null
7236
7236
  }),
7237
7237
  title: activeChat?.title || ext.name,
7238
- ...includeSessionMetadata && { workspace: parent.workspace || null },
7238
+ workspace: parent.workspace || null,
7239
7239
  activeChat,
7240
7240
  ...summaryMetadata && { summaryMetadata },
7241
7241
  ...includeSessionMetadata && { capabilities: ext.sessionCapabilities || EXTENSION_SESSION_CAPABILITIES },
@@ -7275,7 +7275,7 @@ function buildCliSession(state, options) {
7275
7275
  id: state.instanceId,
7276
7276
  parentId: null,
7277
7277
  providerType: state.type,
7278
- ...includeSessionMetadata && { providerName: state.name },
7278
+ providerName: state.name,
7279
7279
  providerSessionId: state.providerSessionId,
7280
7280
  kind: "agent",
7281
7281
  transport: "pty",
@@ -7283,7 +7283,7 @@ function buildCliSession(state, options) {
7283
7283
  activeModal: activeChat?.activeModal || null
7284
7284
  }),
7285
7285
  title: activeChat?.title || state.name,
7286
- ...includeSessionMetadata && { workspace: state.workspace || null },
7286
+ workspace: state.workspace || null,
7287
7287
  ...includeRuntimeMetadata && {
7288
7288
  runtimeKey: state.runtime?.runtimeKey,
7289
7289
  runtimeDisplayName: state.runtime?.displayName,
@@ -7322,14 +7322,14 @@ function buildAcpSession(state, options) {
7322
7322
  id: state.instanceId,
7323
7323
  parentId: null,
7324
7324
  providerType: state.type,
7325
- ...includeSessionMetadata && { providerName: state.name },
7325
+ providerName: state.name,
7326
7326
  kind: "agent",
7327
7327
  transport: "acp",
7328
7328
  status: normalizeManagedStatus(activeChat?.status || state.status, {
7329
7329
  activeModal: activeChat?.activeModal || null
7330
7330
  }),
7331
7331
  title: activeChat?.title || state.name,
7332
- ...includeSessionMetadata && { workspace: state.workspace || null },
7332
+ workspace: state.workspace || null,
7333
7333
  activeChat,
7334
7334
  ...summaryMetadata && { summaryMetadata },
7335
7335
  ...includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES },
@@ -7874,26 +7874,26 @@ function buildReadChatReplayCollapseSignature(message) {
7874
7874
  function shouldCollapseReadChatReplayDuplicate(message) {
7875
7875
  if (!message) return false;
7876
7876
  const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
7877
- if (role !== "assistant" && role !== "system") return false;
7878
- const kind = typeof message.kind === "string" ? message.kind.trim().toLowerCase() : "standard";
7879
- return kind === "tool" || kind === "terminal" || kind === "thought" || kind === "system";
7877
+ return role === "assistant" || role === "system";
7880
7878
  }
7881
7879
  function collapseReplayDuplicatesFromReadChat(messages) {
7882
7880
  const collapsed = [];
7883
- let lastReplayTurnSignature = "";
7881
+ const replaySignaturesInCurrentTurn = /* @__PURE__ */ new Set();
7884
7882
  for (const message of messages) {
7883
+ const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
7884
+ if (role === "user") {
7885
+ replaySignaturesInCurrentTurn.clear();
7886
+ }
7885
7887
  const signature = buildReadChatReplayCollapseSignature(message);
7886
7888
  const previous = collapsed[collapsed.length - 1];
7887
7889
  const previousSignature = buildReadChatReplayCollapseSignature(previous);
7888
7890
  if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
7889
7891
  if (previousSignature === signature) continue;
7890
- if (lastReplayTurnSignature === signature) continue;
7892
+ if (replaySignaturesInCurrentTurn.has(signature)) continue;
7891
7893
  }
7892
7894
  collapsed.push(message);
7893
7895
  if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
7894
- lastReplayTurnSignature = signature;
7895
- } else if ((message.role || "").toLowerCase() === "user") {
7896
- lastReplayTurnSignature = "";
7896
+ replaySignaturesInCurrentTurn.add(signature);
7897
7897
  }
7898
7898
  }
7899
7899
  return collapsed;
@@ -12536,6 +12536,7 @@ var init_provider_cli_runtime = __esm({
12536
12536
  var provider_cli_adapter_exports = {};
12537
12537
  __export(provider_cli_adapter_exports, {
12538
12538
  ProviderCliAdapter: () => ProviderCliAdapter,
12539
+ appendBoundedText: () => appendBoundedText,
12539
12540
  normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
12540
12541
  });
12541
12542
  function normalizeComparableTranscriptText(value) {
@@ -12568,6 +12569,14 @@ function parsedTranscriptIsRicherThanCommitted(parsedMessages, committedMessages
12568
12569
  }
12569
12570
  return false;
12570
12571
  }
12572
+ function appendBoundedText(current, chunk, maxChars) {
12573
+ if (!chunk) return current.length <= maxChars ? current : current.slice(-maxChars);
12574
+ if (maxChars <= 0) return "";
12575
+ if (chunk.length >= maxChars) return chunk.slice(-maxChars);
12576
+ const keepFromCurrent = maxChars - chunk.length;
12577
+ if (current.length <= keepFromCurrent) return current + chunk;
12578
+ return current.slice(-keepFromCurrent) + chunk;
12579
+ }
12571
12580
  var os13, ProviderCliAdapter;
12572
12581
  var init_provider_cli_adapter = __esm({
12573
12582
  "../../oss/packages/daemon-core/src/cli-adapters/provider-cli-adapter.ts"() {
@@ -12645,15 +12654,17 @@ var init_provider_cli_adapter = __esm({
12645
12654
  startupFirstOutputAt = 0;
12646
12655
  // PTY I/O
12647
12656
  onPtyDataCallback = null;
12648
- pendingOutputParseBuffer = "";
12657
+ pendingOutputParseChunks = [];
12649
12658
  pendingOutputParseTimer = null;
12650
- ptyOutputBuffer = "";
12659
+ ptyOutputChunks = [];
12651
12660
  ptyOutputFlushTimer = null;
12652
12661
  pendingTerminalQueryTail = "";
12653
12662
  lastOutputAt = 0;
12654
12663
  lastNonEmptyOutputAt = 0;
12655
12664
  lastScreenChangeAt = 0;
12656
12665
  lastScreenSnapshot = "";
12666
+ lastScreenText = "";
12667
+ lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
12657
12668
  // Server log forwarding
12658
12669
  serverConn = null;
12659
12670
  logBuffer = [];
@@ -12699,6 +12710,9 @@ var init_provider_cli_adapter = __esm({
12699
12710
  traceSeq = 0;
12700
12711
  traceSessionId = "";
12701
12712
  parsedStatusCache = null;
12713
+ lastStatusHotPathParseAt = Number.NEGATIVE_INFINITY;
12714
+ static STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS = 1e3;
12715
+ static SCREEN_SNAPSHOT_MIN_INTERVAL_MS = 250;
12702
12716
  static MAX_TRACE_ENTRIES = 250;
12703
12717
  providerResolutionMeta;
12704
12718
  static FINISH_RETRY_DELAY_MS = 300;
@@ -12707,6 +12721,30 @@ var init_provider_cli_adapter = __esm({
12707
12721
  this.messages = [...this.committedMessages];
12708
12722
  this.structuredMessages = [...this.committedMessages];
12709
12723
  }
12724
+ readTerminalScreenText(now = Date.now()) {
12725
+ const screenText = this.terminalScreen.getText() || "";
12726
+ this.lastScreenText = screenText;
12727
+ this.lastScreenSnapshotReadAt = now;
12728
+ return screenText;
12729
+ }
12730
+ shouldReadTerminalScreenSnapshot(now) {
12731
+ if (!this.lastScreenText) return true;
12732
+ return now - this.lastScreenSnapshotReadAt >= _ProviderCliAdapter.SCREEN_SNAPSHOT_MIN_INTERVAL_MS;
12733
+ }
12734
+ resetTerminalScreen(rows, cols) {
12735
+ this.terminalScreen.reset(rows, cols);
12736
+ this.lastScreenText = "";
12737
+ this.lastScreenSnapshot = "";
12738
+ this.lastScreenChangeAt = 0;
12739
+ this.lastScreenSnapshotReadAt = Number.NEGATIVE_INFINITY;
12740
+ }
12741
+ getFreshParsedStatusCache() {
12742
+ const cached2 = this.parsedStatusCache;
12743
+ if (cached2 && cached2.committedMessagesRef === this.committedMessages && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBuffer === this.accumulatedRawBuffer && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName && cached2.lastOutputAt === this.lastOutputAt) {
12744
+ return cached2.result;
12745
+ }
12746
+ return null;
12747
+ }
12710
12748
  getIdleFinishConfirmMs() {
12711
12749
  return this.timeouts.idleFinishConfirm;
12712
12750
  }
@@ -12821,9 +12859,9 @@ var init_provider_cli_adapter = __esm({
12821
12859
  clearTimeout(this.pendingOutputParseTimer);
12822
12860
  this.pendingOutputParseTimer = null;
12823
12861
  }
12824
- if (!this.pendingOutputParseBuffer) return;
12825
- const rawData = this.pendingOutputParseBuffer;
12826
- this.pendingOutputParseBuffer = "";
12862
+ if (this.pendingOutputParseChunks.length === 0) return;
12863
+ const rawData = this.pendingOutputParseChunks.join("");
12864
+ this.pendingOutputParseChunks = [];
12827
12865
  this.handleOutput(rawData);
12828
12866
  }
12829
12867
  async spawn() {
@@ -12878,7 +12916,7 @@ var init_provider_cli_adapter = __esm({
12878
12916
  terminalScreen: this.terminalScreen
12879
12917
  });
12880
12918
  }
12881
- this.pendingOutputParseBuffer += data;
12919
+ this.pendingOutputParseChunks.push(data);
12882
12920
  if (!this.pendingOutputParseTimer) {
12883
12921
  this.pendingOutputParseTimer = setTimeout(() => {
12884
12922
  this.pendingOutputParseTimer = null;
@@ -12886,13 +12924,13 @@ var init_provider_cli_adapter = __esm({
12886
12924
  }, this.timeouts.ptyFlush);
12887
12925
  }
12888
12926
  if (this.onPtyDataCallback) {
12889
- this.ptyOutputBuffer += data;
12927
+ this.ptyOutputChunks.push(data);
12890
12928
  if (!this.ptyOutputFlushTimer) {
12891
12929
  this.ptyOutputFlushTimer = setTimeout(() => {
12892
- if (this.ptyOutputBuffer && this.onPtyDataCallback) {
12893
- this.onPtyDataCallback(this.ptyOutputBuffer);
12930
+ if (this.ptyOutputChunks.length > 0 && this.onPtyDataCallback) {
12931
+ this.onPtyDataCallback(this.ptyOutputChunks.join(""));
12894
12932
  }
12895
- this.ptyOutputBuffer = "";
12933
+ this.ptyOutputChunks = [];
12896
12934
  this.ptyOutputFlushTimer = null;
12897
12935
  }, this.timeouts.ptyFlush);
12898
12936
  }
@@ -12917,7 +12955,7 @@ var init_provider_cli_adapter = __esm({
12917
12955
  clearTimeout(this.startupSettleTimer);
12918
12956
  this.startupSettleTimer = null;
12919
12957
  }
12920
- this.terminalScreen.reset(24, 80);
12958
+ this.resetTerminalScreen(24, 80);
12921
12959
  this.pendingTerminalQueryTail = "";
12922
12960
  this.currentTurnScope = null;
12923
12961
  this.finishRetryCount = 0;
@@ -12939,11 +12977,12 @@ var init_provider_cli_adapter = __esm({
12939
12977
  this.terminalScreen.write(rawData);
12940
12978
  const cleanData = sanitizeTerminalText(rawData);
12941
12979
  const now = Date.now();
12942
- const screenText = this.terminalScreen.getText();
12943
- const normalizedScreenSnapshot = normalizeScreenSnapshot(screenText);
12980
+ const shouldReadScreen = this.shouldReadTerminalScreenSnapshot(now);
12981
+ const screenText = shouldReadScreen ? this.readTerminalScreenText(now) : this.lastScreenText;
12982
+ const normalizedScreenSnapshot = shouldReadScreen ? normalizeScreenSnapshot(screenText) : this.lastScreenSnapshot;
12944
12983
  this.lastOutputAt = now;
12945
12984
  if (cleanData.trim()) this.lastNonEmptyOutputAt = now;
12946
- if (normalizedScreenSnapshot !== this.lastScreenSnapshot) {
12985
+ if (shouldReadScreen && normalizedScreenSnapshot !== this.lastScreenSnapshot) {
12947
12986
  this.lastScreenSnapshot = normalizedScreenSnapshot;
12948
12987
  this.lastScreenChangeAt = now;
12949
12988
  }
@@ -12965,7 +13004,7 @@ var init_provider_cli_adapter = __esm({
12965
13004
  this.scheduleStartupSettleCheck();
12966
13005
  }
12967
13006
  if (this.isWaitingForResponse && cleanData) {
12968
- this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
13007
+ this.responseBuffer = appendBoundedText(this.responseBuffer, cleanData, 8e3);
12969
13008
  }
12970
13009
  if (cleanData.trim()) {
12971
13010
  if (this.serverConn) {
@@ -12974,11 +13013,11 @@ var init_provider_cli_adapter = __esm({
12974
13013
  this.logBuffer.push({ message: cleanData.trim(), level: "info" });
12975
13014
  }
12976
13015
  }
12977
- this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
12978
13016
  const prevAccumulatedLen = this.accumulatedBuffer.length;
12979
13017
  const prevAccumulatedRawLen = this.accumulatedRawBuffer.length;
12980
- this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
12981
- this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
13018
+ this.recentOutputBuffer = appendBoundedText(this.recentOutputBuffer, cleanData, 1e3);
13019
+ this.accumulatedBuffer = appendBoundedText(this.accumulatedBuffer, cleanData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
13020
+ this.accumulatedRawBuffer = appendBoundedText(this.accumulatedRawBuffer, rawData, _ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
12982
13021
  if (this.currentTurnScope) {
12983
13022
  const droppedClean = prevAccumulatedLen + cleanData.length - this.accumulatedBuffer.length;
12984
13023
  const droppedRaw = prevAccumulatedRawLen + rawData.length - this.accumulatedRawBuffer.length;
@@ -12989,14 +13028,14 @@ var init_provider_cli_adapter = __esm({
12989
13028
  this.currentTurnScope.rawBufferStart = Math.max(0, this.currentTurnScope.rawBufferStart - droppedRaw);
12990
13029
  }
12991
13030
  }
12992
- this.resolveStartupState("output");
13031
+ this.resolveStartupState("output", screenText, normalizedScreenSnapshot, now);
12993
13032
  this.scheduleSettle();
12994
13033
  }
12995
- resolveStartupState(trigger) {
13034
+ resolveStartupState(trigger, screenTextOverride, normalizedScreenOverride, nowOverride) {
12996
13035
  if (!this.startupParseGate) return;
12997
- const now = Date.now();
12998
- const screenText = this.terminalScreen.getText() || "";
12999
- const normalizedScreen = normalizeScreenSnapshot(screenText);
13036
+ const now = typeof nowOverride === "number" ? nowOverride : Date.now();
13037
+ const screenText = typeof screenTextOverride === "string" ? screenTextOverride : this.readTerminalScreenText();
13038
+ const normalizedScreen = typeof normalizedScreenOverride === "string" ? normalizedScreenOverride : normalizeScreenSnapshot(screenText);
13000
13039
  const hasStartupOutput = !!this.startupFirstOutputAt || !!normalizedScreen.trim();
13001
13040
  if (!hasStartupOutput) return;
13002
13041
  const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
@@ -13231,40 +13270,28 @@ var init_provider_cli_adapter = __esm({
13231
13270
  }, delayTime);
13232
13271
  return;
13233
13272
  }
13234
- const tail = this.settledBuffer;
13235
- const screenText = this.terminalScreen.getText() || "";
13236
13273
  this.resolveStartupState("settled");
13237
- if (this.startupParseGate) {
13238
- return;
13239
- }
13240
- const parsedTranscript = this.parseCurrentTranscript(
13241
- this.committedMessages,
13242
- this.responseBuffer,
13243
- this.currentTurnScope
13244
- );
13245
- const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsedTranscript.activeModal : null;
13246
- const modal = this.runParseApproval(tail) || parsedModal;
13247
- const rawScriptStatus = this.runDetectStatus(tail);
13248
- const scriptStatus = parsedTranscript?.status === "waiting_approval" && modal ? "waiting_approval" : rawScriptStatus;
13249
- const parsedMessages = Array.isArray(parsedTranscript?.messages) ? normalizeCliParsedMessages(parsedTranscript.messages, {
13274
+ if (this.startupParseGate) return;
13275
+ const session = this.runParseSession();
13276
+ if (!session) return;
13277
+ const { status, messages, modal, parsedStatus } = session;
13278
+ const parsedMessages = normalizeCliParsedMessages(messages, {
13250
13279
  committedMessages: this.committedMessages,
13251
13280
  scope: this.currentTurnScope,
13252
13281
  lastOutputAt: this.lastOutputAt
13253
- }) : [];
13254
- if (this.maybeCommitVisibleIdleTranscript(parsedTranscript)) {
13255
- return;
13256
- }
13257
- const lastParsedAssistant = [...parsedMessages].reverse().find((message) => message.role === "assistant");
13258
- const parsedShowsLiveAssistantProgress = parsedTranscript?.status === "generating" && !!lastParsedAssistant && parsedMessages.length > this.committedMessages.length;
13282
+ });
13283
+ if (this.maybeCommitVisibleIdleTranscript(session, parsedMessages)) return;
13284
+ const lastParsedAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant");
13259
13285
  const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet || this.currentTurnScope?.prompt || "");
13286
+ const screenText = this.terminalScreen.getText() || "";
13260
13287
  this.recordTrace("settled", {
13261
- tail: summarizeCliTraceText(tail, 500),
13288
+ tail: summarizeCliTraceText(this.settledBuffer, 500),
13262
13289
  screenText: summarizeCliTraceText(screenText, 1200),
13263
- detectStatus: scriptStatus,
13264
- parsedStatus: parsedTranscript?.status || null,
13290
+ detectStatus: status,
13291
+ parsedStatus: parsedStatus || null,
13265
13292
  parsedMessageCount: parsedMessages.length,
13266
13293
  parsedLastAssistant: lastParsedAssistant ? summarizeCliTraceText(lastParsedAssistant.content, 280) : "",
13267
- parsedActiveModal: parsedTranscript?.activeModal ?? null,
13294
+ parsedActiveModal: modal,
13268
13295
  approval: modal,
13269
13296
  ...buildCliTraceParseSnapshot({
13270
13297
  accumulatedBuffer: this.accumulatedBuffer,
@@ -13298,36 +13325,36 @@ var init_provider_cli_adapter = __esm({
13298
13325
  `[${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 || "-"}`
13299
13326
  );
13300
13327
  }
13301
- if (!scriptStatus) return;
13328
+ if (!status) return;
13302
13329
  const prevStatus = this.currentStatus;
13303
- const ctx = { now, screenText, modal, scriptStatus, parsedTranscript, parsedMessages, lastParsedAssistant, parsedShowsLiveAssistantProgress, prevStatus };
13330
+ const ctx = { now, modal, status, parsedMessages, lastParsedAssistant, parsedStatus: parsedStatus || null, prevStatus };
13304
13331
  if (!this.applyPendingScriptStatusDebounce(ctx)) return;
13305
13332
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
13306
13333
  LOG.info(
13307
13334
  "CLI",
13308
- `[${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)}`
13335
+ `[${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)}`
13309
13336
  );
13310
- const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedTranscript?.status === "idle" && !!lastParsedAssistant);
13337
+ const shouldHoldGenerating = status === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity && !(parsedStatus === "idle" && !!lastParsedAssistant);
13311
13338
  if (shouldHoldGenerating) {
13312
13339
  this.applyHoldGenerating(ctx, recentInteractiveActivity);
13313
13340
  return;
13314
13341
  }
13315
- if (scriptStatus === "waiting_approval") {
13342
+ if (status === "waiting_approval") {
13316
13343
  this.applyWaitingApproval(ctx);
13317
13344
  return;
13318
13345
  }
13319
- if (scriptStatus === "generating") {
13346
+ if (status === "generating") {
13320
13347
  this.applyGenerating(ctx);
13321
13348
  return;
13322
13349
  }
13323
- if (scriptStatus === "idle") {
13350
+ if (status === "idle") {
13324
13351
  this.applyIdle(ctx, now);
13325
13352
  }
13326
13353
  }
13327
13354
  // Returns false if the caller should bail out (debounce pending).
13328
13355
  applyPendingScriptStatusDebounce(ctx) {
13329
- const { now, scriptStatus, prevStatus } = ctx;
13330
- const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (scriptStatus === "generating" || scriptStatus === "waiting_approval");
13356
+ const { now, status, prevStatus } = ctx;
13357
+ const shouldDebounce = prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
13331
13358
  if (!shouldDebounce) {
13332
13359
  this.pendingScriptStatus = null;
13333
13360
  this.pendingScriptStatusSince = 0;
@@ -13345,8 +13372,8 @@ var init_provider_cli_adapter = __esm({
13345
13372
  this.evaluateSettled();
13346
13373
  }, delayMs);
13347
13374
  };
13348
- if (this.pendingScriptStatus !== scriptStatus) {
13349
- this.pendingScriptStatus = scriptStatus;
13375
+ if (this.pendingScriptStatus !== status) {
13376
+ this.pendingScriptStatus = status;
13350
13377
  this.pendingScriptStatusSince = now;
13351
13378
  armPending(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
13352
13379
  return false;
@@ -13359,7 +13386,7 @@ var init_provider_cli_adapter = __esm({
13359
13386
  return true;
13360
13387
  }
13361
13388
  applyHoldGenerating(ctx, recentInteractiveActivity) {
13362
- const { scriptStatus } = ctx;
13389
+ const { status } = ctx;
13363
13390
  this.clearIdleFinishCandidate("hold_generating_recent_activity");
13364
13391
  this.setStatus("generating", "recent_activity_hold");
13365
13392
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
@@ -13370,7 +13397,7 @@ var init_provider_cli_adapter = __esm({
13370
13397
  }
13371
13398
  }, this.timeouts.generatingIdle);
13372
13399
  this.recordTrace("hold_generating_recent_activity", {
13373
- scriptStatus,
13400
+ scriptStatus: status,
13374
13401
  recentInteractiveActivity,
13375
13402
  lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
13376
13403
  lastScreenChangeAt: this.lastScreenChangeAt,
@@ -13424,11 +13451,13 @@ var init_provider_cli_adapter = __esm({
13424
13451
  }
13425
13452
  }
13426
13453
  applyGenerating(ctx) {
13427
- const { screenText, modal, parsedShowsLiveAssistantProgress, prevStatus } = ctx;
13454
+ const { modal, parsedMessages, lastParsedAssistant, parsedStatus, prevStatus } = ctx;
13428
13455
  this.clearIdleFinishCandidate("generating");
13456
+ const screenText = this.terminalScreen.getText() || "";
13429
13457
  const effectiveScreenText = screenText || this.accumulatedBuffer;
13430
13458
  const noActiveTurn = !this.currentTurnScope;
13431
13459
  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));
13460
+ const parsedShowsLiveAssistantProgress = parsedStatus === "generating" && !!lastParsedAssistant && parsedMessages.length > this.committedMessages.length;
13432
13461
  if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome && !parsedShowsLiveAssistantProgress) {
13433
13462
  return;
13434
13463
  }
@@ -13455,7 +13484,7 @@ var init_provider_cli_adapter = __esm({
13455
13484
  this.onStatusChange?.();
13456
13485
  }
13457
13486
  applyIdle(ctx, now) {
13458
- const { screenText, modal, lastParsedAssistant, prevStatus } = ctx;
13487
+ const { modal, lastParsedAssistant, prevStatus } = ctx;
13459
13488
  if (prevStatus === "waiting_approval") {
13460
13489
  if (this.approvalExitTimeout) {
13461
13490
  clearTimeout(this.approvalExitTimeout);
@@ -13577,20 +13606,15 @@ var init_provider_cli_adapter = __esm({
13577
13606
  this.setStatus("idle", "response_finished");
13578
13607
  this.onStatusChange?.();
13579
13608
  }
13580
- maybeCommitVisibleIdleTranscript(parsed) {
13609
+ maybeCommitVisibleIdleTranscript(session, parsedMessages) {
13581
13610
  const allowImmediateScriptIdleCommit = this.provider.allowInputDuringGeneration === true;
13582
13611
  if (!allowImmediateScriptIdleCommit) return false;
13583
- if (!parsed || !Array.isArray(parsed.messages) || parsed.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || parsed.activeModal) {
13612
+ if (!session || session.status !== "idle" || !this.isWaitingForResponse || !this.currentTurnScope || this.activeModal || session.modal) {
13584
13613
  return false;
13585
13614
  }
13586
- const hydratedForIdleCommit = normalizeCliParsedMessages(parsed.messages, {
13587
- committedMessages: this.committedMessages,
13588
- scope: this.currentTurnScope,
13589
- lastOutputAt: this.lastOutputAt
13590
- });
13591
- const visibleAssistant = [...hydratedForIdleCommit].reverse().find((message) => message.role === "assistant" && message.content.trim());
13615
+ const visibleAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant" && m.content.trim());
13592
13616
  if (!visibleAssistant) return false;
13593
- this.committedMessages = hydratedForIdleCommit;
13617
+ this.committedMessages = parsedMessages;
13594
13618
  this.trimLastAssistantEcho(this.committedMessages, this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages));
13595
13619
  this.clearAllTimers();
13596
13620
  this.syncMessageViews();
@@ -13668,6 +13692,59 @@ var init_provider_cli_adapter = __esm({
13668
13692
  };
13669
13693
  }
13670
13694
  // ─── Script Execution ──────────────────────────
13695
+ runParseSession() {
13696
+ if (typeof this.cliScripts?.parseSession === "function") {
13697
+ try {
13698
+ const screenText = this.terminalScreen.getText();
13699
+ const tail = this.recentOutputBuffer.slice(-500);
13700
+ const input = buildCliParseInput({
13701
+ accumulatedBuffer: this.accumulatedBuffer,
13702
+ accumulatedRawBuffer: this.accumulatedRawBuffer,
13703
+ recentOutputBuffer: this.recentOutputBuffer,
13704
+ terminalScreenText: screenText,
13705
+ baseMessages: this.committedMessages,
13706
+ partialResponse: this.responseBuffer,
13707
+ isWaitingForResponse: this.isWaitingForResponse,
13708
+ scope: this.currentTurnScope,
13709
+ runtimeSettings: this.runtimeSettings
13710
+ });
13711
+ const session = this.cliScripts.parseSession({ ...input, tail, tailScreen: buildCliScreenSnapshot(tail) });
13712
+ this.parseErrorMessage = null;
13713
+ return session && typeof session === "object" ? session : null;
13714
+ } catch (e) {
13715
+ const message = e?.message || String(e);
13716
+ this.parseErrorMessage = message;
13717
+ LOG.warn("CLI", `[${this.cliType}] parseSession error: ${message}`);
13718
+ return null;
13719
+ }
13720
+ }
13721
+ if (!this.cliScripts?.detectStatus && !this.cliScripts?.parseOutput) return null;
13722
+ try {
13723
+ const tail = this.settledBuffer;
13724
+ const parsedTranscript = this.parseCurrentTranscript(
13725
+ this.committedMessages,
13726
+ this.responseBuffer,
13727
+ this.currentTurnScope
13728
+ );
13729
+ const parsedModal = parsedTranscript?.activeModal && Array.isArray(parsedTranscript.activeModal.buttons) && parsedTranscript.activeModal.buttons.some((b) => typeof b === "string" && b.trim()) ? parsedTranscript.activeModal : null;
13730
+ const approval = this.runParseApproval(tail);
13731
+ const modal = approval || parsedModal;
13732
+ const rawStatus = this.runDetectStatus(tail);
13733
+ const parsedStatus = typeof parsedTranscript?.status === "string" ? parsedTranscript.status : null;
13734
+ const effectiveStatus = parsedStatus === "waiting_approval" && modal ? "waiting_approval" : rawStatus || parsedStatus || "idle";
13735
+ return {
13736
+ status: effectiveStatus,
13737
+ messages: Array.isArray(parsedTranscript?.messages) ? parsedTranscript.messages : [],
13738
+ modal,
13739
+ parsedStatus
13740
+ };
13741
+ } catch (e) {
13742
+ const message = e?.message || String(e);
13743
+ this.parseErrorMessage = message;
13744
+ LOG.warn("CLI", `[${this.cliType}] parseSession fallback error: ${message}`);
13745
+ return null;
13746
+ }
13747
+ }
13671
13748
  runDetectStatus(text) {
13672
13749
  if (!this.cliScripts?.detectStatus) return null;
13673
13750
  try {
@@ -13741,14 +13818,21 @@ var init_provider_cli_adapter = __esm({
13741
13818
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
13742
13819
  let effectiveModal = startupModal || this.activeModal;
13743
13820
  if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
13744
- try {
13745
- const parsed = this.getScriptParsedStatus();
13746
- const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons) && parsed.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsed.activeModal : null;
13747
- if (parsed?.status === "waiting_approval" && parsedModal) {
13748
- effectiveStatus = "waiting_approval";
13749
- effectiveModal = parsedModal;
13821
+ let parsed = this.getFreshParsedStatusCache();
13822
+ if (!parsed && effectiveStatus !== "idle") {
13823
+ const now = Date.now();
13824
+ if (now - this.lastStatusHotPathParseAt >= _ProviderCliAdapter.STATUS_HOT_PATH_PARSE_MIN_INTERVAL_MS) {
13825
+ this.lastStatusHotPathParseAt = now;
13826
+ try {
13827
+ parsed = this.getScriptParsedStatus();
13828
+ } catch {
13829
+ }
13750
13830
  }
13751
- } catch {
13831
+ }
13832
+ const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons) && parsed.activeModal.buttons.some((button) => typeof button === "string" && button.trim()) ? parsed.activeModal : null;
13833
+ if (parsed?.status === "waiting_approval" && parsedModal) {
13834
+ effectiveStatus = "waiting_approval";
13835
+ effectiveModal = parsedModal;
13752
13836
  }
13753
13837
  }
13754
13838
  return {
@@ -13780,7 +13864,7 @@ var init_provider_cli_adapter = __esm({
13780
13864
  * Called by command handler / dashboard for rich content rendering.
13781
13865
  */
13782
13866
  getScriptParsedStatus() {
13783
- const screenText = this.terminalScreen.getText();
13867
+ const screenText = this.readTerminalScreenText();
13784
13868
  const cached2 = this.parsedStatusCache;
13785
13869
  if (cached2 && cached2.committedMessagesRef === this.committedMessages && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBuffer === this.accumulatedRawBuffer && cached2.screenText === screenText && cached2.currentStatus === this.currentStatus && cached2.activeModal === this.activeModal && cached2.cliName === this.cliName && cached2.lastOutputAt === this.lastOutputAt) {
13786
13870
  return cached2.result;
@@ -13800,8 +13884,21 @@ var init_provider_cli_adapter = __esm({
13800
13884
  this.onStatusChange?.();
13801
13885
  }
13802
13886
  }
13803
- if (this.maybeCommitVisibleIdleTranscript(parsed)) {
13804
- return this.getScriptParsedStatus();
13887
+ if (parsed && Array.isArray(parsed.messages)) {
13888
+ const hydratedForCommit = normalizeCliParsedMessages(parsed.messages, {
13889
+ committedMessages: this.committedMessages,
13890
+ scope: this.currentTurnScope,
13891
+ lastOutputAt: this.lastOutputAt
13892
+ });
13893
+ const fakeSession = {
13894
+ status: parsed.status || "idle",
13895
+ messages: parsed.messages,
13896
+ modal: parsedModal,
13897
+ parsedStatus: parsed.status || null
13898
+ };
13899
+ if (this.maybeCommitVisibleIdleTranscript(fakeSession, hydratedForCommit)) {
13900
+ return this.getScriptParsedStatus();
13901
+ }
13805
13902
  }
13806
13903
  const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
13807
13904
  let result;
@@ -13983,6 +14080,17 @@ var init_provider_cli_adapter = __esm({
13983
14080
  }
13984
14081
  await this.sendMessage(promptText);
13985
14082
  }
14083
+ isSubmitStuck(normalizedPromptSnippet) {
14084
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return false;
14085
+ if (this.hasActionableApproval()) return false;
14086
+ if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return false;
14087
+ const screenText = this.terminalScreen.getText();
14088
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return false;
14089
+ const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
14090
+ if (liveApproval) return false;
14091
+ const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
14092
+ return liveStatus !== "generating" && liveStatus !== "waiting_approval";
14093
+ }
13986
14094
  async writeToPty(data) {
13987
14095
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
13988
14096
  await this.ptyProcess.write(data);
@@ -14006,6 +14114,121 @@ var init_provider_cli_adapter = __esm({
14006
14114
  this.finishRetryTimer = null;
14007
14115
  }
14008
14116
  }
14117
+ commitSendUserTurn(state) {
14118
+ if (state.didCommitUserTurn) return;
14119
+ state.didCommitUserTurn = true;
14120
+ this.committedMessages.push({ role: "user", content: state.text, timestamp: Date.now() });
14121
+ this.syncMessageViews();
14122
+ }
14123
+ armResponseTimeout() {
14124
+ if (this.responseTimeout) clearTimeout(this.responseTimeout);
14125
+ this.responseTimeout = setTimeout(() => {
14126
+ if (this.isWaitingForResponse) this.finishResponse();
14127
+ }, this.timeouts.maxResponse);
14128
+ }
14129
+ writeSubmitKeyForRetry(mode) {
14130
+ void this.writeToPty(this.sendKey).catch((error48) => {
14131
+ LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error48?.message || error48}`);
14132
+ });
14133
+ }
14134
+ retrySubmitIfStuck(state, attempt) {
14135
+ this.submitRetryTimer = null;
14136
+ if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
14137
+ const screenText = this.terminalScreen.getText();
14138
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
14139
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
14140
+ this.recordTrace("submit_write", {
14141
+ mode: "submit_retry",
14142
+ attempt,
14143
+ sendKey: this.sendKey,
14144
+ screenText: summarizeCliTraceText(screenText, 500)
14145
+ });
14146
+ this.writeSubmitKeyForRetry("submit_retry");
14147
+ if (attempt >= 3) {
14148
+ this.submitRetryUsed = true;
14149
+ return;
14150
+ }
14151
+ this.submitRetryTimer = setTimeout(() => this.retrySubmitIfStuck(state, attempt + 1), state.retryDelayMs);
14152
+ }
14153
+ retryImmediateSubmitIfStuck(state) {
14154
+ this.submitRetryTimer = null;
14155
+ if (!this.isSubmitStuck(state.normalizedPromptSnippet)) return;
14156
+ const screenText = this.terminalScreen.getText();
14157
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
14158
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
14159
+ this.recordTrace("submit_write", {
14160
+ mode: "immediate_retry",
14161
+ attempt: 1,
14162
+ sendKey: this.sendKey,
14163
+ screenText: summarizeCliTraceText(screenText, 500)
14164
+ });
14165
+ this.writeSubmitKeyForRetry("immediate_retry");
14166
+ this.submitRetryUsed = true;
14167
+ }
14168
+ submitSendKey(state, completion) {
14169
+ if (!this.ptyProcess) {
14170
+ completion.resolveOnce();
14171
+ return;
14172
+ }
14173
+ this.submitPendingUntil = 0;
14174
+ const screenText = this.terminalScreen.getText();
14175
+ this.recordTrace("submit_write", {
14176
+ mode: "submit_key",
14177
+ sendKey: this.sendKey,
14178
+ screenText: summarizeCliTraceText(screenText, 500)
14179
+ });
14180
+ void this.writeToPty(this.sendKey).then(() => {
14181
+ this.commitSendUserTurn(state);
14182
+ this.submitRetryTimer = setTimeout(() => this.retrySubmitIfStuck(state, 1), state.retryDelayMs);
14183
+ this.armResponseTimeout();
14184
+ completion.resolveOnce();
14185
+ }, completion.rejectOnce);
14186
+ }
14187
+ submitImmediatePrompt(state, completion) {
14188
+ this.submitPendingUntil = 0;
14189
+ this.recordTrace("submit_write", {
14190
+ mode: "immediate",
14191
+ text: summarizeCliTraceText(state.text, 500),
14192
+ sendKey: this.sendKey,
14193
+ screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
14194
+ });
14195
+ void this.writeToPty(state.text + this.sendKey).then(() => {
14196
+ this.commitSendUserTurn(state);
14197
+ this.submitRetryTimer = setTimeout(() => this.retryImmediateSubmitIfStuck(state), state.retryDelayMs);
14198
+ this.armResponseTimeout();
14199
+ completion.resolveOnce();
14200
+ }, completion.rejectOnce);
14201
+ }
14202
+ waitForEchoAndSubmit(state, completion, submitStartedAt, lastNormalizedScreen = "", lastScreenChangeAt = submitStartedAt) {
14203
+ if (!this.ptyProcess) {
14204
+ completion.resolveOnce();
14205
+ return;
14206
+ }
14207
+ const now = Date.now();
14208
+ const elapsed = now - submitStartedAt;
14209
+ const screenText = this.terminalScreen.getText();
14210
+ const normalizedScreen = normalizePromptText(screenText);
14211
+ const nextScreenChangeAt = normalizedScreen !== lastNormalizedScreen ? now : lastScreenChangeAt;
14212
+ const echoVisible = !state.normalizedPromptSnippet || promptLikelyVisible(screenText, state.normalizedPromptSnippet);
14213
+ if (echoVisible) {
14214
+ const screenSettled = now - nextScreenChangeAt >= 500;
14215
+ if (elapsed >= state.submitDelayMs && screenSettled) {
14216
+ this.submitSendKey(state, completion);
14217
+ return;
14218
+ }
14219
+ }
14220
+ if (elapsed >= state.maxEchoWaitMs) {
14221
+ this.submitSendKey(state, completion);
14222
+ return;
14223
+ }
14224
+ setTimeout(() => this.waitForEchoAndSubmit(
14225
+ state,
14226
+ completion,
14227
+ submitStartedAt,
14228
+ normalizedScreen,
14229
+ nextScreenChangeAt
14230
+ ), 50);
14231
+ }
14009
14232
  async sendMessage(text) {
14010
14233
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
14011
14234
  const allowInputDuringGeneration = this.provider.allowInputDuringGeneration === true;
@@ -14081,12 +14304,13 @@ var init_provider_cli_adapter = __esm({
14081
14304
  const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
14082
14305
  const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
14083
14306
  const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
14084
- let didCommitUserTurn = false;
14085
- const commitUserTurn = () => {
14086
- if (didCommitUserTurn) return;
14087
- didCommitUserTurn = true;
14088
- this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
14089
- this.syncMessageViews();
14307
+ const sendState = {
14308
+ text,
14309
+ normalizedPromptSnippet,
14310
+ submitDelayMs,
14311
+ maxEchoWaitMs,
14312
+ retryDelayMs,
14313
+ didCommitUserTurn: false
14090
14314
  };
14091
14315
  if (this.settleTimer) {
14092
14316
  clearTimeout(this.settleTimer);
@@ -14094,110 +14318,23 @@ var init_provider_cli_adapter = __esm({
14094
14318
  }
14095
14319
  this.responseEpoch += 1;
14096
14320
  this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
14097
- const startResponseTimeout = () => {
14098
- if (this.responseTimeout) clearTimeout(this.responseTimeout);
14099
- this.responseTimeout = setTimeout(() => {
14100
- if (this.isWaitingForResponse) this.finishResponse();
14101
- }, this.timeouts.maxResponse);
14102
- };
14103
14321
  await new Promise((resolve18, reject) => {
14104
14322
  let resolved = false;
14105
- const resolveOnce = () => {
14106
- if (resolved) return;
14107
- resolved = true;
14108
- resolve18();
14109
- };
14110
- const rejectOnce = (error48) => {
14111
- if (resolved) return;
14112
- this.resetPendingSendState("send_write_failed");
14113
- resolved = true;
14114
- reject(error48);
14115
- };
14116
- const writeRetryKey = (mode) => {
14117
- void this.writeToPty(this.sendKey).catch((error48) => {
14118
- LOG.warn("CLI", `[${this.cliType}] ${mode} write failed: ${error48?.message || error48}`);
14119
- });
14120
- };
14121
- const submit = () => {
14122
- if (!this.ptyProcess) {
14123
- resolveOnce();
14124
- return;
14323
+ const completion = {
14324
+ resolveOnce: () => {
14325
+ if (resolved) return;
14326
+ resolved = true;
14327
+ resolve18();
14328
+ },
14329
+ rejectOnce: (error48) => {
14330
+ if (resolved) return;
14331
+ this.resetPendingSendState("send_write_failed");
14332
+ resolved = true;
14333
+ reject(error48);
14125
14334
  }
14126
- this.submitPendingUntil = 0;
14127
- const screenText = this.terminalScreen.getText();
14128
- this.recordTrace("submit_write", {
14129
- mode: "submit_key",
14130
- sendKey: this.sendKey,
14131
- screenText: summarizeCliTraceText(screenText, 500)
14132
- });
14133
- const retrySubmitIfStuck = (attempt) => {
14134
- this.submitRetryTimer = null;
14135
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
14136
- if (this.hasActionableApproval()) return;
14137
- if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
14138
- const screenText2 = this.terminalScreen.getText();
14139
- if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
14140
- const liveApproval = this.runParseApproval(screenText2) || this.runParseApproval(this.recentOutputBuffer);
14141
- if (liveApproval) return;
14142
- const liveStatus = this.runDetectStatus(screenText2) || this.runDetectStatus(this.recentOutputBuffer);
14143
- if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
14144
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
14145
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
14146
- this.recordTrace("submit_write", {
14147
- mode: "submit_retry",
14148
- attempt,
14149
- sendKey: this.sendKey,
14150
- screenText: summarizeCliTraceText(screenText2, 500)
14151
- });
14152
- writeRetryKey("submit_retry");
14153
- if (attempt >= 3) {
14154
- this.submitRetryUsed = true;
14155
- return;
14156
- }
14157
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
14158
- };
14159
- void this.writeToPty(this.sendKey).then(() => {
14160
- commitUserTurn();
14161
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
14162
- startResponseTimeout();
14163
- resolveOnce();
14164
- }, rejectOnce);
14165
14335
  };
14166
14336
  if (this.submitStrategy === "immediate") {
14167
- this.submitPendingUntil = 0;
14168
- this.recordTrace("submit_write", {
14169
- mode: "immediate",
14170
- text: summarizeCliTraceText(text, 500),
14171
- sendKey: this.sendKey,
14172
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
14173
- });
14174
- void this.writeToPty(text + this.sendKey).then(() => {
14175
- commitUserTurn();
14176
- this.submitRetryTimer = setTimeout(() => {
14177
- this.submitRetryTimer = null;
14178
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
14179
- if (this.hasActionableApproval()) return;
14180
- if (this.hasMeaningfulResponseBuffer(normalizedPromptSnippet)) return;
14181
- const screenText = this.terminalScreen.getText();
14182
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
14183
- const liveApproval = this.runParseApproval(screenText) || this.runParseApproval(this.recentOutputBuffer);
14184
- if (liveApproval) return;
14185
- const liveStatus = this.runDetectStatus(screenText) || this.runDetectStatus(this.recentOutputBuffer);
14186
- if (liveStatus === "generating" || liveStatus === "waiting_approval") return;
14187
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
14188
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
14189
- this.recordTrace("submit_write", {
14190
- mode: "immediate_retry",
14191
- attempt: 1,
14192
- sendKey: this.sendKey,
14193
- screenText: summarizeCliTraceText(screenText, 500)
14194
- });
14195
- writeRetryKey("immediate_retry");
14196
- this.submitRetryUsed = true;
14197
- }, retryDelayMs);
14198
- startResponseTimeout();
14199
- resolveOnce();
14200
- }, rejectOnce);
14337
+ this.submitImmediatePrompt(sendState, completion);
14201
14338
  return;
14202
14339
  }
14203
14340
  if (submitDelayMs > 0) {
@@ -14210,36 +14347,10 @@ var init_provider_cli_adapter = __esm({
14210
14347
  screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
14211
14348
  });
14212
14349
  const submitStartedAt = Date.now();
14213
- let lastNormalizedScreen = "";
14214
- let lastScreenChangeAt = submitStartedAt;
14215
- const waitForEchoAndSubmit = () => {
14216
- if (!this.ptyProcess) {
14217
- resolveOnce();
14218
- return;
14219
- }
14220
- const now = Date.now();
14221
- const elapsed = now - submitStartedAt;
14222
- const screenText = this.terminalScreen.getText();
14223
- const normalizedScreen = normalizePromptText(screenText);
14224
- if (normalizedScreen !== lastNormalizedScreen) {
14225
- lastNormalizedScreen = normalizedScreen;
14226
- lastScreenChangeAt = now;
14227
- }
14228
- const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
14229
- if (echoVisible) {
14230
- const screenSettled = now - lastScreenChangeAt >= 500;
14231
- if (elapsed >= submitDelayMs && screenSettled) {
14232
- submit();
14233
- return;
14234
- }
14235
- }
14236
- if (elapsed >= maxEchoWaitMs) {
14237
- submit();
14238
- return;
14239
- }
14240
- setTimeout(waitForEchoAndSubmit, 50);
14241
- };
14242
- void this.writeToPty(text).then(() => waitForEchoAndSubmit(), rejectOnce);
14350
+ void this.writeToPty(text).then(
14351
+ () => this.waitForEchoAndSubmit(sendState, completion, submitStartedAt),
14352
+ completion.rejectOnce
14353
+ );
14243
14354
  });
14244
14355
  }
14245
14356
  getPartialResponse() {
@@ -14316,9 +14427,9 @@ var init_provider_cli_adapter = __esm({
14316
14427
  shutdown() {
14317
14428
  this.clearIdleFinishCandidate("shutdown");
14318
14429
  this.clearAllTimers();
14319
- this.pendingOutputParseBuffer = "";
14430
+ this.pendingOutputParseChunks = [];
14320
14431
  this.pendingTerminalQueryTail = "";
14321
- this.ptyOutputBuffer = "";
14432
+ this.ptyOutputChunks = [];
14322
14433
  this.finishRetryCount = 0;
14323
14434
  if (this.ptyProcess) {
14324
14435
  this.ptyProcess.write("");
@@ -14339,9 +14450,9 @@ var init_provider_cli_adapter = __esm({
14339
14450
  detach() {
14340
14451
  this.clearIdleFinishCandidate("detach");
14341
14452
  this.clearAllTimers();
14342
- this.pendingOutputParseBuffer = "";
14453
+ this.pendingOutputParseChunks = [];
14343
14454
  this.pendingTerminalQueryTail = "";
14344
- this.ptyOutputBuffer = "";
14455
+ this.ptyOutputChunks = [];
14345
14456
  this.finishRetryCount = 0;
14346
14457
  if (this.ptyProcess) {
14347
14458
  try {
@@ -14372,19 +14483,19 @@ var init_provider_cli_adapter = __esm({
14372
14483
  clearTimeout(this.pendingOutputParseTimer);
14373
14484
  this.pendingOutputParseTimer = null;
14374
14485
  }
14375
- this.pendingOutputParseBuffer = "";
14486
+ this.pendingOutputParseChunks = [];
14376
14487
  this.pendingTerminalQueryTail = "";
14377
14488
  if (this.ptyOutputFlushTimer) {
14378
14489
  clearTimeout(this.ptyOutputFlushTimer);
14379
14490
  this.ptyOutputFlushTimer = null;
14380
14491
  }
14381
- this.ptyOutputBuffer = "";
14492
+ this.ptyOutputChunks = [];
14382
14493
  if (this.finishRetryTimer) {
14383
14494
  clearTimeout(this.finishRetryTimer);
14384
14495
  this.finishRetryTimer = null;
14385
14496
  }
14386
14497
  this.finishRetryCount = 0;
14387
- this.terminalScreen.reset();
14498
+ this.resetTerminalScreen();
14388
14499
  this.ptyProcess?.clearBuffer?.();
14389
14500
  this.onStatusChange?.();
14390
14501
  }
@@ -14501,7 +14612,7 @@ var init_provider_cli_adapter = __esm({
14501
14612
  traceEntryCount: this.traceEntries.length,
14502
14613
  statusHistory: this.statusHistory.slice(-30),
14503
14614
  timeouts: this.timeouts,
14504
- pendingOutputParseBufferLength: this.pendingOutputParseBuffer.length,
14615
+ pendingOutputParseBufferLength: this.pendingOutputParseChunks.reduce((total, chunk) => total + chunk.length, 0),
14505
14616
  pendingOutputParseScheduled: !!this.pendingOutputParseTimer,
14506
14617
  ptyAlive: !!this.ptyProcess
14507
14618
  };
@@ -37651,6 +37762,18 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
37651
37762
  const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
37652
37763
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
37653
37764
  }
37765
+ function projectLiveSessionFromFull(session) {
37766
+ const {
37767
+ capabilities: _capabilities,
37768
+ controlValues: _controlValues,
37769
+ providerControls: _providerControls,
37770
+ ...rest
37771
+ } = session;
37772
+ return {
37773
+ ...rest,
37774
+ activeChat: normalizeActiveChatData(session.activeChat, LIVE_STATUS_ACTIVE_CHAT_OPTIONS)
37775
+ };
37776
+ }
37654
37777
  function buildRecentLaunches(recentActivity) {
37655
37778
  return recentActivity.map((item) => ({
37656
37779
  id: item.id,
@@ -37675,7 +37798,7 @@ function buildStatusSnapshot(options) {
37675
37798
  options.cdpManagers,
37676
37799
  { profile: "full" }
37677
37800
  );
37678
- const sessions = profile === "full" ? unreadSourceSessions : buildSessionEntries(
37801
+ const sessions = profile === "full" ? unreadSourceSessions : profile === "live" ? unreadSourceSessions.map(projectLiveSessionFromFull) : buildSessionEntries(
37679
37802
  options.allStates,
37680
37803
  options.cdpManagers,
37681
37804
  { profile }
@@ -37779,6 +37902,7 @@ var init_snapshot = __esm({
37779
37902
  init_terminal_screen();
37780
37903
  init_logger();
37781
37904
  init_builders();
37905
+ init_normalize();
37782
37906
  init_recent_activity();
37783
37907
  READ_DEBUG_ENABLED = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
37784
37908
  recentReadDebugSignatureBySession = /* @__PURE__ */ new Map();
@@ -38844,7 +38968,6 @@ var init_reporter = __esm({
38844
38968
  "use strict";
38845
38969
  init_logger();
38846
38970
  init_runtime_defaults();
38847
- init_builders();
38848
38971
  init_snapshot();
38849
38972
  DaemonStatusReporter = class {
38850
38973
  deps;
@@ -39018,10 +39141,6 @@ var init_reporter = __esm({
39018
39141
  LOG.info("StatusReport", `\u2192${target} ${baseSummary}`);
39019
39142
  }
39020
39143
  }
39021
- const sessions = buildSessionEntries(
39022
- allStates,
39023
- this.deps.cdpManagers
39024
- );
39025
39144
  const payload = {
39026
39145
  ...buildStatusSnapshot({
39027
39146
  allStates,
@@ -39041,9 +39160,9 @@ var init_reporter = __esm({
39041
39160
  }),
39042
39161
  screenshotUsage: this.deps.getScreenshotUsage?.() || null
39043
39162
  };
39044
- const payloadBytes = JSON.stringify(payload).length;
39045
39163
  const p2pSent = this.sendP2PPayload(payload);
39046
39164
  if (p2pSent) {
39165
+ const payloadBytes = JSON.stringify(payload).length;
39047
39166
  LOG.debug("P2P", `sent (${payloadBytes} bytes)`);
39048
39167
  if (payloadBytes > 256 * 1024) {
39049
39168
  LOG.warn(
@@ -39053,8 +39172,10 @@ var init_reporter = __esm({
39053
39172
  }
39054
39173
  }
39055
39174
  if (opts?.p2pOnly) return;
39175
+ if (!serverConnected || !serverConn) return;
39176
+ const payloadSessions = Array.isArray(payload.sessions) ? payload.sessions : [];
39056
39177
  const wsPayload = {
39057
- sessions: sessions.map((session) => ({
39178
+ sessions: payloadSessions.map((session) => ({
39058
39179
  id: session.id,
39059
39180
  parentId: session.parentId,
39060
39181
  providerType: session.providerType,
@@ -39080,8 +39201,9 @@ var init_reporter = __esm({
39080
39201
  return;
39081
39202
  }
39082
39203
  this.lastServerStatusHash = wsHash;
39204
+ const wsPayloadBytes = JSON.stringify(wsPayload).length;
39083
39205
  serverConn.sendMessage("status_report", wsPayload);
39084
- LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
39206
+ LOG.debug("Server", `sent status_report (${wsPayloadBytes} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
39085
39207
  }
39086
39208
  // ─── P2P ─────────────────────────────────────────
39087
39209
  sendP2PPayload(payload) {
@@ -78840,53 +78962,63 @@ function routeDataChannelMessage(peerId, msg, peers, handlers) {
78840
78962
  function handleSubscribe(peerId, msg, peers, handlers) {
78841
78963
  const peer = peers.get(peerId);
78842
78964
  if (!peer) return;
78843
- if (msg.topic === "session.chat_tail") {
78844
- const targetSessionId = typeof msg.params?.targetSessionId === "string" ? msg.params.targetSessionId.trim() : "";
78965
+ const topic = msg.topic;
78966
+ const params = msg.params || {};
78967
+ if (topic === "session.chat_tail") {
78968
+ const targetSessionId = typeof params?.targetSessionId === "string" ? params.targetSessionId.trim() : "";
78845
78969
  if (!targetSessionId) return;
78846
78970
  if (!peer.chatSubscriptions) peer.chatSubscriptions = /* @__PURE__ */ new Map();
78847
78971
  peer.chatSubscriptions.set(msg.key, {
78848
78972
  key: msg.key,
78849
- params: msg.params,
78973
+ params,
78850
78974
  seq: 0,
78851
78975
  cursor: {
78852
- knownMessageCount: Math.max(0, Number(msg.params.knownMessageCount || 0)),
78853
- lastMessageSignature: typeof msg.params.lastMessageSignature === "string" ? msg.params.lastMessageSignature : "",
78854
- tailLimit: Math.max(0, Number(msg.params.tailLimit || 0))
78976
+ knownMessageCount: Math.max(0, Number(params.knownMessageCount || 0)),
78977
+ lastMessageSignature: typeof params.lastMessageSignature === "string" ? params.lastMessageSignature : "",
78978
+ tailLimit: Math.max(0, Number(params.tailLimit || 0))
78855
78979
  },
78856
78980
  lastDeliveredSignature: ""
78857
78981
  });
78858
- } else if (msg.topic === "machine.runtime") {
78982
+ } else if (topic === "session.runtime_output") {
78983
+ const targetSessionId = typeof params?.targetSessionId === "string" ? params.targetSessionId.trim() : "";
78984
+ if (!targetSessionId) return;
78985
+ if (!peer.runtimeOutputSubscriptions) peer.runtimeOutputSubscriptions = /* @__PURE__ */ new Map();
78986
+ peer.runtimeOutputSubscriptions.set(msg.key, {
78987
+ key: msg.key,
78988
+ params: { targetSessionId }
78989
+ });
78990
+ } else if (topic === "machine.runtime") {
78859
78991
  if (!peer.machineRuntimeSubscriptions) peer.machineRuntimeSubscriptions = /* @__PURE__ */ new Map();
78860
78992
  peer.machineRuntimeSubscriptions.set(msg.key, {
78861
78993
  key: msg.key,
78862
- params: msg.params,
78994
+ params,
78863
78995
  seq: 0,
78864
78996
  lastSentAt: 0
78865
78997
  });
78866
- } else if (msg.topic === "session_host.diagnostics") {
78998
+ } else if (topic === "session_host.diagnostics") {
78867
78999
  if (!peer.sessionHostDiagnosticsSubscriptions) peer.sessionHostDiagnosticsSubscriptions = /* @__PURE__ */ new Map();
78868
79000
  peer.sessionHostDiagnosticsSubscriptions.set(msg.key, {
78869
79001
  key: msg.key,
78870
- params: msg.params,
79002
+ params,
78871
79003
  seq: 0,
78872
79004
  lastSentAt: 0
78873
79005
  });
78874
- } else if (msg.topic === "session.modal") {
78875
- const targetSessionId = typeof msg.params?.targetSessionId === "string" ? msg.params.targetSessionId.trim() : "";
79006
+ } else if (topic === "session.modal") {
79007
+ const targetSessionId = typeof params?.targetSessionId === "string" ? params.targetSessionId.trim() : "";
78876
79008
  if (!targetSessionId) return;
78877
79009
  if (!peer.sessionModalSubscriptions) peer.sessionModalSubscriptions = /* @__PURE__ */ new Map();
78878
79010
  peer.sessionModalSubscriptions.set(msg.key, {
78879
79011
  key: msg.key,
78880
- params: msg.params,
79012
+ params,
78881
79013
  seq: 0,
78882
79014
  lastSentAt: 0,
78883
79015
  lastDeliveredSignature: ""
78884
79016
  });
78885
- } else if (msg.topic === "daemon.metadata") {
79017
+ } else if (topic === "daemon.metadata") {
78886
79018
  if (!peer.daemonMetadataSubscriptions) peer.daemonMetadataSubscriptions = /* @__PURE__ */ new Map();
78887
79019
  peer.daemonMetadataSubscriptions.set(msg.key, {
78888
79020
  key: msg.key,
78889
- params: msg.params,
79021
+ params,
78890
79022
  seq: 0,
78891
79023
  lastSentAt: 0
78892
79024
  });
@@ -78898,23 +79030,28 @@ function handleSubscribe(peerId, msg, peers, handlers) {
78898
79030
  function handleUnsubscribe(peerId, msg, peers) {
78899
79031
  const peer = peers.get(peerId);
78900
79032
  if (!peer) return;
78901
- if (msg.topic === "session.chat_tail") {
79033
+ const topic = msg.topic;
79034
+ if (topic === "session.chat_tail") {
78902
79035
  peer.chatSubscriptions?.delete(msg.key);
78903
79036
  return;
78904
79037
  }
78905
- if (msg.topic === "machine.runtime") {
79038
+ if (topic === "session.runtime_output") {
79039
+ peer.runtimeOutputSubscriptions?.delete(msg.key);
79040
+ return;
79041
+ }
79042
+ if (topic === "machine.runtime") {
78906
79043
  peer.machineRuntimeSubscriptions?.delete(msg.key);
78907
79044
  return;
78908
79045
  }
78909
- if (msg.topic === "session_host.diagnostics") {
79046
+ if (topic === "session_host.diagnostics") {
78910
79047
  peer.sessionHostDiagnosticsSubscriptions?.delete(msg.key);
78911
79048
  return;
78912
79049
  }
78913
- if (msg.topic === "session.modal") {
79050
+ if (topic === "session.modal") {
78914
79051
  peer.sessionModalSubscriptions?.delete(msg.key);
78915
79052
  return;
78916
79053
  }
78917
- if (msg.topic === "daemon.metadata") {
79054
+ if (topic === "daemon.metadata") {
78918
79055
  peer.daemonMetadataSubscriptions?.delete(msg.key);
78919
79056
  }
78920
79057
  }
@@ -79667,7 +79804,24 @@ ${e?.stack || ""}`);
79667
79804
  return this.screenshotSender.sendStatusEvent(this.peers, event);
79668
79805
  }
79669
79806
  broadcastSessionOutput(sessionId, data) {
79670
- return this.screenshotSender.broadcastSessionOutput(this.peers, sessionId, data);
79807
+ if (!sessionId || typeof data !== "string" || data.length === 0) return false;
79808
+ const targetPeers = /* @__PURE__ */ new Map();
79809
+ for (const [peerId, peer] of this.peers.entries()) {
79810
+ if (peer.state !== "connected") continue;
79811
+ const subscriptions = peer.runtimeOutputSubscriptions;
79812
+ if (!subscriptions || subscriptions.size === 0) continue;
79813
+ let matches = false;
79814
+ for (const subscription of subscriptions.values()) {
79815
+ if (subscription.params.targetSessionId === sessionId) {
79816
+ matches = true;
79817
+ break;
79818
+ }
79819
+ }
79820
+ if (!matches) continue;
79821
+ targetPeers.set(peerId, peer);
79822
+ }
79823
+ if (targetPeers.size === 0) return false;
79824
+ return this.screenshotSender.broadcastSessionOutput(targetPeers, sessionId, data);
79671
79825
  }
79672
79826
  sendScreenshot(base64Data) {
79673
79827
  return this.screenshotSender.sendScreenshot(this.peers, base64Data);
@@ -87246,7 +87400,7 @@ var init_adhdev_daemon = __esm({
87246
87400
  init_version();
87247
87401
  init_src();
87248
87402
  init_runtime_defaults();
87249
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.19" });
87403
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.21" });
87250
87404
  AdhdevDaemon = class _AdhdevDaemon {
87251
87405
  localHttpServer = null;
87252
87406
  localWss = null;