@adhdev/daemon-core 0.6.57 → 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.
Files changed (37) hide show
  1. package/dist/index.d.ts +17 -0
  2. package/dist/index.js +252 -78
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/providers/_builtin/cli/aider-cli/scripts/1.0/parse_output.js +51 -3
  6. package/providers/_builtin/cli/claude-cli/provider.json +18 -6
  7. package/providers/_builtin/cli/claude-cli/scripts/1.0/detect_status.js +68 -16
  8. package/providers/_builtin/cli/claude-cli/scripts/1.0/parse_approval.js +81 -22
  9. package/providers/_builtin/cli/claude-cli/scripts/1.0/parse_output.js +347 -94
  10. package/providers/_builtin/cli/codex-cli/provider.json +2 -0
  11. package/providers/_builtin/cli/codex-cli/scripts/1.0/detect_status.js +44 -10
  12. package/providers/_builtin/cli/codex-cli/scripts/1.0/parse_approval.js +83 -7
  13. package/providers/_builtin/cli/codex-cli/scripts/1.0/parse_output.js +501 -47
  14. package/providers/_builtin/cli/cursor-cli/scripts/1.0/parse_output.js +1 -1
  15. package/providers/_builtin/cli/github-copilot-cli/scripts/1.0/parse_output.js +1 -1
  16. package/providers/_builtin/cli/goose-cli/scripts/1.0/parse_output.js +1 -1
  17. package/providers/_builtin/cli/opencode-cli/scripts/1.0/parse_output.js +1 -1
  18. package/providers/_builtin/ide/vscode/provider.json +5 -1
  19. package/providers/_builtin/ide/vscode/scripts/1.0/focus_editor.js +1 -0
  20. package/providers/_builtin/ide/vscode/scripts/1.0/list_models.js +1 -0
  21. package/providers/_builtin/ide/vscode/scripts/1.0/list_sessions.js +1 -0
  22. package/providers/_builtin/ide/vscode/scripts/1.0/new_session.js +1 -0
  23. package/providers/_builtin/ide/vscode/scripts/1.0/open_panel.js +1 -0
  24. package/providers/_builtin/ide/vscode/scripts/1.0/read_chat.js +1 -0
  25. package/providers/_builtin/ide/vscode/scripts/1.0/resolve_action.js +1 -0
  26. package/providers/_builtin/ide/vscode/scripts/1.0/scripts.js +25 -0
  27. package/providers/_builtin/ide/vscode/scripts/1.0/send_message.js +1 -0
  28. package/providers/_builtin/ide/vscode/scripts/1.0/set_model.js +1 -0
  29. package/providers/_builtin/ide/vscode/scripts/1.0/switch_session.js +1 -0
  30. package/providers/_builtin/registry.json +1 -1
  31. package/src/cli-adapters/provider-cli-adapter.ts +223 -48
  32. package/src/commands/chat-commands.ts +7 -1
  33. package/src/config/chat-history.ts +53 -1
  34. package/src/daemon/dev-server.ts +7 -9
  35. package/src/providers/cli-provider-instance.ts +10 -23
  36. package/src/providers/provider-instance.ts +1 -0
  37. package/src/providers/version-archive.ts +4 -1
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ interface ActiveChatData {
18
18
  message: string;
19
19
  buttons: string[];
20
20
  } | null;
21
+ terminalHistory?: string;
21
22
  inputContent?: string;
22
23
  }
23
24
  /** Standardized error reasons across all provider categories */
@@ -1653,6 +1654,8 @@ declare class ChatHistoryWriter {
1653
1654
  private lastSeenCounts;
1654
1655
  /** Last seen message hash per agent (deduplication) */
1655
1656
  private lastSeenHashes;
1657
+ /** Last seen append-only terminal transcript per agent */
1658
+ private lastSeenTerminal;
1656
1659
  private rotated;
1657
1660
  /**
1658
1661
  * Append new messages to history
@@ -1667,6 +1670,7 @@ declare class ChatHistoryWriter {
1667
1670
  content: string;
1668
1671
  receivedAt?: number;
1669
1672
  }>, sessionTitle?: string, instanceId?: string): void;
1673
+ appendTerminalHistory(agentType: string, terminalHistory: string, sessionTitle?: string, instanceId?: string): void;
1670
1674
  /** Called when agent session is explicitly changed */
1671
1675
  onSessionChange(agentType: string): void;
1672
1676
  /** Delete history files older than 30 days */
@@ -2563,6 +2567,7 @@ interface CliSessionStatus {
2563
2567
  message: string;
2564
2568
  buttons: string[];
2565
2569
  } | null;
2570
+ terminalHistory?: string;
2566
2571
  }
