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/index.js CHANGED
@@ -2379,6 +2379,8 @@ var init_chat_history = __esm({
2379
2379
  lastSeenCounts = /* @__PURE__ */ new Map();
2380
2380
  /** Last seen message hash per agent (deduplication) */
2381
2381
  lastSeenHashes = /* @__PURE__ */ new Map();
2382
+ /** Last seen append-only terminal transcript per agent */
2383
+ lastSeenTerminal = /* @__PURE__ */ new Map();
2382
2384
  rotated = false;
2383
2385
  /**
2384
2386
  * Append new messages to history
@@ -2436,10 +2438,51 @@ var init_chat_history = __esm({
2436
2438
  } catch {
2437
2439
  }
2438
2440
  }
2441
+ appendTerminalHistory(agentType, terminalHistory, sessionTitle, instanceId) {
2442
+ const next = String(terminalHistory || "");
2443
+ if (!next.trim()) return;
2444
+ try {
2445
+ const dedupKey = instanceId ? `${agentType}:${instanceId}:terminal` : `${agentType}:terminal`;
2446
+ const prev = this.lastSeenTerminal.get(dedupKey) || "";
2447
+ if (prev === next) return;
2448
+ let delta = "";
2449
+ if (!prev) {
2450
+ delta = next;
2451
+ } else if (next.startsWith(prev)) {
2452
+ delta = next.slice(prev.length);
2453
+ } else if (prev.includes(next)) {
2454
+ this.lastSeenTerminal.set(dedupKey, next);
2455
+ return;
2456
+ } else {
2457
+ delta = `
2458
+
2459
+ [terminal snapshot reset ${(/* @__PURE__ */ new Date()).toISOString()} | ${sessionTitle || agentType}]
2460
+ ${next}`;
2461
+ }
2462
+ if (!delta) {
2463
+ this.lastSeenTerminal.set(dedupKey, next);
2464
+ return;
2465
+ }
2466
+ const dir = path4.join(HISTORY_DIR, this.sanitize(agentType));
2467
+ fs3.mkdirSync(dir, { recursive: true });
2468
+ const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2469
+ const filePrefix = instanceId ? `${this.sanitize(instanceId)}_` : "";
2470
+ const filePath = path4.join(dir, `${filePrefix}${date5}.terminal.log`);
2471
+ fs3.appendFileSync(filePath, delta, "utf-8");
2472
+ this.lastSeenTerminal.set(dedupKey, next);
2473
+ if (!this.rotated) {
2474
+ this.rotated = true;
2475
+ this.rotateOldFiles().catch(() => {
2476
+ });
2477
+ }
2478
+ } catch {
2479
+ }
2480
+ }
2439
2481
  /** Called when agent session is explicitly changed */
2440
2482
  onSessionChange(agentType) {
2441
2483
  this.lastSeenHashes.delete(agentType);
2442
2484
  this.lastSeenCounts.delete(agentType);
2485
+ this.lastSeenTerminal.delete(`${agentType}:terminal`);
2443
2486
  }
2444
2487
  /** Delete history files older than 30 days */
2445
2488
  async rotateOldFiles() {
@@ -2449,7 +2492,7 @@ var init_chat_history = __esm({
2449
2492
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
2450
2493
  for (const dir of agentDirs) {
2451
2494
  const dirPath = path4.join(HISTORY_DIR, dir.name);
2452
- const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
2495
+ const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
2453
2496
  for (const file2 of files) {
2454
2497
  const filePath = path4.join(dirPath, file2);
2455
2498
  const stat = fs3.statSync(filePath);
@@ -3196,7 +3239,13 @@ async function handleReadChat(h, args) {
3196
3239
  _log(`${provider.category} adapter: ${adapter.cliType}`);
3197
3240
  const status = adapter.getStatus?.();
3198
3241
  if (status) {
3199
- return { success: true, messages: status.messages || [], status: status.status, activeModal: status.activeModal };
3242
+ return {
3243
+ success: true,
3244
+ messages: status.messages || [],
3245
+ status: status.status,
3246
+ activeModal: status.activeModal,
3247
+ terminalHistory: status.terminalHistory || ""
3248
+ };
3200
3249
  }
3201
3250
  }
3202
3251
  return { success: false, error: `${provider.category} adapter not found` };
@@ -16284,6 +16333,68 @@ function shSingleQuote(arg) {
16284
16333
  if (/^[a-zA-Z0-9@%_+=:,./-]+$/.test(arg)) return arg;
16285
16334
  return `'${arg.replace(/'/g, `'\\''`)}'`;
16286
16335
  }
