adhdev 0.6.56 → 0.6.58

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
@@ -2386,6 +2386,8 @@ var init_chat_history = __esm({
2386
2386
  lastSeenCounts = /* @__PURE__ */ new Map();
2387
2387
  /** Last seen message hash per agent (deduplication) */
2388
2388
  lastSeenHashes = /* @__PURE__ */ new Map();
2389
+ /** Last seen append-only terminal transcript per agent */
2390
+ lastSeenTerminal = /* @__PURE__ */ new Map();
2389
2391
  rotated = false;
2390
2392
  /**
2391
2393
  * Append new messages to history
@@ -2443,10 +2445,51 @@ var init_chat_history = __esm({
2443
2445
  } catch {
2444
2446
  }
2445
2447
  }
2448
+ appendTerminalHistory(agentType, terminalHistory, sessionTitle, instanceId) {
2449
+ const next = String(terminalHistory || "");
2450
+ if (!next.trim()) return;
2451
+ try {
2452
+ const dedupKey = instanceId ? `${agentType}:${instanceId}:terminal` : `${agentType}:terminal`;
2453
+ const prev = this.lastSeenTerminal.get(dedupKey) || "";
2454
+ if (prev === next) return;
2455
+ let delta = "";
2456
+ if (!prev) {
2457
+ delta = next;
2458
+ } else if (next.startsWith(prev)) {
2459
+ delta = next.slice(prev.length);
2460
+ } else if (prev.includes(next)) {
2461
+ this.lastSeenTerminal.set(dedupKey, next);
2462
+ return;
2463
+ } else {
2464
+ delta = `
2465
+
2466
+ [terminal snapshot reset ${(/* @__PURE__ */ new Date()).toISOString()} | ${sessionTitle || agentType}]
2467
+ ${next}`;
2468
+ }
2469
+ if (!delta) {
2470
+ this.lastSeenTerminal.set(dedupKey, next);
2471
+ return;
2472
+ }
2473
+ const dir = path4.join(HISTORY_DIR, this.sanitize(agentType));
2474
+ fs3.mkdirSync(dir, { recursive: true });
2475
+ const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2476
+ const filePrefix = instanceId ? `${this.sanitize(instanceId)}_` : "";
2477
+ const filePath = path4.join(dir, `${filePrefix}${date5}.terminal.log`);
2478
+ fs3.appendFileSync(filePath, delta, "utf-8");
2479
+ this.lastSeenTerminal.set(dedupKey, next);
2480
+ if (!this.rotated) {
2481
+ this.rotated = true;
2482
+ this.rotateOldFiles().catch(() => {
2483
+ });
2484
+ }
2485
+ } catch {
2486
+ }
2487
+ }
2446
2488
  /** Called when agent session is explicitly changed */
2447
2489
  onSessionChange(agentType) {
2448
2490
  this.lastSeenHashes.delete(agentType);
2449
2491
  this.lastSeenCounts.delete(agentType);
2492
+ this.lastSeenTerminal.delete(`${agentType}:terminal`);
2450
2493
  }
2451
2494
  /** Delete history files older than 30 days */
2452
2495
  async rotateOldFiles() {
@@ -2456,7 +2499,7 @@ var init_chat_history = __esm({
2456
2499
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
2457
2500
  for (const dir of agentDirs) {
2458
2501
  const dirPath = path4.join(HISTORY_DIR, dir.name);
2459
- const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
2502
+ const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
2460
2503
  for (const file2 of files) {
2461
2504
  const filePath = path4.join(dirPath, file2);
2462
2505
  const stat = fs3.statSync(filePath);
@@ -3364,7 +3407,13 @@ async function handleReadChat(h, args) {
3364
3407
  _log(`${provider.category} adapter: ${adapter.cliType}`);
3365
3408
  const status = adapter.getStatus?.();
3366
3409
  if (status) {
3367
- return { success: true, messages: status.messages || [], status: status.status, activeModal: status.activeModal };
3410
+ return {
3411
+ success: true,
3412
+ messages: status.messages || [],
3413
+ status: status.status,
3414
+ activeModal: status.activeModal,
3415
+ terminalHistory: status.terminalHistory || ""
3416
+ };
3368
3417
  }
3369
3418
  }
3370
3419
  return { success: false, error: `${provider.category} adapter not found` };
@@ -16480,6 +16529,68 @@ function shSingleQuote(arg) {
16480
16529
  if (/^[a-zA-Z0-9@%_+=:,./-]+$/.test(arg)) return arg;
16481
16530
  return `'${arg.replace(/'/g, `'\\''`)}'`;
16482
16531
  }
