agent-nuvira 3.5.7 → 3.5.10

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 (134) hide show
  1. package/README.md +19 -1
  2. package/dist/agents/agents/package-agent.d.ts +22 -0
  3. package/dist/agents/agents/package-agent.d.ts.map +1 -1
  4. package/dist/agents/agents/package-agent.js +92 -4
  5. package/dist/agents/agents/package-agent.js.map +1 -1
  6. package/dist/agents/release-preflight.d.ts +42 -5
  7. package/dist/agents/release-preflight.d.ts.map +1 -1
  8. package/dist/agents/release-preflight.js +82 -5
  9. package/dist/agents/release-preflight.js.map +1 -1
  10. package/dist/cli/chat.d.ts.map +1 -1
  11. package/dist/cli/chat.js +99 -9
  12. package/dist/cli/chat.js.map +1 -1
  13. package/dist/cli/cli-program.d.ts.map +1 -1
  14. package/dist/cli/cli-program.js +6 -0
  15. package/dist/cli/cli-program.js.map +1 -1
  16. package/dist/cli/code-map.d.ts +51 -0
  17. package/dist/cli/code-map.d.ts.map +1 -1
  18. package/dist/cli/code-map.js +174 -2
  19. package/dist/cli/code-map.js.map +1 -1
  20. package/dist/cli/credentials.d.ts.map +1 -1
  21. package/dist/cli/credentials.js +4 -1
  22. package/dist/cli/credentials.js.map +1 -1
  23. package/dist/cli/execute.js +5 -5
  24. package/dist/cli/execute.js.map +1 -1
  25. package/dist/cli/loop-executor.d.ts.map +1 -1
  26. package/dist/cli/loop-executor.js +76 -13
  27. package/dist/cli/loop-executor.js.map +1 -1
  28. package/dist/cli/motion.d.ts +53 -0
  29. package/dist/cli/motion.d.ts.map +1 -0
  30. package/dist/cli/motion.js +516 -0
  31. package/dist/cli/motion.js.map +1 -0
  32. package/dist/config/process-env.d.ts.map +1 -1
  33. package/dist/config/process-env.js +2 -2
  34. package/dist/config/process-env.js.map +1 -1
  35. package/dist/editing/types.d.ts +23 -1
  36. package/dist/editing/types.d.ts.map +1 -1
  37. package/dist/editing/types.js +51 -7
  38. package/dist/editing/types.js.map +1 -1
  39. package/dist/inference/route-resolver.d.ts +39 -0
  40. package/dist/inference/route-resolver.d.ts.map +1 -1
  41. package/dist/inference/route-resolver.js +51 -2
  42. package/dist/inference/route-resolver.js.map +1 -1
  43. package/dist/inference/thinking-replay.d.ts +86 -0
  44. package/dist/inference/thinking-replay.d.ts.map +1 -0
  45. package/dist/inference/thinking-replay.js +110 -0
  46. package/dist/inference/thinking-replay.js.map +1 -0
  47. package/dist/inference/tools.d.ts +9 -0
  48. package/dist/inference/tools.d.ts.map +1 -1
  49. package/dist/inference/tools.js +12 -1
  50. package/dist/inference/tools.js.map +1 -1
  51. package/dist/learning/assessment-protocol.d.ts +120 -0
  52. package/dist/learning/assessment-protocol.d.ts.map +1 -0
  53. package/dist/learning/assessment-protocol.js +236 -0
  54. package/dist/learning/assessment-protocol.js.map +1 -0
  55. package/dist/learning/assessment-turn-context.d.ts +54 -0
  56. package/dist/learning/assessment-turn-context.d.ts.map +1 -0
  57. package/dist/learning/assessment-turn-context.js +71 -0
  58. package/dist/learning/assessment-turn-context.js.map +1 -0
  59. package/dist/learning/exhaustion-report.d.ts +24 -1
  60. package/dist/learning/exhaustion-report.d.ts.map +1 -1
  61. package/dist/learning/exhaustion-report.js +18 -6
  62. package/dist/learning/exhaustion-report.js.map +1 -1
  63. package/dist/learning/machine-facts.d.ts +7 -0
  64. package/dist/learning/machine-facts.d.ts.map +1 -1
  65. package/dist/learning/machine-facts.js +37 -2
  66. package/dist/learning/machine-facts.js.map +1 -1
  67. package/dist/learning/perf-probe.d.ts +140 -0
  68. package/dist/learning/perf-probe.d.ts.map +1 -0
  69. package/dist/learning/perf-probe.js +316 -0
  70. package/dist/learning/perf-probe.js.map +1 -0
  71. package/dist/learning/self-check-discovery.d.ts +117 -0
  72. package/dist/learning/self-check-discovery.d.ts.map +1 -0
  73. package/dist/learning/self-check-discovery.js +334 -0
  74. package/dist/learning/self-check-discovery.js.map +1 -0
  75. package/dist/skills/bundled-skills-phase3.d.ts.map +1 -1
  76. package/dist/skills/bundled-skills-phase3.js +3 -3
  77. package/dist/skills/bundled-skills-phase3.js.map +1 -1
  78. package/dist/skills/sandbox-executor.d.ts.map +1 -1
  79. package/dist/skills/sandbox-executor.js +115 -34
  80. package/dist/skills/sandbox-executor.js.map +1 -1
  81. package/dist/tools/child-agent-runtime.js +19 -1
  82. package/dist/tools/child-agent-runtime.js.map +1 -1
  83. package/dist/tools/credentials-tool.d.ts.map +1 -1
  84. package/dist/tools/credentials-tool.js +4 -1
  85. package/dist/tools/credentials-tool.js.map +1 -1
  86. package/dist/tools/loop-project-context.d.ts +7 -0
  87. package/dist/tools/loop-project-context.d.ts.map +1 -1
  88. package/dist/tools/loop-project-context.js +44 -0
  89. package/dist/tools/loop-project-context.js.map +1 -1
  90. package/dist/tools/modality/motion-tool.d.ts +28 -1
  91. package/dist/tools/modality/motion-tool.d.ts.map +1 -1
  92. package/dist/tools/modality/motion-tool.js +261 -10
  93. package/dist/tools/modality/motion-tool.js.map +1 -1
  94. package/dist/tools/modality/motion.d.ts +403 -3
  95. package/dist/tools/modality/motion.d.ts.map +1 -1
  96. package/dist/tools/modality/motion.js +1096 -41
  97. package/dist/tools/modality/motion.js.map +1 -1
  98. package/dist/tools/pipeline-tool.d.ts.map +1 -1
  99. package/dist/tools/pipeline-tool.js +19 -1
  100. package/dist/tools/pipeline-tool.js.map +1 -1
  101. package/dist/tools/plan-store.d.ts +90 -6
  102. package/dist/tools/plan-store.d.ts.map +1 -1
  103. package/dist/tools/plan-store.js +140 -13
  104. package/dist/tools/plan-store.js.map +1 -1
  105. package/dist/tools/publish-tool.d.ts.map +1 -1
  106. package/dist/tools/publish-tool.js +9 -0
  107. package/dist/tools/publish-tool.js.map +1 -1
  108. package/dist/tools/registry.d.ts.map +1 -1
  109. package/dist/tools/registry.js +59 -7
  110. package/dist/tools/registry.js.map +1 -1
  111. package/dist/tools/tool-loop.d.ts +17 -0
  112. package/dist/tools/tool-loop.d.ts.map +1 -1
  113. package/dist/tools/tool-loop.js +4 -1
  114. package/dist/tools/tool-loop.js.map +1 -1
  115. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  116. package/dist/web-dashboard/chat-console.js +11 -1
  117. package/dist/web-dashboard/chat-console.js.map +1 -1
  118. package/dist/web-dashboard/motion-data.d.ts +62 -0
  119. package/dist/web-dashboard/motion-data.d.ts.map +1 -0
  120. package/dist/web-dashboard/motion-data.js +91 -0
  121. package/dist/web-dashboard/motion-data.js.map +1 -0
  122. package/dist/web-dashboard/project-context.js +6 -0
  123. package/dist/web-dashboard/project-context.js.map +1 -1
  124. package/dist/web-dashboard/server.d.ts.map +1 -1
  125. package/dist/web-dashboard/server.js +83 -33
  126. package/dist/web-dashboard/server.js.map +1 -1
  127. package/dist/web-dashboard/src/types.d.ts +31 -0
  128. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  129. package/package.json +1 -1
  130. package/src/web-dashboard/public/assets/{index-CzRU84ET.css → index-CXdBvA0N.css} +1 -1
  131. package/src/web-dashboard/public/assets/{index-V0rIqVbg.js → index-DfjkO16t.js} +81 -81
  132. package/src/web-dashboard/public/assets/index-DfjkO16t.js.map +1 -0
  133. package/src/web-dashboard/public/index.html +2 -2
  134. package/src/web-dashboard/public/assets/index-V0rIqVbg.js.map +0 -1
