cawdex 1.35.74 → 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 (87) 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 +197 -65
  37. package/dist/index.js.map +1 -1
  38. package/dist/instant-artifact.d.ts +6 -0
  39. package/dist/instant-artifact.js +397 -0
  40. package/dist/instant-artifact.js.map +1 -0
  41. package/dist/live-queue.js +1 -1
  42. package/dist/live-queue.js.map +1 -1
  43. package/dist/model-aliases.d.ts +37 -0
  44. package/dist/model-aliases.js +203 -0
  45. package/dist/model-aliases.js.map +1 -0
  46. package/dist/orchestration.js +15 -15
  47. package/dist/permissions.d.ts +6 -0
  48. package/dist/permissions.js +53 -0
  49. package/dist/permissions.js.map +1 -1
  50. package/dist/pm2-manager.js +26 -26
  51. package/dist/query.d.ts +0 -1
  52. package/dist/query.js +74 -39
  53. package/dist/query.js.map +1 -1
  54. package/dist/refactor.js +87 -87
  55. package/dist/repo-command.js +7 -1
  56. package/dist/repo-command.js.map +1 -1
  57. package/dist/search-first.js +92 -92
  58. package/dist/skill-create.js +100 -100
  59. package/dist/stitch.js +1 -1
  60. package/dist/system-prompt.d.ts +2 -1
  61. package/dist/system-prompt.js +10 -5
  62. package/dist/system-prompt.js.map +1 -1
  63. package/dist/tools/github-repo-digest.d.ts +1 -1
  64. package/dist/tools/github-repo-digest.js +38 -6
  65. package/dist/tools/github-repo-digest.js.map +1 -1
  66. package/dist/types.d.ts +3 -0
  67. package/dist/types.js.map +1 -1
  68. package/dist/verification.js +55 -55
  69. package/package.json +1 -1
  70. package/resources/__init__.py +1 -1
  71. package/resources/exgentic/cawdex_agent/README.md +114 -114
  72. package/resources/exgentic/cawdex_agent/__init__.py +5 -5
  73. package/resources/exgentic/cawdex_agent/agent.py +605 -605
  74. package/resources/exgentic/cawdex_agent/requirements.txt +2 -2
  75. package/resources/exgentic/cawdex_agent/setup.sh +21 -21
  76. package/resources/exgentic/cawdex_agent/utils.py +1061 -1061
  77. package/resources/hal/cawdex_agent/README.md +24 -24
  78. package/resources/hal/cawdex_agent/__init__.py +1 -1
  79. package/resources/hal/cawdex_agent/main.py +550 -550
  80. package/resources/hal/cawdex_agent/requirements.txt +2 -2
  81. package/resources/kbench/cawdex_agent/README.md +107 -107
  82. package/resources/kbench/cawdex_agent/adapter.manifest.json +19 -19
  83. package/resources/kbench/cawdex_agent/runner.mjs +753 -753
  84. package/resources/open_agent_leaderboard/cawdex-agent-card.md +119 -119
  85. package/resources/terminal_bench/__init__.py +1 -1
  86. package/resources/terminal_bench/cawdex_agent.py +174 -174
  87. 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';
