muonroi-cli 1.7.2 → 1.8.1
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/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 +143 -6
- 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 +7 -4
- 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 +36 -1
- 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/flow/compaction/index.d.ts +1 -0
- package/dist/src/flow/compaction/index.js +4 -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 +59 -62
- 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 +13 -5
- package/dist/src/lsp/manager.test.js +41 -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/auto-setup.js +0 -8
- 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/self-verify-jobs.d.ts +1 -0
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/self-verify-runner.js +1 -1
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- 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 +3 -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 +2 -0
- package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -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 +366 -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 +23 -5
- package/dist/src/orchestrator/message-processor.js +226 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +11 -2
- package/dist/src/orchestrator/orchestrator.js +530 -43
- package/dist/src/orchestrator/pending-calls.js +2 -1
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +146 -0
- package/dist/src/orchestrator/prompts.d.ts +16 -0
- package/dist/src/orchestrator/prompts.js +130 -67
- 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-cap.d.ts +2 -0
- package/dist/src/orchestrator/sub-agent-cap.js +12 -4
- package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +21 -0
- package/dist/src/orchestrator/subagent-compactor.js +148 -24
- package/dist/src/orchestrator/subagent-compactor.spec.js +249 -1
- 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 +153 -0
- package/dist/src/orchestrator/tool-engine.js +3138 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +3 -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__/llm-classify.test.js +79 -1
- 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 +62 -7
- 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.d.ts +11 -0
- package/dist/src/pil/llm-classify.js +196 -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/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +2 -2
- 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/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/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/self-qa/agentic-loop.js +30 -21
- 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__/hierarchical-session.test.d.ts +1 -0
- package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
- package/dist/src/storage/__tests__/sweep-stale-pending.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/migrations.js +13 -1
- package/dist/src/storage/transcript-response-entry.test.js +18 -5
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +3 -0
- package/dist/src/storage/transcript.js +254 -73
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +22 -5
- 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 +66 -1
- 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 +6 -0
- package/dist/src/tools/registry.js +215 -13
- package/dist/src/types/index.d.ts +8 -2
- package/dist/src/ui/app.d.ts +1 -0
- 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/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/expand.js +14 -1
- 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/use-app-logic.d.ts +26 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/relaunch.js +1 -1
- 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/__tests__/llm-deadline.test.js +16 -4
- package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
- package/dist/src/utils/__tests__/logger.test.js +115 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
- package/dist/src/utils/install-manager.d.ts +1 -0
- package/dist/src/utils/install-manager.js +76 -7
- package/dist/src/utils/install-manager.test.js +43 -1
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- package/dist/src/utils/logger.d.ts +28 -0
- package/dist/src/utils/logger.js +171 -0
- 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 +36 -32
- package/dist/src/utils/settings.js +151 -123
- package/dist/src/utils/update-checker.test.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/lsp/client-normalize.test.ts
|
|
3
|
+
*
|
|
4
|
+
* Regression guard for the Windows diagnostics-cache key mismatch: the LSP
|
|
5
|
+
* client stores publishDiagnostics under normalizeUriPath(uri) (a LOWERCASE
|
|
6
|
+
* drive, decoded from `file:///c%3A/…`) but retrieves under
|
|
7
|
+
* normalizeFsPath(path.resolve(...)) (the cwd's UPPERCASE drive). Without
|
|
8
|
+
* canonicalizing the drive letter the keys never matched, so diagnostics were
|
|
9
|
+
* permanently [] on Windows — silently breaking write-time LSP feedback AND the
|
|
10
|
+
* commit gate. normalizeFsPath must produce the SAME key for both casings.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, expect, it } from "vitest";
|
|
13
|
+
import { normalizeFsPath } from "./client.js";
|
|
14
|
+
describe("normalizeFsPath", () => {
|
|
15
|
+
it("converts backslashes to forward slashes", () => {
|
|
16
|
+
expect(normalizeFsPath("C:\\Users\\x\\bug.ts")).toBe("c:/Users/x/bug.ts");
|
|
17
|
+
});
|
|
18
|
+
it("lowercases the Windows drive letter so store/retrieve keys converge", () => {
|
|
19
|
+
// The whole point: an UPPERCASE-drive query path and a LOWERCASE-drive
|
|
20
|
+
// publishDiagnostics URI path must normalize to the identical cache key.
|
|
21
|
+
expect(normalizeFsPath("C:/Users/x/bug.ts")).toBe(normalizeFsPath("c:/Users/x/bug.ts"));
|
|
22
|
+
expect(normalizeFsPath("C:/Users/x/bug.ts")).toBe("c:/Users/x/bug.ts");
|
|
23
|
+
expect(normalizeFsPath("D:\\proj\\a.ts")).toBe("d:/proj/a.ts");
|
|
24
|
+
});
|
|
25
|
+
it("leaves POSIX absolute paths untouched (no drive letter)", () => {
|
|
26
|
+
expect(normalizeFsPath("/home/u/bug.ts")).toBe("/home/u/bug.ts");
|
|
27
|
+
});
|
|
28
|
+
it("does not mangle a non-drive leading segment", () => {
|
|
29
|
+
// Only a single leading letter + ":/" is a drive; a path segment that
|
|
30
|
+
// merely starts with a letter must be preserved.
|
|
31
|
+
expect(normalizeFsPath("src/lsp/client.ts")).toBe("src/lsp/client.ts");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=client-normalize.test.js.map
|
package/dist/src/lsp/client.d.ts
CHANGED
package/dist/src/lsp/client.js
CHANGED
|
@@ -267,8 +267,16 @@ function normalizeUriPath(uri) {
|
|
|
267
267
|
}
|
|
268
268
|
return normalizeFsPath(uri);
|
|
269
269
|
}
|
|
270
|
-
function normalizeFsPath(filePath) {
|
|
271
|
-
|
|
270
|
+
export function normalizeFsPath(filePath) {
|
|
271
|
+
const slashed = filePath.replace(/\\/g, "/");
|
|
272
|
+
// Windows drive letters are case-insensitive, but the two sides of the
|
|
273
|
+
// diagnostics cache disagree on case: publishDiagnostics URIs decode to a
|
|
274
|
+
// LOWERCASE drive (tsserver emits `file:///c%3A/…`), while query paths come
|
|
275
|
+
// from `path.resolve(cwd, …)` which yields the cwd's (usually UPPERCASE)
|
|
276
|
+
// drive. Without canonicalizing the drive, diagnostics stored under `c:/…`
|
|
277
|
+
// are never found under `C:/…` → the cache always returns [] on Windows
|
|
278
|
+
// (silently breaking both write-time LSP feedback and the commit gate).
|
|
279
|
+
return slashed.replace(/^([a-zA-Z]):\//, (_m, d) => `${d.toLowerCase()}:/`);
|
|
272
280
|
}
|
|
273
281
|
function ensureActive(stopped, serverId) {
|
|
274
282
|
if (stopped) {
|
|
@@ -11,7 +11,7 @@ interface WorkspaceLspManagerOptions {
|
|
|
11
11
|
}
|
|
12
12
|
export interface WorkspaceLspManager {
|
|
13
13
|
touchFile(filePath: string, waitForDiagnostics?: boolean): Promise<LspDiagnosticFile[]>;
|
|
14
|
-
syncFile(filePath: string, content: string, save?: boolean, waitForDiagnostics?: boolean): Promise<LspDiagnosticFile[]>;
|
|
14
|
+
syncFile(filePath: string, content: string, save?: boolean, waitForDiagnostics?: boolean, diagnosticsTimeoutMs?: number): Promise<LspDiagnosticFile[]>;
|
|
15
15
|
query(input: LspQueryInput): Promise<LspToolResponse>;
|
|
16
16
|
close(): Promise<void>;
|
|
17
17
|
}
|
package/dist/src/lsp/manager.js
CHANGED
|
@@ -64,10 +64,18 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
|
|
|
64
64
|
return resolved.filter((value) => value !== null);
|
|
65
65
|
}
|
|
66
66
|
async function touchFile(filePath, waitForDiagnostics = true) {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
try {
|
|
68
|
+
const content = await readFile(filePath, "utf8");
|
|
69
|
+
return await syncFile(filePath, content, false, waitForDiagnostics);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (err instanceof Error && err.code === "ENOENT") {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
69
77
|
}
|
|
70
|
-
async function syncFile(filePath, content, save = true, waitForDiagnostics = true) {
|
|
78
|
+
async function syncFile(filePath, content, save = true, waitForDiagnostics = true, diagnosticsTimeoutMs) {
|
|
71
79
|
const records = await getClientsForFile(filePath);
|
|
72
80
|
if (records.length === 0)
|
|
73
81
|
return [];
|
|
@@ -80,7 +88,7 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
|
|
|
80
88
|
await client.saveFile(filePath);
|
|
81
89
|
}
|
|
82
90
|
if (waitForDiagnostics) {
|
|
83
|
-
await client.waitForDiagnostics(filePath);
|
|
91
|
+
await client.waitForDiagnostics(filePath, diagnosticsTimeoutMs);
|
|
84
92
|
}
|
|
85
93
|
return {
|
|
86
94
|
filePath,
|
|
@@ -104,7 +112,7 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
|
|
|
104
112
|
output: `No LSP server available for ${path.extname(normalizedPath) || "this file type"}.`,
|
|
105
113
|
};
|
|
106
114
|
}
|
|
107
|
-
const lspDiagnostics = await touchFile(normalizedPath, true);
|
|
115
|
+
const lspDiagnostics = input.operation === "workspaceSymbol" ? [] : await touchFile(normalizedPath, true);
|
|
108
116
|
const params = createOperationParams(input, normalizedPath);
|
|
109
117
|
const timeoutMs = settings.requestTimeoutMs;
|
|
110
118
|
const results = (await Promise.all(records.map(async ({ key, client }) => {
|
|
@@ -60,7 +60,7 @@ describe("createWorkspaceLspManager", () => {
|
|
|
60
60
|
expect(result.success).toBe(true);
|
|
61
61
|
expect(result.output).toContain("file:///demo.ts");
|
|
62
62
|
expect(client.openOrChangeFile).toHaveBeenCalledWith(filePath, "typescript", "const demo = 1;\n");
|
|
63
|
-
expect(client.waitForDiagnostics).toHaveBeenCalledWith(filePath);
|
|
63
|
+
expect(client.waitForDiagnostics).toHaveBeenCalledWith(filePath, undefined);
|
|
64
64
|
await manager.close();
|
|
65
65
|
expect(client.stop).toHaveBeenCalled();
|
|
66
66
|
});
|
|
@@ -92,7 +92,7 @@ describe("createWorkspaceLspManager", () => {
|
|
|
92
92
|
const result = await manager.syncFile(filePath, "const broken = true;\n", true, true);
|
|
93
93
|
expect(result).toEqual(diagnostics);
|
|
94
94
|
expect(client.saveFile).toHaveBeenCalledWith(filePath);
|
|
95
|
-
expect(client.waitForDiagnostics).toHaveBeenCalledWith(filePath);
|
|
95
|
+
expect(client.waitForDiagnostics).toHaveBeenCalledWith(filePath, undefined);
|
|
96
96
|
await manager.close();
|
|
97
97
|
});
|
|
98
98
|
it("times out a hanging request without dropping the client", async () => {
|
|
@@ -131,6 +131,45 @@ describe("createWorkspaceLspManager", () => {
|
|
|
131
131
|
expect(result.output).toContain("No LSP server available");
|
|
132
132
|
await manager.close();
|
|
133
133
|
});
|
|
134
|
+
it("handles missing files gracefully and avoids touchFile on workspaceSymbol", async () => {
|
|
135
|
+
const root = await createTempWorkspace();
|
|
136
|
+
const filePath = path.join(root, "nonexistent.ts");
|
|
137
|
+
const sendRequest = vi.fn(async (method, params) => {
|
|
138
|
+
expect(method).toBe("workspace/symbol");
|
|
139
|
+
expect(params).toEqual({ query: "my-query" });
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
name: "MySymbol",
|
|
143
|
+
kind: 1,
|
|
144
|
+
location: {
|
|
145
|
+
uri: "file:///demo.ts",
|
|
146
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 8 } },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
});
|
|
151
|
+
const client = createFakeClient({ sendRequest });
|
|
152
|
+
const manager = createWorkspaceLspManager(root, BASE_SETTINGS, {
|
|
153
|
+
createClient: async () => client,
|
|
154
|
+
});
|
|
155
|
+
const result = await manager.query({
|
|
156
|
+
operation: "workspaceSymbol",
|
|
157
|
+
filePath,
|
|
158
|
+
query: "my-query",
|
|
159
|
+
});
|
|
160
|
+
expect(result.success).toBe(true);
|
|
161
|
+
expect(result.output).toContain("MySymbol");
|
|
162
|
+
expect(client.openOrChangeFile).not.toHaveBeenCalled();
|
|
163
|
+
const resultDef = await manager.query({
|
|
164
|
+
operation: "goToDefinition",
|
|
165
|
+
filePath,
|
|
166
|
+
line: 1,
|
|
167
|
+
character: 1,
|
|
168
|
+
});
|
|
169
|
+
expect(resultDef.success).toBe(true);
|
|
170
|
+
expect(resultDef.output).toContain("No results found");
|
|
171
|
+
await manager.close();
|
|
172
|
+
});
|
|
134
173
|
});
|
|
135
174
|
async function createTempWorkspace() {
|
|
136
175
|
const root = await mkdtemp(path.join(os.tmpdir(), "muonroi-lsp-manager-"));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LspDiagnosticFile, LspQueryInput, LspToolResponse } from "./types.js";
|
|
2
2
|
export declare function queryLsp(cwd: string, input: LspQueryInput): Promise<LspToolResponse>;
|
|
3
|
-
export declare function syncFileWithLsp(cwd: string, filePath: string, content: string, save?: boolean, waitForDiagnostics?: boolean): Promise<LspDiagnosticFile[]>;
|
|
3
|
+
export declare function syncFileWithLsp(cwd: string, filePath: string, content: string, save?: boolean, waitForDiagnostics?: boolean, diagnosticsTimeoutMs?: number): Promise<LspDiagnosticFile[]>;
|
|
4
4
|
export declare function isLspToolEnabled(_cwd: string): boolean;
|
|
5
5
|
export declare function summarizeDiagnostics(diagnostics: LspDiagnosticFile[]): string | null;
|
|
6
6
|
export declare function shutdownWorkspaceLspManager(cwd: string): Promise<void>;
|
package/dist/src/lsp/runtime.js
CHANGED
|
@@ -10,9 +10,9 @@ export async function queryLsp(cwd, input) {
|
|
|
10
10
|
filePath: path.isAbsolute(input.filePath) ? input.filePath : path.resolve(cwd, input.filePath),
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
export async function syncFileWithLsp(cwd, filePath, content, save = true, waitForDiagnostics = true) {
|
|
13
|
+
export async function syncFileWithLsp(cwd, filePath, content, save = true, waitForDiagnostics = true, diagnosticsTimeoutMs) {
|
|
14
14
|
const manager = getOrCreateManager(cwd);
|
|
15
|
-
return manager.syncFile(path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath), content, save, waitForDiagnostics);
|
|
15
|
+
return manager.syncFile(path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath), content, save, waitForDiagnostics, diagnosticsTimeoutMs);
|
|
16
16
|
}
|
|
17
17
|
export function isLspToolEnabled(_cwd) {
|
|
18
18
|
const settings = getCurrentLspSettings();
|
|
@@ -12,9 +12,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
12
12
|
// ── Mocks ──────────────────────────────────────────────────────────────────
|
|
13
13
|
// vi.hoisted ensures the factory variable is available when vi.mock is hoisted.
|
|
14
14
|
const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
|
|
15
|
-
vi.mock("node:child_process", () =>
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
vi.mock("node:child_process", async () => {
|
|
16
|
+
const actual = await vi.importActual("node:child_process");
|
|
17
|
+
return {
|
|
18
|
+
...actual,
|
|
19
|
+
execFile: mockExecFile,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
18
22
|
vi.mock("node:fs", async (importOriginal) => {
|
|
19
23
|
const actual = (await importOriginal());
|
|
20
24
|
return {
|
|
@@ -11,9 +11,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
11
11
|
// ── Mocks ──────────────────────────────────────────────────────────────────
|
|
12
12
|
// vi.hoisted ensures the factory variable is available when vi.mock is hoisted.
|
|
13
13
|
const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
|
|
14
|
-
vi.mock("node:child_process", () =>
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
vi.mock("node:child_process", async () => {
|
|
15
|
+
const actual = await vi.importActual("node:child_process");
|
|
16
|
+
return {
|
|
17
|
+
...actual,
|
|
18
|
+
execFile: mockExecFile,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
17
21
|
vi.mock("../../council/leader.js", () => ({
|
|
18
22
|
pickCouncilTaskModel: vi.fn((_task, leaderId) => leaderId),
|
|
19
23
|
}));
|
|
@@ -54,7 +54,7 @@ describe("acquireMcpTools — cross-turn client pool", () => {
|
|
|
54
54
|
client: { close: async () => { } },
|
|
55
55
|
}));
|
|
56
56
|
const b1 = await acquireMcpTools([srv("fs")]);
|
|
57
|
-
await expect(b1.tools
|
|
57
|
+
await expect(b1.tools.mcp_fs__boom.execute({}, {})).rejects.toThrow(/transport closed/);
|
|
58
58
|
// Initial connect + exactly ONE in-turn reconnect — the retry is not looped.
|
|
59
59
|
expect(connectOneServer).toHaveBeenCalledTimes(2);
|
|
60
60
|
});
|
|
@@ -77,7 +77,7 @@ describe("acquireMcpTools — cross-turn client pool", () => {
|
|
|
77
77
|
};
|
|
78
78
|
});
|
|
79
79
|
const b = await acquireMcpTools([srv("docs")]);
|
|
80
|
-
const result = await b.tools
|
|
80
|
+
const result = await b.tools.mcp_docs__ping.execute({}, {});
|
|
81
81
|
expect(result).toBe("pong"); // recovered within the SAME turn
|
|
82
82
|
expect(connectOneServer).toHaveBeenCalledTimes(2); // drop + one reconnect
|
|
83
83
|
});
|
|
@@ -104,7 +104,7 @@ describe("acquireMcpTools — cross-turn client pool", () => {
|
|
|
104
104
|
};
|
|
105
105
|
});
|
|
106
106
|
const b = await acquireMcpTools([srv("docs")]);
|
|
107
|
-
const tool = b.tools
|
|
107
|
+
const tool = b.tools.mcp_docs__ping;
|
|
108
108
|
const results = await Promise.all(Array.from({ length: 14 }, () => tool.execute({}, {})));
|
|
109
109
|
expect(results.every((r) => r === "pong")).toBe(true);
|
|
110
110
|
expect(connectOneServer).toHaveBeenCalledTimes(2); // 14 failures → exactly ONE shared reconnect
|
|
@@ -46,7 +46,7 @@ describe("ee-tools", () => {
|
|
|
46
46
|
}),
|
|
47
47
|
health: async () => ({ ok: true, status: 200 }),
|
|
48
48
|
}));
|
|
49
|
-
const out = rawTextOf(await handlers
|
|
49
|
+
const out = rawTextOf(await handlers.ee_query({ query: "redactor" }));
|
|
50
50
|
expect(out.isError).toBeFalsy();
|
|
51
51
|
expect(out.text).toContain("recall:redactor");
|
|
52
52
|
expect(out.text).toContain("[id:abc col:experience-behavioral]"); // handle preserved for exp-feedback
|
|
@@ -58,7 +58,7 @@ describe("ee-tools", () => {
|
|
|
58
58
|
recall: async () => ({ text: "x".repeat(50_000), entries: [], count: 42 }),
|
|
59
59
|
health: async () => ({ ok: true, status: 200 }),
|
|
60
60
|
}));
|
|
61
|
-
const out = rawTextOf(await handlers
|
|
61
|
+
const out = rawTextOf(await handlers.ee_query({ query: "wide", maxChars: 6000 }));
|
|
62
62
|
expect(out.isError).toBeFalsy();
|
|
63
63
|
expect(out.text.length).toBeLessThan(7000); // capped, not the full 50k dump
|
|
64
64
|
expect(out.text).toContain("truncated");
|
|
@@ -73,18 +73,18 @@ describe("ee-tools", () => {
|
|
|
73
73
|
},
|
|
74
74
|
health: async () => ({ ok: true, status: 200 }),
|
|
75
75
|
}));
|
|
76
|
-
await handlers
|
|
76
|
+
await handlers.ee_query({ query: "scope filter", project: "storyflow" });
|
|
77
77
|
expect(seenProject).toBe("storyflow");
|
|
78
78
|
});
|
|
79
79
|
it("ee_query returns ee_unavailable when recall yields null", async () => {
|
|
80
80
|
const handlers = collectTools((s) => registerEETools(s, { recall: async () => null, health: async () => ({ ok: false, status: 0 }) }));
|
|
81
|
-
const out = textOf(await handlers
|
|
81
|
+
const out = textOf(await handlers.ee_query({ query: "x" }));
|
|
82
82
|
expect(out.isError).toBe(true);
|
|
83
83
|
expect(out.json.error).toBe("ee_unavailable");
|
|
84
84
|
});
|
|
85
85
|
it("ee_health returns the injected status", async () => {
|
|
86
86
|
const handlers = collectTools((s) => registerEETools(s, { recall: async () => null, health: async () => ({ ok: true, status: 200 }) }));
|
|
87
|
-
const out = textOf(await handlers
|
|
87
|
+
const out = textOf(await handlers.ee_health({}));
|
|
88
88
|
expect(out.json).toEqual({ ok: true, status: 200 });
|
|
89
89
|
});
|
|
90
90
|
it("ee_health returns ee_unavailable when health throws", async () => {
|
|
@@ -94,7 +94,7 @@ describe("ee-tools", () => {
|
|
|
94
94
|
throw new Error("boom");
|
|
95
95
|
},
|
|
96
96
|
}));
|
|
97
|
-
const out = textOf(await handlers
|
|
97
|
+
const out = textOf(await handlers.ee_health({}));
|
|
98
98
|
expect(out.isError).toBe(true);
|
|
99
99
|
expect(out.json.error).toBe("ee_unavailable");
|
|
100
100
|
});
|
|
@@ -111,7 +111,7 @@ describe("ee-tools", () => {
|
|
|
111
111
|
return { ok: true, resolvedId: id, verdict: verdict === "followed" ? "FOLLOWED" : verdict };
|
|
112
112
|
},
|
|
113
113
|
}));
|
|
114
|
-
const out = textOf(await handlers
|
|
114
|
+
const out = textOf(await handlers.ee_feedback({ id: "abc", collection: "experience-behavioral", verdict: "followed" }));
|
|
115
115
|
expect(out.isError).toBeFalsy();
|
|
116
116
|
expect(seen).toEqual({ id: "abc", collection: "experience-behavioral", verdict: "followed", reason: undefined });
|
|
117
117
|
expect(out.json.ok).toBe(true);
|
|
@@ -120,7 +120,7 @@ describe("ee-tools", () => {
|
|
|
120
120
|
});
|
|
121
121
|
it("ee_feedback requires a reason for verdict=noise", async () => {
|
|
122
122
|
const handlers = collectTools((s) => registerEETools(s, { feedback: async () => ({ ok: true }) }));
|
|
123
|
-
const out = textOf(await handlers
|
|
123
|
+
const out = textOf(await handlers.ee_feedback({ id: "x", collection: "experience-selfqa", verdict: "noise" }));
|
|
124
124
|
expect(out.isError).toBe(true);
|
|
125
125
|
expect(out.json.error).toBe("reason_required");
|
|
126
126
|
});
|
|
@@ -128,7 +128,7 @@ describe("ee-tools", () => {
|
|
|
128
128
|
const ledger = createRecallLedger();
|
|
129
129
|
ledger.record([{ id: "abc", collection: "experience-behavioral" }], "q");
|
|
130
130
|
const handlers = collectTools((s) => registerEETools(s, { ledger, feedback: async () => ({ ok: false, error: "HTTP 500" }) }));
|
|
131
|
-
const out = textOf(await handlers
|
|
131
|
+
const out = textOf(await handlers.ee_feedback({ id: "abc", collection: "experience-behavioral", verdict: "ignored" }));
|
|
132
132
|
expect(out.isError).toBe(true);
|
|
133
133
|
expect(out.json.error).toBe("feedback_failed");
|
|
134
134
|
expect(ledger.pendingCount()).toBe(1); // NOT cleared on failure
|
|
@@ -145,11 +145,11 @@ describe("ee-tools", () => {
|
|
|
145
145
|
health: async () => ({ ok: true, status: 200 }),
|
|
146
146
|
}));
|
|
147
147
|
// First recall — no prior debt, so no gate prefix.
|
|
148
|
-
const first = rawTextOf(await handlers
|
|
148
|
+
const first = rawTextOf(await handlers.ee_query({ query: "first" }));
|
|
149
149
|
expect(first.text).not.toContain("still unrated");
|
|
150
150
|
expect(ledger.pendingCount()).toBe(1);
|
|
151
151
|
// Second recall — the prior id1 is still unrated → soft prefix, but recall still returned.
|
|
152
|
-
const second = rawTextOf(await handlers
|
|
152
|
+
const second = rawTextOf(await handlers.ee_query({ query: "second" }));
|
|
153
153
|
expect(second.isError).toBeFalsy();
|
|
154
154
|
expect(second.text).toContain("still unrated");
|
|
155
155
|
expect(second.text).toContain("id1");
|
|
@@ -172,7 +172,7 @@ describe("ee-tools", () => {
|
|
|
172
172
|
return { text: "should not run", entries: [], count: 0 };
|
|
173
173
|
},
|
|
174
174
|
}));
|
|
175
|
-
const out = textOf(await handlers
|
|
175
|
+
const out = textOf(await handlers.ee_query({ query: "blocked" }));
|
|
176
176
|
expect(out.isError).toBe(true);
|
|
177
177
|
expect(out.json.error).toBe("feedback_required");
|
|
178
178
|
expect(out.json.message).toContain("FEEDBACK GATE");
|
|
@@ -190,9 +190,37 @@ describe("ee-tools", () => {
|
|
|
190
190
|
}),
|
|
191
191
|
health: async () => ({ ok: true, status: 200 }),
|
|
192
192
|
}));
|
|
193
|
-
await handlers
|
|
194
|
-
await handlers
|
|
193
|
+
await handlers.ee_query({ query: "one" });
|
|
194
|
+
await handlers.ee_query({ query: "two" });
|
|
195
195
|
expect(ledger.pendingCount()).toBe(0); // nothing stamped when gate is off
|
|
196
196
|
});
|
|
197
|
+
// ─── ee_write (Layer 1 — agent records a new lesson) ───────────────────────
|
|
198
|
+
it("ee_write forwards the lesson to the write helper and returns the new id", async () => {
|
|
199
|
+
let seen = null;
|
|
200
|
+
const handlers = collectTools((s) => registerEETools(s, {
|
|
201
|
+
write: async (lesson, opts) => {
|
|
202
|
+
seen = { lesson, opts };
|
|
203
|
+
return { ok: true, id: "new-point-1" };
|
|
204
|
+
},
|
|
205
|
+
}));
|
|
206
|
+
const out = textOf(await handlers.ee_write({
|
|
207
|
+
lesson: "always call flushFrob() before reindex or reads go stale",
|
|
208
|
+
title: "reindex pitfall",
|
|
209
|
+
project: "experience-engine",
|
|
210
|
+
}));
|
|
211
|
+
expect(out.isError).toBeFalsy();
|
|
212
|
+
expect(out.json.ok).toBe(true);
|
|
213
|
+
expect(out.json.id).toBe("new-point-1");
|
|
214
|
+
expect(out.json.collection).toBe("experience-behavioral");
|
|
215
|
+
expect(seen.lesson).toContain("flushFrob");
|
|
216
|
+
expect(seen.opts.collection).toBe("experience-behavioral");
|
|
217
|
+
expect(seen.opts.projectSlug).toBe("experience-engine");
|
|
218
|
+
});
|
|
219
|
+
it("ee_write surfaces write_failed when the write helper fails", async () => {
|
|
220
|
+
const handlers = collectTools((s) => registerEETools(s, { write: async () => ({ ok: false, error: "import-memory HTTP 500" }) }));
|
|
221
|
+
const out = textOf(await handlers.ee_write({ lesson: "a sufficiently long lesson body here" }));
|
|
222
|
+
expect(out.isError).toBe(true);
|
|
223
|
+
expect(out.json.error).toBe("write_failed");
|
|
224
|
+
});
|
|
197
225
|
});
|
|
198
226
|
//# sourceMappingURL=ee-tools.test.js.map
|
|
@@ -32,20 +32,20 @@ const fakeSummary = (id) => ({
|
|
|
32
32
|
describe("forensics-tools", () => {
|
|
33
33
|
it("usage_forensics returns the summary for a unique prefix", async () => {
|
|
34
34
|
const handlers = collectTools((s) => registerForensicsTools(s, { resolve: () => ["sess123"], collect: (id) => fakeSummary(id) }));
|
|
35
|
-
const out = parse(await handlers
|
|
35
|
+
const out = parse(await handlers.usage_forensics({ prefix: "sess" }));
|
|
36
36
|
expect(out.isError).toBeFalsy();
|
|
37
37
|
expect(out.json.sessionId).toBe("sess123");
|
|
38
38
|
expect(out.json.peakSingleCallInput).toBe(100);
|
|
39
39
|
});
|
|
40
40
|
it("usage_forensics returns not_found for zero matches", async () => {
|
|
41
41
|
const handlers = collectTools((s) => registerForensicsTools(s, { resolve: () => [], collect: () => fakeSummary("x") }));
|
|
42
|
-
const out = parse(await handlers
|
|
42
|
+
const out = parse(await handlers.usage_forensics({ prefix: "nope" }));
|
|
43
43
|
expect(out.isError).toBe(true);
|
|
44
44
|
expect(out.json.error).toBe("not_found");
|
|
45
45
|
});
|
|
46
46
|
it("usage_forensics returns ambiguous for multiple matches", async () => {
|
|
47
47
|
const handlers = collectTools((s) => registerForensicsTools(s, { resolve: () => ["a1", "a2"], collect: () => fakeSummary("a1") }));
|
|
48
|
-
const out = parse(await handlers
|
|
48
|
+
const out = parse(await handlers.usage_forensics({ prefix: "a" }));
|
|
49
49
|
expect(out.isError).toBe(true);
|
|
50
50
|
expect(out.json.error).toBe("ambiguous");
|
|
51
51
|
expect(out.json.matches).toEqual(["a1", "a2"]);
|
|
@@ -18,13 +18,13 @@ describe("lsp-tools", () => {
|
|
|
18
18
|
it("lsp_query passes through the LspToolResponse when enabled", async () => {
|
|
19
19
|
const resp = { success: true, output: "def at file.ts:10" };
|
|
20
20
|
const handlers = collectTools((s) => registerLspTools(s, { enabled: () => true, query: async () => resp }));
|
|
21
|
-
const out = parse(await handlers
|
|
21
|
+
const out = parse(await handlers.lsp_query({ operation: "goToDefinition", filePath: "a.ts", line: 1, character: 2 }));
|
|
22
22
|
expect(out.isError).toBeFalsy();
|
|
23
23
|
expect(out.json).toEqual(resp);
|
|
24
24
|
});
|
|
25
25
|
it("lsp_query returns lsp_disabled when LSP is off", async () => {
|
|
26
26
|
const handlers = collectTools((s) => registerLspTools(s, { enabled: () => false, query: async () => ({ success: true, output: "x" }) }));
|
|
27
|
-
const out = parse(await handlers
|
|
27
|
+
const out = parse(await handlers.lsp_query({ operation: "hover", filePath: "a.ts", line: 0, character: 0 }));
|
|
28
28
|
expect(out.isError).toBe(true);
|
|
29
29
|
expect(out.json.error).toBe("lsp_disabled");
|
|
30
30
|
});
|
|
@@ -35,7 +35,7 @@ describe("lsp-tools", () => {
|
|
|
35
35
|
throw new Error("server launch failed");
|
|
36
36
|
},
|
|
37
37
|
}));
|
|
38
|
-
const out = parse(await handlers
|
|
38
|
+
const out = parse(await handlers.lsp_query({ operation: "findReferences", filePath: "a.ts", line: 3, character: 4 }));
|
|
39
39
|
expect(out.isError).toBe(true);
|
|
40
40
|
expect(out.json.error).toBe("lsp_error");
|
|
41
41
|
expect(out.json.message).toContain("server launch failed");
|
|
@@ -47,7 +47,7 @@ describe("lsp-tools", () => {
|
|
|
47
47
|
},
|
|
48
48
|
query: async () => ({ success: true, output: "x" }),
|
|
49
49
|
}));
|
|
50
|
-
const out = parse(await handlers
|
|
50
|
+
const out = parse(await handlers.lsp_query({ operation: "hover", filePath: "a.ts", line: 0, character: 0 }));
|
|
51
51
|
expect(out.isError).toBe(true);
|
|
52
52
|
expect(out.json.error).toBe("lsp_error");
|
|
53
53
|
expect(out.json.message).toContain("settings read failed");
|
|
@@ -39,14 +39,6 @@ const DEFAULT_CONFIGS = [
|
|
|
39
39
|
transport: "http",
|
|
40
40
|
url: "https://docs-mcp.muonroi.com/mcp",
|
|
41
41
|
},
|
|
42
|
-
{
|
|
43
|
-
id: "filesystem",
|
|
44
|
-
label: "Filesystem",
|
|
45
|
-
enabled: true,
|
|
46
|
-
transport: "stdio",
|
|
47
|
-
command: "npx",
|
|
48
|
-
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
|
49
|
-
},
|
|
50
42
|
{
|
|
51
43
|
id: "playwright",
|
|
52
44
|
label: "Playwright",
|
|
@@ -44,7 +44,7 @@ export declare function acquireMcpTools(servers: McpServerConfig[], opts?: McpBu
|
|
|
44
44
|
* has connected. Idempotent (cached entries are reused); a failed connect is
|
|
45
45
|
* evicted by getOrConnect so a real turn retries.
|
|
46
46
|
*/
|
|
47
|
-
export declare function warmMcpClients(servers: McpServerConfig[]): void
|
|
47
|
+
export declare function warmMcpClients(servers: McpServerConfig[], syncAndLog?: boolean): Promise<void>;
|
|
48
48
|
/** Tear down every pooled client. Call on orchestrator/process shutdown. */
|
|
49
49
|
export declare function closeAllMcpClients(): Promise<void>;
|
|
50
50
|
/** Test-only: reset pool state between cases. */
|
|
@@ -236,15 +236,31 @@ export async function acquireMcpTools(servers, opts) {
|
|
|
236
236
|
* has connected. Idempotent (cached entries are reused); a failed connect is
|
|
237
237
|
* evicted by getOrConnect so a real turn retries.
|
|
238
238
|
*/
|
|
239
|
-
export function warmMcpClients(servers) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
239
|
+
export async function warmMcpClients(servers, syncAndLog = false) {
|
|
240
|
+
const validServers = servers.filter(s => s.enabled && validateMcpServerConfig(s).ok);
|
|
241
|
+
if (syncAndLog && validServers.length > 0) {
|
|
242
|
+
console.log(`[MCP] Warming up ${validServers.length} server(s)...`);
|
|
243
|
+
}
|
|
244
|
+
const promises = validServers.map(async (s) => {
|
|
245
|
+
try {
|
|
246
|
+
await getOrConnect(s);
|
|
247
|
+
if (syncAndLog) {
|
|
248
|
+
console.log(`[MCP] ✅ ${s.id}: healthy`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
if (syncAndLog) {
|
|
253
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
254
|
+
console.log(`[MCP] ❌ ${s.id}: failed - ${msg}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
if (syncAndLog) {
|
|
259
|
+
await Promise.all(promises);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Fire-and-forget for background warmup
|
|
263
|
+
void Promise.all(promises);
|
|
248
264
|
}
|
|
249
265
|
}
|
|
250
266
|
/** Tear down every pooled client. Call on orchestrator/process shutdown. */
|
|
@@ -30,6 +30,16 @@ export interface EEToolDeps {
|
|
|
30
30
|
status: number;
|
|
31
31
|
}>;
|
|
32
32
|
feedback?: (pointId: string, collection: string, verdict: FeedbackVerdict, reason?: NoiseReason) => Promise<FeedbackResult>;
|
|
33
|
+
write?: (lesson: string, opts: {
|
|
34
|
+
collection?: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
projectSlug?: string;
|
|
37
|
+
confidence?: number;
|
|
38
|
+
}) => Promise<{
|
|
39
|
+
ok: boolean;
|
|
40
|
+
id?: string;
|
|
41
|
+
error?: string;
|
|
42
|
+
}>;
|
|
33
43
|
/** Session pending-recall ledger. Defaults to the process singleton. */
|
|
34
44
|
ledger?: RecallLedger;
|
|
35
45
|
}
|
package/dist/src/mcp/ee-tools.js
CHANGED
|
@@ -155,5 +155,39 @@ export function registerEETools(server, deps = {}) {
|
|
|
155
155
|
return fail("ee_unavailable", e instanceof Error ? e.message : String(e));
|
|
156
156
|
}
|
|
157
157
|
});
|
|
158
|
+
server.registerTool("ee_write", {
|
|
159
|
+
description: "Save a NEW lesson to the Experience Engine brain so you and future sessions recall it. Call the MOMENT you " +
|
|
160
|
+
"hit a mistake / error / dead-end and find the working fix: record the pitfall AND the fix in one concise, " +
|
|
161
|
+
"generalizable lesson (1-3 sentences — what to do or avoid next time, NOT a play-by-play of this turn). The " +
|
|
162
|
+
"lesson is embedded immediately (via /api/import-memory: dense + sparse) and becomes recallable via ee_query " +
|
|
163
|
+
"in this and future sessions. Use ee_query first if unsure a lesson already exists. collection defaults to " +
|
|
164
|
+
"experience-behavioral (use experience-principles only for a broad, project-independent principle); project " +
|
|
165
|
+
"scopes the lesson.",
|
|
166
|
+
inputSchema: {
|
|
167
|
+
lesson: z.string().min(12).max(4000),
|
|
168
|
+
title: z.string().max(200).optional(),
|
|
169
|
+
collection: z.enum(["experience-behavioral", "experience-principles"]).optional(),
|
|
170
|
+
project: z.string().max(200).optional(),
|
|
171
|
+
},
|
|
172
|
+
}, async ({ lesson, title, collection, project }) => {
|
|
173
|
+
const trimmed = lesson.trim();
|
|
174
|
+
const text = trimmed.length > 1500 ? `${trimmed.slice(0, 1497)}...` : trimmed;
|
|
175
|
+
const targetCollection = collection ?? "experience-behavioral";
|
|
176
|
+
const write = deps.write ?? ((l, o) => import("../ee/search.js").then((m) => m.writeExperienceEE(l, o)));
|
|
177
|
+
try {
|
|
178
|
+
const result = await write(text, {
|
|
179
|
+
collection: targetCollection,
|
|
180
|
+
title,
|
|
181
|
+
projectSlug: project,
|
|
182
|
+
confidence: 0.65,
|
|
183
|
+
});
|
|
184
|
+
if (!result.ok)
|
|
185
|
+
return fail("write_failed", result.error ?? "import-memory POST failed");
|
|
186
|
+
return ok({ ok: true, id: result.id, collection: targetCollection, recallable: "now via ee_query" });
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
return fail("write_failed", e instanceof Error ? e.message : String(e));
|
|
190
|
+
}
|
|
191
|
+
});
|
|
158
192
|
}
|
|
159
193
|
//# sourceMappingURL=ee-tools.js.map
|
|
@@ -34,9 +34,7 @@ export class JobManager {
|
|
|
34
34
|
job.logBuffer.splice(0, job.logBuffer.length - LOG_CAP);
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
const run = opts.kind === "tier1"
|
|
38
|
-
? this.runner.tier1(opts, log, ctrl.signal)
|
|
39
|
-
: this.runner.agentic(opts, log, ctrl.signal);
|
|
37
|
+
const run = opts.kind === "tier1" ? this.runner.tier1(opts, log, ctrl.signal) : this.runner.agentic(opts, log, ctrl.signal);
|
|
40
38
|
run.then((report) => {
|
|
41
39
|
this.controllers.delete(runId); // I2: keep maps symmetric after natural completion
|
|
42
40
|
if (job.status === "cancelled")
|
|
@@ -25,7 +25,7 @@ export const defaultRunner = {
|
|
|
25
25
|
async agentic(opts, log) {
|
|
26
26
|
const { createLLMBrain, runAgenticLoop } = await import("../self-qa/agentic-loop.js");
|
|
27
27
|
const brain = await createLLMBrain({ modelId: opts.llm });
|
|
28
|
-
return runAgenticLoop({ goal: opts.goal, brain, maxTurns: opts.turns ?? 20, log });
|
|
28
|
+
return runAgenticLoop({ goal: opts.goal, brain, maxTurns: opts.turns ?? 20, mockLlmDir: opts.mockLlmDir, log });
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
31
|
let shared = null;
|