@adhdev/daemon-core 0.8.15 → 0.8.16

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.
@@ -66,6 +66,7 @@ export interface CliScriptInput {
66
66
  screenText: string;
67
67
  messages: CliChatMessage[];
68
68
  partialResponse: string;
69
+ promptText?: string;
69
70
  }
70
71
  export interface CliTraceEntry {
71
72
  id: number;
@@ -172,6 +173,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
172
173
  private submitRetryUsed;
173
174
  private submitRetryPromptSnippet;
174
175
  private idleFinishCandidate;
176
+ private finishRetryTimer;
177
+ private finishRetryCount;
175
178
  private resizeSuppressUntil;
176
179
  private statusHistory;
177
180
  private cliScripts;
@@ -190,6 +193,8 @@ export declare class ProviderCliAdapter implements CliAdapter {
190
193
  private static readonly MAX_TRACE_ENTRIES;
191
194
  private readonly providerResolutionMeta;
192
195
  private static readonly IDLE_FINISH_CONFIRM_MS;
196
+ private static readonly FINISH_RETRY_DELAY_MS;
197
+ private static readonly MAX_FINISH_RETRIES;
193
198
  private syncMessageViews;
194
199
  private normalizeParsedMessages;
195
200
  private sliceFromOffset;
@@ -220,6 +225,10 @@ export declare class ProviderCliAdapter implements CliAdapter {
220
225
  private scheduleSettle;
221
226
  private armApprovalExitTimeout;
222
227
  private looksLikeVisibleIdlePrompt;
228
+ private looksLikeVisibleAssistantCandidate;
229
+ private shouldRetryFinishResponse;
230
+ private getStartupConfirmationModal;
231
+ private shouldResolveModalWithEnter;
223
232
  private waitForInteractivePrompt;
224
233
  private evaluateSettled;
225
234
  private finishResponse;
@@ -19,6 +19,7 @@ type CliExerciseVerification = {
19
19
  lastAssistantMustNotMatchAny?: string[];
20
20
  inspectFields?: string[];
21
21
  description?: string;
22
+ focusAreas?: string[];
22
23
  fixtureName?: string;
23
24
  fixtureNames?: string[];
24
25
  };
package/dist/index.js CHANGED
@@ -905,6 +905,40 @@ function normalizeScreenSnapshot(text) {
905
905
  function normalizeComparableMessageContent(text) {
906
906
  return String(text || "").replace(/\s+/g, " ").trim();
907
907
  }
908
+ function trimPromptEchoPrefix(text, promptText) {
909
+ const prompt = normalizeComparableMessageContent(String(promptText || ""));
910
+ if (!prompt) return String(text || "");
911
+ const lines = String(text || "").split(/\r\n|\n|\r/g);
912
+ let dropCount = 0;
913
+ for (let index = 0; index < Math.min(lines.length, 6); index += 1) {
914
+ const fragment = normalizeComparableMessageContent(lines[index].replace(/^[.…]+\s*/, ""));
915
+ if (!fragment) {
916
+ if (dropCount === index) dropCount = index + 1;
917
+ continue;
918
+ }
919
+ const fragmentWordCount = fragment ? fragment.split(/\s+/).filter(Boolean).length : 0;
920
+ const canBePromptEcho = fragment.length >= 16 || fragmentWordCount >= 4;
921
+ if (canBePromptEcho && prompt.includes(fragment)) {
922
+ dropCount = index + 1;
923
+ continue;
924
+ }
925
+ break;
926
+ }
927
+ return lines.slice(dropCount).join("\n").trim();
928
+ }
929
+ function getLastUserPromptText(messages) {
930
+ const items = Array.isArray(messages) ? messages : [];
931
+ for (let index = items.length - 1; index >= 0; index -= 1) {
932
+ const message = items[index];
933
+ if (message?.role === "user" && typeof message.content === "string" && message.content.trim()) {
934
+ return message.content;
935
+ }
936
+ }
937
+ return "";
938
+ }
939
+ function looksLikeConfirmOnlyLabel(label) {
940
+ return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
941
+ }
908
942
  function parsePatternEntry(x) {
909
943
  if (x instanceof RegExp) return x;
910
944
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -1041,6 +1075,8 @@ var init_provider_cli_adapter = __esm({
1041
1075
  submitRetryUsed = false;
1042
1076
  submitRetryPromptSnippet = "";
1043
1077
  idleFinishCandidate = null;
1078
+ finishRetryTimer = null;
1079
+ finishRetryCount = 0;
1044
1080
  // Resize redraw suppression
1045
1081
  resizeSuppressUntil = 0;
1046
1082
  // Debug: status transition history
@@ -1062,6 +1098,8 @@ var init_provider_cli_adapter = __esm({
1062
1098
  static MAX_TRACE_ENTRIES = 250;
1063
1099
  providerResolutionMeta;
1064
1100
  static IDLE_FINISH_CONFIRM_MS = 900;
1101
+ static FINISH_RETRY_DELAY_MS = 300;
1102
+ static MAX_FINISH_RETRIES = 2;
1065
1103
  syncMessageViews() {
1066
1104
  this.messages = [...this.committedMessages];
1067
1105
  this.structuredMessages = [...this.committedMessages];
@@ -1121,7 +1159,8 @@ var init_provider_cli_adapter = __esm({
1121
1159
  recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
1122
1160
  screenText: this.terminalScreen.getText(),
1123
1161
  messages: [...baseMessages],
1124
- partialResponse
1162
+ partialResponse,
1163
+ promptText: scope?.prompt || ""
1125
1164
  };
1126
1165
  }
1127
1166
  setStatus(status, trigger) {
@@ -1364,6 +1403,11 @@ var init_provider_cli_adapter = __esm({
1364
1403
  this.terminalScreen.reset(24, 80);
1365
1404
  this.pendingTerminalQueryTail = "";
1366
1405
  this.currentTurnScope = null;
1406
+ this.finishRetryCount = 0;
1407
+ if (this.finishRetryTimer) {
1408
+ clearTimeout(this.finishRetryTimer);
1409
+ this.finishRetryTimer = null;
1410
+ }
1367
1411
  this.ready = false;
1368
1412
  await this.ptyProcess.ready;
1369
1413
  this.recordTrace("ready", {
@@ -1410,11 +1454,12 @@ var init_provider_cli_adapter = __esm({
1410
1454
  if (this.startupParseGate) {
1411
1455
  this.startupBuffer += cleanData;
1412
1456
  const elapsed = Date.now() - this.spawnAt;
1413
- const scriptStatus = this.runDetectStatus(this.startupBuffer);
1414
1457
  const screenText = this.terminalScreen.getText() || "";
1458
+ const startupModal = this.getStartupConfirmationModal(screenText);
1459
+ const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
1415
1460
  const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
1416
1461
  const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
1417
- const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
1462
+ const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
1418
1463
  if (isReady) {
1419
1464
  this.startupParseGate = false;
1420
1465
  this.ready = true;
@@ -1446,7 +1491,9 @@ var init_provider_cli_adapter = __esm({
1446
1491
  this.approvalExitTimeout = setTimeout(() => {
1447
1492
  if (this.currentStatus !== "waiting_approval") return;
1448
1493
  const tail = this.recentOutputBuffer;
1449
- const modal = this.runParseApproval(tail);
1494
+ const screenText = this.terminalScreen.getText() || "";
1495
+ const startupModal = this.getStartupConfirmationModal(screenText);
1496
+ const modal = this.runParseApproval(tail) || startupModal;
1450
1497
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
1451
1498
  if (stillWaiting) {
1452
1499
  this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
@@ -1466,6 +1513,63 @@ var init_provider_cli_adapter = __esm({
1466
1513
  if (!text.trim()) return false;
1467
1514
  return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
1468
1515
  }
1516
+ looksLikeVisibleAssistantCandidate(screenText) {
1517
+ const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
1518
+ for (const line of lines) {
1519
+ const trimmed = String(line || "").trim();
1520
+ if (!trimmed) continue;
1521
+ if (/^➜\s+\S+/.test(trimmed)) continue;
1522
+ if (/^Update available!/i.test(trimmed)) continue;
1523
+ if (/Claude Code v\d/i.test(trimmed)) continue;
1524
+ if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
1525
+ if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
1526
+ if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
1527
+ if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
1528
+ const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
1529
+ if (!assistantMatch) continue;
1530
+ const content = assistantMatch[1].trim();
1531
+ if (!content) continue;
1532
+ if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
1533
+ if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
1534
+ return true;
1535
+ }
1536
+ return false;
1537
+ }
1538
+ shouldRetryFinishResponse(commitResult) {
1539
+ if (!this.currentTurnScope) return false;
1540
+ if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
1541
+ if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
1542
+ if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
1543
+ const screenText = this.terminalScreen.getText() || "";
1544
+ if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
1545
+ const now = Date.now();
1546
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
1547
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
1548
+ return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
1549
+ }
1550
+ getStartupConfirmationModal(screenText) {
1551
+ const text = sanitizeTerminalText(String(screenText || ""));
1552
+ if (!text.trim()) return null;
1553
+ if (this.cliType === "claude-cli") {
1554
+ const hasTrustPrompt = /Quick safety check/i.test(text) || /Is this a project you trust/i.test(text) || /Do you trust (?:this project|the contents of this directory|the files in this folder)/i.test(text);
1555
+ const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
1556
+ if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
1557
+ return {
1558
+ message: "Confirm Claude Code project trust",
1559
+ buttons: ["Continue"]
1560
+ };
1561
+ }
1562
+ }
1563
+ return null;
1564
+ }
1565
+ shouldResolveModalWithEnter(modal, buttonIndex) {
1566
+ if (!modal || buttonIndex !== 0) return false;
1567
+ const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
1568
+ if (buttons.length !== 1) return false;
1569
+ const buttonLabel = String(buttons[0] || "").trim();
1570
+ const modalText = `${modal.message || ""} ${buttonLabel}`.trim();
1571
+ return looksLikeConfirmOnlyLabel(buttonLabel) || /Quick safety check|project trust|trust (?:this project|the contents of this directory|the files in this folder)|Enter to confirm/i.test(modalText);
1572
+ }
1469
1573
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
1470
1574
  const startedAt = Date.now();
1471
1575
  let loggedWait = false;
@@ -1515,9 +1619,10 @@ var init_provider_cli_adapter = __esm({
1515
1619
  }
1516
1620
  const tail = this.settledBuffer;
1517
1621
  const screenText = this.terminalScreen.getText() || "";
1518
- const modal = this.runParseApproval(tail);
1622
+ const startupModal = this.getStartupConfirmationModal(screenText);
1623
+ const modal = this.runParseApproval(tail) || startupModal;
1519
1624
  const rawScriptStatus = this.runDetectStatus(tail);
1520
- const scriptStatus = rawScriptStatus;
1625
+ const scriptStatus = startupModal ? "waiting_approval" : rawScriptStatus;
1521
1626
  const parsedTranscript = this.parseCurrentTranscript(
1522
1627
  this.committedMessages,
1523
1628
  this.responseBuffer,
@@ -1712,7 +1817,24 @@ var init_provider_cli_adapter = __esm({
1712
1817
  this.recordTrace("finish_response", {
1713
1818
  ...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
1714
1819
  });
1715
- this.commitCurrentTranscript();
1820
+ const commitResult = this.commitCurrentTranscript();
1821
+ if (this.shouldRetryFinishResponse(commitResult)) {
1822
+ this.finishRetryCount += 1;
1823
+ this.recordTrace("finish_response_retry", {
1824
+ retryCount: this.finishRetryCount,
1825
+ retryDelayMs: _ProviderCliAdapter.FINISH_RETRY_DELAY_MS,
1826
+ assistantContent: this.summarizeTraceText(commitResult.assistantContent, 220),
1827
+ ...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
1828
+ });
1829
+ if (this.finishRetryTimer) clearTimeout(this.finishRetryTimer);
1830
+ this.finishRetryTimer = setTimeout(() => {
1831
+ this.finishRetryTimer = null;
1832
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
1833
+ this.finishResponse();
1834
+ }
1835
+ }, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
1836
+ return;
1837
+ }
1716
1838
  if (this.responseTimeout) {
1717
1839
  clearTimeout(this.responseTimeout);
1718
1840
  this.responseTimeout = null;
@@ -1729,11 +1851,16 @@ var init_provider_cli_adapter = __esm({
1729
1851
  clearTimeout(this.submitRetryTimer);
1730
1852
  this.submitRetryTimer = null;
1731
1853
  }
1854
+ if (this.finishRetryTimer) {
1855
+ clearTimeout(this.finishRetryTimer);
1856
+ this.finishRetryTimer = null;
1857
+ }
1732
1858
  this.responseBuffer = "";
1733
1859
  this.isWaitingForResponse = false;
1734
1860
  this.responseSettleIgnoreUntil = 0;
1735
1861
  this.submitRetryUsed = false;
1736
1862
  this.submitRetryPromptSnippet = "";
1863
+ this.finishRetryCount = 0;
1737
1864
  this.currentTurnScope = null;
1738
1865
  this.activeModal = null;
1739
1866
  this.setStatus("idle", "response_finished");
@@ -1747,6 +1874,13 @@ var init_provider_cli_adapter = __esm({
1747
1874
  );
1748
1875
  if (parsed && Array.isArray(parsed.messages)) {
1749
1876
  this.committedMessages = this.normalizeParsedMessages(parsed.messages);
1877
+ const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
1878
+ if (promptForTrim) {
1879
+ const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
1880
+ if (lastAssistantForTrim) {
1881
+ lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
1882
+ }
1883
+ }
1750
1884
  this.syncMessageViews();
1751
1885
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
1752
1886
  this.recordTrace("commit_transcript", {
@@ -1762,7 +1896,15 @@ var init_provider_cli_adapter = __esm({
1762
1896
  `[${this.cliType}] Commit without assistant turn: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(this.summarizeTraceText(this.responseBuffer, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
1763
1897
  );
1764
1898
  }
1899
+ return {
1900
+ hasAssistant: !!lastAssistant,
1901
+ assistantContent: lastAssistant?.content || ""
1902
+ };
1765
1903
  }
1904
+ return {
1905
+ hasAssistant: false,
1906
+ assistantContent: ""
1907
+ };
1766
1908
  }
1767
1909
  // ─── Script Execution ──────────────────────────
1768
1910
  runDetectStatus(text) {
@@ -1841,7 +1983,15 @@ var init_provider_cli_adapter = __esm({
1841
1983
  if (!this.cliScripts?.parseOutput) return null;
1842
1984
  try {
1843
1985
  const input = this.buildParseInput(baseMessages, partialResponse, scope);
1844
- return this.cliScripts.parseOutput(input);
1986
+ const parsed = this.cliScripts.parseOutput(input);
1987
+ const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
1988
+ if (parsed && Array.isArray(parsed.messages) && promptForTrim) {
1989
+ const lastAssistant = [...parsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
1990
+ if (lastAssistant) {
1991
+ lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
1992
+ }
1993
+ }
1994
+ return parsed;
1845
1995
  } catch (e) {
1846
1996
  LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
1847
1997
  return null;
@@ -1886,10 +2036,19 @@ ${data.message || ""}`.trim();
1886
2036
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
1887
2037
  if (this.isWaitingForResponse) return;
1888
2038
  await this.waitForInteractivePrompt();
2039
+ const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
2040
+ if (blockingModal || this.currentStatus === "waiting_approval") {
2041
+ throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
2042
+ }
1889
2043
  this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
1890
2044
  this.syncMessageViews();
1891
2045
  this.isWaitingForResponse = true;
1892
2046
  this.responseBuffer = "";
2047
+ this.finishRetryCount = 0;
2048
+ if (this.finishRetryTimer) {
2049
+ clearTimeout(this.finishRetryTimer);
2050
+ this.finishRetryTimer = null;
2051
+ }
1893
2052
  this.clearIdleFinishCandidate("send_message");
1894
2053
  this.currentTurnScope = {
1895
2054
  prompt: text,
@@ -2117,6 +2276,10 @@ ${data.message || ""}`.trim();
2117
2276
  clearTimeout(this.submitRetryTimer);
2118
2277
  this.submitRetryTimer = null;
2119
2278
  }
2279
+ if (this.finishRetryTimer) {
2280
+ clearTimeout(this.finishRetryTimer);
2281
+ this.finishRetryTimer = null;
2282
+ }
2120
2283
  if (this.responseTimeout) {
2121
2284
  clearTimeout(this.responseTimeout);
2122
2285
  this.responseTimeout = null;
@@ -2140,6 +2303,7 @@ ${data.message || ""}`.trim();
2140
2303
  this.ptyOutputFlushTimer = null;
2141
2304
  }
2142
2305
  this.ptyOutputBuffer = "";
2306
+ this.finishRetryCount = 0;
2143
2307
  if (this.ptyProcess) {
2144
2308
  this.ptyProcess.write("");
2145
2309
  setTimeout(() => {
@@ -2170,6 +2334,10 @@ ${data.message || ""}`.trim();
2170
2334
  clearTimeout(this.submitRetryTimer);
2171
2335
  this.submitRetryTimer = null;
2172
2336
  }
2337
+ if (this.finishRetryTimer) {
2338
+ clearTimeout(this.finishRetryTimer);
2339
+ this.finishRetryTimer = null;
2340
+ }
2173
2341
  if (this.responseTimeout) {
2174
2342
  clearTimeout(this.responseTimeout);
2175
2343
  this.responseTimeout = null;
@@ -2193,6 +2361,7 @@ ${data.message || ""}`.trim();
2193
2361
  this.ptyOutputFlushTimer = null;
2194
2362
  }
2195
2363
  this.ptyOutputBuffer = "";
2364
+ this.finishRetryCount = 0;
2196
2365
  if (this.ptyProcess) {
2197
2366
  try {
2198
2367
  if (typeof this.ptyProcess.detach === "function") {
@@ -2229,6 +2398,11 @@ ${data.message || ""}`.trim();
2229
2398
  this.ptyOutputFlushTimer = null;
2230
2399
  }
2231
2400
  this.ptyOutputBuffer = "";
2401
+ if (this.finishRetryTimer) {
2402
+ clearTimeout(this.finishRetryTimer);
2403
+ this.finishRetryTimer = null;
2404
+ }
2405
+ this.finishRetryCount = 0;
2232
2406
  this.terminalScreen.reset();
2233
2407
  this.ptyProcess?.clearBuffer?.();
2234
2408
  this.onStatusChange?.();
@@ -2248,10 +2422,11 @@ ${data.message || ""}`.trim();
2248
2422
  }
2249
2423
  resolveModal(buttonIndex) {
2250
2424
  if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
2425
+ const modal = this.activeModal;
2251
2426
  this.clearIdleFinishCandidate("resolve_modal");
2252
2427
  this.recordTrace("resolve_modal", {
2253
2428
  buttonIndex,
2254
- activeModal: this.activeModal
2429
+ activeModal: modal
2255
2430
  });
2256
2431
  this.activeModal = null;
2257
2432
  this.lastApprovalResolvedAt = Date.now();
@@ -2262,7 +2437,9 @@ ${data.message || ""}`.trim();
2262
2437
  }
2263
2438
  this.setStatus("generating", "approval_resolved");
2264
2439
  this.onStatusChange?.();
2265
- if (buttonIndex in this.approvalKeys) {
2440
+ if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
2441
+ this.ptyProcess.write("\r");
2442
+ } else if (buttonIndex in this.approvalKeys) {
2266
2443
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
2267
2444
  } else {
2268
2445
  const DOWN = "\x1B[B";
@@ -11674,7 +11851,10 @@ function appendUpgradeLog(message) {
11674
11851
  }
11675
11852
  }
11676
11853
  function getNpmExecutable() {
11677
- return process.platform === "win32" ? "npm.cmd" : "npm";
11854
+ return "npm";
11855
+ }
11856
+ function getNpmExecOptions() {
11857
+ return { shell: process.platform === "win32" };
11678
11858
  }
11679
11859
  function killPid(pid) {
11680
11860
  try {
@@ -11736,9 +11916,10 @@ function removeDaemonPidFile() {
11736
11916
  }
11737
11917
  }
11738
11918
  function cleanupStaleGlobalInstallDirs(pkgName) {
11739
- const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8" }).trim();
11919
+ const npmExecOpts = getNpmExecOptions();
11920
+ const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
11740
11921
  if (!npmRoot) return;
11741
- const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8" }).trim();
11922
+ const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
11742
11923
  const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
11743
11924
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
11744
11925
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
@@ -11799,7 +11980,8 @@ async function runDaemonUpgradeHelper(payload) {
11799
11980
  {
11800
11981
  encoding: "utf8",
11801
11982
  stdio: "pipe",
11802
- maxBuffer: 20 * 1024 * 1024
11983
+ maxBuffer: 20 * 1024 * 1024,
11984
+ ...getNpmExecOptions()
11803
11985
  }
11804
11986
  );
11805
11987
  if (installOutput.trim()) {
@@ -15021,6 +15203,53 @@ async function runCliExerciseInternal(ctx, body) {
15021
15203
  let lastModalKey = "";
15022
15204
  let idleSince = 0;
15023
15205
  let sawBusy = false;
15206
+ const noteStatus = (status) => {
15207
+ if (status !== lastStatus) {
15208
+ statusesSeen.push(status);
15209
+ lastStatus = status;
15210
+ }
15211
+ };
15212
+ const resolveActiveModalIfNeeded = (status, modal) => {
15213
+ if (!autoResolveApprovals || status !== "waiting_approval" || !modal || !Array.isArray(modal.buttons) || modal.buttons.length === 0) {
15214
+ return false;
15215
+ }
15216
+ const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
15217
+ const modalKey = JSON.stringify({
15218
+ message: modal.message || "",
15219
+ buttons: modal.buttons,
15220
+ index: clampedIndex
15221
+ });
15222
+ if (modalKey === lastModalKey || typeof bundle?.adapter?.resolveModal !== "function") {
15223
+ return false;
15224
+ }
15225
+ lastModalKey = modalKey;
15226
+ approvalsResolved.push({
15227
+ at: Date.now(),
15228
+ buttonIndex: clampedIndex,
15229
+ label: modal.buttons[clampedIndex] || null
15230
+ });
15231
+ bundle.adapter.resolveModal(clampedIndex);
15232
+ return true;
15233
+ };
15234
+ const preflightStartedAt = Date.now();
15235
+ while (Date.now() - preflightStartedAt < Math.max(1e3, readyTimeoutMs)) {
15236
+ bundle = getCliTargetBundle(ctx, type, bundle.target.instanceId);
15237
+ if (!bundle) {
15238
+ throw new Error("CLI instance disappeared before exercise send");
15239
+ }
15240
+ const debug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
15241
+ const trace = typeof bundle.adapter.getTraceState === "function" ? bundle.adapter.getTraceState(traceLimit) : null;
15242
+ const status = String(debug?.status || bundle.target.status || "unknown");
15243
+ const modal = debug?.activeModal || trace?.activeModal || null;
15244
+ noteStatus(status);
15245
+ if (resolveActiveModalIfNeeded(status, modal)) {
15246
+ await sleep(150);
15247
+ continue;
15248
+ }
15249
+ const startupParseGate = !!debug?.startupParseGate;
15250
+ if (status === "idle" && !startupParseGate) break;
15251
+ await sleep(150);
15252
+ }
15024
15253
  ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
15025
15254
  while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
15026
15255
  await sleep(150);
@@ -15035,32 +15264,14 @@ async function runCliExerciseInternal(ctx, body) {
15035
15264
  const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
15036
15265
  const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
15037
15266
  const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
15038
- if (status !== lastStatus) {
15039
- statusesSeen.push(status);
15040
- lastStatus = status;
15041
- }
15267
+ noteStatus(status);
15042
15268
  if (status === "generating" || status === "waiting_approval") {
15043
15269
  sawBusy = true;
15044
15270
  idleSince = 0;
15045
15271
  }
15046
15272
  const modal = debug?.activeModal || trace?.activeModal || null;
15047
- if (autoResolveApprovals && status === "waiting_approval" && modal && Array.isArray(modal.buttons) && modal.buttons.length > 0) {
15048
- const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
15049
- const modalKey = JSON.stringify({
15050
- message: modal.message || "",
15051
- buttons: modal.buttons,
15052
- index: clampedIndex
15053
- });
15054
- if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
15055
- lastModalKey = modalKey;
15056
- approvalsResolved.push({
15057
- at: Date.now(),
15058
- buttonIndex: clampedIndex,
15059
- label: modal.buttons[clampedIndex] || null
15060
- });
15061
- bundle.adapter.resolveModal(clampedIndex);
15062
- continue;
15063
- }
15273
+ if (resolveActiveModalIfNeeded(status, modal)) {
15274
+ continue;
15064
15275
  }
15065
15276
  const traceCount = Number(trace?.entryCount || 0);
15066
15277
  const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
@@ -16715,6 +16926,13 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
16715
16926
  lines.push("19. Literal string checks are allowed only for stable proper nouns or exact product chrome that cannot be expressed safely as a broader pattern. Everything else should generalize.");
16716
16927
  lines.push("20. When a bug comes from noisy PTY text, first normalize and classify the line family; do NOT just append another special-case substring to the parser.");
16717
16928
  lines.push("");
16929
+ if (verification?.focusAreas?.length) {
16930
+ lines.push("## Provider-Specific Focus Areas");
16931
+ for (const area of verification.focusAreas) {
16932
+ lines.push(`- ${area}`);
16933
+ }
16934
+ lines.push("");
16935
+ }
16718
16936
  lines.push("## Task");
16719
16937
  lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
16720
16938
  lines.push("");