agent-nuvira 3.3.11 → 3.3.13

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 (220) hide show
  1. package/README.md +32 -1
  2. package/dist/agents/agents/reasoner.d.ts.map +1 -1
  3. package/dist/agents/agents/reasoner.js +13 -0
  4. package/dist/agents/agents/reasoner.js.map +1 -1
  5. package/dist/agents/orchestrator.d.ts.map +1 -1
  6. package/dist/agents/orchestrator.js +56 -20
  7. package/dist/agents/orchestrator.js.map +1 -1
  8. package/dist/cli/chat.d.ts +36 -0
  9. package/dist/cli/chat.d.ts.map +1 -1
  10. package/dist/cli/chat.js +456 -43
  11. package/dist/cli/chat.js.map +1 -1
  12. package/dist/cli/cli-program.d.ts.map +1 -1
  13. package/dist/cli/cli-program.js +8 -0
  14. package/dist/cli/cli-program.js.map +1 -1
  15. package/dist/cli/config.d.ts.map +1 -1
  16. package/dist/cli/config.js +35 -4
  17. package/dist/cli/config.js.map +1 -1
  18. package/dist/cli/execute.d.ts.map +1 -1
  19. package/dist/cli/execute.js +14 -0
  20. package/dist/cli/execute.js.map +1 -1
  21. package/dist/cli/knowledge.d.ts +23 -0
  22. package/dist/cli/knowledge.d.ts.map +1 -0
  23. package/dist/cli/knowledge.js +132 -0
  24. package/dist/cli/knowledge.js.map +1 -0
  25. package/dist/cli/loop-executor.d.ts.map +1 -1
  26. package/dist/cli/loop-executor.js +57 -13
  27. package/dist/cli/loop-executor.js.map +1 -1
  28. package/dist/cli/omniroute-control.d.ts +88 -0
  29. package/dist/cli/omniroute-control.d.ts.map +1 -0
  30. package/dist/cli/omniroute-control.js +179 -0
  31. package/dist/cli/omniroute-control.js.map +1 -0
  32. package/dist/cli/omniroute.d.ts +21 -0
  33. package/dist/cli/omniroute.d.ts.map +1 -0
  34. package/dist/cli/omniroute.js +67 -0
  35. package/dist/cli/omniroute.js.map +1 -0
  36. package/dist/cli/tool-install-prompt.d.ts +58 -0
  37. package/dist/cli/tool-install-prompt.d.ts.map +1 -1
  38. package/dist/cli/tool-install-prompt.js +137 -0
  39. package/dist/cli/tool-install-prompt.js.map +1 -1
  40. package/dist/cli/weak-model-prompt.d.ts +16 -7
  41. package/dist/cli/weak-model-prompt.d.ts.map +1 -1
  42. package/dist/cli/weak-model-prompt.js +24 -8
  43. package/dist/cli/weak-model-prompt.js.map +1 -1
  44. package/dist/config/types.d.ts +44 -0
  45. package/dist/config/types.d.ts.map +1 -1
  46. package/dist/context/cache.d.ts +35 -1
  47. package/dist/context/cache.d.ts.map +1 -1
  48. package/dist/context/cache.js +31 -2
  49. package/dist/context/cache.js.map +1 -1
  50. package/dist/context/history.d.ts +21 -0
  51. package/dist/context/history.d.ts.map +1 -1
  52. package/dist/context/history.js +50 -0
  53. package/dist/context/history.js.map +1 -1
  54. package/dist/gateway/hook-contract.d.ts +173 -0
  55. package/dist/gateway/hook-contract.d.ts.map +1 -0
  56. package/dist/gateway/hook-contract.js +352 -0
  57. package/dist/gateway/hook-contract.js.map +1 -0
  58. package/dist/gateway/hooks.d.ts.map +1 -1
  59. package/dist/gateway/hooks.js +14 -0
  60. package/dist/gateway/hooks.js.map +1 -1
  61. package/dist/inference/interface.d.ts +17 -0
  62. package/dist/inference/interface.d.ts.map +1 -1
  63. package/dist/inference/model-catalog.d.ts +17 -0
  64. package/dist/inference/model-catalog.d.ts.map +1 -1
  65. package/dist/inference/model-catalog.js +42 -7
  66. package/dist/inference/model-catalog.js.map +1 -1
  67. package/dist/inference/model-id-validation.d.ts +57 -0
  68. package/dist/inference/model-id-validation.d.ts.map +1 -0
  69. package/dist/inference/model-id-validation.js +127 -0
  70. package/dist/inference/model-id-validation.js.map +1 -0
  71. package/dist/inference/openai-compat-adapter.d.ts.map +1 -1
  72. package/dist/inference/openai-compat-adapter.js +12 -1
  73. package/dist/inference/openai-compat-adapter.js.map +1 -1
  74. package/dist/inference/provider-catalog.d.ts +11 -0
  75. package/dist/inference/provider-catalog.d.ts.map +1 -1
  76. package/dist/inference/provider-catalog.js +80 -0
  77. package/dist/inference/provider-catalog.js.map +1 -1
  78. package/dist/inference/route-resolver.d.ts +2 -8
  79. package/dist/inference/route-resolver.d.ts.map +1 -1
  80. package/dist/inference/route-resolver.js +17 -0
  81. package/dist/inference/route-resolver.js.map +1 -1
  82. package/dist/inference/tool-call-utils.d.ts +4 -3
  83. package/dist/inference/tool-call-utils.d.ts.map +1 -1
  84. package/dist/inference/tool-call-utils.js +26 -12
  85. package/dist/inference/tool-call-utils.js.map +1 -1
  86. package/dist/inference/tools.d.ts +1 -0
  87. package/dist/inference/tools.d.ts.map +1 -1
  88. package/dist/inference/tools.js +20 -3
  89. package/dist/inference/tools.js.map +1 -1
  90. package/dist/learning/agentic-route-gate.d.ts +104 -0
  91. package/dist/learning/agentic-route-gate.d.ts.map +1 -0
  92. package/dist/learning/agentic-route-gate.js +125 -0
  93. package/dist/learning/agentic-route-gate.js.map +1 -0
  94. package/dist/learning/auto-router.d.ts +89 -0
  95. package/dist/learning/auto-router.d.ts.map +1 -1
  96. package/dist/learning/auto-router.js +188 -8
  97. package/dist/learning/auto-router.js.map +1 -1
  98. package/dist/learning/build-prerequisites.d.ts +68 -0
  99. package/dist/learning/build-prerequisites.d.ts.map +1 -0
  100. package/dist/learning/build-prerequisites.js +267 -0
  101. package/dist/learning/build-prerequisites.js.map +1 -0
  102. package/dist/learning/continuation-intent.d.ts +43 -0
  103. package/dist/learning/continuation-intent.d.ts.map +1 -0
  104. package/dist/learning/continuation-intent.js +82 -0
  105. package/dist/learning/continuation-intent.js.map +1 -0
  106. package/dist/learning/error-repair.d.ts +23 -1
  107. package/dist/learning/error-repair.d.ts.map +1 -1
  108. package/dist/learning/error-repair.js +58 -0
  109. package/dist/learning/error-repair.js.map +1 -1
  110. package/dist/learning/knowledge-base.d.ts +124 -0
  111. package/dist/learning/knowledge-base.d.ts.map +1 -0
  112. package/dist/learning/knowledge-base.js +337 -0
  113. package/dist/learning/knowledge-base.js.map +1 -0
  114. package/dist/learning/model-harness.d.ts +19 -0
  115. package/dist/learning/model-harness.d.ts.map +1 -1
  116. package/dist/learning/model-harness.js +27 -0
  117. package/dist/learning/model-harness.js.map +1 -1
  118. package/dist/learning/model-selection.d.ts +2 -1
  119. package/dist/learning/model-selection.d.ts.map +1 -1
  120. package/dist/learning/model-selection.js +35 -2
  121. package/dist/learning/model-selection.js.map +1 -1
  122. package/dist/learning/project-docs.d.ts +83 -0
  123. package/dist/learning/project-docs.d.ts.map +1 -0
  124. package/dist/learning/project-docs.js +132 -0
  125. package/dist/learning/project-docs.js.map +1 -0
  126. package/dist/learning/prompt-budget.d.ts +91 -0
  127. package/dist/learning/prompt-budget.d.ts.map +1 -0
  128. package/dist/learning/prompt-budget.js +99 -0
  129. package/dist/learning/prompt-budget.js.map +1 -0
  130. package/dist/learning/quota-ledger.d.ts +13 -0
  131. package/dist/learning/quota-ledger.d.ts.map +1 -1
  132. package/dist/learning/quota-ledger.js +28 -0
  133. package/dist/learning/quota-ledger.js.map +1 -1
  134. package/dist/learning/reasoning-trace.d.ts +55 -2
  135. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  136. package/dist/learning/reasoning-trace.js +48 -0
  137. package/dist/learning/reasoning-trace.js.map +1 -1
  138. package/dist/learning/resilient-call.d.ts.map +1 -1
  139. package/dist/learning/resilient-call.js +16 -3
  140. package/dist/learning/resilient-call.js.map +1 -1
  141. package/dist/learning/routing-history.d.ts +13 -0
  142. package/dist/learning/routing-history.d.ts.map +1 -1
  143. package/dist/learning/routing-history.js.map +1 -1
  144. package/dist/learning/seeded-benchmark.d.ts +14 -0
  145. package/dist/learning/seeded-benchmark.d.ts.map +1 -1
  146. package/dist/learning/seeded-benchmark.js +43 -4
  147. package/dist/learning/seeded-benchmark.js.map +1 -1
  148. package/dist/learning/turn-report.d.ts +78 -0
  149. package/dist/learning/turn-report.d.ts.map +1 -0
  150. package/dist/learning/turn-report.js +140 -0
  151. package/dist/learning/turn-report.js.map +1 -0
  152. package/dist/security/secret-scan.d.ts +156 -0
  153. package/dist/security/secret-scan.d.ts.map +1 -0
  154. package/dist/security/secret-scan.js +525 -0
  155. package/dist/security/secret-scan.js.map +1 -0
  156. package/dist/tools/edit-verification.d.ts +2 -11
  157. package/dist/tools/edit-verification.d.ts.map +1 -1
  158. package/dist/tools/edit-verification.js +33 -1
  159. package/dist/tools/edit-verification.js.map +1 -1
  160. package/dist/tools/loop-skill-hint.d.ts +51 -0
  161. package/dist/tools/loop-skill-hint.d.ts.map +1 -1
  162. package/dist/tools/loop-skill-hint.js +87 -2
  163. package/dist/tools/loop-skill-hint.js.map +1 -1
  164. package/dist/tools/plan-store.d.ts +126 -3
  165. package/dist/tools/plan-store.d.ts.map +1 -1
  166. package/dist/tools/plan-store.js +290 -6
  167. package/dist/tools/plan-store.js.map +1 -1
  168. package/dist/tools/registry.d.ts +20 -1
  169. package/dist/tools/registry.d.ts.map +1 -1
  170. package/dist/tools/registry.js +165 -4
  171. package/dist/tools/registry.js.map +1 -1
  172. package/dist/tools/remediation-ladder.d.ts +91 -0
  173. package/dist/tools/remediation-ladder.d.ts.map +1 -0
  174. package/dist/tools/remediation-ladder.js +308 -0
  175. package/dist/tools/remediation-ladder.js.map +1 -0
  176. package/dist/tools/run-terminal.d.ts.map +1 -1
  177. package/dist/tools/run-terminal.js +60 -2
  178. package/dist/tools/run-terminal.js.map +1 -1
  179. package/dist/tools/skills-hub.d.ts +7 -0
  180. package/dist/tools/skills-hub.d.ts.map +1 -1
  181. package/dist/tools/skills-hub.js +11 -2
  182. package/dist/tools/skills-hub.js.map +1 -1
  183. package/dist/tools/step-artifact.d.ts +35 -0
  184. package/dist/tools/step-artifact.d.ts.map +1 -0
  185. package/dist/tools/step-artifact.js +154 -0
  186. package/dist/tools/step-artifact.js.map +1 -0
  187. package/dist/tools/tool-loop.d.ts +35 -0
  188. package/dist/tools/tool-loop.d.ts.map +1 -1
  189. package/dist/tools/tool-loop.js +284 -9
  190. package/dist/tools/tool-loop.js.map +1 -1
  191. package/dist/tools/toolsets.js +4 -4
  192. package/dist/tools/toolsets.js.map +1 -1
  193. package/dist/tools/worktree.d.ts +11 -2
  194. package/dist/tools/worktree.d.ts.map +1 -1
  195. package/dist/tools/worktree.js +11 -2
  196. package/dist/tools/worktree.js.map +1 -1
  197. package/dist/utils/effect-verification.js +1 -1
  198. package/dist/utils/effect-verification.js.map +1 -1
  199. package/dist/web-dashboard/chat-console.d.ts +21 -1
  200. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  201. package/dist/web-dashboard/chat-console.js +33 -5
  202. package/dist/web-dashboard/chat-console.js.map +1 -1
  203. package/dist/web-dashboard/server.d.ts +29 -0
  204. package/dist/web-dashboard/server.d.ts.map +1 -1
  205. package/dist/web-dashboard/server.js +410 -5
  206. package/dist/web-dashboard/server.js.map +1 -1
  207. package/dist/web-dashboard/src/types.d.ts +137 -0
  208. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  209. package/dist/web-dashboard/workspace-guard.d.ts +7 -0
  210. package/dist/web-dashboard/workspace-guard.d.ts.map +1 -1
  211. package/dist/web-dashboard/workspace-guard.js +70 -0
  212. package/dist/web-dashboard/workspace-guard.js.map +1 -1
  213. package/package.json +4 -1
  214. package/src/web-dashboard/public/assets/index-DhiuR4S2.js +210 -0
  215. package/src/web-dashboard/public/assets/index-DhiuR4S2.js.map +1 -0
  216. package/src/web-dashboard/public/assets/index-tK8Y2qHB.css +1 -0
  217. package/src/web-dashboard/public/index.html +2 -2
  218. package/src/web-dashboard/public/assets/index-BdKf5Xw2.js +0 -207
  219. package/src/web-dashboard/public/assets/index-BdKf5Xw2.js.map +0 -1
  220. package/src/web-dashboard/public/assets/index-C9eBskc9.css +0 -1
