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
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* visual-quality.ts — programmatic visual-quality heuristics over a VisualFrame.
|
|
3
|
+
*
|
|
4
|
+
* P1 of Harness TUI v2. Given the ground-truth rendered cell grid, compute cheap
|
|
5
|
+
* ground-truth signals a human would call "messy" — WITHOUT a vision model:
|
|
6
|
+
* - blank / near-blank row ratios (the council "↳" + "· Skip" garbage)
|
|
7
|
+
* - long runs of consecutive blank rows (dead space)
|
|
8
|
+
* - whitespace density
|
|
9
|
+
* - mojibake (U+FFFD replacement chars + misdecoded-UTF8 clusters like `💡`)
|
|
10
|
+
*
|
|
11
|
+
* These are the exact defects the semantic tree (LiveFrame) is blind to — the
|
|
12
|
+
* 2217600e1f27 export had 12 near-empty rows + mojibake that no existing
|
|
13
|
+
* self-verify check could see. A composite `score` + `issues[]` lets self-QA
|
|
14
|
+
* fail a render regression automatically.
|
|
15
|
+
*
|
|
16
|
+
* Thresholds are conservative defaults; tune per surface. This is a heuristic,
|
|
17
|
+
* not a proof — a low score flags "look closer", it does not assert a bug.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_VISUAL_QUALITY_THRESHOLDS = {
|
|
20
|
+
nearEmptyMaxChars: 2,
|
|
21
|
+
nearEmptyRowPct: 0.5,
|
|
22
|
+
maxConsecutiveBlankRows: 4,
|
|
23
|
+
mojibakeCount: 1,
|
|
24
|
+
whitespacePct: 0.92,
|
|
25
|
+
};
|
|
26
|
+
// Characters that legitimately never (or almost never) appear in this TUI but
|
|
27
|
+
// are produced when UTF-8 bytes are re-decoded as CP437/latin1. Presence in a
|
|
28
|
+
// 2+ cluster is a strong mojibake signal (e.g. 💡 → "≡ƒÆí", em-dash → "ΓÇô").
|
|
29
|
+
const MOJIBAKE_SIGNAL = new Set([
|
|
30
|
+
..."≡ƒÆÇÃÂ’ÿÖÜúÑŒ¬".split(""),
|
|
31
|
+
"�", // replacement char
|
|
32
|
+
]);
|
|
33
|
+
function rowText(line) {
|
|
34
|
+
return line.spans.map((s) => s.text).join("");
|
|
35
|
+
}
|
|
36
|
+
function countMojibake(text) {
|
|
37
|
+
let count = 0;
|
|
38
|
+
let sample;
|
|
39
|
+
// Definitive: U+FFFD replacement chars.
|
|
40
|
+
for (const ch of text) {
|
|
41
|
+
if (ch === "�")
|
|
42
|
+
count++;
|
|
43
|
+
}
|
|
44
|
+
// Clusters of 2+ consecutive signal chars — mojibake arrives in runs, which
|
|
45
|
+
// keeps ordinary accented text (single é, ü) from tripping the heuristic.
|
|
46
|
+
const chars = [...text];
|
|
47
|
+
let run = 0;
|
|
48
|
+
for (let i = 0; i <= chars.length; i++) {
|
|
49
|
+
const isSignal = i < chars.length && MOJIBAKE_SIGNAL.has(chars[i]) && chars[i] !== "�";
|
|
50
|
+
if (isSignal) {
|
|
51
|
+
run++;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (run >= 2) {
|
|
55
|
+
count += run;
|
|
56
|
+
if (!sample)
|
|
57
|
+
sample = chars.slice(Math.max(0, i - run - 4), i + 2).join("");
|
|
58
|
+
}
|
|
59
|
+
run = 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { count, sample };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compute visual-quality signals for a rendered frame. Pure + deterministic.
|
|
66
|
+
*/
|
|
67
|
+
export function computeVisualQuality(frame, thresholds = DEFAULT_VISUAL_QUALITY_THRESHOLDS) {
|
|
68
|
+
const rows = frame.lines.length;
|
|
69
|
+
const cols = frame.cols;
|
|
70
|
+
let contentChars = 0;
|
|
71
|
+
let totalCells = 0;
|
|
72
|
+
let blankRows = 0;
|
|
73
|
+
let nearEmptyRows = 0;
|
|
74
|
+
let maxConsecutiveBlankRows = 0;
|
|
75
|
+
let curBlankRun = 0;
|
|
76
|
+
let mojibakeCount = 0;
|
|
77
|
+
const mojibakeSamples = [];
|
|
78
|
+
for (const line of frame.lines) {
|
|
79
|
+
const text = rowText(line);
|
|
80
|
+
const nonSpace = [...text].filter((c) => c.trim().length > 0).length;
|
|
81
|
+
contentChars += nonSpace;
|
|
82
|
+
totalCells += Math.max(text.length, cols);
|
|
83
|
+
if (nonSpace === 0) {
|
|
84
|
+
blankRows++;
|
|
85
|
+
curBlankRun++;
|
|
86
|
+
maxConsecutiveBlankRows = Math.max(maxConsecutiveBlankRows, curBlankRun);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
curBlankRun = 0;
|
|
90
|
+
}
|
|
91
|
+
// "Low-content" rows = blank OR only a glyph or two (skeleton noise like the
|
|
92
|
+
// council "↳" / "· Skip" rows, and the blank separators between them). Both
|
|
93
|
+
// are visual garbage, so they count together against the total row budget.
|
|
94
|
+
if (nonSpace <= thresholds.nearEmptyMaxChars)
|
|
95
|
+
nearEmptyRows++;
|
|
96
|
+
const mj = countMojibake(text);
|
|
97
|
+
if (mj.count > 0) {
|
|
98
|
+
mojibakeCount += mj.count;
|
|
99
|
+
if (mj.sample && mojibakeSamples.length < 5)
|
|
100
|
+
mojibakeSamples.push(mj.sample);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Ratio of low-content (blank or near-empty) rows over the whole frame.
|
|
104
|
+
const nearEmptyRowPct = rows > 0 ? nearEmptyRows / rows : 0;
|
|
105
|
+
const whitespacePct = totalCells > 0 ? 1 - contentChars / totalCells : 1;
|
|
106
|
+
const issues = [];
|
|
107
|
+
if (nearEmptyRowPct > thresholds.nearEmptyRowPct) {
|
|
108
|
+
issues.push(`${nearEmptyRows}/${rows} rows are blank or near-empty (<=${thresholds.nearEmptyMaxChars} chars) — skeleton/garbage rows`);
|
|
109
|
+
}
|
|
110
|
+
if (maxConsecutiveBlankRows > thresholds.maxConsecutiveBlankRows) {
|
|
111
|
+
issues.push(`${maxConsecutiveBlankRows} consecutive blank rows — large dead gap`);
|
|
112
|
+
}
|
|
113
|
+
if (mojibakeCount >= thresholds.mojibakeCount) {
|
|
114
|
+
issues.push(`${mojibakeCount} mojibake signal(s) detected (misdecoded UTF-8 / U+FFFD)${mojibakeSamples.length ? ` e.g. ${JSON.stringify(mojibakeSamples[0])}` : ""}`);
|
|
115
|
+
}
|
|
116
|
+
if (whitespacePct > thresholds.whitespacePct && contentChars > 0) {
|
|
117
|
+
issues.push(`${(whitespacePct * 100).toFixed(0)}% of the grid is whitespace — very sparse render`);
|
|
118
|
+
}
|
|
119
|
+
// Composite score: start at 100, subtract weighted penalties.
|
|
120
|
+
let score = 100;
|
|
121
|
+
score -= Math.min(40, Math.round(nearEmptyRowPct * 60));
|
|
122
|
+
score -= Math.min(25, Math.max(0, maxConsecutiveBlankRows - thresholds.maxConsecutiveBlankRows) * 4);
|
|
123
|
+
score -= Math.min(30, mojibakeCount * 6);
|
|
124
|
+
score -= whitespacePct > thresholds.whitespacePct ? 10 : 0;
|
|
125
|
+
score = Math.max(0, Math.min(100, score));
|
|
126
|
+
return {
|
|
127
|
+
rows,
|
|
128
|
+
cols,
|
|
129
|
+
contentChars,
|
|
130
|
+
blankRows,
|
|
131
|
+
nearEmptyRows,
|
|
132
|
+
nearEmptyRowPct,
|
|
133
|
+
maxConsecutiveBlankRows,
|
|
134
|
+
whitespacePct,
|
|
135
|
+
mojibakeCount,
|
|
136
|
+
mojibakeSamples,
|
|
137
|
+
score,
|
|
138
|
+
issues,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=visual-quality.js.map
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { LiveEvent } from "@muonroi/agent-harness-core/protocol";
|
|
19
19
|
import type { SemanticRegistry } from "./reconciler-hook.js";
|
|
20
|
+
import { type RendererLike } from "./visual-capture.js";
|
|
20
21
|
export type AgentModeOptions = {
|
|
21
22
|
cols: number;
|
|
22
23
|
rows: number;
|
|
@@ -40,6 +41,11 @@ export type AgentModeRuntime = {
|
|
|
40
41
|
onCommand: (h: (cmd: unknown) => void) => void;
|
|
41
42
|
/** Get the current timestamp (real or fake clock). */
|
|
42
43
|
now: () => number;
|
|
44
|
+
/**
|
|
45
|
+
* Attach the OpenTUI renderer once it exists (constructed AFTER this runtime
|
|
46
|
+
* — see src/index.ts). Enables VisualFrame capture of the real cell grid.
|
|
47
|
+
*/
|
|
48
|
+
attachRenderer: (renderer: RendererLike) => void;
|
|
43
49
|
/** Tear down: close streams, dispose idle detector. */
|
|
44
50
|
dispose: () => void;
|
|
45
51
|
};
|
|
@@ -22,6 +22,7 @@ import { createIdleDetector } from "@muonroi/agent-harness-core/idle";
|
|
|
22
22
|
import { createLineSplitter, createSidechannelWriter } from "@muonroi/agent-harness-core/transports/sidechannel";
|
|
23
23
|
import { installOpenTUIHarness } from "./install.js";
|
|
24
24
|
import { createSemanticRegistry } from "./reconciler-hook.js";
|
|
25
|
+
import { createVisualCaptureHook } from "./visual-capture.js";
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
26
27
|
// Factory
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
@@ -90,6 +91,11 @@ export async function startAgentMode(opts) {
|
|
|
90
91
|
const now = () => (opts.fakeClock ? 0 : Date.now());
|
|
91
92
|
// --- Semantic registry ---------------------------------------------------
|
|
92
93
|
const registry = createSemanticRegistry();
|
|
94
|
+
// --- Visual capture (real rendered cell grid) ----------------------------
|
|
95
|
+
// The renderer does not exist yet — it is constructed after this runtime and
|
|
96
|
+
// handed back via attachRenderer(). The hook reads it late-bound.
|
|
97
|
+
let rendererRef;
|
|
98
|
+
const visualHook = createVisualCaptureHook(() => rendererRef);
|
|
93
99
|
// --- Idle detector -------------------------------------------------------
|
|
94
100
|
const idle = createIdleDetector({
|
|
95
101
|
quiescenceMs: opts.idleMs,
|
|
@@ -116,6 +122,9 @@ export async function startAgentMode(opts) {
|
|
|
116
122
|
// becomes deterministic (seq*16). Without this, ts = Date.now() and the
|
|
117
123
|
// determinism spec sees timestamps differ between runs.
|
|
118
124
|
fakeClock: opts.fakeClock,
|
|
125
|
+
// Emit a VisualFrame (real rendered cell grid) alongside each semantic
|
|
126
|
+
// change. No-op until attachRenderer() supplies the renderer.
|
|
127
|
+
captureVisual: (seq, ts) => visualHook.capture(seq, ts),
|
|
119
128
|
});
|
|
120
129
|
// --- Command channel (in stream → handlers) ------------------------------
|
|
121
130
|
const commandHandlers = [];
|
|
@@ -165,6 +174,10 @@ export async function startAgentMode(opts) {
|
|
|
165
174
|
const onCommand = (h) => {
|
|
166
175
|
commandHandlers.push(h);
|
|
167
176
|
};
|
|
177
|
+
const attachRenderer = (renderer) => {
|
|
178
|
+
rendererRef = renderer;
|
|
179
|
+
visualHook.resetDedup(); // force the first visual frame after attach
|
|
180
|
+
};
|
|
168
181
|
const dispose = () => {
|
|
169
182
|
harnessHandle.uninstall(); // stops poll interval; transport.close() ends outStream
|
|
170
183
|
idle.dispose();
|
|
@@ -173,6 +186,6 @@ export async function startAgentMode(opts) {
|
|
|
173
186
|
inStream.destroy();
|
|
174
187
|
}
|
|
175
188
|
};
|
|
176
|
-
return { registry, capture, emitEvent, onCommand, now, dispose };
|
|
189
|
+
return { registry, capture, emitEvent, onCommand, now, attachRenderer, dispose };
|
|
177
190
|
}
|
|
178
191
|
//# sourceMappingURL=agent-mode.js.map
|
|
@@ -13,15 +13,7 @@
|
|
|
13
13
|
* raw byte. We construct ParsedKey directly so we don't depend on terminal
|
|
14
14
|
* escape sequence conventions.
|
|
15
15
|
*/
|
|
16
|
+
import type { KeyEvent } from "@opentui/core";
|
|
16
17
|
import type { AgentModeRuntime } from "./agent-mode.js";
|
|
17
|
-
|
|
18
|
-
* Subscribe to agentRuntime.onCommand and inject synthetic key events into
|
|
19
|
-
* OpenTUI's keyHandler. Safe no-op when agentRuntime is undefined (normal user
|
|
20
|
-
* mode).
|
|
21
|
-
*
|
|
22
|
-
* Special key `__focus__:<id>` is intentionally NOT translated to OpenTUI input
|
|
23
|
-
* here — it is a driver-level signal currently ignored at this layer. (Focus
|
|
24
|
-
* routing in OpenTUI is owned by individual components; a global focus dispatch
|
|
25
|
-
* is out of scope for this bridge.)
|
|
26
|
-
*/
|
|
18
|
+
export declare function keyForNamed(key: string): KeyEvent | null;
|
|
27
19
|
export declare function useAgentInputBridge(agentRuntime: AgentModeRuntime | undefined): void;
|
|
@@ -35,7 +35,54 @@ const NAMED = {
|
|
|
35
35
|
PageUp: { name: "pageup", sequence: "\x1b[5~", raw: "\x1b[5~" },
|
|
36
36
|
PageDown: { name: "pagedown", sequence: "\x1b[6~", raw: "\x1b[6~" },
|
|
37
37
|
};
|
|
38
|
-
|
|
38
|
+
// Case-insensitive view of NAMED. The map above is capitalized, but drivers
|
|
39
|
+
// naturally send lowercase ("enter", "escape", "tab", "space") — those missed
|
|
40
|
+
// the exact-case lookup, so keyForNamed returned null and the keystroke was a
|
|
41
|
+
// SILENT no-op (Enter never submitted, Escape never dismissed a modal). Resolve
|
|
42
|
+
// on the lowercased base so every casing works. Includes a couple of common
|
|
43
|
+
// short aliases drivers reach for.
|
|
44
|
+
const NAMED_LC = {
|
|
45
|
+
...Object.fromEntries(Object.entries(NAMED).map(([k, v]) => [k.toLowerCase(), v])),
|
|
46
|
+
ret: NAMED.Return,
|
|
47
|
+
del: NAMED.Delete,
|
|
48
|
+
spacebar: NAMED.Space,
|
|
49
|
+
pgup: NAMED.PageUp,
|
|
50
|
+
pgdn: NAMED.PageDown,
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Strip modifier prefixes (`C-` ctrl, `M-` meta/alt, `S-` shift) off a harness
|
|
54
|
+
* key string so combos like `C-b` or `C-o` can be driven. Prefixes may stack in
|
|
55
|
+
* any order (`C-S-tab`). The remainder is the base key (named or single char).
|
|
56
|
+
*/
|
|
57
|
+
function parseModifiers(key) {
|
|
58
|
+
const mods = {};
|
|
59
|
+
let base = key;
|
|
60
|
+
// Only consume a prefix when a base remains after it, so a literal "C" or a
|
|
61
|
+
// hyphen key is not mis-parsed.
|
|
62
|
+
for (;;) {
|
|
63
|
+
if (base.length > 2 && base[1] === "-") {
|
|
64
|
+
const p = base[0];
|
|
65
|
+
if (p === "C" || p === "c") {
|
|
66
|
+
mods.ctrl = true;
|
|
67
|
+
base = base.slice(2);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (p === "M" || p === "m") {
|
|
71
|
+
mods.meta = true;
|
|
72
|
+
base = base.slice(2);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (p === "S") {
|
|
76
|
+
mods.shift = true;
|
|
77
|
+
base = base.slice(2);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return { mods, base };
|
|
84
|
+
}
|
|
85
|
+
function makeKey(partial, mods = {}) {
|
|
39
86
|
// Some app key-handlers conditionally call key.preventDefault()/stopPropagation()
|
|
40
87
|
// (typed `?.()` so they tolerate undefined). When the host's keypress arrives
|
|
41
88
|
// through a terminal, OpenTUI attaches these methods; in agent-mode we
|
|
@@ -48,9 +95,9 @@ function makeKey(partial) {
|
|
|
48
95
|
let _propagationStopped = false;
|
|
49
96
|
const ev = {
|
|
50
97
|
name: partial.name,
|
|
51
|
-
ctrl: false,
|
|
52
|
-
meta: false,
|
|
53
|
-
shift: false,
|
|
98
|
+
ctrl: mods.ctrl ?? false,
|
|
99
|
+
meta: mods.meta ?? false,
|
|
100
|
+
shift: mods.shift ?? false,
|
|
54
101
|
option: false,
|
|
55
102
|
sequence: partial.sequence,
|
|
56
103
|
number: false,
|
|
@@ -72,18 +119,22 @@ function makeKey(partial) {
|
|
|
72
119
|
};
|
|
73
120
|
return ev;
|
|
74
121
|
}
|
|
75
|
-
function keyForChar(ch) {
|
|
76
|
-
return makeKey({ name: ch, sequence: ch, raw: ch });
|
|
122
|
+
function keyForChar(ch, mods = {}) {
|
|
123
|
+
return makeKey({ name: ch, sequence: ch, raw: ch }, mods);
|
|
77
124
|
}
|
|
78
|
-
function keyForNamed(key) {
|
|
79
|
-
const
|
|
125
|
+
export function keyForNamed(key) {
|
|
126
|
+
const { mods, base } = parseModifiers(key);
|
|
127
|
+
// Exact case first (fast path), then case-insensitive so "enter"/"Enter"/
|
|
128
|
+
// "ENTER"/"return" all resolve. Single-letter bases never collide with a
|
|
129
|
+
// NAMED word, so they still fall through to the literal-char path below.
|
|
130
|
+
const m = NAMED[base] ?? NAMED_LC[base.toLowerCase()];
|
|
80
131
|
if (!m) {
|
|
81
132
|
// Unknown named key — fall back to treating it as a literal character if it's 1 char.
|
|
82
|
-
if (
|
|
83
|
-
return keyForChar(
|
|
133
|
+
if (base.length === 1)
|
|
134
|
+
return keyForChar(base, mods);
|
|
84
135
|
return null;
|
|
85
136
|
}
|
|
86
|
-
return makeKey(m);
|
|
137
|
+
return makeKey(m, mods);
|
|
87
138
|
}
|
|
88
139
|
/**
|
|
89
140
|
* Subscribe to agentRuntime.onCommand and inject synthetic key events into
|
|
@@ -95,15 +146,51 @@ function keyForNamed(key) {
|
|
|
95
146
|
* routing in OpenTUI is owned by individual components; a global focus dispatch
|
|
96
147
|
* is out of scope for this bridge.)
|
|
97
148
|
*/
|
|
149
|
+
// Runtimes that already have the input bridge wired — module-level so the guard
|
|
150
|
+
// survives App remounts (a per-component ref would reset and re-register).
|
|
151
|
+
const mountedRuntimes = new WeakSet();
|
|
98
152
|
export function useAgentInputBridge(agentRuntime) {
|
|
99
|
-
const
|
|
153
|
+
const ctx = useAppContext();
|
|
154
|
+
const keyHandler = ctx.keyHandler;
|
|
155
|
+
// Focused renderables (the composer textarea) subscribe to keypresses via
|
|
156
|
+
// renderer._internalKeyInput.onInternal("keypress"), NOT via the public
|
|
157
|
+
// keyHandler (useAppContext().keyHandler === renderer.keyInput). On the real
|
|
158
|
+
// TTY renderer both are the SAME InternalKeyHandler, but under the agent-mode
|
|
159
|
+
// headless renderer they are DISTINCT objects — so emitting only on keyHandler
|
|
160
|
+
// reached app-level useKeyboard shortcuts yet never the textarea, and typed
|
|
161
|
+
// text silently vanished (the tier-2 harness-drive bug). Prefer
|
|
162
|
+
// _internalKeyInput: its emitWithPriority dispatches to BOTH the focused
|
|
163
|
+
// renderable AND global listeners registered on it.
|
|
164
|
+
const internal = ctx.renderer?._internalKeyInput;
|
|
100
165
|
useEffect(() => {
|
|
101
|
-
if (!agentRuntime
|
|
166
|
+
if (!agentRuntime)
|
|
167
|
+
return;
|
|
168
|
+
// onCommand has no unsubscribe (it only pushes to a handler array), so
|
|
169
|
+
// registering twice doubles every keystroke (observed: a typed prompt
|
|
170
|
+
// arrived duplicated). A per-component ref is not enough — the App can
|
|
171
|
+
// remount (e.g. hero → chat transition) giving a fresh ref while the prior
|
|
172
|
+
// handler stays registered on the same long-lived runtime. Track mounted
|
|
173
|
+
// runtimes in a module-level WeakSet so registration is once-per-runtime
|
|
174
|
+
// across remounts.
|
|
175
|
+
if (mountedRuntimes.has(agentRuntime))
|
|
176
|
+
return;
|
|
177
|
+
const target = internal ?? keyHandler;
|
|
178
|
+
if (!target)
|
|
102
179
|
return;
|
|
103
180
|
// Toast-only stub in normal interactive mode provides emitEvent but not
|
|
104
181
|
// onCommand. Guard so the bridge no-ops cleanly instead of crashing the TUI.
|
|
105
182
|
if (typeof agentRuntime.onCommand !== "function")
|
|
106
183
|
return;
|
|
184
|
+
mountedRuntimes.add(agentRuntime);
|
|
185
|
+
// Emit to the renderable channel (the focused textarea), and also to the
|
|
186
|
+
// public keyHandler when it is a distinct object so global shortcuts still
|
|
187
|
+
// fire. The `!== target` guard avoids double-dispatch on the real renderer
|
|
188
|
+
// where the two are identical.
|
|
189
|
+
const emitKey = (k) => {
|
|
190
|
+
target.emit("keypress", k);
|
|
191
|
+
if (keyHandler && keyHandler !== target)
|
|
192
|
+
keyHandler.emit("keypress", k);
|
|
193
|
+
};
|
|
107
194
|
agentRuntime.onCommand((cmd) => {
|
|
108
195
|
if (!cmd || typeof cmd !== "object")
|
|
109
196
|
return;
|
|
@@ -113,14 +200,14 @@ export function useAgentInputBridge(agentRuntime) {
|
|
|
113
200
|
return;
|
|
114
201
|
const k = keyForNamed(c.key);
|
|
115
202
|
if (k)
|
|
116
|
-
|
|
203
|
+
emitKey(k);
|
|
117
204
|
}
|
|
118
205
|
else if (c.op === "type" && typeof c.text === "string") {
|
|
119
206
|
for (const ch of c.text) {
|
|
120
|
-
|
|
207
|
+
emitKey(keyForChar(ch));
|
|
121
208
|
}
|
|
122
209
|
}
|
|
123
210
|
});
|
|
124
|
-
}, [agentRuntime, keyHandler]);
|
|
211
|
+
}, [agentRuntime, internal, keyHandler]);
|
|
125
212
|
}
|
|
126
213
|
//# sourceMappingURL=input-bridge.js.map
|
|
@@ -41,6 +41,14 @@ export interface InstallOpenTUIHarnessOptions {
|
|
|
41
41
|
* the `--agent-fake-clock` CLI flag.
|
|
42
42
|
*/
|
|
43
43
|
fakeClock?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Optional visual-frame capture. When provided, a VisualFrame (the actual
|
|
46
|
+
* rendered cell grid — colors + attributes) is captured and sent right after
|
|
47
|
+
* each SEMANTIC frame change. Piggybacking on semantic changes bounds the
|
|
48
|
+
* per-frame `getSpanLines()` allocation to real UI updates instead of every
|
|
49
|
+
* poll tick. Returns null on dedup / unavailable renderer.
|
|
50
|
+
*/
|
|
51
|
+
captureVisual?: (seq: number, ts: number) => object | null;
|
|
44
52
|
}
|
|
45
53
|
export interface OpenTUIHarnessHandle {
|
|
46
54
|
/**
|
|
@@ -28,6 +28,7 @@ export function installOpenTUIHarness(opts) {
|
|
|
28
28
|
const fps = opts.fps ?? 60;
|
|
29
29
|
const intervalMs = Math.max(1, Math.round(1000 / fps));
|
|
30
30
|
let seq = 0;
|
|
31
|
+
let vseq = 0;
|
|
31
32
|
const fakeClock = opts.fakeClock ?? false;
|
|
32
33
|
const hook = createReconcilerHook({
|
|
33
34
|
registry: opts.registry,
|
|
@@ -43,6 +44,15 @@ export function installOpenTUIHarness(opts) {
|
|
|
43
44
|
try {
|
|
44
45
|
opts.transport.send(`${JSON.stringify(frame)}\n`);
|
|
45
46
|
opts.onFrame?.(frame);
|
|
47
|
+
// Piggyback the visual (rendered cell-grid) frame on semantic changes so
|
|
48
|
+
// getSpanLines() only runs when the UI actually changed, not every tick.
|
|
49
|
+
if (opts.captureVisual) {
|
|
50
|
+
const vframe = opts.captureVisual(vseq, fakeClock ? vseq * 16 : Date.now());
|
|
51
|
+
if (vframe !== null) {
|
|
52
|
+
vseq++;
|
|
53
|
+
opts.transport.send(`${JSON.stringify(vframe)}\n`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
46
56
|
}
|
|
47
57
|
catch {
|
|
48
58
|
// Drop frame on transport error — survivor of next tick will retry.
|
|
@@ -31,22 +31,24 @@ export function Semantic(props) {
|
|
|
31
31
|
const unregister = registry.register({ ...nodeRef.current, parentId });
|
|
32
32
|
return unregister;
|
|
33
33
|
}, [registry, parentId, node.id]);
|
|
34
|
-
// Updates:
|
|
35
|
-
//
|
|
36
|
-
//
|
|
34
|
+
// Updates: patch fields on prop change via registry.update(). nodeKey is the
|
|
35
|
+
// intentional cache-bust dep — it serializes the node fields so this effect
|
|
36
|
+
// re-runs whenever any field changes; Map.set on an existing key preserves
|
|
37
|
+
// insertion order.
|
|
37
38
|
React.useEffect(() => {
|
|
38
39
|
if (!registry)
|
|
39
40
|
return;
|
|
40
41
|
if (!nodeKey)
|
|
41
42
|
return;
|
|
42
43
|
// Skip the very first run — the mount effect above already registered.
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
44
|
+
// On mount this effect runs after the mount effect, so the entry IS
|
|
45
|
+
// present; update() patches fields in place (Map.set on an existing key
|
|
46
|
+
// preserves insertion order). On subsequent renders with a different
|
|
47
|
+
// nodeKey it patches the changed fields. Using update() (not register())
|
|
48
|
+
// is the cheaper intended path — it merges instead of rebuilding the
|
|
49
|
+
// entry, and it is a no-op if the entry was already unmounted.
|
|
50
|
+
const { id: _id, ...patch } = nodeRef.current;
|
|
51
|
+
registry.update(nodeRef.current.id, patch);
|
|
50
52
|
// No cleanup — the mount effect's cleanup is the single source of
|
|
51
53
|
// truth for unmounting. If we returned a cleanup here, it would race
|
|
52
54
|
// with re-renders and re-delete the entry.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* visual-capture.ts — read OpenTUI's ACTUAL rendered cell grid.
|
|
3
|
+
*
|
|
4
|
+
* The semantic harness (reconciler-hook) reports STRUCTURE (id/role/name/props)
|
|
5
|
+
* but is blind to what a human SEES — colors, bold/italic, alignment, blank
|
|
6
|
+
* rows, mojibake. This module snapshots the ground-truth render buffer instead:
|
|
7
|
+
* OpenTUI's `CliRenderer.currentRenderBuffer` is a cell grid it already paints,
|
|
8
|
+
* and `OptimizedBuffer.getSpanLines()` returns per-cell char + RGBA fg/bg +
|
|
9
|
+
* attribute bits (verified against @opentui/core@0.1.107 buffer.d.ts:43 +
|
|
10
|
+
* lib/RGBA.d.ts). No OCR — the buffer is authoritative and in-process.
|
|
11
|
+
*
|
|
12
|
+
* Consumers get a `VisualFrame` (protocol) they can query per-cell, render to
|
|
13
|
+
* annotated text, or (P2) rasterize to PNG for a vision model.
|
|
14
|
+
*/
|
|
15
|
+
import { type VisualFrame } from "@muonroi/agent-harness-core/protocol";
|
|
16
|
+
interface RgbaLike {
|
|
17
|
+
toInts?: () => [number, number, number, number];
|
|
18
|
+
r?: number;
|
|
19
|
+
g?: number;
|
|
20
|
+
b?: number;
|
|
21
|
+
a?: number;
|
|
22
|
+
}
|
|
23
|
+
interface CapturedSpanLike {
|
|
24
|
+
text: string;
|
|
25
|
+
fg: RgbaLike;
|
|
26
|
+
bg: RgbaLike;
|
|
27
|
+
attributes: number;
|
|
28
|
+
width: number;
|
|
29
|
+
}
|
|
30
|
+
interface CapturedLineLike {
|
|
31
|
+
spans: CapturedSpanLike[];
|
|
32
|
+
}
|
|
33
|
+
interface OptimizedBufferLike {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
getSpanLines: () => CapturedLineLike[];
|
|
37
|
+
}
|
|
38
|
+
export interface RendererLike {
|
|
39
|
+
currentRenderBuffer?: OptimizedBufferLike;
|
|
40
|
+
nextRenderBuffer?: OptimizedBufferLike;
|
|
41
|
+
}
|
|
42
|
+
export interface VisualCaptureHook {
|
|
43
|
+
/** Build a VisualFrame from the renderer's current buffer. Returns null when
|
|
44
|
+
* no renderer is attached, the buffer is unreadable, or content is unchanged
|
|
45
|
+
* (content-hash dedup — same contract as the semantic reconciler hook). */
|
|
46
|
+
capture(seq: number, ts: number): VisualFrame | null;
|
|
47
|
+
/** Reset dedup state so the next capture always emits. */
|
|
48
|
+
resetDedup(): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a visual-capture hook. `getRenderer` is a late-bound accessor because
|
|
52
|
+
* the OpenTUI renderer is constructed AFTER the agent-mode runtime (which owns
|
|
53
|
+
* this hook) — see src/index.ts attachRenderer wiring.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createVisualCaptureHook(getRenderer: () => RendererLike | undefined): VisualCaptureHook;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* visual-capture.ts — read OpenTUI's ACTUAL rendered cell grid.
|
|
3
|
+
*
|
|
4
|
+
* The semantic harness (reconciler-hook) reports STRUCTURE (id/role/name/props)
|
|
5
|
+
* but is blind to what a human SEES — colors, bold/italic, alignment, blank
|
|
6
|
+
* rows, mojibake. This module snapshots the ground-truth render buffer instead:
|
|
7
|
+
* OpenTUI's `CliRenderer.currentRenderBuffer` is a cell grid it already paints,
|
|
8
|
+
* and `OptimizedBuffer.getSpanLines()` returns per-cell char + RGBA fg/bg +
|
|
9
|
+
* attribute bits (verified against @opentui/core@0.1.107 buffer.d.ts:43 +
|
|
10
|
+
* lib/RGBA.d.ts). No OCR — the buffer is authoritative and in-process.
|
|
11
|
+
*
|
|
12
|
+
* Consumers get a `VisualFrame` (protocol) they can query per-cell, render to
|
|
13
|
+
* annotated text, or (P2) rasterize to PNG for a vision model.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { PROTOCOL_VERSION, } from "@muonroi/agent-harness-core/protocol";
|
|
17
|
+
/** Low byte of OpenTUI's TextAttributes (ATTRIBUTE_BASE_MASK = 255). */
|
|
18
|
+
const ATTR_MASK = 255;
|
|
19
|
+
function clampByte(n) {
|
|
20
|
+
return Math.max(0, Math.min(255, Math.round(n)));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert an OpenTUI RGBA to a "#rrggbb" (or "#rrggbbaa" when translucent) hex
|
|
24
|
+
* string. Prefers `toInts()` (0-255 ints); falls back to component getters,
|
|
25
|
+
* scaling 0-1 floats up to 0-255 when the renderer exposes normalized channels.
|
|
26
|
+
*/
|
|
27
|
+
function toHex(c) {
|
|
28
|
+
let r = 0;
|
|
29
|
+
let g = 0;
|
|
30
|
+
let b = 0;
|
|
31
|
+
let a = 255;
|
|
32
|
+
if (c && typeof c.toInts === "function") {
|
|
33
|
+
const ints = c.toInts();
|
|
34
|
+
r = ints[0];
|
|
35
|
+
g = ints[1];
|
|
36
|
+
b = ints[2];
|
|
37
|
+
a = ints[3];
|
|
38
|
+
}
|
|
39
|
+
else if (c) {
|
|
40
|
+
const scale = (v) => (v === undefined ? 0 : v <= 1 ? v * 255 : v);
|
|
41
|
+
r = scale(c.r);
|
|
42
|
+
g = scale(c.g);
|
|
43
|
+
b = scale(c.b);
|
|
44
|
+
a = c.a === undefined ? 255 : c.a <= 1 ? c.a * 255 : c.a;
|
|
45
|
+
}
|
|
46
|
+
const h = (n) => clampByte(n).toString(16).padStart(2, "0");
|
|
47
|
+
return a < 255 ? `#${h(r)}${h(g)}${h(b)}${h(a)}` : `#${h(r)}${h(g)}${h(b)}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a visual-capture hook. `getRenderer` is a late-bound accessor because
|
|
51
|
+
* the OpenTUI renderer is constructed AFTER the agent-mode runtime (which owns
|
|
52
|
+
* this hook) — see src/index.ts attachRenderer wiring.
|
|
53
|
+
*/
|
|
54
|
+
export function createVisualCaptureHook(getRenderer) {
|
|
55
|
+
let lastHash;
|
|
56
|
+
function capture(seq, ts) {
|
|
57
|
+
const renderer = getRenderer();
|
|
58
|
+
const buffer = renderer?.currentRenderBuffer ?? renderer?.nextRenderBuffer;
|
|
59
|
+
if (!buffer || typeof buffer.getSpanLines !== "function")
|
|
60
|
+
return null;
|
|
61
|
+
let captured;
|
|
62
|
+
try {
|
|
63
|
+
captured = buffer.getSpanLines();
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
// getSpanLines allocates + decodes native Zig memory; a mid-resize/destroy
|
|
67
|
+
// race can throw. Skip this frame (the next tick retries). Debug-gated log
|
|
68
|
+
// only — unconditional logging here would spam the render loop.
|
|
69
|
+
if (process.env.MUONROI_DEBUG_VISUAL) {
|
|
70
|
+
console.error(`[visual-capture] getSpanLines failed: ${err?.message}`);
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const lines = captured.map((ln) => ({
|
|
75
|
+
spans: (ln.spans ?? []).map((s) => ({
|
|
76
|
+
text: s.text,
|
|
77
|
+
fg: toHex(s.fg),
|
|
78
|
+
bg: toHex(s.bg),
|
|
79
|
+
attrs: (s.attributes ?? 0) & ATTR_MASK,
|
|
80
|
+
width: s.width,
|
|
81
|
+
})),
|
|
82
|
+
}));
|
|
83
|
+
const hash = createHash("sha1").update(JSON.stringify(lines)).digest("hex");
|
|
84
|
+
if (lastHash === hash)
|
|
85
|
+
return null;
|
|
86
|
+
lastHash = hash;
|
|
87
|
+
return {
|
|
88
|
+
mode: "visual",
|
|
89
|
+
version: PROTOCOL_VERSION,
|
|
90
|
+
seq,
|
|
91
|
+
ts,
|
|
92
|
+
cols: buffer.width,
|
|
93
|
+
rows: buffer.height,
|
|
94
|
+
cursor: null,
|
|
95
|
+
lines,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function resetDedup() {
|
|
99
|
+
lastHash = undefined;
|
|
100
|
+
}
|
|
101
|
+
return { capture, resetDedup };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=visual-capture.js.map
|