@@ -88,8 +92,9 @@ import { COMMAND_CATALOG, allSlashCommandNames, completeSlashCommandNames, resol
88
92
  import { inlineSuggest, resolveInlineSuggestQuestionInput } from './inline-suggest.js';
89
93
  import { normalizeTypeaheadDraftForPrompt } from './prompt-buffer.js';
90
94
  import { maybeInstantAnswer } from './instant-answer.js';
95
+ import { maybeCreateInstantArtifact } from './instant-artifact.js';
91
96
  import { getCurrentVersion, startStartupUpdateCheck } from './updater.js';
92
- import { activateFooter, askWithFooterPrompt, buildFooterSnapshot, deactivateFooter, isFooterActive, shouldUseFixedFooter, updateFooter, } from './fixed-footer.js';
97
+ import { activateFooter, askWithFooterPrompt, buildFooterSnapshot, deactivateFooter, isFooterActive, shouldUseFixedFooter, updateFooter, writeScrollableLine, } from './fixed-footer.js';
93
98
  /**
94
99
  * Unified prompt resolver — prefers the bundled ECC prompt for a given
95
100
  * intent and falls back to the built-in builder when ECC isn't installed.
@@ -103,6 +108,15 @@ function buildUnifiedPrompt(eccName, args, builtin) {
103
108
  return builtin();
104
109
  return args.trim() ? `${ecc}\n\n## User Input\n\n${args}` : ecc;
105
110
  }
111
+ function printLocalResponse(message) {
112
+ if (!isFooterActive()) {
113
+ console.log(theme.primary(message));
114
+ return;
115
+ }
116
+ for (const line of message.split(/\r?\n/)) {
117
+ writeScrollableLine(line ? theme.primary(line) : '');
118
+ }
119
+ }
106
120
  function openRouterContextHintForModel(model, catalogModel) {
107
121
  const id = model.trim().toLowerCase();
108
122
  if (id === 'openrouter/free')
@@ -357,25 +371,13 @@ async function setupWizard(rl, currentConfig) {
357
371
  const modelInput = await rl.question(chalk.yellow(` Model [${model}]: `));
358
372
  if (modelInput.trim())
359
373
  model = modelInput.trim();
360
- // Auto-heal known-slow / known-flaky OpenRouter models. These
361
- // IDs frequently return empty responses, the literal string "ERROR",
362
- // or no first stream event. Warning-only was not enough: users could
363
- // save a broken first-run config and every prompt looked like it was
364
- // stuck in the live queue. Keep an explicit env escape hatch for model
365
- // 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.
366
376
  if (isKnownFlakyOpenRouterModel({ provider: provider.name, model })) {
367
377
  console.log('');
368
378
  console.log(chalk.yellow(` ⚠ "${model}" is known to stall badly in interactive OpenRouter sessions`));
369
- console.log(chalk.yellow(` or return empty / "ERROR" responses.`));
370
- if (process.env.CAWDEX_ALLOW_FLAKY_MODELS === '1') {
371
- console.log(chalk.dim(' Keeping it because CAWDEX_ALLOW_FLAKY_MODELS=1 is set.'));
372
- }
373
- else {
374
- const safer = providerKey === 'openrouter' ? PROVIDERS.openrouter.defaultModel : provider.defaultModel;
375
- console.log(chalk.dim(` Using safer default instead: ${safer}`));
376
- console.log(chalk.dim(' Override only for debugging: CAWDEX_ALLOW_FLAKY_MODELS=1 cawdex'));
377
- model = safer;
378
- }
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.'));
379
381
  console.log('');
380
382
  }
381
383
  if (providerKey === 'openrouter' && !isOpenRouterFreeModelId(model)) {
@@ -516,8 +518,6 @@ async function setupWizard(rl, currentConfig) {
516
518
  console.log(chalk.dim(` Configured: /model, /fallback, /perm, and /swarm. Memory stays under /memory.`));
517
519
  console.log();
518
520
  return config;
519
- console.log();
520
- return config;
521
521
  }
522
522
  /**
523
523
  * Pretty-print a resumed session's conversation history to stdout so
@@ -954,6 +954,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
954
954
  console.log(d(' ') + c('/harness') + d(' — file-level harness component map'));
955
955
  console.log(d(' ') + c('/rules') + d(' — show coding rules'));
956
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'));
957
958
  console.log(d(' ') + c('/perm-reset') + d(' — clear the per-tool always-allow list'));
958
959
  console.log(d(' ') + c('/sandbox [level]') + d(' — OS-native bash sandbox (off / standard / strict)'));
959
960
  console.log(d(' ') + c('/dry-run') + d(' — toggle dry-run mode'));
@@ -967,6 +968,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
967
968
  console.log(d(' ') + c('/update-docs') + d(' — sync documentation with code'));
968
969
  console.log(d(' ') + c('/checkpoint [label]') + d(' — save git state checkpoint'));
969
970
  console.log(d(' ') + c('/checkpoints') + d(' — list saved checkpoints'));
971
+ console.log(d(' ') + c('/context brief') + d(' — cheap local repo/context preflight'));
970
972
  console.log(d(' ') + c('/search-first <task>') + d(' — research before coding'));
971
973
  console.log(d(' ') + c('/sources <query>') + d(' — direct arXiv/GitHub/HF/Kaggle source scan'));
972
974
  console.log(d(' ') + c('/benchmark-repos') + d(' — public Terminal-Bench repo catalog'));
@@ -1389,19 +1391,70 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1389
1391
  // ── Model ─────────────────────────────────────────
1390
1392
  case '/model':
1391
1393
  if (args) {
1392
- const newModel = switchModel(config, args);
1393
- if (newModel) {
1394
- 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
+ }
1395
1414
  saveConfig(config);
1396
- resetClient();
1397
- console.log(chalk.green(` Model: ${config.model}`));
1415
+ console.log(chalk.green(` Model alias ${alias.toLowerCase()} -> ${set.model}`));
1416
+ return { handled: true };
1398
1417
  }
1399
- else {
1400
- applyModelSelection(config, args);
1401
- saveConfig(config);
1402
- resetClient();
1403
- 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 };
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 };
1404
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)}`));
1405
1458
  return { handled: true };
1406
1459
  }
1407
1460
  // No args. On OpenRouter, the user wants the interactive
@@ -1845,10 +1898,61 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1845
1898
  console.log(report.output);
1846
1899
  return { handled: true };
1847
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
+ }
1848
1913
  case '/rules':
1849
1914
  printRules();
1850
1915
  return { handled: true };
1916
+ case '/agents':
1917
+ console.log(formatAgentsInstructionsReport(process.cwd(), config.model, ALL_TOOLS));
1918
+ return { handled: true };
1851
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
+ }
1852
1956
  if (args && ['ask', 'auto', 'yolo'].includes(args)) {
1853
1957
  config.permissionMode = args;
1854
1958
  saveConfig(config);
@@ -1966,7 +2070,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1966
2070
  }
1967
2071
  // ── Learning & Cost ───────────────────────────────
1968
2072
  case '/usage':
1969
- printUsageSummary();
2073
+ printUsageSummary(session.id);
1970
2074
  return { handled: true };
1971
2075
  case '/budget': {
1972
2076
  const [daily, monthly] = args.split(/\s+/).map(Number);
@@ -3519,34 +3623,6 @@ async function main() {
3519
3623
  // for screen readers, so they're force-off in that mode. Sighted
3520
3624
  // users get them by default; the CAWDEX_ANIMATIONS=0 env var still
3521
3625
  // overrides for users who specifically don't want the motion.
3522
- // Saved configs can contain OpenRouter preview models that are known
3523
- // to hang before the first token. Do not let the CLI boot into that
3524
- // broken state by default; keep an env escape hatch for deliberate
3525
- // provider debugging.
3526
- const runtimeModelOverride = !!process.env.CAWDEX_MODEL_OVERRIDE || !!process.env.CAWDEX_MODEL;
3527
- const knownFlakyFallback = fallbackModelForKnownFlakyTurn(config)
3528
- || (process.env.CAWDEX_ALLOW_FLAKY_MODELS !== '1'
3529
- && isKnownFlakyOpenRouterModel(config)
3530
- && /openrouter/i.test(config.provider)
3531
- ? PROVIDERS.openrouter.defaultModel
3532
- : null);
3533
- if (knownFlakyFallback) {
3534
- const previousModel = config.model;
3535
- applyModelSelection(config, knownFlakyFallback);
3536
- config.fallbackModel = knownFlakyFallback;
3537
- resetClient();
3538
- if (!nonInteractive && !runtimeModelOverride) {
3539
- saveConfig(config);
3540
- console.log(theme.warning(` ⚠ Saved model "${previousModel}" is known to stall; switched config to ${knownFlakyFallback}.`));
3541
- console.log(theme.dim(' Use CAWDEX_ALLOW_FLAKY_MODELS=1 only if you are deliberately testing that model.'));
3542
- console.log('');
3543
- }
3544
- else if (!nonInteractive) {
3545
- console.log(theme.warning(` ⚠ Requested model "${previousModel}" is known to stall; using ${knownFlakyFallback} for this session.`));
3546
- console.log(theme.dim(' Use CAWDEX_ALLOW_FLAKY_MODELS=1 to force the requested model.'));
3547
- console.log('');
3548
- }
3549
- }
3550
3626
  {
3551
3627
  const { setAnimationConfig } = await import('./animations.js');
3552
3628
  setAnimationConfig({
@@ -3638,6 +3714,29 @@ async function main() {
3638
3714
  },
3639
3715
  });
3640
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
+ };
3641
3740
  // ── F-key hotkey listener ────────────────────────────────
3642
3741
  // Voice / accessibility hotkeys — all on the F-row.
3643
3742
  //
@@ -4449,6 +4548,22 @@ async function main() {
4449
4548
  catch { /* noop */ }