16532
+ function estimatePromptDisplayLines(text, cols = 100) {
16533
+ const normalized = String(text || "").replace(/\r/g, "");
16534
+ if (!normalized) return 1;
16535
+ return normalized.split("\n").reduce((sum, line) => sum + Math.max(1, Math.ceil(Math.max(1, line.length) / cols)), 0);
16536
+ }
16537
+ function extractPromptRetrySnippet(text) {
16538
+ const lines = String(text || "").replace(/\r/g, "").split("\n").map((line) => line.trim()).filter(Boolean);
16539
+ const candidate = lines[lines.length - 1] || lines[0] || "";
16540
+ return candidate.slice(-120);
16541
+ }
16542
+ function normalizePromptText(text) {
16543
+ return String(text || "").replace(/\s+/g, " ").trim();
16544
+ }
16545
+ function compactPromptText(text) {
16546
+ return String(text || "").replace(/\s+/g, "").trim();
16547
+ }
16548
+ function promptLikelyVisible(screenText, promptSnippet) {
16549
+ const snippet = normalizePromptText(promptSnippet);
16550
+ if (!snippet) return false;
16551
+ const normalizedScreen = normalizePromptText(screenText);
16552
+ if (normalizedScreen.includes(snippet)) return true;
16553
+ const compactScreen = compactPromptText(screenText);
16554
+ const compactSnippet = compactPromptText(promptSnippet);
16555
+ if (compactSnippet && compactScreen.includes(compactSnippet)) return true;
16556
+ const tokens = snippet.split(/[^A-Za-z0-9_.:/-]+/).map((token) => token.trim()).filter((token) => token.length >= 4);
16557
+ if (tokens.length === 0) return false;
16558
+ const required2 = Math.min(tokens.length, 3);
16559
+ const matched = tokens.filter(
16560
+ (token) => normalizedScreen.includes(token) || compactScreen.includes(compactPromptText(token))
16561
+ ).length;
16562
+ return matched >= required2;
16563
+ }
16564
+ function splitHistoryLines(text) {
16565
+ return String(text || "").split("\n").map((line) => line.replace(/\s+$/, ""));
16566
+ }
16567
+ function normalizeHistoryLine(line) {
16568
+ return String(line || "").replace(/\s+/g, " ").trim();
16569
+ }
16570
+ function mergeTerminalHistory(existing, snapshot) {
16571
+ const next = String(snapshot || "").trim();
16572
+ if (!next) return existing;
16573
+ const prev = String(existing || "").trim();
16574
+ if (!prev) return next;
16575
+ if (prev === next || prev.endsWith(next)) return prev;
16576
+ const prevLines = splitHistoryLines(prev);
16577
+ const nextLines = splitHistoryLines(next);
16578
+ const prevNorm = prevLines.map(normalizeHistoryLine);
16579
+ const nextNorm = nextLines.map(normalizeHistoryLine);
16580
+ const maxOverlap = Math.min(prevLines.length, nextLines.length);
16581
+ for (let overlap = maxOverlap; overlap >= 1; overlap -= 1) {
16582
+ const prevTail = prevNorm.slice(prevNorm.length - overlap);
16583
+ const nextHead = nextNorm.slice(0, overlap);
16584
+ if (prevTail.every((line, index) => line === nextHead[index])) {
16585
+ return [...prevLines, ...nextLines.slice(overlap)].join("\n").trim();
16586
+ }
16587
+ }
16588
+ const compactPrev = prevNorm.join("\n");
16589
+ const compactNext = nextNorm.join("\n");
16590
+ if (compactPrev.includes(compactNext)) return prev;
16591
+ return `${prev}
16592
+ ${next}`.trim();
16593
+ }
16483
16594
  function parsePatternEntry(x) {
16484
16595
  if (x instanceof RegExp) return x;
16485
16596
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -16556,6 +16667,7 @@ var init_provider_cli_adapter = __esm({
16556
16667
  this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
16557
16668
  this.sendDelayMs = typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0;
16558
16669
  this.sendKey = typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r";
16670
+ this.submitStrategy = provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo";
16559
16671
  this.cliScripts = provider.scripts || {};
16560
16672
  const scriptNames = Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function");
16561
16673
  if (scriptNames.length > 0) {
@@ -16570,6 +16682,7 @@ var init_provider_cli_adapter = __esm({
16570
16682
  provider;
16571
16683
  ptyProcess = null;
16572
16684
  messages = [];
16685
+ committedMessages = [];
16573
16686
  structuredMessages = [];
16574
16687
  currentStatus = "starting";
16575
16688
  onStatusChange = null;
@@ -16599,6 +16712,11 @@ var init_provider_cli_adapter = __esm({
16599
16712
  settleTimer = null;
16600
16713
  settledBuffer = "";
16601
16714
  submitPendingUntil = 0;
16715
+ responseSettleIgnoreUntil = 0;
16716
+ responseEpoch = 0;
16717
+ submitRetryTimer = null;
16718
+ submitRetryUsed = false;
16719
+ submitRetryPromptSnippet = "";
16602
16720
  // Resize redraw suppression
16603
16721
  resizeSuppressUntil = 0;
16604
16722
  // Debug: status transition history
@@ -16611,8 +16729,35 @@ var init_provider_cli_adapter = __esm({
16611
16729
  accumulatedRawBuffer = "";
16612
16730
  /** Current visible terminal screen snapshot */
16613
16731
  terminalScreen = new TerminalScreen(40, 120);
16732
+ /** Rolling append-only terminal transcript built from screen snapshots */
16733
+ terminalHistory = "";
16614
16734
  /** Max accumulated buffer size (last 50KB) */
16615
16735
  static MAX_ACCUMULATED_BUFFER = 5e4;
16736
+ currentTurnScope = null;
16737
+ syncMessageViews() {
16738
+ this.messages = [...this.committedMessages];
16739
+ this.structuredMessages = [...this.committedMessages];
16740
+ }
16741
+ sliceFromOffset(text, start) {
16742
+ if (!text) return "";
16743
+ if (!Number.isFinite(start) || start <= 0) return text;
16744
+ if (start >= text.length) return "";
16745
+ return text.slice(start);
16746
+ }
16747
+ buildParseInput(baseMessages, partialResponse, scope) {
16748
+ const buffer = scope ? this.sliceFromOffset(this.terminalHistory, scope.terminalHistoryStart) || this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
16749
+ const rawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
16750
+ const terminalHistory = scope ? this.sliceFromOffset(this.terminalHistory, scope.terminalHistoryStart) || this.terminalHistory : this.terminalHistory;
16751
+ return {
16752
+ buffer,
16753
+ rawBuffer,
16754
+ recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
16755
+ screenText: this.terminalScreen.getText(),
16756
+ terminalHistory,
16757
+ messages: [...baseMessages],
16758
+ partialResponse
16759
+ };
16760
+ }
16616
16761
  setStatus(status, trigger) {
16617
16762
  const prev = this.currentStatus;
16618
16763
  if (prev === status) return;
@@ -16627,6 +16772,7 @@ var init_provider_cli_adapter = __esm({
16627
16772
  approvalKeys;
16628
16773
  sendDelayMs;
16629
16774
  sendKey;
16775
+ submitStrategy;
16630
16776
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
16631
16777
  setCliScripts(scripts) {
16632
16778
  this.cliScripts = scripts;
@@ -16722,7 +16868,9 @@ var init_provider_cli_adapter = __esm({
16722
16868
  this.startupParseGate = true;
16723
16869
  this.startupBuffer = "";
16724
16870
  this.terminalScreen.reset(40, 120);
16725
- this.ready = true;
16871
+ this.terminalHistory = "";
16872
+ this.currentTurnScope = null;
16873
+ this.ready = false;
16726
16874
  this.setStatus("idle", "pty_ready");
16727
16875
  this.onStatusChange?.();
16728
16876
  }
@@ -16733,6 +16881,7 @@ var init_provider_cli_adapter = __esm({
16733
16881
  this.ptyProcess?.write("\x1B[1;1R");
16734
16882
  }
16735
16883
  this.terminalScreen.write(rawData);
16884
+ this.terminalHistory = mergeTerminalHistory(this.terminalHistory, this.terminalScreen.getText());
16736
16885
  const cleanData = stripAnsi(rawData);
16737
16886
  if (this.isWaitingForResponse && cleanData) {
16738
16887
  this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
@@ -16767,7 +16916,9 @@ var init_provider_cli_adapter = __esm({
16767
16916
  const isReady = scriptStatus === "idle" || elapsed > 8e3 || bufCap;
16768
16917
  if (isReady) {
16769
16918
  this.startupParseGate = false;
16919
+ this.ready = true;
16770
16920
  LOG.info("CLI", `[${this.cliType}] Startup gate end (${elapsed}ms, scriptStatus=${scriptStatus})`);
16921
+ this.onStatusChange?.();
16771
16922
  } else {
16772
16923
  return;
16773
16924
  }
@@ -16776,19 +16927,45 @@ var init_provider_cli_adapter = __esm({
16776
16927
  }
16777
16928
  scheduleSettle() {
16778
16929
  if (this.settleTimer) clearTimeout(this.settleTimer);
16930
+ const settleEpoch = this.responseEpoch;
16779
16931
  const delay = Math.max(
16780
16932
  this.timeouts.outputSettle,
16781
16933
  this.submitPendingUntil > Date.now() ? this.submitPendingUntil - Date.now() + this.timeouts.outputSettle : 0
16782
16934
  );
16783
16935
  this.settleTimer = setTimeout(() => {
16784
16936
  this.settleTimer = null;
16937
+ if (settleEpoch !== this.responseEpoch) return;
16785
16938
  this.settledBuffer = this.recentOutputBuffer;
16786
16939
  this.evaluateSettled();
16787
16940
  }, delay);
16788
16941
  }
16942
+ armApprovalExitTimeout() {
16943
+ if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
16944
+ this.approvalExitTimeout = setTimeout(() => {
16945
+ if (this.currentStatus !== "waiting_approval") return;
16946
+ const tail = this.recentOutputBuffer;
16947
+ const modal = this.runParseApproval(tail);
16948
+ const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
16949
+ if (stillWaiting) {
16950
+ this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
16951
+ this.onStatusChange?.();
16952
+ this.armApprovalExitTimeout();
16953
+ return;
16954
+ }
16955
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
16956
+ this.activeModal = null;
16957
+ this.lastApprovalResolvedAt = Date.now();
16958
+ this.setStatus("idle", "approval_timeout");
16959
+ this.onStatusChange?.();
16960
+ }, 6e4);
16961
+ }
16789
16962
  evaluateSettled() {
16963
+ if (this.submitPendingUntil > Date.now()) return;
16964
+ if (this.responseSettleIgnoreUntil > Date.now()) return;
16790
16965
  const tail = this.settledBuffer;
16791
- const scriptStatus = this.runDetectStatus(tail);
16966
+ const modal = this.runParseApproval(tail);
16967
+ const rawScriptStatus = this.runDetectStatus(tail);
16968
+ const scriptStatus = rawScriptStatus === "waiting_approval" || modal ? "waiting_approval" : rawScriptStatus;
16792
16969
  if (!scriptStatus) return;
16793
16970
  const prevStatus = this.currentStatus;
16794
16971
  if (scriptStatus === "waiting_approval") {
@@ -16796,19 +16973,9 @@ var init_provider_cli_adapter = __esm({
16796
16973
  if (!inCooldown) {
16797
16974
  this.isWaitingForResponse = true;
16798
16975
  this.setStatus("waiting_approval", "script_detect");
16799
- const modal = this.runParseApproval(tail);
16800
16976
  this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
16801
16977
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
16802
- if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
16803
- this.approvalExitTimeout = setTimeout(() => {
16804
- if (this.currentStatus === "waiting_approval") {
16805
- LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
16806
- this.activeModal = null;
16807
- this.lastApprovalResolvedAt = Date.now();
16808
- this.setStatus("idle", "approval_timeout");
16809
- this.onStatusChange?.();
16810
- }
16811
- }, 6e4);
16978
+ this.armApprovalExitTimeout();
16812
16979
  this.onStatusChange?.();
16813
16980
  return;
16814
16981
  }
@@ -16844,7 +17011,12 @@ var init_provider_cli_adapter = __esm({
16844
17011
  this.lastApprovalResolvedAt = Date.now();
16845
17012
  }
16846
17013
  if (this.isWaitingForResponse) {
16847
- this.finishResponse();
17014
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
17015
+ this.idleTimeout = setTimeout(() => {
17016
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
17017
+ this.finishResponse();
17018
+ }
17019
+ }, this.timeouts.idleFinish);
16848
17020
  } else if (prevStatus !== "idle") {
16849
17021
  this.setStatus("idle", "script_detect");
16850
17022
  this.onStatusChange?.();
@@ -16852,6 +17024,9 @@ var init_provider_cli_adapter = __esm({
16852
17024
  }
16853
17025
  }
16854
17026
  finishResponse() {
17027
+ if (this.submitPendingUntil > Date.now()) return;
17028
+ if (this.responseSettleIgnoreUntil > Date.now()) return;
17029
+ this.commitCurrentTranscript();
16855
17030
  if (this.responseTimeout) {
16856
17031
  clearTimeout(this.responseTimeout);
16857
17032
  this.responseTimeout = null;
@@ -16864,12 +17039,67 @@ var init_provider_cli_adapter = __esm({
16864
17039
  clearTimeout(this.approvalExitTimeout);
16865
17040
  this.approvalExitTimeout = null;
16866
17041
  }
17042
+ if (this.submitRetryTimer) {
17043
+ clearTimeout(this.submitRetryTimer);
17044
+ this.submitRetryTimer = null;
17045
+ }
16867
17046
  this.responseBuffer = "";
16868
17047
  this.isWaitingForResponse = false;
17048
+ this.responseSettleIgnoreUntil = 0;
17049
+ this.submitRetryUsed = false;
17050
+ this.submitRetryPromptSnippet = "";
17051
+ this.currentTurnScope = null;
16869
17052
  this.activeModal = null;
16870
17053
  this.setStatus("idle", "response_finished");
16871
17054
  this.onStatusChange?.();
16872
17055
  }
17056
+ commitCurrentTranscript() {
17057
+ const baseMessages = [...this.committedMessages];
17058
+ const parsed = this.parseCurrentTranscript(baseMessages, "", this.currentTurnScope);
17059
+ if (parsed && Array.isArray(parsed.messages) && parsed.messages.length > 0) {
17060
+ const parsedMessages = parsed.messages.filter((m) => m && (m.role === "user" || m.role === "assistant")).map((m) => ({
17061
+ role: m.role,
17062
+ content: typeof m.content === "string" ? m.content : String(m.content || ""),
17063
+ timestamp: m.timestamp
17064
+ }));
17065
+ const latestAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant" && m.content.trim());
17066
+ if (latestAssistant) {
17067
+ LOG.info("CLI", `[${this.cliType}] commitCurrentTranscript parsed assistant len=${latestAssistant.content.length} scopePrompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 120)}`);
17068
+ const nextMessages = [...baseMessages];
17069
+ const last2 = nextMessages[nextMessages.length - 1];
17070
+ if (last2?.role === "assistant") {
17071
+ last2.content = latestAssistant.content;
17072
+ last2.timestamp = latestAssistant.timestamp || last2.timestamp;
17073
+ } else if (last2?.role === "user") {
17074
+ nextMessages.push({
17075
+ role: "assistant",
17076
+ content: latestAssistant.content,
17077
+ timestamp: latestAssistant.timestamp || Date.now()
17078
+ });
17079
+ } else {
17080
+ nextMessages.push({
17081
+ role: "assistant",
17082
+ content: latestAssistant.content,
17083
+ timestamp: latestAssistant.timestamp || Date.now()
17084
+ });
17085
+ }
17086
+ this.committedMessages = nextMessages;
17087
+ this.syncMessageViews();
17088
+ return;
17089
+ }
17090
+ }
17091
+ const fallback = String(this.responseBuffer || "").trim();
17092
+ LOG.info("CLI", `[${this.cliType}] commitCurrentTranscript fallback len=${fallback.length} scopePrompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 120)}`);
17093
+ if (!fallback) return;
17094
+ const last = baseMessages[baseMessages.length - 1];
17095
+ if (last?.role === "assistant") {
17096
+ last.content = fallback;
17097
+ } else {
17098
+ baseMessages.push({ role: "assistant", content: fallback, timestamp: Date.now() });
17099
+ }
17100
+ this.committedMessages = baseMessages;
17101
+ this.syncMessageViews();
17102
+ }
16873
17103
  // ─── Script Execution ──────────────────────────
16874
17104
  runDetectStatus(text) {
16875
17105
  if (!this.cliScripts?.detectStatus) return null;
@@ -16899,24 +17129,12 @@ var init_provider_cli_adapter = __esm({
16899
17129
  }
16900
17130
  // ─── Public API (CliAdapter) ───────────────────
16901
17131
  getStatus() {
16902
- const scriptResult = this.getScriptParsedStatus();
16903
- if (scriptResult) {
16904
- return {
16905
- status: this.currentStatus,
16906
- messages: (scriptResult.messages || []).map((m) => ({
16907
- role: m.role,
16908
- content: m.content,
16909
- timestamp: m.timestamp
16910
- })),
16911
- workingDir: this.workingDir,
16912
- activeModal: this.activeModal
16913
- };
16914
- }
16915
17132
  return {
16916
17133
  status: this.currentStatus,
16917
- messages: [...this.messages],
17134
+ messages: [...this.committedMessages],
16918
17135
  workingDir: this.workingDir,
16919
- activeModal: this.activeModal
17136
+ activeModal: this.activeModal,
17137
+ terminalHistory: this.terminalHistory
16920
17138
  };
16921
17139
  }
16922
17140
  /**
@@ -16924,31 +17142,32 @@ var init_provider_cli_adapter = __esm({
16924
17142
  * Called by command handler / dashboard for rich content rendering.
16925
17143
  */
16926
17144
  getScriptParsedStatus() {
17145
+ const messages = [...this.committedMessages];
17146
+ return {
17147
+ id: "cli_session",
17148
+ status: this.currentStatus,
17149
+ title: this.cliName,
17150
+ terminalHistory: this.terminalHistory,
17151
+ messages: messages.slice(-50).map((message, index) => ({
17152
+ id: `msg_${index}`,
17153
+ role: message.role,
17154
+ content: message.content,
17155
+ timestamp: message.timestamp,
17156
+ index,
17157
+ kind: "standard"
17158
+ })),
17159
+ activeModal: this.activeModal
17160
+ };
17161
+ }
17162
+ parseCurrentTranscript(baseMessages, partialResponse, scope) {
16927
17163
  if (!this.cliScripts?.parseOutput) return null;
16928
17164
  try {
16929
- const input = {
16930
- buffer: this.accumulatedBuffer,
16931
- rawBuffer: this.accumulatedRawBuffer,
16932
- recentBuffer: this.recentOutputBuffer,
16933
- screenText: this.terminalScreen.getText(),
16934
- messages: [...this.structuredMessages.length > 0 ? this.structuredMessages : this.messages],
16935
- partialResponse: this.responseBuffer
16936
- };
16937
- const result = this.cliScripts.parseOutput(input);
16938
- if (result && typeof result === "object") {
16939
- if (Array.isArray(result.messages)) {
16940
- this.structuredMessages = result.messages.map((m) => ({
16941
- role: m.role,
16942
- content: m.content,
16943
- timestamp: m.timestamp
16944
- }));
16945
- }
16946
- return result;
16947
- }
17165
+ const input = this.buildParseInput(baseMessages, partialResponse, scope);
17166
+ return this.cliScripts.parseOutput(input);
16948
17167
  } catch (e) {
16949
17168
  LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
17169
+ return null;
16950
17170
  }
16951
- return null;
16952
17171
  }
16953
17172
  /** Whether this adapter has CLI scripts loaded */
16954
17173
  hasCliScripts() {
@@ -16980,29 +17199,125 @@ ${data.message || ""}`.trim();
16980
17199
  }
16981
17200
  async sendMessage(text) {
16982
17201
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
17202
+ if (this.startupParseGate) {
17203
+ const deadline = Date.now() + 1e4;
17204
+ while (this.startupParseGate && Date.now() < deadline) {
17205
+ await new Promise((resolve8) => setTimeout(resolve8, 50));
17206
+ }
17207
+ }
16983
17208
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
16984
17209
  if (this.isWaitingForResponse) return;
16985
- this.messages.push({ role: "user", content: text, timestamp: Date.now() });
16986
- this.structuredMessages.push({ role: "user", content: text, timestamp: Date.now() });
17210
+ this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
17211
+ this.syncMessageViews();
16987
17212
  this.isWaitingForResponse = true;
16988
17213
  this.responseBuffer = "";
17214
+ this.currentTurnScope = {
17215
+ prompt: text,
17216
+ startedAt: Date.now(),
17217
+ bufferStart: this.accumulatedBuffer.length,
17218
+ rawBufferStart: this.accumulatedRawBuffer.length,
17219
+ terminalHistoryStart: this.terminalHistory.length
17220
+ };
17221
+ LOG.info("CLI", `[${this.cliType}] sendMessage turn scope buffer=${this.currentTurnScope.bufferStart} raw=${this.currentTurnScope.rawBufferStart} terminal=${this.currentTurnScope.terminalHistoryStart} prompt=${JSON.stringify(text).slice(0, 120)}`);
17222
+ this.submitRetryUsed = false;
17223
+ this.submitRetryPromptSnippet = extractPromptRetrySnippet(text);
17224
+ const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet);
17225
+ if (this.submitRetryTimer) {
17226
+ clearTimeout(this.submitRetryTimer);
17227
+ this.submitRetryTimer = null;
17228
+ }
17229
+ const estimatedLines = estimatePromptDisplayLines(text);
17230
+ const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
17231
+ const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
17232
+ const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
17233
+ if (this.settleTimer) {
17234
+ clearTimeout(this.settleTimer);
17235
+ this.settleTimer = null;
17236
+ }
17237
+ this.responseEpoch += 1;
17238
+ this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
16989
17239
  this.setStatus("generating", "sendMessage");
16990
17240
  this.onStatusChange?.();
16991
- this.ptyProcess.write(text);
17241
+ const startResponseTimeout = () => {
17242
+ if (this.responseTimeout) clearTimeout(this.responseTimeout);
17243
+ this.responseTimeout = setTimeout(() => {
17244
+ if (this.isWaitingForResponse) this.finishResponse();
17245
+ }, this.timeouts.maxResponse);
17246
+ };
16992
17247
  const submit = () => {
16993
17248
  if (!this.ptyProcess) return;
16994
17249
  this.submitPendingUntil = 0;
16995
17250
  this.ptyProcess.write(this.sendKey);
16996
- this.responseTimeout = setTimeout(() => {
16997
- if (this.isWaitingForResponse) this.finishResponse();
16998
- }, this.timeouts.maxResponse);
17251
+ const retrySubmitIfStuck = (attempt) => {
17252
+ this.submitRetryTimer = null;
17253
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
17254
+ if (this.currentStatus !== "generating") return;
17255
+ if ((this.responseBuffer || "").trim()) return;
17256
+ const screenText = this.terminalScreen.getText();
17257
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
17258
+ 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(screenText)) return;
17259
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
17260
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
17261
+ this.ptyProcess.write(this.sendKey);
17262
+ if (attempt >= 3) {
17263
+ this.submitRetryUsed = true;
17264
+ return;
17265
+ }
17266
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
17267
+ };
17268
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
17269
+ startResponseTimeout();
16999
17270
  };
17000
- if (this.sendDelayMs > 0) {
17001
- this.submitPendingUntil = Date.now() + this.sendDelayMs;
17002
- setTimeout(submit, this.sendDelayMs);
17003
- } else {
17004
- submit();
17271
+ if (this.submitStrategy === "immediate") {
17272
+ this.submitPendingUntil = 0;
17273
+ this.ptyProcess.write(text + this.sendKey);
17274
+ this.submitRetryTimer = setTimeout(() => {
17275
+ this.submitRetryTimer = null;
17276
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
17277
+ if (this.currentStatus !== "generating") return;
17278
+ if ((this.responseBuffer || "").trim()) return;
17279
+ const screenText = this.terminalScreen.getText();
17280
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
17281
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
17282
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
17283
+ this.ptyProcess.write(this.sendKey);
17284
+ this.submitRetryUsed = true;
17285
+ }, retryDelayMs);
17286
+ startResponseTimeout();
17287
+ return;
17288
+ }
17289
+ if (submitDelayMs > 0) {
17290
+ this.submitPendingUntil = Date.now() + submitDelayMs;
17005
17291
  }
17292
+ this.ptyProcess.write(text);
17293
+ const submitStartedAt = Date.now();
17294
+ let lastNormalizedScreen = "";
17295
+ let lastScreenChangeAt = submitStartedAt;
17296
+ const waitForEchoAndSubmit = () => {
17297
+ if (!this.ptyProcess) return;
17298
+ const now = Date.now();
17299
+ const elapsed = now - submitStartedAt;
17300
+ const screenText = this.terminalScreen.getText();
17301
+ const normalizedScreen = normalizePromptText(screenText);
17302
+ if (normalizedScreen !== lastNormalizedScreen) {
17303
+ lastNormalizedScreen = normalizedScreen;
17304
+ lastScreenChangeAt = now;
17305
+ }
17306
+ const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
17307
+ if (echoVisible) {
17308
+ const screenSettled = now - lastScreenChangeAt >= 500;
17309
+ if (elapsed >= submitDelayMs && screenSettled) {
17310
+ submit();
17311
+ return;
17312
+ }
17313
+ }
17314
+ if (elapsed >= maxEchoWaitMs) {
17315
+ submit();
17316
+ return;
17317
+ }
17318
+ setTimeout(waitForEchoAndSubmit, 50);
17319
+ };
17320
+ waitForEchoAndSubmit();
17006
17321
  }
17007
17322
  getPartialResponse() {
17008
17323
  if (!this.isWaitingForResponse) return "";
@@ -17020,6 +17335,10 @@ ${data.message || ""}`.trim();
17020
17335
  clearTimeout(this.approvalExitTimeout);
17021
17336
  this.approvalExitTimeout = null;
17022
17337
  }
17338
+ if (this.submitRetryTimer) {
17339
+ clearTimeout(this.submitRetryTimer);
17340
+ this.submitRetryTimer = null;
17341
+ }
17023
17342
  if (this.ptyProcess) {
17024
17343
  this.ptyProcess.write("");
17025
17344
  setTimeout(() => {
@@ -17037,10 +17356,14 @@ ${data.message || ""}`.trim();
17037
17356
  }
17038
17357
  }
17039
17358
  clearHistory() {
17040
- this.messages = [];
17041
- this.structuredMessages = [];
17359
+ this.committedMessages = [];
17360
+ this.syncMessageViews();
17042
17361
  this.accumulatedBuffer = "";
17043
17362
  this.accumulatedRawBuffer = "";
17363
+ this.terminalHistory = "";
17364
+ this.currentTurnScope = null;
17365
+ this.submitRetryUsed = false;
17366
+ this.submitRetryPromptSnippet = "";
17044
17367
  this.terminalScreen.reset();
17045
17368
  this.onStatusChange?.();
17046
17369
  }
@@ -17054,7 +17377,16 @@ ${data.message || ""}`.trim();
17054
17377
  this.ptyProcess?.write(data);
17055
17378
  }
17056
17379
  resolveModal(buttonIndex) {
17057
- if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
17380
+ if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
17381
+ this.activeModal = null;
17382
+ this.lastApprovalResolvedAt = Date.now();
17383
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
17384
+ if (this.approvalExitTimeout) {
17385
+ clearTimeout(this.approvalExitTimeout);
17386
+ this.approvalExitTimeout = null;
17387
+ }
17388
+ this.setStatus("generating", "approval_resolved");
17389
+ this.onStatusChange?.();
17058
17390
  if (buttonIndex in this.approvalKeys) {
17059
17391
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
17060
17392
  } else {
@@ -17083,9 +17415,12 @@ ${data.message || ""}`.trim();
17083
17415
  spawnAt: this.spawnAt,
17084
17416
  workingDir: this.workingDir,
17085
17417
  messages: this.messages.slice(-20),
17418
+ committedMessages: this.committedMessages.slice(-20),
17086
17419
  structuredMessages: this.structuredMessages.slice(-20),
17087
- messageCount: this.messages.length,
17420
+ messageCount: this.committedMessages.length,
17088
17421
  screenText: this.terminalScreen.getText().slice(-4e3),
17422
+ terminalHistory: this.terminalHistory.slice(-8e3),
17423
+ currentTurnScope: this.currentTurnScope,
17089
17424
  startupBuffer: this.startupBuffer.slice(-4e3),
17090
17425
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
17091
17426
  settledBuffer: this.settledBuffer.slice(-500),
@@ -17096,6 +17431,11 @@ ${data.message || ""}`.trim();
17096
17431
  isWaitingForResponse: this.isWaitingForResponse,
17097
17432
  activeModal: this.activeModal,
17098
17433
  lastApprovalResolvedAt: this.lastApprovalResolvedAt,
17434
+ sendDelayMs: this.sendDelayMs,
17435
+ sendKey: this.sendKey,
17436
+ submitStrategy: this.submitStrategy,
17437
+ submitPendingUntil: this.submitPendingUntil,
17438
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
17099
17439
  resizeSuppressUntil: this.resizeSuppressUntil,
17100
17440
  hasCliScripts: this.hasCliScripts(),
17101
17441
  scriptNames: Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function"),
@@ -17166,31 +17506,12 @@ var init_cli_provider_instance = __esm({
17166
17506
  async onTick() {
17167
17507
  }
17168
17508
  getState() {
17169
- const rawStatus = this.adapter.getStatus();
17170
- const parsedStatus = this.adapter.getScriptParsedStatus();
17171
- const adapterStatus = parsedStatus ? {
17172
- ...rawStatus,
17173
- messages: parsedStatus.messages || rawStatus.messages,
17174
- activeModal: parsedStatus.activeModal || rawStatus.activeModal
17175
- } : rawStatus;
17509
+ const adapterStatus = this.adapter.getStatus();
17176
17510
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
17177
17511
  const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
17178
17512
  const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
17179
17513
  return { ...m, content };
17180
17514
  });
17181
- const partial2 = this.adapter.getPartialResponse();
17182
- const shouldAppendRawPartial = !parsedStatus;
17183
- if (shouldAppendRawPartial && adapterStatus.status === "generating" && partial2) {
17184
- const cleaned = partial2.trim();
17185
- if (cleaned && cleaned !== "(generating...)") {
17186
- recentMessages.push({
17187
- role: "assistant",
17188
- content: (cleaned.length > 8e3 ? cleaned.slice(0, 8e3) + "..." : cleaned) + "...",
17189
- timestamp: Date.now(),
17190
- meta: { streaming: true }
17191
- });
17192
- }
17193
- }
17194
17515
  if (recentMessages.length > 0) {
17195
17516
  const dirName2 = this.workingDir.split("/").filter(Boolean).pop() || "session";
17196
17517
  this.historyWriter.appendNewMessages(
@@ -17200,6 +17521,14 @@ var init_cli_provider_instance = __esm({
17200
17521
  this.instanceId
17201
17522
  );
17202
17523
  }
17524
+ if (adapterStatus.terminalHistory?.trim()) {
17525
+ this.historyWriter.appendTerminalHistory(
17526
+ this.type,
17527
+ adapterStatus.terminalHistory,
17528
+ `${this.provider.name} \xB7 ${dirName}`,
17529
+ this.instanceId
17530
+ );
17531
+ }
17203
17532
  return {
17204
17533
  type: this.type,
17205
17534
  name: this.provider.name,
@@ -17212,6 +17541,7 @@ var init_cli_provider_instance = __esm({
17212
17541
  status: adapterStatus.status,
17213
17542
  messages: recentMessages,
17214
17543
  activeModal: adapterStatus.activeModal,
17544
+ terminalHistory: adapterStatus.terminalHistory,
17215
17545
  inputContent: ""
17216
17546
  },
17217
17547
  workspace: this.workingDir,
@@ -35695,7 +36025,8 @@ async function detectAllVersions(loader, archive) {
35695
36025
  binary: null,
35696
36026
  detectedAt: (/* @__PURE__ */ new Date()).toISOString()
35697
36027
  };
35698
- const versionCommand = provider.versionCommand;
36028
+ const verCmdConfig = provider.versionCommand;
36029
+ const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[currentOs] : verCmdConfig;
35699
36030
  if (provider.category === "ide") {
35700
36031
  const osPaths = provider.paths?.[currentOs] || [];
35701
36032
  const appPath = checkPathExists2(osPaths);
@@ -37180,11 +37511,7 @@ var init_dev_server = __esm({
37180
37511
  return;
37181
37512
  }
37182
37513
  let targetDir;
37183
- if (location === "user") {
37184
- targetDir = this.providerLoader.getUserProviderDir(category, type);
37185
- } else {
37186
- targetDir = this.providerLoader.getBuiltinProviderDir(category, type);
37187
- }
37514
+ targetDir = this.providerLoader.getUserProviderDir(category, type);
37188
37515
  const jsonPath = path12.join(targetDir, "provider.json");
37189
37516
  if (fs10.existsSync(jsonPath)) {
37190
37517
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
@@ -37982,8 +38309,7 @@ var init_dev_server = __esm({
37982
38309
  }
37983
38310
  loadAutoImplReferenceScripts(category, referenceType) {
37984
38311
  if (!referenceType) return {};
37985
- const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
37986
- const refDir = path12.join(builtinDir, category, referenceType);
38312
+ const refDir = this.providerLoader.getUpstreamProviderDir(category, referenceType);
37987
38313
  if (!fs10.existsSync(refDir)) return {};
37988
38314
  const referenceScripts = {};
37989
38315
  const scriptsDir = path12.join(refDir, "scripts");
@@ -38222,7 +38548,7 @@ var init_dev_server = __esm({
38222
38548
  }
38223
38549
  if (model) args.push("--model", model);
38224
38550
  const escapedArgs = args.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
38225
- const metaPrompt = `Read the file at ${promptFile} and follow ALL instructions recursively. You have full authority to implement ALL required script files, update provider.json configurations based on the reference patterns, and independently test them against 127.0.0.1:19280 via CDP CURL. Upon complete validation of ALL assigned files, print exactly "_PIPELINE_COMPLETE_SIGNAL_" to gracefully close the pipeline. DO NOT WAIT FOR APPROVAL, execute completely autonomously.`;
38551
+ const metaPrompt = `Read the file at ${promptFile} and follow ALL instructions strictly. DO NOT spend time exploring the filesystem or other providers. You have full authority to implement ALL required script files and independently test them against 127.0.0.1:19280 via CDP CURL. Upon complete validation of ALL assigned files, print exactly "_PIPELINE_COMPLETE_SIGNAL_" to gracefully close the pipeline. DO NOT WAIT FOR APPROVAL, execute completely autonomously.`;
38226
38552
  shellCmd = `${command} ${escapedArgs} "${metaPrompt}"`;
38227
38553
  } else {
38228
38554
  const escapedArgs = baseArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
@@ -38485,6 +38811,8 @@ var init_dev_server = __esm({
38485
38811
  lines.push("5. Do NOT modify `scripts.js` router \u2014 only edit individual `*.js` files");
38486
38812
  lines.push("6. All scripts run in the browser (CDP evaluate) \u2014 use DOM APIs only");
38487
38813
  lines.push('7. **Cross-Platform Compatibility**: If you use ARIA labels that contain keyboard shortcuts (e.g., `Cascade (\u2318L)`), you MUST use substring matches (`aria-label*="Cascade"`) or handle both macOS (`\u2318`, `Cmd`) and Windows (`Ctrl`) so the script does not break on other operating systems.');
38814
+ lines.push("8. **CRITICAL: DO NOT explore the filesystem or read other providers.** The reference implementation pattern is already provided below. Do not run `find`, `rg`, or `cat` on upstream providers. Doing so wastes context tokens and will crash the agent session. Focus entirely on modifying the target files.");
38815
+ lines.push("9. Do NOT delete any files. Implement the logic by replacing the empty stubs.");
38488
38816
  lines.push("");
38489
38817
  lines.push("## Required Return Format");
38490
38818
  lines.push("| Function | Return JSON |");
@@ -40872,7 +41200,7 @@ var init_adhdev_daemon = __esm({
40872
41200
  fs12 = __toESM(require("fs"));
40873
41201
  path14 = __toESM(require("path"));
40874
41202
  import_chalk2 = __toESM(require("chalk"));
40875
- pkgVersion = "0.6.56";
41203
+ pkgVersion = "0.6.58";
40876
41204
  if (pkgVersion === "unknown") {
40877
41205
  try {
40878
41206
  const possiblePaths = [