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
|
@@ -54,6 +54,14 @@ const ALLOWED_FIELDS = {
|
|
|
54
54
|
round: "pass",
|
|
55
55
|
correlationId: "pass",
|
|
56
56
|
},
|
|
57
|
+
"council-turn-length": {
|
|
58
|
+
role: "pass",
|
|
59
|
+
round: "pass",
|
|
60
|
+
charCount: "pass",
|
|
61
|
+
wordCount: "pass",
|
|
62
|
+
model: "pass",
|
|
63
|
+
correlationId: "pass",
|
|
64
|
+
},
|
|
57
65
|
"askcard-open": {
|
|
58
66
|
questionId: "pass",
|
|
59
67
|
question: 300, // cap to 300 chars, then scrub
|
|
@@ -46,6 +46,22 @@ export declare function validateStartArgs(args: string[]): {
|
|
|
46
46
|
bad: string;
|
|
47
47
|
};
|
|
48
48
|
export declare function sanitizeEnv(env: Record<string, string>): Record<string, string>;
|
|
49
|
+
/**
|
|
50
|
+
* Opt-in extra cwd roots for tui.start, layered ON TOP of the default
|
|
51
|
+
* {home, repo-root} allowlist. The posture stays deny-by-default: only roots an
|
|
52
|
+
* operator has explicitly listed are added. Two union sources:
|
|
53
|
+
*
|
|
54
|
+
* 1. env `MUONROI_HARNESS_EXTRA_ROOTS` — a path list separated by the OS path
|
|
55
|
+
* separator (";" on win32, ":" elsewhere) and/or commas.
|
|
56
|
+
* 2. `<REPO_ROOT>/.muonroi-harness-roots.json` — `{ "roots": string[] }`.
|
|
57
|
+
*
|
|
58
|
+
* Rationale: the drive-harness needs to dogfood sibling ecosystem repos (e.g.
|
|
59
|
+
* `D:\sources\Core\*`) that live outside both home and the muonroi-cli checkout.
|
|
60
|
+
* Without an explicit opt-in those cwds are rejected, which blocks real-task
|
|
61
|
+
* evaluation. Clean checkouts have neither the env nor the (gitignored) config
|
|
62
|
+
* file, so behaviour is identical to the original home+repo-only boundary.
|
|
63
|
+
*/
|
|
64
|
+
export declare function loadExtraRoots(): string[];
|
|
49
65
|
export declare function validateCwd(cwd: string): {
|
|
50
66
|
ok: true;
|
|
51
67
|
} | {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* fd 3/4, named pipes, WebSocket, …). The consumer (muonroi-cli) provides the
|
|
12
12
|
* spawn implementation via createMcpHarnessServer({ spawn }).
|
|
13
13
|
*/
|
|
14
|
-
import { existsSync, realpathSync } from "node:fs";
|
|
14
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { isAbsolute, resolve } from "node:path";
|
|
17
17
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -50,6 +50,51 @@ export function sanitizeEnv(env) {
|
|
|
50
50
|
return out;
|
|
51
51
|
}
|
|
52
52
|
const REPO_ROOT = process.cwd();
|
|
53
|
+
/**
|
|
54
|
+
* Opt-in extra cwd roots for tui.start, layered ON TOP of the default
|
|
55
|
+
* {home, repo-root} allowlist. The posture stays deny-by-default: only roots an
|
|
56
|
+
* operator has explicitly listed are added. Two union sources:
|
|
57
|
+
*
|
|
58
|
+
* 1. env `MUONROI_HARNESS_EXTRA_ROOTS` — a path list separated by the OS path
|
|
59
|
+
* separator (";" on win32, ":" elsewhere) and/or commas.
|
|
60
|
+
* 2. `<REPO_ROOT>/.muonroi-harness-roots.json` — `{ "roots": string[] }`.
|
|
61
|
+
*
|
|
62
|
+
* Rationale: the drive-harness needs to dogfood sibling ecosystem repos (e.g.
|
|
63
|
+
* `D:\sources\Core\*`) that live outside both home and the muonroi-cli checkout.
|
|
64
|
+
* Without an explicit opt-in those cwds are rejected, which blocks real-task
|
|
65
|
+
* evaluation. Clean checkouts have neither the env nor the (gitignored) config
|
|
66
|
+
* file, so behaviour is identical to the original home+repo-only boundary.
|
|
67
|
+
*/
|
|
68
|
+
export function loadExtraRoots() {
|
|
69
|
+
const roots = [];
|
|
70
|
+
const envVal = process.env.MUONROI_HARNESS_EXTRA_ROOTS;
|
|
71
|
+
if (envVal) {
|
|
72
|
+
// Split on the OS path separator or commas. On win32 the separator is ";"
|
|
73
|
+
// (not ":") so drive letters like "D:\..." stay intact.
|
|
74
|
+
const listSep = process.platform === "win32" ? ";" : ":";
|
|
75
|
+
for (const part of envVal.split(new RegExp(`[${listSep},]`))) {
|
|
76
|
+
const trimmed = part.trim();
|
|
77
|
+
if (trimmed)
|
|
78
|
+
roots.push(trimmed);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const cfgPath = resolve(REPO_ROOT, ".muonroi-harness-roots.json");
|
|
82
|
+
if (existsSync(cfgPath)) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(readFileSync(cfgPath, "utf8"));
|
|
85
|
+
if (Array.isArray(parsed.roots)) {
|
|
86
|
+
for (const r of parsed.roots) {
|
|
87
|
+
if (typeof r === "string" && r.trim())
|
|
88
|
+
roots.push(r.trim());
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.error(`[harness/mcp-server] failed to parse ${cfgPath}: ${err?.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return roots;
|
|
97
|
+
}
|
|
53
98
|
export function validateCwd(cwd) {
|
|
54
99
|
let real;
|
|
55
100
|
try {
|
|
@@ -58,16 +103,22 @@ export function validateCwd(cwd) {
|
|
|
58
103
|
catch {
|
|
59
104
|
return { ok: false, reason: "cwd does not exist or unreadable" };
|
|
60
105
|
}
|
|
61
|
-
const home = realpathSync(homedir());
|
|
62
|
-
const root = realpathSync(REPO_ROOT);
|
|
63
106
|
const sep = process.platform === "win32" ? "\\" : "/";
|
|
64
|
-
|
|
65
|
-
|
|
107
|
+
const allowedRoots = [realpathSync(homedir()), realpathSync(REPO_ROOT)];
|
|
108
|
+
for (const extra of loadExtraRoots()) {
|
|
109
|
+
try {
|
|
110
|
+
allowedRoots.push(realpathSync(extra));
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
console.error(`[harness/mcp-server] extra cwd root unresolved, skipping: ${extra} (${err?.message})`);
|
|
114
|
+
}
|
|
66
115
|
}
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
for (const root of allowedRoots) {
|
|
117
|
+
if (real === root || real.startsWith(root + sep)) {
|
|
118
|
+
return { ok: true };
|
|
119
|
+
}
|
|
69
120
|
}
|
|
70
|
-
return { ok: false, reason: "cwd escapes home and
|
|
121
|
+
return { ok: false, reason: "cwd escapes home, repo root, and configured extra roots" };
|
|
71
122
|
}
|
|
72
123
|
export function validateMockLlmPath(value) {
|
|
73
124
|
const resolved = isAbsolute(value) ? value : resolve(REPO_ROOT, value);
|
|
@@ -255,7 +306,34 @@ export function registerAsyncTools(server, getDriver, deps) {
|
|
|
255
306
|
});
|
|
256
307
|
server.registerTool("tui.last_event", {
|
|
257
308
|
description: "Return the most recent event of the given kind (null if none).",
|
|
258
|
-
|
|
309
|
+
// Full protocol event set (minus the idle sentinel) so an external agent can
|
|
310
|
+
// observe lifecycle events — council/sprint/route/askcard, not just toasts.
|
|
311
|
+
// The Driver accepts any kind; this enum is the MCP-boundary validation.
|
|
312
|
+
inputSchema: {
|
|
313
|
+
kind: z.enum([
|
|
314
|
+
"toast",
|
|
315
|
+
"stream.delta",
|
|
316
|
+
"llm-token",
|
|
317
|
+
"llm-done",
|
|
318
|
+
"council-step",
|
|
319
|
+
"council-speaker",
|
|
320
|
+
"council-turn-length",
|
|
321
|
+
"askcard-open",
|
|
322
|
+
"askcard-answered",
|
|
323
|
+
"askcard-cancel",
|
|
324
|
+
"sprint-stage",
|
|
325
|
+
"sprint-halt",
|
|
326
|
+
"sprint-plan-committed",
|
|
327
|
+
"route-decision",
|
|
328
|
+
"steer-inject",
|
|
329
|
+
"usage",
|
|
330
|
+
"grounding-flag",
|
|
331
|
+
"ee-timeout",
|
|
332
|
+
"ee-error",
|
|
333
|
+
"stream-retry",
|
|
334
|
+
"disconnect",
|
|
335
|
+
]),
|
|
336
|
+
},
|
|
259
337
|
}, async ({ kind }) => {
|
|
260
338
|
const d = getDriver();
|
|
261
339
|
if (!d)
|
|
@@ -353,7 +431,7 @@ export function createMcpHarnessServer({ spawn }) {
|
|
|
353
431
|
}
|
|
354
432
|
if (!finalArgs.includes("--agent-mode"))
|
|
355
433
|
finalArgs.push("--agent-mode");
|
|
356
|
-
const entry = process.cwd()
|
|
434
|
+
const entry = `${process.cwd()}/src/index.ts`;
|
|
357
435
|
// Delegate to the injected spawn implementation — the core package has no
|
|
358
436
|
// knowledge of the concrete transport (fd 3/4, named pipes, WebSocket …).
|
|
359
437
|
let spawnResult;
|
|
@@ -73,6 +73,21 @@ export type LiveEvent = {
|
|
|
73
73
|
round?: number;
|
|
74
74
|
/** Correlation ID linking this speaker event to the enclosing council run. */
|
|
75
75
|
correlationId: string;
|
|
76
|
+
} | {
|
|
77
|
+
t: "event";
|
|
78
|
+
kind: "council-turn-length";
|
|
79
|
+
/** Council role label (e.g. "architect"); matches council-speaker.role. */
|
|
80
|
+
role: string;
|
|
81
|
+
/** Debate round: 0 = opening statements, 1+ = discussion rounds. */
|
|
82
|
+
round: number;
|
|
83
|
+
/** Full character count of the speaker's output (trimmed; never truncated). */
|
|
84
|
+
charCount: number;
|
|
85
|
+
/** Word count of the speaker's output (whitespace-split, empties dropped). */
|
|
86
|
+
wordCount: number;
|
|
87
|
+
/** The speaker's model id, for per-model thrift attribution. */
|
|
88
|
+
model: string;
|
|
89
|
+
/** Correlation ID tying turns to the enclosing council run (= sessionId). */
|
|
90
|
+
correlationId: string;
|
|
76
91
|
} | {
|
|
77
92
|
t: "event";
|
|
78
93
|
kind: "askcard-open";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const _FLAGS = new Set(["focus", "selected", "disabled"]);
|
|
2
2
|
export function parseSelector(input) {
|
|
3
3
|
// First, split by child combinator >>
|
|
4
4
|
const childCombinatorRegex = /\s*>>\s*/;
|
|
@@ -146,7 +146,7 @@ function readField(node, key) {
|
|
|
146
146
|
return node.state;
|
|
147
147
|
if (key.startsWith("props.")) {
|
|
148
148
|
const dot = key.slice("props.".length);
|
|
149
|
-
return
|
|
149
|
+
return node.props?.[dot];
|
|
150
150
|
}
|
|
151
151
|
return undefined;
|
|
152
152
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const MAX_BYTES = 1024 * 1024; // 1 MiB
|
|
2
2
|
function serialize(msg) {
|
|
3
|
-
const line = JSON.stringify(msg)
|
|
3
|
+
const line = `${JSON.stringify(msg)}\n`;
|
|
4
4
|
if (Buffer.byteLength(line, "utf8") > MAX_BYTES) {
|
|
5
5
|
throw new Error(`sidechannel message exceeds 1 MiB cap`);
|
|
6
6
|
}
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
19
19
|
import { createEventFilter } from "@muonroi/agent-harness-core/event-filter";
|
|
20
|
-
import { createIdleDetector } from "@muonroi/agent-harness-core/idle";
|
|
21
20
|
import { redactEvent } from "@muonroi/agent-harness-core/event-redact";
|
|
21
|
+
import { createIdleDetector } from "@muonroi/agent-harness-core/idle";
|
|
22
22
|
import { createLineSplitter, createSidechannelWriter } from "@muonroi/agent-harness-core/transports/sidechannel";
|
|
23
23
|
import { installOpenTUIHarness } from "./install.js";
|
|
24
24
|
import { createSemanticRegistry } from "./reconciler-hook.js";
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* Any module that transitively imports db.ts will resolve to this stub.
|
|
4
4
|
*/
|
|
5
5
|
import { vi } from "vitest";
|
|
6
|
+
// vitest 2+ removed vi.mocked(). This shim restores it for the 93+ call sites across the
|
|
7
|
+
// codebase without a codemod. Safe no-op: vi.mocked(item) just returns item (typed cast).
|
|
8
|
+
// @ts-expect-error — vi.mocked is intentionally absent from vitest 4 types.
|
|
9
|
+
vi.mocked ??= ((item, _options) => item);
|
|
6
10
|
// @opentui/react is a pre-bundled CJS package that requires 'react-reconciler/constants'
|
|
7
11
|
// without the .js extension — this fails in the vitest ESM environment. None of the unit
|
|
8
12
|
// tests exercise OpenTUI hooks directly (those are covered by E2E harness specs), so mocking
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
+
import { APICallError } from "@ai-sdk/provider";
|
|
8
9
|
import { generateObject, stepCountIs, streamText, tool } from "ai";
|
|
9
10
|
import { afterAll, describe, expect, it } from "vitest";
|
|
10
11
|
import { z } from "zod";
|
|
12
|
+
import { humanizeApiError } from "../../orchestrator/error-utils.js";
|
|
11
13
|
import { createMockModel, loadMockModelFromDir, textOnlyStream, toolCallStream } from "../mock-model.js";
|
|
12
14
|
// biome-ignore lint/suspicious/noExplicitAny: streamText result generic is provider-specific
|
|
13
15
|
async function drainStream(result) {
|
|
@@ -188,6 +190,58 @@ describe("loadMockModelFromDir", () => {
|
|
|
188
190
|
});
|
|
189
191
|
expect(object).toEqual({ ok: true, label: "built" });
|
|
190
192
|
});
|
|
193
|
+
it("constructs an APICallError from a structured error fixture (drives the humanizeApiError 5xx branch)", async () => {
|
|
194
|
+
// A pure-JSON fixture cannot otherwise build an APICallError (it arrives as
|
|
195
|
+
// a plain string). buildFixtureError() in mock-model.ts upgrades a structured
|
|
196
|
+
// `error` object carrying a statusCode into a real APICallError so harness/
|
|
197
|
+
// unit specs can exercise the orchestrator's status-aware 5xx path.
|
|
198
|
+
const dir = mkFixtureDir({
|
|
199
|
+
provider: "mock",
|
|
200
|
+
modelId: "mock-gpt",
|
|
201
|
+
stream: [
|
|
202
|
+
[
|
|
203
|
+
{ type: "stream-start", warnings: [] },
|
|
204
|
+
{
|
|
205
|
+
type: "error",
|
|
206
|
+
error: {
|
|
207
|
+
apiCallError: true,
|
|
208
|
+
statusCode: 500,
|
|
209
|
+
message: "AI_APICallError: Request failed",
|
|
210
|
+
responseBody: JSON.stringify({ code: 60000, message: "Request failed: Unknown error." }),
|
|
211
|
+
url: "https://api.siliconflow.cn/v1/chat/completions",
|
|
212
|
+
isRetryable: true,
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
type: "finish",
|
|
217
|
+
finishReason: { unified: "error", raw: null },
|
|
218
|
+
usage: {
|
|
219
|
+
inputTokens: { total: 0, noCache: 0, cacheRead: null, cacheWrite: null },
|
|
220
|
+
outputTokens: { total: 0, text: 0, reasoning: null },
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
const handle = await loadMockModelFromDir(dir);
|
|
227
|
+
expect(handle).not.toBeNull();
|
|
228
|
+
const r = streamText({ model: handle.model, prompt: "hi" });
|
|
229
|
+
let errPart = null;
|
|
230
|
+
for await (const part of r.fullStream) {
|
|
231
|
+
if (part.type === "error")
|
|
232
|
+
errPart = part;
|
|
233
|
+
}
|
|
234
|
+
expect(errPart).not.toBeNull();
|
|
235
|
+
expect(APICallError.isInstance(errPart?.error)).toBe(true);
|
|
236
|
+
expect((errPart?.error).statusCode).toBe(500);
|
|
237
|
+
// The 5xx branch must surface the canned actionable text + (HTTP 500) and
|
|
238
|
+
// SUPPRESS the opaque provider body (the SiliconFlow "Unknown error" mask).
|
|
239
|
+
const msg = humanizeApiError(errPart?.error);
|
|
240
|
+
expect(msg).toContain("HTTP 500");
|
|
241
|
+
expect(msg.toLowerCase()).toContain("try again later");
|
|
242
|
+
expect(msg).not.toContain("Unknown error");
|
|
243
|
+
expect(msg).not.toContain("Request failed");
|
|
244
|
+
});
|
|
191
245
|
it("supports multi-round stream arrays from the fixture file", async () => {
|
|
192
246
|
const dir = mkFixtureDir({
|
|
193
247
|
provider: "mock",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* const calls = handle.calls; // LanguageModelV3CallOptions[]
|
|
23
23
|
* expect(calls[0].maxOutputTokens).toBeUndefined();
|
|
24
24
|
*/
|
|
25
|
-
import type
|
|
25
|
+
import { type LanguageModelV3CallOptions, type LanguageModelV3StreamPart } from "@ai-sdk/provider";
|
|
26
26
|
import { MockLanguageModelV3 } from "ai/test";
|
|
27
27
|
export type StreamChunks = LanguageModelV3StreamPart[];
|
|
28
28
|
export interface MockModelFixture {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* const calls = handle.calls; // LanguageModelV3CallOptions[]
|
|
23
23
|
* expect(calls[0].maxOutputTokens).toBeUndefined();
|
|
24
24
|
*/
|
|
25
|
+
import { APICallError } from "@ai-sdk/provider";
|
|
25
26
|
import { simulateReadableStream } from "ai";
|
|
26
27
|
import { MockLanguageModelV3 } from "ai/test";
|
|
27
28
|
function isNestedArray(v) {
|
|
@@ -71,7 +72,7 @@ export function createMockModel(fx) {
|
|
|
71
72
|
const text = generates.length > 0 ? generates[Math.min(genIdx, generates.length - 1)] : "{}";
|
|
72
73
|
genIdx += 1;
|
|
73
74
|
if (process.env.MUONROI_DEBUG_MOCK_MODEL === "1") {
|
|
74
|
-
process.stderr.write(`[mock-model] doGenerate #${genIdx} → ${text.length} chars\n`);
|
|
75
|
+
process.stderr.write(`[mock-model] doGenerate #${genIdx} → ${text.length} chars\nCALLER:\n${new Error().stack}\n`);
|
|
75
76
|
}
|
|
76
77
|
return {
|
|
77
78
|
content: [{ type: "text", text }],
|
|
@@ -198,14 +199,150 @@ export function toolCallStream(opts) {
|
|
|
198
199
|
* string payloads here so the rest of the pipeline sees a uniform shape.
|
|
199
200
|
*/
|
|
200
201
|
function normalizeStreamChunks(chunks) {
|
|
201
|
-
|
|
202
|
-
if (c
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
202
|
+
const normalized = chunks.map((c) => {
|
|
203
|
+
if (!c)
|
|
204
|
+
return c;
|
|
205
|
+
const part = c;
|
|
206
|
+
if (part.type === "error") {
|
|
207
|
+
const raw = part.error;
|
|
208
|
+
return { ...part, error: buildFixtureError(raw) };
|
|
209
|
+
}
|
|
210
|
+
if (part.type === "text-delta") {
|
|
211
|
+
const delta = part.delta ?? part.textDelta ?? part.text ?? "";
|
|
212
|
+
const id = part.id ?? "txt-0";
|
|
213
|
+
return { ...part, id, delta };
|
|
214
|
+
}
|
|
215
|
+
if (part.type === "reasoning-delta") {
|
|
216
|
+
const delta = part.delta ?? part.textDelta ?? part.text ?? "";
|
|
217
|
+
const id = part.id ?? "reasoning-0";
|
|
218
|
+
return { ...part, id, delta };
|
|
206
219
|
}
|
|
207
220
|
return c;
|
|
208
221
|
});
|
|
222
|
+
const result = [];
|
|
223
|
+
const textIds = new Set();
|
|
224
|
+
const reasoningIds = new Set();
|
|
225
|
+
for (const part of normalized) {
|
|
226
|
+
if (!part)
|
|
227
|
+
continue;
|
|
228
|
+
const p = part;
|
|
229
|
+
if (p.type === "text-delta") {
|
|
230
|
+
textIds.add(p.id ?? "txt-0");
|
|
231
|
+
}
|
|
232
|
+
else if (p.type === "reasoning-delta") {
|
|
233
|
+
reasoningIds.add(p.id ?? "reasoning-0");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const seenTextStart = new Set();
|
|
237
|
+
const seenTextEnd = new Set();
|
|
238
|
+
const seenReasoningStart = new Set();
|
|
239
|
+
const seenReasoningEnd = new Set();
|
|
240
|
+
for (const part of normalized) {
|
|
241
|
+
if (!part)
|
|
242
|
+
continue;
|
|
243
|
+
const p = part;
|
|
244
|
+
if (p.type === "text-start") {
|
|
245
|
+
seenTextStart.add(p.id);
|
|
246
|
+
}
|
|
247
|
+
else if (p.type === "text-end") {
|
|
248
|
+
seenTextEnd.add(p.id);
|
|
249
|
+
}
|
|
250
|
+
else if (p.type === "reasoning-start") {
|
|
251
|
+
seenReasoningStart.add(p.id);
|
|
252
|
+
}
|
|
253
|
+
else if (p.type === "reasoning-end") {
|
|
254
|
+
seenReasoningEnd.add(p.id);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const firstTextDeltaIdx = new Map();
|
|
258
|
+
const lastTextDeltaIdx = new Map();
|
|
259
|
+
const firstReasoningDeltaIdx = new Map();
|
|
260
|
+
const lastReasoningDeltaIdx = new Map();
|
|
261
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
262
|
+
const p = normalized[i];
|
|
263
|
+
if (!p)
|
|
264
|
+
continue;
|
|
265
|
+
if (p.type === "text-delta") {
|
|
266
|
+
const id = p.id ?? "txt-0";
|
|
267
|
+
if (!firstTextDeltaIdx.has(id))
|
|
268
|
+
firstTextDeltaIdx.set(id, i);
|
|
269
|
+
lastTextDeltaIdx.set(id, i);
|
|
270
|
+
}
|
|
271
|
+
else if (p.type === "reasoning-delta") {
|
|
272
|
+
const id = p.id ?? "reasoning-0";
|
|
273
|
+
if (!firstReasoningDeltaIdx.has(id))
|
|
274
|
+
firstReasoningDeltaIdx.set(id, i);
|
|
275
|
+
lastReasoningDeltaIdx.set(id, i);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
279
|
+
const p = normalized[i];
|
|
280
|
+
if (!p) {
|
|
281
|
+
result.push(p);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (p.type === "text-delta") {
|
|
285
|
+
const id = p.id ?? "txt-0";
|
|
286
|
+
if (firstTextDeltaIdx.get(id) === i && !seenTextStart.has(id)) {
|
|
287
|
+
result.push({ type: "text-start", id });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else if (p.type === "reasoning-delta") {
|
|
291
|
+
const id = p.id ?? "reasoning-0";
|
|
292
|
+
if (firstReasoningDeltaIdx.get(id) === i && !seenReasoningStart.has(id)) {
|
|
293
|
+
result.push({ type: "reasoning-start", id });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (p.type === "finish") {
|
|
297
|
+
const usage = p.usage ?? buildUsage(10, 10);
|
|
298
|
+
result.push({ ...p, usage });
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
result.push(p);
|
|
302
|
+
}
|
|
303
|
+
if (p.type === "text-delta") {
|
|
304
|
+
const id = p.id ?? "txt-0";
|
|
305
|
+
if (lastTextDeltaIdx.get(id) === i && !seenTextEnd.has(id)) {
|
|
306
|
+
result.push({ type: "text-end", id });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else if (p.type === "reasoning-delta") {
|
|
310
|
+
const id = p.id ?? "reasoning-0";
|
|
311
|
+
if (lastReasoningDeltaIdx.get(id) === i && !seenReasoningEnd.has(id)) {
|
|
312
|
+
result.push({ type: "reasoning-end", id });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Turn a fixture `error` payload into the right runtime shape:
|
|
320
|
+
* - an `APICallError` when the payload is an object carrying a `statusCode`
|
|
321
|
+
* (or `apiCallError: true`), so harness specs can exercise the orchestrator's
|
|
322
|
+
* status-aware paths (humanizeApiError 5xx canned text, summarizeApiErrorForLog
|
|
323
|
+
* forensics, retry classifier). `APICallError.isInstance` duck-types on a
|
|
324
|
+
* shared marker, so constructing it here satisfies the `isInstance` checks in
|
|
325
|
+
* error-utils.ts. A pure JSON fixture cannot otherwise build one (it would
|
|
326
|
+
* arrive as a plain string).
|
|
327
|
+
* - a plain `Error` for string payloads (the common transient-message case).
|
|
328
|
+
*/
|
|
329
|
+
function buildFixtureError(raw) {
|
|
330
|
+
if (raw instanceof Error)
|
|
331
|
+
return raw;
|
|
332
|
+
if (raw && typeof raw === "object") {
|
|
333
|
+
const o = raw;
|
|
334
|
+
if (o.apiCallError === true || typeof o.statusCode === "number") {
|
|
335
|
+
return new APICallError({
|
|
336
|
+
message: o.message ?? `mock API error${o.statusCode ? ` (HTTP ${o.statusCode})` : ""}`,
|
|
337
|
+
url: o.url ?? "https://mock.invalid/v1/chat/completions",
|
|
338
|
+
requestBodyValues: {},
|
|
339
|
+
statusCode: o.statusCode,
|
|
340
|
+
responseBody: o.responseBody,
|
|
341
|
+
isRetryable: o.isRetryable,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return new Error(typeof raw === "string" ? raw : "mock LLM error");
|
|
209
346
|
}
|
|
210
347
|
export async function loadMockModelFromDir(dir) {
|
|
211
348
|
// Diagnostic logging path. Without these messages, every failure mode
|
|
@@ -131,7 +131,9 @@ async function spawnWindows(args, opts) {
|
|
|
131
131
|
await waitForHandshake(outSocket, timeoutMs);
|
|
132
132
|
}
|
|
133
133
|
catch (err) {
|
|
134
|
-
proc.kill
|
|
134
|
+
if (proc && typeof proc.kill === "function") {
|
|
135
|
+
proc.kill();
|
|
136
|
+
}
|
|
135
137
|
inServer.close();
|
|
136
138
|
outServer.close();
|
|
137
139
|
throw err;
|
|
@@ -86,7 +86,15 @@ export async function deleteChatSecret(id) {
|
|
|
86
86
|
const kt = await loadKeytar();
|
|
87
87
|
if (!kt?.deletePassword)
|
|
88
88
|
return false;
|
|
89
|
-
|
|
89
|
+
try {
|
|
90
|
+
return await kt.deletePassword(KEYCHAIN_SERVICE, ACCOUNT_BY_CHAT[id]);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (process.env.DEBUG || process.env.MUONROI_DEBUG_KEYCHAIN) {
|
|
94
|
+
console.error(`[chat-keychain] deletePassword backend error for ${id}:`, err?.message || err);
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
/**
|
|
92
100
|
* List all stored chat secret IDs.
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { getProviderCapabilities } from "../providers/capabilities.js";
|
|
12
12
|
import { detectProviderForModel } from "../providers/runtime.js";
|
|
13
13
|
import { getDatabase } from "../storage/db.js";
|
|
14
|
+
import { getSessionChain } from "../storage/index.js";
|
|
14
15
|
import { selectSessionExperience } from "../storage/session-experience-store.js";
|
|
15
16
|
function resolveSessionId(prefix) {
|
|
16
17
|
const rows = getDatabase()
|
|
@@ -42,24 +43,26 @@ export function collectCostForensics(sessionId) {
|
|
|
42
43
|
const session = db.prepare(`SELECT id, model, created_at FROM sessions WHERE id = ?`).get(sessionId);
|
|
43
44
|
if (!session)
|
|
44
45
|
throw new Error(`Session not found: ${sessionId}`);
|
|
46
|
+
const chain = getSessionChain(sessionId);
|
|
47
|
+
const placeholders = chain.map(() => "?").join(",");
|
|
45
48
|
const rows = db
|
|
46
49
|
.prepare(`
|
|
47
50
|
SELECT id, source, model, message_seq, input_tokens, output_tokens,
|
|
48
51
|
cache_read_tokens, cache_creation_tokens, cost_micros, created_at,
|
|
49
52
|
provider_options_shape
|
|
50
53
|
FROM usage_events
|
|
51
|
-
WHERE session_id
|
|
54
|
+
WHERE session_id IN (${placeholders})
|
|
52
55
|
ORDER BY id ASC
|
|
53
56
|
`)
|
|
54
|
-
.all(
|
|
57
|
+
.all(...chain);
|
|
55
58
|
const counts = db
|
|
56
59
|
.prepare(`
|
|
57
60
|
SELECT event_type, COUNT(*) AS c
|
|
58
61
|
FROM interaction_logs
|
|
59
|
-
WHERE session_id
|
|
62
|
+
WHERE session_id IN (${placeholders})
|
|
60
63
|
GROUP BY event_type
|
|
61
64
|
`)
|
|
62
|
-
.all(
|
|
65
|
+
.all(...chain);
|
|
63
66
|
const countMap = new Map(counts.map((r) => [r.event_type, r.c]));
|
|
64
67
|
const events = rows.map((r) => ({
|
|
65
68
|
id: r.id,
|
package/dist/src/cli/keys.d.ts
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* keys delete <provider> — remove a stored key
|
|
16
16
|
* keys import-bw [providers] — pull from Bitwarden vault, store in keychain
|
|
17
17
|
* keys cleanup-settings — strip plaintext keys from user-settings.json
|
|
18
|
-
* keys login <provider> — OAuth login (openai, google)
|
|
19
|
-
* keys logout <provider> — OAuth logout (openai, google)
|
|
18
|
+
* keys login <provider> — OAuth login (openai, google/agy)
|
|
19
|
+
* keys logout <provider> — OAuth logout (openai, google/agy)
|
|
20
20
|
*/
|
|
21
21
|
import { type ChatSecretId } from "../chat/chat-keychain.js";
|
|
22
22
|
export interface KeysSetOptions {
|