4450
4549
  process.exit(0);
4451
4550
  }
4551
+ const instantArtifact = maybeCreateInstantArtifact(resolvedPrompt.prompt, process.cwd());
4552
+ if (instantArtifact) {
4553
+ printLocalResponse(instantArtifact.message);
4554
+ messages.push({ role: 'user', content: resolvedPrompt.prompt });
4555
+ messages.push({ role: 'assistant', content: instantArtifact.assistantMessage });
4556
+ await saveWithSnapshot();
4557
+ try {
4558
+ await runHooks({ event: 'SessionStop', sessionId: session.id, cwd: process.cwd(), permissionMode: config.permissionMode });
4559
+ }
4560
+ catch { /* never fail a local artifact on hook errors */ }
4561
+ try {
4562
+ rl.close();
4563
+ }
4564
+ catch { /* noop */ }
4565
+ process.exit(0);
4566
+ }
4452
4567
  // ── F9: Empty-engagement guard (non-interactive nudge) ──
4453
4568
  //
4454
4569
  // Some failures in the 2026-05-25 baseline run came from the
@@ -4626,6 +4741,10 @@ async function main() {
4626
4741
  messages.length = 0;
4627
4742
  messages.push(...result.newMessages);
4628
4743
  }
4744
+ if (result.turnOverride !== undefined) {
4745
+ nextTurnOverride = result.turnOverride;
4746
+ }
4747
+ syncFooter();
4629
4748
  if (trimmed.startsWith('/config') && !result?.shouldExit) {
4630
4749
  deactivateFooter();
4631
4750
  config = await setupWizard(rl, config);
@@ -4788,7 +4907,10 @@ async function main() {
4788
4907
  messages.push({ role: 'user', content: result.injectPrompt });
4789
4908
  }
4790
4909
  await saveWithSnapshot();
4791
- 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();
4792
4914
  await saveWithSnapshot();
4793
4915
  continue;
4794
4916
  }