package/dist/cli/chat.js CHANGED
@@ -5,7 +5,7 @@ import { Command } from 'commander';
5
5
  import inquirer from 'inquirer';
6
6
  import { BaseCommand } from './commands.js';
7
7
  import { resolveProvider } from './router.js';
8
- import { resolveRoute, strictModelMode, strictPinRefusal } from '../inference/route-resolver.js';
8
+ import { resolveRoute, strictModelMode, strictPinPolicy, strictPinRefusal, withStrictModel } from '../inference/route-resolver.js';
9
9
  import { showModelPicker } from './model-picker.js';
10
10
  import { ContextParser } from '../context/parser.js';
11
11
  import { getCache } from '../context/cache.js';
@@ -32,6 +32,7 @@ import { UnusableModelResponseError } from '../learning/response-usability.js';
32
32
  import { resolveThreadBudgetChars } from '../learning/context-budget.js';
33
33
  import { getAutoRouter, isAgentAutoRoute, isAutoModel, isAutoProvider, governanceVerdict, adminBudgetVerdict, isAgenticTask } from '../learning/auto-router.js';
34
34
  import { continuationSoftwareText } from '../learning/continuation-intent.js';
35
+ import { buildAssessmentTurnContext } from '../learning/assessment-turn-context.js';
35
36
  import { TaskModelContinuity, taskSignature } from '../learning/task-model-continuity.js';
