cawdex 1.35.75 → 1.35.76

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 (84) hide show
  1. package/README.md +5 -5
  2. package/bin/anycode.js +2 -2
  3. package/bin/cawdex.js +408 -408
  4. package/bin/ecc-hooks.cjs +11 -11
  5. package/dist/agents-md.d.ts +31 -0
  6. package/dist/agents-md.js +340 -0
  7. package/dist/agents-md.js.map +1 -0
  8. package/dist/agents.js +1424 -1424
  9. package/dist/api.d.ts +1 -0
  10. package/dist/api.js +19 -14
  11. package/dist/api.js.map +1 -1
  12. package/dist/autonomous-loops.js +287 -287
  13. package/dist/benchmark-repos.d.ts +31 -0
  14. package/dist/benchmark-repos.js +234 -8
  15. package/dist/benchmark-repos.js.map +1 -1
  16. package/dist/command-palette.js +4 -2
  17. package/dist/command-palette.js.map +1 -1
  18. package/dist/compaction.js +8 -8
  19. package/dist/config.js +51 -36
  20. package/dist/config.js.map +1 -1
  21. package/dist/content-engine.js +543 -543
  22. package/dist/context-brief.d.ts +4 -0
  23. package/dist/context-brief.js +230 -0
  24. package/dist/context-brief.js.map +1 -0
  25. package/dist/cost-tracker.d.ts +33 -14
  26. package/dist/cost-tracker.js +81 -19
  27. package/dist/cost-tracker.js.map +1 -1
  28. package/dist/coverage.js +39 -39
  29. package/dist/docs-sync.js +98 -98
  30. package/dist/evaluation.js +452 -452
  31. package/dist/fixed-footer.d.ts +7 -1
  32. package/dist/fixed-footer.js +92 -18
  33. package/dist/fixed-footer.js.map +1 -1
  34. package/dist/git-workflow.js +49 -49
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +161 -63
  37. package/dist/index.js.map +1 -1
  38. package/dist/live-queue.js +1 -1
  39. package/dist/live-queue.js.map +1 -1
  40. package/dist/model-aliases.d.ts +37 -0
  41. package/dist/model-aliases.js +203 -0
  42. package/dist/model-aliases.js.map +1 -0
  43. package/dist/orchestration.js +15 -15
  44. package/dist/permissions.d.ts +6 -0
  45. package/dist/permissions.js +53 -0
  46. package/dist/permissions.js.map +1 -1
  47. package/dist/pm2-manager.js +26 -26
  48. package/dist/query.d.ts +0 -1
  49. package/dist/query.js +74 -39
  50. package/dist/query.js.map +1 -1
  51. package/dist/refactor.js +87 -87
  52. package/dist/repo-command.js +7 -1
  53. package/dist/repo-command.js.map +1 -1
  54. package/dist/search-first.js +92 -92
  55. package/dist/skill-create.js +100 -100
  56. package/dist/stitch.js +1 -1
  57. package/dist/system-prompt.d.ts +2 -1
  58. package/dist/system-prompt.js +10 -5
  59. package/dist/system-prompt.js.map +1 -1
  60. package/dist/tools/github-repo-digest.d.ts +1 -1
  61. package/dist/tools/github-repo-digest.js +38 -6
  62. package/dist/tools/github-repo-digest.js.map +1 -1
  63. package/dist/types.d.ts +3 -0
  64. package/dist/types.js.map +1 -1
  65. package/dist/verification.js +55 -55
  66. package/package.json +1 -1
  67. package/resources/__init__.py +1 -1
  68. package/resources/exgentic/cawdex_agent/README.md +114 -114
  69. package/resources/exgentic/cawdex_agent/__init__.py +5 -5
  70. package/resources/exgentic/cawdex_agent/agent.py +605 -605
  71. package/resources/exgentic/cawdex_agent/requirements.txt +2 -2
  72. package/resources/exgentic/cawdex_agent/setup.sh +21 -21
  73. package/resources/exgentic/cawdex_agent/utils.py +1061 -1061
  74. package/resources/hal/cawdex_agent/README.md +24 -24
  75. package/resources/hal/cawdex_agent/__init__.py +1 -1
  76. package/resources/hal/cawdex_agent/main.py +550 -550
  77. package/resources/hal/cawdex_agent/requirements.txt +2 -2
  78. package/resources/kbench/cawdex_agent/README.md +107 -107
  79. package/resources/kbench/cawdex_agent/adapter.manifest.json +19 -19
  80. package/resources/kbench/cawdex_agent/runner.mjs +753 -753
  81. package/resources/open_agent_leaderboard/cawdex-agent-card.md +119 -119
  82. package/resources/terminal_bench/__init__.py +1 -1
  83. package/resources/terminal_bench/cawdex_agent.py +174 -174
  84. package/resources/terminal_bench/setup.sh +121 -121
