muonroi-cli 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +122 -122
- package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
- package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
- package/dist/packages/agent-harness-core/src/selector.js +2 -2
- package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
- package/dist/packages/agent-harness-opentui/src/install.js +1 -1
- package/dist/src/__test-stubs__/vitest-setup.js +4 -0
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
- package/dist/src/agent-harness/mock-model.d.ts +1 -1
- package/dist/src/agent-harness/mock-model.js +32 -3
- package/dist/src/agent-harness/test-spawn.js +3 -1
- package/dist/src/chat/chat-keychain.js +9 -1
- package/dist/src/cli/cost-forensics.js +12 -12
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +107 -28
- package/dist/src/cli/usage-report.js +2 -2
- package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
- package/dist/src/council/__tests__/abort-threading.test.js +193 -0
- package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
- package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
- package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
- package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
- package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
- package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
- package/dist/src/council/__tests__/research-tools.test.js +4 -0
- package/dist/src/council/__tests__/round-tools.test.js +6 -0
- package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
- package/dist/src/council/clarifier-question-cap.test.js +73 -0
- package/dist/src/council/clarifier.d.ts +29 -1
- package/dist/src/council/clarifier.js +64 -1
- package/dist/src/council/debate-planner.d.ts +2 -1
- package/dist/src/council/debate-planner.js +5 -3
- package/dist/src/council/debate.d.ts +9 -0
- package/dist/src/council/debate.js +88 -8
- package/dist/src/council/index.d.ts +46 -1
- package/dist/src/council/index.js +125 -19
- package/dist/src/council/llm.js +41 -13
- package/dist/src/council/prompts.js +56 -21
- package/dist/src/council/types.d.ts +14 -1
- package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
- package/dist/src/ee/bridge.d.ts +3 -1
- package/dist/src/ee/bridge.js +9 -3
- package/dist/src/ee/bridge.test.js +1 -2
- package/dist/src/ee/client.js +15 -2
- package/dist/src/ee/recall-ledger.d.ts +20 -0
- package/dist/src/ee/recall-ledger.js +40 -1
- package/dist/src/ee/search.d.ts +25 -0
- package/dist/src/ee/search.js +73 -0
- package/dist/src/ee/types.d.ts +15 -1
- package/dist/src/ee/who-am-i.d.ts +41 -0
- package/dist/src/ee/who-am-i.js +143 -0
- package/dist/src/ee/who-am-i.test.d.ts +1 -0
- package/dist/src/ee/who-am-i.test.js +101 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/council-answers.js +1 -0
- package/dist/src/headless/output.d.ts +30 -1
- package/dist/src/headless/output.js +180 -0
- package/dist/src/headless/output.test.js +61 -1
- package/dist/src/hooks/index.js +48 -2
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +143 -104
- package/dist/src/lsp/client-normalize.test.d.ts +12 -0
- package/dist/src/lsp/client-normalize.test.js +34 -0
- package/dist/src/lsp/client.d.ts +1 -0
- package/dist/src/lsp/client.js +10 -2
- package/dist/src/lsp/manager.d.ts +1 -1
- package/dist/src/lsp/manager.js +2 -2
- package/dist/src/lsp/manager.test.js +2 -2
- package/dist/src/lsp/runtime.d.ts +1 -1
- package/dist/src/lsp/runtime.js +2 -2
- package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
- package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
- package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
- package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
- package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
- package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
- package/dist/src/mcp/client-pool.d.ts +1 -1
- package/dist/src/mcp/client-pool.js +25 -9
- package/dist/src/mcp/ee-tools.d.ts +10 -0
- package/dist/src/mcp/ee-tools.js +34 -0
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/setup-guide-text.js +74 -74
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/ops/doctor.js +7 -7
- package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
- package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +2 -0
- package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
- package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
- package/dist/src/orchestrator/agent.test.js +5 -2
- package/dist/src/orchestrator/auto-commit.d.ts +110 -0
- package/dist/src/orchestrator/auto-commit.js +364 -0
- package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
- package/dist/src/orchestrator/batch-turn-runner.js +2 -1
- package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
- package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
- package/dist/src/orchestrator/compaction.d.ts +22 -0
- package/dist/src/orchestrator/compaction.js +42 -0
- package/dist/src/orchestrator/delegations.d.ts +1 -0
- package/dist/src/orchestrator/delegations.js +41 -0
- package/dist/src/orchestrator/error-utils.js +27 -2
- package/dist/src/orchestrator/message-processor.d.ts +21 -5
- package/dist/src/orchestrator/message-processor.js +219 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +8 -2
- package/dist/src/orchestrator/orchestrator.js +187 -34
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +145 -0
- package/dist/src/orchestrator/prompts.d.ts +8 -0
- package/dist/src/orchestrator/prompts.js +76 -60
- package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
- package/dist/src/orchestrator/safety-askcard.js +74 -0
- package/dist/src/orchestrator/sandbox.test.js +8 -5
- package/dist/src/orchestrator/scope-ceiling.js +1 -1
- package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
- package/dist/src/orchestrator/stall-watchdog.js +23 -0
- package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
- package/dist/src/orchestrator/stream-runner.js +52 -30
- package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
- package/dist/src/orchestrator/subagent-compactor.js +83 -10
- package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
- package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
- package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
- package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
- package/dist/src/orchestrator/tool-args-repair.js +1 -1
- package/dist/src/orchestrator/tool-engine.d.ts +151 -0
- package/dist/src/orchestrator/tool-engine.js +3079 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -0
- package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
- package/dist/src/pil/__tests__/discovery.test.js +89 -145
- package/dist/src/pil/__tests__/dual-run.test.js +2 -0
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
- package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
- package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
- package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
- package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
- package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
- package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
- package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
- package/dist/src/pil/__tests__/pipeline.test.js +11 -8
- package/dist/src/pil/__tests__/response-tools.test.js +52 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +5 -3
- package/dist/src/pil/agent-operating-contract.test.js +2 -2
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +1 -1
- package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
- package/dist/src/pil/cheap-model-workbooks.js +1 -1
- package/dist/src/pil/config.d.ts +20 -0
- package/dist/src/pil/config.js +35 -0
- package/dist/src/pil/discovery-types.d.ts +32 -9
- package/dist/src/pil/discovery.d.ts +3 -3
- package/dist/src/pil/discovery.js +138 -244
- package/dist/src/pil/index.d.ts +1 -1
- package/dist/src/pil/index.js +1 -1
- package/dist/src/pil/layer1-intent.d.ts +9 -1
- package/dist/src/pil/layer1-intent.js +99 -44
- package/dist/src/pil/layer1-intent.test.js +112 -2
- package/dist/src/pil/layer15-context-scan.js +1 -1
- package/dist/src/pil/layer16-clarity.d.ts +21 -25
- package/dist/src/pil/layer16-clarity.js +52 -88
- package/dist/src/pil/layer18-acceptance.d.ts +13 -3
- package/dist/src/pil/layer18-acceptance.js +1 -75
- package/dist/src/pil/layer2-personality.js +7 -8
- package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
- package/dist/src/pil/layer2_5-ponytail.js +41 -0
- package/dist/src/pil/layer3-ee-injection.js +180 -14
- package/dist/src/pil/layer4-gsd.js +22 -8
- package/dist/src/pil/layer5-context.d.ts +1 -0
- package/dist/src/pil/layer5-context.js +26 -11
- package/dist/src/pil/layer6-output.js +23 -5
- package/dist/src/pil/llm-classify.js +3 -3
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +4 -3
- package/dist/src/pil/pipeline.js +15 -1
- package/dist/src/pil/response-tools.d.ts +16 -0
- package/dist/src/pil/response-tools.js +41 -0
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +24 -3
- package/dist/src/pil/task-tier-map.js +7 -7
- package/dist/src/pil/types.d.ts +9 -3
- package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
- package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
- package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
- package/dist/src/product-loop/discovery-interview.d.ts +20 -0
- package/dist/src/product-loop/discovery-interview.js +121 -7
- package/dist/src/product-loop/done-gate.js +3 -3
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +20 -20
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- package/dist/src/product-loop/sprint-self-verify.js +2 -2
- package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
- package/dist/src/providers/auth/gcloud.d.ts +28 -0
- package/dist/src/providers/auth/gcloud.js +102 -0
- package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
- package/dist/src/providers/auth/gemini-oauth.js +292 -105
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
- package/dist/src/providers/auth/grok-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +28 -6
- package/dist/src/providers/gemini.d.ts +1 -1
- package/dist/src/providers/gemini.js +10 -3
- package/dist/src/providers/keychain.d.ts +2 -2
- package/dist/src/providers/keychain.js +20 -2
- package/dist/src/providers/mcp-vision-bridge.js +48 -48
- package/dist/src/providers/openai.js +5 -1
- package/dist/src/providers/runtime.d.ts +1 -1
- package/dist/src/providers/runtime.js +11 -2
- package/dist/src/providers/siliconflow-sse-repair.js +1 -1
- package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
- package/dist/src/providers/strategies/google.strategy.js +159 -9
- package/dist/src/providers/strategies/thinking-mode.js +19 -6
- package/dist/src/providers/types.d.ts +1 -1
- package/dist/src/reporter/index.js +1 -1
- package/dist/src/router/decide.test.js +40 -8
- package/dist/src/router/step-router.d.ts +20 -0
- package/dist/src/router/step-router.js +73 -0
- package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
- package/dist/src/self-qa/agentic-loop.js +19 -19
- package/dist/src/self-qa/orchestrator.js +5 -5
- package/dist/src/self-qa/scenario-planner.js +1 -1
- package/dist/src/self-qa/spec-emitter.js +4 -4
- package/dist/src/storage/__tests__/migrations.test.js +2 -2
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.d.ts +6 -0
- package/dist/src/storage/sessions.js +49 -42
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +1 -0
- package/dist/src/storage/transcript.js +55 -0
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/tools/__tests__/native-tools.test.js +6 -0
- package/dist/src/tools/bash.d.ts +36 -23
- package/dist/src/tools/bash.js +59 -301
- package/dist/src/tools/bash.test.js +2 -257
- package/dist/src/tools/file-tracker.d.ts +5 -0
- package/dist/src/tools/file-tracker.js +14 -0
- package/dist/src/tools/file.js +2 -0
- package/dist/src/tools/git-safety.d.ts +38 -1
- package/dist/src/tools/git-safety.js +94 -4
- package/dist/src/tools/git-safety.test.js +45 -1
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +56 -0
- package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
- package/dist/src/tools/registry-git-safety.test.js +17 -6
- package/dist/src/tools/registry.d.ts +5 -0
- package/dist/src/tools/registry.js +185 -11
- package/dist/src/types/index.d.ts +7 -1
- package/dist/src/ui/app.d.ts +2 -1
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.js +37 -3
- package/dist/src/ui/components/structured-response-view.js +7 -0
- package/dist/src/ui/components/task-list-panel.js +3 -12
- package/dist/src/ui/containers/chat-feed.d.ts +40 -0
- package/dist/src/ui/containers/chat-feed.js +66 -0
- package/dist/src/ui/containers/modals-layer.d.ts +87 -0
- package/dist/src/ui/containers/modals-layer.js +18 -0
- package/dist/src/ui/modals/session-picker-modal.js +14 -9
- package/dist/src/ui/modals/update-modal.js +2 -1
- package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/slash/export.js +16 -1
- package/dist/src/ui/slash/menu-items.js +1 -0
- package/dist/src/ui/slash/ponytail.d.ts +2 -0
- package/dist/src/ui/slash/ponytail.js +19 -0
- package/dist/src/ui/status-bar/store.js +0 -1
- package/dist/src/ui/status-bar/store.test.js +2 -2
- package/dist/src/ui/types.d.ts +7 -0
- package/dist/src/ui/use-app-logic.d.ts +26 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/relaunch.d.ts +17 -0
- package/dist/src/ui/utils/relaunch.js +28 -4
- package/dist/src/ui/utils/relaunch.test.js +50 -0
- package/dist/src/ui/utils/text.d.ts +11 -0
- package/dist/src/ui/utils/text.js +21 -1
- package/dist/src/ui/utils/text.test.js +25 -1
- package/dist/src/ui/utils/tools.js +3 -1
- package/dist/src/usage/cost-log.js +2 -2
- package/dist/src/usage/estimator.d.ts +14 -0
- package/dist/src/usage/estimator.js +22 -0
- package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
- package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- package/dist/src/utils/permission-mode.d.ts +33 -0
- package/dist/src/utils/permission-mode.js +164 -3
- package/dist/src/utils/permission-mode.test.js +92 -1
- package/dist/src/utils/settings.d.ts +27 -31
- package/dist/src/utils/settings.js +100 -109
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/package.json +1 -1
package/dist/src/hooks/index.js
CHANGED
|
@@ -33,10 +33,14 @@ let _cachedScope = null;
|
|
|
33
33
|
// Latch: stores the last PreToolUse warning response so PostToolUse can build judgeCtx.
|
|
34
34
|
// Follows the _cachedScope module-level variable pattern.
|
|
35
35
|
let _lastWarningResponse = null;
|
|
36
|
+
// Fix 2: throttle recall-feedback reminder to once per unique pending-set.
|
|
37
|
+
// Tracks the sha of pending IDs so identical reminders don't repeat on every tool call.
|
|
38
|
+
let _lastRecallReminderSha = null;
|
|
36
39
|
/** Reset hook module state — for test teardown only. */
|
|
37
40
|
export function resetHookState() {
|
|
38
41
|
_lastWarningResponse = null;
|
|
39
42
|
_cachedScope = null;
|
|
43
|
+
_lastRecallReminderSha = null;
|
|
40
44
|
}
|
|
41
45
|
function emptyResult() {
|
|
42
46
|
return {
|
|
@@ -200,13 +204,51 @@ export async function executeEventHooks(input, cwd, _signal) {
|
|
|
200
204
|
why: m.why,
|
|
201
205
|
confidence: m.confidence,
|
|
202
206
|
}));
|
|
207
|
+
// Hard EE recall-feedback gate: inject mandatory reminder when there are
|
|
208
|
+
// pending unrated hints. The agent MUST clear all pending ee_feedback before
|
|
209
|
+
// proceeding with the task — system prompt in message-processor.ts enforces
|
|
210
|
+
// this as a first-before-anything directive.
|
|
211
|
+
//
|
|
212
|
+
// Fix 2: throttle to once per unique pending-set. The same 9 hints repeating
|
|
213
|
+
// after each of 10+ tool calls is pure UI noise — the model can't even see
|
|
214
|
+
// yield-content, and the user must scroll past identical blocks. Show the
|
|
215
|
+
// reminder ONCE when the set first appears or changes (e.g. after ee_feedback
|
|
216
|
+
// clears some entries), then stay silent until the set changes again.
|
|
217
|
+
let recallReminder = null;
|
|
218
|
+
try {
|
|
219
|
+
const { sessionRecallLedger, isRecallLedgerEnabled, formatPendingReminder } = await import("../ee/recall-ledger.js");
|
|
220
|
+
if (isRecallLedgerEnabled()) {
|
|
221
|
+
const pending = sessionRecallLedger.pending();
|
|
222
|
+
if (pending.length > 0) {
|
|
223
|
+
const pendingSha = pending
|
|
224
|
+
.map((p) => p.id)
|
|
225
|
+
.sort()
|
|
226
|
+
.join(",");
|
|
227
|
+
if (pendingSha !== _lastRecallReminderSha) {
|
|
228
|
+
_lastRecallReminderSha = pendingSha;
|
|
229
|
+
recallReminder = `⚠️ MANDATORY: ${pending.length} unrated EE recall(s) — call ee_feedback NOW before continuing.\n${formatPendingReminder(pending, { max: 5 })}`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
// All cleared — reset so next batch shows fresh
|
|
234
|
+
_lastRecallReminderSha = null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
/* fail-open */
|
|
240
|
+
}
|
|
203
241
|
if (r.decision === "block") {
|
|
204
242
|
return {
|
|
205
243
|
blocked: true,
|
|
206
244
|
blockingErrors: [{ command: "ee:intercept", stderr: r.reason ?? "ee-blocked" }],
|
|
207
245
|
preventContinuation: true,
|
|
208
246
|
stopReason: r.reason ?? "ee-blocked",
|
|
209
|
-
additionalContexts: [
|
|
247
|
+
additionalContexts: [
|
|
248
|
+
...capturedWarnings,
|
|
249
|
+
...(r.suggestions ?? []),
|
|
250
|
+
...(recallReminder ? [recallReminder] : []),
|
|
251
|
+
],
|
|
210
252
|
decision: "block",
|
|
211
253
|
results: [],
|
|
212
254
|
eeMatches,
|
|
@@ -216,7 +258,11 @@ export async function executeEventHooks(input, cwd, _signal) {
|
|
|
216
258
|
blocked: false,
|
|
217
259
|
blockingErrors: [],
|
|
218
260
|
preventContinuation: false,
|
|
219
|
-
additionalContexts: [
|
|
261
|
+
additionalContexts: [
|
|
262
|
+
...capturedWarnings,
|
|
263
|
+
...(r.suggestions ?? []),
|
|
264
|
+
...(recallReminder ? [recallReminder] : []),
|
|
265
|
+
],
|
|
220
266
|
decision: "approve",
|
|
221
267
|
results: [],
|
|
222
268
|
eeMatches,
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import type { StreamChunk } from "./types/index.js";
|
|
3
|
+
export declare function setTuiActive(active: boolean): void;
|
|
4
|
+
export declare function appendCrashLog(label: string, msg: string): void;
|
|
3
5
|
export declare function setActiveEeYield(fn: ((chunk: StreamChunk) => void) | null): void;
|
package/dist/src/index.js
CHANGED
|
@@ -17,7 +17,7 @@ const packageJson = { version: PACKAGE_VERSION, description: PACKAGE_DESCRIPTION
|
|
|
17
17
|
import { hydrateChatEnvFromKeychain } from "./chat/chat-keychain.js";
|
|
18
18
|
import { setRenderSink } from "./ee/render.js";
|
|
19
19
|
import { createHeadlessCouncilAutoAnswerer, handleCouncilChunk, parseCouncilAnswersFile, } from "./headless/council-answers.js";
|
|
20
|
-
import { createHeadlessJsonlEmitter,
|
|
20
|
+
import { createHeadlessJsonlEmitter, createHeadlessTextEmitter, isHeadlessOutputFormat, renderHeadlessPrelude, } from "./headless/output.js";
|
|
21
21
|
import { loadCatalog, normalizeModelId } from "./models/registry.js";
|
|
22
22
|
// Plan 00-07: boot-order modules — AbortContext + PendingCallsLog (TUI-01, TUI-03, TUI-04).
|
|
23
23
|
import { createAbortContext } from "./orchestrator/abort.js";
|
|
@@ -32,7 +32,7 @@ import { loadUsage } from "./storage/usage-cap.js";
|
|
|
32
32
|
import { startScheduleDaemon } from "./tools/schedule.js";
|
|
33
33
|
import { processAtMentions } from "./utils/at-mentions.js";
|
|
34
34
|
import { runScriptManagedUninstall } from "./utils/install-manager.js";
|
|
35
|
-
import { getApiKey, getBaseURL, getCurrentModel,
|
|
35
|
+
import { getApiKey, getBaseURL, getCurrentModel, saveUserSettings } from "./utils/settings.js";
|
|
36
36
|
import { runUpdate } from "./utils/update-checker.js";
|
|
37
37
|
import { buildVerifyPrompt, getVerifyCliError } from "./verify/entrypoint.js";
|
|
38
38
|
// Hydrate chat secrets from OS keychain before CLI bootstrap
|
|
@@ -41,20 +41,40 @@ const exitCleanlyOnSigterm = () => {
|
|
|
41
41
|
process.exit(0);
|
|
42
42
|
};
|
|
43
43
|
process.on("SIGTERM", exitCleanlyOnSigterm);
|
|
44
|
-
|
|
44
|
+
// Set true while the interactive TUI owns the terminal (raw mode + alternate
|
|
45
|
+
// screen). When it is mounted, writing to stdout/stderr corrupts OpenTUI's
|
|
46
|
+
// framebuffer and a process.exit(1) tears the whole UI down — surfacing to the
|
|
47
|
+
// user as "kicked out of the TUI". A single stray unhandledRejection (e.g. a
|
|
48
|
+
// slash handler whose DB read throws and rejects with no .catch) must not be
|
|
49
|
+
// allowed to do that; while _tuiActive is set we log to crash.log and keep the
|
|
50
|
+
// process alive. Headless / CLI paths keep the original fail-fast behaviour.
|
|
51
|
+
let _tuiActive = false;
|
|
52
|
+
export function setTuiActive(active) {
|
|
53
|
+
_tuiActive = active;
|
|
54
|
+
}
|
|
55
|
+
export function appendCrashLog(label, msg) {
|
|
45
56
|
try {
|
|
46
|
-
require("fs").appendFileSync(require("path").join(require("os").homedir(), ".muonroi-cli", "crash.log"), `[${new Date().toISOString()}]
|
|
57
|
+
require("fs").appendFileSync(require("path").join(require("os").homedir(), ".muonroi-cli", "crash.log"), `[${new Date().toISOString()}] ${label}: ${msg}\n`);
|
|
47
58
|
}
|
|
48
|
-
catch {
|
|
59
|
+
catch {
|
|
60
|
+
/* crash.log is best-effort diagnostics; the logger itself must never throw */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
process.on("uncaughtException", (err) => {
|
|
64
|
+
appendCrashLog("UNCAUGHT", err.stack || err.message);
|
|
49
65
|
console.error("Fatal:", err.message);
|
|
50
66
|
process.exit(1);
|
|
51
67
|
});
|
|
52
68
|
process.on("unhandledRejection", (reason) => {
|
|
53
69
|
const msg = reason instanceof Error ? reason.stack || reason.message : String(reason);
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
appendCrashLog("REJECTION", msg);
|
|
71
|
+
// TUI mounted → do NOT corrupt the framebuffer with console.error and do NOT
|
|
72
|
+
// exit. The rejection is logged; the renderer stays up so the user keeps
|
|
73
|
+
// their session (see slash-dispatch .catch handlers in app.tsx, which also
|
|
74
|
+
// surface the failure in-band).
|
|
75
|
+
if (_tuiActive) {
|
|
76
|
+
return;
|
|
56
77
|
}
|
|
57
|
-
catch { }
|
|
58
78
|
if (reason instanceof Error) {
|
|
59
79
|
console.error("Unhandled rejection:", reason.stack || reason.message);
|
|
60
80
|
}
|
|
@@ -314,7 +334,7 @@ async function firstRunWizard(currentModel) {
|
|
|
314
334
|
return null;
|
|
315
335
|
}
|
|
316
336
|
}
|
|
317
|
-
async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
337
|
+
async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi, session, initialMessage, permissionMode = "safe", injectHalt = false) {
|
|
318
338
|
// ── Plan 00-07 boot order ──────────────────────────────────────────────────
|
|
319
339
|
// 1. redactor.installGlobalPatches() — already at top of file (line 6).
|
|
320
340
|
// 2. loadConfig + loadUsage (validates storage paths, logs usage cap state).
|
|
@@ -377,10 +397,11 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
377
397
|
if (reconciled.abandoned > 0) {
|
|
378
398
|
console.warn(`[muonroi-cli] reconciled ${reconciled.abandoned} abandoned tool calls from prior session`);
|
|
379
399
|
}
|
|
400
|
+
const { warmMcpClients } = await import("./mcp/client-pool.js");
|
|
401
|
+
const { loadMcpServers } = await import("./utils/settings.js");
|
|
402
|
+
await warmMcpClients(loadMcpServers(), !session);
|
|
380
403
|
const agent = new Agent(apiKey, baseURL, model, maxToolRounds, {
|
|
381
404
|
session,
|
|
382
|
-
sandboxMode,
|
|
383
|
-
sandboxSettings,
|
|
384
405
|
batchApi,
|
|
385
406
|
abortContext: orchestratorAbort,
|
|
386
407
|
pendingCalls,
|
|
@@ -391,6 +412,7 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
391
412
|
const { createRoot } = await import("@opentui/react");
|
|
392
413
|
const { createElement } = await import("react");
|
|
393
414
|
const { App } = await import("./ui/app.js");
|
|
415
|
+
const { relaunchWithSession } = await import("./ui/utils/relaunch.js");
|
|
394
416
|
const renderer = await createCliRenderer({
|
|
395
417
|
exitOnCtrlC: false,
|
|
396
418
|
// We manage SIGINT ourselves (orchestrator abort → agent cleanup → renderer destroy).
|
|
@@ -403,6 +425,10 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
403
425
|
alternateKeys: true,
|
|
404
426
|
},
|
|
405
427
|
});
|
|
428
|
+
// The TUI now owns the terminal — route fatal-handler behaviour away from
|
|
429
|
+
// process.exit(1) (see the unhandledRejection handler above). Cleared in
|
|
430
|
+
// restoreTerminalSync() the moment we begin tearing the terminal back down.
|
|
431
|
+
setTuiActive(true);
|
|
406
432
|
/**
|
|
407
433
|
* Restore terminal to main-screen mode before the process exits.
|
|
408
434
|
*
|
|
@@ -417,6 +443,9 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
417
443
|
* flush delay before process.exit().
|
|
418
444
|
*/
|
|
419
445
|
function restoreTerminalSync() {
|
|
446
|
+
// Terminal is being handed back — restore fail-fast crash behaviour so a
|
|
447
|
+
// throw during/after teardown still exits cleanly instead of hanging.
|
|
448
|
+
setTuiActive(false);
|
|
420
449
|
try {
|
|
421
450
|
// 1. Disable Kitty keyboard protocol if enabled.
|
|
422
451
|
if (typeof renderer.disableKittyKeyboard === "function") {
|
|
@@ -441,64 +470,106 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
441
470
|
process.env.TERM_PROGRAM === "WezTerm" ||
|
|
442
471
|
process.env.TERM_PROGRAM === "wezterm" ||
|
|
443
472
|
process.env.MUONROI_FORCE_SHELL_HOLD === "1";
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
/* fall back to async */
|
|
460
|
-
try {
|
|
461
|
-
process.stdout.write(seq);
|
|
462
|
-
}
|
|
463
|
-
catch {
|
|
464
|
-
/* noop */
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
};
|
|
473
|
+
// Restore the terminal to a clean main-screen state: undo Kitty keyboard,
|
|
474
|
+
// raw mode, mouse tracking, bracketed paste, and alt-screen. Shared by the
|
|
475
|
+
// /exit path AND the session-resume relaunch — a relaunch that skips this
|
|
476
|
+
// leaves the child (and, once the parent exits, the shell) with a terminal
|
|
477
|
+
// still in mouse-tracking/alt-screen mode, so stray escape bytes get parsed
|
|
478
|
+
// by the shell as a bogus command ("'…' is not recognized … operable
|
|
479
|
+
// program or batch file").
|
|
480
|
+
function restoreTerminalForHandoff() {
|
|
481
|
+
// Restore terminal state from JS before the native destroyRenderer runs
|
|
482
|
+
restoreTerminalSync();
|
|
483
|
+
renderer.destroy();
|
|
484
|
+
// Use synchronous writes to stdout's underlying fd so escape codes flush
|
|
485
|
+
// before the child inherits stdin — async process.stdout.write() can buffer
|
|
486
|
+
// past the spawn() boundary, leaving the terminal in mouse-tracking mode.
|
|
487
|
+
const writeSync = (seq) => {
|
|
468
488
|
try {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
catch {
|
|
476
|
-
/* noop */
|
|
477
|
-
}
|
|
478
|
-
}
|
|
489
|
+
const fs = require("node:fs");
|
|
490
|
+
fs.writeSync(1, seq);
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
/* fall back to async */
|
|
479
494
|
try {
|
|
480
|
-
process.
|
|
495
|
+
process.stdout.write(seq);
|
|
481
496
|
}
|
|
482
497
|
catch {
|
|
483
498
|
/* noop */
|
|
484
499
|
}
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
try {
|
|
503
|
+
// 1. Take stdin out of raw mode + pause it so no buffered keystrokes
|
|
504
|
+
// (or in-flight mouse-event bytes) hit the child.
|
|
505
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
485
506
|
try {
|
|
486
|
-
process.stdin.
|
|
507
|
+
process.stdin.setRawMode(false);
|
|
487
508
|
}
|
|
488
509
|
catch {
|
|
489
510
|
/* noop */
|
|
490
511
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
writeSync("\x1B[?1006l\x1B[?1015l\x1B[?1005l");
|
|
495
|
-
writeSync("\x1B[?1003l\x1B[?1002l\x1B[?1000l");
|
|
496
|
-
writeSync("\x1B[?2004l\x1B[?25h"); // bracketed paste off, cursor on
|
|
497
|
-
writeSync("\x1B[?1049l\x1B[0m\x1B[!p"); // exit alt-screen, reset SGR, soft reset
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
process.stdin.pause();
|
|
498
515
|
}
|
|
499
516
|
catch {
|
|
500
|
-
|
|
517
|
+
/* noop */
|
|
501
518
|
}
|
|
519
|
+
try {
|
|
520
|
+
process.stdin.unref?.();
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
/* noop */
|
|
524
|
+
}
|
|
525
|
+
// 2. Disable extended-coords first (1006/1015/1005), then the basic
|
|
526
|
+
// tracking modes (1003→1002→1000). Wrong order leaves the terminal
|
|
527
|
+
// emitting SGR-formatted events after the base modes are off.
|
|
528
|
+
writeSync("\x1B[?1006l\x1B[?1015l\x1B[?1005l");
|
|
529
|
+
writeSync("\x1B[?1003l\x1B[?1002l\x1B[?1000l");
|
|
530
|
+
writeSync("\x1B[?2004l\x1B[?25h"); // bracketed paste off, cursor on
|
|
531
|
+
writeSync("\x1B[?1049l\x1B[0m\x1B[!p"); // exit alt-screen, reset SGR, soft reset
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
// best-effort
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Resume a different session by restarting the CLI bound to --session <id>.
|
|
538
|
+
// Routes through the SAME teardown as /exit, then supervises the child so the
|
|
539
|
+
// shell never reclaims the foreground mid-restart (which corrupted the
|
|
540
|
+
// terminal and dropped the user back to a broken prompt).
|
|
541
|
+
const onRelaunch = (sessionId) => {
|
|
542
|
+
void agent.cleanup().finally(() => {
|
|
543
|
+
restoreTerminalForHandoff();
|
|
544
|
+
// Let the terminal process the restore sequences before the child takes
|
|
545
|
+
// over stdin (mirrors the /exit shell-hold timing).
|
|
546
|
+
setTimeout(() => {
|
|
547
|
+
try {
|
|
548
|
+
relaunchWithSession(sessionId, { supervise: true });
|
|
549
|
+
}
|
|
550
|
+
catch (err) {
|
|
551
|
+
console.error(`[relaunch] failed: ${err?.message ?? err}`);
|
|
552
|
+
process.exit(1);
|
|
553
|
+
}
|
|
554
|
+
}, 80);
|
|
555
|
+
});
|
|
556
|
+
};
|
|
557
|
+
const onExit = () => {
|
|
558
|
+
void agent.cleanup().finally(() => {
|
|
559
|
+
restoreTerminalForHandoff();
|
|
560
|
+
const writeSync = (seq) => {
|
|
561
|
+
try {
|
|
562
|
+
require("node:fs").writeSync(1, seq);
|
|
563
|
+
}
|
|
564
|
+
catch {
|
|
565
|
+
try {
|
|
566
|
+
process.stdout.write(seq);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
/* noop */
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
};
|
|
502
573
|
// WezTerm (and similar single-pane terminals) closes the window when
|
|
503
574
|
// the foreground process exits. To keep the pane usable after `/exit`,
|
|
504
575
|
// we hand control over to an interactive shell instead of exiting.
|
|
@@ -539,21 +610,20 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
539
610
|
apiKey,
|
|
540
611
|
baseURL,
|
|
541
612
|
model: agent.getModel(),
|
|
613
|
+
sandboxMode: "off",
|
|
614
|
+
sandboxSettings: {},
|
|
542
615
|
maxToolRounds,
|
|
543
|
-
sandboxMode,
|
|
544
|
-
sandboxSettings,
|
|
545
616
|
version: packageJson.version,
|
|
546
617
|
injectHalt,
|
|
547
618
|
},
|
|
548
619
|
initialMessage,
|
|
549
620
|
onExit,
|
|
621
|
+
onRelaunch,
|
|
550
622
|
}));
|
|
551
623
|
}
|
|
552
|
-
async function runHeadless(prompt, apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
624
|
+
async function runHeadless(prompt, apiKey, baseURL, model, maxToolRounds, batchApi, format, session, permissionMode = "safe", councilAutoAnswer) {
|
|
553
625
|
const agent = new Agent(apiKey, baseURL, model, maxToolRounds, {
|
|
554
626
|
session,
|
|
555
|
-
sandboxMode,
|
|
556
|
-
sandboxSettings,
|
|
557
627
|
batchApi,
|
|
558
628
|
permissionMode,
|
|
559
629
|
});
|
|
@@ -603,14 +673,20 @@ async function runHeadless(prompt, apiKey, baseURL, model, maxToolRounds, batchA
|
|
|
603
673
|
writeSafe(process.stderr, tail.stderr ?? "");
|
|
604
674
|
return;
|
|
605
675
|
}
|
|
676
|
+
const textEmitter = createHeadlessTextEmitter();
|
|
606
677
|
for await (const chunk of agent.processMessage(enhancedMessage)) {
|
|
607
678
|
maybeAutoAnswer(chunk);
|
|
608
|
-
const writes =
|
|
679
|
+
const writes = textEmitter.consumeChunk(chunk);
|
|
609
680
|
if (writes.stdout)
|
|
610
681
|
writeSafe(process.stdout, writes.stdout);
|
|
611
682
|
if (writes.stderr)
|
|
612
683
|
writeSafe(process.stderr, writes.stderr);
|
|
613
684
|
}
|
|
685
|
+
const textTail = textEmitter.flush();
|
|
686
|
+
if (textTail.stdout)
|
|
687
|
+
writeSafe(process.stdout, textTail.stdout);
|
|
688
|
+
if (textTail.stderr)
|
|
689
|
+
writeSafe(process.stderr, textTail.stderr ?? "");
|
|
614
690
|
}
|
|
615
691
|
finally {
|
|
616
692
|
await agent.cleanup();
|
|
@@ -646,13 +722,6 @@ function stringOption(value) {
|
|
|
646
722
|
function collect(value, prev) {
|
|
647
723
|
return [...prev, value];
|
|
648
724
|
}
|
|
649
|
-
function resolveCliSandboxMode(value) {
|
|
650
|
-
if (value === true)
|
|
651
|
-
return "shuru";
|
|
652
|
-
if (value === false)
|
|
653
|
-
return "off";
|
|
654
|
-
return undefined;
|
|
655
|
-
}
|
|
656
725
|
async function runBackgroundDelegation(jobPath, options) {
|
|
657
726
|
let output = "";
|
|
658
727
|
let agent;
|
|
@@ -680,12 +749,8 @@ async function runBackgroundDelegation(jobPath, options) {
|
|
|
680
749
|
"or run 'muonroi-cli keys login <provider>' for subscription OAuth.");
|
|
681
750
|
}
|
|
682
751
|
const maxToolRounds = parseInt(stringOption(options.maxToolRounds) || String(delegation.maxToolRounds), 10) || delegation.maxToolRounds;
|
|
683
|
-
const sandboxMode = resolveCliSandboxMode(options.sandbox) || delegation.sandboxMode || getCurrentSandboxMode();
|
|
684
|
-
const sandboxSettings = mergeSandboxSettings(getCurrentSandboxSettings(), delegation.sandboxSettings);
|
|
685
752
|
agent = new Agent(apiKey, baseURL, model, maxToolRounds, {
|
|
686
753
|
persistSession: false,
|
|
687
|
-
sandboxMode,
|
|
688
|
-
sandboxSettings,
|
|
689
754
|
batchApi: Boolean(delegation.batchApi ?? options.batchApi === true),
|
|
690
755
|
});
|
|
691
756
|
const result = await agent.runTaskRequest({
|
|
@@ -734,22 +799,7 @@ function resolveConfig(options) {
|
|
|
734
799
|
// work; runaway loops abort with the "Reached max tool rounds" error
|
|
735
800
|
// surface so the user can adjust scope. Override via --max-tool-rounds CLI
|
|
736
801
|
// flag or MUONROI_MAX_TOOL_ROUNDS env.
|
|
737
|
-
const maxToolRounds = parseInt(stringOption(options.maxToolRounds) || "
|
|
738
|
-
const sandboxMode = resolveCliSandboxMode(options.sandbox) || getCurrentSandboxMode();
|
|
739
|
-
const cliOverrides = {};
|
|
740
|
-
if (options.allowNet === true)
|
|
741
|
-
cliOverrides.allowNet = true;
|
|
742
|
-
const allowHostValue = options.allowHost;
|
|
743
|
-
if (Array.isArray(allowHostValue) && allowHostValue.length > 0) {
|
|
744
|
-
cliOverrides.allowedHosts = allowHostValue;
|
|
745
|
-
if (!cliOverrides.allowNet)
|
|
746
|
-
cliOverrides.allowNet = true;
|
|
747
|
-
}
|
|
748
|
-
const portValue = options.port;
|
|
749
|
-
if (Array.isArray(portValue) && portValue.length > 0) {
|
|
750
|
-
cliOverrides.ports = portValue;
|
|
751
|
-
}
|
|
752
|
-
const sandboxSettings = mergeSandboxSettings(getCurrentSandboxSettings(), cliOverrides);
|
|
802
|
+
const maxToolRounds = parseInt(stringOption(options.maxToolRounds) || "100", 10) || 100;
|
|
753
803
|
if (typeof options.apiKey === "string" && process.env.MUONROI_TEST_NO_PERSIST !== "1") {
|
|
754
804
|
// Persist to OS keychain (per-provider) instead of plaintext settings.json.
|
|
755
805
|
// Fire-and-forget: keychain write is async; if it fails (no keytar), the key still
|
|
@@ -758,13 +808,7 @@ function resolveConfig(options) {
|
|
|
758
808
|
}
|
|
759
809
|
if (typeof options.model === "string")
|
|
760
810
|
saveUserSettings({ defaultModel: normalizeModelId(options.model) });
|
|
761
|
-
|
|
762
|
-
const resolved = resolveCliSandboxMode(options.sandbox);
|
|
763
|
-
if (resolved) {
|
|
764
|
-
saveUserSettings({ sandboxMode: resolved });
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
return { apiKey, baseURL, model, maxToolRounds, sandboxMode, sandboxSettings };
|
|
811
|
+
return { apiKey, baseURL, model, maxToolRounds };
|
|
768
812
|
}
|
|
769
813
|
function requireApiKey(apiKey) {
|
|
770
814
|
if (!apiKey) {
|
|
@@ -791,11 +835,6 @@ program
|
|
|
791
835
|
.option("-p, --prompt <prompt>", "Run a single prompt headlessly")
|
|
792
836
|
.option("--verify", "Run the built-in verify flow headlessly")
|
|
793
837
|
.option("--format <format>", "Headless output format: text or json", parseHeadlessOutputFormat, "text")
|
|
794
|
-
.option("--sandbox", "Run agent shell commands inside a Shuru sandbox")
|
|
795
|
-
.option("--no-sandbox", "Run agent shell commands directly on the host")
|
|
796
|
-
.option("--allow-net", "Enable network access inside the Shuru sandbox")
|
|
797
|
-
.option("--allow-host <pattern>", "Restrict sandbox network to specific hosts (repeatable)", collect, [])
|
|
798
|
-
.option("--port <mapping>", "Forward a host port to sandbox guest (HOST:GUEST, repeatable)", collect, [])
|
|
799
838
|
.option("-s, --session <id>", "Continue a saved session by id, or use 'latest'")
|
|
800
839
|
.option("--background-task-file <path>", "Run a persisted background delegation")
|
|
801
840
|
.option("--max-tool-rounds <n>", "Max tool execution rounds (ultimate runaway safety net)", "120")
|
|
@@ -1081,15 +1120,15 @@ program
|
|
|
1081
1120
|
console.error(verifyError);
|
|
1082
1121
|
process.exit(1);
|
|
1083
1122
|
}
|
|
1084
|
-
await runHeadless(buildVerifyPrompt(process.cwd()
|
|
1123
|
+
await runHeadless(buildVerifyPrompt(process.cwd()), requireApiKey(config.apiKey), config.baseURL, config.model, config.maxToolRounds, options.batchApi === true, options.format, options.session, options.permission, councilAutoAnswer);
|
|
1085
1124
|
return;
|
|
1086
1125
|
}
|
|
1087
1126
|
if (options.prompt) {
|
|
1088
|
-
await runHeadless(options.prompt, requireApiKey(config.apiKey), config.baseURL, config.model, config.maxToolRounds, options.batchApi === true,
|
|
1127
|
+
await runHeadless(options.prompt, requireApiKey(config.apiKey), config.baseURL, config.model, config.maxToolRounds, options.batchApi === true, options.format, options.session, options.permission, councilAutoAnswer);
|
|
1089
1128
|
return;
|
|
1090
1129
|
}
|
|
1091
1130
|
const initialMessage = message.length > 0 ? message.join(" ") : undefined;
|
|
1092
|
-
await startInteractive(config.apiKey, config.baseURL, config.model, config.maxToolRounds, options.batchApi === true,
|
|
1131
|
+
await startInteractive(config.apiKey, config.baseURL, config.model, config.maxToolRounds, options.batchApi === true, options.session, initialMessage, options.permission, options.injectHalt === true);
|
|
1093
1132
|
});
|
|
1094
1133
|
program
|
|
1095
1134
|
.command("models")
|
|
@@ -1213,7 +1252,7 @@ keys
|
|
|
1213
1252
|
});
|
|
1214
1253
|
keys
|
|
1215
1254
|
.command("login <provider>")
|
|
1216
|
-
.description("Log in to a provider via OAuth subscription (supported: openai, google, xai)")
|
|
1255
|
+
.description("Log in to a provider via OAuth subscription (supported: openai, google/agy, xai). 'google'/'agy' now uses agy client creds.")
|
|
1217
1256
|
.action(async (provider) => {
|
|
1218
1257
|
const { runKeysLogin } = await import("./cli/keys.js");
|
|
1219
1258
|
await runKeysLogin(provider);
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
export {};
|
|
@@ -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
|
@@ -67,7 +67,7 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
|
|
|
67
67
|
const content = await readFile(filePath, "utf8");
|
|
68
68
|
return syncFile(filePath, content, false, waitForDiagnostics);
|
|
69
69
|
}
|
|
70
|
-
async function syncFile(filePath, content, save = true, waitForDiagnostics = true) {
|
|
70
|
+
async function syncFile(filePath, content, save = true, waitForDiagnostics = true, diagnosticsTimeoutMs) {
|
|
71
71
|
const records = await getClientsForFile(filePath);
|
|
72
72
|
if (records.length === 0)
|
|
73
73
|
return [];
|
|
@@ -80,7 +80,7 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
|
|
|
80
80
|
await client.saveFile(filePath);
|
|
81
81
|
}
|
|
82
82
|
if (waitForDiagnostics) {
|
|
83
|
-
await client.waitForDiagnostics(filePath);
|
|
83
|
+
await client.waitForDiagnostics(filePath, diagnosticsTimeoutMs);
|
|
84
84
|
}
|
|
85
85
|
return {
|
|
86
86
|
filePath,
|
|
@@ -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 () => {
|
|
@@ -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>;
|