36
37
  import { describeFailureKind, renderExhaustionReport, } from '../learning/exhaustion-report.js';
37
38
  import { estimateTokens } from '../learning/cost-tracker.js';
@@ -1305,13 +1306,29 @@ export class ChatCommand extends BaseCommand {
1305
1306
  .option('--keep-worktree', 'Keep the isolated worktree after the turn instead of removing it')
1306
1307
  .option('--resume [id]', 'Replay the recorded steps of this ask whose input is unchanged instead of paying for them again (defaults to the record for this goal + directory; also asked for by NUVIRA_RESUME=1)')
1307
1308
  .addHelpText('after', '\nModel routing:\n' +
1308
- ' Pinning -p/--provider (and -m/--model) does NOT stop the router from falling\n' +
1309
- ' over to another model if the pinned one is unavailable — auto routing takes\n' +
1310
- ' over and the turn says so. To work with the pinned model ONLY, set\n' +
1311
- ' NUVIRA_STRICT_MODEL=1: a dead pin then fails with a message naming the pair\n' +
1312
- ' instead of substituting another model.\n')
1309
+ ' Pinning -p/--provider (and -m/--model) HOLDS: the named model is the only one\n' +
1310
+ ' that serves the turn, and a pin that cannot answer fails with a message\n' +
1311
+ ' naming the pair instead of quietly substituting another model. To allow the\n' +
1312
+ ' repair-and-announce behaviour instead (fall over to an available model and\n' +
1313
+ ' report which one answered), set NUVIRA_STRICT_MODEL=0.\n')
1313
1314
  .action(async (prompt, options) => {
1314
- await this.execute(prompt, options || {});
1315
+ const opts = options || {};
1316
+ // ── ASK 1 — A NAMED PAIR HOLDS FOR THE WHOLE TURN ──────────────────
1317
+ //
1318
+ // `--provider`/`--model` name a model; they are not a hint. The `auto`
1319
+ // sentinels are the opposite of a pin and are excluded. When a pair IS
1320
+ // named, the pin's policy (see `strictPinPolicy`) is scoped over the whole
1321
+ // turn — the one-shot answer, its picked followups and every REPL message —
1322
+ // so route resolution, the loop engine's candidate walk and chat's own
1323
+ // pinned-fallback path all read the same answer instead of each deciding
1324
+ // for itself whether substituting is acceptable. `NUVIRA_STRICT_MODEL=0`
1325
+ // is the explicit opt-out and keeps the old repair-and-announce behaviour.
1326
+ const namedPair = Boolean(opts.provider && !isAutoProvider(opts.provider)) || Boolean(opts.model && !isAutoModel(opts.model));
1327
+ if (namedPair && strictPinPolicy(true)) {
1328
+ await withStrictModel(true, () => this.execute(prompt, opts));
1329
+ return;
1330
+ }
1331
+ await this.execute(prompt, opts);
1315
1332
  });
1316
1333
  return command;
1317
1334
  }