package/dist/index.js CHANGED
@@ -10,11 +10,14 @@ import { initDebug, emit as dbgEmit, setDebugLevel, getDebugStatus, tailDebug }
10
10
  import chalk from 'chalk';
11
11
  import { loadConfig, saveConfig, configExists, getConfigDir, loadConfigFromEnv, applyRuntimeConfigOverrides } from './config.js';
12
12
  import { resetClient } from './api.js';
13
+ import { explainPermission } from './permissions.js';
13
14
  import { CHATGPT_CODEX_BASE_URL, getOpenAICodexAuthStatus, runCodexLogin, } from './openai-oauth.js';
14
15
  import { formatOpenAICodexSmokeResult, runOpenAICodexSmokeTest } from './openai-smoke.js';
15
- import { fallbackModelForKnownFlakyTurn, isKnownFlakyOpenRouterModel, isTurnCancelKeySequence, runQuery } from './query.js';
16
+ import { isKnownFlakyOpenRouterModel, isTurnCancelKeySequence, runQuery } from './query.js';
16
17
  import { ALL_TOOLS } from './tools/index.js';
17
18
  import { buildHarnessComponentsReport } from './tools/harness-components.js';
19
+ import { buildContextBrief } from './context-brief.js';
20
+ import { formatAgentsInstructionsReport } from './agents-md.js';
18
21
  import { GitHubRepoDigestTool } from './tools/github-repo-digest.js';
19
22
  import { ResearchSourcesTool } from './tools/research-sources.js';
20
23
  import { decodeBenchmarkReposSentinel, encodeBenchmarkReposSentinel, formatBenchmarkRepoCatalog, formatBenchmarkRepoCatalogUsage, parseBenchmarkRepoCatalogCommandArgs, } from './benchmark-repos.js';
@@ -28,7 +31,8 @@ import { printUsageSummary, setBudget } from './cost-tracker.js';
28
31
  import { getCompactionStats, OPENROUTER_FREE_ROUTER_SAFE_CONTEXT_WINDOW_TOKENS, OPENROUTER_UNKNOWN_FREE_MODEL_CONTEXT_WINDOW_TOKENS, } from './compaction.js';
29
32
  import { extractPatterns, printInstinctStatus, pruneExpired, listInstincts, exportInstincts, importInstincts } from './learning.js';
30
33
  import { MODES, listModes, normalizeModeName } from './modes.js';
31
- import { printModelOptions, switchModel, classifyComplexity, routeModel } from './model-router.js';
34
+ import { printModelOptions, classifyComplexity, routeModel } from './model-router.js';
35
+ import { deleteModelAlias, formatModelResolution, formatTurnOverride, listModelAliases, normalizeReasoningEffort, parseModelOnce, resolveModelReference, setModelAlias, tokenizeModelCommandArgs, } from './model-aliases.js';
32
36
  import { buildCommitPrompt, buildPRPrompt, printDiff, printLog } from './git-workflow.js';
33
37
  import { buildReviewPrompt, buildTDDPrompt, buildSecurityReviewPrompt, runAudit, printAuditReport, buildPlanPrompt, buildE2EPrompt, buildBuildFixPrompt, buildEvalPrompt, buildBenchmarkPrompt, splitBenchmarkArgs, } from './evaluation.js';
34
38
  import { buildDoctorReport, formatDoctorReport, runDoctorCli } from './doctor.js';