@@ -29,6 +29,7 @@ import { ACTION_BY_INTENT } from '../nlu/actions.js';
29
29
  import { detectPermissionSeeking, IRREVERSIBLE_ACTION_RE } from '../learning/autonomy-policy.js';
30
30
  import { createFindingTool } from './finding-tool.js';
31
31
  import { recordArtifact } from './artifact-append.js';
32
+ import { checkStepArtifacts } from './step-artifact.js';
32
33
  // ─── Tool definitions ───────────────────────────────────────────────────────
33
34
  // ─── Shared auto-delivery helper ─────────────────────────────────────────
34
35
  // Tools that produce artifact files (generate_image, speak, video_generate)
@@ -197,6 +198,10 @@ export const planTodoSchema = z.object({
197
198
  .describe('Ordered steps (required for action=create)'),
198
199
  id: z.string().optional().describe('The step id to update (required for action=update)'),
199
200
  status: z.enum(['pending', 'running', 'done', 'blocked']).optional().describe('New status for the step (required for action=update)'),
201
+ note: z
202
+ .string()
203
+ .optional()
204
+ .describe('Optional plain-English note for this step, recorded with the update (e.g. "reproduced with a minimal test", "blocked on the missing API key"). Shown in the progress table and the completion summary.'),
200
205
  });
