muonroi-cli 1.8.4 → 1.8.5
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 +17 -5
- package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
- package/dist/packages/agent-harness-core/src/driver.js +46 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
- package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
- package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
- package/dist/packages/agent-harness-core/src/protocol.js +15 -0
- package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
- package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
- package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
- package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
- package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
- package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
- package/dist/packages/agent-harness-opentui/src/install.js +10 -0
- package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
- package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
- package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
- package/dist/src/agent-harness/mock-model.d.ts +28 -0
- package/dist/src/agent-harness/mock-model.js +63 -1
- package/dist/src/agent-harness/test-spawn.js +31 -0
- package/dist/src/cli/config/screen-providers.js +1 -1
- package/dist/src/cli/cost-forensics.d.ts +10 -0
- package/dist/src/cli/cost-forensics.js +18 -3
- package/dist/src/cli/keys-bundle.d.ts +1 -1
- package/dist/src/cli/keys-bundle.js +1 -1
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +19 -81
- package/dist/src/council/clarifier.d.ts +28 -2
- package/dist/src/council/clarifier.js +81 -15
- package/dist/src/council/context.js +49 -15
- package/dist/src/council/debate-checkpoint.d.ts +129 -0
- package/dist/src/council/debate-checkpoint.js +176 -0
- package/dist/src/council/debate-planner.js +51 -3
- package/dist/src/council/debate-summary.d.ts +25 -0
- package/dist/src/council/debate-summary.js +85 -0
- package/dist/src/council/debate.d.ts +169 -2
- package/dist/src/council/debate.js +1210 -134
- package/dist/src/council/index.d.ts +85 -1
- package/dist/src/council/index.js +634 -196
- package/dist/src/council/leader.d.ts +26 -0
- package/dist/src/council/leader.js +150 -9
- package/dist/src/council/llm.d.ts +32 -0
- package/dist/src/council/llm.js +231 -38
- package/dist/src/council/panel-select.d.ts +30 -0
- package/dist/src/council/panel-select.js +72 -0
- package/dist/src/council/planner.js +23 -0
- package/dist/src/council/preflight.d.ts +7 -0
- package/dist/src/council/preflight.js +14 -2
- package/dist/src/council/prompts.d.ts +30 -3
- package/dist/src/council/prompts.js +234 -64
- package/dist/src/council/stance-recall.d.ts +42 -0
- package/dist/src/council/stance-recall.js +57 -0
- package/dist/src/council/strip-think.d.ts +17 -0
- package/dist/src/council/strip-think.js +33 -0
- package/dist/src/council/types.d.ts +128 -0
- package/dist/src/ee/artifact-cache.d.ts +16 -0
- package/dist/src/ee/artifact-cache.js +32 -0
- package/dist/src/ee/auth.d.ts +1 -0
- package/dist/src/ee/auth.js +15 -2
- package/dist/src/ee/bridge.d.ts +10 -0
- package/dist/src/ee/bridge.js +58 -0
- package/dist/src/ee/client.js +81 -18
- package/dist/src/ee/export-transcripts.d.ts +1 -0
- package/dist/src/ee/export-transcripts.js +8 -10
- package/dist/src/ee/extract-session.js +29 -0
- package/dist/src/ee/extract-style.d.ts +58 -0
- package/dist/src/ee/extract-style.js +270 -0
- package/dist/src/ee/recall-ledger.d.ts +9 -0
- package/dist/src/ee/recall-ledger.js +3 -0
- package/dist/src/ee/scope.d.ts +1 -0
- package/dist/src/ee/scope.js +26 -1
- package/dist/src/ee/search.d.ts +7 -0
- package/dist/src/ee/search.js +24 -0
- package/dist/src/ee/transcript-emit.js +2 -0
- package/dist/src/ee/types.d.ts +22 -0
- package/dist/src/ee/who-am-i-brain.d.ts +35 -0
- package/dist/src/ee/who-am-i-brain.js +220 -0
- package/dist/src/ee/who-am-i.d.ts +10 -3
- package/dist/src/ee/who-am-i.js +12 -0
- package/dist/src/ee/workflow-event.d.ts +48 -0
- package/dist/src/ee/workflow-event.js +81 -0
- package/dist/src/flow/compaction/compress.d.ts +3 -3
- package/dist/src/flow/compaction/compress.js +45 -8
- package/dist/src/flow/compaction/extract.d.ts +4 -7
- package/dist/src/flow/compaction/extract.js +50 -10
- package/dist/src/flow/compaction/index.d.ts +13 -1
- package/dist/src/flow/compaction/index.js +70 -3
- package/dist/src/flow/compaction/input-guard.d.ts +24 -0
- package/dist/src/flow/compaction/input-guard.js +43 -0
- package/dist/src/flow/fold-planning.d.ts +36 -0
- package/dist/src/flow/fold-planning.js +83 -0
- package/dist/src/flow/hierarchy.d.ts +146 -0
- package/dist/src/flow/hierarchy.js +427 -0
- package/dist/src/flow/index.d.ts +1 -0
- package/dist/src/flow/index.js +2 -0
- package/dist/src/flow/run-artifacts.d.ts +102 -0
- package/dist/src/flow/run-artifacts.js +208 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/gsd/assessment-schema.d.ts +44 -0
- package/dist/src/gsd/assessment-schema.js +134 -0
- package/dist/src/gsd/capability-registry.d.ts +45 -0
- package/dist/src/gsd/capability-registry.js +337 -0
- package/dist/src/gsd/complexity-assessor.d.ts +39 -0
- package/dist/src/gsd/complexity-assessor.js +152 -0
- package/dist/src/gsd/config-bridge.d.ts +7 -0
- package/dist/src/gsd/config-bridge.js +114 -0
- package/dist/src/gsd/config-loader.d.ts +27 -0
- package/dist/src/gsd/config-loader.js +50 -0
- package/dist/src/gsd/council-context.d.ts +44 -0
- package/dist/src/gsd/council-context.js +114 -0
- package/dist/src/gsd/ee-closure.d.ts +28 -0
- package/dist/src/gsd/ee-closure.js +49 -0
- package/dist/src/gsd/flags.d.ts +55 -0
- package/dist/src/gsd/flags.js +83 -0
- package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
- package/dist/src/gsd/gsd-dispatch.js +131 -0
- package/dist/src/gsd/gsd-runtime.d.ts +22 -0
- package/dist/src/gsd/gsd-runtime.js +37 -0
- package/dist/src/gsd/host-adapter.d.ts +11 -0
- package/dist/src/gsd/host-adapter.js +29 -0
- package/dist/src/gsd/index.d.ts +24 -1
- package/dist/src/gsd/index.js +27 -0
- package/dist/src/gsd/loop-host-contract.d.ts +21 -0
- package/dist/src/gsd/loop-host-contract.js +39 -0
- package/dist/src/gsd/loop-host.d.ts +69 -0
- package/dist/src/gsd/loop-host.js +245 -0
- package/dist/src/gsd/loop-resolver.d.ts +36 -0
- package/dist/src/gsd/loop-resolver.js +79 -0
- package/dist/src/gsd/model-tier.d.ts +13 -0
- package/dist/src/gsd/model-tier.js +45 -0
- package/dist/src/gsd/mutation-gate.d.ts +16 -0
- package/dist/src/gsd/mutation-gate.js +41 -0
- package/dist/src/gsd/native-roadmap.d.ts +89 -0
- package/dist/src/gsd/native-roadmap.js +343 -0
- package/dist/src/gsd/native-state.d.ts +47 -0
- package/dist/src/gsd/native-state.js +220 -0
- package/dist/src/gsd/paths.d.ts +23 -0
- package/dist/src/gsd/paths.js +66 -0
- package/dist/src/gsd/phase-dag.d.ts +12 -0
- package/dist/src/gsd/phase-dag.js +94 -0
- package/dist/src/gsd/phase-sync.d.ts +42 -0
- package/dist/src/gsd/phase-sync.js +321 -0
- package/dist/src/gsd/pil-gate-context.d.ts +13 -0
- package/dist/src/gsd/pil-gate-context.js +64 -0
- package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
- package/dist/src/gsd/pil-gate-critic.js +74 -0
- package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
- package/dist/src/gsd/plan-council-prompts.js +79 -0
- package/dist/src/gsd/plan-council.d.ts +44 -0
- package/dist/src/gsd/plan-council.js +251 -0
- package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
- package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
- package/dist/src/gsd/product-workspace.d.ts +13 -0
- package/dist/src/gsd/product-workspace.js +124 -0
- package/dist/src/gsd/ship-bridge.d.ts +25 -0
- package/dist/src/gsd/ship-bridge.js +65 -0
- package/dist/src/gsd/state-document.d.ts +40 -0
- package/dist/src/gsd/state-document.js +163 -0
- package/dist/src/gsd/verdict-schema.d.ts +39 -0
- package/dist/src/gsd/verdict-schema.js +144 -0
- package/dist/src/gsd/verify-context.d.ts +22 -0
- package/dist/src/gsd/verify-context.js +27 -0
- package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
- package/dist/src/gsd/verify-council-prompts.js +85 -0
- package/dist/src/gsd/verify-council.d.ts +25 -0
- package/dist/src/gsd/verify-council.js +119 -0
- package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
- package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
- package/dist/src/gsd/workflow-engine.d.ts +60 -0
- package/dist/src/gsd/workflow-engine.js +207 -0
- package/dist/src/gsd/workflow-tools.d.ts +13 -0
- package/dist/src/gsd/workflow-tools.js +277 -0
- package/dist/src/hooks/index.js +1 -1
- package/dist/src/index.js +44 -11
- package/dist/src/maintain/pr-builder.js +23 -13
- package/dist/src/mcp/auto-setup.js +57 -32
- package/dist/src/mcp/client-pool.js +1 -1
- package/dist/src/mcp/ee-tools.js +1 -0
- package/dist/src/mcp/research-onboarding.js +8 -7
- package/dist/src/mcp/runtime.js +34 -2
- package/dist/src/models/catalog-client.d.ts +87 -0
- package/dist/src/models/catalog-client.js +105 -38
- package/dist/src/models/catalog.json +528 -265
- package/dist/src/models/registry.d.ts +22 -7
- package/dist/src/models/registry.js +73 -10
- package/dist/src/ops/doctor.js +1 -1
- package/dist/src/orchestrator/auto-commit.js +1 -1
- package/dist/src/orchestrator/batch-turn-runner.js +2 -2
- package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
- package/dist/src/orchestrator/cache-prefix.js +83 -0
- package/dist/src/orchestrator/compact-request.d.ts +32 -0
- package/dist/src/orchestrator/compact-request.js +41 -0
- package/dist/src/orchestrator/compaction.d.ts +10 -0
- package/dist/src/orchestrator/compaction.js +27 -7
- package/dist/src/orchestrator/council-manager.d.ts +12 -3
- package/dist/src/orchestrator/council-manager.js +65 -24
- package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
- package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
- package/dist/src/orchestrator/error-utils.d.ts +29 -0
- package/dist/src/orchestrator/error-utils.js +132 -24
- package/dist/src/orchestrator/grounding-check.js +39 -1
- package/dist/src/orchestrator/message-processor.js +242 -33
- package/dist/src/orchestrator/orchestrator.d.ts +39 -3
- package/dist/src/orchestrator/orchestrator.js +651 -102
- package/dist/src/orchestrator/preprocessor.js +1 -1
- package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
- package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
- package/dist/src/orchestrator/prompts.js +17 -17
- package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
- package/dist/src/orchestrator/reactive-delegation.js +59 -0
- package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
- package/dist/src/orchestrator/retry-classifier.js +46 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
- package/dist/src/orchestrator/safety-intercept.js +55 -0
- package/dist/src/orchestrator/scope-reminder.js +1 -1
- package/dist/src/orchestrator/session-experience.d.ts +2 -1
- package/dist/src/orchestrator/session-experience.js +2 -1
- package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
- package/dist/src/orchestrator/should-run-gate.js +18 -0
- package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
- package/dist/src/orchestrator/stall-watchdog.js +47 -13
- package/dist/src/orchestrator/stream-runner.js +62 -29
- package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
- package/dist/src/orchestrator/sub-agent-cap.js +16 -1
- package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
- package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
- package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
- package/dist/src/orchestrator/subagent-compactor.js +126 -15
- package/dist/src/orchestrator/tool-engine.d.ts +22 -0
- package/dist/src/orchestrator/tool-engine.js +620 -56
- package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
- package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
- package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
- package/dist/src/orchestrator/turn-watchdog.js +55 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +1 -1
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +5 -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/discovery-types.d.ts +1 -0
- package/dist/src/pil/discovery.js +16 -11
- package/dist/src/pil/layer1-intent.d.ts +18 -6
- package/dist/src/pil/layer1-intent.js +66 -757
- package/dist/src/pil/layer15-context-scan.js +15 -1
- package/dist/src/pil/layer3-ee-injection.js +23 -8
- package/dist/src/pil/layer4-gsd.js +69 -16
- package/dist/src/pil/layer5-context.js +7 -3
- package/dist/src/pil/layer6-output.d.ts +23 -0
- package/dist/src/pil/layer6-output.js +5 -1
- package/dist/src/pil/llm-classify.d.ts +33 -2
- package/dist/src/pil/llm-classify.js +123 -131
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +1 -0
- package/dist/src/pil/pipeline.js +34 -2
- package/dist/src/pil/response-tools.js +5 -3
- package/dist/src/pil/schema.d.ts +1 -0
- package/dist/src/pil/schema.js +2 -0
- package/dist/src/pil/types.d.ts +18 -0
- package/dist/src/playbook/directives.d.ts +4 -0
- package/dist/src/playbook/directives.js +17 -5
- package/dist/src/product-loop/backlog-builder.d.ts +14 -1
- package/dist/src/product-loop/backlog-builder.js +30 -6
- package/dist/src/product-loop/discovery-context-format.js +3 -1
- package/dist/src/product-loop/discovery-ecosystem.js +4 -1
- package/dist/src/product-loop/discovery-interview.js +32 -3
- package/dist/src/product-loop/discovery-schema.js +5 -1
- package/dist/src/product-loop/ideal-trace.d.ts +7 -0
- package/dist/src/product-loop/ideal-trace.js +64 -0
- package/dist/src/product-loop/index.d.ts +13 -1
- package/dist/src/product-loop/index.js +333 -52
- package/dist/src/product-loop/loop-driver.d.ts +7 -0
- package/dist/src/product-loop/loop-driver.js +310 -99
- package/dist/src/product-loop/phase-plan.d.ts +5 -0
- package/dist/src/product-loop/phase-plan.js +39 -2
- package/dist/src/product-loop/phase-runner.js +9 -1
- package/dist/src/product-loop/sprint-runner.d.ts +111 -0
- package/dist/src/product-loop/sprint-runner.js +559 -16
- package/dist/src/product-loop/types.d.ts +36 -5
- package/dist/src/providers/adapter.d.ts +1 -1
- package/dist/src/providers/adapter.js +3 -4
- package/dist/src/providers/auth/browser-flow.d.ts +1 -1
- package/dist/src/providers/auth/browser-flow.js +1 -1
- package/dist/src/providers/auth/openai-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +0 -34
- package/dist/src/providers/auth/token-store.js +4 -1
- package/dist/src/providers/auth/types.d.ts +1 -1
- package/dist/src/providers/auth/types.js +1 -1
- package/dist/src/providers/capabilities.d.ts +24 -5
- package/dist/src/providers/capabilities.js +42 -24
- package/dist/src/providers/endpoints.d.ts +2 -2
- package/dist/src/providers/endpoints.js +11 -10
- package/dist/src/providers/keychain.d.ts +1 -1
- package/dist/src/providers/keychain.js +7 -9
- package/dist/src/providers/mcp-vision-bridge.js +56 -146
- package/dist/src/providers/openai-compatible.js +8 -1
- package/dist/src/providers/pricing.d.ts +2 -2
- package/dist/src/providers/pricing.js +3 -13
- package/dist/src/providers/runtime.d.ts +27 -2
- package/dist/src/providers/runtime.js +78 -15
- package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
- package/dist/src/providers/strategies/base.strategy.js +24 -1
- package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
- package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
- package/dist/src/providers/strategies/registry.js +4 -4
- package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
- package/dist/src/providers/strategies/thinking-mode.js +280 -1
- package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
- package/dist/src/providers/strategies/zai.strategy.js +44 -0
- package/dist/src/providers/types.d.ts +5 -6
- package/dist/src/providers/types.js +2 -2
- package/dist/src/providers/vision-backend.d.ts +47 -0
- package/dist/src/providers/vision-backend.js +258 -0
- package/dist/src/providers/vision-proxy.d.ts +22 -9
- package/dist/src/providers/vision-proxy.js +63 -132
- package/dist/src/providers/wire-debug.js +95 -0
- package/dist/src/router/decide.d.ts +13 -0
- package/dist/src/router/decide.js +138 -36
- package/dist/src/router/peak-hour.d.ts +38 -0
- package/dist/src/router/peak-hour.js +107 -0
- package/dist/src/router/step-router.js +3 -2
- package/dist/src/router/warm.js +4 -5
- package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
- package/dist/src/scaffold/continuation-prompt.js +26 -0
- package/dist/src/scaffold/point-to-existing.d.ts +21 -0
- package/dist/src/scaffold/point-to-existing.js +25 -0
- package/dist/src/self-qa/agentic-loop.js +3 -3
- package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
- package/dist/src/{ui/state → state}/active-run.js +21 -0
- package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
- package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
- package/dist/src/state/turn-trace.d.ts +43 -0
- package/dist/src/state/turn-trace.js +32 -0
- package/dist/src/storage/db.js +2 -1
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/interaction-log.d.ts +1 -1
- package/dist/src/storage/migrations.js +71 -1
- package/dist/src/storage/sessions.d.ts +28 -10
- package/dist/src/storage/sessions.js +78 -21
- package/dist/src/storage/transcript-view.js +1 -1
- package/dist/src/storage/transcript.d.ts +51 -0
- package/dist/src/storage/transcript.js +284 -13
- package/dist/src/tools/file.d.ts +15 -0
- package/dist/src/tools/file.js +32 -0
- package/dist/src/tools/native-tools.js +5 -0
- package/dist/src/tools/registry.d.ts +3 -0
- package/dist/src/tools/registry.js +460 -22
- package/dist/src/tools/research.d.ts +29 -0
- package/dist/src/tools/research.js +233 -0
- package/dist/src/types/index.d.ts +118 -3
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/cards/product-status-card.js +1 -1
- package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
- package/dist/src/ui/components/bubble-body-guard.js +50 -0
- package/dist/src/ui/components/context-rail.d.ts +26 -0
- package/dist/src/ui/components/context-rail.js +33 -0
- package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
- package/dist/src/ui/components/council-conclusion-card.js +420 -0
- package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
- package/dist/src/ui/components/council-debate-pill.js +34 -0
- package/dist/src/ui/components/council-info-card.js +2 -2
- package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
- package/dist/src/ui/components/council-leader-bubble.js +21 -11
- package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
- package/dist/src/ui/components/council-message-bubble.js +16 -15
- package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
- package/dist/src/ui/components/council-phase-timeline.js +49 -15
- package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
- package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
- package/dist/src/ui/components/council-question-card.js +12 -12
- package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
- package/dist/src/ui/components/council-rail-rounds.js +57 -0
- package/dist/src/ui/components/council-round-group.d.ts +38 -0
- package/dist/src/ui/components/council-round-group.js +88 -0
- package/dist/src/ui/components/council-status-list.d.ts +3 -1
- package/dist/src/ui/components/council-status-list.js +36 -24
- package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
- package/dist/src/ui/components/council-synthesis-banner.js +20 -5
- package/dist/src/ui/components/halt-recovery-card.js +9 -5
- package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
- package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
- package/dist/src/ui/components/prompt-box.js +18 -16
- package/dist/src/ui/components/session-tree-card.d.ts +14 -0
- package/dist/src/ui/components/session-tree-card.js +46 -0
- package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
- package/dist/src/ui/components/slash-inline-menu.js +26 -5
- package/dist/src/ui/components/task-list-panel.d.ts +14 -1
- package/dist/src/ui/components/task-list-panel.js +22 -2
- package/dist/src/ui/containers/modals-layer.d.ts +2 -1
- package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
- package/dist/src/ui/mcp-modal.js +2 -4
- package/dist/src/ui/modals/api-key-modal.js +1 -1
- package/dist/src/ui/modals/connect-modal.js +4 -3
- package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
- package/dist/src/ui/modals/session-picker-modal.js +3 -5
- package/dist/src/ui/picker-providers.d.ts +1 -1
- package/dist/src/ui/picker-providers.js +1 -1
- package/dist/src/ui/primitives/index.d.ts +1 -0
- package/dist/src/ui/primitives/index.js +2 -0
- package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
- package/dist/src/ui/primitives/semantic-primitives.js +81 -0
- package/dist/src/ui/slash/compact.js +5 -7
- package/dist/src/ui/slash/cost.js +1 -1
- package/dist/src/ui/slash/council.js +19 -1
- package/dist/src/ui/slash/debug.d.ts +3 -31
- package/dist/src/ui/slash/debug.js +9 -20
- package/dist/src/ui/slash/ideal.d.ts +6 -2
- package/dist/src/ui/slash/ideal.js +97 -7
- package/dist/src/ui/slash/menu-items.d.ts +7 -0
- package/dist/src/ui/slash/menu-items.js +12 -18
- package/dist/src/ui/slash/registry.d.ts +2 -0
- package/dist/src/ui/slash/registry.js +4 -0
- package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
- package/dist/src/ui/status-bar/cache-hit.js +9 -0
- package/dist/src/ui/status-bar/index.d.ts +1 -1
- package/dist/src/ui/status-bar/index.js +7 -3
- package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
- package/dist/src/ui/status-bar/usd-meter.js +6 -4
- package/dist/src/ui/theme.d.ts +1 -0
- package/dist/src/ui/theme.js +2 -0
- package/dist/src/ui/types.d.ts +7 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/format.d.ts +14 -0
- package/dist/src/ui/utils/format.js +23 -3
- package/dist/src/usage/downgrade.js +2 -2
- package/dist/src/usage/product-ledger.js +2 -2
- package/dist/src/utils/install-manager.js +2 -1
- package/dist/src/utils/logger.js +2 -2
- package/dist/src/utils/permission-mode.js +5 -3
- package/dist/src/utils/redactor.js +1 -1
- package/dist/src/utils/settings.d.ts +153 -5
- package/dist/src/utils/settings.js +233 -29
- package/dist/src/utils/visible-retry.d.ts +11 -0
- package/dist/src/utils/visible-retry.js +10 -1
- package/dist/src/verify/entrypoint.d.ts +1 -1
- package/dist/src/verify/entrypoint.js +1 -1
- package/dist/src/verify/recipes.d.ts +13 -0
- package/dist/src/verify/recipes.js +15 -0
- package/package.json +135 -132
- package/dist/src/providers/auth/gcloud.d.ts +0 -28
- package/dist/src/providers/auth/gcloud.js +0 -102
- package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
- package/dist/src/providers/auth/gemini-oauth.js +0 -472
- package/dist/src/providers/gemini.d.ts +0 -11
- package/dist/src/providers/gemini.js +0 -45
- package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
- package/dist/src/providers/siliconflow-sse-repair.js +0 -177
- package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
- package/dist/src/providers/strategies/google.strategy.js +0 -174
- package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
- package/dist/src/ui/containers/chat-feed.d.ts +0 -40
- package/dist/src/ui/containers/chat-feed.js +0 -66
|
@@ -48,39 +48,45 @@
|
|
|
48
48
|
// - A4 (tool_call write-ahead) — persistToolCallWriteAhead
|
|
49
49
|
// - A5 (message_seq write-ahead) — persistMessageWriteAhead
|
|
50
50
|
// - O1 (providerOptions shape forensics) — extractProviderOptionsShape
|
|
51
|
-
// -
|
|
51
|
+
// - reasoning-strip (provider quirk) — turnCaps.sanitizeHistory
|
|
52
52
|
import { generateText, stepCountIs, streamText } from "ai";
|
|
53
|
+
import { getEffectiveCouncilRoleCount } from "../council/leader.js";
|
|
53
54
|
import { recordArtifact } from "../ee/artifact-cache.js";
|
|
54
55
|
import { routeFeedback } from "../ee/bridge.js";
|
|
55
56
|
import { getDefaultEEClient } from "../ee/intercept.js";
|
|
57
|
+
import { isGsdHardGateEnabled } from "../gsd/flags.js";
|
|
58
|
+
import { evaluateMutationGate } from "../gsd/mutation-gate.js";
|
|
56
59
|
import { acquireMcpTools } from "../mcp/client-pool.js";
|
|
57
60
|
import { dropRedundantFsMcpTools, filterMcpServersByMessage } from "../mcp/smart-filter.js";
|
|
58
|
-
import { getModelInfo } from "../models/registry.js";
|
|
61
|
+
import { getModelInfo, isReasoningModel } from "../models/registry.js";
|
|
59
62
|
import { cheapModelShellLine, injectCheapModelPlaybook, injectCheapModelShellDirective, shouldInjectCheapModelPlaybook, } from "../pil/cheap-model-playbook.js";
|
|
60
63
|
import { injectCheapModelWorkbook, shouldInjectCheapModelWorkbook } from "../pil/cheap-model-workbooks.js";
|
|
61
64
|
import { getResponseTaskType, isResponseTool, normalizeStructuredResponseTaskType, shouldHaltOnResponseTool, } from "../pil/index.js";
|
|
62
|
-
import { isMetaAnalysisPrompt } from "../pil/layer6-output.js";
|
|
65
|
+
import { isMetaAnalysisPrompt, isSprintPlanExecution } from "../pil/layer6-output.js";
|
|
63
66
|
import { taskTypeToMaxTokens, taskTypeToReasoningEffort, taskTypeToTier } from "../pil/task-tier-map.js";
|
|
64
67
|
import { mentionsEcosystemScope } from "../playbook/directives.js";
|
|
65
68
|
import { getProviderCapabilities } from "../providers/capabilities.js";
|
|
66
69
|
import { bridgeMcpToolResult, listCachedImages, scrubImagePayloadsInMessages, } from "../providers/mcp-vision-bridge.js";
|
|
67
70
|
import { captureToolSchemas } from "../providers/patch-zod-schema.js";
|
|
68
|
-
import { buildTurnProviderOptions, requireRuntimeProvider, shouldDropParam, } from "../providers/runtime.js";
|
|
71
|
+
import { buildTurnProviderOptions, requireRuntimeProvider, resolveTemperatureParam, shouldDropParam, } from "../providers/runtime.js";
|
|
69
72
|
import { wireDebug } from "../providers/wire-debug.js";
|
|
70
73
|
import { reportRouteOutcome } from "../router/decide.js";
|
|
74
|
+
import { statusBarStore } from "../state/status-bar-store.js";
|
|
75
|
+
import { recordTurnTrace } from "../state/turn-trace.js";
|
|
71
76
|
import { getNextMessageSequence, logInteraction, markMessageErrored, markToolCallErrored, persistToolCallWriteAhead, } from "../storage/index.js";
|
|
72
77
|
import { persistSessionExperience } from "../storage/session-experience-store.js";
|
|
73
78
|
import { createBuiltinTools } from "../tools/registry.js";
|
|
74
79
|
import { snapshotFromTodoWriteArgs } from "../tools/todo-write-snapshot.js";
|
|
75
80
|
import { visionToolsNeeded } from "../tools/vision-gate.js";
|
|
76
|
-
import { recordTurnTrace } from "../ui/slash/debug.js";
|
|
77
|
-
import { statusBarStore } from "../ui/status-bar/store.js";
|
|
78
81
|
import { appendDecisionLog } from "../usage/decision-log.js";
|
|
79
82
|
import { logger } from "../utils/logger.js";
|
|
80
83
|
import { openUrl } from "../utils/open-url.js";
|
|
81
84
|
import { appendAudit, toolNeedsApproval } from "../utils/permission-mode.js";
|
|
82
|
-
import { getAutoCouncilConfidence, getAutoCouncilMinRoles, getProviderStallRetries, getProviderStallTimeoutMs,
|
|
85
|
+
import { getAutoCouncilConfidence, getAutoCouncilMinRoles, getProviderProgressTimeoutMs, getProviderStallRetries, getProviderStallTimeoutMs, getSteerInjectionEnabled, getTopLevelCompactHysteresis, getTopLevelCompactKeepLast, getTopLevelCompactTailBudgetChars, getTopLevelCompactThresholdChars, getTopLevelToolBudgetChars, isAutoCouncilClarifyEnabled, isAutoCouncilEnabled, loadMcpServers, } from "../utils/settings.js";
|
|
86
|
+
import { isAutoCouncilSkipReasoning } from "../utils/settings.js";
|
|
83
87
|
import { resolveShell } from "../utils/shell.js";
|
|
88
|
+
import { foldDynamicTailIntoUserMessage, splitFrontAndDynamicTail } from "./cache-prefix.js";
|
|
89
|
+
import { consumeProactiveCompact } from "./compact-request.js";
|
|
84
90
|
import { relaxCompactionSettings } from "./compaction.js";
|
|
85
91
|
import { wrapToolSetWithDedup } from "./cross-turn-dedup.js";
|
|
86
92
|
import { humanizeApiError, isAuthenticationError, isContextLimitError, summarizeApiErrorForLog } from "./error-utils.js";
|
|
@@ -94,6 +100,7 @@ import { containsEncryptedReasoning, sanitizeModelMessages } from "./reasoning.j
|
|
|
94
100
|
import { repairToolCallHook } from "./repair-tool-call.js";
|
|
95
101
|
import { buildRepetitionReminder, recordAssistantBurst, shouldInjectRepetitionReminder, } from "./repetition-detector.js";
|
|
96
102
|
import { classifyStreamError } from "./retry-classifier.js";
|
|
103
|
+
import { parseSafetyBlock, shouldAutoAllowYolo } from "./safety-intercept.js";
|
|
97
104
|
import { forcedFinalize, incSessionStep, } from "./scope-ceiling.js";
|
|
98
105
|
import { attachReminderToMessages, buildCheckpointReminder, buildScopeReminder, cadenceForSize, shouldInjectCeilingCrossing, shouldInjectReminder, shouldInjectSoftWarn, shouldPreWarnCompaction, } from "./scope-reminder.js";
|
|
99
106
|
import { formatElisionManifest, getSessionExperienceCounts, recordCompaction, recordElision, } from "./session-experience.js";
|
|
@@ -101,10 +108,35 @@ import { attemptStallRescue, pushStallToolResult } from "./stall-rescue.js";
|
|
|
101
108
|
import { createStallWatchdog, STALL_ERROR_MESSAGE, shouldContinueAfterMidLoopStall, shouldRepromptStall, stallRepromptBackoffMs, } from "./stall-watchdog.js";
|
|
102
109
|
import { planSteerInjection } from "./steer-inbox.js";
|
|
103
110
|
import { wrapToolSetWithCap } from "./sub-agent-cap.js";
|
|
104
|
-
import { applyAnthropicPromptCaching, compactSubAgentMessages, cumulativeMessageChars } from "./subagent-compactor.js";
|
|
111
|
+
import { applyAnthropicPromptCaching, applyCompactionHysteresis, compactSubAgentMessages, cumulativeMessageChars, initCompactionHysteresisState, } from "./subagent-compactor.js";
|
|
105
112
|
import { detectTextEmittedToolCall, parseDsmlToolCalls } from "./text-tool-call-detector.js";
|
|
113
|
+
import { getToolLimitAutoRecoverCap, shouldAutoRecoverToolLimit } from "./tool-limit-auto-recover.js";
|
|
106
114
|
import { createToolLoopCapPredicate } from "./tool-loop-cap.js";
|
|
107
115
|
import { buildToolRepetitionAbortMessage, recordToolError as recordToolRepetitionError, recordToolSuccess as recordToolRepetitionSuccess, } from "./tool-repetition-detector.js";
|
|
116
|
+
/**
|
|
117
|
+
* Resolve the per-turn `maxOutputTokens` budget.
|
|
118
|
+
*
|
|
119
|
+
* Normally the budget is derived from the PIL-classified `taskType`
|
|
120
|
+
* (`taskTypeToMaxTokens`). But a sprint IMPLEMENTATION turn — the /ideal
|
|
121
|
+
* loop's handoff into the host orchestrator via `processMessageFn`, marked
|
|
122
|
+
* with `SPRINT_EXECUTION_MARKER` — is a KNOWN code-writing task that must not
|
|
123
|
+
* be starved by a noisy classify. Observed live (2026-07-10, gsd-core
|
|
124
|
+
* migration): the impl prompt was classified `analyze`/default → capped at
|
|
125
|
+
* 4_096 output → the model spent the whole budget narrating its plan, hit
|
|
126
|
+
* `finishReason:"length"` mid-word, produced ZERO code, and the turn wedged.
|
|
127
|
+
*
|
|
128
|
+
* Fix: for a sprint-execution turn, floor the budget at the build/generate
|
|
129
|
+
* tier (12_288) regardless of the classified type. Scoped to the marker only
|
|
130
|
+
* (NOT the broad `isImplementationIntent`) so ordinary refactor/debug turns
|
|
131
|
+
* keep their intentionally tighter L6 budgets.
|
|
132
|
+
*/
|
|
133
|
+
export function resolveTurnMaxOutputTokens(pilCtx) {
|
|
134
|
+
const base = taskTypeToMaxTokens(pilCtx.taskType);
|
|
135
|
+
if (isSprintPlanExecution(pilCtx.raw ?? "")) {
|
|
136
|
+
return Math.max(base, taskTypeToMaxTokens("build"));
|
|
137
|
+
}
|
|
138
|
+
return base;
|
|
139
|
+
}
|
|
108
140
|
/**
|
|
109
141
|
* F2 — approximate the char cost of the FIXED prompt envelope (system +
|
|
110
142
|
* tools JSON-Schema) that streamText re-sends on every step. Used to feed
|
|
@@ -212,9 +244,46 @@ export class SimpleMutex {
|
|
|
212
244
|
}
|
|
213
245
|
}
|
|
214
246
|
import { stripDsmlMarkup } from "./message-processor.js";
|
|
247
|
+
/** Tools that produce zero side-effects — safe for Q&A-only (direct-answer) mode. */
|
|
248
|
+
function stripWriteTools(tools) {
|
|
249
|
+
const readonly = new Set([
|
|
250
|
+
"read_file",
|
|
251
|
+
"grep",
|
|
252
|
+
"bash_output_get",
|
|
253
|
+
"process_list",
|
|
254
|
+
"delegation_read",
|
|
255
|
+
"delegation_list",
|
|
256
|
+
"ee_query",
|
|
257
|
+
"ee_health",
|
|
258
|
+
"usage_forensics",
|
|
259
|
+
"lsp_query",
|
|
260
|
+
"setup_guide",
|
|
261
|
+
"selfverify_status",
|
|
262
|
+
"selfverify_result",
|
|
263
|
+
"selfverify_list",
|
|
264
|
+
"list_vision_cache",
|
|
265
|
+
"ee_feedback",
|
|
266
|
+
"ee_write",
|
|
267
|
+
]);
|
|
268
|
+
const result = {};
|
|
269
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
270
|
+
if (readonly.has(name) || name.startsWith("respond_") || name.startsWith("mcp_")) {
|
|
271
|
+
result[name] = tool;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
215
276
|
export async function* executeToolEngine(args) {
|
|
216
277
|
let { deps, stepRouterPhase, phase2Runtime, runtime, modelInfo, _debugSteps, _ceilingHit, userMessage, pilCtx, turnModelId, turnProvider, _stepCeiling, userModelMessage, userEnrichedMessage, signal, observer, taskHash, provider, system, routerStore, attemptedOverflowRecovery, patternLoopForceHalt, userWriteAheadSeq, streamRetryCount, MAX_STREAM_RETRIES, subagents, systemParts, toolTurnSystem, playwrightGuidance, _hasResponseTools, _pilResponseTools, patternLoopInjectCount, agentLoopDecisionCount, MAX_AGENT_LOOP_DECISIONS, _naturalCeiling, _ceilingTaskType, _ceilingSize, textToolReSteerCount, MAX_TEXT_TOOL_RESTEER, turnStartMs, _debugOn, _debugTurnId, _pilEnrichmentDeltaSnapshot, isChitchat, } = args;
|
|
217
278
|
// Put all extracted code here:
|
|
279
|
+
// Auto-recover budget for "cap" (tool-round ceiling) halts: compact
|
|
280
|
+
// the history and keep going instead of stopping and asking the user
|
|
281
|
+
// to /compact. Turn-scoped (not per-stream-attempt) so a stream-error
|
|
282
|
+
// retry or stall reprompt (`continue streamAttempt`) cannot reset the
|
|
283
|
+
// counter and exceed the intended cap of auto-compactions per turn
|
|
284
|
+
// (default 6, env MUONROI_TOOL_LIMIT_AUTO_RECOVER_CAP).
|
|
285
|
+
let toolLimitAutoRecoverCount = 0;
|
|
286
|
+
const TOOL_LIMIT_AUTO_RECOVER_CAP = getToolLimitAutoRecoverCap();
|
|
218
287
|
let stallTriggered = false;
|
|
219
288
|
// Time-to-first-byte stall RE-PROMPT: some providers (observed:
|
|
220
289
|
// xai/grok-build-0.1) accept the request then never send the first byte —
|
|
@@ -243,6 +312,10 @@ export async function* executeToolEngine(args) {
|
|
|
243
312
|
// the turn when exceeded. Prevents the session 526a83cf22df pattern
|
|
244
313
|
// where 3 user messages burnt 82% of 2.44M tokens in 36 LLM calls.
|
|
245
314
|
let llmCallsThisTurn = 0;
|
|
315
|
+
// Reactive delegation signal: reference to the top-level cap's live state so
|
|
316
|
+
// this turn's cumulative tool-output load can be reported to the Agent at
|
|
317
|
+
// turn end (drives next-turn sub-session escalation). See reactive-delegation.ts.
|
|
318
|
+
let _topLevelCapState = null;
|
|
246
319
|
// Live-queue steering: messages the user typed mid-turn are drained at a
|
|
247
320
|
// prepareStep boundary and accumulated here, then re-appended (deduped) to
|
|
248
321
|
// the messages returned for each subsequent step. Loop-persistent so they
|
|
@@ -269,11 +342,20 @@ export async function* executeToolEngine(args) {
|
|
|
269
342
|
// and stay productive. `plan` keeps the old behaviour (architectural
|
|
270
343
|
// decisions deserve debate regardless of length).
|
|
271
344
|
const autoCouncilTypes = new Set(["plan", "analyze"]);
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
345
|
+
const configuredRoleCount = getEffectiveCouncilRoleCount();
|
|
346
|
+
// Task 8 Step 7: prefer the complexity assessor's own auto-council verdict
|
|
347
|
+
// (pilCtx.gsdAutoCouncil, set at message-processor.ts:685 when the assessor ran)
|
|
348
|
+
// over the raw heavy-tier heuristic below — the assessor already reasoned about
|
|
349
|
+
// depth + task shape, so its verdict is the more intelligent router. Fall back to
|
|
350
|
+
// the heuristic when the assessor didn't run (gsdAutoCouncil undefined).
|
|
351
|
+
const assessorAutoCouncil = pilCtx.gsdAutoCouncil;
|
|
352
|
+
const heavyTier = typeof assessorAutoCouncil === "boolean"
|
|
353
|
+
? assessorAutoCouncil
|
|
354
|
+
: pilCtx.complexityTier === "heavy";
|
|
275
355
|
const autoCouncilConfidence = getAutoCouncilConfidence();
|
|
276
356
|
const autoCouncilMinRoles = getAutoCouncilMinRoles();
|
|
357
|
+
const sessionModelIsReasoning = isReasoningModel(deps.modelId);
|
|
358
|
+
const skipReasoningSetting = isAutoCouncilSkipReasoning();
|
|
277
359
|
const _complexityFromTrace = pilCtx._intentTrace
|
|
278
360
|
?.complexity;
|
|
279
361
|
const _complexityGatePassed = pilCtx.taskType === "plan" || _complexityFromTrace === undefined || _complexityFromTrace !== "low";
|
|
@@ -281,9 +363,13 @@ export async function* executeToolEngine(args) {
|
|
|
281
363
|
autoCouncilTypes.has(pilCtx.taskType) &&
|
|
282
364
|
pilCtx.confidence >= autoCouncilConfidence &&
|
|
283
365
|
_complexityGatePassed;
|
|
366
|
+
// Skip reasoning-model skip for heavy/complex tasks — they benefit from
|
|
367
|
+
// multi-role diversity even when the session model already does extended thinking.
|
|
368
|
+
const shouldSkipForReasoning = sessionModelIsReasoning && skipReasoningSetting && !heavyTier;
|
|
284
369
|
const shouldAutoCouncil = !deps.councilManager.isContinuation &&
|
|
285
370
|
isAutoCouncilEnabled() &&
|
|
286
371
|
configuredRoleCount >= autoCouncilMinRoles &&
|
|
372
|
+
!shouldSkipForReasoning &&
|
|
287
373
|
(taskTypeMatch || heavyTier);
|
|
288
374
|
// Always log the auto-council decision (taken or skipped) with the gate
|
|
289
375
|
// values that decided it. Lets reports answer "why did this turn cost
|
|
@@ -295,6 +381,8 @@ export async function* executeToolEngine(args) {
|
|
|
295
381
|
return "feature-disabled";
|
|
296
382
|
if (configuredRoleCount < autoCouncilMinRoles)
|
|
297
383
|
return `role-count<${autoCouncilMinRoles} (have ${configuredRoleCount})`;
|
|
384
|
+
if (shouldSkipForReasoning)
|
|
385
|
+
return `reasoning-model=${deps.modelId} (internal self-debate active; skip with MUONROI_AUTOCOUNCIL_SKIP_REASONING=0)`;
|
|
298
386
|
if (!taskTypeMatch && !heavyTier) {
|
|
299
387
|
if (!pilCtx.taskType || !autoCouncilTypes.has(pilCtx.taskType))
|
|
300
388
|
return `taskType=${pilCtx.taskType ?? "null"} not in plan|analyze`;
|
|
@@ -322,6 +410,8 @@ export async function* executeToolEngine(args) {
|
|
|
322
410
|
autoCouncilConfidence,
|
|
323
411
|
autoCouncilMinRoles,
|
|
324
412
|
heavyTier,
|
|
413
|
+
sessionModelIsReasoning,
|
|
414
|
+
skipReasoningSetting,
|
|
325
415
|
isContinuation: deps.councilManager.isContinuation,
|
|
326
416
|
},
|
|
327
417
|
}).catch(() => undefined);
|
|
@@ -330,14 +420,35 @@ export async function* executeToolEngine(args) {
|
|
|
330
420
|
? `complexity=heavy${pilCtx.taskType ? ` task=${pilCtx.taskType}` : ""}`
|
|
331
421
|
: `${pilCtx.taskType} task detected with ${(pilCtx.confidence * 100).toFixed(0)}% confidence`;
|
|
332
422
|
yield { type: "content", content: `\n[Auto-council triggered: ${reason}]\n` };
|
|
333
|
-
|
|
423
|
+
// Pre-debate interview: unless disabled, run the model-designed clarification
|
|
424
|
+
// askcards BEFORE the debate so a broadly-scoped "debate mode" request is
|
|
425
|
+
// chốt-ed first (each card's options carry a recommended default + per-option
|
|
426
|
+
// why — see runClarification/buildClarifyOptions). The clarifier is ROI-gated
|
|
427
|
+
// and yields 0 cards on already-detailed topics, so this stays quiet when the
|
|
428
|
+
// prompt is already specific. Skip only when the user turned it off. The
|
|
429
|
+
// clarifier reuses PIL gray-areas as seed questions (no hardcoded questions),
|
|
430
|
+
// and its models come from pickCouncilTaskModel (no hardcoded model/provider).
|
|
431
|
+
yield* deps.runCouncilV2(userMessage, {
|
|
432
|
+
skipClarification: !isAutoCouncilClarifyEnabled(),
|
|
433
|
+
observer,
|
|
434
|
+
userModelMessage,
|
|
435
|
+
});
|
|
334
436
|
const synthesis = deps.councilManager.lastSynthesis;
|
|
437
|
+
const chosenAction = deps.councilManager.lastPostDebateAction;
|
|
335
438
|
deps.councilManager.setLastSynthesis(null);
|
|
336
|
-
|
|
439
|
+
deps.councilManager.setLastPostDebateAction(null);
|
|
440
|
+
// Honor the user's post-debate choice instead of always continuing: an
|
|
441
|
+
// evaluation/decision debate whose deliverable is the conclusion (default
|
|
442
|
+
// save_exit) now returns to the composer rather than being force-fed a
|
|
443
|
+
// meaningless "proceed with the action items" turn. postDebateContinuation
|
|
444
|
+
// is shared with the /council slash path (orchestrator.runCouncilV2).
|
|
445
|
+
const { postDebateContinuation } = await import("../council/index.js");
|
|
446
|
+
const continuationPrompt = synthesis ? postDebateContinuation(chosenAction ?? undefined, synthesis) : null;
|
|
447
|
+
if (continuationPrompt) {
|
|
337
448
|
yield { type: "content", content: "\n[Auto-continuing with council recommendations...]\n" };
|
|
338
449
|
deps.councilManager.setContinuation(true);
|
|
339
450
|
try {
|
|
340
|
-
yield* deps.processMessage(
|
|
451
|
+
yield* deps.processMessage(continuationPrompt, observer);
|
|
341
452
|
}
|
|
342
453
|
finally {
|
|
343
454
|
deps.councilManager.setContinuation(false);
|
|
@@ -345,6 +456,12 @@ export async function* executeToolEngine(args) {
|
|
|
345
456
|
}
|
|
346
457
|
return;
|
|
347
458
|
}
|
|
459
|
+
if (shouldSkipForReasoning) {
|
|
460
|
+
yield {
|
|
461
|
+
type: "content",
|
|
462
|
+
content: `\n[Auto-council skipped: ${deps.modelId} is a reasoning model and already performs internal self-debate. Set MUONROI_AUTOCOUNCIL_SKIP_REASONING=0 or autoCouncilSkipReasoning=false to force council.]\n`,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
348
465
|
if (deps.batchApi) {
|
|
349
466
|
try {
|
|
350
467
|
yield* deps.processMessageBatchTurn({
|
|
@@ -482,14 +599,29 @@ export async function* executeToolEngine(args) {
|
|
|
482
599
|
cachedImageCount: listCachedImages().length,
|
|
483
600
|
priorTurnHadTools: deps.messages.some((m) => m?.role === "tool"),
|
|
484
601
|
});
|
|
602
|
+
const depthTier = pilCtx.modelDepthTier ??
|
|
603
|
+
pilCtx.complexityTier ??
|
|
604
|
+
"standard";
|
|
485
605
|
const baseToolsRaw = createBuiltinTools(deps.bash, deps.mode, {
|
|
486
606
|
runTask: (request, abortSignal) => deps.runTask(request, combineAbortSignals(signal, abortSignal)),
|
|
487
607
|
runDelegation: (request, abortSignal) => deps.runDelegation(request, combineAbortSignals(signal, abortSignal)),
|
|
488
608
|
readDelegation: (id) => deps.readDelegation(id),
|
|
489
609
|
listDelegations: () => deps.listDelegations(),
|
|
490
610
|
modelId: turnModelId,
|
|
611
|
+
depthTier,
|
|
612
|
+
sessionId: deps.session?.id,
|
|
491
613
|
includeVisionTools,
|
|
492
614
|
consultParentSession: deps.consultParentSession,
|
|
615
|
+
runDebate: async (topic) => {
|
|
616
|
+
const gen = deps.runCouncilV2(topic, {
|
|
617
|
+
skipClarification: true,
|
|
618
|
+
userModelMessage: { role: "user", content: `/council ${topic}` },
|
|
619
|
+
});
|
|
620
|
+
for await (const chunk of gen) {
|
|
621
|
+
// Drain the generator
|
|
622
|
+
}
|
|
623
|
+
return deps.councilManager.lastSynthesis ?? "";
|
|
624
|
+
},
|
|
493
625
|
});
|
|
494
626
|
// Top-level cumulative cap state. We accumulate the raw tool set
|
|
495
627
|
// (base + MCP + PIL response tools) across the assembly below,
|
|
@@ -513,16 +645,23 @@ export async function* executeToolEngine(args) {
|
|
|
513
645
|
// history) is preserved.
|
|
514
646
|
const turnCaps = getProviderCapabilities(requireRuntimeProvider(runtime));
|
|
515
647
|
const _priorTurnHadTools = deps.messages.some((m) => m?.role === "tool");
|
|
648
|
+
// Direct-answer mode: purely informational Q&A (no code changes).
|
|
649
|
+
// Strip write tools (bash, edit_file, write_file) AND skip MCP;
|
|
650
|
+
// only readonly tools remain (read_file, grep, ee_query, etc.).
|
|
651
|
+
const isDirectAnswer = pilCtx.directAnswer === true;
|
|
516
652
|
let rawToolSet = !turnCaps.supportsClientTools(runtime.modelInfo)
|
|
517
653
|
? {}
|
|
518
654
|
: isChitchat && !_priorTurnHadTools
|
|
519
655
|
? {}
|
|
520
|
-
:
|
|
521
|
-
|
|
656
|
+
: isDirectAnswer && !_priorTurnHadTools
|
|
657
|
+
? stripWriteTools(baseToolsRaw)
|
|
658
|
+
: baseToolsRaw;
|
|
659
|
+
// MCP skip: chitchat / direct-answer / greeting inputs don't need 7 MCP servers'
|
|
522
660
|
// worth of tool schemas (~20K input tokens). PIL Layer 1 already
|
|
523
661
|
// gates this conservatively (≤10 chars + ≤2 words OR brain "none").
|
|
524
662
|
if (deps.mode === "agent" &&
|
|
525
663
|
(!isChitchat || _priorTurnHadTools) &&
|
|
664
|
+
!isDirectAnswer &&
|
|
526
665
|
turnCaps.supportsClientTools(runtime.modelInfo)) {
|
|
527
666
|
// Smart MCP filter: drop OPTIONAL MCP servers whose category the
|
|
528
667
|
// current message gives no signal for. Browser/vision servers
|
|
@@ -629,11 +768,14 @@ export async function* executeToolEngine(args) {
|
|
|
629
768
|
// Apply the top-level cumulative cap once over the fully-assembled
|
|
630
769
|
// raw tool set. State is per-turn; each turn gets a fresh budget.
|
|
631
770
|
const topLevelCap = wrapToolSetWithCap(rawToolSet, {
|
|
632
|
-
maxCumulativeChars: getTopLevelToolBudgetChars(deps.maxToolRounds),
|
|
771
|
+
maxCumulativeChars: getTopLevelToolBudgetChars(deps.maxToolRounds, contextWindow),
|
|
633
772
|
midTierRatio: 0.5,
|
|
634
773
|
highTierRatio: 0.8,
|
|
635
774
|
label: "top-level",
|
|
636
775
|
});
|
|
776
|
+
// Expose the cap state so the reactive-delegation signal can read this
|
|
777
|
+
// turn's cumulative tool load at turn end (see report at success exit).
|
|
778
|
+
_topLevelCapState = topLevelCap.state;
|
|
637
779
|
// Phase C3: layer cross-turn dedup on top of the top-level cap.
|
|
638
780
|
const tools = wrapToolSetWithReadBudget(wrapToolSetWithDedup(topLevelCap.tools, deps.crossTurnDedup), deps.readBudget);
|
|
639
781
|
// Wrap non-read-only tools in a turn-scoped mutex to prevent race conditions during parallel execution.
|
|
@@ -654,7 +796,15 @@ export async function* executeToolEngine(args) {
|
|
|
654
796
|
"selfverify_result",
|
|
655
797
|
"selfverify_list",
|
|
656
798
|
"list_vision_cache",
|
|
799
|
+
"ee_feedback",
|
|
800
|
+
"ee_write",
|
|
657
801
|
]);
|
|
802
|
+
// Task 8: native GSD mutation gate. Read once per turn (not per call) since
|
|
803
|
+
// hardGateEnabled/directAnswer don't change mid-turn; the gate itself
|
|
804
|
+
// re-reads STATE.md per call (cheap fs read) so it stays live if the
|
|
805
|
+
// model advances phase/verdict mid-turn via gsd_* tools.
|
|
806
|
+
const gsdHardGateEnabled = isGsdHardGateEnabled();
|
|
807
|
+
const gsdDirectAnswer = pilCtx.directAnswer;
|
|
658
808
|
for (const name of Object.keys(tools)) {
|
|
659
809
|
const tool = tools[name];
|
|
660
810
|
if (tool &&
|
|
@@ -663,6 +813,14 @@ export async function* executeToolEngine(args) {
|
|
|
663
813
|
!name.startsWith("respond_")) {
|
|
664
814
|
const originalExecute = tool.execute;
|
|
665
815
|
tool.execute = async (input, context) => {
|
|
816
|
+
const gate = evaluateMutationGate(deps.bash.getCwd(), {
|
|
817
|
+
toolName: name,
|
|
818
|
+
hardGateEnabled: gsdHardGateEnabled,
|
|
819
|
+
directAnswer: gsdDirectAnswer,
|
|
820
|
+
});
|
|
821
|
+
if (gate.blocked) {
|
|
822
|
+
return { success: false, output: gate.reason, error: gate.reason };
|
|
823
|
+
}
|
|
666
824
|
return writeMutex.run(() => originalExecute(input, context));
|
|
667
825
|
};
|
|
668
826
|
}
|
|
@@ -761,7 +919,40 @@ export async function* executeToolEngine(args) {
|
|
|
761
919
|
// (non-cached) system message via the slice below.
|
|
762
920
|
const mcpCapabilityBlock = buildMcpCapabilityBlock(Object.keys(tools));
|
|
763
921
|
const systemWithCaps = mcpCapabilityBlock ? `${systemWithShell}${mcpCapabilityBlock}` : systemWithShell;
|
|
764
|
-
|
|
922
|
+
// Task 3 — non-Claude prompt-cache prefix stability. On non-Claude the
|
|
923
|
+
// system is a single string; per-turn-dynamic content (dynamicSuffix +
|
|
924
|
+
// PIL suffix + MCP roster) that sits AFTER the byte-stable staticPrefix
|
|
925
|
+
// but BEFORE the conversation shifts the cached prefix and nukes the
|
|
926
|
+
// cache on PIL-active turns (session 47a774d272da: pil_active=1 ⟺
|
|
927
|
+
// cache_read=0). We keep the front byte-stable and relocate the dynamic
|
|
928
|
+
// tail into the trailing user message (variant b) — NOT a mid-conversation
|
|
929
|
+
// system-role message, which OpenAI-compatible providers (DeepSeek/GLM)
|
|
930
|
+
// do not reliably accept. Claude keeps its untouched two-block split.
|
|
931
|
+
const _isClaudeModel = runtime.modelId.startsWith("claude");
|
|
932
|
+
const { front: _nonClaudeFront, dynamicTail: _nonClaudeDynamicTail } = _isClaudeModel
|
|
933
|
+
? { front: systemWithCaps, dynamicTail: "" }
|
|
934
|
+
: splitFrontAndDynamicTail({
|
|
935
|
+
modelId: runtime.modelId,
|
|
936
|
+
systemWithCaps,
|
|
937
|
+
staticPrefix: systemParts.staticPrefix,
|
|
938
|
+
});
|
|
939
|
+
// Only relocate when the enriched user message is actually present to
|
|
940
|
+
// receive the tail; otherwise fall back to the original single string so
|
|
941
|
+
// no instruction content is dropped.
|
|
942
|
+
const _willFoldDynamicTail = !_isClaudeModel &&
|
|
943
|
+
_nonClaudeDynamicTail.trim().length > 0 &&
|
|
944
|
+
deps.messages.includes(userModelMessage);
|
|
945
|
+
if (process.env.MUONROI_DEBUG_CACHE_PREFIX === "1") {
|
|
946
|
+
try {
|
|
947
|
+
console.error(`[cache-prefix] model=${runtime.modelId} staticPrefixLen=${systemParts.staticPrefix.length} ` +
|
|
948
|
+
`frontLen=${_nonClaudeFront.length} dynTailLen=${_nonClaudeDynamicTail.length} ` +
|
|
949
|
+
`fold=${_willFoldDynamicTail} msgCount=${deps.messages.length}`);
|
|
950
|
+
}
|
|
951
|
+
catch (err) {
|
|
952
|
+
console.error(`[cache-prefix] log failed: ${err?.message}`);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
const systemForModel = _isClaudeModel
|
|
765
956
|
? [
|
|
766
957
|
{
|
|
767
958
|
role: "system",
|
|
@@ -773,7 +964,9 @@ export async function* executeToolEngine(args) {
|
|
|
773
964
|
content: systemWithCaps.slice(systemParts.staticPrefix.length),
|
|
774
965
|
},
|
|
775
966
|
]
|
|
776
|
-
:
|
|
967
|
+
: _willFoldDynamicTail
|
|
968
|
+
? _nonClaudeFront
|
|
969
|
+
: systemWithCaps;
|
|
777
970
|
// Capture prompt-size breakdown so recordUsage can attach it to the
|
|
778
971
|
// cost-log entry. Without this, "system prompt is huge" is unfalsifiable.
|
|
779
972
|
// chars/4 ≈ tokens for English; reported as chars to keep math obvious.
|
|
@@ -837,13 +1030,27 @@ export async function* executeToolEngine(args) {
|
|
|
837
1030
|
// stubs. Symmetric to the B3 sub-agent path; reuses the same module
|
|
838
1031
|
// with `label: "top-level"` so the stub text reflects which loop
|
|
839
1032
|
// elided the content.
|
|
840
|
-
const topLevelCompactThreshold = getTopLevelCompactThresholdChars();
|
|
841
|
-
const topLevelCompactKeepLast = getTopLevelCompactKeepLast();
|
|
1033
|
+
const topLevelCompactThreshold = getTopLevelCompactThresholdChars(contextWindow);
|
|
1034
|
+
const topLevelCompactKeepLast = getTopLevelCompactKeepLast(contextWindow);
|
|
1035
|
+
// O2 — byte budget for the verbatim tail; shrinks keepLast on read-heavy
|
|
1036
|
+
// turns that stay large at low fill (the 60-80k-per-call bucket).
|
|
1037
|
+
const topLevelCompactTailBudget = getTopLevelCompactTailBudgetChars(contextWindow);
|
|
1038
|
+
// O3 — compaction hysteresis state (per-turn; this scope runs once per
|
|
1039
|
+
// streamText turn). Once we compact, freeze the compacted prefix and
|
|
1040
|
+
// only append new messages until size grows past the hysteresis ceiling,
|
|
1041
|
+
// so the provider prompt-cache prefix stays byte-stable across steps
|
|
1042
|
+
// instead of breaking every step as the keepLast boundary slides.
|
|
1043
|
+
const compactHysteresis = getTopLevelCompactHysteresis();
|
|
1044
|
+
let hysteresisState = initCompactionHysteresisState();
|
|
842
1045
|
// Phase O1 — capture providerOptions SHAPE (types only) for forensics.
|
|
843
1046
|
deps.setLastProviderOptionsShape(Object.keys(providerOpts).length > 0 ? extractProviderOptionsShape(providerOpts) : null);
|
|
844
1047
|
// Substitute the enriched user message for the current turn so the LLM sees PIL additions,
|
|
845
1048
|
// while leaving the DB-persisted `deps.messages` clean for future turns.
|
|
846
|
-
const _messagesForCall = deps.messages.map((m) => m === userModelMessage
|
|
1049
|
+
const _messagesForCall = deps.messages.map((m) => m === userModelMessage
|
|
1050
|
+
? _willFoldDynamicTail
|
|
1051
|
+
? foldDynamicTailIntoUserMessage(userEnrichedMessage, _nonClaudeDynamicTail)
|
|
1052
|
+
: userEnrichedMessage
|
|
1053
|
+
: m);
|
|
847
1054
|
if (wireDebug.enabled) {
|
|
848
1055
|
wireDebug.logRequest({
|
|
849
1056
|
providerId: runtime.modelInfo?.provider ?? "unknown",
|
|
@@ -865,6 +1072,37 @@ export async function* executeToolEngine(args) {
|
|
|
865
1072
|
const _baseDynamicStopWhen = createToolLoopCapPredicate({
|
|
866
1073
|
initialCap: deps.maxToolRounds,
|
|
867
1074
|
ask: async (info) => {
|
|
1075
|
+
// Auto-recover a "cap" (tool-round ceiling) halt: compact the history and keep
|
|
1076
|
+
// going, instead of stopping and telling the user to /compact
|
|
1077
|
+
// (prompts.ts). Capped so a runaway turn still terminates;
|
|
1078
|
+
// pattern-loop halts are excluded (agent is stuck).
|
|
1079
|
+
if (shouldAutoRecoverToolLimit(info, toolLimitAutoRecoverCount, TOOL_LIMIT_AUTO_RECOVER_CAP)) {
|
|
1080
|
+
toolLimitAutoRecoverCount++;
|
|
1081
|
+
try {
|
|
1082
|
+
const _cw = runtime.modelInfo?.contextWindow ?? 0;
|
|
1083
|
+
if (_cw > 0) {
|
|
1084
|
+
await deps.compactForContext(provider, system, _cw, signal, deps.getCompactionSettings(_cw), false);
|
|
1085
|
+
// A compacted round resets context to O(N) input (cheap), so the
|
|
1086
|
+
// hard-cap's cost-runaway purpose is served — grant the turn more
|
|
1087
|
+
// headroom instead of letting the 1.5× hard ceiling strand a
|
|
1088
|
+
// genuinely productive long task. Bounded inside the Agent.
|
|
1089
|
+
deps.extendHardCeilingForAutoCompaction?.();
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
catch (err) {
|
|
1093
|
+
logger.error("orchestrator", "tool-limit auto-recover compaction failed", {
|
|
1094
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
const _ar2 = globalThis.__muonroiAgentRuntime;
|
|
1098
|
+
_ar2?.emitEvent({
|
|
1099
|
+
t: "event",
|
|
1100
|
+
kind: "toast",
|
|
1101
|
+
level: "info",
|
|
1102
|
+
text: `đạt giới hạn bước — đã tự nén ngữ cảnh và tiếp tục (lần ${toolLimitAutoRecoverCount}/${TOOL_LIMIT_AUTO_RECOVER_CAP})`,
|
|
1103
|
+
});
|
|
1104
|
+
return "continue";
|
|
1105
|
+
}
|
|
868
1106
|
if (info.kind === "pattern") {
|
|
869
1107
|
if (patternLoopInjectCount < 1) {
|
|
870
1108
|
patternLoopInjectCount++;
|
|
@@ -1081,8 +1319,24 @@ export async function* executeToolEngine(args) {
|
|
|
1081
1319
|
// on every chunk via stall.pet(), so it never kills an actively
|
|
1082
1320
|
// streaming call. Disposed when the stream ends or errors.
|
|
1083
1321
|
stallTriggered = false;
|
|
1322
|
+
// Second timer (progressTimeoutMs) is the no-forward-progress guard.
|
|
1323
|
+
// stall.pet() re-arms the any-activity timer on EVERY chunk — including
|
|
1324
|
+
// a reasoning model's reasoning-delta — so an endless chain-of-thought
|
|
1325
|
+
// keeps it alive and it never fires (observed live 2026-07-10: a
|
|
1326
|
+
// deepseek-v4-flash sub-SESSION churned reasoning 30+ min, 1.4M input
|
|
1327
|
+
// tokens, ZERO text/tool output; the 2-min stall watchdog never tripped).
|
|
1328
|
+
// The progress timer is reset ONLY by stall.petProgress() on real output
|
|
1329
|
+
// (text-delta / tool-call), aborting a runaway-reasoning loop while a
|
|
1330
|
+
// legitimately long reasoning burst that DOES emit output survives.
|
|
1084
1331
|
const stall = createStallWatchdog(getProviderStallTimeoutMs(), () => {
|
|
1085
1332
|
stallTriggered = true;
|
|
1333
|
+
}, {
|
|
1334
|
+
progressTimeoutMs: getProviderProgressTimeoutMs(),
|
|
1335
|
+
onProgressFire: () => {
|
|
1336
|
+
stallTriggered = true;
|
|
1337
|
+
console.error(`[tool-engine] stream aborted: no text/tool output for ${getProviderProgressTimeoutMs()}ms ` +
|
|
1338
|
+
`(runaway reasoning / no forward progress) model=${runtime.modelId}`);
|
|
1339
|
+
},
|
|
1086
1340
|
});
|
|
1087
1341
|
// F3c — hard-cap LLM calls per turn before this streamText()
|
|
1088
1342
|
if (++llmCallsThisTurn > MAX_LLM_CALLS_PER_TURN) {
|
|
@@ -1260,10 +1514,17 @@ export async function* executeToolEngine(args) {
|
|
|
1260
1514
|
};
|
|
1261
1515
|
// T1.1 + T1.2 — reasoning models (DeepSeek V4 Flash, R1) emit 2K-5K
|
|
1262
1516
|
// CoT tokens per turn that accumulate across the multi-step loop.
|
|
1263
|
-
// Strip old reasoning and compact earlier
|
|
1264
|
-
//
|
|
1517
|
+
// Strip old reasoning and compact earlier to cut cumulative input.
|
|
1518
|
+
// Small-context reasoning models (< 100K) use ratio 0.2 (fire at
|
|
1519
|
+
// 20% fill) because their per-step overhead (system + tools + CoT)
|
|
1520
|
+
// already consumes ~30-40% of the window, leaving little headroom.
|
|
1265
1521
|
const isReasoningModel = runtime.modelInfo?.reasoning === true;
|
|
1266
|
-
const
|
|
1522
|
+
const reasoningFillRatio = isReasoningModel
|
|
1523
|
+
? contextWindowTokens > 0 && contextWindowTokens < 100_000
|
|
1524
|
+
? 0.2
|
|
1525
|
+
: 0.3
|
|
1526
|
+
: undefined;
|
|
1527
|
+
const runCompaction = () => compactSubAgentMessages(stripped, {
|
|
1267
1528
|
thresholdChars: topLevelCompactThreshold,
|
|
1268
1529
|
// Rec #1 (cheap part): on meta/self-eval turns keep a couple more
|
|
1269
1530
|
// trailing tool turns verbatim — those carry the reasoning the
|
|
@@ -1274,28 +1535,120 @@ export async function* executeToolEngine(args) {
|
|
|
1274
1535
|
label: "top-level",
|
|
1275
1536
|
envelopeChars,
|
|
1276
1537
|
contextWindowTokens,
|
|
1277
|
-
contextFillRatio:
|
|
1538
|
+
contextFillRatio: reasoningFillRatio,
|
|
1278
1539
|
keepToolIds: keepToolIds.length ? keepToolIds : undefined,
|
|
1279
1540
|
persistArtifact,
|
|
1280
1541
|
stripOldReasoning: isReasoningModel,
|
|
1542
|
+
tailBudgetChars: topLevelCompactTailBudget,
|
|
1281
1543
|
});
|
|
1282
|
-
|
|
1283
|
-
|
|
1544
|
+
// O3 — compaction hysteresis (holds the frozen compacted prefix
|
|
1545
|
+
// between compactions so the provider prompt-cache prefix stays
|
|
1546
|
+
// byte-stable across steps instead of breaking as the keepLast
|
|
1547
|
+
// boundary slides). See applyCompactionHysteresis.
|
|
1548
|
+
const currChars = cumulativeMessageChars(stripped) + envelopeChars;
|
|
1549
|
+
// Proactive compaction (agent called the `compact` tool). Consume the
|
|
1550
|
+
// one-shot request and FORCE a compaction this step, bypassing the
|
|
1551
|
+
// hysteresis threshold — the agent explicitly asked to shed context,
|
|
1552
|
+
// so a one-time cache-prefix break is the intended trade. Re-seed the
|
|
1553
|
+
// hysteresis state to the fresh compacted prefix so the following
|
|
1554
|
+
// steps hold it steady (no per-step churn) until it grows again.
|
|
1555
|
+
const _proactiveCompact = consumeProactiveCompact();
|
|
1556
|
+
let compacted;
|
|
1557
|
+
if (_proactiveCompact) {
|
|
1558
|
+
const _forced = runCompaction();
|
|
1559
|
+
const _didForce = _forced !== stripped;
|
|
1560
|
+
compacted = _forced;
|
|
1561
|
+
hysteresisState = _didForce
|
|
1562
|
+
? { frozenCompacted: _forced, frozenStrippedLen: stripped.length, lastCompactTriggerChars: currChars }
|
|
1563
|
+
: hysteresisState;
|
|
1564
|
+
if (_didForce)
|
|
1565
|
+
recordCompaction(sn);
|
|
1566
|
+
try {
|
|
1567
|
+
const _arPc = globalThis.__muonroiAgentRuntime;
|
|
1568
|
+
_arPc?.emitEvent({
|
|
1569
|
+
t: "event",
|
|
1570
|
+
kind: "toast",
|
|
1571
|
+
level: "info",
|
|
1572
|
+
text: _didForce
|
|
1573
|
+
? "đã nén ngữ cảnh theo yêu cầu của agent — tiếp tục tác vụ"
|
|
1574
|
+
: "agent yêu cầu nén nhưng chưa có gì để nén — tiếp tục",
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
catch {
|
|
1578
|
+
/* toast best-effort */
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
else {
|
|
1582
|
+
const _hyst = applyCompactionHysteresis({
|
|
1583
|
+
stripped,
|
|
1584
|
+
currChars,
|
|
1585
|
+
hysteresis: compactHysteresis,
|
|
1586
|
+
state: hysteresisState,
|
|
1587
|
+
runCompaction,
|
|
1588
|
+
});
|
|
1589
|
+
compacted = _hyst.compacted;
|
|
1590
|
+
hysteresisState = _hyst.state;
|
|
1591
|
+
// Count only ACTUAL (re)compactions, not held-boundary steps — the
|
|
1592
|
+
// compaction counter drives the cache-churn telemetry this fixes.
|
|
1593
|
+
if (_hyst.didRecompact)
|
|
1594
|
+
recordCompaction(sn);
|
|
1595
|
+
}
|
|
1596
|
+
const coalesced = coalesceReadOnlyMessages(compacted);
|
|
1597
|
+
// (recordCompaction already handled per-branch above.)
|
|
1284
1598
|
// Pre-compaction visibility: give the agent one step of notice
|
|
1285
1599
|
// before B4 actually rewrites history into stubs. This is the
|
|
1286
1600
|
// advance warning that was missing — agent can now decide to
|
|
1287
1601
|
// summarize, finish, or request preservation. Fires when we did
|
|
1288
1602
|
// NOT compact this step (compacted === stripped, restored by the
|
|
1289
|
-
// compactSubAgentMessages no-op ref contract) AND the prompt is
|
|
1290
|
-
// approaching the threshold. Must compare CHARS (messages +
|
|
1291
|
-
// envelope), not stripped.length (a message count that never
|
|
1292
|
-
// exceeds a char-scaled threshold) — session 2b7a10219499.
|
|
1293
1603
|
const _preWarnChars = cumulativeMessageChars(stripped) + envelopeChars;
|
|
1294
|
-
if (
|
|
1604
|
+
if (coalesced === stripped && shouldPreWarnCompaction(_preWarnChars, topLevelCompactThreshold)) {
|
|
1295
1605
|
const _cp = buildCheckpointReminder(sn, true);
|
|
1296
1606
|
const _pre = `[pre-compaction warning at step ${sn} — next step(s) will likely rewrite older tool results to stubs (threshold ${topLevelCompactThreshold}, keepLast=${topLevelCompactKeepLast}). ${_cp} Summarize or finish if possible, or warn the user they can run the "/compact" command if they want a clean compressed history.]`;
|
|
1297
1607
|
return withSteers({ messages: attachReminderToMessages(stripped, _pre) });
|
|
1298
1608
|
}
|
|
1609
|
+
// ---- Read-only tool batching interceptor ----
|
|
1610
|
+
// The system prompt already instructs batching (BATCH ALL TOOL
|
|
1611
|
+
// CALLS — HARD RULE), but models like DeepSeek frequently ignore
|
|
1612
|
+
// it and emit 1 read-only call per step. This injects a concrete,
|
|
1613
|
+
// in-context reminder right before the next LLM call when the
|
|
1614
|
+
// previous step had ≤2 all-read-only tool calls. Cost: ~0 extra
|
|
1615
|
+
// context. Effect: reduces tool rounds 2-3x on the same work.
|
|
1616
|
+
if (sn >= 1) {
|
|
1617
|
+
let _lastAsst = null;
|
|
1618
|
+
for (let _i = stepMessages.length - 1; _i >= 0; _i--) {
|
|
1619
|
+
if (stepMessages[_i].role === "assistant") {
|
|
1620
|
+
_lastAsst = stepMessages[_i];
|
|
1621
|
+
break;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
if (_lastAsst && Array.isArray(_lastAsst.content)) {
|
|
1625
|
+
let _total = 0, _ro = 0;
|
|
1626
|
+
for (const _p of _lastAsst.content) {
|
|
1627
|
+
if (_p.type === "tool-call") {
|
|
1628
|
+
_total++;
|
|
1629
|
+
if (READ_ONLY_TOOLS.has(_p.toolName))
|
|
1630
|
+
_ro++;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
if (_total > 0 && _ro === _total && _total <= 2) {
|
|
1634
|
+
// Prefer the SINGLE-CALL multi-path form (read_file file_paths=[…])
|
|
1635
|
+
// over "parallel tool_calls": a batch of parallel tool_calls is
|
|
1636
|
+
// reshaped into sequential single-call turns for the kimi/glm/
|
|
1637
|
+
// deepseek-go cohort (splitParallelToolCalls) — which re-inflates
|
|
1638
|
+
// history and defeats the batching win. One read_file carrying N
|
|
1639
|
+
// paths is ONE tool_call → never split → the token cut holds on
|
|
1640
|
+
// every provider.
|
|
1641
|
+
const _b = `[Tool batching: you called ${_total} read-only tool(s) one-at-a-time — each extra call re-sends the whole conversation, so N single reads cost O(N²) tokens. To read MULTIPLE files, call read_file ONCE with file_paths=["a","b","c"] — a SINGLE tool_call that no provider splits. For other read-only tools (grep, bash_output_get), emit all pending calls in ONE assistant turn. Do NOT sequence reads across steps.]`;
|
|
1642
|
+
// Attach to `coalesced` (the B4-compacted history), NOT `stripped`:
|
|
1643
|
+
// read-only tools (read_file/grep/…) are exactly what triggers this
|
|
1644
|
+
// reminder, and returning `stripped` here silently discarded the
|
|
1645
|
+
// compaction computed above — so on any loop where the model emits
|
|
1646
|
+
// ≤2 read-only calls per step (DeepSeek does this constantly) older
|
|
1647
|
+
// tool results were never elided and cumulative input grew unbounded.
|
|
1648
|
+
return withSteers({ messages: attachReminderToMessages(coalesced, _b) });
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1299
1652
|
// Phase 4A — scope reminder injection (REQ-005).
|
|
1300
1653
|
// Cadence K = 3/5/8 for small/medium/large. Soft-warn fires
|
|
1301
1654
|
// ONCE per session at floor(ceiling*0.7). Reminder lives in
|
|
@@ -1355,10 +1708,10 @@ export async function* executeToolEngine(args) {
|
|
|
1355
1708
|
? `${buildRepetitionReminder(_ceilingSessionId)}\n${_scopePart}`
|
|
1356
1709
|
: buildRepetitionReminder(_ceilingSessionId)
|
|
1357
1710
|
: _scopePart;
|
|
1358
|
-
const withReminder = attachReminderToMessages(
|
|
1711
|
+
const withReminder = attachReminderToMessages(coalesced, _reminder);
|
|
1359
1712
|
return withSteers({ messages: withReminder });
|
|
1360
1713
|
}
|
|
1361
|
-
if (
|
|
1714
|
+
if (coalesced === stripped && stripped === stepMessages)
|
|
1362
1715
|
return withSteers({});
|
|
1363
1716
|
// Self-awareness note: tell the model compaction happened so it
|
|
1364
1717
|
// knows earlier context was elided and can adjust its behavior.
|
|
@@ -1377,12 +1730,12 @@ export async function* executeToolEngine(args) {
|
|
|
1377
1730
|
})()
|
|
1378
1731
|
: null;
|
|
1379
1732
|
if (_compactNote) {
|
|
1380
|
-
return withSteers({ messages: attachReminderToMessages(
|
|
1733
|
+
return withSteers({ messages: attachReminderToMessages(coalesced, _compactNote) });
|
|
1381
1734
|
}
|
|
1382
|
-
return withSteers({ messages:
|
|
1735
|
+
return withSteers({ messages: coalesced });
|
|
1383
1736
|
},
|
|
1384
|
-
...(
|
|
1385
|
-
...(dropParam("maxOutputTokens") ? {} : { maxOutputTokens:
|
|
1737
|
+
...resolveTemperatureParam(runtime, 0.7),
|
|
1738
|
+
...(dropParam("maxOutputTokens") ? {} : { maxOutputTokens: resolveTurnMaxOutputTokens(pilCtx) }),
|
|
1386
1739
|
...(Object.keys(providerOpts).length > 0 ? { providerOptions: providerOpts } : {}),
|
|
1387
1740
|
experimental_onStepStart: (event) => {
|
|
1388
1741
|
stepNumber = getStepNumber(event, stepNumber + 1);
|
|
@@ -1401,6 +1754,10 @@ export async function* executeToolEngine(args) {
|
|
|
1401
1754
|
finishReason: getFinishReason(event),
|
|
1402
1755
|
usage: stepUsage,
|
|
1403
1756
|
});
|
|
1757
|
+
// Pull any completed background delegations so their results can be
|
|
1758
|
+
// injected (as system messages) for the *next* LLM step in this same turn.
|
|
1759
|
+
// This improves "self wake" for background jobs without waiting for a new user turn.
|
|
1760
|
+
void deps.consumeBackgroundNotifications?.().catch(() => { });
|
|
1404
1761
|
// Realtime status bar update per step
|
|
1405
1762
|
if (stepUsage.inputTokens || stepUsage.outputTokens) {
|
|
1406
1763
|
// O1 — thread THIS turn's providerOptions shape per step so every
|
|
@@ -1475,8 +1832,23 @@ export async function* executeToolEngine(args) {
|
|
|
1475
1832
|
wireDebug.logError(_wireProviderIdTop, part.error);
|
|
1476
1833
|
}
|
|
1477
1834
|
}
|
|
1835
|
+
// Terminal part of the ENTIRE multi-step turn (AI SDK v6 emits exactly
|
|
1836
|
+
// one `finish` after every step's `finish-step`; it carries the final
|
|
1837
|
+
// finishReason/totalUsage). onFinish has already run by now (usage
|
|
1838
|
+
// recorded + llm-done emitted, and result.response is resolved), so
|
|
1839
|
+
// there is nothing left to drain. Some providers — observed live:
|
|
1840
|
+
// xai/grok-composer-2.5-fast — emit `finish` but then never CLOSE the
|
|
1841
|
+
// fullStream async iterator, so `for await` would block on the next
|
|
1842
|
+
// `.next()` until the turn watchdog fires (up to MUONROI_TURN_IDLE_MS).
|
|
1843
|
+
// Breaking on `finish` finalizes the turn immediately instead. Safe:
|
|
1844
|
+
// `finish` is strictly last, so this can never truncate a multi-step
|
|
1845
|
+
// turn (per-step boundaries are `finish-step`, handled by fall-through).
|
|
1846
|
+
if (part.type === "finish") {
|
|
1847
|
+
break;
|
|
1848
|
+
}
|
|
1478
1849
|
switch (part.type) {
|
|
1479
1850
|
case "text-delta":
|
|
1851
|
+
stall.petProgress(); // real forward progress — reset the no-progress guard
|
|
1480
1852
|
assistantText += part.text;
|
|
1481
1853
|
// Task 2.6b — emit llm-token (agent-mode only; high-volume, default-off per Phase 4).
|
|
1482
1854
|
try {
|
|
@@ -1508,6 +1880,7 @@ export async function* executeToolEngine(args) {
|
|
|
1508
1880
|
yield { type: "reasoning", content: part.text };
|
|
1509
1881
|
break;
|
|
1510
1882
|
case "tool-call": {
|
|
1883
|
+
stall.petProgress(); // real forward progress — reset the no-progress guard
|
|
1511
1884
|
const tc = toToolCall(part);
|
|
1512
1885
|
activeToolCalls.push(tc);
|
|
1513
1886
|
// SAMR: track that Phase 1 produced tool calls → transition to Phase 2
|
|
@@ -1728,10 +2101,10 @@ export async function* executeToolEngine(args) {
|
|
|
1728
2101
|
// the global __muonroiSafetyApproved map so registry.ts's
|
|
1729
2102
|
// bash.execute bypasses the block on the retry.
|
|
1730
2103
|
const _outputText = [tr.output, tr.error].filter((x) => typeof x === "string").join("\n");
|
|
1731
|
-
const
|
|
1732
|
-
if (
|
|
1733
|
-
const _blockKind =
|
|
1734
|
-
const _blockReason =
|
|
2104
|
+
const _parsedBlock = parseSafetyBlock(_outputText);
|
|
2105
|
+
if (_parsedBlock && part.toolName === "bash") {
|
|
2106
|
+
const _blockKind = _parsedBlock.kind;
|
|
2107
|
+
const _blockReason = _parsedBlock.reason;
|
|
1735
2108
|
const _command = typeof part.input === "object" && part.input != null
|
|
1736
2109
|
? String(part.input.command ?? "")
|
|
1737
2110
|
: "";
|
|
@@ -1744,15 +2117,33 @@ export async function* executeToolEngine(args) {
|
|
|
1744
2117
|
tr = { ...tr, success: false, error: _outputText, output: _outputText };
|
|
1745
2118
|
}
|
|
1746
2119
|
else {
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
2120
|
+
// yolo mode auto-approves lower-severity blocks (git-safety /
|
|
2121
|
+
// dangerous) so "don't ask me" actually stops asking — but a
|
|
2122
|
+
// catastrophic, irreversible command STILL shows the askcard,
|
|
2123
|
+
// even in yolo. Everything else surfaces the interactive
|
|
2124
|
+
// safety-override card via deps.askSafetyOverride (registered
|
|
2125
|
+
// by the TUI); when no handler is wired (headless / batch),
|
|
2126
|
+
// that resolves to { action: "block" }, preserving the
|
|
2127
|
+
// backward-compatible hard-stop for non-interactive runs.
|
|
2128
|
+
let _verdict;
|
|
2129
|
+
if (shouldAutoAllowYolo(_blockKind, deps.permissionMode)) {
|
|
2130
|
+
_verdict = { action: "allow-once" };
|
|
2131
|
+
yield {
|
|
2132
|
+
type: "content",
|
|
2133
|
+
content: `[yolo: auto-approved blocked command: ${_blockKind}]\n`,
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
else {
|
|
2137
|
+
_verdict = deps.askSafetyOverride
|
|
2138
|
+
? await deps.askSafetyOverride({
|
|
2139
|
+
kind: _blockKind,
|
|
2140
|
+
toolName: part.toolName,
|
|
2141
|
+
blockedItem: _command,
|
|
2142
|
+
reason: _blockReason,
|
|
2143
|
+
source: "bash.execute",
|
|
2144
|
+
})
|
|
2145
|
+
: { action: "block" };
|
|
2146
|
+
}
|
|
1756
2147
|
if (_verdict.action === "allow-once" || _verdict.action === "allow-session") {
|
|
1757
2148
|
// Store approval so registry.ts can bypass the block on retry.
|
|
1758
2149
|
const _globalSafety = globalThis;
|
|
@@ -1886,10 +2277,26 @@ export async function* executeToolEngine(args) {
|
|
|
1886
2277
|
// dedicated chunk so the sticky checklist panel can re-render
|
|
1887
2278
|
// without parsing tool args itself. Skipped when the snapshot
|
|
1888
2279
|
// doesn't parse (malformed args) so the UI is never poisoned.
|
|
1889
|
-
if (tc.function.name === "todo_write"
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
2280
|
+
if (tr.success && (tc.function.name === "todo_write" || tc.function.name.startsWith("gsd_"))) {
|
|
2281
|
+
try {
|
|
2282
|
+
const { getTaskListSnapshotFromGsd } = require("../gsd/phase-sync.js");
|
|
2283
|
+
const snap = getTaskListSnapshotFromGsd(deps.bash.getCwd());
|
|
2284
|
+
if (snap) {
|
|
2285
|
+
yield { type: "task_list_update", taskListSnapshot: snap };
|
|
2286
|
+
}
|
|
2287
|
+
else if (tc.function.name === "todo_write") {
|
|
2288
|
+
const snapLegacy = snapshotFromTodoWriteArgs(tc.function.arguments);
|
|
2289
|
+
if (snapLegacy)
|
|
2290
|
+
yield { type: "task_list_update", taskListSnapshot: snapLegacy };
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
catch (err) {
|
|
2294
|
+
if (tc.function.name === "todo_write") {
|
|
2295
|
+
const snapLegacy = snapshotFromTodoWriteArgs(tc.function.arguments);
|
|
2296
|
+
if (snapLegacy)
|
|
2297
|
+
yield { type: "task_list_update", taskListSnapshot: snapLegacy };
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
1893
2300
|
}
|
|
1894
2301
|
break;
|
|
1895
2302
|
}
|
|
@@ -3021,6 +3428,10 @@ export async function* executeToolEngine(args) {
|
|
|
3021
3428
|
if (modelInfo?.contextWindow) {
|
|
3022
3429
|
void deps.postTurnCompact(provider, system, modelInfo.contextWindow, signal).catch(() => { });
|
|
3023
3430
|
}
|
|
3431
|
+
// Reactive delegation: report this turn's observed tool-output load so
|
|
3432
|
+
// the next turn can escalate to an isolated sub-session when it proves
|
|
3433
|
+
// heavy — independent of the fragile upfront router. See reactive-delegation.ts.
|
|
3434
|
+
deps.reportTurnToolLoad?.(_topLevelCapState?.cumulative ?? 0);
|
|
3024
3435
|
yield { type: "done" };
|
|
3025
3436
|
return;
|
|
3026
3437
|
}
|
|
@@ -3167,6 +3578,10 @@ export async function* executeToolEngine(args) {
|
|
|
3167
3578
|
if (modelInfo?.contextWindow) {
|
|
3168
3579
|
void deps.postTurnCompact(provider, system, modelInfo.contextWindow, signal).catch(() => { });
|
|
3169
3580
|
}
|
|
3581
|
+
// Reactive delegation: report this turn's observed tool-output load so
|
|
3582
|
+
// the next turn can escalate to an isolated sub-session when it proves
|
|
3583
|
+
// heavy — independent of the fragile upfront router. See reactive-delegation.ts.
|
|
3584
|
+
deps.reportTurnToolLoad?.(_topLevelCapState?.cumulative ?? 0);
|
|
3170
3585
|
yield { type: "done" };
|
|
3171
3586
|
return;
|
|
3172
3587
|
}
|
|
@@ -3184,4 +3599,153 @@ export async function* executeToolEngine(args) {
|
|
|
3184
3599
|
}
|
|
3185
3600
|
}
|
|
3186
3601
|
}
|
|
3602
|
+
export function coalesceReadOnlyMessages(messages) {
|
|
3603
|
+
if (!messages || messages.length === 0)
|
|
3604
|
+
return messages;
|
|
3605
|
+
const READ_ONLY_TOOLS = new Set([
|
|
3606
|
+
"read_file",
|
|
3607
|
+
"grep",
|
|
3608
|
+
"bash_output_get",
|
|
3609
|
+
"process_list",
|
|
3610
|
+
"delegation_read",
|
|
3611
|
+
"delegation_list",
|
|
3612
|
+
"ee_query",
|
|
3613
|
+
"ee_health",
|
|
3614
|
+
"usage_forensics",
|
|
3615
|
+
"lsp_query",
|
|
3616
|
+
"setup_guide",
|
|
3617
|
+
"selfverify_status",
|
|
3618
|
+
"selfverify_result",
|
|
3619
|
+
"selfverify_list",
|
|
3620
|
+
"list_vision_cache",
|
|
3621
|
+
"ee_feedback",
|
|
3622
|
+
"ee_write",
|
|
3623
|
+
]);
|
|
3624
|
+
const result = [];
|
|
3625
|
+
let lastUserIdx = -1;
|
|
3626
|
+
for (let k = messages.length - 1; k >= 0; k--) {
|
|
3627
|
+
if (messages[k].role === "user") {
|
|
3628
|
+
lastUserIdx = k;
|
|
3629
|
+
break;
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
if (lastUserIdx === -1)
|
|
3633
|
+
return messages;
|
|
3634
|
+
for (let k = 0; k <= lastUserIdx; k++) {
|
|
3635
|
+
result.push(messages[k]);
|
|
3636
|
+
}
|
|
3637
|
+
const postUser = messages.slice(lastUserIdx + 1);
|
|
3638
|
+
const groups = [];
|
|
3639
|
+
let currentAsst = null;
|
|
3640
|
+
for (const msg of postUser) {
|
|
3641
|
+
if (msg.role === "assistant") {
|
|
3642
|
+
if (currentAsst) {
|
|
3643
|
+
groups.push({ assistant: currentAsst, tool: null });
|
|
3644
|
+
}
|
|
3645
|
+
currentAsst = msg;
|
|
3646
|
+
}
|
|
3647
|
+
else if (msg.role === "tool") {
|
|
3648
|
+
if (currentAsst) {
|
|
3649
|
+
groups.push({ assistant: currentAsst, tool: msg });
|
|
3650
|
+
currentAsst = null;
|
|
3651
|
+
}
|
|
3652
|
+
else {
|
|
3653
|
+
result.push(msg);
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
else {
|
|
3657
|
+
if (currentAsst) {
|
|
3658
|
+
groups.push({ assistant: currentAsst, tool: null });
|
|
3659
|
+
currentAsst = null;
|
|
3660
|
+
}
|
|
3661
|
+
result.push(msg);
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (currentAsst) {
|
|
3665
|
+
groups.push({ assistant: currentAsst, tool: null });
|
|
3666
|
+
}
|
|
3667
|
+
const coalescedGroups = [];
|
|
3668
|
+
for (const group of groups) {
|
|
3669
|
+
const isReadOnly = (() => {
|
|
3670
|
+
if (!group.tool || !group.assistant)
|
|
3671
|
+
return false;
|
|
3672
|
+
const toolCalls = getToolCalls(group.assistant);
|
|
3673
|
+
if (toolCalls.length === 0)
|
|
3674
|
+
return false;
|
|
3675
|
+
return toolCalls.every((tc) => {
|
|
3676
|
+
const name = tc.toolName || tc.function?.name;
|
|
3677
|
+
return name && READ_ONLY_TOOLS.has(name);
|
|
3678
|
+
});
|
|
3679
|
+
})();
|
|
3680
|
+
if (isReadOnly) {
|
|
3681
|
+
const prev = coalescedGroups[coalescedGroups.length - 1];
|
|
3682
|
+
const prevIsReadOnly = prev &&
|
|
3683
|
+
(() => {
|
|
3684
|
+
if (!prev.tool || !prev.assistant)
|
|
3685
|
+
return false;
|
|
3686
|
+
const toolCalls = getToolCalls(prev.assistant);
|
|
3687
|
+
if (toolCalls.length === 0)
|
|
3688
|
+
return false;
|
|
3689
|
+
return toolCalls.every((tc) => {
|
|
3690
|
+
const name = tc.toolName || tc.function?.name;
|
|
3691
|
+
return name && READ_ONLY_TOOLS.has(name);
|
|
3692
|
+
});
|
|
3693
|
+
})();
|
|
3694
|
+
if (prevIsReadOnly) {
|
|
3695
|
+
prev.assistant = mergeAssistantMessages(prev.assistant, group.assistant);
|
|
3696
|
+
prev.tool = mergeToolMessages(prev.tool, group.tool);
|
|
3697
|
+
}
|
|
3698
|
+
else {
|
|
3699
|
+
coalescedGroups.push({ ...group });
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
else {
|
|
3703
|
+
coalescedGroups.push(group);
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
for (const group of coalescedGroups) {
|
|
3707
|
+
if (group.assistant)
|
|
3708
|
+
result.push(group.assistant);
|
|
3709
|
+
if (group.tool)
|
|
3710
|
+
result.push(group.tool);
|
|
3711
|
+
}
|
|
3712
|
+
return result;
|
|
3713
|
+
}
|
|
3714
|
+
function getToolCalls(msg) {
|
|
3715
|
+
if (Array.isArray(msg.toolCalls))
|
|
3716
|
+
return msg.toolCalls;
|
|
3717
|
+
if (Array.isArray(msg.content)) {
|
|
3718
|
+
return msg.content.filter((p) => p && p.type === "tool-call");
|
|
3719
|
+
}
|
|
3720
|
+
return [];
|
|
3721
|
+
}
|
|
3722
|
+
function mergeAssistantMessages(msg1, msg2) {
|
|
3723
|
+
const parts1 = Array.isArray(msg1.content)
|
|
3724
|
+
? msg1.content
|
|
3725
|
+
: typeof msg1.content === "string" && msg1.content
|
|
3726
|
+
? [{ type: "text", text: msg1.content }]
|
|
3727
|
+
: [];
|
|
3728
|
+
const parts2 = Array.isArray(msg2.content)
|
|
3729
|
+
? msg2.content
|
|
3730
|
+
: typeof msg2.content === "string" && msg2.content
|
|
3731
|
+
? [{ type: "text", text: msg2.content }]
|
|
3732
|
+
: [];
|
|
3733
|
+
const mergedContent = [...parts1, ...parts2];
|
|
3734
|
+
const res = {
|
|
3735
|
+
role: "assistant",
|
|
3736
|
+
content: mergedContent,
|
|
3737
|
+
};
|
|
3738
|
+
if (Array.isArray(msg1.toolCalls) || Array.isArray(msg2.toolCalls)) {
|
|
3739
|
+
res.toolCalls = [...(msg1.toolCalls ?? []), ...(msg2.toolCalls ?? [])];
|
|
3740
|
+
}
|
|
3741
|
+
return res;
|
|
3742
|
+
}
|
|
3743
|
+
function mergeToolMessages(msg1, msg2) {
|
|
3744
|
+
const parts1 = Array.isArray(msg1.content) ? msg1.content : [];
|
|
3745
|
+
const parts2 = Array.isArray(msg2.content) ? msg2.content : [];
|
|
3746
|
+
return {
|
|
3747
|
+
role: "tool",
|
|
3748
|
+
content: [...parts1, ...parts2],
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3187
3751
|
//# sourceMappingURL=tool-engine.js.map
|