@@ -367,25 +371,13 @@ async function setupWizard(rl, currentConfig) {
367
371
  const modelInput = await rl.question(chalk.yellow(` Model [${model}]: `));
368
372
  if (modelInput.trim())
369
373
  model = modelInput.trim();
370
- // Auto-heal known-slow / known-flaky OpenRouter models. These
371
- // IDs frequently return empty responses, the literal string "ERROR",
372
- // or no first stream event. Warning-only was not enough: users could
373
- // save a broken first-run config and every prompt looked like it was
374
- // stuck in the live queue. Keep an explicit env escape hatch for model
375
- // debugging, but default normal users back to the free router.
374
+ // Warn only. The user-selected model is part of their config and must
375
+ // never be silently replaced by a "safer" default.
376
376
  if (isKnownFlakyOpenRouterModel({ provider: provider.name, model })) {
377
377
  console.log('');
378
378
  console.log(chalk.yellow(` ⚠ "${model}" is known to stall badly in interactive OpenRouter sessions`));
379
- console.log(chalk.yellow(` or return empty / "ERROR" responses.`));
380
- if (process.env.CAWDEX_ALLOW_FLAKY_MODELS === '1') {
381
- console.log(chalk.dim(' Keeping it because CAWDEX_ALLOW_FLAKY_MODELS=1 is set.'));
382
- }
383
- else {
384
- const safer = providerKey === 'openrouter' ? PROVIDERS.openrouter.defaultModel : provider.defaultModel;
385
- console.log(chalk.dim(` Using safer default instead: ${safer}`));
386
- console.log(chalk.dim(' Override only for debugging: CAWDEX_ALLOW_FLAKY_MODELS=1 cawdex'));
387
- model = safer;
388
- }
379
+ console.log(chalk.yellow(` or return empty / "ERROR" responses. Keeping your selected model.`));
380
+ console.log(chalk.dim(' Configure /fallback explicitly if you want an automatic retry after a real failure.'));
389
381
  console.log('');
390
382
  }
391
383
  if (providerKey === 'openrouter' && !isOpenRouterFreeModelId(model)) {
@@ -526,8 +518,6 @@ async function setupWizard(rl, currentConfig) {
526
518
  console.log(chalk.dim(` Configured: /model, /fallback, /perm, and /swarm. Memory stays under /memory.`));
527
519
  console.log();
528
520
  return config;
529
- console.log();
530
- return config;
531
521
  }
532
522
  /**
533
523
  * Pretty-print a resumed session's conversation history to stdout so
@@ -964,6 +954,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
964
954
  console.log(d(' ') + c('/harness') + d(' — file-level harness component map'));
965
955
  console.log(d(' ') + c('/rules') + d(' — show coding rules'));
966
956
  console.log(d(' ') + c('/perm <mode>') + d(' — set permission mode (ask/auto/yolo); no arg shows current + always-allow list'));
957
+ console.log(d(' ') + c('/perm why <tool>') + d(' — explain whether a tool call allows, prompts, or blocks'));
967
958
  console.log(d(' ') + c('/perm-reset') + d(' — clear the per-tool always-allow list'));
968
959
  console.log(d(' ') + c('/sandbox [level]') + d(' — OS-native bash sandbox (off / standard / strict)'));
969
960
  console.log(d(' ') + c('/dry-run') + d(' — toggle dry-run mode'));
@@ -977,6 +968,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
977
968
  console.log(d(' ') + c('/update-docs') + d(' — sync documentation with code'));
978
969
  console.log(d(' ') + c('/checkpoint [label]') + d(' — save git state checkpoint'));
979
970
  console.log(d(' ') + c('/checkpoints') + d(' — list saved checkpoints'));
971
+ console.log(d(' ') + c('/context brief') + d(' — cheap local repo/context preflight'));
980
972
  console.log(d(' ') + c('/search-first <task>') + d(' — research before coding'));
981
973
  console.log(d(' ') + c('/sources <query>') + d(' — direct arXiv/GitHub/HF/Kaggle source scan'));
982
974
  console.log(d(' ') + c('/benchmark-repos') + d(' — public Terminal-Bench repo catalog'));
@@ -1399,19 +1391,70 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1399
1391
  // ── Model ─────────────────────────────────────────
1400
1392
  case '/model':
1401
1393
  if (args) {
1402
- const newModel = switchModel(config, args);
1403
- if (newModel) {
1404
- applyModelSelection(config, newModel);
1394
+ const tokens = tokenizeModelCommandArgs(args);
1395
+ const sub = (tokens[0] || '').toLowerCase();
1396
+ if (sub === 'alias' || sub === 'aliases') {
1397
+ if (tokens.length === 1 || sub === 'aliases') {
1398
+ const aliases = listModelAliases(config);
1399
+ console.log(chalk.cyan('\n Model aliases:'));
1400
+ for (const item of aliases) {
1401
+ const source = item.source === 'builtin' ? 'built-in' : 'user';
1402
+ console.log(chalk.dim(` ${item.alias.padEnd(18)} -> ${item.model} (${source})`));
1403
+ }
1404
+ console.log(chalk.dim('\n Set: /model alias <name> <model-id> Remove: /model unalias <name>'));
1405
+ return { handled: true };
1406
+ }
1407
+ const alias = tokens[1] || '';
1408
+ const target = tokens.slice(2).join(' ');
1409
+ const set = setModelAlias(config, alias, target);
1410
+ if (!set.ok) {
1411
+ console.log(chalk.yellow(` ${set.error}`));
1412
+ return { handled: true };
1413
+ }
1405
1414
  saveConfig(config);
1406
- resetClient();
1407
- console.log(chalk.green(` Model: ${config.model}`));
1415
+ console.log(chalk.green(` Model alias ${alias.toLowerCase()} -> ${set.model}`));
1416
+ return { handled: true };
1408
1417
  }
1409
- else {
1410
- applyModelSelection(config, args);
1411
- saveConfig(config);
1412
- resetClient();
1413
- console.log(chalk.green(` Model: ${config.model} (custom)`));
1418
+ if (sub === 'unalias' || sub === 'remove' || sub === 'rm' || sub === 'delete') {
1419
+ const alias = tokens[1] || '';
1420
+ if (!alias) {
1421
+ console.log(chalk.yellow(' Usage: /model unalias <name>'));
1422
+ return { handled: true };
1423
+ }
1424
+ const removed = deleteModelAlias(config, alias);
1425
+ if (removed) {
1426
+ saveConfig(config);
1427
+ console.log(chalk.green(` Removed model alias ${alias.toLowerCase()}`));
1428
+ }
1429
+ else {
1430
+ console.log(chalk.yellow(` No user model alias named ${alias}.`));
1431
+ }
1432
+ return { handled: true };
1414
1433
  }
1434
+ if (sub === 'once' || sub === 'next') {
1435
+ const parsedOnce = parseModelOnce(config, tokens.slice(1).join(' '));
1436
+ if (parsedOnce.error || !parsedOnce.override) {
1437
+ console.log(chalk.yellow(` ${parsedOnce.error || 'Could not parse one-shot model override.'}`));
1438
+ return { handled: true };
1439
+ }
1440
+ console.log(chalk.green(` Next turn override: ${formatTurnOverride(parsedOnce.override, config.model)}`));
1441
+ return { handled: true, turnOverride: parsedOnce.override };
1442
+ }
1443
+ if (sub === 'effort' || sub === 'reasoning') {
1444
+ const effort = normalizeReasoningEffort(tokens[1]);
1445
+ if (!effort) {
1446
+ console.log(chalk.yellow(' Usage: /model effort none|minimal|low|medium|high|xhigh'));
1447
+ return { handled: true };
1448
+ }
1449
+ const turnOverride = { reasoningEffort: effort, source: 'manual' };
1450
+ console.log(chalk.green(` Next turn reasoning effort: ${effort}`));
1451
+ return { handled: true, turnOverride };
1452
+ }
1453
+ const resolvedModel = resolveModelReference(config, args);
1454
+ applyModelSelection(config, resolvedModel.model);
1455
+ saveConfig(config);
1456
+ resetClient();
1457
+ console.log(chalk.green(` Model: ${formatModelResolution(resolvedModel)}`));
1415
1458
  return { handled: true };
1416
1459
  }
1417
1460
  // No args. On OpenRouter, the user wants the interactive
@@ -1855,10 +1898,61 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1855
1898
  console.log(report.output);
1856
1899
  return { handled: true };
1857
1900
  }
1901
+ case '/context': {
1902
+ const parts = args.split(/\s+/).map((part) => part.trim()).filter(Boolean);
1903
+ const sub = (parts.shift() || '').toLowerCase();
1904
+ if (!sub || sub === 'brief') {
1905
+ const target = parts.join(' ').trim() || process.cwd();
1906
+ console.log(buildContextBrief(target));
1907
+ }
1908
+ else {
1909
+ console.log(chalk.yellow(' Usage: /context brief [path]'));
1910
+ }
1911
+ return { handled: true };
1912
+ }
1858
1913
  case '/rules':
1859
1914
  printRules();
1860
1915
  return { handled: true };
1916
+ case '/agents':
1917
+ console.log(formatAgentsInstructionsReport(process.cwd(), config.model, ALL_TOOLS));
1918
+ return { handled: true };
1861
1919
  case '/perm':
1920
+ if (args.trim().toLowerCase().startsWith('why')) {
1921
+ const parts = args.split(/\s+/).filter(Boolean);
1922
+ parts.shift();
1923
+ const toolName = parts.shift();
1924
+ if (!toolName) {
1925
+ console.log(chalk.yellow(' Usage: /perm why <tool> [command-or-path]'));
1926
+ console.log(chalk.dim(' Examples: /perm why bash git status'));
1927
+ console.log(chalk.dim(' /perm why write_file src/example.ts'));
1928
+ return { handled: true };
1929
+ }
1930
+ const tool = ALL_TOOLS.find((candidate) => candidate.name === toolName);
1931
+ if (!tool) {
1932
+ console.log(chalk.red(` Unknown tool: ${toolName}`));
1933
+ console.log(chalk.dim(' Run /tools to list available tool names.'));
1934
+ return { handled: true };
1935
+ }
1936
+ const rest = parts.join(' ');
1937
+ const input = tool.name === 'bash'
1938
+ ? { command: rest }
1939
+ : tool.name === 'write_file' || tool.name === 'edit_file' || tool.name === 'read_file'
1940
+ ? { file_path: rest }
1941
+ : {};
1942
+ const explanation = explainPermission(tool, input, config);
1943
+ const color = explanation.decision === 'allow'
1944
+ ? chalk.green
1945
+ : explanation.decision === 'deny'
1946
+ ? chalk.red
1947
+ : chalk.yellow;
1948
+ console.log(chalk.cyan('\n Permission explanation'));
1949
+ console.log(color(` Decision: ${explanation.decision.toUpperCase()} - ${explanation.reason}`));
1950
+ for (const line of explanation.lines)
1951
+ console.log(chalk.dim(` ${line}`));
1952
+ const sandbox = config.sandbox?.level || 'off';
1953
+ console.log(chalk.dim(` sandbox: ${sandbox} (separate from permission prompts)`));
1954
+ return { handled: true };
1955
+ }
1862
1956
  if (args && ['ask', 'auto', 'yolo'].includes(args)) {
1863
1957
  config.permissionMode = args;
1864
1958
  saveConfig(config);
@@ -1976,7 +2070,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1976
2070
  }
1977
2071
  // ── Learning & Cost ───────────────────────────────
1978
2072
  case '/usage':
1979
- printUsageSummary();
2073
+ printUsageSummary(session.id);
1980
2074
  return { handled: true };
1981
2075
  case '/budget': {
1982
2076
  const [daily, monthly] = args.split(/\s+/).map(Number);
@@ -3529,34 +3623,6 @@ async function main() {
3529
3623
  // for screen readers, so they're force-off in that mode. Sighted
3530
3624
  // users get them by default; the CAWDEX_ANIMATIONS=0 env var still
3531
3625
  // overrides for users who specifically don't want the motion.
3532
- // Saved configs can contain OpenRouter preview models that are known
3533
- // to hang before the first token. Do not let the CLI boot into that
3534
- // broken state by default; keep an env escape hatch for deliberate
3535
- // provider debugging.
3536
- const runtimeModelOverride = !!process.env.CAWDEX_MODEL_OVERRIDE || !!process.env.CAWDEX_MODEL;
3537
- const knownFlakyFallback = fallbackModelForKnownFlakyTurn(config)
3538
- || (process.env.CAWDEX_ALLOW_FLAKY_MODELS !== '1'
3539
- && isKnownFlakyOpenRouterModel(config)
3540
- && /openrouter/i.test(config.provider)
3541
- ? PROVIDERS.openrouter.defaultModel
3542
- : null);
3543
- if (knownFlakyFallback) {
3544
- const previousModel = config.model;
3545
- applyModelSelection(config, knownFlakyFallback);
3546
- config.fallbackModel = knownFlakyFallback;
3547
- resetClient();
3548
- if (!nonInteractive && !runtimeModelOverride) {
3549
- saveConfig(config);
3550
- console.log(theme.warning(` ⚠ Saved model "${previousModel}" is known to stall; switched config to ${knownFlakyFallback}.`));
3551
- console.log(theme.dim(' Use CAWDEX_ALLOW_FLAKY_MODELS=1 only if you are deliberately testing that model.'));
3552
- console.log('');
3553
- }
3554
- else if (!nonInteractive) {
3555
- console.log(theme.warning(` ⚠ Requested model "${previousModel}" is known to stall; using ${knownFlakyFallback} for this session.`));
3556
- console.log(theme.dim(' Use CAWDEX_ALLOW_FLAKY_MODELS=1 to force the requested model.'));
3557
- console.log('');
3558
- }
3559
- }
3560
3626
  {
3561
3627
  const { setAnimationConfig } = await import('./animations.js');
3562
3628
  setAnimationConfig({
@@ -3648,6 +3714,29 @@ async function main() {
3648
3714
  },
3649
3715
  });
3650
3716
  let autoRoute = false;
3717
+ let nextTurnOverride = null;
3718
+ const consumeTurnConfig = () => {
3719
+ if (!nextTurnOverride)
3720
+ return config;
3721
+ const override = nextTurnOverride;
3722
+ nextTurnOverride = null;
3723
+ const turnConfig = {
3724
+ ...config,
3725
+ memory: config.memory ? { ...config.memory } : config.memory,
3726
+ sandbox: config.sandbox ? { ...config.sandbox } : config.sandbox,
3727
+ voice: config.voice ? { ...config.voice } : config.voice,
3728
+ openaiAuth: config.openaiAuth ? { ...config.openaiAuth } : config.openaiAuth,
3729
+ swarm: config.swarm ? { ...config.swarm } : config.swarm,
3730
+ footer: config.footer ? { ...config.footer } : config.footer,
3731
+ modelAliases: config.modelAliases ? { ...config.modelAliases } : config.modelAliases,
3732
+ };
3733
+ if (override.model)
3734
+ applyModelSelection(turnConfig, override.model);
3735
+ if (override.reasoningEffort)
3736
+ turnConfig.reasoningEffort = override.reasoningEffort;
3737
+ console.log(chalk.dim(` [one-shot: ${formatTurnOverride(override, config.model)}]`));
3738
+ return turnConfig;
3739
+ };
3651
3740
  // ── F-key hotkey listener ────────────────────────────────
3652
3741
  // Voice / accessibility hotkeys — all on the F-row.
3653
3742
  //
@@ -4652,6 +4741,10 @@ async function main() {
4652
4741
  messages.length = 0;
4653
4742
  messages.push(...result.newMessages);
4654
4743
  }
4744
+ if (result.turnOverride !== undefined) {
4745
+ nextTurnOverride = result.turnOverride;
4746
+ }
4747
+ syncFooter();
4655
4748
  if (trimmed.startsWith('/config') && !result?.shouldExit) {
4656
4749
  deactivateFooter();
4657
4750
  config = await setupWizard(rl, config);
@@ -4814,7 +4907,10 @@ async function main() {
4814
4907
  messages.push({ role: 'user', content: result.injectPrompt });
4815
4908
  }
4816
4909
  await saveWithSnapshot();
4817
- await runQuery({ config, messages, cwd: process.cwd(), rl, sessionId: session.id, mode: mode.current });
4910
+ const turnConfig = consumeTurnConfig();
4911
+ updateFooter(buildFooterSnapshot(turnConfig, mode.current, session, process.cwd()));
4912
+ await runQuery({ config: turnConfig, messages, cwd: process.cwd(), rl, sessionId: session.id, mode: mode.current });
4913
+ syncFooter();
4818
4914
  await saveWithSnapshot();
4819
4915
  continue;
4820
4916
  }
@@ -4843,22 +4939,24 @@ async function main() {
4843
4939
  const route = routeModel(config, complexity);
4844
4940
  if (route.model !== config.model) {
4845
4941
  console.log(chalk.dim(` [routing: ${route.reason}]`));
4846
- applyModelSelection(config, route.model);
4847
- resetClient();
4942
+ nextTurnOverride = { model: route.model, source: 'route' };
4848
4943
  }
4849
4944
  autoRoute = false;
4850
4945
  }
4851
4946
  // Add user message and run query
4852
4947
  messages.push({ role: 'user', content: trimmed });
4853
4948
  await saveWithSnapshot();
4949
+ const turnConfig = consumeTurnConfig();
4950
+ updateFooter(buildFooterSnapshot(turnConfig, mode.current, session, process.cwd()));
4854
4951
  await runQuery({
4855
- config,
4952
+ config: turnConfig,
4856
4953
  messages,
4857
4954
  cwd: process.cwd(),
4858
4955
  rl,
4859
4956
  sessionId: session.id,
4860
4957
  mode: mode.current,
4861
4958
  });
4959
+ syncFooter();
4862
4960
  // Auto-save session
4863
4961
  await saveWithSnapshot();
4864
4962
  // Strategic compaction check