201
206
  /** H2 — delegate tool args: a focused subtask for a specialized sub-agent. */
202
207
  const delegateSchema = z.object({
@@ -527,7 +532,13 @@ registerTool({
527
532
  // Narrow on purpose — both conditions must hold, and any question naming an
528
533
  // irreversible action (overwrite, delete, publish, deploy, send, pay…) is
529
534
  // passed straight through, because that decision IS the user's.
530
- if ((ctx.writesAuthorized?.authorized === true || Boolean(ctx.envelope)) &&
535
+ // A confirmation the TOOL ITSELF demanded (run_terminal's "call ask_user")
536
+ // must not be swallowed by this gate — that is the deadlock fixed in
537
+ // ToolContext.pendingConfirmation. Consume the flag and fall through so the
538
+ // question reaches the user once.
539
+ const pending = ctx.pendingConfirmation;
540
+ if (!pending &&
541
+ (ctx.writesAuthorized?.authorized === true || Boolean(ctx.envelope)) &&
531
542
  detectPermissionSeeking(question) &&
532
543
  !IRREVERSIBLE_ACTION_RE.test(question)) {
533
544
  const recommended = choices[0]?.label ?? '';
@@ -551,6 +562,9 @@ registerTool({
551
562
  'high-impact and irreversible, and has no sensible default — re-call ask_user naming ' +
552
563
  'that specific irreversible choice (e.g. whether to overwrite an existing file).');
553
564
  }
565
+ // One confirmation refusal buys exactly ONE ask — clear it before rendering.
566
+ if (pending)
567
+ ctx.pendingConfirmation = undefined;
554
568
  const render = ctx.askUser || (await import('./ask-user.js')).renderAskUser;
555
569
  const answer = await render(question, choices, multi_select);
556
570
  const picked = Array.isArray(answer.answer) ? answer.answer.join(', ') : answer.answer;
@@ -668,12 +682,12 @@ registerTool({
668
682
  // cards show tool calls; this shows the plan itself).
669
683
  registerTool({
670
684
  name: 'plan_todo',
671
- description: 'Declare and track a multi-step plan: create ordered steps with descriptions, then mark each as running/done/blocked as you work through them. Use for any job with 2+ steps — the checklist persists across the whole conversation, so a later turn can reference "step N is done". Call create once at the start, then update per step as each completes.',
685
+ description: 'Declare and track a multi-step plan as a live progress table: create ordered steps with descriptions, then update each step (running → done/blocked) as you work, ideally with a short note saying what happened. Use for any job with 2+ steps — the table persists across the whole conversation AND across sessions, so a later turn can reference "step N is done", and the plan closes out with an achieved summary. Call create once at the start, then update each step as it starts and again as it finishes.',
672
686
  category: 'workflow',
673
687
  inputSchema: planTodoSchema,
674
688
  endsAgentStep: false,
675
689
  run: async (args, ctx) => {
676
- const { action, goal, steps, id, status } = planTodoSchema.parse(args);
690
+ const { action, goal, steps, id, status, note } = planTodoSchema.parse(args);
677
691
  const { defaultPlanStore } = await import('./plan-store.js');
678
692
  const store = ctx.planStore ?? defaultPlanStore();
679
693
  if (action === 'create') {
@@ -683,7 +697,30 @@ registerTool({
683
697
  store.create(goal, steps);
684
698
  }
685
699
  else if (id && status) {
686
- store.update(id, status);
700
+ // A1 — never accept an update that matched nothing. Silently returning
701
+ // the unchanged plan reads as success to the model (and as tracking to
702
+ // the user) while the counter stays frozen; observed live on the `cal`
703
+ // Android run, where the checklist sat at `1/7` across four successful
704
+ // steps. Resolve the reference first and, when it names no step, say so
705
+ // with the ids that DO exist so the model can re-send it and move on.
706
+ const resolved = store.resolveStepId ? store.resolveStepId(id) : id;
707
+ if (!resolved) {
708
+ const valid = (store.snapshot?.()?.steps ?? []).map((s) => s.id);
709
+ return `Error: plan_todo found no step "${id}" — valid step ids: ${valid.length > 0 ? valid.join(', ') : '(none — declare the plan with action "create" first)'}. Re-send this update with one of those ids.`;
710
+ }
711
+ // A6 — a step that NAMES an artifact is done only once that artifact
712
+ // exists. The plan can otherwise advance past undelivered work (the `cal`
713
+ // run kept going after its APK step never produced an APK). Checked
714
+ // against the workspace; a step with no file-like token is unaffected.
715
+ if (status === 'done' && ctx.cwd) {
716
+ const step = store.snapshot?.()?.steps.find((s) => s.id === resolved);
717
+ const { missing } = checkStepArtifacts(step?.description ?? '', ctx.cwd);
718
+ if (missing.length > 0) {
719
+ return (`Error: step "${resolved}" names artifact(s) that do not exist yet: ${missing.join(', ')}. ` +
720
+ `Create them in ${ctx.cwd}, then mark the step done — a step is not done while its artifact is missing.`);
721
+ }
722
+ }
723
+ store.update(resolved, status, note);
687
724
  }
688
725
  else {
689
726
  return 'Error: plan_todo update needs id + status (pending|running|done|blocked).';
@@ -1880,6 +1917,53 @@ registerTool({
1880
1917
  return JSON.stringify(m.getSecurityScorer().score(filePath));
1881
1918
  }),
1882
1919
  });
1920
+ // Secret scan tool — LOCAL, dependency-free, on-demand.
1921
+ //
1922
+ // `security_score`/`ast_audit` scan ONE named file and report a score. This is
1923
+ // the other question an agent that writes and commits code actually has to
1924
+ // answer before it commits: "is anything secret-shaped anywhere in here?". It
1925
+ // walks the tree with bounded coverage, needs no `gitleaks`/`ggshield` binary
1926
+ // and no network, and reports masked values only — see src/security/secret-scan.ts.
1927
+ registerTool({
1928
+ name: 'secret_scan',
1929
+ description: 'Scan the workspace for secret-shaped strings (API keys, tokens, private keys, credentials in URLs) before committing or publishing. Fully local — no network and no external scanner binary — and every reported value is masked. Returns findings with path:line and severity, plus a summary. Set history:true to scan git HISTORY as well (every committed version of every file) — a key deleted in a later commit is STILL committed and must be rotated. A clean result means "nothing matched a known shape", NOT "no secrets": report it as a lint, never as a guarantee.',
1930
+ category: 'workflow',
1931
+ inputSchema: z.object({
1932
+ path: z.string().optional().describe('Directory to scan (relative to the workspace, or absolute). Default: the workspace root.'),
1933
+ subdir: z.string().optional().describe('Narrow the scan to one subtree, e.g. "src" or "docs".'),
1934
+ maxFiles: z.number().optional().describe('Cap on files visited (default 5000) — a huge repo does not hang the turn.'),
1935
+ history: z.boolean().optional().describe('Also scan git history (reachable blobs) for secrets ever committed. Default false. Use before publishing or when auditing an existing repo.'),
1936
+ maxBlobs: z.number().optional().describe('With history:true, cap on distinct historical blobs read (default 2000).'),
1937
+ }),
1938
+ endsAgentStep: false,
1939
+ run: async (args, ctx) => {
1940
+ const { join } = await import('node:path');
1941
+ const m = await import('../security/secret-scan.js');
1942
+ const { path: target, subdir, maxFiles, history, maxBlobs } = args;
1943
+ const cwd = ctx?.cwd ?? process.cwd();
1944
+ const root = target
1945
+ ? (target.startsWith('/') || /^[A-Za-z]:[\\/]/.test(target) ? target : join(cwd, target))
1946
+ : cwd;
1947
+ if (history) {
1948
+ // History only: the tree scan runs first (fast, no git), then history is
1949
+ // appended as its own section so a reader can tell the two apart.
1950
+ const tree = m.scanDirectory(root, {
1951
+ ...(subdir ? { subdir } : {}),
1952
+ ...(maxFiles ? { maxFiles } : {}),
1953
+ });
1954
+ const hist = m.scanGitHistory(root, {
1955
+ ...(subdir ? { subdir } : {}),
1956
+ ...(maxBlobs ? { maxBlobs } : {}),
1957
+ });
1958
+ return [m.formatSecretScan(tree), '', '── git history ──', m.formatSecretHistoryScan(hist)].join('\n');
1959
+ }
1960
+ const result = m.scanDirectory(root, {
1961
+ ...(subdir ? { subdir } : {}),
1962
+ ...(maxFiles ? { maxFiles } : {}),
1963
+ });
1964
+ return m.formatSecretScan(result);
1965
+ },
1966
+ });
1883
1967
  // ─── Batch 5: Infrastructure ──────────────────────────────────────────────
1884
1968
  // Tool search tool — the tiered-exposure discovery surface. Two actions:
1885
1969
  // - search (default): fuzzy-find tools by query (the pre-existing behavior).
@@ -2901,6 +2985,83 @@ registerTool({
2901
2985
  return mgr.extract(filePath, { ocr: Boolean(ocr) }).then((r) => JSON.stringify(r));
2902
2986
  }),
2903
2987
  });
2988
+ // Knowledge — tag-scoped, vectorized retrieval over the user's own documents
2989
+ //
2990
+ // WHY A TOOL. The user's flow is "bring documents, tag them, ask questions scoped
2991
+ // to that tag." Retrieval must be something the AGENT can drive mid-task without
2992
+ // a CLI round-trip, and it must pay the read+embed cost ONCE per document rather
2993
+ // than re-reading the file into context every turn. This tool is that surface:
2994
+ // `add` ingests under a tag (documents are extracted, chunked, embedded once),
2995
+ // `query` returns the tag's top-k chunks with their source files so the model can
2996
+ // answer the data half from the user's data and the generic half from its own
2997
+ // knowledge / web-research.
2998
+ registerTool({
2999
+ name: 'knowledge',
3000
+ description: 'Answer questions from the USER\'S OWN tagged documents. `add` ingests files/folders under a tag '
3001
+ + '(extract → chunk → embed, done once); `query` retrieves the tag\'s most relevant passages, labeled '
3002
+ + 'with their source file, so the data part of an answer comes from the user\'s data and the general '
3003
+ + 'part from model knowledge or web-research. Use this instead of re-reading a large document on every '
3004
+ + 'turn. Actions: add | query | list | forget | stats. Tags are normalized (spaces/capitals/underscores '
3005
+ + 'become lowercase-hyphen), e.g. "Dheeraj health report" → "dheeraj-health-report".',
3006
+ category: 'workflow',
3007
+ inputSchema: z.object({
3008
+ action: z.enum(['add', 'query', 'list', 'forget', 'stats']).describe('Action'),
3009
+ tag: z.string().optional().describe('Tag/hashtag scoping the data (e.g. "dheeraj-health-report")'),
3010
+ paths: z.array(z.string()).optional().describe('Files or folders to ingest (action: add)'),
3011
+ question: z.string().optional().describe('Question to answer from the tagged data (action: query)'),
3012
+ topK: z.number().optional().describe('Chunks to retrieve (action: query, default 6)'),
3013
+ }),
3014
+ endsAgentStep: false,
3015
+ run: (args) => import('../learning/knowledge-base.js').then(async (m) => {
3016
+ const { action, tag, paths, question, topK } = args;
3017
+ switch (action) {
3018
+ case 'add': {
3019
+ if (!tag)
3020
+ return 'tag is required for action: add';
3021
+ if (!paths || paths.length === 0)
3022
+ return 'paths is required for action: add';
3023
+ const result = await m.ingestKnowledge(tag, paths);
3024
+ const skipped = result.skipped.length
3025
+ ? `\nSkipped: ${result.skipped.map((s) => `${s.path} (${s.reason})`).join('; ')}`
3026
+ : '';
3027
+ return `✅ Ingested ${result.files} document(s) / ${result.chunks} chunk(s) under tag '${result.tag}'.`
3028
+ + `\nQuery it with action: query, tag: '${result.tag}'.${skipped}`;
3029
+ }
3030
+ case 'query': {
3031
+ if (!tag)
3032
+ return 'tag is required for action: query';
3033
+ if (!question)
3034
+ return 'question is required for action: query';
3035
+ const hits = await m.queryKnowledge(tag, question, { topK });
3036
+ return m.formatKnowledgeContext(tag, hits);
3037
+ }
3038
+ case 'list': {
3039
+ const tags = m.listKnowledgeTags();
3040
+ if (tags.length === 0)
3041
+ return 'No knowledge tags yet. Ingest documents with action: add.';
3042
+ return tags
3043
+ .map((t) => ` • ${t.tag} — ${t.chunkCount} chunk(s) across ${t.documents.length} document(s)`)
3044
+ .join('\n');
3045
+ }
3046
+ case 'forget': {
3047
+ if (!tag)
3048
+ return 'tag is required for action: forget';
3049
+ const removed = await m.forgetKnowledgeTag(tag);
3050
+ return removed ? `🗑️ Removed knowledge tag '${m.normalizeKnowledgeTag(tag)}'.` : `No knowledge tag '${tag}' found.`;
3051
+ }
3052
+ case 'stats': {
3053
+ if (!tag)
3054
+ return 'tag is required for action: stats';
3055
+ const entry = m.getKnowledgeTag(tag);
3056
+ if (!entry)
3057
+ return `No knowledge tag '${tag}' found.`;
3058
+ return JSON.stringify(entry);
3059
+ }
3060
+ default:
3061
+ return `Unknown action '${action}'`;
3062
+ }
3063
+ }),
3064
+ });
2904
3065
  // Credential Files — Credential file management
2905
3066
  registerTool({
2906
3067
  name: 'credential_files',