@@ -4797,34 +4919,44 @@ async function main() {
4797
4919
  }
4798
4920
  const instantAnswer = maybeInstantAnswer(trimmed);
4799
4921
  if (instantAnswer) {
4800
- console.log(theme.primary(instantAnswer));
4922
+ printLocalResponse(instantAnswer);
4801
4923
  messages.push({ role: 'user', content: trimmed });
4802
4924
  messages.push({ role: 'assistant', content: instantAnswer });
4803
4925
  await saveWithSnapshot();
4804
4926
  continue;
4805
4927
  }
4928
+ const instantArtifact = maybeCreateInstantArtifact(trimmed, process.cwd());
4929
+ if (instantArtifact) {
4930
+ printLocalResponse(instantArtifact.message);
4931
+ messages.push({ role: 'user', content: trimmed });
4932
+ messages.push({ role: 'assistant', content: instantArtifact.assistantMessage });
4933
+ await saveWithSnapshot();
4934
+ continue;
4935
+ }
4806
4936
  // Auto-route model if enabled
4807
4937
  if (autoRoute) {
4808
4938
  const complexity = classifyComplexity(trimmed);
4809
4939
  const route = routeModel(config, complexity);
4810
4940
  if (route.model !== config.model) {
4811
4941
  console.log(chalk.dim(` [routing: ${route.reason}]`));
4812
- applyModelSelection(config, route.model);
4813
- resetClient();
4942
+ nextTurnOverride = { model: route.model, source: 'route' };
4814
4943
  }
4815
4944
  autoRoute = false;
4816
4945
  }
4817
4946
  // Add user message and run query
4818
4947
  messages.push({ role: 'user', content: trimmed });
4819
4948
  await saveWithSnapshot();
4949
+ const turnConfig = consumeTurnConfig();
4950
+ updateFooter(buildFooterSnapshot(turnConfig, mode.current, session, process.cwd()));
4820
4951
  await runQuery({
4821
- config,
4952
+ config: turnConfig,
4822
4953
  messages,
4823
4954
  cwd: process.cwd(),
4824
4955
  rl,
4825
4956
  sessionId: session.id,
4826
4957
  mode: mode.current,
4827
4958
  });
4959
+ syncFooter();
4828
4960
  // Auto-save session
4829
4961
  await saveWithSnapshot();
4830
4962
  // Strategic compaction check