muonroi-cli 1.8.3 → 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 +21 -21
- package/README.md +133 -122
- 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 +30 -15
- 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 +254 -84
- 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/oauth-callback.js +2 -2
- package/dist/src/mcp/research-onboarding.js +8 -7
- package/dist/src/mcp/runtime.js +34 -2
- package/dist/src/mcp/setup-guide-text.d.ts +1 -1
- package/dist/src/mcp/setup-guide-text.js +77 -76
- 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 +8 -8
- 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 +159 -159
- 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 +26 -0
- package/dist/src/orchestrator/tool-engine.js +669 -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/layer2_5-ponytail.js +8 -8
- 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/done-gate.js +3 -3
- 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 +327 -116
- 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/progress-snapshot.js +4 -4
- 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 +82 -172
- 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/reporter/index.js +1 -1
- 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/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
- package/dist/src/scaffold/continuation-prompt.js +86 -60
- package/dist/src/scaffold/init-new.js +453 -453
- 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 +22 -22
- 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/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +196 -126
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.d.ts +28 -10
- package/dist/src/storage/sessions.js +112 -55
- package/dist/src/storage/transcript-view.js +1 -1
- package/dist/src/storage/transcript.d.ts +51 -0
- package/dist/src/storage/transcript.js +383 -112
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- 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-inspect.js +4 -4
- 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/clipboard-image.js +23 -23
- package/dist/src/utils/install-manager.js +14 -11
- 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/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- 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
|
|
@@ -193,10 +225,65 @@ export function rewriteSafetyApprovedToolResults(messages) {
|
|
|
193
225
|
return changed ? { ...m, content: newContent } : m;
|
|
194
226
|
});
|
|
195
227
|
}
|
|
228
|
+
export class SimpleMutex {
|
|
229
|
+
queue = Promise.resolve();
|
|
230
|
+
async run(fn) {
|
|
231
|
+
let resolveLock;
|
|
232
|
+
const lockPromise = new Promise((resolve) => {
|
|
233
|
+
resolveLock = resolve;
|
|
234
|
+
});
|
|
235
|
+
const previous = this.queue;
|
|
236
|
+
this.queue = lockPromise;
|
|
237
|
+
await previous;
|
|
238
|
+
try {
|
|
239
|
+
return await fn();
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
resolveLock();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
196
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
|
+
}
|
|
197
276
|
export async function* executeToolEngine(args) {
|
|
198
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;
|
|
199
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();
|
|
200
287
|
let stallTriggered = false;
|
|
201
288
|
// Time-to-first-byte stall RE-PROMPT: some providers (observed:
|
|
202
289
|
// xai/grok-build-0.1) accept the request then never send the first byte —
|
|
@@ -225,6 +312,10 @@ export async function* executeToolEngine(args) {
|
|
|
225
312
|
// the turn when exceeded. Prevents the session 526a83cf22df pattern
|
|
226
313
|
// where 3 user messages burnt 82% of 2.44M tokens in 36 LLM calls.
|
|
227
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;
|
|
228
319
|
// Live-queue steering: messages the user typed mid-turn are drained at a
|
|
229
320
|
// prepareStep boundary and accumulated here, then re-appended (deduped) to
|
|
230
321
|
// the messages returned for each subsequent step. Loop-persistent so they
|
|
@@ -251,11 +342,20 @@ export async function* executeToolEngine(args) {
|
|
|
251
342
|
// and stay productive. `plan` keeps the old behaviour (architectural
|
|
252
343
|
// decisions deserve debate regardless of length).
|
|
253
344
|
const autoCouncilTypes = new Set(["plan", "analyze"]);
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
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";
|
|
257
355
|
const autoCouncilConfidence = getAutoCouncilConfidence();
|
|
258
356
|
const autoCouncilMinRoles = getAutoCouncilMinRoles();
|
|
357
|
+
const sessionModelIsReasoning = isReasoningModel(deps.modelId);
|
|
358
|
+
const skipReasoningSetting = isAutoCouncilSkipReasoning();
|
|
259
359
|
const _complexityFromTrace = pilCtx._intentTrace
|
|
260
360
|
?.complexity;
|
|
261
361
|
const _complexityGatePassed = pilCtx.taskType === "plan" || _complexityFromTrace === undefined || _complexityFromTrace !== "low";
|
|
@@ -263,9 +363,13 @@ export async function* executeToolEngine(args) {
|
|
|
263
363
|
autoCouncilTypes.has(pilCtx.taskType) &&
|
|
264
364
|
pilCtx.confidence >= autoCouncilConfidence &&
|
|
265
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;
|
|
266
369
|
const shouldAutoCouncil = !deps.councilManager.isContinuation &&
|
|
267
370
|
isAutoCouncilEnabled() &&
|
|
268
371
|
configuredRoleCount >= autoCouncilMinRoles &&
|
|
372
|
+
!shouldSkipForReasoning &&
|
|
269
373
|
(taskTypeMatch || heavyTier);
|
|
270
374
|
// Always log the auto-council decision (taken or skipped) with the gate
|
|
271
375
|
// values that decided it. Lets reports answer "why did this turn cost
|
|
@@ -277,6 +381,8 @@ export async function* executeToolEngine(args) {
|
|
|
277
381
|
return "feature-disabled";
|
|
278
382
|
if (configuredRoleCount < autoCouncilMinRoles)
|
|
279
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)`;
|
|
280
386
|
if (!taskTypeMatch && !heavyTier) {
|
|
281
387
|
if (!pilCtx.taskType || !autoCouncilTypes.has(pilCtx.taskType))
|
|
282
388
|
return `taskType=${pilCtx.taskType ?? "null"} not in plan|analyze`;
|
|
@@ -304,6 +410,8 @@ export async function* executeToolEngine(args) {
|
|
|
304
410
|
autoCouncilConfidence,
|
|
305
411
|
autoCouncilMinRoles,
|
|
306
412
|
heavyTier,
|
|
413
|
+
sessionModelIsReasoning,
|
|
414
|
+
skipReasoningSetting,
|
|
307
415
|
isContinuation: deps.councilManager.isContinuation,
|
|
308
416
|
},
|
|
309
417
|
}).catch(() => undefined);
|
|
@@ -312,14 +420,35 @@ export async function* executeToolEngine(args) {
|
|
|
312
420
|
? `complexity=heavy${pilCtx.taskType ? ` task=${pilCtx.taskType}` : ""}`
|
|
313
421
|
: `${pilCtx.taskType} task detected with ${(pilCtx.confidence * 100).toFixed(0)}% confidence`;
|
|
314
422
|
yield { type: "content", content: `\n[Auto-council triggered: ${reason}]\n` };
|
|
315
|
-
|
|
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
|
+
});
|
|
316
436
|
const synthesis = deps.councilManager.lastSynthesis;
|
|
437
|
+
const chosenAction = deps.councilManager.lastPostDebateAction;
|
|
317
438
|
deps.councilManager.setLastSynthesis(null);
|
|
318
|
-
|
|
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) {
|
|
319
448
|
yield { type: "content", content: "\n[Auto-continuing with council recommendations...]\n" };
|
|
320
449
|
deps.councilManager.setContinuation(true);
|
|
321
450
|
try {
|
|
322
|
-
yield* deps.processMessage(
|
|
451
|
+
yield* deps.processMessage(continuationPrompt, observer);
|
|
323
452
|
}
|
|
324
453
|
finally {
|
|
325
454
|
deps.councilManager.setContinuation(false);
|
|
@@ -327,6 +456,12 @@ export async function* executeToolEngine(args) {
|
|
|
327
456
|
}
|
|
328
457
|
return;
|
|
329
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
|
+
}
|
|
330
465
|
if (deps.batchApi) {
|
|
331
466
|
try {
|
|
332
467
|
yield* deps.processMessageBatchTurn({
|
|
@@ -464,14 +599,29 @@ export async function* executeToolEngine(args) {
|
|
|
464
599
|
cachedImageCount: listCachedImages().length,
|
|
465
600
|
priorTurnHadTools: deps.messages.some((m) => m?.role === "tool"),
|
|
466
601
|
});
|
|
602
|
+
const depthTier = pilCtx.modelDepthTier ??
|
|
603
|
+
pilCtx.complexityTier ??
|
|
604
|
+
"standard";
|
|
467
605
|
const baseToolsRaw = createBuiltinTools(deps.bash, deps.mode, {
|
|
468
606
|
runTask: (request, abortSignal) => deps.runTask(request, combineAbortSignals(signal, abortSignal)),
|
|
469
607
|
runDelegation: (request, abortSignal) => deps.runDelegation(request, combineAbortSignals(signal, abortSignal)),
|
|
470
608
|
readDelegation: (id) => deps.readDelegation(id),
|
|
471
609
|
listDelegations: () => deps.listDelegations(),
|
|
472
610
|
modelId: turnModelId,
|
|
611
|
+
depthTier,
|
|
612
|
+
sessionId: deps.session?.id,
|
|
473
613
|
includeVisionTools,
|
|
474
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
|
+
},
|
|
475
625
|
});
|
|
476
626
|
// Top-level cumulative cap state. We accumulate the raw tool set
|
|
477
627
|
// (base + MCP + PIL response tools) across the assembly below,
|
|
@@ -495,16 +645,23 @@ export async function* executeToolEngine(args) {
|
|
|
495
645
|
// history) is preserved.
|
|
496
646
|
const turnCaps = getProviderCapabilities(requireRuntimeProvider(runtime));
|
|
497
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;
|
|
498
652
|
let rawToolSet = !turnCaps.supportsClientTools(runtime.modelInfo)
|
|
499
653
|
? {}
|
|
500
654
|
: isChitchat && !_priorTurnHadTools
|
|
501
655
|
? {}
|
|
502
|
-
:
|
|
503
|
-
|
|
656
|
+
: isDirectAnswer && !_priorTurnHadTools
|
|
657
|
+
? stripWriteTools(baseToolsRaw)
|
|
658
|
+
: baseToolsRaw;
|
|
659
|
+
// MCP skip: chitchat / direct-answer / greeting inputs don't need 7 MCP servers'
|
|
504
660
|
// worth of tool schemas (~20K input tokens). PIL Layer 1 already
|
|
505
661
|
// gates this conservatively (≤10 chars + ≤2 words OR brain "none").
|
|
506
662
|
if (deps.mode === "agent" &&
|
|
507
663
|
(!isChitchat || _priorTurnHadTools) &&
|
|
664
|
+
!isDirectAnswer &&
|
|
508
665
|
turnCaps.supportsClientTools(runtime.modelInfo)) {
|
|
509
666
|
// Smart MCP filter: drop OPTIONAL MCP servers whose category the
|
|
510
667
|
// current message gives no signal for. Browser/vision servers
|
|
@@ -611,13 +768,63 @@ export async function* executeToolEngine(args) {
|
|
|
611
768
|
// Apply the top-level cumulative cap once over the fully-assembled
|
|
612
769
|
// raw tool set. State is per-turn; each turn gets a fresh budget.
|
|
613
770
|
const topLevelCap = wrapToolSetWithCap(rawToolSet, {
|
|
614
|
-
maxCumulativeChars: getTopLevelToolBudgetChars(deps.maxToolRounds),
|
|
771
|
+
maxCumulativeChars: getTopLevelToolBudgetChars(deps.maxToolRounds, contextWindow),
|
|
615
772
|
midTierRatio: 0.5,
|
|
616
773
|
highTierRatio: 0.8,
|
|
617
774
|
label: "top-level",
|
|
618
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;
|
|
619
779
|
// Phase C3: layer cross-turn dedup on top of the top-level cap.
|
|
620
780
|
const tools = wrapToolSetWithReadBudget(wrapToolSetWithDedup(topLevelCap.tools, deps.crossTurnDedup), deps.readBudget);
|
|
781
|
+
// Wrap non-read-only tools in a turn-scoped mutex to prevent race conditions during parallel execution.
|
|
782
|
+
const writeMutex = new SimpleMutex();
|
|
783
|
+
const READ_ONLY_TOOLS = new Set([
|
|
784
|
+
"read_file",
|
|
785
|
+
"grep",
|
|
786
|
+
"bash_output_get",
|
|
787
|
+
"process_list",
|
|
788
|
+
"delegation_read",
|
|
789
|
+
"delegation_list",
|
|
790
|
+
"ee_query",
|
|
791
|
+
"ee_health",
|
|
792
|
+
"usage_forensics",
|
|
793
|
+
"lsp_query",
|
|
794
|
+
"setup_guide",
|
|
795
|
+
"selfverify_status",
|
|
796
|
+
"selfverify_result",
|
|
797
|
+
"selfverify_list",
|
|
798
|
+
"list_vision_cache",
|
|
799
|
+
"ee_feedback",
|
|
800
|
+
"ee_write",
|
|
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;
|
|
808
|
+
for (const name of Object.keys(tools)) {
|
|
809
|
+
const tool = tools[name];
|
|
810
|
+
if (tool &&
|
|
811
|
+
typeof tool.execute === "function" &&
|
|
812
|
+
!READ_ONLY_TOOLS.has(name) &&
|
|
813
|
+
!name.startsWith("respond_")) {
|
|
814
|
+
const originalExecute = tool.execute;
|
|
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
|
+
}
|
|
824
|
+
return writeMutex.run(() => originalExecute(input, context));
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
}
|
|
621
828
|
captureToolSchemas(tools);
|
|
622
829
|
let responseToolCalled = false;
|
|
623
830
|
// A turn must surface exactly ONE final structured answer. Cheap
|
|
@@ -712,7 +919,40 @@ export async function* executeToolEngine(args) {
|
|
|
712
919
|
// (non-cached) system message via the slice below.
|
|
713
920
|
const mcpCapabilityBlock = buildMcpCapabilityBlock(Object.keys(tools));
|
|
714
921
|
const systemWithCaps = mcpCapabilityBlock ? `${systemWithShell}${mcpCapabilityBlock}` : systemWithShell;
|
|
715
|
-
|
|
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
|
|
716
956
|
? [
|
|
717
957
|
{
|
|
718
958
|
role: "system",
|
|
@@ -724,7 +964,9 @@ export async function* executeToolEngine(args) {
|
|
|
724
964
|
content: systemWithCaps.slice(systemParts.staticPrefix.length),
|
|
725
965
|
},
|
|
726
966
|
]
|
|
727
|
-
:
|
|
967
|
+
: _willFoldDynamicTail
|
|
968
|
+
? _nonClaudeFront
|
|
969
|
+
: systemWithCaps;
|
|
728
970
|
// Capture prompt-size breakdown so recordUsage can attach it to the
|
|
729
971
|
// cost-log entry. Without this, "system prompt is huge" is unfalsifiable.
|
|
730
972
|
// chars/4 ≈ tokens for English; reported as chars to keep math obvious.
|
|
@@ -788,13 +1030,27 @@ export async function* executeToolEngine(args) {
|
|
|
788
1030
|
// stubs. Symmetric to the B3 sub-agent path; reuses the same module
|
|
789
1031
|
// with `label: "top-level"` so the stub text reflects which loop
|
|
790
1032
|
// elided the content.
|
|
791
|
-
const topLevelCompactThreshold = getTopLevelCompactThresholdChars();
|
|
792
|
-
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();
|
|
793
1045
|
// Phase O1 — capture providerOptions SHAPE (types only) for forensics.
|
|
794
1046
|
deps.setLastProviderOptionsShape(Object.keys(providerOpts).length > 0 ? extractProviderOptionsShape(providerOpts) : null);
|
|
795
1047
|
// Substitute the enriched user message for the current turn so the LLM sees PIL additions,
|
|
796
1048
|
// while leaving the DB-persisted `deps.messages` clean for future turns.
|
|
797
|
-
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);
|
|
798
1054
|
if (wireDebug.enabled) {
|
|
799
1055
|
wireDebug.logRequest({
|
|
800
1056
|
providerId: runtime.modelInfo?.provider ?? "unknown",
|
|
@@ -816,6 +1072,37 @@ export async function* executeToolEngine(args) {
|
|
|
816
1072
|
const _baseDynamicStopWhen = createToolLoopCapPredicate({
|
|
817
1073
|
initialCap: deps.maxToolRounds,
|
|
818
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
|
+
}
|
|
819
1106
|
if (info.kind === "pattern") {
|
|
820
1107
|
if (patternLoopInjectCount < 1) {
|
|
821
1108
|
patternLoopInjectCount++;
|
|
@@ -1032,8 +1319,24 @@ export async function* executeToolEngine(args) {
|
|
|
1032
1319
|
// on every chunk via stall.pet(), so it never kills an actively
|
|
1033
1320
|
// streaming call. Disposed when the stream ends or errors.
|
|
1034
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.
|
|
1035
1331
|
const stall = createStallWatchdog(getProviderStallTimeoutMs(), () => {
|
|
1036
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
|
+
},
|
|
1037
1340
|
});
|
|
1038
1341
|
// F3c — hard-cap LLM calls per turn before this streamText()
|
|
1039
1342
|
if (++llmCallsThisTurn > MAX_LLM_CALLS_PER_TURN) {
|
|
@@ -1211,10 +1514,17 @@ export async function* executeToolEngine(args) {
|
|
|
1211
1514
|
};
|
|
1212
1515
|
// T1.1 + T1.2 — reasoning models (DeepSeek V4 Flash, R1) emit 2K-5K
|
|
1213
1516
|
// CoT tokens per turn that accumulate across the multi-step loop.
|
|
1214
|
-
// Strip old reasoning and compact earlier
|
|
1215
|
-
//
|
|
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.
|
|
1216
1521
|
const isReasoningModel = runtime.modelInfo?.reasoning === true;
|
|
1217
|
-
const
|
|
1522
|
+
const reasoningFillRatio = isReasoningModel
|
|
1523
|
+
? contextWindowTokens > 0 && contextWindowTokens < 100_000
|
|
1524
|
+
? 0.2
|
|
1525
|
+
: 0.3
|
|
1526
|
+
: undefined;
|
|
1527
|
+
const runCompaction = () => compactSubAgentMessages(stripped, {
|
|
1218
1528
|
thresholdChars: topLevelCompactThreshold,
|
|
1219
1529
|
// Rec #1 (cheap part): on meta/self-eval turns keep a couple more
|
|
1220
1530
|
// trailing tool turns verbatim — those carry the reasoning the
|
|
@@ -1225,28 +1535,120 @@ export async function* executeToolEngine(args) {
|
|
|
1225
1535
|
label: "top-level",
|
|
1226
1536
|
envelopeChars,
|
|
1227
1537
|
contextWindowTokens,
|
|
1228
|
-
contextFillRatio:
|
|
1538
|
+
contextFillRatio: reasoningFillRatio,
|
|
1229
1539
|
keepToolIds: keepToolIds.length ? keepToolIds : undefined,
|
|
1230
1540
|
persistArtifact,
|
|
1231
1541
|
stripOldReasoning: isReasoningModel,
|
|
1542
|
+
tailBudgetChars: topLevelCompactTailBudget,
|
|
1232
1543
|
});
|
|
1233
|
-
|
|
1234
|
-
|
|
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.)
|
|
1235
1598
|
// Pre-compaction visibility: give the agent one step of notice
|
|
1236
1599
|
// before B4 actually rewrites history into stubs. This is the
|
|
1237
1600
|
// advance warning that was missing — agent can now decide to
|
|
1238
1601
|
// summarize, finish, or request preservation. Fires when we did
|
|
1239
1602
|
// NOT compact this step (compacted === stripped, restored by the
|
|
1240
|
-
// compactSubAgentMessages no-op ref contract) AND the prompt is
|
|
1241
|
-
// approaching the threshold. Must compare CHARS (messages +
|
|
1242
|
-
// envelope), not stripped.length (a message count that never
|
|
1243
|
-
// exceeds a char-scaled threshold) — session 2b7a10219499.
|
|
1244
1603
|
const _preWarnChars = cumulativeMessageChars(stripped) + envelopeChars;
|
|
1245
|
-
if (
|
|
1604
|
+
if (coalesced === stripped && shouldPreWarnCompaction(_preWarnChars, topLevelCompactThreshold)) {
|
|
1246
1605
|
const _cp = buildCheckpointReminder(sn, true);
|
|
1247
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.]`;
|
|
1248
1607
|
return withSteers({ messages: attachReminderToMessages(stripped, _pre) });
|
|
1249
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
|
+
}
|
|
1250
1652
|
// Phase 4A — scope reminder injection (REQ-005).
|
|
1251
1653
|
// Cadence K = 3/5/8 for small/medium/large. Soft-warn fires
|
|
1252
1654
|
// ONCE per session at floor(ceiling*0.7). Reminder lives in
|
|
@@ -1306,10 +1708,10 @@ export async function* executeToolEngine(args) {
|
|
|
1306
1708
|
? `${buildRepetitionReminder(_ceilingSessionId)}\n${_scopePart}`
|
|
1307
1709
|
: buildRepetitionReminder(_ceilingSessionId)
|
|
1308
1710
|
: _scopePart;
|
|
1309
|
-
const withReminder = attachReminderToMessages(
|
|
1711
|
+
const withReminder = attachReminderToMessages(coalesced, _reminder);
|
|
1310
1712
|
return withSteers({ messages: withReminder });
|
|
1311
1713
|
}
|
|
1312
|
-
if (
|
|
1714
|
+
if (coalesced === stripped && stripped === stepMessages)
|
|
1313
1715
|
return withSteers({});
|
|
1314
1716
|
// Self-awareness note: tell the model compaction happened so it
|
|
1315
1717
|
// knows earlier context was elided and can adjust its behavior.
|
|
@@ -1328,12 +1730,12 @@ export async function* executeToolEngine(args) {
|
|
|
1328
1730
|
})()
|
|
1329
1731
|
: null;
|
|
1330
1732
|
if (_compactNote) {
|
|
1331
|
-
return withSteers({ messages: attachReminderToMessages(
|
|
1733
|
+
return withSteers({ messages: attachReminderToMessages(coalesced, _compactNote) });
|
|
1332
1734
|
}
|
|
1333
|
-
return withSteers({ messages:
|
|
1735
|
+
return withSteers({ messages: coalesced });
|
|
1334
1736
|
},
|
|
1335
|
-
...(
|
|
1336
|
-
...(dropParam("maxOutputTokens") ? {} : { maxOutputTokens:
|
|
1737
|
+
...resolveTemperatureParam(runtime, 0.7),
|
|
1738
|
+
...(dropParam("maxOutputTokens") ? {} : { maxOutputTokens: resolveTurnMaxOutputTokens(pilCtx) }),
|
|
1337
1739
|
...(Object.keys(providerOpts).length > 0 ? { providerOptions: providerOpts } : {}),
|
|
1338
1740
|
experimental_onStepStart: (event) => {
|
|
1339
1741
|
stepNumber = getStepNumber(event, stepNumber + 1);
|
|
@@ -1352,6 +1754,10 @@ export async function* executeToolEngine(args) {
|
|
|
1352
1754
|
finishReason: getFinishReason(event),
|
|
1353
1755
|
usage: stepUsage,
|
|
1354
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(() => { });
|
|
1355
1761
|
// Realtime status bar update per step
|
|
1356
1762
|
if (stepUsage.inputTokens || stepUsage.outputTokens) {
|
|
1357
1763
|
// O1 — thread THIS turn's providerOptions shape per step so every
|
|
@@ -1426,8 +1832,23 @@ export async function* executeToolEngine(args) {
|
|
|
1426
1832
|
wireDebug.logError(_wireProviderIdTop, part.error);
|
|
1427
1833
|
}
|
|
1428
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
|
+
}
|
|
1429
1849
|
switch (part.type) {
|
|
1430
1850
|
case "text-delta":
|
|
1851
|
+
stall.petProgress(); // real forward progress — reset the no-progress guard
|
|
1431
1852
|
assistantText += part.text;
|
|
1432
1853
|
// Task 2.6b — emit llm-token (agent-mode only; high-volume, default-off per Phase 4).
|
|
1433
1854
|
try {
|
|
@@ -1459,6 +1880,7 @@ export async function* executeToolEngine(args) {
|
|
|
1459
1880
|
yield { type: "reasoning", content: part.text };
|
|
1460
1881
|
break;
|
|
1461
1882
|
case "tool-call": {
|
|
1883
|
+
stall.petProgress(); // real forward progress — reset the no-progress guard
|
|
1462
1884
|
const tc = toToolCall(part);
|
|
1463
1885
|
activeToolCalls.push(tc);
|
|
1464
1886
|
// SAMR: track that Phase 1 produced tool calls → transition to Phase 2
|
|
@@ -1679,10 +2101,10 @@ export async function* executeToolEngine(args) {
|
|
|
1679
2101
|
// the global __muonroiSafetyApproved map so registry.ts's
|
|
1680
2102
|
// bash.execute bypasses the block on the retry.
|
|
1681
2103
|
const _outputText = [tr.output, tr.error].filter((x) => typeof x === "string").join("\n");
|
|
1682
|
-
const
|
|
1683
|
-
if (
|
|
1684
|
-
const _blockKind =
|
|
1685
|
-
const _blockReason =
|
|
2104
|
+
const _parsedBlock = parseSafetyBlock(_outputText);
|
|
2105
|
+
if (_parsedBlock && part.toolName === "bash") {
|
|
2106
|
+
const _blockKind = _parsedBlock.kind;
|
|
2107
|
+
const _blockReason = _parsedBlock.reason;
|
|
1686
2108
|
const _command = typeof part.input === "object" && part.input != null
|
|
1687
2109
|
? String(part.input.command ?? "")
|
|
1688
2110
|
: "";
|
|
@@ -1695,15 +2117,33 @@ export async function* executeToolEngine(args) {
|
|
|
1695
2117
|
tr = { ...tr, success: false, error: _outputText, output: _outputText };
|
|
1696
2118
|
}
|
|
1697
2119
|
else {
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
+
}
|
|
1707
2147
|
if (_verdict.action === "allow-once" || _verdict.action === "allow-session") {
|
|
1708
2148
|
// Store approval so registry.ts can bypass the block on retry.
|
|
1709
2149
|
const _globalSafety = globalThis;
|
|
@@ -1837,10 +2277,26 @@ export async function* executeToolEngine(args) {
|
|
|
1837
2277
|
// dedicated chunk so the sticky checklist panel can re-render
|
|
1838
2278
|
// without parsing tool args itself. Skipped when the snapshot
|
|
1839
2279
|
// doesn't parse (malformed args) so the UI is never poisoned.
|
|
1840
|
-
if (tc.function.name === "todo_write"
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
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
|
+
}
|
|
1844
2300
|
}
|
|
1845
2301
|
break;
|
|
1846
2302
|
}
|
|
@@ -2972,6 +3428,10 @@ export async function* executeToolEngine(args) {
|
|
|
2972
3428
|
if (modelInfo?.contextWindow) {
|
|
2973
3429
|
void deps.postTurnCompact(provider, system, modelInfo.contextWindow, signal).catch(() => { });
|
|
2974
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);
|
|
2975
3435
|
yield { type: "done" };
|
|
2976
3436
|
return;
|
|
2977
3437
|
}
|
|
@@ -3118,6 +3578,10 @@ export async function* executeToolEngine(args) {
|
|
|
3118
3578
|
if (modelInfo?.contextWindow) {
|
|
3119
3579
|
void deps.postTurnCompact(provider, system, modelInfo.contextWindow, signal).catch(() => { });
|
|
3120
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);
|
|
3121
3585
|
yield { type: "done" };
|
|
3122
3586
|
return;
|
|
3123
3587
|
}
|
|
@@ -3135,4 +3599,153 @@ export async function* executeToolEngine(args) {
|
|
|
3135
3599
|
}
|
|
3136
3600
|
}
|
|
3137
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
|
+
}
|
|
3138
3751
|
//# sourceMappingURL=tool-engine.js.map
|