@@ -1845,10 +1862,46 @@ export class ChatCommand extends BaseCommand {
1845
1862
  * about one directory.
1846
1863
  */
1847
1864
  const turnScope = ctxOverrides?.projectPath || process.cwd();
1865
+ // ── ASSESSMENT TURN — one detector, three consumers ────────────────────
1866
+ //
1867
+ // The evidence protocol and everything it carries (the project's own
1868
+ // discovered checks, the performance probes) comes from the SHARED assembler,
1869
+ // which is also what `loop-executor.ts` and `pipeline-tool.ts` call — so
1870
+ // `nuvira execute` and a gateway-run audit get the same rules as this surface
1871
+ // instead of an unadorned ask. See `learning/assessment-turn-context.ts`.
1872
+ //
1873
+ // The symbol map in the ambient project context (which a findings turn repays
1874
+ // and a change turn does not) and the response-cache bypass below read the SAME
1875
+ // boolean, so they can never disagree about whether the turn is an assessment.
1876
+ // Computed here because the cache check is the first thing that needs it.
1877
+ //
1878
+ // Deterministic and NARROW: the assembler tests the goal BEFORE it touches the
1879
+ // filesystem, so a change request returns '' and does no discovery work — its
1880
+ // turn is byte-identical to one from a build without any of this.
1881
+ const assessmentContext = buildAssessmentTurnContext(message, turnScope);
1882
+ const assessmentBlock = assessmentContext.block;
1883
+ const assessmentTurn = assessmentContext.isAssessment;
1848
1884
  // Cache check first (same as the legacy path).
1849
1885
  const cache = getCache();
1850
1886
  const cacheModel = this.cacheModelFor(session);
1851
- if (cacheEnabled) {
1887
+ // ASSESSMENT TURNS DO NOT REPLAY A CACHED ANSWER.
1888
+ //
1889
+ // A cached answer is a statement about the tree AS IT WAS when the entry was
1890
+ // written, and an assessment is a statement about the tree NOW — so the key
1891
+ // (prompt + model + provider + workspace) is exactly the four things that stay
1892
+ // equal when the code has changed underneath. Observed on 2026-10-16: after a
1893
+ // patched build was installed, the same audit prompt in the same directory
1894
+ // returned `cache.hit {chars:5744}` — the PRE-patch report, identical down to a
1895
+ // mid-sentence truncation, replayed as if it were a fresh look at the tree.
1896
+ //
1897
+ // The cost is one real turn where a replay used to be instant, and it is only
1898
+ // paid by assessment turns: a change request, a question, a repeat of anything
1899
+ // else keeps its cache hit. Truth over speed, in the one place the two can
1900
+ // disagree about the same input.
1901
+ if (cacheEnabled && assessmentTurn) {
1902
+ debugLog?.event('cache.bypass', { reason: 'assessment turn — the tree may have changed', scope: turnScope });
1903
+ }
1904
+ if (cacheEnabled && !assessmentTurn) {
1852
1905
  try {
1853
1906
  // #30 — read the entry WITH its recorded activity, not just the text: a
1854
1907
  // replay that dropped `toolCalls` rendered no tool cards on the dashboard
@@ -2078,7 +2131,7 @@ export class ChatCommand extends BaseCommand {
2078
2131
  let ambientProjectContext;
2079
2132
  if (!ctxOverrides?.projectContext) {
2080
2133
  try {
2081
- const built = await buildLoopProjectContext(turnCwd);
2134
+ const built = await buildLoopProjectContext(turnCwd, { symbolMap: assessmentTurn });
2082
2135
  if (built)
2083
2136
  ambientProjectContext = built;
2084
2137
  }
@@ -2144,6 +2197,8 @@ export class ChatCommand extends BaseCommand {
2144
2197
  // conversation stays contiguous, and it is a USER-TURN message rather than
2145
2198
  // system text, so the system prompt stays byte-stable and cacheable.
2146
2199
  ...(knowledgeBlock ? [{ role: 'user', content: knowledgeBlock }] : []),
2200
+ // The assessment protocol for this ask, when the ask IS an assessment.
2201
+ ...(assessmentBlock ? [{ role: 'user', content: assessmentBlock }] : []),
2147
2202
  // P4 — the recalled project context (prior sessions + facts) rides in
2148
2203
  // next, so the model starts from what this project was last doing.
2149
2204
  ...(ctxOverrides?.recallContext
@@ -2398,6 +2453,10 @@ export class ChatCommand extends BaseCommand {
2398
2453
  // Machine facts are FACTS about the host, not a recommendation, so they
2399
2454
  // drop LAST among the optional blocks (higher priority = kept longer).
2400
2455
  { name: 'machine-facts', chars: machineBlock.length, dropPriority: 15 },
2456
+ // The assessment protocol governs how THIS turn's findings may be
2457
+ // claimed, so it outranks every other optional block and is dropped only
2458
+ // after the machine facts.
2459
+ { name: 'assessment-protocol', chars: assessmentBlock.length, dropPriority: 18 },
2401
2460
  { name: 'working-state', chars: workingStateBlock.length, dropPriority: 25 },
2402
2461
  { name: 'decisions', chars: ctxOverrides?.decisionContext?.length ?? 0, dropPriority: 28 },
2403
2462
  { name: 'recall', chars: ctxOverrides?.recallContext?.length ?? 0, dropPriority: 30 },
@@ -2421,6 +2480,16 @@ export class ChatCommand extends BaseCommand {
2421
2480
  (trimmed.has('skill-hint') ? '' : skillHint),
2422
2481
  };
2423
2482
  }
2483
+ // Applied rather than merely reported (as the two system blocks above are):
2484
+ // a protocol the budget says to drop must not still be in the thread, or the
2485
+ // report and the wire disagree. Matched by exact content so no other
2486
+ // user-turn block is ever touched.
2487
+ if (trimmed.has('assessment-protocol') && assessmentBlock) {
2488
+ for (let i = thread.length - 1; i >= 1; i--) {
2489
+ if (thread[i].role === 'user' && thread[i].content === assessmentBlock)
2490
+ thread.splice(i, 1);
2491
+ }
2492
+ }
2424
2493
  if (report.level !== 'ok') {
2425
2494
  recordTraceEvent(chatTraceId, {
2426
2495
  kind: 'decision',
@@ -2671,6 +2740,9 @@ export class ChatCommand extends BaseCommand {
2671
2740
  // and is now additionally gated on the model having the context for it.
2672
2741
  toolExposure: harness.exposure,
2673
2742
  maxParallelReads: harness.maxParallelReads,
2743
+ // An assessment turn has no edits to verify, so the loop's self-review
2744
+ // gate would never fire for it — see `selfReviewWhenUnchanged`.
2745
+ ...(assessmentTurn ? { selfReviewWhenUnchanged: true } : {}),
2674
2746
  // WS3 — the turn span the loop hangs each tool call under.
2675
2747
  otel: otelSpan,
2676
2748
  // WS4 — the label a tool hook reports this call under.
@@ -2890,6 +2962,15 @@ export class ChatCommand extends BaseCommand {
2890
2962
  // respects the quality bar they set instead of inviting a downgrade
2891
2963
  // (see ExhaustionInput.pinnedModel).
2892
2964
  ...(session.model && session.model !== 'default' ? { pinnedModel: session.model } : {}),
2965
+ // ...and whether that pin is HELD, which decides whether the report
2966
+ // may promise an automatic next attempt at all. A held pin is the
2967
+ // default for a named pair, so this is the common case, and telling
2968
+ // the user to wait for a switch the pin forbids was the measured bug
2969
+ // (see ExhaustionInput.pinnedHeld). Read from the same predicate
2970
+ // routing obeys, in this turn's own scope.
2971
+ ...(session.model && session.model !== 'default' && strictModelMode()
2972
+ ? { pinnedHeld: true }
2973
+ : {}),
2893
2974
  // The underlying line is kept as the REASON, so a specific,
2894
2975
  // actionable cause is never buried by the report.
2895
2976
  ...(result.generationFailed && result.content.trim() ? { cause: result.content } : {}),
@@ -3074,6 +3155,15 @@ export class ChatCommand extends BaseCommand {
3074
3155
  if (turnCarriesHonestyFlag(result)) {
3075
3156
  debugLog?.event('cache.skip', { reason: 'honesty-flag', scope: turnScope });
3076
3157
  }
3158
+ else if (assessmentTurn) {
3159
+ // Same rule as the honesty flag above, for the same reason: an
3160
+ // assessment is a claim about the tree at THIS moment, so storing it
3161
+ // lets a later read of that entry — another surface, or a path without
3162
+ // this detector — replay it as a fresh look at code that may have
3163
+ // changed underneath it. The turn is not replayed and not stored; it is
3164
+ // re-derived.
3165
+ debugLog?.event('cache.skip', { reason: 'assessment turn — tree-scoped claim', scope: turnScope });
3166
+ }
3077
3167
  else {
3078
3168
  // Keyed by the model that ACTUALLY answered (tryGenerate records it
3079
3169
  // on success), so a weak model's reply is never replayed as a strong