cawdex 1.35.68 → 1.35.73

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 (45) hide show
  1. package/README.md +188 -188
  2. package/dist/command-palette.js +5 -4
  3. package/dist/command-palette.js.map +1 -1
  4. package/dist/config.d.ts +1 -1
  5. package/dist/config.js +20 -13
  6. package/dist/config.js.map +1 -1
  7. package/dist/curator.d.ts +2 -2
  8. package/dist/curator.js +2 -2
  9. package/dist/fixed-footer.d.ts +29 -0
  10. package/dist/fixed-footer.js +383 -0
  11. package/dist/fixed-footer.js.map +1 -0
  12. package/dist/hooks.d.ts +1 -1
  13. package/dist/hooks.js +7 -7
  14. package/dist/hooks.js.map +1 -1
  15. package/dist/index.js +362 -66
  16. package/dist/index.js.map +1 -1
  17. package/dist/instant-answer.d.ts +6 -1
  18. package/dist/instant-answer.js +35 -1
  19. package/dist/instant-answer.js.map +1 -1
  20. package/dist/live-queue.d.ts +0 -32
  21. package/dist/live-queue.js +15 -0
  22. package/dist/live-queue.js.map +1 -1
  23. package/dist/modes.d.ts +2 -1
  24. package/dist/modes.js +368 -361
  25. package/dist/modes.js.map +1 -1
  26. package/dist/query.d.ts +1 -0
  27. package/dist/query.js +162 -42
  28. package/dist/query.js.map +1 -1
  29. package/dist/swarm.d.ts +8 -3
  30. package/dist/swarm.js +39 -14
  31. package/dist/swarm.js.map +1 -1
  32. package/dist/system-prompt.js +14 -14
  33. package/dist/system-prompt.js.map +1 -1
  34. package/dist/theme.d.ts +4 -0
  35. package/dist/theme.js +22 -0
  36. package/dist/theme.js.map +1 -1
  37. package/dist/tools/skill.d.ts +2 -2
  38. package/dist/tools/skill.js +2 -2
  39. package/dist/types.d.ts +15 -0
  40. package/dist/types.js.map +1 -1
  41. package/dist/updater.d.ts +16 -0
  42. package/dist/updater.js +157 -0
  43. package/dist/updater.js.map +1 -0
  44. package/dist/walkthrough.js +7 -7
  45. package/package.json +1 -2