2567
2572
  /**
2568
2573
  * CLI Script Functions.
@@ -2597,6 +2602,7 @@ interface CliScriptInput {
2597
2602
  rawBuffer: string;
2598
2603
  recentBuffer: string;
2599
2604
  screenText: string;
2605
+ terminalHistory?: string;
2600
2606
  messages: CliChatMessage[];
2601
2607
  partialResponse: string;
2602
2608
  }
@@ -2607,6 +2613,7 @@ interface CliProviderModule {
2607
2613
  binary: string;
2608
2614
  sendDelayMs?: number;
2609
2615
  sendKey?: string;
2616
+ submitStrategy?: 'wait_for_echo' | 'immediate';
2610
2617
  spawn: {
2611
2618
  command: string;
2612
2619
  args: string[];
@@ -2640,6 +2647,7 @@ declare class ProviderCliAdapter implements CliAdapter {
2640
2647
  private provider;
2641
2648
  private ptyProcess;
2642
2649
  private messages;
2650
+ private committedMessages;
2643
2651
  private structuredMessages;
2644
2652
  private currentStatus;
2645
2653
  private onStatusChange;
@@ -2678,13 +2686,20 @@ declare class ProviderCliAdapter implements CliAdapter {
2678
2686
  private accumulatedRawBuffer;
2679
2687
  /** Current visible terminal screen snapshot */
2680
2688
  private terminalScreen;
2689
+ /** Rolling append-only terminal transcript built from screen snapshots */
2690
+ private terminalHistory;
2681
2691
  /** Max accumulated buffer size (last 50KB) */
2682
2692
  private static readonly MAX_ACCUMULATED_BUFFER;
2693
+ private currentTurnScope;
2694
+ private syncMessageViews;
2695
+ private sliceFromOffset;
2696
+ private buildParseInput;
2683
2697
  private setStatus;
2684
2698
  private readonly timeouts;
2685
2699
  private readonly approvalKeys;
2686
2700
  private readonly sendDelayMs;
2687
2701
  private readonly sendKey;
2702
+ private readonly submitStrategy;
2688
2703
  constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[]);
2689
2704
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
2690
2705
  setCliScripts(scripts: CliScripts): void;