16336
+ function estimatePromptDisplayLines(text, cols = 100) {
16337
+ const normalized = String(text || "").replace(/\r/g, "");
16338
+ if (!normalized) return 1;
16339
+ return normalized.split("\n").reduce((sum, line) => sum + Math.max(1, Math.ceil(Math.max(1, line.length) / cols)), 0);
16340
+ }
16341
+ function extractPromptRetrySnippet(text) {
16342
+ const lines = String(text || "").replace(/\r/g, "").split("\n").map((line) => line.trim()).filter(Boolean);
16343
+ const candidate = lines[lines.length - 1] || lines[0] || "";
16344
+ return candidate.slice(-120);
16345
+ }
16346
+ function normalizePromptText(text) {
16347
+ return String(text || "").replace(/\s+/g, " ").trim();
16348
+ }
16349
+ function compactPromptText(text) {
16350
+ return String(text || "").replace(/\s+/g, "").trim();
16351
+ }
16352
+ function promptLikelyVisible(screenText, promptSnippet) {
16353
+ const snippet = normalizePromptText(promptSnippet);
16354
+ if (!snippet) return false;
16355
+ const normalizedScreen = normalizePromptText(screenText);
16356
+ if (normalizedScreen.includes(snippet)) return true;
16357
+ const compactScreen = compactPromptText(screenText);
16358
+ const compactSnippet = compactPromptText(promptSnippet);
16359
+ if (compactSnippet && compactScreen.includes(compactSnippet)) return true;
16360
+ const tokens = snippet.split(/[^A-Za-z0-9_.:/-]+/).map((token) => token.trim()).filter((token) => token.length >= 4);
16361
+ if (tokens.length === 0) return false;
16362
+ const required2 = Math.min(tokens.length, 3);
16363
+ const matched = tokens.filter(
16364
+ (token) => normalizedScreen.includes(token) || compactScreen.includes(compactPromptText(token))
16365
+ ).length;
16366
+ return matched >= required2;
16367
+ }
16368
+ function splitHistoryLines(text) {
16369
+ return String(text || "").split("\n").map((line) => line.replace(/\s+$/, ""));
16370
+ }
16371
+ function normalizeHistoryLine(line) {
16372
+ return String(line || "").replace(/\s+/g, " ").trim();
16373
+ }
16374
+ function mergeTerminalHistory(existing, snapshot) {
16375
+ const next = String(snapshot || "").trim();
16376
+ if (!next) return existing;
16377
+ const prev = String(existing || "").trim();
16378
+ if (!prev) return next;
16379
+ if (prev === next || prev.endsWith(next)) return prev;
16380
+ const prevLines = splitHistoryLines(prev);
16381
+ const nextLines = splitHistoryLines(next);
16382
+ const prevNorm = prevLines.map(normalizeHistoryLine);
16383
+ const nextNorm = nextLines.map(normalizeHistoryLine);
16384
+ const maxOverlap = Math.min(prevLines.length, nextLines.length);
16385
+ for (let overlap = maxOverlap; overlap >= 1; overlap -= 1) {
16386
+ const prevTail = prevNorm.slice(prevNorm.length - overlap);
16387
+ const nextHead = nextNorm.slice(0, overlap);
16388
+ if (prevTail.every((line, index) => line === nextHead[index])) {
16389
+ return [...prevLines, ...nextLines.slice(overlap)].join("\n").trim();
16390
+ }
16391
+ }
16392
+ const compactPrev = prevNorm.join("\n");
16393
+ const compactNext = nextNorm.join("\n");
16394
+ if (compactPrev.includes(compactNext)) return prev;
16395
+ return `${prev}
16396
+ ${next}`.trim();
16397
+ }
16287
16398
  function parsePatternEntry(x) {
16288
16399
  if (x instanceof RegExp) return x;
16289
16400
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -16360,6 +16471,7 @@ var init_provider_cli_adapter = __esm({
16360
16471
  this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
16361
16472
  this.sendDelayMs = typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0;
16362
16473
  this.sendKey = typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r";
16474
+ this.submitStrategy = provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo";
16363
16475
  this.cliScripts = provider.scripts || {};
16364
16476
  const scriptNames = Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function");
16365
16477
  if (scriptNames.length > 0) {
@@ -16374,6 +16486,7 @@ var init_provider_cli_adapter = __esm({
16374
16486
  provider;
16375
16487
  ptyProcess = null;
16376
16488
  messages = [];
16489
+ committedMessages = [];
16377
16490
  structuredMessages = [];
16378
16491
  currentStatus = "starting";
16379
16492
  onStatusChange = null;
@@ -16403,6 +16516,11 @@ var init_provider_cli_adapter = __esm({
16403
16516
  settleTimer = null;
16404
16517
  settledBuffer = "";
16405
16518
  submitPendingUntil = 0;
16519
+ responseSettleIgnoreUntil = 0;
16520
+ responseEpoch = 0;
16521
+ submitRetryTimer = null;
16522
+ submitRetryUsed = false;
16523
+ submitRetryPromptSnippet = "";
16406
16524
  // Resize redraw suppression
16407
16525
  resizeSuppressUntil = 0;
16408
16526
  // Debug: status transition history
@@ -16415,8 +16533,35 @@ var init_provider_cli_adapter = __esm({
16415
16533
  accumulatedRawBuffer = "";
16416
16534
  /** Current visible terminal screen snapshot */
16417
16535
  terminalScreen = new TerminalScreen(40, 120);
16536
+ /** Rolling append-only terminal transcript built from screen snapshots */
16537
+ terminalHistory = "";
16418
16538
  /** Max accumulated buffer size (last 50KB) */
16419
16539
  static MAX_ACCUMULATED_BUFFER = 5e4;
16540
+ currentTurnScope = null;
16541
+ syncMessageViews() {
16542
+ this.messages = [...this.committedMessages];
16543
+ this.structuredMessages = [...this.committedMessages];
16544
+ }
16545
+ sliceFromOffset(text, start) {
16546
+ if (!text) return "";
16547
+ if (!Number.isFinite(start) || start <= 0) return text;
16548
+ if (start >= text.length) return "";
16549
+ return text.slice(start);
16550
+ }
16551
+ buildParseInput(baseMessages, partialResponse, scope) {
16552
+ const buffer = scope ? this.sliceFromOffset(this.terminalHistory, scope.terminalHistoryStart) || this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
16553
+ const rawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
16554
+ const terminalHistory = scope ? this.sliceFromOffset(this.terminalHistory, scope.terminalHistoryStart) || this.terminalHistory : this.terminalHistory;
16555
+ return {
16556
+ buffer,
16557
+ rawBuffer,
16558
+ recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
16559
+ screenText: this.terminalScreen.getText(),
16560
+ terminalHistory,
16561
+ messages: [...baseMessages],
16562
+ partialResponse
16563
+ };
16564
+ }
16420
16565
  setStatus(status, trigger) {
16421
16566
  const prev = this.currentStatus;
16422
16567
  if (prev === status) return;
@@ -16431,6 +16576,7 @@ var init_provider_cli_adapter = __esm({
16431
16576
  approvalKeys;
16432
16577
  sendDelayMs;
16433
16578
  sendKey;
16579
+ submitStrategy;
16434
16580
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
16435
16581
  setCliScripts(scripts) {
16436
16582
  this.cliScripts = scripts;
@@ -16526,7 +16672,9 @@ var init_provider_cli_adapter = __esm({
16526
16672
  this.startupParseGate = true;
16527
16673
  this.startupBuffer = "";
16528
16674
  this.terminalScreen.reset(40, 120);
16529
- this.ready = true;
16675
+ this.terminalHistory = "";
16676
+ this.currentTurnScope = null;
16677
+ this.ready = false;
16530
16678
  this.setStatus("idle", "pty_ready");
16531
16679
  this.onStatusChange?.();
16532
16680
  }
@@ -16537,6 +16685,7 @@ var init_provider_cli_adapter = __esm({
16537
16685
  this.ptyProcess?.write("\x1B[1;1R");
16538
16686
  }
16539
16687
  this.terminalScreen.write(rawData);
16688
+ this.terminalHistory = mergeTerminalHistory(this.terminalHistory, this.terminalScreen.getText());
16540
16689
  const cleanData = stripAnsi(rawData);
16541
16690
  if (this.isWaitingForResponse && cleanData) {
16542
16691
  this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
@@ -16571,7 +16720,9 @@ var init_provider_cli_adapter = __esm({
16571
16720
  const isReady = scriptStatus === "idle" || elapsed > 8e3 || bufCap;
16572
16721
  if (isReady) {
16573
16722
  this.startupParseGate = false;
16723
+ this.ready = true;
16574
16724
  LOG.info("CLI", `[${this.cliType}] Startup gate end (${elapsed}ms, scriptStatus=${scriptStatus})`);
16725
+ this.onStatusChange?.();
16575
16726
  } else {
16576
16727
  return;
16577
16728
  }
@@ -16580,19 +16731,45 @@ var init_provider_cli_adapter = __esm({
16580
16731
  }
16581
16732
  scheduleSettle() {
16582
16733
  if (this.settleTimer) clearTimeout(this.settleTimer);
16734
+ const settleEpoch = this.responseEpoch;
16583
16735
  const delay = Math.max(
16584
16736
  this.timeouts.outputSettle,
16585
16737
  this.submitPendingUntil > Date.now() ? this.submitPendingUntil - Date.now() + this.timeouts.outputSettle : 0
16586
16738
  );
16587
16739
  this.settleTimer = setTimeout(() => {
16588
16740
  this.settleTimer = null;
16741
+ if (settleEpoch !== this.responseEpoch) return;
16589
16742
  this.settledBuffer = this.recentOutputBuffer;
16590
16743
  this.evaluateSettled();
16591
16744
  }, delay);
16592
16745
  }
16746
+ armApprovalExitTimeout() {
16747
+ if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
16748
+ this.approvalExitTimeout = setTimeout(() => {
16749
+ if (this.currentStatus !== "waiting_approval") return;
16750
+ const tail = this.recentOutputBuffer;
16751
+ const modal = this.runParseApproval(tail);
16752
+ const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
16753
+ if (stillWaiting) {
16754
+ this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
16755
+ this.onStatusChange?.();
16756
+ this.armApprovalExitTimeout();
16757
+ return;
16758
+ }
16759
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
16760
+ this.activeModal = null;
16761
+ this.lastApprovalResolvedAt = Date.now();
16762
+ this.setStatus("idle", "approval_timeout");
16763
+ this.onStatusChange?.();
16764
+ }, 6e4);
16765
+ }
16593
16766
  evaluateSettled() {
16767
+ if (this.submitPendingUntil > Date.now()) return;
16768
+ if (this.responseSettleIgnoreUntil > Date.now()) return;
16594
16769
  const tail = this.settledBuffer;
16595
- const scriptStatus = this.runDetectStatus(tail);
16770
+ const modal = this.runParseApproval(tail);
16771
+ const rawScriptStatus = this.runDetectStatus(tail);
16772
+ const scriptStatus = rawScriptStatus === "waiting_approval" || modal ? "waiting_approval" : rawScriptStatus;
16596
16773
  if (!scriptStatus) return;
16597
16774
  const prevStatus = this.currentStatus;
16598
16775
  if (scriptStatus === "waiting_approval") {
@@ -16600,19 +16777,9 @@ var init_provider_cli_adapter = __esm({
16600
16777
  if (!inCooldown) {
16601
16778
  this.isWaitingForResponse = true;
16602
16779
  this.setStatus("waiting_approval", "script_detect");
16603
- const modal = this.runParseApproval(tail);
16604
16780
  this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
16605
16781
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
16606
- if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
16607
- this.approvalExitTimeout = setTimeout(() => {
16608
- if (this.currentStatus === "waiting_approval") {
16609
- LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
16610
- this.activeModal = null;
16611
- this.lastApprovalResolvedAt = Date.now();
16612
- this.setStatus("idle", "approval_timeout");
16613
- this.onStatusChange?.();
16614
- }
16615
- }, 6e4);
16782
+ this.armApprovalExitTimeout();
16616
16783
  this.onStatusChange?.();
16617
16784
  return;
16618
16785
  }
@@ -16648,7 +16815,12 @@ var init_provider_cli_adapter = __esm({
16648
16815
  this.lastApprovalResolvedAt = Date.now();
16649
16816
  }
16650
16817
  if (this.isWaitingForResponse) {
16651
- this.finishResponse();
16818
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
16819
+ this.idleTimeout = setTimeout(() => {
16820
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
16821
+ this.finishResponse();
16822
+ }
16823
+ }, this.timeouts.idleFinish);
16652
16824
  } else if (prevStatus !== "idle") {
16653
16825
  this.setStatus("idle", "script_detect");
16654
16826
  this.onStatusChange?.();
@@ -16656,6 +16828,9 @@ var init_provider_cli_adapter = __esm({
16656
16828
  }
16657
16829
  }
16658
16830
  finishResponse() {
16831
+ if (this.submitPendingUntil > Date.now()) return;
16832
+ if (this.responseSettleIgnoreUntil > Date.now()) return;
16833
+ this.commitCurrentTranscript();
16659
16834
  if (this.responseTimeout) {
16660
16835
  clearTimeout(this.responseTimeout);
16661
16836
  this.responseTimeout = null;
@@ -16668,12 +16843,67 @@ var init_provider_cli_adapter = __esm({
16668
16843
  clearTimeout(this.approvalExitTimeout);
16669
16844
  this.approvalExitTimeout = null;
16670
16845
  }
16846
+ if (this.submitRetryTimer) {
16847
+ clearTimeout(this.submitRetryTimer);
16848
+ this.submitRetryTimer = null;
16849
+ }
16671
16850
  this.responseBuffer = "";
16672
16851
  this.isWaitingForResponse = false;
16852
+ this.responseSettleIgnoreUntil = 0;
16853
+ this.submitRetryUsed = false;
16854
+ this.submitRetryPromptSnippet = "";
16855
+ this.currentTurnScope = null;
16673
16856
  this.activeModal = null;
16674
16857
  this.setStatus("idle", "response_finished");
16675
16858
  this.onStatusChange?.();
16676
16859
  }
16860
+ commitCurrentTranscript() {
16861
+ const baseMessages = [...this.committedMessages];
16862
+ const parsed = this.parseCurrentTranscript(baseMessages, "", this.currentTurnScope);
16863
+ if (parsed && Array.isArray(parsed.messages) && parsed.messages.length > 0) {
16864
+ const parsedMessages = parsed.messages.filter((m) => m && (m.role === "user" || m.role === "assistant")).map((m) => ({
16865
+ role: m.role,
16866
+ content: typeof m.content === "string" ? m.content : String(m.content || ""),
16867
+ timestamp: m.timestamp
16868
+ }));
16869
+ const latestAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant" && m.content.trim());
16870
+ if (latestAssistant) {
16871
+ LOG.info("CLI", `[${this.cliType}] commitCurrentTranscript parsed assistant len=${latestAssistant.content.length} scopePrompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 120)}`);
16872
+ const nextMessages = [...baseMessages];
16873
+ const last2 = nextMessages[nextMessages.length - 1];
16874
+ if (last2?.role === "assistant") {
16875
+ last2.content = latestAssistant.content;
16876
+ last2.timestamp = latestAssistant.timestamp || last2.timestamp;
16877
+ } else if (last2?.role === "user") {
16878
+ nextMessages.push({
16879
+ role: "assistant",
16880
+ content: latestAssistant.content,
16881
+ timestamp: latestAssistant.timestamp || Date.now()
16882
+ });
16883
+ } else {
16884
+ nextMessages.push({
16885
+ role: "assistant",
16886
+ content: latestAssistant.content,
16887
+ timestamp: latestAssistant.timestamp || Date.now()
16888
+ });
16889
+ }
16890
+ this.committedMessages = nextMessages;
16891
+ this.syncMessageViews();
16892
+ return;
16893
+ }
16894
+ }
16895
+ const fallback = String(this.responseBuffer || "").trim();
16896
+ LOG.info("CLI", `[${this.cliType}] commitCurrentTranscript fallback len=${fallback.length} scopePrompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 120)}`);
16897
+ if (!fallback) return;
16898
+ const last = baseMessages[baseMessages.length - 1];
16899
+ if (last?.role === "assistant") {
16900
+ last.content = fallback;
16901
+ } else {
16902
+ baseMessages.push({ role: "assistant", content: fallback, timestamp: Date.now() });
16903
+ }
16904
+ this.committedMessages = baseMessages;
16905
+ this.syncMessageViews();
16906
+ }
16677
16907
  // ─── Script Execution ──────────────────────────
16678
16908
  runDetectStatus(text) {
16679
16909
  if (!this.cliScripts?.detectStatus) return null;
@@ -16703,24 +16933,12 @@ var init_provider_cli_adapter = __esm({
16703
16933
  }
16704
16934
  // ─── Public API (CliAdapter) ───────────────────
16705
16935
  getStatus() {
16706
- const scriptResult = this.getScriptParsedStatus();
16707
- if (scriptResult) {
16708
- return {
16709
- status: this.currentStatus,
16710
- messages: (scriptResult.messages || []).map((m) => ({
16711
- role: m.role,
16712
- content: m.content,
16713
- timestamp: m.timestamp
16714
- })),
16715
- workingDir: this.workingDir,
16716
- activeModal: this.activeModal
16717
- };
16718
- }
16719
16936
  return {
16720
16937
  status: this.currentStatus,
16721
- messages: [...this.messages],
16938
+ messages: [...this.committedMessages],
16722
16939
  workingDir: this.workingDir,
16723
- activeModal: this.activeModal
16940
+ activeModal: this.activeModal,
16941
+ terminalHistory: this.terminalHistory
16724
16942
  };
16725
16943
  }
16726
16944
  /**
@@ -16728,31 +16946,32 @@ var init_provider_cli_adapter = __esm({
16728
16946
  * Called by command handler / dashboard for rich content rendering.
16729
16947
  */
16730
16948
  getScriptParsedStatus() {
16949
+ const messages = [...this.committedMessages];
16950
+ return {
16951
+ id: "cli_session",
16952
+ status: this.currentStatus,
16953
+ title: this.cliName,
16954
+ terminalHistory: this.terminalHistory,
16955
+ messages: messages.slice(-50).map((message, index) => ({
16956
+ id: `msg_${index}`,
16957
+ role: message.role,
16958
+ content: message.content,
16959
+ timestamp: message.timestamp,
16960
+ index,
16961
+ kind: "standard"
16962
+ })),
16963
+ activeModal: this.activeModal
16964
+ };
16965
+ }
16966
+ parseCurrentTranscript(baseMessages, partialResponse, scope) {
16731
16967
  if (!this.cliScripts?.parseOutput) return null;
16732
16968
  try {
16733
- const input = {
16734
- buffer: this.accumulatedBuffer,
16735
- rawBuffer: this.accumulatedRawBuffer,
16736
- recentBuffer: this.recentOutputBuffer,
16737
- screenText: this.terminalScreen.getText(),
16738
- messages: [...this.structuredMessages.length > 0 ? this.structuredMessages : this.messages],
16739
- partialResponse: this.responseBuffer
16740
- };
16741
- const result = this.cliScripts.parseOutput(input);
16742
- if (result && typeof result === "object") {
16743
- if (Array.isArray(result.messages)) {
16744
- this.structuredMessages = result.messages.map((m) => ({
16745
- role: m.role,
16746
- content: m.content,
16747
- timestamp: m.timestamp
16748
- }));
16749
- }
16750
- return result;
16751
- }
16969
+ const input = this.buildParseInput(baseMessages, partialResponse, scope);
16970
+ return this.cliScripts.parseOutput(input);
16752
16971
  } catch (e) {
16753
16972
  LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
16973
+ return null;
16754
16974
  }
16755
- return null;
16756
16975
  }
16757
16976
  /** Whether this adapter has CLI scripts loaded */
16758
16977
  hasCliScripts() {
@@ -16784,29 +17003,125 @@ ${data.message || ""}`.trim();
16784
17003
  }
16785
17004
  async sendMessage(text) {
16786
17005
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
17006
+ if (this.startupParseGate) {
17007
+ const deadline = Date.now() + 1e4;
17008
+ while (this.startupParseGate && Date.now() < deadline) {
17009
+ await new Promise((resolve8) => setTimeout(resolve8, 50));
17010
+ }
17011
+ }
16787
17012
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
16788
17013
  if (this.isWaitingForResponse) return;
16789
- this.messages.push({ role: "user", content: text, timestamp: Date.now() });
16790
- this.structuredMessages.push({ role: "user", content: text, timestamp: Date.now() });
17014
+ this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
17015
+ this.syncMessageViews();
16791
17016
  this.isWaitingForResponse = true;
16792
17017
  this.responseBuffer = "";
17018
+ this.currentTurnScope = {
17019
+ prompt: text,
17020
+ startedAt: Date.now(),
17021
+ bufferStart: this.accumulatedBuffer.length,
17022
+ rawBufferStart: this.accumulatedRawBuffer.length,
17023
+ terminalHistoryStart: this.terminalHistory.length
17024
+ };
17025
+ 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)}`);
17026
+ this.submitRetryUsed = false;
17027
+ this.submitRetryPromptSnippet = extractPromptRetrySnippet(text);
17028
+ const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet);
17029
+ if (this.submitRetryTimer) {
17030
+ clearTimeout(this.submitRetryTimer);
17031
+ this.submitRetryTimer = null;
17032
+ }
17033
+ const estimatedLines = estimatePromptDisplayLines(text);
17034
+ const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
17035
+ const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
17036
+ const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
17037
+ if (this.settleTimer) {
17038
+ clearTimeout(this.settleTimer);
17039
+ this.settleTimer = null;
17040
+ }
17041
+ this.responseEpoch += 1;
17042
+ this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
16793
17043
  this.setStatus("generating", "sendMessage");
16794
17044
  this.onStatusChange?.();
16795
- this.ptyProcess.write(text);
17045
+ const startResponseTimeout = () => {
17046
+ if (this.responseTimeout) clearTimeout(this.responseTimeout);
17047
+ this.responseTimeout = setTimeout(() => {
17048
+ if (this.isWaitingForResponse) this.finishResponse();
17049
+ }, this.timeouts.maxResponse);
17050
+ };
16796
17051
  const submit = () => {
16797
17052
  if (!this.ptyProcess) return;
16798
17053
  this.submitPendingUntil = 0;
16799
17054
  this.ptyProcess.write(this.sendKey);
16800
- this.responseTimeout = setTimeout(() => {
16801
- if (this.isWaitingForResponse) this.finishResponse();
16802
- }, this.timeouts.maxResponse);
17055
+ const retrySubmitIfStuck = (attempt) => {
17056
+ this.submitRetryTimer = null;
17057
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
17058
+ if (this.currentStatus !== "generating") return;
17059
+ if ((this.responseBuffer || "").trim()) return;
17060
+ const screenText = this.terminalScreen.getText();
17061
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
17062
+ 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;
17063
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
17064
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
17065
+ this.ptyProcess.write(this.sendKey);
17066
+ if (attempt >= 3) {
17067
+ this.submitRetryUsed = true;
17068
+ return;
17069
+ }
17070
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
17071
+ };
17072
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
17073
+ startResponseTimeout();
16803
17074
  };
16804
- if (this.sendDelayMs > 0) {
16805
- this.submitPendingUntil = Date.now() + this.sendDelayMs;
16806
- setTimeout(submit, this.sendDelayMs);
16807
- } else {
16808
- submit();
17075
+ if (this.submitStrategy === "immediate") {
17076
+ this.submitPendingUntil = 0;
17077
+ this.ptyProcess.write(text + this.sendKey);
17078
+ this.submitRetryTimer = setTimeout(() => {
17079
+ this.submitRetryTimer = null;
17080
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
17081
+ if (this.currentStatus !== "generating") return;
17082
+ if ((this.responseBuffer || "").trim()) return;
17083
+ const screenText = this.terminalScreen.getText();
17084
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
17085
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
17086
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
17087
+ this.ptyProcess.write(this.sendKey);
17088
+ this.submitRetryUsed = true;
17089
+ }, retryDelayMs);
17090
+ startResponseTimeout();
17091
+ return;
17092
+ }
17093
+ if (submitDelayMs > 0) {
17094
+ this.submitPendingUntil = Date.now() + submitDelayMs;
16809
17095
  }
17096
+ this.ptyProcess.write(text);
17097
+ const submitStartedAt = Date.now();
17098
+ let lastNormalizedScreen = "";
17099
+ let lastScreenChangeAt = submitStartedAt;
17100
+ const waitForEchoAndSubmit = () => {
17101
+ if (!this.ptyProcess) return;
17102
+ const now = Date.now();
17103
+ const elapsed = now - submitStartedAt;
17104
+ const screenText = this.terminalScreen.getText();
17105
+ const normalizedScreen = normalizePromptText(screenText);
17106
+ if (normalizedScreen !== lastNormalizedScreen) {
17107
+ lastNormalizedScreen = normalizedScreen;
17108
+ lastScreenChangeAt = now;
17109
+ }
17110
+ const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
17111
+ if (echoVisible) {
17112
+ const screenSettled = now - lastScreenChangeAt >= 500;
17113
+ if (elapsed >= submitDelayMs && screenSettled) {
17114
+ submit();
17115
+ return;
17116
+ }
17117
+ }
17118
+ if (elapsed >= maxEchoWaitMs) {
17119
+ submit();
17120
+ return;
17121
+ }
17122
+ setTimeout(waitForEchoAndSubmit, 50);
17123
+ };
17124
+ waitForEchoAndSubmit();
16810
17125
  }
16811
17126
  getPartialResponse() {
16812
17127
  if (!this.isWaitingForResponse) return "";
@@ -16824,6 +17139,10 @@ ${data.message || ""}`.trim();
16824
17139
  clearTimeout(this.approvalExitTimeout);
16825
17140
  this.approvalExitTimeout = null;
16826
17141
  }
17142
+ if (this.submitRetryTimer) {
17143
+ clearTimeout(this.submitRetryTimer);
17144
+ this.submitRetryTimer = null;
17145
+ }
16827
17146
  if (this.ptyProcess) {
16828
17147
  this.ptyProcess.write("");
16829
17148
  setTimeout(() => {
@@ -16841,10 +17160,14 @@ ${data.message || ""}`.trim();
16841
17160
  }
16842
17161
  }
16843
17162
  clearHistory() {
16844
- this.messages = [];
16845
- this.structuredMessages = [];
17163
+ this.committedMessages = [];
17164
+ this.syncMessageViews();
16846
17165
  this.accumulatedBuffer = "";
16847
17166
  this.accumulatedRawBuffer = "";
17167
+ this.terminalHistory = "";
17168
+ this.currentTurnScope = null;
17169
+ this.submitRetryUsed = false;
17170
+ this.submitRetryPromptSnippet = "";
16848
17171
  this.terminalScreen.reset();
16849
17172
  this.onStatusChange?.();
16850
17173
  }
@@ -16858,7 +17181,16 @@ ${data.message || ""}`.trim();
16858
17181
  this.ptyProcess?.write(data);
16859
17182
  }
16860
17183
  resolveModal(buttonIndex) {
16861
- if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
17184
+ if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
17185
+ this.activeModal = null;
17186
+ this.lastApprovalResolvedAt = Date.now();
17187
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
17188
+ if (this.approvalExitTimeout) {
17189
+ clearTimeout(this.approvalExitTimeout);
17190
+ this.approvalExitTimeout = null;
17191
+ }
17192
+ this.setStatus("generating", "approval_resolved");
17193
+ this.onStatusChange?.();
16862
17194
  if (buttonIndex in this.approvalKeys) {
16863
17195
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
16864
17196
  } else {
@@ -16887,9 +17219,12 @@ ${data.message || ""}`.trim();
16887
17219
  spawnAt: this.spawnAt,
16888
17220
  workingDir: this.workingDir,
16889
17221
  messages: this.messages.slice(-20),
17222
+ committedMessages: this.committedMessages.slice(-20),
16890
17223
  structuredMessages: this.structuredMessages.slice(-20),
16891
- messageCount: this.messages.length,
17224
+ messageCount: this.committedMessages.length,
16892
17225
  screenText: this.terminalScreen.getText().slice(-4e3),
17226
+ terminalHistory: this.terminalHistory.slice(-8e3),
17227
+ currentTurnScope: this.currentTurnScope,
16893
17228
  startupBuffer: this.startupBuffer.slice(-4e3),
16894
17229
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
16895
17230
  settledBuffer: this.settledBuffer.slice(-500),
@@ -16900,6 +17235,11 @@ ${data.message || ""}`.trim();
16900
17235
  isWaitingForResponse: this.isWaitingForResponse,
16901
17236
  activeModal: this.activeModal,
16902
17237
  lastApprovalResolvedAt: this.lastApprovalResolvedAt,
17238
+ sendDelayMs: this.sendDelayMs,
17239
+ sendKey: this.sendKey,
17240
+ submitStrategy: this.submitStrategy,
17241
+ submitPendingUntil: this.submitPendingUntil,
17242
+ responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
16903
17243
  resizeSuppressUntil: this.resizeSuppressUntil,
16904
17244
  hasCliScripts: this.hasCliScripts(),
16905
17245
  scriptNames: Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function"),
@@ -16970,31 +17310,12 @@ var init_cli_provider_instance = __esm({
16970
17310
  async onTick() {
16971
17311
  }
16972
17312
  getState() {
16973
- const rawStatus = this.adapter.getStatus();
16974
- const parsedStatus = this.adapter.getScriptParsedStatus();
16975
- const adapterStatus = parsedStatus ? {
16976
- ...rawStatus,
16977
- messages: parsedStatus.messages || rawStatus.messages,
16978
- activeModal: parsedStatus.activeModal || rawStatus.activeModal
16979
- } : rawStatus;
17313
+ const adapterStatus = this.adapter.getStatus();
16980
17314
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
16981
17315
  const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
16982
17316
  const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
16983
17317
  return { ...m, content };
16984
17318
  });
16985
- const partial2 = this.adapter.getPartialResponse();
16986
- const shouldAppendRawPartial = !parsedStatus;
16987
- if (shouldAppendRawPartial && adapterStatus.status === "generating" && partial2) {
16988
- const cleaned = partial2.trim();
16989
- if (cleaned && cleaned !== "(generating...)") {
16990
- recentMessages.push({
16991
- role: "assistant",
16992
- content: (cleaned.length > 8e3 ? cleaned.slice(0, 8e3) + "..." : cleaned) + "...",
16993
- timestamp: Date.now(),
16994
- meta: { streaming: true }
16995
- });
16996
- }
16997
- }
16998
17319
  if (recentMessages.length > 0) {
16999
17320
  const dirName2 = this.workingDir.split("/").filter(Boolean).pop() || "session";
17000
17321
  this.historyWriter.appendNewMessages(
@@ -17004,6 +17325,14 @@ var init_cli_provider_instance = __esm({
17004
17325
  this.instanceId
17005
17326
  );
17006
17327
  }
17328
+ if (adapterStatus.terminalHistory?.trim()) {
17329
+ this.historyWriter.appendTerminalHistory(
17330
+ this.type,
17331
+ adapterStatus.terminalHistory,
17332
+ `${this.provider.name} \xB7 ${dirName}`,
17333
+ this.instanceId
17334
+ );
17335
+ }
17007
17336
  return {
17008
17337
  type: this.type,
17009
17338
  name: this.provider.name,
@@ -17016,6 +17345,7 @@ var init_cli_provider_instance = __esm({
17016
17345
  status: adapterStatus.status,
17017
17346
  messages: recentMessages,
17018
17347
  activeModal: adapterStatus.activeModal,
17348
+ terminalHistory: adapterStatus.terminalHistory,
17019
17349
  inputContent: ""
17020
17350
  },
17021
17351
  workspace: this.workingDir,
@@ -35498,7 +35828,8 @@ async function detectAllVersions(loader, archive) {
35498
35828
  binary: null,
35499
35829
  detectedAt: (/* @__PURE__ */ new Date()).toISOString()
35500
35830
  };
35501
- const versionCommand = provider.versionCommand;
35831
+ const verCmdConfig = provider.versionCommand;
35832
+ const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[currentOs] : verCmdConfig;
35502
35833
  if (provider.category === "ide") {
35503
35834
  const osPaths = provider.paths?.[currentOs] || [];
35504
35835
  const appPath = checkPathExists2(osPaths);
@@ -36983,11 +37314,7 @@ var init_dev_server = __esm({
36983
37314
  return;
36984
37315
  }
36985
37316
  let targetDir;
36986
- if (location === "user") {
36987
- targetDir = this.providerLoader.getUserProviderDir(category, type);
36988
- } else {
36989
- targetDir = this.providerLoader.getBuiltinProviderDir(category, type);
36990
- }
37317
+ targetDir = this.providerLoader.getUserProviderDir(category, type);
36991
37318
  const jsonPath = path12.join(targetDir, "provider.json");
36992
37319
  if (fs10.existsSync(jsonPath)) {
36993
37320
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
@@ -37785,8 +38112,7 @@ var init_dev_server = __esm({
37785
38112
  }
37786
38113
  loadAutoImplReferenceScripts(category, referenceType) {
37787
38114
  if (!referenceType) return {};
37788
- const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
37789
- const refDir = path12.join(builtinDir, category, referenceType);
38115
+ const refDir = this.providerLoader.getUpstreamProviderDir(category, referenceType);
37790
38116
  if (!fs10.existsSync(refDir)) return {};
37791
38117
  const referenceScripts = {};
37792
38118
  const scriptsDir = path12.join(refDir, "scripts");
@@ -38025,7 +38351,7 @@ var init_dev_server = __esm({
38025
38351
  }
38026
38352
  if (model) args.push("--model", model);
38027
38353
  const escapedArgs = args.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
38028
- 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.`;
38354
+ 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.`;
38029
38355
  shellCmd = `${command} ${escapedArgs} "${metaPrompt}"`;
38030
38356
  } else {
38031
38357
  const escapedArgs = baseArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
@@ -38288,6 +38614,8 @@ var init_dev_server = __esm({
38288
38614
  lines.push("5. Do NOT modify `scripts.js` router \u2014 only edit individual `*.js` files");
38289
38615
  lines.push("6. All scripts run in the browser (CDP evaluate) \u2014 use DOM APIs only");
38290
38616
  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.');
38617
+ 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.");
38618
+ lines.push("9. Do NOT delete any files. Implement the logic by replacing the empty stubs.");
38291
38619
  lines.push("");
38292
38620
  lines.push("## Required Return Format");
38293
38621
  lines.push("| Function | Return JSON |");
@@ -40432,7 +40760,7 @@ var init_adhdev_daemon = __esm({
40432
40760
  fs12 = __toESM(require("fs"));
40433
40761
  path14 = __toESM(require("path"));
40434
40762
  import_chalk2 = __toESM(require("chalk"));
40435
- pkgVersion = "0.6.56";
40763
+ pkgVersion = "0.6.58";
40436
40764
  if (pkgVersion === "unknown") {
40437
40765
  try {
40438
40766
  const possiblePaths = [