agent-nuvira 3.3.13 → 3.3.15
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.
- package/README.md +67 -1
- package/dist/agents/agents/planner.d.ts.map +1 -1
- package/dist/agents/agents/planner.js +9 -0
- package/dist/agents/agents/planner.js.map +1 -1
- package/dist/agents/orchestrator.d.ts +31 -0
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +74 -126
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/cli/chat.d.ts +89 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +765 -33
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/cli-program.d.ts.map +1 -1
- package/dist/cli/cli-program.js +12 -0
- package/dist/cli/cli-program.js.map +1 -1
- package/dist/cli/config.d.ts +12 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +86 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/decisions.d.ts +28 -0
- package/dist/cli/decisions.d.ts.map +1 -0
- package/dist/cli/decisions.js +140 -0
- package/dist/cli/decisions.js.map +1 -0
- package/dist/cli/doctor.d.ts +6 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +46 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/failover-runner.d.ts.map +1 -1
- package/dist/cli/failover-runner.js +19 -1
- package/dist/cli/failover-runner.js.map +1 -1
- package/dist/cli/feedback.d.ts.map +1 -1
- package/dist/cli/feedback.js +5 -1
- package/dist/cli/feedback.js.map +1 -1
- package/dist/cli/hooks.d.ts +30 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +224 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/knowledge.d.ts +9 -1
- package/dist/cli/knowledge.d.ts.map +1 -1
- package/dist/cli/knowledge.js +93 -2
- package/dist/cli/knowledge.js.map +1 -1
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +47 -12
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/model.d.ts +20 -0
- package/dist/cli/model.d.ts.map +1 -1
- package/dist/cli/model.js +294 -14
- package/dist/cli/model.js.map +1 -1
- package/dist/cli/rate.d.ts +78 -0
- package/dist/cli/rate.d.ts.map +1 -0
- package/dist/cli/rate.js +294 -0
- package/dist/cli/rate.js.map +1 -0
- package/dist/config/process-env.d.ts.map +1 -1
- package/dist/config/process-env.js +10 -0
- package/dist/config/process-env.js.map +1 -1
- package/dist/enterprise/vault.d.ts.map +1 -1
- package/dist/enterprise/vault.js +5 -21
- package/dist/enterprise/vault.js.map +1 -1
- package/dist/gateway/hook-contract.d.ts +33 -0
- package/dist/gateway/hook-contract.d.ts.map +1 -1
- package/dist/gateway/hook-contract.js +97 -0
- package/dist/gateway/hook-contract.js.map +1 -1
- package/dist/inference/gemini-adapter.d.ts.map +1 -1
- package/dist/inference/gemini-adapter.js +4 -0
- package/dist/inference/gemini-adapter.js.map +1 -1
- package/dist/inference/interface.d.ts +37 -0
- package/dist/inference/interface.d.ts.map +1 -1
- package/dist/inference/native-tools.d.ts +16 -1
- package/dist/inference/native-tools.d.ts.map +1 -1
- package/dist/inference/native-tools.js +34 -10
- package/dist/inference/native-tools.js.map +1 -1
- package/dist/inference/route-resolver.d.ts +30 -0
- package/dist/inference/route-resolver.d.ts.map +1 -1
- package/dist/inference/route-resolver.js +158 -1
- package/dist/inference/route-resolver.js.map +1 -1
- package/dist/inference/tools.d.ts +16 -1
- package/dist/inference/tools.d.ts.map +1 -1
- package/dist/inference/tools.js +51 -18
- package/dist/inference/tools.js.map +1 -1
- package/dist/learning/acceptance-model.d.ts +247 -0
- package/dist/learning/acceptance-model.d.ts.map +1 -0
- package/dist/learning/acceptance-model.js +708 -0
- package/dist/learning/acceptance-model.js.map +1 -0
- package/dist/learning/auto-router.d.ts +68 -24
- package/dist/learning/auto-router.d.ts.map +1 -1
- package/dist/learning/auto-router.js +301 -96
- package/dist/learning/auto-router.js.map +1 -1
- package/dist/learning/autonomy-policy.d.ts +12 -16
- package/dist/learning/autonomy-policy.d.ts.map +1 -1
- package/dist/learning/autonomy-policy.js +50 -48
- package/dist/learning/autonomy-policy.js.map +1 -1
- package/dist/learning/behavioural-labels.d.ts +86 -0
- package/dist/learning/behavioural-labels.d.ts.map +1 -0
- package/dist/learning/behavioural-labels.js +166 -0
- package/dist/learning/behavioural-labels.js.map +1 -0
- package/dist/learning/capability-evidence.d.ts +167 -0
- package/dist/learning/capability-evidence.d.ts.map +1 -0
- package/dist/learning/capability-evidence.js +210 -0
- package/dist/learning/capability-evidence.js.map +1 -0
- package/dist/learning/capability-readiness.d.ts +64 -0
- package/dist/learning/capability-readiness.d.ts.map +1 -0
- package/dist/learning/capability-readiness.js +74 -0
- package/dist/learning/capability-readiness.js.map +1 -0
- package/dist/learning/capability-types.d.ts +169 -0
- package/dist/learning/capability-types.d.ts.map +1 -0
- package/dist/learning/capability-types.js +20 -0
- package/dist/learning/capability-types.js.map +1 -0
- package/dist/learning/catalog-feed.d.ts +89 -0
- package/dist/learning/catalog-feed.d.ts.map +1 -0
- package/dist/learning/catalog-feed.js +211 -0
- package/dist/learning/catalog-feed.js.map +1 -0
- package/dist/learning/cli-intent-effects.d.ts +60 -0
- package/dist/learning/cli-intent-effects.d.ts.map +1 -0
- package/dist/learning/cli-intent-effects.js +153 -0
- package/dist/learning/cli-intent-effects.js.map +1 -0
- package/dist/learning/command-adaptation.d.ts +41 -0
- package/dist/learning/command-adaptation.d.ts.map +1 -0
- package/dist/learning/command-adaptation.js +122 -0
- package/dist/learning/command-adaptation.js.map +1 -0
- package/dist/learning/consent-picture.d.ts +42 -0
- package/dist/learning/consent-picture.d.ts.map +1 -0
- package/dist/learning/consent-picture.js +53 -0
- package/dist/learning/consent-picture.js.map +1 -0
- package/dist/learning/context-budget.d.ts +37 -6
- package/dist/learning/context-budget.d.ts.map +1 -1
- package/dist/learning/context-budget.js +38 -9
- package/dist/learning/context-budget.js.map +1 -1
- package/dist/learning/decision-log.d.ts +114 -0
- package/dist/learning/decision-log.d.ts.map +1 -0
- package/dist/learning/decision-log.js +296 -0
- package/dist/learning/decision-log.js.map +1 -0
- package/dist/learning/deliverable-class.d.ts.map +1 -1
- package/dist/learning/deliverable-class.js +19 -0
- package/dist/learning/deliverable-class.js.map +1 -1
- package/dist/learning/deliverable-corpus.d.ts +117 -0
- package/dist/learning/deliverable-corpus.d.ts.map +1 -0
- package/dist/learning/deliverable-corpus.js +176 -0
- package/dist/learning/deliverable-corpus.js.map +1 -0
- package/dist/learning/error-repair.d.ts +22 -1
- package/dist/learning/error-repair.d.ts.map +1 -1
- package/dist/learning/error-repair.js +37 -0
- package/dist/learning/error-repair.js.map +1 -1
- package/dist/learning/exhaustion-report.d.ts +136 -0
- package/dist/learning/exhaustion-report.d.ts.map +1 -0
- package/dist/learning/exhaustion-report.js +239 -0
- package/dist/learning/exhaustion-report.js.map +1 -0
- package/dist/learning/failure-bookkeeping.d.ts +16 -0
- package/dist/learning/failure-bookkeeping.d.ts.map +1 -1
- package/dist/learning/failure-bookkeeping.js +51 -5
- package/dist/learning/failure-bookkeeping.js.map +1 -1
- package/dist/learning/gate-friction.d.ts +39 -0
- package/dist/learning/gate-friction.d.ts.map +1 -0
- package/dist/learning/gate-friction.js +91 -0
- package/dist/learning/gate-friction.js.map +1 -0
- package/dist/learning/harness-narration.d.ts +34 -0
- package/dist/learning/harness-narration.d.ts.map +1 -0
- package/dist/learning/harness-narration.js +58 -0
- package/dist/learning/harness-narration.js.map +1 -0
- package/dist/learning/hub-skill-catalog.d.ts.map +1 -1
- package/dist/learning/hub-skill-catalog.js +1 -2
- package/dist/learning/hub-skill-catalog.js.map +1 -1
- package/dist/learning/hybrid-router.d.ts +65 -0
- package/dist/learning/hybrid-router.d.ts.map +1 -1
- package/dist/learning/hybrid-router.js +186 -0
- package/dist/learning/hybrid-router.js.map +1 -1
- package/dist/learning/knowledge-base.d.ts +229 -3
- package/dist/learning/knowledge-base.d.ts.map +1 -1
- package/dist/learning/knowledge-base.js +603 -25
- package/dist/learning/knowledge-base.js.map +1 -1
- package/dist/learning/knowledge-eval.d.ts +171 -0
- package/dist/learning/knowledge-eval.d.ts.map +1 -0
- package/dist/learning/knowledge-eval.js +196 -0
- package/dist/learning/knowledge-eval.js.map +1 -0
- package/dist/learning/knowledge-turn.d.ts +92 -0
- package/dist/learning/knowledge-turn.d.ts.map +1 -0
- package/dist/learning/knowledge-turn.js +190 -0
- package/dist/learning/knowledge-turn.js.map +1 -0
- package/dist/learning/learned-commands.d.ts +64 -0
- package/dist/learning/learned-commands.d.ts.map +1 -0
- package/dist/learning/learned-commands.js +108 -0
- package/dist/learning/learned-commands.js.map +1 -0
- package/dist/learning/lexical-search.d.ts +50 -0
- package/dist/learning/lexical-search.d.ts.map +1 -0
- package/dist/learning/lexical-search.js +111 -0
- package/dist/learning/lexical-search.js.map +1 -0
- package/dist/learning/machine-facts.d.ts +71 -0
- package/dist/learning/machine-facts.d.ts.map +1 -0
- package/dist/learning/machine-facts.js +164 -0
- package/dist/learning/machine-facts.js.map +1 -0
- package/dist/learning/markdown-outline.d.ts +73 -0
- package/dist/learning/markdown-outline.d.ts.map +1 -0
- package/dist/learning/markdown-outline.js +136 -0
- package/dist/learning/markdown-outline.js.map +1 -0
- package/dist/learning/model-first-router.d.ts +33 -1
- package/dist/learning/model-first-router.d.ts.map +1 -1
- package/dist/learning/model-first-router.js +123 -9
- package/dist/learning/model-first-router.js.map +1 -1
- package/dist/learning/model-identity.d.ts +80 -0
- package/dist/learning/model-identity.d.ts.map +1 -0
- package/dist/learning/model-identity.js +115 -0
- package/dist/learning/model-identity.js.map +1 -0
- package/dist/learning/model-registry.d.ts +141 -2
- package/dist/learning/model-registry.d.ts.map +1 -1
- package/dist/learning/model-registry.js +324 -38
- package/dist/learning/model-registry.js.map +1 -1
- package/dist/learning/model-scoring.d.ts.map +1 -1
- package/dist/learning/model-scoring.js +8 -1
- package/dist/learning/model-scoring.js.map +1 -1
- package/dist/learning/model-selection.d.ts.map +1 -1
- package/dist/learning/model-selection.js +41 -0
- package/dist/learning/model-selection.js.map +1 -1
- package/dist/learning/outcome-observation.d.ts +49 -0
- package/dist/learning/outcome-observation.d.ts.map +1 -0
- package/dist/learning/outcome-observation.js +95 -0
- package/dist/learning/outcome-observation.js.map +1 -0
- package/dist/learning/pair-entitlement.d.ts +184 -0
- package/dist/learning/pair-entitlement.d.ts.map +1 -0
- package/dist/learning/pair-entitlement.js +214 -0
- package/dist/learning/pair-entitlement.js.map +1 -0
- package/dist/learning/provider-fallback.d.ts +23 -1
- package/dist/learning/provider-fallback.d.ts.map +1 -1
- package/dist/learning/provider-fallback.js +24 -0
- package/dist/learning/provider-fallback.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +65 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +123 -1
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/requirement-probe.d.ts +55 -0
- package/dist/learning/requirement-probe.d.ts.map +1 -0
- package/dist/learning/requirement-probe.js +77 -0
- package/dist/learning/requirement-probe.js.map +1 -0
- package/dist/learning/resilient-call.d.ts +20 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +42 -4
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/response-usability.d.ts +85 -0
- package/dist/learning/response-usability.d.ts.map +1 -0
- package/dist/learning/response-usability.js +135 -0
- package/dist/learning/response-usability.js.map +1 -0
- package/dist/learning/router-bandit.d.ts +132 -0
- package/dist/learning/router-bandit.d.ts.map +1 -1
- package/dist/learning/router-bandit.js +253 -0
- package/dist/learning/router-bandit.js.map +1 -1
- package/dist/learning/routing-history.d.ts +41 -2
- package/dist/learning/routing-history.d.ts.map +1 -1
- package/dist/learning/routing-history.js.map +1 -1
- package/dist/learning/run-trace.d.ts +21 -0
- package/dist/learning/run-trace.d.ts.map +1 -1
- package/dist/learning/run-trace.js +38 -0
- package/dist/learning/run-trace.js.map +1 -1
- package/dist/learning/session-grant.d.ts +75 -0
- package/dist/learning/session-grant.d.ts.map +1 -0
- package/dist/learning/session-grant.js +112 -0
- package/dist/learning/session-grant.js.map +1 -0
- package/dist/learning/task-model-continuity.d.ts +73 -0
- package/dist/learning/task-model-continuity.d.ts.map +1 -0
- package/dist/learning/task-model-continuity.js +87 -0
- package/dist/learning/task-model-continuity.js.map +1 -0
- package/dist/learning/turn-feedback.d.ts +93 -0
- package/dist/learning/turn-feedback.d.ts.map +1 -0
- package/dist/learning/turn-feedback.js +127 -0
- package/dist/learning/turn-feedback.js.map +1 -0
- package/dist/learning/turn-report.d.ts +104 -2
- package/dist/learning/turn-report.d.ts.map +1 -1
- package/dist/learning/turn-report.js +161 -6
- package/dist/learning/turn-report.js.map +1 -1
- package/dist/learning/verdict-routing.d.ts +51 -0
- package/dist/learning/verdict-routing.d.ts.map +1 -0
- package/dist/learning/verdict-routing.js +122 -0
- package/dist/learning/verdict-routing.js.map +1 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +5 -0
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/manager.d.ts +10 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +20 -0
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/mcp-discovery.d.ts +34 -0
- package/dist/mcp/mcp-discovery.d.ts.map +1 -0
- package/dist/mcp/mcp-discovery.js +71 -0
- package/dist/mcp/mcp-discovery.js.map +1 -0
- package/dist/mcp/mcp-schema-cache.d.ts +17 -0
- package/dist/mcp/mcp-schema-cache.d.ts.map +1 -1
- package/dist/mcp/mcp-schema-cache.js +47 -3
- package/dist/mcp/mcp-schema-cache.js.map +1 -1
- package/dist/mcp/types.d.ts +28 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/parity/drivers.d.ts.map +1 -1
- package/dist/parity/drivers.js +3 -0
- package/dist/parity/drivers.js.map +1 -1
- package/dist/parity/observation.d.ts +12 -0
- package/dist/parity/observation.d.ts.map +1 -1
- package/dist/parity/observation.js +14 -0
- package/dist/parity/observation.js.map +1 -1
- package/dist/tools/ask-user.d.ts.map +1 -1
- package/dist/tools/ask-user.js +4 -0
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/capability-registry.d.ts +150 -0
- package/dist/tools/capability-registry.d.ts.map +1 -0
- package/dist/tools/capability-registry.js +400 -0
- package/dist/tools/capability-registry.js.map +1 -0
- package/dist/tools/child-agent-runtime.d.ts.map +1 -1
- package/dist/tools/child-agent-runtime.js +19 -5
- package/dist/tools/child-agent-runtime.js.map +1 -1
- package/dist/tools/coding-tools.d.ts +17 -5
- package/dist/tools/coding-tools.d.ts.map +1 -1
- package/dist/tools/coding-tools.js +80 -11
- package/dist/tools/coding-tools.js.map +1 -1
- package/dist/tools/command-memo.d.ts +116 -0
- package/dist/tools/command-memo.d.ts.map +1 -0
- package/dist/tools/command-memo.js +202 -0
- package/dist/tools/command-memo.js.map +1 -0
- package/dist/tools/git-tool.d.ts.map +1 -1
- package/dist/tools/git-tool.js +13 -3
- package/dist/tools/git-tool.js.map +1 -1
- package/dist/tools/harness-directive.d.ts +42 -0
- package/dist/tools/harness-directive.d.ts.map +1 -0
- package/dist/tools/harness-directive.js +45 -0
- package/dist/tools/harness-directive.js.map +1 -0
- package/dist/tools/loop-skill-hint.d.ts +74 -174
- package/dist/tools/loop-skill-hint.d.ts.map +1 -1
- package/dist/tools/loop-skill-hint.js +110 -536
- package/dist/tools/loop-skill-hint.js.map +1 -1
- package/dist/tools/mcp-client-tool.d.ts +2 -0
- package/dist/tools/mcp-client-tool.d.ts.map +1 -1
- package/dist/tools/mcp-client-tool.js +3 -0
- package/dist/tools/mcp-client-tool.js.map +1 -1
- package/dist/tools/modality/shared.d.ts +1 -4
- package/dist/tools/modality/shared.d.ts.map +1 -1
- package/dist/tools/modality/shared.js +7 -22
- package/dist/tools/modality/shared.js.map +1 -1
- package/dist/tools/plan-store.d.ts +20 -2
- package/dist/tools/plan-store.d.ts.map +1 -1
- package/dist/tools/plan-store.js +51 -7
- package/dist/tools/plan-store.js.map +1 -1
- package/dist/tools/registry.d.ts +76 -3
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +486 -26
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/remediation-ladder.d.ts.map +1 -1
- package/dist/tools/remediation-ladder.js +8 -0
- package/dist/tools/remediation-ladder.js.map +1 -1
- package/dist/tools/run-cli.d.ts.map +1 -1
- package/dist/tools/run-cli.js +29 -4
- package/dist/tools/run-cli.js.map +1 -1
- package/dist/tools/run-terminal.d.ts +11 -0
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +265 -10
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/tool-loop.d.ts +321 -13
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +867 -66
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/utils/atomic-store.d.ts +53 -0
- package/dist/utils/atomic-store.d.ts.map +1 -0
- package/dist/utils/atomic-store.js +178 -0
- package/dist/utils/atomic-store.js.map +1 -0
- package/dist/utils/binary-probe.d.ts +25 -0
- package/dist/utils/binary-probe.d.ts.map +1 -0
- package/dist/utils/binary-probe.js +48 -0
- package/dist/utils/binary-probe.js.map +1 -0
- package/dist/utils/workspace-path.d.ts +50 -0
- package/dist/utils/workspace-path.d.ts.map +1 -0
- package/dist/utils/workspace-path.js +86 -0
- package/dist/utils/workspace-path.js.map +1 -0
- package/dist/web-dashboard/chat-console.d.ts +24 -0
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js +38 -0
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/project-context.d.ts +27 -2
- package/dist/web-dashboard/project-context.d.ts.map +1 -1
- package/dist/web-dashboard/project-context.js +32 -2
- package/dist/web-dashboard/project-context.js.map +1 -1
- package/dist/web-dashboard/server.d.ts +11 -0
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +362 -11
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +170 -1
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/dist/web-dashboard/workspace-guard.d.ts +11 -2
- package/dist/web-dashboard/workspace-guard.d.ts.map +1 -1
- package/dist/web-dashboard/workspace-guard.js +24 -12
- package/dist/web-dashboard/workspace-guard.js.map +1 -1
- package/dist/web-dashboard/workspace-resolution.d.ts +87 -0
- package/dist/web-dashboard/workspace-resolution.d.ts.map +1 -0
- package/dist/web-dashboard/workspace-resolution.js +105 -0
- package/dist/web-dashboard/workspace-resolution.js.map +1 -0
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/index-zjbdJnO-.js +210 -0
- package/src/web-dashboard/public/assets/index-zjbdJnO-.js.map +1 -0
- package/src/web-dashboard/public/index.html +1 -1
- package/dist/agents/nvda-addon.d.ts +0 -14
- package/dist/agents/nvda-addon.js +0 -52
- package/dist/forwarded.d.ts +0 -2
- package/dist/forwarded.js +0 -2
- package/src/web-dashboard/public/assets/index-DhiuR4S2.js +0 -210
- package/src/web-dashboard/public/assets/index-DhiuR4S2.js.map +0 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge TURN wiring (`src/learning/knowledge-turn.ts`) — the deterministic,
|
|
3
|
+
* tag-scoped pre-step for a chat turn.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS. `knowledge-base.ts` is the STORE, and the `knowledge` tool
|
|
6
|
+
* lets the model drive it mid-task. Neither answers "answer from THIS document",
|
|
7
|
+
* because that shape makes the model the trigger: it has to know the tag exists
|
|
8
|
+
* and decide to use it, and a wrong guess is a silent answer from the wrong
|
|
9
|
+
* corpus. Here a message that OPENS with a resolvable tag has that tag's
|
|
10
|
+
* relevant passages retrieved and placed in front of the model BEFORE it sees
|
|
11
|
+
* the question. The trigger is syntax, not judgement — which is what makes it
|
|
12
|
+
* deterministic, explainable, and testable.
|
|
13
|
+
*
|
|
14
|
+
* THE CAPABILITY GUARANTEE (the whole point of this module). It is INERT unless
|
|
15
|
+
* the message opens with a marker that resolves to a tag the user actually has:
|
|
16
|
+
*
|
|
17
|
+
* - no marker, or no tags at all → `block: ''`, NO embedder call, and the turn
|
|
18
|
+
* is byte-identical to one from a build without this feature;
|
|
19
|
+
* - marker names an unknown tag → a short "no such tag" note, and the question
|
|
20
|
+
* is answered normally — the wrong corpus is never substituted;
|
|
21
|
+
* - tag exists but nothing clears the relevance floor → a note saying the
|
|
22
|
+
* tagged documents did not cover it, which is the honest answer;
|
|
23
|
+
* - any failure at all → `block: ''` (best-effort, mirroring the ambient
|
|
24
|
+
* project-context contract in `src/tools/loop-project-context.ts`).
|
|
25
|
+
*
|
|
26
|
+
* So it cannot add cost, latency, noise, or a wrong corpus to a turn that did
|
|
27
|
+
* not ask for it. And because the block is a USER-TURN message (never the system
|
|
28
|
+
* prompt), the prompt stays byte-stable and therefore cacheable — the same rule
|
|
29
|
+
* the channel-policy block follows in `src/cli/chat.ts`.
|
|
30
|
+
*
|
|
31
|
+
* WHERE IT RUNS. One seam — `runChatAnswer` in `src/cli/chat.ts` — is shared by
|
|
32
|
+
* the CLI, the dashboard console (`debugSurface: 'dashboard-chat'`) and the
|
|
33
|
+
* gateway (`gateway-chat`), so all three surfaces get the same behaviour and
|
|
34
|
+
* cannot drift.
|
|
35
|
+
*
|
|
36
|
+
* THE MARKER. `#tag` as the FIRST token, followed by whitespace or end of input.
|
|
37
|
+
* A leading `/` is already the interactive chat's command prefix, so it is not
|
|
38
|
+
* used here. The whole-word requirement plus requiring the tag to start with a
|
|
39
|
+
* LETTER is what keeps `#1 priority`, a Markdown heading (`# Title`), a shebang
|
|
40
|
+
* (`#!/bin/bash`), and mid-sentence `C#` from resolving. A pasted `#include`
|
|
41
|
+
* DOES parse as the tag name `include` — which is exactly why an unknown marker
|
|
42
|
+
* only ever produces a note, and why the whole feature is inert until the user
|
|
43
|
+
* has at least one tag of their own.
|
|
44
|
+
*/
|
|
45
|
+
import { formatKnowledgeContext, listKnowledgeTags, normalizeKnowledgeTag, queryKnowledge, } from './knowledge-base.js';
|
|
46
|
+
import { logger } from '../utils/logger.js';
|
|
47
|
+
/**
|
|
48
|
+
* The marker: `#tag` as the first token. The tag must begin with a LETTER (so
|
|
49
|
+
* `#1`, `#!`, and `#/` cannot match) and must end at whitespace or end-of-input
|
|
50
|
+
* (so `#fff,` does not run on into the sentence).
|
|
51
|
+
*/
|
|
52
|
+
export const KNOWLEDGE_MARKER_RE = /^\s*#([A-Za-z][A-Za-z0-9_-]*)(?=\s|$)/;
|
|
53
|
+
/**
|
|
54
|
+
* Hard cap on the injected block. `topK` already bounds the chunk COUNT, but a
|
|
55
|
+
* pathological document set could still produce a large block, and a context
|
|
56
|
+
* block must never crowd out the ask it was meant to help answer. Truncation is
|
|
57
|
+
* explicit, never silent.
|
|
58
|
+
*/
|
|
59
|
+
export const MAX_KNOWLEDGE_BLOCK_CHARS = 12_000;
|
|
60
|
+
/** How many candidates a "did you mean" note may name. */
|
|
61
|
+
export const MAX_TAG_SUGGESTIONS = 3;
|
|
62
|
+
/** The tag a message opens with, normalized, or null when it opens with none. */
|
|
63
|
+
export function parseKnowledgeMarker(message) {
|
|
64
|
+
const match = KNOWLEDGE_MARKER_RE.exec(message);
|
|
65
|
+
if (!match)
|
|
66
|
+
return null;
|
|
67
|
+
const tag = normalizeKnowledgeTag(match[1]);
|
|
68
|
+
return tag.length > 0 ? tag : null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The message with its leading marker removed — the marker is retrieval syntax,
|
|
72
|
+
* not part of the question, and leaving `#tag` in the text it is embedded from
|
|
73
|
+
* would nudge the query vector toward the tag token instead of the ask.
|
|
74
|
+
*/
|
|
75
|
+
export function stripKnowledgeMarker(message) {
|
|
76
|
+
const match = KNOWLEDGE_MARKER_RE.exec(message);
|
|
77
|
+
if (!match)
|
|
78
|
+
return message;
|
|
79
|
+
return message.slice(match[0].length).trimStart();
|
|
80
|
+
}
|
|
81
|
+
/** Bounded Levenshtein distance — tags are short, so this is cheap and exact. */
|
|
82
|
+
function editDistance(a, b) {
|
|
83
|
+
if (a === b)
|
|
84
|
+
return 0;
|
|
85
|
+
let prev = new Array(b.length + 1);
|
|
86
|
+
let curr = new Array(b.length + 1);
|
|
87
|
+
for (let j = 0; j <= b.length; j += 1)
|
|
88
|
+
prev[j] = j;
|
|
89
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
90
|
+
curr[0] = i;
|
|
91
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
92
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
93
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
94
|
+
}
|
|
95
|
+
const swap = prev;
|
|
96
|
+
prev = curr;
|
|
97
|
+
curr = swap;
|
|
98
|
+
}
|
|
99
|
+
return prev[b.length];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* "Did you mean" candidates for an unknown tag, best first.
|
|
103
|
+
*
|
|
104
|
+
* A prefix relationship is the common typo ("spec" for "spec-v2") and is worth
|
|
105
|
+
* naming even when the edit distance is larger than the cap. Suggestion only —
|
|
106
|
+
* nothing here ever substitutes one tag for another.
|
|
107
|
+
*/
|
|
108
|
+
export function suggestKnowledgeTags(unknown, tags) {
|
|
109
|
+
const target = normalizeKnowledgeTag(unknown);
|
|
110
|
+
if (!target)
|
|
111
|
+
return [];
|
|
112
|
+
const maxDistance = target.length <= 4 ? 1 : 2;
|
|
113
|
+
return tags
|
|
114
|
+
.map((entry) => {
|
|
115
|
+
const candidate = entry.tag;
|
|
116
|
+
return {
|
|
117
|
+
tag: candidate,
|
|
118
|
+
distance: editDistance(target, candidate),
|
|
119
|
+
prefix: candidate.startsWith(target) || target.startsWith(candidate),
|
|
120
|
+
};
|
|
121
|
+
})
|
|
122
|
+
.filter((c) => c.prefix || c.distance <= maxDistance)
|
|
123
|
+
.sort((a, b) => (a.prefix === b.prefix ? a.distance - b.distance : a.prefix ? -1 : 1))
|
|
124
|
+
.slice(0, MAX_TAG_SUGGESTIONS)
|
|
125
|
+
.map((c) => c.tag);
|
|
126
|
+
}
|
|
127
|
+
/** Honest, explicit truncation of an over-long block. */
|
|
128
|
+
function capBlock(block) {
|
|
129
|
+
if (block.length <= MAX_KNOWLEDGE_BLOCK_CHARS)
|
|
130
|
+
return block;
|
|
131
|
+
return `${block.slice(0, MAX_KNOWLEDGE_BLOCK_CHARS)}\n[... knowledge context truncated at ${MAX_KNOWLEDGE_BLOCK_CHARS} characters ...]`;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build the knowledge block for a turn. Never throws, never blocks a turn on a
|
|
135
|
+
* failure, and returns an empty block unless the message genuinely asked for a
|
|
136
|
+
* tag the user has.
|
|
137
|
+
*/
|
|
138
|
+
export async function buildKnowledgeTurnContext(message, opts = {}) {
|
|
139
|
+
try {
|
|
140
|
+
const marker = parseKnowledgeMarker(message);
|
|
141
|
+
if (!marker)
|
|
142
|
+
return { block: '', outcome: 'none' };
|
|
143
|
+
const tags = listKnowledgeTags();
|
|
144
|
+
// No tags at all: the feature is inert. Without this, a stray leading
|
|
145
|
+
// `#word` (a pasted `#include`, a `#fff` colour) would produce a note about
|
|
146
|
+
// a corpus that does not exist.
|
|
147
|
+
if (tags.length === 0)
|
|
148
|
+
return { block: '', outcome: 'none' };
|
|
149
|
+
const match = tags.find((entry) => entry.tag === marker);
|
|
150
|
+
if (!match) {
|
|
151
|
+
const suggestions = suggestKnowledgeTags(marker, tags);
|
|
152
|
+
const hint = suggestions.length > 0
|
|
153
|
+
? ` Did you mean ${suggestions.map((s) => `#${s}`).join(' or ')}?`
|
|
154
|
+
: ` Known tags: ${tags
|
|
155
|
+
.slice(0, MAX_TAG_SUGGESTIONS * 2)
|
|
156
|
+
.map((t) => `#${t.tag}`)
|
|
157
|
+
.join(', ')}.`;
|
|
158
|
+
return {
|
|
159
|
+
block: `[Knowledge] No knowledge tag '#${marker}'.${hint} ` +
|
|
160
|
+
'Answer the question from your own knowledge — do not guess at documents the user has not tagged.',
|
|
161
|
+
outcome: 'unknown-tag',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const question = stripKnowledgeMarker(message) || message;
|
|
165
|
+
const hits = await queryKnowledge(match.tag, question, opts);
|
|
166
|
+
if (hits.length === 0) {
|
|
167
|
+
// The relevance floor dropped everything. This is the honest case the
|
|
168
|
+
// store could not previously express, and it must NOT be softened into a
|
|
169
|
+
// general answer read off the tagged documents.
|
|
170
|
+
return {
|
|
171
|
+
block: `[Knowledge] Tag '#${match.tag}' has ${match.chunkCount} indexed chunk(s), but none matched this ` +
|
|
172
|
+
'question closely enough to be used. Answer from your own knowledge and say the tagged documents did not cover it.',
|
|
173
|
+
outcome: 'empty',
|
|
174
|
+
tag: match.tag,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
block: capBlock(`[Knowledge] ${formatKnowledgeContext(match.tag, hits)}`),
|
|
179
|
+
outcome: 'matched',
|
|
180
|
+
tag: match.tag,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
// Best-effort, exactly like the ambient project context: a retrieval failure
|
|
185
|
+
// must never break or delay the turn's actual answer.
|
|
186
|
+
logger.debug(`Knowledge turn context failed: ${err instanceof Error ? err.message : err}`);
|
|
187
|
+
return { block: '', outcome: 'none' };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=knowledge-turn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-turn.js","sourceRoot":"","sources":["../../src/learning/knowledge-turn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learned commands — a per-OS command the MODEL worked out, kept so it is known
|
|
3
|
+
* next time.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS. The per-OS command map (Bundle 47) declares the command for
|
|
6
|
+
* the two verbs where the OS determines it; machine facts (Bundle 50) say what is
|
|
7
|
+
* installed. Neither can enumerate the long tail — every tool, every OS, every
|
|
8
|
+
* version — and trying to would be a table that is wrong the day it ships. So the
|
|
9
|
+
* table GROWS BY USE instead: when the model works out how to do a verb on this
|
|
10
|
+
* OS (and, in the verify loop, it works), the command is recorded here, keyed by
|
|
11
|
+
* `(verb, os)`, and surfaced to the next search and the next resolve.
|
|
12
|
+
*
|
|
13
|
+
* This is the "model-derived, harness-remembered" half of the loop. The MODEL
|
|
14
|
+
* decides the command (it is the general intelligence); the harness persists it
|
|
15
|
+
* and hands it back, so the same discovery is not paid for twice.
|
|
16
|
+
*
|
|
17
|
+
* ISOLATION. The store resolves through `resolveNuviraDataPath`, so
|
|
18
|
+
* `$NUVIRA_CONFIG_DIR` isolates it — an isolated (test/CI/sandbox) process never
|
|
19
|
+
* reads or writes the developer's real store (the bug `resolveNuviraHome` invites).
|
|
20
|
+
*
|
|
21
|
+
* BEST-EFFORT AND BOUNDED. Every read/write is wrapped; a corrupt or missing file
|
|
22
|
+
* is simply an empty store, and the file is capped so a runaway cannot grow
|
|
23
|
+
* forever (newest wins). A command never TRUSTS a learned entry blindly — the
|
|
24
|
+
* caller still checks it against the machine — so a stale entry is a hint, not a
|
|
25
|
+
* fact.
|
|
26
|
+
*/
|
|
27
|
+
import { type NormalizedOs } from './machine-facts.js';
|
|
28
|
+
/** One learned command: how to do `verb` on `os`, as the model derived it. */
|
|
29
|
+
export interface LearnedCommand {
|
|
30
|
+
/** The verb it accomplishes, normalized (lowercased) — e.g. "install java". */
|
|
31
|
+
verb: string;
|
|
32
|
+
/** The OS it was derived for. */
|
|
33
|
+
os: NormalizedOs;
|
|
34
|
+
/** The command, exactly as it should run. */
|
|
35
|
+
command: string;
|
|
36
|
+
/** The executable the command starts with, when it has one. */
|
|
37
|
+
binary?: string;
|
|
38
|
+
/** Optional note (why, a caveat). */
|
|
39
|
+
note?: string;
|
|
40
|
+
/** Where it came from: the model's resolution, or an observed successful run. */
|
|
41
|
+
source: 'model' | 'observed';
|
|
42
|
+
/** When it was recorded (ms epoch). */
|
|
43
|
+
learnedAt: number;
|
|
44
|
+
}
|
|
45
|
+
/** Read the store. A missing/corrupt file is an empty store, never a throw. */
|
|
46
|
+
export declare function listLearnedCommands(): LearnedCommand[];
|
|
47
|
+
/** The learned command for `verb` on `os` (default: this machine's OS), or null. */
|
|
48
|
+
export declare function learnedCommandFor(verb: string, os?: NormalizedOs): LearnedCommand | null;
|
|
49
|
+
/**
|
|
50
|
+
* Record (or replace) the command for `(verb, os)`. Returns the stored entry, or
|
|
51
|
+
* null when the input is unusable (no verb or no command). Best-effort: a failed
|
|
52
|
+
* write returns the entry without persisting, never a throw.
|
|
53
|
+
*/
|
|
54
|
+
export declare function recordLearnedCommand(input: {
|
|
55
|
+
verb: string;
|
|
56
|
+
command: string;
|
|
57
|
+
binary?: string;
|
|
58
|
+
note?: string;
|
|
59
|
+
source?: 'model' | 'observed';
|
|
60
|
+
os?: NormalizedOs;
|
|
61
|
+
}): LearnedCommand | null;
|
|
62
|
+
/** Forget the command for `verb` on `os` (default: this machine). True if removed. */
|
|
63
|
+
export declare function forgetLearnedCommand(verb: string, os?: NormalizedOs): boolean;
|
|
64
|
+
//# sourceMappingURL=learned-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learned-commands.d.ts","sourceRoot":"","sources":["../../src/learning/learned-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE3E,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,EAAE,EAAE,YAAY,CAAC;IACjB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,MAAM,EAAE,OAAO,GAAG,UAAU,CAAC;IAC7B,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAmBD,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,cAAc,EAAE,CAQtD;AAgBD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,EAAE,GAAE,YAAsC,GACzC,cAAc,GAAG,IAAI,CAIvB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9B,EAAE,CAAC,EAAE,YAAY,CAAC;CACnB,GAAG,cAAc,GAAG,IAAI,CAiBxB;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,YAAY,GAAG,OAAO,CAQ7E"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learned commands — a per-OS command the MODEL worked out, kept so it is known
|
|
3
|
+
* next time.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS. The per-OS command map (Bundle 47) declares the command for
|
|
6
|
+
* the two verbs where the OS determines it; machine facts (Bundle 50) say what is
|
|
7
|
+
* installed. Neither can enumerate the long tail — every tool, every OS, every
|
|
8
|
+
* version — and trying to would be a table that is wrong the day it ships. So the
|
|
9
|
+
* table GROWS BY USE instead: when the model works out how to do a verb on this
|
|
10
|
+
* OS (and, in the verify loop, it works), the command is recorded here, keyed by
|
|
11
|
+
* `(verb, os)`, and surfaced to the next search and the next resolve.
|
|
12
|
+
*
|
|
13
|
+
* This is the "model-derived, harness-remembered" half of the loop. The MODEL
|
|
14
|
+
* decides the command (it is the general intelligence); the harness persists it
|
|
15
|
+
* and hands it back, so the same discovery is not paid for twice.
|
|
16
|
+
*
|
|
17
|
+
* ISOLATION. The store resolves through `resolveNuviraDataPath`, so
|
|
18
|
+
* `$NUVIRA_CONFIG_DIR` isolates it — an isolated (test/CI/sandbox) process never
|
|
19
|
+
* reads or writes the developer's real store (the bug `resolveNuviraHome` invites).
|
|
20
|
+
*
|
|
21
|
+
* BEST-EFFORT AND BOUNDED. Every read/write is wrapped; a corrupt or missing file
|
|
22
|
+
* is simply an empty store, and the file is capped so a runaway cannot grow
|
|
23
|
+
* forever (newest wins). A command never TRUSTS a learned entry blindly — the
|
|
24
|
+
* caller still checks it against the machine — so a stale entry is a hint, not a
|
|
25
|
+
* fact.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
28
|
+
import { dirname } from 'node:path';
|
|
29
|
+
import { resolveNuviraDataPath } from '../config/paths.js';
|
|
30
|
+
import { detectMachineFacts } from './machine-facts.js';
|
|
31
|
+
const FILE_NAME = 'learned-commands.json';
|
|
32
|
+
/** Cap the store: newest wins, so a runaway cannot grow without bound. */
|
|
33
|
+
const MAX_COMMANDS = 500;
|
|
34
|
+
function storePath() {
|
|
35
|
+
return resolveNuviraDataPath(FILE_NAME);
|
|
36
|
+
}
|
|
37
|
+
function isValid(c) {
|
|
38
|
+
const e = c;
|
|
39
|
+
return !!e && typeof e.verb === 'string' && typeof e.os === 'string' && typeof e.command === 'string';
|
|
40
|
+
}
|
|
41
|
+
/** Read the store. A missing/corrupt file is an empty store, never a throw. */
|
|
42
|
+
export function listLearnedCommands() {
|
|
43
|
+
try {
|
|
44
|
+
if (!existsSync(storePath()))
|
|
45
|
+
return [];
|
|
46
|
+
const parsed = JSON.parse(readFileSync(storePath(), 'utf-8'));
|
|
47
|
+
return Array.isArray(parsed.commands) ? parsed.commands.filter(isValid) : [];
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function writeStore(commands) {
|
|
54
|
+
try {
|
|
55
|
+
const path = storePath();
|
|
56
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
57
|
+
writeFileSync(path, JSON.stringify({ commands }, null, 2), 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Best-effort — a failed write must never break the turn.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function normalizeVerb(verb) {
|
|
64
|
+
return String(verb ?? '').trim().toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
/** The learned command for `verb` on `os` (default: this machine's OS), or null. */
|
|
67
|
+
export function learnedCommandFor(verb, os = detectMachineFacts().os) {
|
|
68
|
+
const v = normalizeVerb(verb);
|
|
69
|
+
if (!v)
|
|
70
|
+
return null;
|
|
71
|
+
return listLearnedCommands().find((c) => c.verb === v && c.os === os) ?? null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Record (or replace) the command for `(verb, os)`. Returns the stored entry, or
|
|
75
|
+
* null when the input is unusable (no verb or no command). Best-effort: a failed
|
|
76
|
+
* write returns the entry without persisting, never a throw.
|
|
77
|
+
*/
|
|
78
|
+
export function recordLearnedCommand(input) {
|
|
79
|
+
const verb = normalizeVerb(input.verb);
|
|
80
|
+
const command = String(input.command ?? '').trim();
|
|
81
|
+
if (!verb || !command)
|
|
82
|
+
return null;
|
|
83
|
+
const os = input.os ?? detectMachineFacts().os;
|
|
84
|
+
const entry = {
|
|
85
|
+
verb,
|
|
86
|
+
os,
|
|
87
|
+
command,
|
|
88
|
+
...(input.binary ? { binary: input.binary } : {}),
|
|
89
|
+
...(input.note ? { note: input.note } : {}),
|
|
90
|
+
source: input.source ?? 'model',
|
|
91
|
+
learnedAt: Date.now(),
|
|
92
|
+
};
|
|
93
|
+
const rest = listLearnedCommands().filter((c) => !(c.verb === verb && c.os === os));
|
|
94
|
+
writeStore([entry, ...rest].slice(0, MAX_COMMANDS));
|
|
95
|
+
return entry;
|
|
96
|
+
}
|
|
97
|
+
/** Forget the command for `verb` on `os` (default: this machine). True if removed. */
|
|
98
|
+
export function forgetLearnedCommand(verb, os) {
|
|
99
|
+
const v = normalizeVerb(verb);
|
|
100
|
+
const target = os ?? detectMachineFacts().os;
|
|
101
|
+
const before = listLearnedCommands();
|
|
102
|
+
const after = before.filter((c) => !(c.verb === v && c.os === target));
|
|
103
|
+
if (after.length === before.length)
|
|
104
|
+
return false;
|
|
105
|
+
writeStore(after);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=learned-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learned-commands.js","sourceRoot":"","sources":["../../src/learning/learned-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;AAwB3E,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAC1C,0EAA0E;AAC1E,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB,SAAS,SAAS;IAChB,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,OAAO,CAAC,CAAU;IACzB,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC;AACxG,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,mBAAmB;IACjC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAAE,OAAO,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC,CAAmB,CAAC;QAChF,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,QAA0B;IAC5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;QACzB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAkB,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACjD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,KAAmB,kBAAkB,EAAE,CAAC,EAAE;IAE1C,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,OAAO,mBAAmB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAOpC;IACC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnD,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,kBAAkB,EAAE,CAAC,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAmB;QAC5B,IAAI;QACJ,EAAE;QACF,OAAO;QACP,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,OAAO;QAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACpF,UAAU,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,EAAiB;IAClE,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAAE,CAAC,EAAE,CAAC;IAC7C,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACjD,UAAU,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexical search (`src/learning/lexical-search.ts`) — BM25 over a candidate set.
|
|
3
|
+
*
|
|
4
|
+
* WHY. Embeddings answer "what is this about"; they are unreliable at "which
|
|
5
|
+
* passage contains `ERR_TOKEN_FAMILY_REVOKED`". A user's own documents are full
|
|
6
|
+
* of exactly that — identifiers, clause numbers, dates, error codes, product
|
|
7
|
+
* names — and a question built from those words fails a dense retriever and is
|
|
8
|
+
* trivial for a lexical one. That is why retrieval runs both and fuses them
|
|
9
|
+
* rather than choosing: the two fail on different questions.
|
|
10
|
+
*
|
|
11
|
+
* WHY THERE IS NO PERSISTED INVERTED INDEX. The index is built from the
|
|
12
|
+
* namespace's stored chunks at query time. That sounds wasteful, and it is the
|
|
13
|
+
* deliberate trade: the chunks are ALREADY being scanned to compute cosine
|
|
14
|
+
* against every vector (the JSON backend is an exact linear scan, and the
|
|
15
|
+
* candidate set here is a user's document folder, not a web crawl), so a second
|
|
16
|
+
* on-disk structure would buy asymptotics nobody needs while adding a NEW
|
|
17
|
+
* consistency problem — an index that can drift out of step with the vectors
|
|
18
|
+
* every time a document is re-ingested, deleted or forgotten. A rebuild every
|
|
19
|
+
* query cannot drift. If a corpus ever grows past the point where this is the
|
|
20
|
+
* bottleneck, the cache is the place to add one, and the test suite will say so.
|
|
21
|
+
*
|
|
22
|
+
* Also here: the tokenizer, shared with the eval so that "what counts as a term"
|
|
23
|
+
* is one definition rather than two that can disagree.
|
|
24
|
+
*/
|
|
25
|
+
/** Function words carry no topic signal and inflate every score. */
|
|
26
|
+
export declare const STOPWORDS: Set<string>;
|
|
27
|
+
/** Lowercased content tokens (length ≥ 3, not a stopword), in order. */
|
|
28
|
+
export declare function contentTokens(text: string): string[];
|
|
29
|
+
/** FNV-1a — small, fast, deterministic, and dependency-free. */
|
|
30
|
+
export declare function fnv1a(input: string): number;
|
|
31
|
+
export interface LexicalDocument {
|
|
32
|
+
id: string;
|
|
33
|
+
text: string;
|
|
34
|
+
}
|
|
35
|
+
export interface LexicalHit {
|
|
36
|
+
id: string;
|
|
37
|
+
score: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Rank documents against a query with Okapi BM25.
|
|
41
|
+
*
|
|
42
|
+
* A document scores only on terms it actually contains, so a query sharing no
|
|
43
|
+
* vocabulary with any candidate returns nothing at all — which is what keeps an
|
|
44
|
+
* unrelated question out even when the dense half is floored.
|
|
45
|
+
*
|
|
46
|
+
* The ordering is fully deterministic: equal scores fall back to the document id,
|
|
47
|
+
* so a fixture corpus scores identically on every run.
|
|
48
|
+
*/
|
|
49
|
+
export declare function bm25Search(documents: LexicalDocument[], query: string, k: number): LexicalHit[];
|
|
50
|
+
//# sourceMappingURL=lexical-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lexical-search.d.ts","sourceRoot":"","sources":["../../src/learning/lexical-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,oEAAoE;AACpE,eAAO,MAAM,SAAS,aAOpB,CAAC;AAEH,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAKpD;AAED,gEAAgE;AAChE,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO3C;AAMD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAiD/F"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexical search (`src/learning/lexical-search.ts`) — BM25 over a candidate set.
|
|
3
|
+
*
|
|
4
|
+
* WHY. Embeddings answer "what is this about"; they are unreliable at "which
|
|
5
|
+
* passage contains `ERR_TOKEN_FAMILY_REVOKED`". A user's own documents are full
|
|
6
|
+
* of exactly that — identifiers, clause numbers, dates, error codes, product
|
|
7
|
+
* names — and a question built from those words fails a dense retriever and is
|
|
8
|
+
* trivial for a lexical one. That is why retrieval runs both and fuses them
|
|
9
|
+
* rather than choosing: the two fail on different questions.
|
|
10
|
+
*
|
|
11
|
+
* WHY THERE IS NO PERSISTED INVERTED INDEX. The index is built from the
|
|
12
|
+
* namespace's stored chunks at query time. That sounds wasteful, and it is the
|
|
13
|
+
* deliberate trade: the chunks are ALREADY being scanned to compute cosine
|
|
14
|
+
* against every vector (the JSON backend is an exact linear scan, and the
|
|
15
|
+
* candidate set here is a user's document folder, not a web crawl), so a second
|
|
16
|
+
* on-disk structure would buy asymptotics nobody needs while adding a NEW
|
|
17
|
+
* consistency problem — an index that can drift out of step with the vectors
|
|
18
|
+
* every time a document is re-ingested, deleted or forgotten. A rebuild every
|
|
19
|
+
* query cannot drift. If a corpus ever grows past the point where this is the
|
|
20
|
+
* bottleneck, the cache is the place to add one, and the test suite will say so.
|
|
21
|
+
*
|
|
22
|
+
* Also here: the tokenizer, shared with the eval so that "what counts as a term"
|
|
23
|
+
* is one definition rather than two that can disagree.
|
|
24
|
+
*/
|
|
25
|
+
/** Function words carry no topic signal and inflate every score. */
|
|
26
|
+
export const STOPWORDS = new Set([
|
|
27
|
+
'a', 'an', 'and', 'are', 'as', 'at', 'be', 'but', 'by', 'can', 'do', 'does',
|
|
28
|
+
'for', 'from', 'has', 'have', 'how', 'i', 'if', 'in', 'is', 'it', 'its', 'me',
|
|
29
|
+
'my', 'no', 'not', 'of', 'on', 'or', 'our', 'should', 'so', 'than', 'that',
|
|
30
|
+
'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'to', 'up',
|
|
31
|
+
'us', 'was', 'we', 'what', 'when', 'where', 'which', 'who', 'why', 'will',
|
|
32
|
+
'with', 'you', 'your',
|
|
33
|
+
]);
|
|
34
|
+
/** Lowercased content tokens (length ≥ 3, not a stopword), in order. */
|
|
35
|
+
export function contentTokens(text) {
|
|
36
|
+
return text
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.split(/[^a-z0-9]+/)
|
|
39
|
+
.filter((token) => token.length >= 3 && !STOPWORDS.has(token));
|
|
40
|
+
}
|
|
41
|
+
/** FNV-1a — small, fast, deterministic, and dependency-free. */
|
|
42
|
+
export function fnv1a(input) {
|
|
43
|
+
let hash = 0x811c9dc5;
|
|
44
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
45
|
+
hash ^= input.charCodeAt(i);
|
|
46
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
47
|
+
}
|
|
48
|
+
return hash >>> 0;
|
|
49
|
+
}
|
|
50
|
+
/** BM25 term-frequency saturation and length-normalisation. */
|
|
51
|
+
const K1 = 1.2;
|
|
52
|
+
const B = 0.75;
|
|
53
|
+
/**
|
|
54
|
+
* Rank documents against a query with Okapi BM25.
|
|
55
|
+
*
|
|
56
|
+
* A document scores only on terms it actually contains, so a query sharing no
|
|
57
|
+
* vocabulary with any candidate returns nothing at all — which is what keeps an
|
|
58
|
+
* unrelated question out even when the dense half is floored.
|
|
59
|
+
*
|
|
60
|
+
* The ordering is fully deterministic: equal scores fall back to the document id,
|
|
61
|
+
* so a fixture corpus scores identically on every run.
|
|
62
|
+
*/
|
|
63
|
+
export function bm25Search(documents, query, k) {
|
|
64
|
+
const queryTerms = [...new Set(contentTokens(query))];
|
|
65
|
+
if (queryTerms.length === 0 || documents.length === 0 || k <= 0)
|
|
66
|
+
return [];
|
|
67
|
+
const documentLengths = new Map();
|
|
68
|
+
/** term → docId → term frequency. */
|
|
69
|
+
const postings = new Map();
|
|
70
|
+
let totalLength = 0;
|
|
71
|
+
for (const doc of documents) {
|
|
72
|
+
const tokens = contentTokens(doc.text);
|
|
73
|
+
documentLengths.set(doc.id, tokens.length);
|
|
74
|
+
totalLength += tokens.length;
|
|
75
|
+
const frequencies = new Map();
|
|
76
|
+
for (const token of tokens)
|
|
77
|
+
frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
78
|
+
for (const [term, frequency] of frequencies) {
|
|
79
|
+
let posting = postings.get(term);
|
|
80
|
+
if (!posting) {
|
|
81
|
+
posting = new Map();
|
|
82
|
+
postings.set(term, posting);
|
|
83
|
+
}
|
|
84
|
+
posting.set(doc.id, frequency);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const total = documents.length;
|
|
88
|
+
const averageLength = totalLength / total || 1;
|
|
89
|
+
const scores = new Map();
|
|
90
|
+
for (const term of queryTerms) {
|
|
91
|
+
const posting = postings.get(term);
|
|
92
|
+
if (!posting)
|
|
93
|
+
continue;
|
|
94
|
+
const documentFrequency = posting.size;
|
|
95
|
+
// The standard BM25 idf: a term every document carries is worth ~nothing, a
|
|
96
|
+
// term only one document carries is worth a lot.
|
|
97
|
+
const idf = Math.log(1 + (total - documentFrequency + 0.5) / (documentFrequency + 0.5));
|
|
98
|
+
for (const [docId, frequency] of posting) {
|
|
99
|
+
const length = documentLengths.get(docId) ?? 0;
|
|
100
|
+
const denominator = frequency + K1 * (1 - B + (B * length) / averageLength);
|
|
101
|
+
const contribution = idf * ((frequency * (K1 + 1)) / denominator);
|
|
102
|
+
scores.set(docId, (scores.get(docId) ?? 0) + contribution);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return [...scores.entries()]
|
|
106
|
+
.filter(([, score]) => score > 0)
|
|
107
|
+
.map(([id, score]) => ({ id, score }))
|
|
108
|
+
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
109
|
+
.slice(0, k);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=lexical-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lexical-search.js","sourceRoot":"","sources":["../../src/learning/lexical-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine facts — what THIS machine is, detected once per run.
|
|
3
|
+
*
|
|
4
|
+
* WHY. The agent reasons about "how do I do X here" constantly, and the honest
|
|
5
|
+
* inputs to that reasoning are facts about the host: which OS and release, which
|
|
6
|
+
* architecture, which shell, and — the one that actually decides the command —
|
|
7
|
+
* WHICH PACKAGE MANAGERS ARE INSTALLED. A model that assumes `apt` on a machine
|
|
8
|
+
* that only has `dnf`, or `brew` on Windows, produces a command that cannot run.
|
|
9
|
+
* The harness should hand it the facts once rather than let it guess per turn.
|
|
10
|
+
*
|
|
11
|
+
* THE ASYMMETRY WITH THE PER-OS COMMAND MAP (Bundle 47). That map declares the
|
|
12
|
+
* command for the two verbs where the OS DETERMINES it and a wrong guess is
|
|
13
|
+
* expensive. This module is the opposite side: it detects what is PRESENT on the
|
|
14
|
+
* host, from the same `which`/`where` resolver the shell uses (`binary-probe`).
|
|
15
|
+
* Presence is a fact, not a declaration, so it cannot drift — and it is the input
|
|
16
|
+
* that lets the model derive a command for anything the map does not cover,
|
|
17
|
+
* instead of the map having to enumerate the world.
|
|
18
|
+
*
|
|
19
|
+
* DETECTED ONCE. The whole result is memoized for the process (OS and installed
|
|
20
|
+
* tools cannot change mid-run), and `binary-probe` is memoized a second time per
|
|
21
|
+
* name, so the ~25 lookups are paid once. `resetMachineFactsCache()` exists for
|
|
22
|
+
* test isolation.
|
|
23
|
+
*
|
|
24
|
+
* INPUTS ARE INJECTABLE. A test can pass a platform and a fake `binary` probe;
|
|
25
|
+
* the real call uses `process.platform`, `process.env` and `binaryOnPath`.
|
|
26
|
+
*/
|
|
27
|
+
import type { Capability } from './capability-types.js';
|
|
28
|
+
/** The OS vocabulary shared with the per-OS command map. */
|
|
29
|
+
export type NormalizedOs = 'windows' | 'macos' | 'linux' | 'other';
|
|
30
|
+
export interface MachineFacts {
|
|
31
|
+
/** When the facts were detected (ms epoch). */
|
|
32
|
+
detectedAt: number;
|
|
33
|
+
/** Raw `process.platform` (win32 | darwin | linux | …). */
|
|
34
|
+
platform: NodeJS.Platform;
|
|
35
|
+
/** Normalized OS name. */
|
|
36
|
+
os: NormalizedOs;
|
|
37
|
+
/** Human label: "Windows 10.0.22631", "macOS (Darwin 23.5.0)", "Ubuntu 22.04". */
|
|
38
|
+
osName: string;
|
|
39
|
+
/** Linux distro id from /etc/os-release (e.g. "ubuntu"), else null. */
|
|
40
|
+
distro: string | null;
|
|
41
|
+
/** `process.arch` (x64 | arm64 | …). */
|
|
42
|
+
arch: string;
|
|
43
|
+
/** Full shell path, when the environment declares one. */
|
|
44
|
+
shell: string | null;
|
|
45
|
+
/** Shell's short name (zsh | bash | powershell | cmd). */
|
|
46
|
+
shellName: string | null;
|
|
47
|
+
/** Package managers/toolchain binaries actually present, in preference order. */
|
|
48
|
+
packageManagers: string[];
|
|
49
|
+
}
|
|
50
|
+
/** Options for a deterministic test (the real call passes none). */
|
|
51
|
+
export interface MachineFactsProbe {
|
|
52
|
+
platform?: NodeJS.Platform;
|
|
53
|
+
env?: NodeJS.ProcessEnv;
|
|
54
|
+
binary?: (name: string) => boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Clear the memo (test isolation). */
|
|
57
|
+
export declare function resetMachineFactsCache(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Detect this machine's facts. Memoized for the process when called with no
|
|
60
|
+
* overrides (the real path); an override call (a test) always computes fresh.
|
|
61
|
+
*/
|
|
62
|
+
export declare function detectMachineFacts(probe?: MachineFactsProbe): MachineFacts;
|
|
63
|
+
/** One bounded line the model reads — the machine, stated, not assumed. */
|
|
64
|
+
export declare function buildMachineFactsBlock(facts?: MachineFacts): string;
|
|
65
|
+
/**
|
|
66
|
+
* The machine facts as a discoverable capability, so the SAME facts the prompt
|
|
67
|
+
* carries are reachable through a capability search (and so readiness can reason
|
|
68
|
+
* about what is present). Read-only, no requirements, no grant.
|
|
69
|
+
*/
|
|
70
|
+
export declare function machineFactsCapability(facts?: MachineFacts): Capability;
|
|
71
|
+
//# sourceMappingURL=machine-facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-facts.d.ts","sourceRoot":"","sources":["../../src/learning/machine-facts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,0BAA0B;IAC1B,EAAE,EAAE,YAAY,CAAC;IACjB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iFAAiF;IACjF,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuBD,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACpC;AAID,uCAAuC;AACvC,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAqCD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,GAAE,iBAAsB,GAAG,YAAY,CAmC9E;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,YAAmC,GAAG,MAAM,CASzF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,GAAE,YAAmC,GAAG,UAAU,CAa7F"}
|