@@ -2697,6 +2712,7 @@ declare class ProviderCliAdapter implements CliAdapter {
2697
2712
  private armApprovalExitTimeout;
2698
2713
  private evaluateSettled;
2699
2714
  private finishResponse;
2715
+ private commitCurrentTranscript;
2700
2716
  private runDetectStatus;
2701
2717
  private runParseApproval;
2702
2718
  getStatus(): CliSessionStatus;
@@ -2705,6 +2721,7 @@ declare class ProviderCliAdapter implements CliAdapter {
2705
2721
  * Called by command handler / dashboard for rich content rendering.
2706
2722
  */
2707
2723
  getScriptParsedStatus(): any;
2724
+ private parseCurrentTranscript;
2708
2725
  /** Whether this adapter has CLI scripts loaded */
2709
2726
  hasCliScripts(): boolean;
2710
2727
  /**
package/dist/index.js CHANGED
@@ -750,6 +750,36 @@ function promptLikelyVisible(screenText, promptSnippet) {
750
750
  ).length;
751
751
  return matched >= required;
752
752
  }
753
+ function splitHistoryLines(text) {
754
+ return String(text || "").split("\n").map((line) => line.replace(/\s+$/, ""));
755
+ }
756
+ function normalizeHistoryLine(line) {
757
+ return String(line || "").replace(/\s+/g, " ").trim();
758
+ }
759
+ function mergeTerminalHistory(existing, snapshot) {
760
+ const next = String(snapshot || "").trim();
761
+ if (!next) return existing;
762
+ const prev = String(existing || "").trim();
763
+ if (!prev) return next;
764
+ if (prev === next || prev.endsWith(next)) return prev;
765
+ const prevLines = splitHistoryLines(prev);
766
+ const nextLines = splitHistoryLines(next);
767
+ const prevNorm = prevLines.map(normalizeHistoryLine);
768
+ const nextNorm = nextLines.map(normalizeHistoryLine);
769
+ const maxOverlap = Math.min(prevLines.length, nextLines.length);
770
+ for (let overlap = maxOverlap; overlap >= 1; overlap -= 1) {
771
+ const prevTail = prevNorm.slice(prevNorm.length - overlap);
772
+ const nextHead = nextNorm.slice(0, overlap);
773
+ if (prevTail.every((line, index) => line === nextHead[index])) {
774
+ return [...prevLines, ...nextLines.slice(overlap)].join("\n").trim();
775
+ }
776
+ }
777
+ const compactPrev = prevNorm.join("\n");
778
+ const compactNext = nextNorm.join("\n");
779
+ if (compactPrev.includes(compactNext)) return prev;
780
+ return `${prev}
781
+ ${next}`.trim();
782
+ }
753
783
  function parsePatternEntry(x) {
754
784
  if (x instanceof RegExp) return x;
755
785
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -826,6 +856,7 @@ var init_provider_cli_adapter = __esm({
826
856
  this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
827
857
  this.sendDelayMs = typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0;
828
858
  this.sendKey = typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r";
859
+ this.submitStrategy = provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo";
829
860
  this.cliScripts = provider.scripts || {};
830
861
  const scriptNames = Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function");
831
862
  if (scriptNames.length > 0) {
@@ -840,6 +871,7 @@ var init_provider_cli_adapter = __esm({
840
871
  provider;
841
872
  ptyProcess = null;
842
873
  messages = [];
874
+ committedMessages = [];
843
875
  structuredMessages = [];
844
876
  currentStatus = "starting";
845
877
  onStatusChange = null;
@@ -886,8 +918,35 @@ var init_provider_cli_adapter = __esm({
886
918
  accumulatedRawBuffer = "";
887
919
  /** Current visible terminal screen snapshot */
888
920
  terminalScreen = new TerminalScreen(40, 120);
921
+ /** Rolling append-only terminal transcript built from screen snapshots */
922
+ terminalHistory = "";
889
923
  /** Max accumulated buffer size (last 50KB) */
890
924
  static MAX_ACCUMULATED_BUFFER = 5e4;
925
+ currentTurnScope = null;
926
+ syncMessageViews() {
927
+ this.messages = [...this.committedMessages];
928
+ this.structuredMessages = [...this.committedMessages];
929
+ }
930
+ sliceFromOffset(text, start) {
931
+ if (!text) return "";
932
+ if (!Number.isFinite(start) || start <= 0) return text;
933
+ if (start >= text.length) return "";
934
+ return text.slice(start);
935
+ }
936
+ buildParseInput(baseMessages, partialResponse, scope) {
937
+ const buffer = scope ? this.sliceFromOffset(this.terminalHistory, scope.terminalHistoryStart) || this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
938
+ const rawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
939
+ const terminalHistory = scope ? this.sliceFromOffset(this.terminalHistory, scope.terminalHistoryStart) || this.terminalHistory : this.terminalHistory;
940
+ return {
941
+ buffer,
942
+ rawBuffer,
943
+ recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
944
+ screenText: this.terminalScreen.getText(),
945
+ terminalHistory,
946
+ messages: [...baseMessages],
947
+ partialResponse
948
+ };
949
+ }
891
950
  setStatus(status, trigger) {
892
951
  const prev = this.currentStatus;
893
952
  if (prev === status) return;
@@ -902,6 +961,7 @@ var init_provider_cli_adapter = __esm({
902
961
  approvalKeys;
903
962
  sendDelayMs;
904
963
  sendKey;
964
+ submitStrategy;
905
965
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
906
966
  setCliScripts(scripts) {
907
967
  this.cliScripts = scripts;
@@ -997,6 +1057,8 @@ var init_provider_cli_adapter = __esm({
997
1057
  this.startupParseGate = true;
998
1058
  this.startupBuffer = "";
999
1059
  this.terminalScreen.reset(40, 120);
1060
+ this.terminalHistory = "";
1061
+ this.currentTurnScope = null;
1000
1062
  this.ready = false;
1001
1063
  this.setStatus("idle", "pty_ready");
1002
1064
  this.onStatusChange?.();
@@ -1008,6 +1070,7 @@ var init_provider_cli_adapter = __esm({
1008
1070
  this.ptyProcess?.write("\x1B[1;1R");
1009
1071
  }
1010
1072
  this.terminalScreen.write(rawData);
1073
+ this.terminalHistory = mergeTerminalHistory(this.terminalHistory, this.terminalScreen.getText());
1011
1074
  const cleanData = stripAnsi(rawData);
1012
1075
  if (this.isWaitingForResponse && cleanData) {
1013
1076
  this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
@@ -1152,6 +1215,7 @@ var init_provider_cli_adapter = __esm({
1152
1215
  finishResponse() {
1153
1216
  if (this.submitPendingUntil > Date.now()) return;
1154
1217
  if (this.responseSettleIgnoreUntil > Date.now()) return;
1218
+ this.commitCurrentTranscript();
1155
1219
  if (this.responseTimeout) {
1156
1220
  clearTimeout(this.responseTimeout);
1157
1221
  this.responseTimeout = null;
@@ -1173,10 +1237,58 @@ var init_provider_cli_adapter = __esm({
1173
1237
  this.responseSettleIgnoreUntil = 0;
1174
1238
  this.submitRetryUsed = false;
1175
1239
  this.submitRetryPromptSnippet = "";
1240
+ this.currentTurnScope = null;
1176
1241
  this.activeModal = null;
1177
1242
  this.setStatus("idle", "response_finished");
1178
1243
  this.onStatusChange?.();
1179
1244
  }
1245
+ commitCurrentTranscript() {
1246
+ const baseMessages = [...this.committedMessages];
1247
+ const parsed = this.parseCurrentTranscript(baseMessages, "", this.currentTurnScope);
1248
+ if (parsed && Array.isArray(parsed.messages) && parsed.messages.length > 0) {
1249
+ const parsedMessages = parsed.messages.filter((m) => m && (m.role === "user" || m.role === "assistant")).map((m) => ({
1250
+ role: m.role,
1251
+ content: typeof m.content === "string" ? m.content : String(m.content || ""),
1252
+ timestamp: m.timestamp
1253
+ }));
1254
+ const latestAssistant = [...parsedMessages].reverse().find((m) => m.role === "assistant" && m.content.trim());
1255
+ if (latestAssistant) {
1256
+ LOG.info("CLI", `[${this.cliType}] commitCurrentTranscript parsed assistant len=${latestAssistant.content.length} scopePrompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 120)}`);
1257
+ const nextMessages = [...baseMessages];
1258
+ const last2 = nextMessages[nextMessages.length - 1];
1259
+ if (last2?.role === "assistant") {
1260
+ last2.content = latestAssistant.content;
1261
+ last2.timestamp = latestAssistant.timestamp || last2.timestamp;
1262
+ } else if (last2?.role === "user") {
1263
+ nextMessages.push({
1264
+ role: "assistant",
1265
+ content: latestAssistant.content,
1266
+ timestamp: latestAssistant.timestamp || Date.now()
1267
+ });
1268
+ } else {
1269
+ nextMessages.push({
1270
+ role: "assistant",
1271
+ content: latestAssistant.content,
1272
+ timestamp: latestAssistant.timestamp || Date.now()
1273
+ });
1274
+ }
1275
+ this.committedMessages = nextMessages;
1276
+ this.syncMessageViews();
1277
+ return;
1278
+ }
1279
+ }
1280
+ const fallback = String(this.responseBuffer || "").trim();
1281
+ LOG.info("CLI", `[${this.cliType}] commitCurrentTranscript fallback len=${fallback.length} scopePrompt=${JSON.stringify(this.currentTurnScope?.prompt || "").slice(0, 120)}`);
1282
+ if (!fallback) return;
1283
+ const last = baseMessages[baseMessages.length - 1];
1284
+ if (last?.role === "assistant") {
1285
+ last.content = fallback;
1286
+ } else {
1287
+ baseMessages.push({ role: "assistant", content: fallback, timestamp: Date.now() });
1288
+ }
1289
+ this.committedMessages = baseMessages;
1290
+ this.syncMessageViews();
1291
+ }
1180
1292
  // ─── Script Execution ──────────────────────────
1181
1293
  runDetectStatus(text) {
1182
1294
  if (!this.cliScripts?.detectStatus) return null;
@@ -1206,24 +1318,12 @@ var init_provider_cli_adapter = __esm({
1206
1318
  }
1207
1319
  // ─── Public API (CliAdapter) ───────────────────
1208
1320
  getStatus() {
1209
- const scriptResult = this.getScriptParsedStatus();
1210
- if (scriptResult) {
1211
- return {
1212
- status: this.currentStatus,
1213
- messages: (scriptResult.messages || []).map((m) => ({
1214
- role: m.role,
1215
- content: m.content,
1216
- timestamp: m.timestamp
1217
- })),
1218
- workingDir: this.workingDir,
1219
- activeModal: this.activeModal
1220
- };
1221
- }
1222
1321
  return {
1223
1322
  status: this.currentStatus,
1224
- messages: [...this.messages],
1323
+ messages: [...this.committedMessages],
1225
1324
  workingDir: this.workingDir,
1226
- activeModal: this.activeModal
1325
+ activeModal: this.activeModal,
1326
+ terminalHistory: this.terminalHistory
1227
1327
  };
1228
1328
  }
1229
1329
  /**
@@ -1231,31 +1331,32 @@ var init_provider_cli_adapter = __esm({
1231
1331
  * Called by command handler / dashboard for rich content rendering.
1232
1332
  */
1233
1333
  getScriptParsedStatus() {
1334
+ const messages = [...this.committedMessages];
1335
+ return {
1336
+ id: "cli_session",
1337
+ status: this.currentStatus,
1338
+ title: this.cliName,
1339
+ terminalHistory: this.terminalHistory,
1340
+ messages: messages.slice(-50).map((message, index) => ({
1341
+ id: `msg_${index}`,
1342
+ role: message.role,
1343
+ content: message.content,
1344
+ timestamp: message.timestamp,
1345
+ index,
1346
+ kind: "standard"
1347
+ })),
1348
+ activeModal: this.activeModal
1349
+ };
1350
+ }
1351
+ parseCurrentTranscript(baseMessages, partialResponse, scope) {
1234
1352
  if (!this.cliScripts?.parseOutput) return null;
1235
1353
  try {
1236
- const input = {
1237
- buffer: this.accumulatedBuffer,
1238
- rawBuffer: this.accumulatedRawBuffer,
1239
- recentBuffer: this.recentOutputBuffer,
1240
- screenText: this.terminalScreen.getText(),
1241
- messages: [...this.structuredMessages.length > 0 ? this.structuredMessages : this.messages],
1242
- partialResponse: this.responseBuffer
1243
- };
1244
- const result = this.cliScripts.parseOutput(input);
1245
- if (result && typeof result === "object") {
1246
- if (Array.isArray(result.messages)) {
1247
- this.structuredMessages = result.messages.map((m) => ({
1248
- role: m.role,
1249
- content: m.content,
1250
- timestamp: m.timestamp
1251
- }));
1252
- }
1253
- return result;
1254
- }
1354
+ const input = this.buildParseInput(baseMessages, partialResponse, scope);
1355
+ return this.cliScripts.parseOutput(input);
1255
1356
  } catch (e) {
1256
1357
  LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
1358
+ return null;
1257
1359
  }
1258
- return null;
1259
1360
  }
1260
1361
  /** Whether this adapter has CLI scripts loaded */
1261
1362
  hasCliScripts() {
@@ -1295,10 +1396,18 @@ ${data.message || ""}`.trim();
1295
1396
  }
1296
1397
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
1297
1398
  if (this.isWaitingForResponse) return;
1298
- this.messages.push({ role: "user", content: text, timestamp: Date.now() });
1299
- this.structuredMessages.push({ role: "user", content: text, timestamp: Date.now() });
1399
+ this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
1400
+ this.syncMessageViews();
1300
1401
  this.isWaitingForResponse = true;
1301
1402
  this.responseBuffer = "";
1403
+ this.currentTurnScope = {
1404
+ prompt: text,
1405
+ startedAt: Date.now(),
1406
+ bufferStart: this.accumulatedBuffer.length,
1407
+ rawBufferStart: this.accumulatedRawBuffer.length,
1408
+ terminalHistoryStart: this.terminalHistory.length
1409
+ };
1410
+ 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)}`);
1302
1411
  this.submitRetryUsed = false;
1303
1412
  this.submitRetryPromptSnippet = extractPromptRetrySnippet(text);
1304
1413
  const normalizedPromptSnippet = normalizePromptText(this.submitRetryPromptSnippet);
@@ -1318,10 +1427,12 @@ ${data.message || ""}`.trim();
1318
1427
  this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
1319
1428
  this.setStatus("generating", "sendMessage");
1320
1429
  this.onStatusChange?.();
1321
- if (submitDelayMs > 0) {
1322
- this.submitPendingUntil = Date.now() + submitDelayMs;
1323
- }
1324
- this.ptyProcess.write(text);
1430
+ const startResponseTimeout = () => {
1431
+ if (this.responseTimeout) clearTimeout(this.responseTimeout);
1432
+ this.responseTimeout = setTimeout(() => {
1433
+ if (this.isWaitingForResponse) this.finishResponse();
1434
+ }, this.timeouts.maxResponse);
1435
+ };
1325
1436
  const submit = () => {
1326
1437
  if (!this.ptyProcess) return;
1327
1438
  this.submitPendingUntil = 0;
@@ -1344,10 +1455,30 @@ ${data.message || ""}`.trim();
1344
1455
  this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
1345
1456
  };
1346
1457
  this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
1347
- this.responseTimeout = setTimeout(() => {
1348
- if (this.isWaitingForResponse) this.finishResponse();
1349
- }, this.timeouts.maxResponse);
1458
+ startResponseTimeout();
1350
1459
  };
1460
+ if (this.submitStrategy === "immediate") {
1461
+ this.submitPendingUntil = 0;
1462
+ this.ptyProcess.write(text + this.sendKey);
1463
+ this.submitRetryTimer = setTimeout(() => {
1464
+ this.submitRetryTimer = null;
1465
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
1466
+ if (this.currentStatus !== "generating") return;
1467
+ if ((this.responseBuffer || "").trim()) return;
1468
+ const screenText = this.terminalScreen.getText();
1469
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
1470
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
1471
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
1472
+ this.ptyProcess.write(this.sendKey);
1473
+ this.submitRetryUsed = true;
1474
+ }, retryDelayMs);
1475
+ startResponseTimeout();
1476
+ return;
1477
+ }
1478
+ if (submitDelayMs > 0) {
1479
+ this.submitPendingUntil = Date.now() + submitDelayMs;
1480
+ }
1481
+ this.ptyProcess.write(text);
1351
1482
  const submitStartedAt = Date.now();
1352
1483
  let lastNormalizedScreen = "";
1353
1484
  let lastScreenChangeAt = submitStartedAt;
@@ -1414,10 +1545,12 @@ ${data.message || ""}`.trim();
1414
1545
  }
1415
1546
  }
1416
1547
  clearHistory() {
1417
- this.messages = [];
1418
- this.structuredMessages = [];
1548
+ this.committedMessages = [];
1549
+ this.syncMessageViews();
1419
1550
  this.accumulatedBuffer = "";
1420
1551
  this.accumulatedRawBuffer = "";
1552
+ this.terminalHistory = "";
1553
+ this.currentTurnScope = null;
1421
1554
  this.submitRetryUsed = false;
1422
1555
  this.submitRetryPromptSnippet = "";
1423
1556
  this.terminalScreen.reset();
@@ -1471,9 +1604,12 @@ ${data.message || ""}`.trim();
1471
1604
  spawnAt: this.spawnAt,
1472
1605
  workingDir: this.workingDir,
1473
1606
  messages: this.messages.slice(-20),
1607
+ committedMessages: this.committedMessages.slice(-20),
1474
1608
  structuredMessages: this.structuredMessages.slice(-20),
1475
- messageCount: this.messages.length,
1609
+ messageCount: this.committedMessages.length,
1476
1610
  screenText: this.terminalScreen.getText().slice(-4e3),
1611
+ terminalHistory: this.terminalHistory.slice(-8e3),
1612
+ currentTurnScope: this.currentTurnScope,
1477
1613
  startupBuffer: this.startupBuffer.slice(-4e3),
1478
1614
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
1479
1615
  settledBuffer: this.settledBuffer.slice(-500),
@@ -1486,6 +1622,7 @@ ${data.message || ""}`.trim();
1486
1622
  lastApprovalResolvedAt: this.lastApprovalResolvedAt,
1487
1623
  sendDelayMs: this.sendDelayMs,
1488
1624
  sendKey: this.sendKey,
1625
+ submitStrategy: this.submitStrategy,
1489
1626
  submitPendingUntil: this.submitPendingUntil,
1490
1627
  responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
1491
1628
  resizeSuppressUntil: this.resizeSuppressUntil,
@@ -3278,6 +3415,8 @@ var ChatHistoryWriter = class {
3278
3415
  lastSeenCounts = /* @__PURE__ */ new Map();
3279
3416
  /** Last seen message hash per agent (deduplication) */
3280
3417
  lastSeenHashes = /* @__PURE__ */ new Map();
3418
+ /** Last seen append-only terminal transcript per agent */
3419
+ lastSeenTerminal = /* @__PURE__ */ new Map();
3281
3420
  rotated = false;
3282
3421
  /**
3283
3422
  * Append new messages to history
@@ -3335,10 +3474,51 @@ var ChatHistoryWriter = class {
3335
3474
  } catch {
3336
3475
  }
3337
3476
  }
3477
+ appendTerminalHistory(agentType, terminalHistory, sessionTitle, instanceId) {
3478
+ const next = String(terminalHistory || "");
3479
+ if (!next.trim()) return;
3480
+ try {
3481
+ const dedupKey = instanceId ? `${agentType}:${instanceId}:terminal` : `${agentType}:terminal`;
3482
+ const prev = this.lastSeenTerminal.get(dedupKey) || "";
3483
+ if (prev === next) return;
3484
+ let delta = "";
3485
+ if (!prev) {
3486
+ delta = next;
3487
+ } else if (next.startsWith(prev)) {
3488
+ delta = next.slice(prev.length);
3489
+ } else if (prev.includes(next)) {
3490
+ this.lastSeenTerminal.set(dedupKey, next);
3491
+ return;
3492
+ } else {
3493
+ delta = `
3494
+
3495
+ [terminal snapshot reset ${(/* @__PURE__ */ new Date()).toISOString()} | ${sessionTitle || agentType}]
3496
+ ${next}`;
3497
+ }
3498
+ if (!delta) {
3499
+ this.lastSeenTerminal.set(dedupKey, next);
3500
+ return;
3501
+ }
3502
+ const dir = path4.join(HISTORY_DIR, this.sanitize(agentType));
3503
+ fs3.mkdirSync(dir, { recursive: true });
3504
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3505
+ const filePrefix = instanceId ? `${this.sanitize(instanceId)}_` : "";
3506
+ const filePath = path4.join(dir, `${filePrefix}${date}.terminal.log`);
3507
+ fs3.appendFileSync(filePath, delta, "utf-8");
3508
+ this.lastSeenTerminal.set(dedupKey, next);
3509
+ if (!this.rotated) {
3510
+ this.rotated = true;
3511
+ this.rotateOldFiles().catch(() => {
3512
+ });
3513
+ }
3514
+ } catch {
3515
+ }
3516
+ }
3338
3517
  /** Called when agent session is explicitly changed */
3339
3518
  onSessionChange(agentType) {
3340
3519
  this.lastSeenHashes.delete(agentType);
3341
3520
  this.lastSeenCounts.delete(agentType);
3521
+ this.lastSeenTerminal.delete(`${agentType}:terminal`);
3342
3522
  }
3343
3523
  /** Delete history files older than 30 days */
3344
3524
  async rotateOldFiles() {
@@ -3348,7 +3528,7 @@ var ChatHistoryWriter = class {
3348
3528
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
3349
3529
  for (const dir of agentDirs) {
3350
3530
  const dirPath = path4.join(HISTORY_DIR, dir.name);
3351
- const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
3531
+ const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
3352
3532
  for (const file of files) {
3353
3533
  const filePath = path4.join(dirPath, file);
3354
3534
  const stat = fs3.statSync(filePath);
@@ -4256,7 +4436,13 @@ async function handleReadChat(h, args) {
4256
4436
  _log(`${provider.category} adapter: ${adapter.cliType}`);
4257
4437
  const status = adapter.getStatus?.();
4258
4438
  if (status) {
4259
- return { success: true, messages: status.messages || [], status: status.status, activeModal: status.activeModal };
4439
+ return {
4440
+ success: true,
4441
+ messages: status.messages || [],
4442
+ status: status.status,
4443
+ activeModal: status.activeModal,
4444
+ terminalHistory: status.terminalHistory || ""
4445
+ };
4260
4446
  }
4261
4447
  }
4262
4448
  return { success: false, error: `${provider.category} adapter not found` };
@@ -8136,31 +8322,12 @@ var CliProviderInstance = class {
8136
8322
  async onTick() {
8137
8323
  }
8138
8324
  getState() {
8139
- const rawStatus = this.adapter.getStatus();
8140
- const parsedStatus = this.adapter.getScriptParsedStatus();
8141
- const adapterStatus = parsedStatus ? {
8142
- ...rawStatus,
8143
- messages: parsedStatus.messages || rawStatus.messages,
8144
- activeModal: parsedStatus.activeModal || rawStatus.activeModal
8145
- } : rawStatus;
8325
+ const adapterStatus = this.adapter.getStatus();
8146
8326
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
8147
8327
  const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
8148
8328
  const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
8149
8329
  return { ...m, content };
8150
8330
  });
8151
- const partial = this.adapter.getPartialResponse();
8152
- const shouldAppendRawPartial = !parsedStatus;
8153
- if (shouldAppendRawPartial && adapterStatus.status === "generating" && partial) {
8154
- const cleaned = partial.trim();
8155
- if (cleaned && cleaned !== "(generating...)") {
8156
- recentMessages.push({
8157
- role: "assistant",
8158
- content: (cleaned.length > 8e3 ? cleaned.slice(0, 8e3) + "..." : cleaned) + "...",
8159
- timestamp: Date.now(),
8160
- meta: { streaming: true }
8161
- });
8162
- }
8163
- }
8164
8331
  if (recentMessages.length > 0) {
8165
8332
  const dirName2 = this.workingDir.split("/").filter(Boolean).pop() || "session";
8166
8333
  this.historyWriter.appendNewMessages(
@@ -8170,6 +8337,14 @@ var CliProviderInstance = class {
8170
8337
  this.instanceId
8171
8338
  );
8172
8339
  }
8340
+ if (adapterStatus.terminalHistory?.trim()) {
8341
+ this.historyWriter.appendTerminalHistory(
8342
+ this.type,
8343
+ adapterStatus.terminalHistory,
8344
+ `${this.provider.name} \xB7 ${dirName}`,
8345
+ this.instanceId
8346
+ );
8347
+ }
8173
8348
  return {
8174
8349
  type: this.type,
8175
8350
  name: this.provider.name,
@@ -8182,6 +8357,7 @@ var CliProviderInstance = class {
8182
8357
  status: adapterStatus.status,
8183
8358
  messages: recentMessages,
8184
8359
  activeModal: adapterStatus.activeModal,
8360
+ terminalHistory: adapterStatus.terminalHistory,
8185
8361
  inputContent: ""
8186
8362
  },
8187
8363
  workspace: this.workingDir,
@@ -10342,7 +10518,8 @@ async function detectAllVersions(loader, archive) {
10342
10518
  binary: null,
10343
10519
  detectedAt: (/* @__PURE__ */ new Date()).toISOString()
10344
10520
  };
10345
- const versionCommand = provider.versionCommand;
10521
+ const verCmdConfig = provider.versionCommand;
10522
+ const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[currentOs] : verCmdConfig;
10346
10523
  if (provider.category === "ide") {
10347
10524
  const osPaths = provider.paths?.[currentOs] || [];
10348
10525
  const appPath = checkPathExists2(osPaths);
@@ -11754,11 +11931,7 @@ var DevServer = class _DevServer {
11754
11931
  return;
11755
11932
  }
11756
11933
  let targetDir;
11757
- if (location === "user") {
11758
- targetDir = this.providerLoader.getUserProviderDir(category, type);
11759
- } else {
11760
- targetDir = this.providerLoader.getBuiltinProviderDir(category, type);
11761
- }
11934
+ targetDir = this.providerLoader.getUserProviderDir(category, type);
11762
11935
  const jsonPath = path12.join(targetDir, "provider.json");
11763
11936
  if (fs9.existsSync(jsonPath)) {
11764
11937
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
@@ -12556,8 +12729,7 @@ var DevServer = class _DevServer {
12556
12729
  }
12557
12730
  loadAutoImplReferenceScripts(category, referenceType) {
12558
12731
  if (!referenceType) return {};
12559
- const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
12560
- const refDir = path12.join(builtinDir, category, referenceType);
12732
+ const refDir = this.providerLoader.getUpstreamProviderDir(category, referenceType);
12561
12733
  if (!fs9.existsSync(refDir)) return {};
12562
12734
  const referenceScripts = {};
12563
12735
  const scriptsDir = path12.join(refDir, "scripts");
@@ -12796,7 +12968,7 @@ var DevServer = class _DevServer {
12796
12968
  }
12797
12969
  if (model) args.push("--model", model);
12798
12970
  const escapedArgs = args.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
12799
- 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.`;
12971
+ 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.`;
12800
12972
  shellCmd = `${command} ${escapedArgs} "${metaPrompt}"`;
12801
12973
  } else {
12802
12974
  const escapedArgs = baseArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
@@ -13059,6 +13231,8 @@ var DevServer = class _DevServer {
13059
13231
  lines.push("5. Do NOT modify `scripts.js` router \u2014 only edit individual `*.js` files");
13060
13232
  lines.push("6. All scripts run in the browser (CDP evaluate) \u2014 use DOM APIs only");
13061
13233
  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.');
13234
+ 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.");
13235
+ lines.push("9. Do NOT delete any files. Implement the logic by replacing the empty stubs.");
13062
13236
  lines.push("");
13063
13237
  lines.push("## Required Return Format");
13064
13238
  lines.push("| Function | Return JSON |");