muonroi-cli 1.7.1 → 1.8.0
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/LICENSE +21 -21
- package/README.md +122 -122
- package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
- package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
- package/dist/packages/agent-harness-core/src/selector.js +2 -2
- package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
- package/dist/packages/agent-harness-opentui/src/install.js +1 -1
- package/dist/src/__test-stubs__/vitest-setup.js +4 -0
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
- package/dist/src/agent-harness/mock-model.d.ts +1 -1
- package/dist/src/agent-harness/mock-model.js +32 -3
- package/dist/src/agent-harness/test-spawn.js +3 -1
- package/dist/src/chat/chat-keychain.js +9 -1
- package/dist/src/cli/cost-forensics.js +12 -12
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +107 -28
- package/dist/src/cli/usage-report.js +2 -2
- package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
- package/dist/src/council/__tests__/abort-threading.test.js +193 -0
- package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
- package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
- package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
- package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
- package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
- package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
- package/dist/src/council/__tests__/research-tools.test.js +4 -0
- package/dist/src/council/__tests__/round-tools.test.js +6 -0
- package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
- package/dist/src/council/clarifier-question-cap.test.js +73 -0
- package/dist/src/council/clarifier.d.ts +29 -1
- package/dist/src/council/clarifier.js +64 -1
- package/dist/src/council/debate-planner.d.ts +2 -1
- package/dist/src/council/debate-planner.js +5 -3
- package/dist/src/council/debate.d.ts +9 -0
- package/dist/src/council/debate.js +88 -8
- package/dist/src/council/index.d.ts +46 -1
- package/dist/src/council/index.js +125 -19
- package/dist/src/council/llm.js +41 -13
- package/dist/src/council/prompts.js +56 -21
- package/dist/src/council/types.d.ts +14 -1
- package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
- package/dist/src/ee/bridge.d.ts +3 -1
- package/dist/src/ee/bridge.js +9 -3
- package/dist/src/ee/bridge.test.js +1 -2
- package/dist/src/ee/client.js +15 -2
- package/dist/src/ee/recall-ledger.d.ts +20 -0
- package/dist/src/ee/recall-ledger.js +40 -1
- package/dist/src/ee/search.d.ts +25 -0
- package/dist/src/ee/search.js +73 -0
- package/dist/src/ee/types.d.ts +15 -1
- package/dist/src/ee/who-am-i.d.ts +41 -0
- package/dist/src/ee/who-am-i.js +143 -0
- package/dist/src/ee/who-am-i.test.d.ts +1 -0
- package/dist/src/ee/who-am-i.test.js +101 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/council-answers.js +1 -0
- package/dist/src/headless/output.d.ts +30 -1
- package/dist/src/headless/output.js +180 -0
- package/dist/src/headless/output.test.js +61 -1
- package/dist/src/hooks/index.js +48 -2
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +143 -104
- package/dist/src/lsp/client-normalize.test.d.ts +12 -0
- package/dist/src/lsp/client-normalize.test.js +34 -0
- package/dist/src/lsp/client.d.ts +1 -0
- package/dist/src/lsp/client.js +10 -2
- package/dist/src/lsp/manager.d.ts +1 -1
- package/dist/src/lsp/manager.js +2 -2
- package/dist/src/lsp/manager.test.js +2 -2
- package/dist/src/lsp/runtime.d.ts +1 -1
- package/dist/src/lsp/runtime.js +2 -2
- package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
- package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
- package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
- package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
- package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
- package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
- package/dist/src/mcp/client-pool.d.ts +1 -1
- package/dist/src/mcp/client-pool.js +25 -9
- package/dist/src/mcp/ee-tools.d.ts +10 -0
- package/dist/src/mcp/ee-tools.js +34 -0
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/setup-guide-text.js +74 -74
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/ops/doctor.js +7 -7
- package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
- package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +2 -0
- package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
- package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
- package/dist/src/orchestrator/agent.test.js +5 -2
- package/dist/src/orchestrator/auto-commit.d.ts +110 -0
- package/dist/src/orchestrator/auto-commit.js +364 -0
- package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
- package/dist/src/orchestrator/batch-turn-runner.js +2 -1
- package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
- package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
- package/dist/src/orchestrator/compaction.d.ts +22 -0
- package/dist/src/orchestrator/compaction.js +42 -0
- package/dist/src/orchestrator/delegations.d.ts +1 -0
- package/dist/src/orchestrator/delegations.js +41 -0
- package/dist/src/orchestrator/error-utils.js +27 -2
- package/dist/src/orchestrator/message-processor.d.ts +21 -5
- package/dist/src/orchestrator/message-processor.js +219 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +8 -2
- package/dist/src/orchestrator/orchestrator.js +187 -34
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +145 -0
- package/dist/src/orchestrator/prompts.d.ts +8 -0
- package/dist/src/orchestrator/prompts.js +76 -60
- package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
- package/dist/src/orchestrator/safety-askcard.js +74 -0
- package/dist/src/orchestrator/sandbox.test.js +8 -5
- package/dist/src/orchestrator/scope-ceiling.js +1 -1
- package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
- package/dist/src/orchestrator/stall-watchdog.js +23 -0
- package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
- package/dist/src/orchestrator/stream-runner.js +52 -30
- package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
- package/dist/src/orchestrator/subagent-compactor.js +83 -10
- package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
- package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
- package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
- package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
- package/dist/src/orchestrator/tool-args-repair.js +1 -1
- package/dist/src/orchestrator/tool-engine.d.ts +151 -0
- package/dist/src/orchestrator/tool-engine.js +3079 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -0
- package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
- package/dist/src/pil/__tests__/discovery.test.js +89 -145
- package/dist/src/pil/__tests__/dual-run.test.js +2 -0
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
- package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
- package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
- package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
- package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
- package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
- package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
- package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
- package/dist/src/pil/__tests__/pipeline.test.js +11 -8
- package/dist/src/pil/__tests__/response-tools.test.js +52 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +5 -3
- package/dist/src/pil/agent-operating-contract.test.js +2 -2
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +1 -1
- package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
- package/dist/src/pil/cheap-model-workbooks.js +1 -1
- package/dist/src/pil/config.d.ts +20 -0
- package/dist/src/pil/config.js +35 -0
- package/dist/src/pil/discovery-types.d.ts +32 -9
- package/dist/src/pil/discovery.d.ts +3 -3
- package/dist/src/pil/discovery.js +138 -244
- package/dist/src/pil/index.d.ts +1 -1
- package/dist/src/pil/index.js +1 -1
- package/dist/src/pil/layer1-intent.d.ts +9 -1
- package/dist/src/pil/layer1-intent.js +99 -44
- package/dist/src/pil/layer1-intent.test.js +112 -2
- package/dist/src/pil/layer15-context-scan.js +1 -1
- package/dist/src/pil/layer16-clarity.d.ts +21 -25
- package/dist/src/pil/layer16-clarity.js +52 -88
- package/dist/src/pil/layer18-acceptance.d.ts +13 -3
- package/dist/src/pil/layer18-acceptance.js +1 -75
- package/dist/src/pil/layer2-personality.js +7 -8
- package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
- package/dist/src/pil/layer2_5-ponytail.js +41 -0
- package/dist/src/pil/layer3-ee-injection.js +180 -14
- package/dist/src/pil/layer4-gsd.js +22 -8
- package/dist/src/pil/layer5-context.d.ts +1 -0
- package/dist/src/pil/layer5-context.js +26 -11
- package/dist/src/pil/layer6-output.js +23 -5
- package/dist/src/pil/llm-classify.js +3 -3
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +4 -3
- package/dist/src/pil/pipeline.js +15 -1
- package/dist/src/pil/response-tools.d.ts +16 -0
- package/dist/src/pil/response-tools.js +41 -0
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +24 -3
- package/dist/src/pil/task-tier-map.js +7 -7
- package/dist/src/pil/types.d.ts +9 -3
- package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
- package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
- package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
- package/dist/src/product-loop/discovery-interview.d.ts +20 -0
- package/dist/src/product-loop/discovery-interview.js +121 -7
- package/dist/src/product-loop/done-gate.js +3 -3
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +20 -20
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- package/dist/src/product-loop/sprint-self-verify.js +2 -2
- package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
- package/dist/src/providers/auth/gcloud.d.ts +28 -0
- package/dist/src/providers/auth/gcloud.js +102 -0
- package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
- package/dist/src/providers/auth/gemini-oauth.js +292 -105
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
- package/dist/src/providers/auth/grok-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +28 -6
- package/dist/src/providers/gemini.d.ts +1 -1
- package/dist/src/providers/gemini.js +10 -3
- package/dist/src/providers/keychain.d.ts +2 -2
- package/dist/src/providers/keychain.js +20 -2
- package/dist/src/providers/mcp-vision-bridge.js +48 -48
- package/dist/src/providers/openai.js +5 -1
- package/dist/src/providers/runtime.d.ts +1 -1
- package/dist/src/providers/runtime.js +11 -2
- package/dist/src/providers/siliconflow-sse-repair.js +1 -1
- package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
- package/dist/src/providers/strategies/google.strategy.js +159 -9
- package/dist/src/providers/strategies/thinking-mode.js +19 -6
- package/dist/src/providers/types.d.ts +1 -1
- package/dist/src/reporter/index.js +1 -1
- package/dist/src/router/decide.test.js +40 -8
- package/dist/src/router/step-router.d.ts +20 -0
- package/dist/src/router/step-router.js +73 -0
- package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
- package/dist/src/self-qa/agentic-loop.js +19 -19
- package/dist/src/self-qa/orchestrator.js +5 -5
- package/dist/src/self-qa/scenario-planner.js +1 -1
- package/dist/src/self-qa/spec-emitter.js +4 -4
- package/dist/src/storage/__tests__/migrations.test.js +2 -2
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.d.ts +6 -0
- package/dist/src/storage/sessions.js +49 -42
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +1 -0
- package/dist/src/storage/transcript.js +55 -0
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/tools/__tests__/native-tools.test.js +6 -0
- package/dist/src/tools/bash.d.ts +36 -23
- package/dist/src/tools/bash.js +59 -301
- package/dist/src/tools/bash.test.js +2 -257
- package/dist/src/tools/file-tracker.d.ts +5 -0
- package/dist/src/tools/file-tracker.js +14 -0
- package/dist/src/tools/file.js +2 -0
- package/dist/src/tools/git-safety.d.ts +38 -1
- package/dist/src/tools/git-safety.js +94 -4
- package/dist/src/tools/git-safety.test.js +45 -1
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +56 -0
- package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
- package/dist/src/tools/registry-git-safety.test.js +17 -6
- package/dist/src/tools/registry.d.ts +5 -0
- package/dist/src/tools/registry.js +185 -11
- package/dist/src/types/index.d.ts +7 -1
- package/dist/src/ui/app.d.ts +2 -1
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.js +37 -3
- package/dist/src/ui/components/structured-response-view.js +7 -0
- package/dist/src/ui/components/task-list-panel.js +3 -12
- package/dist/src/ui/containers/chat-feed.d.ts +40 -0
- package/dist/src/ui/containers/chat-feed.js +66 -0
- package/dist/src/ui/containers/modals-layer.d.ts +87 -0
- package/dist/src/ui/containers/modals-layer.js +18 -0
- package/dist/src/ui/modals/session-picker-modal.js +14 -9
- package/dist/src/ui/modals/update-modal.js +2 -1
- package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/slash/export.js +16 -1
- package/dist/src/ui/slash/menu-items.js +1 -0
- package/dist/src/ui/slash/ponytail.d.ts +2 -0
- package/dist/src/ui/slash/ponytail.js +19 -0
- package/dist/src/ui/status-bar/store.js +0 -1
- package/dist/src/ui/status-bar/store.test.js +2 -2
- package/dist/src/ui/types.d.ts +7 -0
- package/dist/src/ui/use-app-logic.d.ts +26 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/relaunch.d.ts +17 -0
- package/dist/src/ui/utils/relaunch.js +28 -4
- package/dist/src/ui/utils/relaunch.test.js +50 -0
- package/dist/src/ui/utils/text.d.ts +11 -0
- package/dist/src/ui/utils/text.js +21 -1
- package/dist/src/ui/utils/text.test.js +25 -1
- package/dist/src/ui/utils/tools.js +3 -1
- package/dist/src/usage/cost-log.js +2 -2
- package/dist/src/usage/estimator.d.ts +14 -0
- package/dist/src/usage/estimator.js +22 -0
- package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
- package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- package/dist/src/utils/permission-mode.d.ts +33 -0
- package/dist/src/utils/permission-mode.js +164 -3
- package/dist/src/utils/permission-mode.test.js +92 -1
- package/dist/src/utils/settings.d.ts +27 -31
- package/dist/src/utils/settings.js +100 -109
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/package.json +1 -1
|
@@ -28,6 +28,11 @@ export function buildClarificationPrompt(topic, conversationContext, previousQA)
|
|
|
28
28
|
`the project IS the one described in the "## Current Project" section of the context. DO NOT ask which project.\n` +
|
|
29
29
|
`- Only ask about project identity when the topic mentions multiple distinct projects or external products.\n` +
|
|
30
30
|
`- Use the project's package.json name and description as implicit context for follow-up questions.\n\n` +
|
|
31
|
+
`## Language Rule (mandatory)\n` +
|
|
32
|
+
`Write the "question", "why", and every "suggestions"/"recommended" option in the SAME ` +
|
|
33
|
+
`language the user used in the Topic below — detect it; if the user wrote Vietnamese, write ` +
|
|
34
|
+
`Vietnamese; if English, English. The user reads and answers these on a card, so they must ` +
|
|
35
|
+
`NOT default to English. Keep code identifiers, file paths, tech/product names, and JSON keys in English.\n\n` +
|
|
31
36
|
`Output ONLY a JSON array (no markdown, no preamble):\n` +
|
|
32
37
|
`[{"question": "...", "why": "why this matters for a focused discussion", "suggestions": ["option A", "option B"], "recommended": "option A", "isRequired": true}]\n\n` +
|
|
33
38
|
`Rules for "recommended" (be decisive — the user should never face an unranked list):\n` +
|
|
@@ -49,7 +54,15 @@ export function buildSpecSynthesisPrompt(topic, conversationContext, qa) {
|
|
|
49
54
|
` "constraints": ["constraint 1", "constraint 2"],\n` +
|
|
50
55
|
` "successCriteria": ["criterion 1", "criterion 2"],\n` +
|
|
51
56
|
` "scope": "what is in and out of scope"\n` +
|
|
52
|
-
`}
|
|
57
|
+
`}\n\n` +
|
|
58
|
+
`## Language Rule (mandatory)\n` +
|
|
59
|
+
`Write EVERY field (problemStatement, constraints, successCriteria, scope) in the SAME ` +
|
|
60
|
+
`language the user used — detect it from the Original Topic and Clarification Q&A below. ` +
|
|
61
|
+
`If the user wrote Vietnamese, write Vietnamese; if English, English. Do NOT default to ` +
|
|
62
|
+
`English. This brief is shown to the user on the approval card AND the final synthesis ` +
|
|
63
|
+
`detects its output language from this problemStatement, so writing it in the wrong ` +
|
|
64
|
+
`language drags the whole council's output off the user's language. Keep code identifiers, ` +
|
|
65
|
+
`tech/product names, and JSON keys in English.`,
|
|
53
66
|
prompt: `## Original Topic\n${topic}\n\n` +
|
|
54
67
|
(conversationContext ? `## Context\n${conversationContext}\n\n` : "") +
|
|
55
68
|
`## Clarification Q&A\n${qa.map((qa) => `Q: ${qa.question}\nA: ${qa.answer}`).join("\n\n")}`,
|
|
@@ -146,6 +159,25 @@ const EVIDENCE_RULE_FOLLOWUP = `\n## Evidence Rule\n` +
|
|
|
146
159
|
`- Most follow-up turns will not need any tool call.\n` +
|
|
147
160
|
`Tag verified results: \`[CONFIRMED via <tool>:<evidence>]\` or \`[REFUTED via <tool>:<evidence>]\`.\n` +
|
|
148
161
|
`Uncited numbers you cannot verify: \`[UNVERIFIED: <claim>]\`.\n`;
|
|
162
|
+
/**
|
|
163
|
+
* Length discipline injected into every debate turn (F13 token-thrift). The
|
|
164
|
+
* debate-turn ceilings are generous (4096-6144 maxTokens) and reasoning models
|
|
165
|
+
* left unbounded emit ~400-600 words/turn — ~17 turns/debate is token-heavy and
|
|
166
|
+
* directly fights the "tiết kiệm token" goal. A concrete word cap + "density
|
|
167
|
+
* over length" cuts filler / preamble / brief-restating while keeping the
|
|
168
|
+
* analytical core (position, reasoning bullets, the cross-question). This does
|
|
169
|
+
* NOT lower maxTokens: reasoning models need that headroom to think without
|
|
170
|
+
* empty-turn truncation (see llm.ts) — the saving comes from the model writing
|
|
171
|
+
* less, not from a hard output ceiling. The text deliberately contains none of
|
|
172
|
+
* the prompt substrings the council mocks route on (e.g. "responding to",
|
|
173
|
+
* "continuing a discussion", "team lead", "Summarize this discussion").
|
|
174
|
+
*/
|
|
175
|
+
function concisenessRule(maxWords) {
|
|
176
|
+
return (`\n## Length (be token-thrifty)\n` +
|
|
177
|
+
`Keep this turn under ~${maxWords} words. Open with your position in one line, ` +
|
|
178
|
+
`then 2-4 bullets of your strongest reasoning. No preamble, no restating the ` +
|
|
179
|
+
`brief, no filler — density over length. A tight argument beats a long one.\n`);
|
|
180
|
+
}
|
|
149
181
|
/** Resolve the persona label used inside debate prompts. Stance wins; role is fallback. */
|
|
150
182
|
function personaOf(role, stance) {
|
|
151
183
|
if (stance) {
|
|
@@ -171,6 +203,7 @@ export function buildOpeningPrompt(ctx) {
|
|
|
171
203
|
focusLine +
|
|
172
204
|
ENGLISH_ONLY_RULE +
|
|
173
205
|
EVIDENCE_RULE_OPENING +
|
|
206
|
+
concisenessRule(220) +
|
|
174
207
|
(stackLock ? `\n${stackLock}\n` : "") +
|
|
175
208
|
guardrails +
|
|
176
209
|
(ctx.conversationContext ? `\n## Conversation Context\n${ctx.conversationContext}\n\n---\n\n` : "\n") +
|
|
@@ -192,6 +225,7 @@ export function buildResponsePrompt(ctx) {
|
|
|
192
225
|
system: `You are the "${me.label}" (lens: ${me.lens}) responding to the "${them.label}" (lens: ${them.lens}).\n` +
|
|
193
226
|
ENGLISH_ONLY_RULE +
|
|
194
227
|
EVIDENCE_RULE_RESPONSE +
|
|
228
|
+
concisenessRule(160) +
|
|
195
229
|
(stackLock ? `\n${stackLock}\n` : "") +
|
|
196
230
|
`\n## Discussion Brief\n` +
|
|
197
231
|
`Problem: ${ctx.spec.problemStatement}\n` +
|
|
@@ -216,6 +250,7 @@ export function buildFollowupPrompt(ctx) {
|
|
|
216
250
|
system: `You are the "${me.label}" (lens: ${me.lens}) continuing a discussion (round ${ctx.round}) with the "${them.label}" (lens: ${them.lens}).\n` +
|
|
217
251
|
ENGLISH_ONLY_RULE +
|
|
218
252
|
EVIDENCE_RULE_FOLLOWUP +
|
|
253
|
+
concisenessRule(140) +
|
|
219
254
|
(stackLock ? `\n${stackLock}\n` : "") +
|
|
220
255
|
`\n` +
|
|
221
256
|
(ctx.runningSummary
|
|
@@ -513,33 +548,33 @@ export function buildSynthesisPrompt(ctx) {
|
|
|
513
548
|
}
|
|
514
549
|
let extraContext = "";
|
|
515
550
|
if (ctx.refineContext) {
|
|
516
|
-
extraContext += `
|
|
517
|
-
## User Refinements
|
|
518
|
-
${ctx.refineContext}
|
|
551
|
+
extraContext += `
|
|
552
|
+
## User Refinements
|
|
553
|
+
${ctx.refineContext}
|
|
519
554
|
`;
|
|
520
555
|
}
|
|
521
556
|
if (ctx.planEmphasis) {
|
|
522
|
-
extraContext += `
|
|
523
|
-
## Additional Instruction
|
|
524
|
-
The user has requested a concrete action plan with executable steps. Each action item MUST be an object with these fields:
|
|
525
|
-
{
|
|
526
|
-
"step": "<imperative action>",
|
|
527
|
-
"owner_lens": "<which stance owns this — frontend / backend / architecture / etc>",
|
|
528
|
-
"time_estimate": "<rough — e.g. '2h', '1d', '~30min'>",
|
|
529
|
-
"depends_on": ["<step keys this requires>"] or [],
|
|
530
|
-
"acceptance_criteria": "<how we know it's done>"
|
|
531
|
-
}
|
|
532
|
-
Order action items by dependency: predecessors first, dependents after.
|
|
533
|
-
Risks MUST be objects with: {"description", "severity": "High|Medium|Low", "mitigation"}.
|
|
534
|
-
Do NOT emit loose strings for these fields — the user needs structured plan output.
|
|
557
|
+
extraContext += `
|
|
558
|
+
## Additional Instruction
|
|
559
|
+
The user has requested a concrete action plan with executable steps. Each action item MUST be an object with these fields:
|
|
560
|
+
{
|
|
561
|
+
"step": "<imperative action>",
|
|
562
|
+
"owner_lens": "<which stance owns this — frontend / backend / architecture / etc>",
|
|
563
|
+
"time_estimate": "<rough — e.g. '2h', '1d', '~30min'>",
|
|
564
|
+
"depends_on": ["<step keys this requires>"] or [],
|
|
565
|
+
"acceptance_criteria": "<how we know it's done>"
|
|
566
|
+
}
|
|
567
|
+
Order action items by dependency: predecessors first, dependents after.
|
|
568
|
+
Risks MUST be objects with: {"description", "severity": "High|Medium|Low", "mitigation"}.
|
|
569
|
+
Do NOT emit loose strings for these fields — the user needs structured plan output.
|
|
535
570
|
`;
|
|
536
571
|
}
|
|
537
572
|
return {
|
|
538
573
|
system,
|
|
539
|
-
prompt: `Final positions:
|
|
540
|
-
${ctx.finalPositions}
|
|
541
|
-
|
|
542
|
-
Full discussion:
|
|
574
|
+
prompt: `Final positions:
|
|
575
|
+
${ctx.finalPositions}
|
|
576
|
+
|
|
577
|
+
Full discussion:
|
|
543
578
|
${ctx.allExchanges}${extraContext}`,
|
|
544
579
|
};
|
|
545
580
|
}
|
|
@@ -211,6 +211,13 @@ export interface CouncilConfig {
|
|
|
211
211
|
internetFirst?: boolean;
|
|
212
212
|
/** When true, leader sub-tasks downshift to cheaper tier models on the same provider. */
|
|
213
213
|
costAware?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Enclosing council run id (= sessionId). Used only as the correlationId on the
|
|
216
|
+
* observe-only `council-turn-length` harness event so per-turn length samples can
|
|
217
|
+
* be grouped by run. Optional: direct callers/tests may omit it (falls back to a
|
|
218
|
+
* stable literal); has no effect on debate behaviour.
|
|
219
|
+
*/
|
|
220
|
+
runId?: string;
|
|
214
221
|
}
|
|
215
222
|
/**
|
|
216
223
|
* JSON shape persisted as `[Council Memory] {...}` in the session messages.
|
|
@@ -270,7 +277,13 @@ export interface CouncilCallUsage {
|
|
|
270
277
|
}
|
|
271
278
|
export type UsageCallback = (usage: CouncilCallUsage) => void;
|
|
272
279
|
export interface CouncilLLM {
|
|
273
|
-
generate(modelId: string, system: string, prompt: string, maxTokens?: number, onUsage?: UsageCallback
|
|
280
|
+
generate(modelId: string, system: string, prompt: string, maxTokens?: number, onUsage?: UsageCallback,
|
|
281
|
+
/**
|
|
282
|
+
* User-abort signal. Threaded so a mid-council cancel (Esc/Ctrl-C) aborts
|
|
283
|
+
* the in-flight generate call. Trailing+optional for back-compat — existing
|
|
284
|
+
* positional callers/mocks are unaffected. See withCouncilSignal in index.ts.
|
|
285
|
+
*/
|
|
286
|
+
signal?: AbortSignal): Promise<string>;
|
|
274
287
|
research(modelId: string, topic: string, conversationContext: string, signal?: AbortSignal, persistTrace?: ToolTraceEmitter, options?: {
|
|
275
288
|
internetFirst?: boolean;
|
|
276
289
|
}, onUsage?: UsageCallback): Promise<string>;
|
|
@@ -49,7 +49,7 @@ function bootstrapDb(sessions) {
|
|
|
49
49
|
db.close();
|
|
50
50
|
}
|
|
51
51
|
beforeEach(() => {
|
|
52
|
-
tmpHome = join(tmpdir(),
|
|
52
|
+
tmpHome = join(tmpdir(), `muonroi-test-export-${Date.now()}`);
|
|
53
53
|
const muonroiDir = join(tmpHome, ".muonroi-cli");
|
|
54
54
|
const experienceDir = join(tmpHome, ".experience");
|
|
55
55
|
mkdirSync(muonroiDir, { recursive: true });
|
package/dist/src/ee/bridge.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type PilContextResponse } from "../pil/schema.js";
|
|
2
|
+
export type { WhoAmIDim, WhoAmIDimName, WhoAmIProfile } from "./who-am-i.js";
|
|
3
|
+
export { getWhoAmIProfile, outputStyleFromProfile, resetWhoAmICache } from "./who-am-i.js";
|
|
2
4
|
interface EEPoint {
|
|
3
5
|
id: string | number;
|
|
4
6
|
score?: number;
|
|
@@ -37,7 +39,7 @@ export type { EEPoint, EERouteResult, EERouteTaskResult };
|
|
|
37
39
|
* 2. Fat clients (local experience-core.js present) → in-process call.
|
|
38
40
|
* 3. Otherwise → null (caller falls back to regex/keyword paths).
|
|
39
41
|
*/
|
|
40
|
-
export declare function classifyViaBrain(prompt: string, timeoutMs?: number): Promise<string | null>;
|
|
42
|
+
export declare function classifyViaBrain(prompt: string, timeoutMs?: number, options?: import("./types.js").BrainProxyOptions): Promise<string | null>;
|
|
41
43
|
/**
|
|
42
44
|
* Search a named Qdrant collection by embedding vector.
|
|
43
45
|
* Falls back to FileStore inside experience-core.js if Qdrant is unavailable.
|
package/dist/src/ee/bridge.js
CHANGED
|
@@ -11,6 +11,12 @@ import * as os from "node:os";
|
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import { PilContextResponseSchema } from "../pil/schema.js";
|
|
13
13
|
import { classifyEeError, logEeFailure, readTimeoutEnv, withEeTimeout } from "../utils/ee-logger.js";
|
|
14
|
+
// "Who Am I" v4.0 profile access. Re-exported through the bridge so PIL layers
|
|
15
|
+
// reach it via the single sanctioned EE entry point (see the no-network-in-pil
|
|
16
|
+
// arch guard: layer1/types may import `../ee/bridge` only). The provider is
|
|
17
|
+
// local-only (no network), cached, and fail-open — the same contract as the rest
|
|
18
|
+
// of this bridge.
|
|
19
|
+
export { getWhoAmIProfile, outputStyleFromProfile, resetWhoAmICache } from "./who-am-i.js";
|
|
14
20
|
/**
|
|
15
21
|
* Phase 21.5 — `routeModel` / `routeTask` budget. EE server unreachability used
|
|
16
22
|
* to hang `/ideal` for the OS-level fetch connect timeout (~5 min on Windows).
|
|
@@ -82,7 +88,7 @@ async function silentCall(fn) {
|
|
|
82
88
|
* 2. Fat clients (local experience-core.js present) → in-process call.
|
|
83
89
|
* 3. Otherwise → null (caller falls back to regex/keyword paths).
|
|
84
90
|
*/
|
|
85
|
-
export async function classifyViaBrain(prompt, timeoutMs = 5000) {
|
|
91
|
+
export async function classifyViaBrain(prompt, timeoutMs = 5000, options) {
|
|
86
92
|
// Thin-client HTTP path — required for CLIs without local experience-core.js.
|
|
87
93
|
try {
|
|
88
94
|
const { getCachedEEClientMode } = await import("./client-mode.js");
|
|
@@ -92,7 +98,7 @@ export async function classifyViaBrain(prompt, timeoutMs = 5000) {
|
|
|
92
98
|
: !!(await import("./auth.js")).getCachedServerBaseUrl();
|
|
93
99
|
if (useRemote) {
|
|
94
100
|
const { getDefaultEEClient } = await import("./intercept.js");
|
|
95
|
-
const result = await getDefaultEEClient().brainProxy(prompt, timeoutMs);
|
|
101
|
+
const result = await getDefaultEEClient().brainProxy(prompt, timeoutMs, options);
|
|
96
102
|
if (typeof result === "string")
|
|
97
103
|
return result;
|
|
98
104
|
// Server unreachable — fall through to in-process path so a fat-client
|
|
@@ -110,7 +116,7 @@ export async function classifyViaBrain(prompt, timeoutMs = 5000) {
|
|
|
110
116
|
try {
|
|
111
117
|
// Race the in-process call against `timeoutMs + 200ms` so a buggy core that
|
|
112
118
|
// ignores its own timeout argument cannot hang the host CLI.
|
|
113
|
-
return await withEeTimeout(silentCall(() => core.classifyViaBrain(prompt, timeoutMs)), timeoutMs + 200);
|
|
119
|
+
return await withEeTimeout(silentCall(() => core.classifyViaBrain(prompt, timeoutMs, options)), timeoutMs + 200);
|
|
114
120
|
}
|
|
115
121
|
catch (err) {
|
|
116
122
|
logEeFailure("bridge.classifyViaBrain", classifyEeError(err), err, {
|
|
@@ -52,7 +52,7 @@ describe("bridge — loaded core", () => {
|
|
|
52
52
|
it("Test 1: classifyViaBrain returns classification string when core loaded", async () => {
|
|
53
53
|
const result = await classifyViaBrain("write a function to sort an array");
|
|
54
54
|
expect(result).toBe("generate");
|
|
55
|
-
expect(mockCore.classifyViaBrain).toHaveBeenCalledWith("write a function to sort an array", 5000);
|
|
55
|
+
expect(mockCore.classifyViaBrain).toHaveBeenCalledWith("write a function to sort an array", 5000, undefined);
|
|
56
56
|
});
|
|
57
57
|
it("Test 2: searchCollection returns EEPoint[] when core loaded", async () => {
|
|
58
58
|
const vector = [0.1, 0.2, 0.3];
|
|
@@ -210,7 +210,6 @@ describe("bridge — config isolation (BRIDGE-03)", () => {
|
|
|
210
210
|
const _validCall = () => classifyViaBrain("prompt");
|
|
211
211
|
const _validCallWithTimeout = () => classifyViaBrain("prompt", 3000);
|
|
212
212
|
// These would cause @ts-expect-error if uncommented:
|
|
213
|
-
// @ts-expect-error — qdrantUrl is not a valid parameter
|
|
214
213
|
const _invalidCall = () => classifyViaBrain("prompt", 3000, "http://localhost:6333");
|
|
215
214
|
expect(_validCall).toBeDefined();
|
|
216
215
|
expect(_validCallWithTimeout).toBeDefined();
|
package/dist/src/ee/client.js
CHANGED
|
@@ -568,7 +568,7 @@ export function createEEClient(opts = {}) {
|
|
|
568
568
|
return null;
|
|
569
569
|
}
|
|
570
570
|
},
|
|
571
|
-
async brainProxy(prompt, timeoutMs = 2000) {
|
|
571
|
+
async brainProxy(prompt, timeoutMs = 2000, options) {
|
|
572
572
|
try {
|
|
573
573
|
// Tight client-side ceiling (~150ms grace over caller's intent budget)
|
|
574
574
|
// so we never wait longer than the PIL pipeline can use the result.
|
|
@@ -576,10 +576,23 @@ export function createEEClient(opts = {}) {
|
|
|
576
576
|
// we forward in the body — both ends abort together.
|
|
577
577
|
const ctrl = new AbortController();
|
|
578
578
|
const t = setTimeout(() => ctrl.abort(), timeoutMs + 150);
|
|
579
|
+
const body = { prompt, timeoutMs };
|
|
580
|
+
if (options) {
|
|
581
|
+
if (options.systemPrompt)
|
|
582
|
+
body.systemPrompt = options.systemPrompt;
|
|
583
|
+
if (options.responseFormat)
|
|
584
|
+
body.responseFormat = options.responseFormat;
|
|
585
|
+
if (options.model)
|
|
586
|
+
body.model = options.model;
|
|
587
|
+
if (options.maxTokens != null)
|
|
588
|
+
body.maxTokens = options.maxTokens;
|
|
589
|
+
if (options.provider)
|
|
590
|
+
body.provider = options.provider;
|
|
591
|
+
}
|
|
579
592
|
const resp = await f(`${baseUrl}/api/brain`, {
|
|
580
593
|
method: "POST",
|
|
581
594
|
headers: headers(),
|
|
582
|
-
body: JSON.stringify(
|
|
595
|
+
body: JSON.stringify(body),
|
|
583
596
|
signal: ctrl.signal,
|
|
584
597
|
});
|
|
585
598
|
clearTimeout(t);
|
|
@@ -22,6 +22,8 @@ export interface RecallLedger {
|
|
|
22
22
|
record(entries: EERecallEntry[] | undefined, query: string): void;
|
|
23
23
|
/** Clear one id once it has been rated. Returns true if it was actually pending. */
|
|
24
24
|
clear(id: string): boolean;
|
|
25
|
+
/** Check if an id was ever cleared (fed back) in this session. Used by PIL Layer 3 to suppress re-injection. */
|
|
26
|
+
wasCleared(id: string): boolean;
|
|
25
27
|
/** Oldest-first list of still-unrated recalls. */
|
|
26
28
|
pending(): PendingRecall[];
|
|
27
29
|
pendingCount(): number;
|
|
@@ -30,3 +32,21 @@ export interface RecallLedger {
|
|
|
30
32
|
export declare function createRecallLedger(): RecallLedger;
|
|
31
33
|
/** Process-scoped singleton = the current MCP session's unrated-recall debt. */
|
|
32
34
|
export declare const sessionRecallLedger: RecallLedger;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the recall-feedback ledger is active. Mirrors the gate env the external
|
|
37
|
+
* MCP ee.query already honours (EXPERIENCE_RECALL_FEEDBACK_GATE) so an operator
|
|
38
|
+
* can disable in-CLI ledger accounting with the same switch. In-CLI we only need
|
|
39
|
+
* on/off — never a hard refusal — so a turn is never blocked. Default on (soft).
|
|
40
|
+
*/
|
|
41
|
+
export declare function isRecallLedgerEnabled(): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Compact, token-bounded reminder of still-unrated surfaced/recalled handles, for
|
|
44
|
+
* injection next to the `[id]` handles the agent already saw. Names the actual
|
|
45
|
+
* `{id, collection}` pairs so an `ee_feedback(id, collection, verdict)` call is
|
|
46
|
+
* actionable — the legacy static nudge named no ids, so the model could not
|
|
47
|
+
* complete the rating even when willing. Capped so a long session can't bloat the
|
|
48
|
+
* prompt (token-thrift).
|
|
49
|
+
*/
|
|
50
|
+
export declare function formatPendingReminder(pending: PendingRecall[], opts?: {
|
|
51
|
+
max?: number;
|
|
52
|
+
}): string;
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export function createRecallLedger() {
|
|
14
14
|
const map = new Map();
|
|
15
|
+
// Tracks ids that have been fed back, so PIL Layer 3 can suppress re-injection.
|
|
16
|
+
const cleared = new Set();
|
|
15
17
|
return {
|
|
16
18
|
record(entries, query) {
|
|
17
19
|
if (!Array.isArray(entries))
|
|
@@ -34,7 +36,14 @@ export function createRecallLedger() {
|
|
|
34
36
|
}
|
|
35
37
|
},
|
|
36
38
|
clear(id) {
|
|
37
|
-
|
|
39
|
+
const nid = String(id ?? "").trim();
|
|
40
|
+
const deleted = map.delete(nid);
|
|
41
|
+
if (deleted)
|
|
42
|
+
cleared.add(nid);
|
|
43
|
+
return deleted;
|
|
44
|
+
},
|
|
45
|
+
wasCleared(id) {
|
|
46
|
+
return cleared.has(String(id ?? "").trim());
|
|
38
47
|
},
|
|
39
48
|
pending() {
|
|
40
49
|
return [...map.values()].sort((a, b) => a.ts - b.ts);
|
|
@@ -44,9 +53,39 @@ export function createRecallLedger() {
|
|
|
44
53
|
},
|
|
45
54
|
reset() {
|
|
46
55
|
map.clear();
|
|
56
|
+
cleared.clear();
|
|
47
57
|
},
|
|
48
58
|
};
|
|
49
59
|
}
|
|
50
60
|
/** Process-scoped singleton = the current MCP session's unrated-recall debt. */
|
|
51
61
|
export const sessionRecallLedger = createRecallLedger();
|
|
62
|
+
/**
|
|
63
|
+
* Whether the recall-feedback ledger is active. Mirrors the gate env the external
|
|
64
|
+
* MCP ee.query already honours (EXPERIENCE_RECALL_FEEDBACK_GATE) so an operator
|
|
65
|
+
* can disable in-CLI ledger accounting with the same switch. In-CLI we only need
|
|
66
|
+
* on/off — never a hard refusal — so a turn is never blocked. Default on (soft).
|
|
67
|
+
*/
|
|
68
|
+
export function isRecallLedgerEnabled() {
|
|
69
|
+
return (String(process.env.EXPERIENCE_RECALL_FEEDBACK_GATE ?? "soft")
|
|
70
|
+
.trim()
|
|
71
|
+
.toLowerCase() !== "off");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compact, token-bounded reminder of still-unrated surfaced/recalled handles, for
|
|
75
|
+
* injection next to the `[id]` handles the agent already saw. Names the actual
|
|
76
|
+
* `{id, collection}` pairs so an `ee_feedback(id, collection, verdict)` call is
|
|
77
|
+
* actionable — the legacy static nudge named no ids, so the model could not
|
|
78
|
+
* complete the rating even when willing. Capped so a long session can't bloat the
|
|
79
|
+
* prompt (token-thrift).
|
|
80
|
+
*/
|
|
81
|
+
export function formatPendingReminder(pending, opts = {}) {
|
|
82
|
+
if (pending.length === 0)
|
|
83
|
+
return "";
|
|
84
|
+
const max = Math.max(1, Math.min(opts.max ?? 5, 20));
|
|
85
|
+
const shown = pending.slice(0, max);
|
|
86
|
+
const lines = shown.map((p) => ` - [${p.id} ${p.collection ?? "?"}]`);
|
|
87
|
+
const more = pending.length > max ? `\n …and ${pending.length - max} more` : "";
|
|
88
|
+
return (`↳ ${pending.length} earlier EE hint(s) still unrated — rate the one(s) you acted on so the brain keeps what helped: ` +
|
|
89
|
+
`ee_feedback(id, collection, followed|ignored|noise).\n${lines.join("\n")}${more}`);
|
|
90
|
+
}
|
|
52
91
|
//# sourceMappingURL=recall-ledger.js.map
|
package/dist/src/ee/search.d.ts
CHANGED
|
@@ -92,6 +92,31 @@ export declare function feedbackEE(pointId: string, collection: string, verdict:
|
|
|
92
92
|
* compute unrated-recall debt as recalled ids minus fed-back ids. Best-effort.
|
|
93
93
|
*/
|
|
94
94
|
export declare function mirrorFeedbackLocally(pointId: string, collection: string, verdict: string, reason?: string | null, logPath?: string): Promise<void>;
|
|
95
|
+
export interface WriteExperienceResult {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
id?: string;
|
|
98
|
+
error?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Write a NEW experience/lesson into the brain mid-session so it is RECALLABLE.
|
|
102
|
+
* POSTs to /api/import-memory → storeImportedExperience → upsertEntry, which
|
|
103
|
+
* embeds the text AND writes the `text_bm25` sparse vector (+ a proper payload via
|
|
104
|
+
* buildStorePayload). That dense+sparse write is what makes the lesson reliably
|
|
105
|
+
* recallable via ee_query / passive injection (both the dense and lexical legs can
|
|
106
|
+
* match it). NB: /api/ingest-point writes DENSE-ONLY (no sparse) — its points are
|
|
107
|
+
* lexically invisible and only surface on a strong dense match, so they are NOT
|
|
108
|
+
* reliably recallable; do not use it for recall-critical writes.
|
|
109
|
+
*
|
|
110
|
+
* This is the missing "agent proactively records a mistake→fix lesson" arm of the
|
|
111
|
+
* recall loop: ee_query recalls, ee_feedback rates, ee_write creates. Mirrors
|
|
112
|
+
* feedbackEE's direct-fetch transport (cached baseUrl + bearer token); fails soft.
|
|
113
|
+
*/
|
|
114
|
+
export declare function writeExperienceEE(lesson: string, opts?: {
|
|
115
|
+
collection?: string;
|
|
116
|
+
title?: string;
|
|
117
|
+
projectSlug?: string;
|
|
118
|
+
confidence?: number;
|
|
119
|
+
}): Promise<WriteExperienceResult>;
|
|
95
120
|
/** Reachability probe for the EE server. */
|
|
96
121
|
export declare function healthEE(): Promise<{
|
|
97
122
|
ok: boolean;
|
package/dist/src/ee/search.js
CHANGED
|
@@ -211,6 +211,79 @@ export async function mirrorFeedbackLocally(pointId, collection, verdict, reason
|
|
|
211
211
|
logEeFailure("search.mirrorFeedbackLocally", classifyEeError(err), err);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Write a NEW experience/lesson into the brain mid-session so it is RECALLABLE.
|
|
216
|
+
* POSTs to /api/import-memory → storeImportedExperience → upsertEntry, which
|
|
217
|
+
* embeds the text AND writes the `text_bm25` sparse vector (+ a proper payload via
|
|
218
|
+
* buildStorePayload). That dense+sparse write is what makes the lesson reliably
|
|
219
|
+
* recallable via ee_query / passive injection (both the dense and lexical legs can
|
|
220
|
+
* match it). NB: /api/ingest-point writes DENSE-ONLY (no sparse) — its points are
|
|
221
|
+
* lexically invisible and only surface on a strong dense match, so they are NOT
|
|
222
|
+
* reliably recallable; do not use it for recall-critical writes.
|
|
223
|
+
*
|
|
224
|
+
* This is the missing "agent proactively records a mistake→fix lesson" arm of the
|
|
225
|
+
* recall loop: ee_query recalls, ee_feedback rates, ee_write creates. Mirrors
|
|
226
|
+
* feedbackEE's direct-fetch transport (cached baseUrl + bearer token); fails soft.
|
|
227
|
+
*/
|
|
228
|
+
export async function writeExperienceEE(lesson, opts = {}) {
|
|
229
|
+
const { loadEEAuthToken, getCachedServerBaseUrl } = await import("./auth.js");
|
|
230
|
+
const { randomUUID } = await import("node:crypto");
|
|
231
|
+
const authToken = (await loadEEAuthToken()) ?? undefined;
|
|
232
|
+
const baseUrl = (getCachedServerBaseUrl() ?? "http://localhost:8082").replace(/\/+$/, "");
|
|
233
|
+
const collection = opts.collection ?? "experience-behavioral";
|
|
234
|
+
const id = randomUUID();
|
|
235
|
+
// storeImportedExperience embeds `${qa.trigger} ${qa.question} ${qa.solution}`,
|
|
236
|
+
// so put the lesson in solution (and the title in question) to anchor the vector.
|
|
237
|
+
const qa = {
|
|
238
|
+
trigger: "",
|
|
239
|
+
question: opts.title ?? "",
|
|
240
|
+
solution: lesson,
|
|
241
|
+
scope: opts.projectSlug ? { project_slug: opts.projectSlug } : {},
|
|
242
|
+
};
|
|
243
|
+
const body = {
|
|
244
|
+
experiences: [
|
|
245
|
+
{
|
|
246
|
+
id,
|
|
247
|
+
collection,
|
|
248
|
+
qa,
|
|
249
|
+
tier: 2,
|
|
250
|
+
confidence: opts.confidence ?? 0.65,
|
|
251
|
+
runtime: "muonroi-cli-agent",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
};
|
|
255
|
+
const headers = { "Content-Type": "application/json" };
|
|
256
|
+
if (authToken)
|
|
257
|
+
headers.Authorization = `Bearer ${authToken}`;
|
|
258
|
+
try {
|
|
259
|
+
const res = await fetch(`${baseUrl}/api/import-memory`, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers,
|
|
262
|
+
body: JSON.stringify(body),
|
|
263
|
+
});
|
|
264
|
+
const respText = await res.text();
|
|
265
|
+
let parsed = null;
|
|
266
|
+
try {
|
|
267
|
+
parsed = JSON.parse(respText);
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
/* non-JSON error body — fall through to status handling */
|
|
271
|
+
}
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
return { ok: false, error: parsed?.error || respText || `HTTP ${res.status}` };
|
|
274
|
+
}
|
|
275
|
+
const r0 = parsed?.results?.[0];
|
|
276
|
+
if (!parsed?.stored || r0?.ok !== true) {
|
|
277
|
+
return { ok: false, error: r0?.reason ?? "not_stored" };
|
|
278
|
+
}
|
|
279
|
+
return { ok: true, id };
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
const { logEeFailure, classifyEeError } = await import("../utils/ee-logger.js");
|
|
283
|
+
logEeFailure("search.writeExperienceEE", classifyEeError(err), err);
|
|
284
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
214
287
|
/** Reachability probe for the EE server. */
|
|
215
288
|
export async function healthEE() {
|
|
216
289
|
const { createEEClient } = await import("./client.js");
|
package/dist/src/ee/types.d.ts
CHANGED
|
@@ -361,6 +361,20 @@ export interface EERecallOptions {
|
|
|
361
361
|
export interface EEUserResponse {
|
|
362
362
|
user: string;
|
|
363
363
|
}
|
|
364
|
+
export interface BrainProxyOptions {
|
|
365
|
+
/** Override the default system prompt sent to the LLM. */
|
|
366
|
+
systemPrompt?: string;
|
|
367
|
+
/** Request JSON-mode response_format (e.g. { type: "json_object" }). */
|
|
368
|
+
responseFormat?: {
|
|
369
|
+
type: string;
|
|
370
|
+
};
|
|
371
|
+
/** Override the LLM model id used for classification. */
|
|
372
|
+
model?: string;
|
|
373
|
+
/** Override max_tokens for the LLM response. */
|
|
374
|
+
maxTokens?: number;
|
|
375
|
+
/** Override the brain provider (siliconflow | ollama | openai | ...). */
|
|
376
|
+
provider?: string;
|
|
377
|
+
}
|
|
364
378
|
export interface EEClient {
|
|
365
379
|
health(): Promise<{
|
|
366
380
|
ok: boolean;
|
|
@@ -392,7 +406,7 @@ export interface EEClient {
|
|
|
392
406
|
/** Active recall via /api/recall (recallMode). Returns [id col] index + records a surface. */
|
|
393
407
|
recall(query: string, opts?: EERecallOptions): Promise<EERecallResponse | null>;
|
|
394
408
|
user(): Promise<EEUserResponse | null>;
|
|
395
|
-
brainProxy(prompt: string, timeoutMs?: number): Promise<string | null>;
|
|
409
|
+
brainProxy(prompt: string, timeoutMs?: number, options?: BrainProxyOptions): Promise<string | null>;
|
|
396
410
|
pilContext(prompt: string, options?: {
|
|
397
411
|
localeHint?: string;
|
|
398
412
|
projectCtx?: Record<string, unknown>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { OutputStyle } from "../pil/types.js";
|
|
2
|
+
export type PrivacyLevel = "minimal" | "standard" | "full";
|
|
3
|
+
export type WhoAmIDimName = "communication.question_style" | "communication.feedback_style" | "communication.brevity" | "personality.conflict_style" | "personality.risk_tolerance" | "personality.decision_speed" | "work_patterns.energy" | "work_patterns.multitasking" | "work_patterns.session_length" | "work_patterns.delegation_style";
|
|
4
|
+
export interface WhoAmIDim {
|
|
5
|
+
value: string;
|
|
6
|
+
confidence: number;
|
|
7
|
+
samples: number;
|
|
8
|
+
}
|
|
9
|
+
export interface WhoAmIProfile {
|
|
10
|
+
level: PrivacyLevel;
|
|
11
|
+
dims: Partial<Record<WhoAmIDimName, WhoAmIDim>>;
|
|
12
|
+
}
|
|
13
|
+
interface RawDim {
|
|
14
|
+
value: string | null;
|
|
15
|
+
confidence?: number;
|
|
16
|
+
sampleCount?: number;
|
|
17
|
+
samples?: number;
|
|
18
|
+
}
|
|
19
|
+
interface RawProfile {
|
|
20
|
+
dimensions?: Record<string, RawDim>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* PURE: project a raw EE profile to the privacy-gated dims for `level`, applying the
|
|
24
|
+
* positive name allowlist + value!=null commit gate + per-tier confidence floor.
|
|
25
|
+
*/
|
|
26
|
+
export declare function selectWhoAmIDims(raw: RawProfile | null | undefined, level: PrivacyLevel): WhoAmIProfile["dims"];
|
|
27
|
+
/**
|
|
28
|
+
* PURE: derive an output-style baseline from the profile. brevity is the primary
|
|
29
|
+
* signal; decision_speed is a weaker fallback. Returns null when no usable signal
|
|
30
|
+
* exists so the caller keeps its own per-turn default.
|
|
31
|
+
*/
|
|
32
|
+
export declare function outputStyleFromProfile(profile: WhoAmIProfile | null): OutputStyle | null;
|
|
33
|
+
/**
|
|
34
|
+
* Load + cache the privacy-gated profile for this process. The profile changes
|
|
35
|
+
* slowly (rebuilt by the EE Stop hook), so one read per process is enough.
|
|
36
|
+
* Returns null when EE is not installed, privacy is off, or nothing is committed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getWhoAmIProfile(): WhoAmIProfile | null;
|
|
39
|
+
/** Clear the cache — test-only / after a known profile change. */
|
|
40
|
+
export declare function resetWhoAmICache(): void;
|
|
41
|
+
export {};
|