package/dist/modes.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"modes.js","sourceRoot":"","sources":["../src/modes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"modes.js","sourceRoot":"","sources":["../src/modes.ts"],"names":[],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as readline from 'node:readline/promises';
2
2
  import type { Message, CawdexConfig } from './types.js';
3
3
  import type { Mode } from './modes.js';
4
4
  export declare function resolveFirstTokenTimeoutMs(config: Pick<CawdexConfig, 'model' | 'provider'>): number;
5
+ export declare function resolveStreamIdleTimeoutMs(fastDirect?: boolean): number;
5
6
  export declare function isKnownFlakyOpenRouterModel(config: Pick<CawdexConfig, 'model' | 'provider'>): boolean;
6
7
  export declare function fallbackModelForKnownFlakyTurn(config: CawdexConfig, usedFallbackModel?: boolean): string | null;
7
8
  export declare function isTurnCancelKeySequence(chunk: Buffer): boolean;
package/dist/query.js CHANGED
@@ -10,13 +10,14 @@ import { runHooks } from './hooks.js';
10
10
  import { scanToolCall, printSecurityWarning } from './security.js';
11
11
  import { trackUsage } from './cost-tracker.js';
12
12
  import { shouldCompact, compactMessages, quickCompact, buildCompactionConfig, contextCapTokens, enforceContextCap, inferContextWindowTokens, } from './compaction.js';
13
- import { theme, sym, printToolRun, printToolResult, printThinkingOpen, printThinkingText, printThinkingClose, printCost, printApiError, formatDuration, categorizeApiError } from './theme.js';
13
+ import { assistantTranscriptPrefix, theme, sym, printToolRun, printToolResult, printThinkingOpen, printThinkingText, printThinkingClose, printCost, printApiError, formatDuration, categorizeApiError } from './theme.js';
14
14
  import { isVoiceEnabled, getTtsConfig, getAccessibilityConfig, speakAssistantResponse, speak, speakUserEcho, } from './voice.js';
15
15
  import { isLikelyDestructive, describeDestructive, countWords, summarize } from './accessibility.js';
16
16
  import { audioCue } from './audio.js';
17
17
  import { setStatus } from './status.js';
18
18
  import { collapseCompletedTurns } from './turn-context.js';
19
19
  import * as liveQueue from './live-queue.js';
20
+ import { isFooterActive, setFooterActivity, setFooterCost, writeScrollableLine } from './fixed-footer.js';
20
21
  import { applyQueuedInputChunk, drainQueuedInputBytes, queuedInputBytesToText } from './prompt-buffer.js';
21
22
  import { emit as dbgEmit } from './debug.js';
22
23
  import { buildBenchmarkCompletionReminder, buildBenchmarkTrajectorySystemBlock, makeBenchmarkInvalidToolActionEvent, makeBenchmarkTraceEvent, writeBenchmarkTrace, } from './benchmark-trace.js';
@@ -34,6 +35,9 @@ const INTERACTIVE_FLAKY_FIRST_TOKEN_TIMEOUT_MS = 6_000;
34
35
  const NON_INTERACTIVE_FIRST_TOKEN_TIMEOUT_MS = 60_000;
35
36
  const NON_INTERACTIVE_FLAKY_FIRST_TOKEN_TIMEOUT_MS = 20_000;
36
37
  const FAST_DIRECT_FIRST_TOKEN_TIMEOUT_MS = 8_000;
38
+ const INTERACTIVE_STREAM_IDLE_TIMEOUT_MS = 45_000;
39
+ const NON_INTERACTIVE_STREAM_IDLE_TIMEOUT_MS = 120_000;
40
+ const FAST_DIRECT_STREAM_IDLE_TIMEOUT_MS = 20_000;
37
41
  const KNOWN_FLAKY_OPENROUTER_MODEL_PATTERNS = [
38
42
  'owl-alpha',
39
43
  'horizon-alpha',
@@ -41,6 +45,7 @@ const KNOWN_FLAKY_OPENROUTER_MODEL_PATTERNS = [
41
45
  'optimus-alpha',
42
46
  'quasar-alpha',
43
47
  'deepseek-v4-flash',
48
+ 'deepseek-v4-pro',
44
49
  ];
45
50
  function envTimeoutMs(name, fallback) {
46
51
  const raw = process.env[name];
@@ -59,6 +64,14 @@ export function resolveFirstTokenTimeoutMs(config) {
59
64
  : (flaky ? INTERACTIVE_FLAKY_FIRST_TOKEN_TIMEOUT_MS : INTERACTIVE_FIRST_TOKEN_TIMEOUT_MS);
60
65
  return envTimeoutMs('CAWDEX_FIRST_TOKEN_TIMEOUT_MS', fallback);
61
66
  }
67
+ export function resolveStreamIdleTimeoutMs(fastDirect = false) {
68
+ const fallback = process.env.CAWDEX_NON_INTERACTIVE === '1'
69
+ ? NON_INTERACTIVE_STREAM_IDLE_TIMEOUT_MS
70
+ : fastDirect
71
+ ? FAST_DIRECT_STREAM_IDLE_TIMEOUT_MS
72
+ : INTERACTIVE_STREAM_IDLE_TIMEOUT_MS;
73
+ return envTimeoutMs('CAWDEX_STREAM_IDLE_TIMEOUT_MS', fallback);
74
+ }
62
75
  export function isKnownFlakyOpenRouterModel(config) {
63
76
  const model = String(config.model || '').toLowerCase();
64
77
  const provider = String(config.provider || '').toLowerCase();
@@ -80,7 +93,10 @@ export function fallbackModelForKnownFlakyTurn(config, usedFallbackModel = false
80
93
  }
81
94
  export function isTurnCancelKeySequence(chunk) {
82
95
  const seq = chunk.toString('utf8');
83
- return (seq === '\x1b[15~' ||
96
+ return (seq === '\x1b' ||
97
+ seq === '\x1b\x1b' ||
98
+ /^\x1b\[27(?:;\d+)*~$/.test(seq) ||
99
+ seq === '\x1b[15~' ||
84
100
  seq === '\x1b[15;2~' ||
85
101
  seq === '\x1b[15;5~' ||
86
102
  seq === '\x1b[15;6~' ||
@@ -90,6 +106,10 @@ const FAST_DIRECT_SYSTEM_PROMPT = 'You are Cawdex. Answer the user directly and
90
106
  'Do not mention tools, repositories, or implementation steps unless the user asks.';
91
107
  const FAST_DIRECT_POSITIVE = [
92
108
  /^(hi|hello|hey|thanks|thank you)\b/i,
109
+ /^(?:i\s+)?(?:need|want)\s+(?:your\s+)?help\b/i,
110
+ /^(?:help me|can you help|could you help|would you help)\b/i,
111
+ /^(?:i|we)\s+(?:like|love|hate|prefer|think|feel|am|are)\b/i,
112
+ /^(?:sorry,?\s*)?(?:make|rewrite|revise|adjust|redo|try)\s+(?:it|that|this)\b/i,
93
113
  /^(what|who|when|where|why|how|which)\b/i,
94
114
  /^(?:please\s+)?(?:can|could|would|will)\s+you\s+(?:please\s+)?(?:explain|define|summarize|translate|calculate|compute|write|draft|make|create|tell|give)\b/i,
95
115
  /^(?:please\s+)?(?:tell|give)\s+me\b/i,
@@ -125,14 +145,20 @@ function printInteractiveTurnAccepted(config) {
125
145
  return;
126
146
  if (!process.stdout.isTTY)
127
147
  return;
128
- console.log(theme.dim(` submitted to ${config.provider} · ${config.model}. Waiting for the first model event; Esc or F5 cancels.`));
148
+ const line = theme.dim(` submitted to ${config.provider} · ${config.model}. Waiting for model events; Esc or F5 cancels.`);
149
+ if (isFooterActive()) {
150
+ writeScrollableLine(line);
151
+ }
152
+ else {
153
+ console.log(line);
154
+ }
129
155
  }
130
156
  export function formatWorkingIndicatorFrame(elapsedMs, frameIndex = 0, message = 'Working') {
131
- const frames = ['', '', '', ''];
157
+ const frames = ['\u25e6', '\u25c7', '\u25c6', '\u25c7'];
132
158
  const frame = frames[Math.abs(frameIndex) % frames.length];
133
- return ` ${frame} ${message} (${formatDuration(elapsedMs)} esc to interrupt)`;
159
+ return ` ${frame} ${message} (${formatDuration(elapsedMs)} \u2022 esc to interrupt)`;
134
160
  }
135
- function startWorkingIndicator(startedAtMs, screenReader) {
161
+ function startWorkingIndicator(startedAtMs, screenReader, turn = 0) {
136
162
  if (screenReader)
137
163
  return null;
138
164
  if (process.env.CAWDEX_NON_INTERACTIVE === '1')
@@ -140,7 +166,7 @@ function startWorkingIndicator(startedAtMs, screenReader) {
140
166
  if (!process.stdout.isTTY)
141
167
  return null;
142
168
  const messages = [
143
- 'Working',
169
+ 'Working hard on your request',
144
170
  'Sumi ink settling',
145
171
  'Edo lanterns lit',
146
172
  'Kamon crest aligned',
@@ -149,7 +175,13 @@ function startWorkingIndicator(startedAtMs, screenReader) {
149
175
  let frame = 0;
150
176
  let stopped = false;
151
177
  const paint = () => {
152
- const line = formatWorkingIndicatorFrame(Date.now() - startedAtMs, frame, messages[Math.floor(frame / 8) % messages.length]);
178
+ const message = messages[Math.floor(frame / 8) % messages.length];
179
+ if (isFooterActive()) {
180
+ setFooterActivity(message, turn, startedAtMs);
181
+ frame++;
182
+ return;
183
+ }
184
+ const line = formatWorkingIndicatorFrame(Date.now() - startedAtMs, frame, message);
153
185
  process.stdout.write('\r\x1b[K' + theme.dim(line));
154
186
  frame++;
155
187
  };
@@ -163,7 +195,10 @@ function startWorkingIndicator(startedAtMs, screenReader) {
163
195
  return;
164
196
  stopped = true;
165
197
  clearInterval(timer);
166
- process.stdout.write('\r\x1b[K');
198
+ if (isFooterActive())
199
+ setFooterActivity('Receiving response', turn, startedAtMs);
200
+ else
201
+ process.stdout.write('\r\x1b[K');
167
202
  },
168
203
  };
169
204
  }
@@ -278,6 +313,15 @@ function startInputSuppression(screenReader = false) {
278
313
  }
279
314
  return;
280
315
  }
316
+ if (detached && isTurnCancelKeySequence(chunk)) {
317
+ if (steerHandler) {
318
+ try {
319
+ steerHandler();
320
+ }
321
+ catch { /* never break input on a cancel error */ }
322
+ }
323
+ return;
324
+ }
281
325
  // Esc (0x1B) → Steer trigger, but only for a BARE Esc (chunk of
282
326
  // exactly one byte). Multi-byte chunks starting with 0x1B are ANSI
283
327
  // escape sequences for arrow keys / function keys / Alt+letter, and
@@ -438,7 +482,7 @@ export function buildStateBlock(messages) {
438
482
  const olderCount = actions.length - recent.length;
439
483
  const lines = [
440
484
  '<task_state>',
441
- `Original goal: ${goal}${goal.length >= STATE_BLOCK_GOAL_MAX_CHARS ? '' : ''}`,
485
+ `Original goal: ${goal}${goal.length >= STATE_BLOCK_GOAL_MAX_CHARS ? '\u2026' : ''}`,
442
486
  `Actions completed: ${actions.length}`,
443
487
  ];
444
488
  if (olderCount > 0) {
@@ -448,7 +492,7 @@ export function buildStateBlock(messages) {
448
492
  lines.push(`Actions:`);
449
493
  }
450
494
  recent.forEach((a, i) => {
451
- lines.push(` ${i + 1}. ${a.tool}(${a.argsPreview}${a.argsPreview.length >= 80 ? '' : ''})`);
495
+ lines.push(` ${i + 1}. ${a.tool}(${a.argsPreview}${a.argsPreview.length >= 80 ? '\u2026' : ''})`);
452
496
  });
453
497
  lines.push('');
454
498
  lines.push('Stay focused on the goal. Do not re-issue actions you have already completed — refer to their results in the conversation above.');
@@ -1091,7 +1135,7 @@ export async function runQuery(ctx) {
1091
1135
  const failedModel = ctx.config.model;
1092
1136
  ctx.config.model = immediateFallback;
1093
1137
  resetClient();
1094
- console.log(theme.warning(` ${sym.warn} ${failedModel} is a known-stuck OpenRouter preview model; switching this turn to ${immediateFallback}.`));
1138
+ console.log(theme.warning(` ${sym.warn} ${failedModel} is known to stall in interactive OpenRouter sessions; switching this turn to ${immediateFallback}.`));
1095
1139
  console.log(theme.dim(' Override only if you really want it: CAWDEX_ALLOW_FLAKY_MODELS=1'));
1096
1140
  }
1097
1141
  if (!chainFastDirect) {
@@ -1103,12 +1147,12 @@ export async function runQuery(ctx) {
1103
1147
  // because we only care about "in this whole exchange did we see any
1104
1148
  // reasoning at all".
1105
1149
  let sawAnyThinking = false;
1106
- // Skill-graduation telemetry. The Hermes audit's deterministic rule
1150
+ // Skill-graduation telemetry. The Sentience audit's deterministic rule
1107
1151
  // for "this work was worth remembering" — the model is a bad judge of
1108
1152
  // its own complexity, so the dispatcher counts and decides. Thresholds:
1109
1153
  // - 5+ tool calls in this chain → complex task
1110
1154
  // - any tool errored then recovered → learned-from-failure
1111
- // Only used to inform a chain-end suggestion in hermes mode; we don't
1155
+ // Only used to inform a chain-end suggestion in sentience mode; we don't
1112
1156
  // auto-create skills (which would burn an extra LLM call). We surface
1113
1157
  // the opportunity with a one-line nudge so the user can /skill-create
1114
1158
  // or /learn if they want.
@@ -1278,6 +1322,7 @@ export async function runQuery(ctx) {
1278
1322
  let hasOutput = false;
1279
1323
  let thinkingActive = false;
1280
1324
  let leadingTrimmed = false; // strip leading whitespace from the model's first text chunk
1325
+ let assistantPrefixed = false;
1281
1326
  let lastCharWasNewline = false; // collapse 3+ consecutive newlines down to 2
1282
1327
  let consecutiveNewlines = 0;
1283
1328
  const turnStart = Date.now();
@@ -1320,6 +1365,10 @@ export async function runQuery(ctx) {
1320
1365
  if (out.length === 0)
1321
1366
  return;
1322
1367
  lastCharWasNewline = out.endsWith('\n');
1368
+ if (!assistantPrefixed) {
1369
+ process.stdout.write(assistantTranscriptPrefix());
1370
+ assistantPrefixed = true;
1371
+ }
1323
1372
  process.stdout.write(theme.primary(out));
1324
1373
  fullText += out;
1325
1374
  // ── Loop detection ────────────────────────────────────
@@ -1406,7 +1455,7 @@ export async function runQuery(ctx) {
1406
1455
  // Live waiting indicator on the response line. It keeps elapsed time
1407
1456
  // and the interrupt hint visible while still clearing itself before
1408
1457
  // the first model event writes real output.
1409
- let workingIndicator = startWorkingIndicator(turnStart, isScreenReader);
1458
+ let workingIndicator = startWorkingIndicator(turnStart, isScreenReader, turns);
1410
1459
  // Slow-model warning and first-token watchdog. The warning is a
1411
1460
  // UX hint; the watchdog is the hard recovery path for providers
1412
1461
  // that accept a request but then never produce a stream event.
@@ -1418,7 +1467,7 @@ export async function runQuery(ctx) {
1418
1467
  workingIndicator = null;
1419
1468
  console.log(chalk.yellow(` ⏳ model is taking longer than 30s. Shift+F5 cancels, Ctrl+C exits. Often means the model returned no tokens (try /model <other> if this hangs).`));
1420
1469
  if (!firstTokenSeen) {
1421
- workingIndicator = startWorkingIndicator(turnStart, isScreenReader);
1470
+ workingIndicator = startWorkingIndicator(turnStart, isScreenReader, turns);
1422
1471
  }
1423
1472
  }
1424
1473
  }, 30_000);
@@ -1426,30 +1475,63 @@ export async function runQuery(ctx) {
1426
1475
  const firstTokenTimeoutMs = fastDirect
1427
1476
  ? Math.min(resolveFirstTokenTimeoutMs(ctx.config), FAST_DIRECT_FIRST_TOKEN_TIMEOUT_MS)
1428
1477
  : resolveFirstTokenTimeoutMs(ctx.config);
1429
- const firstTokenTimer = firstTokenTimeoutMs > 0
1430
- ? setTimeout(() => {
1431
- if (!firstTokenSeen) {
1432
- firstTokenTimedOut = true;
1433
- try {
1434
- streamAbort.abort();
1435
- }
1436
- catch { /* noop */ }
1437
- }
1438
- }, firstTokenTimeoutMs)
1439
- : null;
1478
+ let streamIdleTimedOut = false;
1479
+ const streamIdleTimeoutMs = resolveStreamIdleTimeoutMs(fastDirect);
1480
+ let streamWaitTimer = null;
1440
1481
  try {
1441
1482
  const requestConfig = fastDirect
1442
1483
  ? { ...ctx.config, maxTokens: Math.min(ctx.config.maxTokens ?? 700, 700) }
1443
1484
  : ctx.config;
1444
1485
  const requestTools = fastDirect ? [] : ALL_TOOLS;
1445
- for await (const event of streamChat(requestConfig, apiMessages, requestTools, streamAbort.signal)) {
1486
+ const stream = streamChat(requestConfig, apiMessages, requestTools, streamAbort.signal);
1487
+ const iterator = stream[Symbol.asyncIterator]();
1488
+ while (true) {
1489
+ const nextPromise = iterator.next();
1490
+ let next;
1491
+ const waitTimeoutMs = !firstTokenSeen ? firstTokenTimeoutMs : streamIdleTimeoutMs;
1492
+ if (waitTimeoutMs > 0) {
1493
+ const timeoutPromise = new Promise((_, reject) => {
1494
+ streamWaitTimer = setTimeout(() => {
1495
+ try {
1496
+ streamAbort.abort();
1497
+ }
1498
+ catch { /* noop */ }
1499
+ if (!firstTokenSeen) {
1500
+ firstTokenTimedOut = true;
1501
+ reject(new Error('__CAWDEX_FIRST_TOKEN_TIMEOUT__'));
1502
+ }
1503
+ else {
1504
+ streamIdleTimedOut = true;
1505
+ reject(new Error('__CAWDEX_STREAM_IDLE_TIMEOUT__'));
1506
+ }
1507
+ }, waitTimeoutMs);
1508
+ });
1509
+ try {
1510
+ next = await Promise.race([nextPromise, timeoutPromise]);
1511
+ }
1512
+ catch (err) {
1513
+ nextPromise.catch(() => { });
1514
+ throw err;
1515
+ }
1516
+ finally {
1517
+ if (streamWaitTimer)
1518
+ clearTimeout(streamWaitTimer);
1519
+ streamWaitTimer = null;
1520
+ }
1521
+ }
1522
+ else {
1523
+ next = await nextPromise;
1524
+ }
1525
+ if (next.done)
1526
+ break;
1527
+ const event = next.value;
1446
1528
  // First event of any kind — model is alive. Cancel the slow-
1447
1529
  // model warning timer; subsequent events are normal streaming.
1448
1530
  if (!firstTokenSeen) {
1449
1531
  firstTokenSeen = true;
1450
1532
  clearTimeout(slowTimer);
1451
- if (firstTokenTimer)
1452
- clearTimeout(firstTokenTimer);
1533
+ if (streamWaitTimer)
1534
+ clearTimeout(streamWaitTimer);
1453
1535
  // Tear down the live waiting indicator so the next print
1454
1536
  // (thinking header, response text, or tool call) lands cleanly.
1455
1537
  workingIndicator?.stop();
@@ -1499,17 +1581,24 @@ export async function runQuery(ctx) {
1499
1581
  totalTokens: u.total || u.prompt + u.completion,
1500
1582
  estimatedCostUsd: cost,
1501
1583
  });
1584
+ setFooterCost(cost, u.prompt, u.completion);
1502
1585
  // Single newline separator if we just streamed text, then the
1503
1586
  // compact telemetry line.
1504
1587
  if (hasOutput && !lastCharWasNewline)
1505
1588
  process.stdout.write('\n');
1506
1589
  printCost(u.prompt, u.completion, cost, warning, Date.now() - turnStart);
1507
1590
  }
1591
+ try {
1592
+ streamAbort.abort();
1593
+ }
1594
+ catch { /* close any provider socket left open after done */ }
1595
+ void iterator.return?.(undefined).catch(() => { });
1596
+ break;
1508
1597
  }
1509
1598
  }
1510
1599
  clearTimeout(slowTimer);
1511
- if (firstTokenTimer)
1512
- clearTimeout(firstTokenTimer);
1600
+ if (streamWaitTimer)
1601
+ clearTimeout(streamWaitTimer);
1513
1602
  workingIndicator?.stop();
1514
1603
  workingIndicator = null;
1515
1604
  }
@@ -1518,8 +1607,8 @@ export async function runQuery(ctx) {
1518
1607
  // timer so its 30s callback doesn't fire after the error is
1519
1608
  // already on stdout (would look like a false positive).
1520
1609
  clearTimeout(slowTimer);
1521
- if (firstTokenTimer)
1522
- clearTimeout(firstTokenTimer);
1610
+ if (streamWaitTimer)
1611
+ clearTimeout(streamWaitTimer);
1523
1612
  // Tear down the waiting indicator if it's still ticking — error
1524
1613
  // print below shouldn't trail an animated row.
1525
1614
  workingIndicator?.stop();
@@ -1542,10 +1631,40 @@ export async function runQuery(ctx) {
1542
1631
  }
1543
1632
  const timeoutMsg = `${failedModel} produced no stream events for ${formatDuration(firstTokenTimeoutMs)}`;
1544
1633
  console.log(theme.error(` ${sym.warn} ${timeoutMsg}.`));
1545
- console.log(theme.dim(' The request was cancelled so the prompt can recover. Try /openrouter-free or /model <known-good-model>.'));
1634
+ console.log(theme.dim(' No fallback is configured, so the request was cancelled and the prompt can recover.'));
1635
+ console.log(theme.dim(' Use /fallback <model-id> to enable automatic retry, or /model <known-good-model> to switch primary models.'));
1546
1636
  ctx.messages.push({ role: 'assistant', content: `[Provider timeout: ${timeoutMsg}]` });
1547
1637
  break;
1548
1638
  }
1639
+ if (streamIdleTimedOut) {
1640
+ const failedModel = ctx.config.model;
1641
+ const timeoutMsg = `${failedModel} stream stalled for ${formatDuration(streamIdleTimeoutMs)} after the first event`;
1642
+ const fallback = !hasOutput && !toolCalls
1643
+ ? fallbackModelForTurn(ctx.config, usedFallbackModel)
1644
+ : null;
1645
+ if (fallback) {
1646
+ usedFallbackModel = true;
1647
+ ctx.config.model = fallback;
1648
+ resetClient();
1649
+ console.log(theme.warning(` ${sym.warn} ${timeoutMsg} — retrying once with fallback model ${fallback}.`));
1650
+ console.log(theme.dim(' Configure with /fallback <model-id>, disable with /fallback off, or switch now with /openrouter-free.'));
1651
+ turns--;
1652
+ continue;
1653
+ }
1654
+ if (fullText.trim()) {
1655
+ const partial = `${fullText.trimEnd()}\n[Provider timeout: stream stalled before completion]`;
1656
+ console.log(theme.warning(` ${sym.warn} stream stalled before completion; returning the partial response.`));
1657
+ accumulatedAssistantText += (accumulatedAssistantText ? '\n\n' : '') + partial;
1658
+ ctx.messages.push({ role: 'assistant', content: partial });
1659
+ }
1660
+ else {
1661
+ console.log(theme.error(` ${sym.warn} ${timeoutMsg}.`));
1662
+ console.log(theme.dim(' The request was cancelled so the prompt can recover instead of hanging indefinitely.'));
1663
+ console.log(theme.dim(' Use /fallback <model-id> to enable automatic retry, or /model <known-good-model> to switch primary models.'));
1664
+ ctx.messages.push({ role: 'assistant', content: `[Provider timeout: ${timeoutMsg}]` });
1665
+ }
1666
+ break;
1667
+ }
1549
1668
  // ── User cancel path (graceful — not an error) ─────────
1550
1669
  // If the user pressed Ctrl+G / Esc / F5 during streaming, the
1551
1670
  // AbortController fired and the OpenAI SDK threw something like
@@ -1648,7 +1767,8 @@ export async function runQuery(ctx) {
1648
1767
  }
1649
1768
  const emptyMsg = `${failedModel} returned an empty response`;
1650
1769
  console.log(theme.error(` ${sym.warn} ${emptyMsg}.`));
1651
- console.log(theme.dim(' Try /openrouter-free or /model <known-good-model>.'));
1770
+ console.log(theme.dim(' No fallback is configured, so this empty response was not retried.'));
1771
+ console.log(theme.dim(' Use /fallback <model-id> to enable automatic retry, or /model <known-good-model> to switch primary models.'));
1652
1772
  ctx.messages.push({ role: 'assistant', content: `[Provider empty response: ${emptyMsg}]` });
1653
1773
  break;
1654
1774
  }
@@ -1810,10 +1930,10 @@ export async function runQuery(ctx) {
1810
1930
  console.log(theme.dim(` Reasoning models (DeepSeek-R1, o1, Claude with extended thinking) emit them; most general-purpose models don't.`));
1811
1931
  console.log(theme.dim(` Hide this hint with /thinking (toggles off).`));
1812
1932
  }
1813
- // ── Skill graduation hint (Hermes audit, M2 item 2) ─────────
1933
+ // ── Skill graduation hint (Sentience audit, M2 item 2) ─────────
1814
1934
  // Deterministic "this work was worth remembering" trigger. The model
1815
- // is a bad judge of its own complexity (Hermes audit's exact wording);
1816
- // we count instead. Fires at most once per chain in hermes mode, and
1935
+ // is a bad judge of its own complexity (Sentience audit's exact wording);
1936
+ // we count instead. Fires at most once per chain in sentience mode, and
1817
1937
  // only when a clear threshold was crossed:
1818
1938
  //
1819
1939
  // - 5+ tool calls → complex multi-step task
@@ -1821,9 +1941,9 @@ export async function runQuery(ctx) {
1821
1941
  //
1822
1942
  // We don't auto-execute /skill-create (it would burn another LLM call
1823
1943
  // and might extract noise); we surface the opportunity so the user can
1824
- // decide. Outside hermes mode, this is silent — keeps the noise floor
1944
+ // decide. Outside sentience mode, this is silent — keeps the noise floor
1825
1945
  // low for regular dev/review/debug sessions.
1826
- if (ctx.mode === 'hermes') {
1946
+ if (ctx.mode === 'sentience' || ctx.mode === 'hermes') {
1827
1947
  const complex = chainStats.toolCallCount >= 5;
1828
1948
  const learnedFromFailure = chainStats.sawToolError && chainStats.sawToolRecovery;
1829
1949
  if (complex || learnedFromFailure) {
@@ -1832,7 +1952,7 @@ export async function runQuery(ctx) {
1832
1952
  : complex
1833
1953
  ? `${chainStats.toolCallCount} tools — complex enough that a learned pattern might save time next time`
1834
1954
  : `recovered from a tool error — the working path is worth banking`;
1835
- console.log(theme.dim(` [hermes] graduation candidate: ${reason}.`));
1955
+ console.log(theme.dim(` [sentience] graduation candidate: ${reason}.`));
1836
1956
  console.log(theme.dim(` Run /skill-create or /learn to bank it. Skip if it was one-off.`));
1837
1957
  }
1838
1958
  }