mixdog 0.9.2 → 0.9.4
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/package.json +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/telegram-format.mjs +19 -22
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +6 -14
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +259 -80
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +56 -588
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -462
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
package/src/tui/figures.mjs
CHANGED
|
@@ -13,6 +13,11 @@ export const BULLET_OPERATOR = '\u00b7'; // · - inline separator
|
|
|
13
13
|
export const UP_ARROW = '\u2191'; // ↑ - history/scroll up
|
|
14
14
|
export const DOWN_ARROW = '\u2193'; // ↓ - history/scroll down
|
|
15
15
|
|
|
16
|
+
// Agent surface markers — dispatch (call out) vs response (result back).
|
|
17
|
+
// Basic Arrows block renders reliably in Cascadia/Consolas (same block as ↑↓).
|
|
18
|
+
export const RIGHT_ARROW = '\u2192'; // → - agent call/dispatch marker
|
|
19
|
+
export const LEFT_ARROW = '\u2190'; // ← - agent response marker
|
|
20
|
+
|
|
16
21
|
// Tool-result tree gutter. \u2514 (└, BOX DRAWINGS LIGHT UP AND RIGHT) renders
|
|
17
22
|
// in virtually every monospace font; the previous \u23bf (bracket extension)
|
|
18
23
|
// had no glyph in common terminal fonts (Cascadia/Consolas) and fell back to a
|
package/src/tui/index.jsx
CHANGED
|
@@ -141,6 +141,101 @@ function resolveTuiStderrLogPath() {
|
|
|
141
141
|
|| join(process.env.MIXDOG_RUNTIME_ROOT || join(tmpdir(), 'mixdog'), 'mixdog-tui.stderr.log');
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/** `rgb(r,g,b)` → truecolor SGR prefix ('' when unparsable). */
|
|
145
|
+
function ansiFg(rgb) {
|
|
146
|
+
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ''));
|
|
147
|
+
return m ? `\x1b[38;2;${m[1]};${m[2]};${m[3]}m` : '';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Paint a static boot splash into the (already-entered) alternate screen so
|
|
152
|
+
* the seconds between alt-screen entry and the first ink frame are not a blank
|
|
153
|
+
* void. The logo is drawn at the SAME rows/columns as App.jsx's welcome banner
|
|
154
|
+
* (marginTop 3 + 5 logo rows + blank + subtitle), so ink's first real frame
|
|
155
|
+
* overprints it in place with no visible jump. The bottom panel silhouette
|
|
156
|
+
* carries an animated "Loading projects…" spinner so the boot reads as an
|
|
157
|
+
* explicit loading state instead of an empty box that later fills with the
|
|
158
|
+
* list. Returns { stop } — call it right before ink mounts.
|
|
159
|
+
*/
|
|
160
|
+
function paintBootSplash() {
|
|
161
|
+
try {
|
|
162
|
+
const cols = Math.max(1, Number(process.stdout.columns) || 80);
|
|
163
|
+
const rows = Math.max(1, Number(process.stdout.rows) || 24);
|
|
164
|
+
const center = (s) => `${' '.repeat(Math.max(0, Math.floor((cols - s.length) / 2)))}${s}`;
|
|
165
|
+
const logo = [
|
|
166
|
+
'███╗ ███╗██╗██╗ ██╗██████╗ ██████╗ ██████╗ ',
|
|
167
|
+
'████╗ ████║██║╚██╗██╔╝██╔══██╗██╔═══██╗██╔════╝ ',
|
|
168
|
+
'██╔████╔██║██║ ╚███╔╝ ██║ ██║██║ ██║██║ ███╗',
|
|
169
|
+
'██║╚██╔╝██║██║ ██╔██╗ ██║ ██║██║ ██║██║ ██║',
|
|
170
|
+
'██║ ╚═╝ ██║██║██╔╝ ██╗██████╔╝╚██████╔╝╚██████╔╝',
|
|
171
|
+
];
|
|
172
|
+
const textFg = ansiFg(theme.text);
|
|
173
|
+
const logoFg = ansiFg(theme.logo ?? theme.claude) || textFg;
|
|
174
|
+
const subtleFg = ansiFg(theme.inactive);
|
|
175
|
+
const bold = '\x1b[1m';
|
|
176
|
+
const reset = '\x1b[0m';
|
|
177
|
+
let out = '\x1b[4;1H'; // row 4 == App banner's marginTop={3}
|
|
178
|
+
for (let i = 0; i < logo.length; i++) {
|
|
179
|
+
const fg = i < 2 ? textFg : logoFg;
|
|
180
|
+
out += `${bold}${fg}${center(logo[i])}${reset}\r\n`;
|
|
181
|
+
}
|
|
182
|
+
out += '\r\n';
|
|
183
|
+
out += `${subtleFg}${center(`mixdog coding agent · ${process.cwd()}`)}${reset}`;
|
|
184
|
+
// Bottom placeholder: the first REAL screen is the project picker — a tall
|
|
185
|
+
// floating panel sitting directly above the 3-row statusline — not the
|
|
186
|
+
// 3-row prompt box. Painting a short prompt silhouette here made the panel
|
|
187
|
+
// look like it "unfolded" from 3 rows to full height on the first ink
|
|
188
|
+
// frame. Mirror App.jsx's startup math instead:
|
|
189
|
+
// floatingPanelRows = min(17, rows - 15), bottom row = rows - 3.
|
|
190
|
+
const floating = Math.min(17, rows - 15);
|
|
191
|
+
let spinnerRow = 0;
|
|
192
|
+
let spinnerFg = '';
|
|
193
|
+
if (floating >= 3) {
|
|
194
|
+
const borderFg = ansiFg(theme.promptBorder) || subtleFg;
|
|
195
|
+
// Leave the last COLUMN untouched: writing the bottom-right cell makes
|
|
196
|
+
// Windows Terminal/conhost scroll the alt screen one row (same reason
|
|
197
|
+
// App.jsx reserves rightSafetyColumns), which made the whole splash jump
|
|
198
|
+
// when the box was painted at full width on the final row.
|
|
199
|
+
const boxW = Math.max(4, cols - 1);
|
|
200
|
+
const innerW = boxW - 2;
|
|
201
|
+
const topRow = rows - 3 - floating + 1;
|
|
202
|
+
out += `\x1b[${topRow};1H${borderFg}╭${'─'.repeat(innerW)}╮`;
|
|
203
|
+
for (let i = 1; i < floating - 1; i++) {
|
|
204
|
+
out += `\x1b[${topRow + i};1H│${' '.repeat(innerW)}│`;
|
|
205
|
+
}
|
|
206
|
+
out += `\x1b[${rows - 3};1H╰${'─'.repeat(innerW)}╯${reset}`;
|
|
207
|
+
// "Loading projects…" line inside the panel silhouette. Picker.jsx
|
|
208
|
+
// renders border, then (paddingX=1, no paddingY) title/help row as its
|
|
209
|
+
// very first interior row — not vertically centered. Match that rhythm
|
|
210
|
+
// (topRow + 1) instead of centering, so ink's first frame doesn't jump
|
|
211
|
+
// the text down/up when it overprints this splash.
|
|
212
|
+
spinnerRow = topRow + 1;
|
|
213
|
+
spinnerFg = subtleFg;
|
|
214
|
+
out += `\x1b[${spinnerRow};3H${subtleFg} Loading projects…${reset}`;
|
|
215
|
+
}
|
|
216
|
+
// Park the cursor at home so ink's first frame paints top-down over the
|
|
217
|
+
// splash instead of starting at the bottom row and scrolling the screen.
|
|
218
|
+
out += '\x1b[H';
|
|
219
|
+
process.stdout.write(out);
|
|
220
|
+
// Animate only the single spinner glyph cell — never reflow anything else,
|
|
221
|
+
// so stopping mid-frame can't leave torn output for ink to overwrite.
|
|
222
|
+
if (spinnerRow > 0) {
|
|
223
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
224
|
+
let tick = 0;
|
|
225
|
+
const timer = setInterval(() => {
|
|
226
|
+
try {
|
|
227
|
+
process.stdout.write(`\x1b[${spinnerRow};3H${spinnerFg}${frames[tick % frames.length]}${reset}\x1b[H`);
|
|
228
|
+
tick += 1;
|
|
229
|
+
} catch { /* ignore */ }
|
|
230
|
+
}, 80);
|
|
231
|
+
timer.unref?.();
|
|
232
|
+
return { stop: () => clearInterval(timer) };
|
|
233
|
+
}
|
|
234
|
+
return { stop: () => {} };
|
|
235
|
+
} catch { /* cosmetic only — never block boot */ }
|
|
236
|
+
return { stop: () => {} };
|
|
237
|
+
}
|
|
238
|
+
|
|
144
239
|
/** Drain stdin so queued key/mouse bytes do not leak into the shell after exit. */
|
|
145
240
|
function drainStdin(stdin = process.stdin) {
|
|
146
241
|
if (!stdin.isTTY) return;
|
|
@@ -277,17 +372,27 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
|
|
|
277
372
|
try { await loadThemeSettingFromConfig(); } catch { /* default theme stays */ }
|
|
278
373
|
emitTerminalBackground(theme.background);
|
|
279
374
|
|
|
375
|
+
// Static splash + loading spinner while createEngineSession() warms up
|
|
376
|
+
// (config, providers, sessions). Without this the user stares at an empty
|
|
377
|
+
// alt screen for the whole boot, then the UI "pops in" — the splash makes
|
|
378
|
+
// the first ink frame an in-place repaint instead.
|
|
379
|
+
const splash = paintBootSplash() || { stop: () => {} };
|
|
380
|
+
|
|
280
381
|
let store;
|
|
281
382
|
try {
|
|
282
383
|
store = await createEngineSession({ provider, model, toolMode, remote });
|
|
283
384
|
bootProfile('store:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
284
385
|
} catch (error) {
|
|
386
|
+
splash.stop();
|
|
285
387
|
restoreTerminal();
|
|
286
388
|
try { process.off('exit', restoreTerminal); } catch { /* ignore */ }
|
|
287
389
|
restoreStderr();
|
|
288
390
|
process.stderr.write(`mixdog: ${error?.message || error}\n`);
|
|
289
391
|
return 1;
|
|
290
392
|
}
|
|
393
|
+
// Stop the spinner BEFORE ink mounts so no stray splash write can land
|
|
394
|
+
// between (or after) ink's first frames.
|
|
395
|
+
splash.stop();
|
|
291
396
|
|
|
292
397
|
// Keep mouse handling app-owned by default: native terminal selections are
|
|
293
398
|
// cleared by the fullscreen redraws that happen while a turn streams. Users
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { displayWidth } from './display-width.mjs';
|
|
2
2
|
|
|
3
3
|
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
4
4
|
|
|
@@ -122,32 +122,79 @@ export function lineEnd(text, offset) {
|
|
|
122
122
|
return end === -1 ? value.length : end;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
// Return the [start,end) run of same-class characters (word-like vs
|
|
126
|
+
// non-word-like) surrounding `offset`, used by double-click word selection.
|
|
127
|
+
// Mirrors typical desktop double-click behavior: clicking inside a run of
|
|
128
|
+
// letters/digits selects that whole run; clicking on a run of punctuation or
|
|
129
|
+
// whitespace selects that run instead (still deterministic, never empty).
|
|
130
|
+
export function wordRangeAt(text, offset) {
|
|
131
|
+
const value = String(text || '');
|
|
132
|
+
const units = graphemeUnits(value);
|
|
133
|
+
if (units.length === 0) return { start: 0, end: 0 };
|
|
134
|
+
const off = clampOffset(value, offset);
|
|
135
|
+
let idx = units.findIndex((u) => off >= u.start && off < u.end);
|
|
136
|
+
if (idx === -1) {
|
|
137
|
+
idx = units.length - 1;
|
|
138
|
+
for (let i = 0; i < units.length; i += 1) {
|
|
139
|
+
if (units[i].start >= off) { idx = i > 0 ? i - 1 : 0; break; }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const targetIsWord = wordLike(units[idx].segment);
|
|
143
|
+
let start = idx;
|
|
144
|
+
while (start > 0 && wordLike(units[start - 1].segment) === targetIsWord) start -= 1;
|
|
145
|
+
let end = idx;
|
|
146
|
+
while (end < units.length - 1 && wordLike(units[end + 1].segment) === targetIsWord) end += 1;
|
|
147
|
+
return { start: units[start].start, end: units[end].end };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// `hasTrailingContent` (default derived from the full text) decides whether a
|
|
151
|
+
// caret that lands FLUSH on the last column (col === w) rolls to the next row.
|
|
152
|
+
// It must reflect whether ANY rendered cell follows this offset in the FULL
|
|
153
|
+
// text — not just the sliced prefix — otherwise a caret at a mid-text soft-wrap
|
|
154
|
+
// boundary reports {row N, col w} while ink already rendered the next glyph on
|
|
155
|
+
// row N+1, dropping the caret outside the box. PromptInput also appends a
|
|
156
|
+
// trailing space cell when the cursor sits at end-of-input, so "end of text"
|
|
157
|
+
// still has a following cell there; callers pass hasTrailingContent=true for
|
|
158
|
+
// that case.
|
|
159
|
+
export function caretPosition(text, offset, width, hasTrailingContent = undefined) {
|
|
126
160
|
const value = String(text || '');
|
|
127
161
|
const before = value.slice(0, offset);
|
|
128
162
|
const w = safeWidth(width);
|
|
163
|
+
const followsInFullText = hasTrailingContent === undefined
|
|
164
|
+
? offset < value.length
|
|
165
|
+
: hasTrailingContent === true;
|
|
129
166
|
let row = 0;
|
|
130
167
|
let col = 0;
|
|
131
|
-
|
|
132
|
-
for (
|
|
168
|
+
const segments = [...graphemeSegmenter.segment(before)];
|
|
169
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
170
|
+
const { segment } = segments[i];
|
|
133
171
|
if (segment === '\n') {
|
|
134
172
|
row += 1;
|
|
135
173
|
col = 0;
|
|
136
174
|
continue;
|
|
137
175
|
}
|
|
138
176
|
|
|
139
|
-
const segmentWidth =
|
|
177
|
+
const segmentWidth = displayWidth(segment);
|
|
140
178
|
if (segmentWidth === 0) continue;
|
|
141
179
|
|
|
180
|
+
// Mirror ink's wrap-ansi wrapWord (wrap="hard", wordWrap:false): a glyph
|
|
181
|
+
// that would overflow the row is pushed WHOLE to the next row (col never
|
|
182
|
+
// exceeds w for wide chars — no half-column left behind), and a glyph that
|
|
183
|
+
// lands flush on the last column starts a new row ONLY when more rendered
|
|
184
|
+
// content follows. Whether content follows is decided from the FULL text
|
|
185
|
+
// (followsInFullText), not the sliced prefix, so a caret at a mid-text wrap
|
|
186
|
+
// boundary rolls to row N+1 exactly as ink renders it. The old
|
|
187
|
+
// `col >= w → row += floor(col/w)` over-counted a row for width-2 glyphs at
|
|
188
|
+
// odd widths (col could reach w+1), landing the cursor one row too low.
|
|
142
189
|
if (col > 0 && col + segmentWidth > w) {
|
|
143
190
|
row += 1;
|
|
144
191
|
col = 0;
|
|
145
192
|
}
|
|
146
|
-
|
|
147
193
|
col += segmentWidth;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
194
|
+
const moreFollows = i < segments.length - 1 || followsInFullText;
|
|
195
|
+
if (col === w && moreFollows) {
|
|
196
|
+
row += 1;
|
|
197
|
+
col = 0;
|
|
151
198
|
}
|
|
152
199
|
}
|
|
153
200
|
|
|
@@ -185,13 +232,16 @@ function boundaryPositions(text, width) {
|
|
|
185
232
|
for (const unit of graphemeUnits(value)) offsets.push(unit.end);
|
|
186
233
|
|
|
187
234
|
const seen = new Set();
|
|
188
|
-
|
|
235
|
+
const positions = offsets
|
|
189
236
|
.filter((offset) => {
|
|
190
237
|
if (seen.has(offset)) return false;
|
|
191
238
|
seen.add(offset);
|
|
192
239
|
return true;
|
|
193
240
|
})
|
|
241
|
+
// Decide the flush-boundary advance from the FULL text so a soft-wrap
|
|
242
|
+
// offset reports {row N+1, col 0} (matching ink) instead of {row N, col w}.
|
|
194
243
|
.map((offset) => ({ offset, ...caretPosition(value, offset, width) }));
|
|
244
|
+
return positions;
|
|
195
245
|
}
|
|
196
246
|
|
|
197
247
|
export function verticalOffset(text, offset, width, direction, preferredColumn = null) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/tui/keyboard-protocol.mjs — terminal extended-keys capability gate.
|
|
3
3
|
*
|
|
4
|
-
* We do NOT query the terminal. Instead,
|
|
4
|
+
* We do NOT query the terminal. Instead, we enable the
|
|
5
5
|
* kitty keyboard protocol AND xterm modifyOtherKeys SYNCHRONOUSLY at the moment
|
|
6
6
|
* raw mode turns on (see App.jsx's mount effect), gated by the allowlist below.
|
|
7
7
|
* Writing both enables unconditionally is safe — a terminal honors whichever it
|
|
@@ -22,7 +22,7 @@ export const POP_KITTY = '\x1b[<u';
|
|
|
22
22
|
export const DISABLE_MODIFY_OTHER_KEYS = '\x1b[>4;0m';
|
|
23
23
|
|
|
24
24
|
// Allowlist of terminals known to honor kitty and/or xterm modifyOtherKeys.
|
|
25
|
-
//
|
|
25
|
+
// VS Code's xterm.js
|
|
26
26
|
// integrated terminal mishandles these sequences, so it is excluded first.
|
|
27
27
|
// Honors the MIXDOG_TUI_EXTENDED_KEYS opt-out (=0) / override (=1).
|
|
28
28
|
export function supportsExtendedKeys() {
|
|
@@ -80,28 +80,42 @@ export function isVoiceEnabled() {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// Coalesce ensure*'s onProgress ({ phase, downloaded, total } | { phase:'extra', ... })
|
|
83
|
-
// into a
|
|
84
|
-
// model download doesn't spam the notice toast on every
|
|
85
|
-
|
|
83
|
+
// into a sticky, in-place input-hint-line progress bar (setProgressHint) so a
|
|
84
|
+
// multi-hundred-MB model download doesn't spam the notice toast on every
|
|
85
|
+
// chunk. Falls back to the old throttled (2s) toast-spam behavior when the
|
|
86
|
+
// caller (e.g. the channels pipeline) doesn't have a progress-hint slot to
|
|
87
|
+
// render into — only pushNotice is guaranteed to exist everywhere.
|
|
88
|
+
const PROGRESS_BAR_CELLS = 6;
|
|
89
|
+
function renderProgressBarText(phase, downloaded, total) {
|
|
90
|
+
const label = phaseLabel(phase);
|
|
91
|
+
if (total > 0) {
|
|
92
|
+
const ratio = Math.max(0, Math.min(1, downloaded / total));
|
|
93
|
+
const filled = Math.round(ratio * PROGRESS_BAR_CELLS);
|
|
94
|
+
const bar = '▓'.repeat(filled) + '░'.repeat(PROGRESS_BAR_CELLS - filled);
|
|
95
|
+
return `⬇ Voice ${label} ${bar} ${Math.round(ratio * 100)}%`;
|
|
96
|
+
}
|
|
97
|
+
return `⬇ Voice ${label} …`;
|
|
98
|
+
}
|
|
99
|
+
function phaseLabel(phase) {
|
|
100
|
+
if (phase === 'model') return 'model';
|
|
101
|
+
if (phase === 'ffmpeg') return 'ffmpeg';
|
|
102
|
+
if (phase === 'extra') return 'extra data';
|
|
103
|
+
return 'whisper runtime';
|
|
104
|
+
}
|
|
105
|
+
function makeThrottledProgressNotice({ pushNotice, setProgressHint } = {}, intervalMs = 500) {
|
|
86
106
|
let lastEmitAt = 0;
|
|
87
|
-
const phaseLabel = (phase) => {
|
|
88
|
-
if (phase === 'model') return 'model';
|
|
89
|
-
if (phase === 'ffmpeg') return 'ffmpeg';
|
|
90
|
-
if (phase === 'extra') return 'extra data';
|
|
91
|
-
return 'whisper runtime';
|
|
92
|
-
};
|
|
93
107
|
return (progress = {}) => {
|
|
94
|
-
if (typeof pushNotice !== 'function') return;
|
|
95
108
|
const now = Date.now();
|
|
96
109
|
if (now - lastEmitAt < intervalMs) return;
|
|
97
110
|
lastEmitAt = now;
|
|
98
111
|
const total = Number(progress.total) || 0;
|
|
99
112
|
const downloaded = Number(progress.downloaded) || 0;
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
const text = renderProgressBarText(progress.phase, downloaded, total);
|
|
114
|
+
if (typeof setProgressHint === 'function') {
|
|
115
|
+
setProgressHint(text, 'info');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (typeof pushNotice === 'function') pushNotice(text, 'info');
|
|
105
119
|
};
|
|
106
120
|
}
|
|
107
121
|
|
|
@@ -112,12 +126,12 @@ function makeThrottledProgressNotice(pushNotice, intervalMs = 2000) {
|
|
|
112
126
|
* runtime descriptor (resolveVoiceRuntime shape) once `installed` is true;
|
|
113
127
|
* throws on any ensure* failure (manifest fetch, sha256 mismatch, etc.).
|
|
114
128
|
*/
|
|
115
|
-
export async function ensureVoiceRuntimeReady({ dataDir = resolvePluginData(), pushNotice } = {}) {
|
|
129
|
+
export async function ensureVoiceRuntimeReady({ dataDir = resolvePluginData(), pushNotice, setProgressHint } = {}) {
|
|
116
130
|
const fetcher = await loadVoiceRuntimeFetcher();
|
|
117
131
|
let runtime = fetcher.resolveVoiceRuntime(dataDir);
|
|
118
132
|
if (runtime.installed) return runtime;
|
|
119
133
|
|
|
120
|
-
const onProgress = makeThrottledProgressNotice(pushNotice);
|
|
134
|
+
const onProgress = makeThrottledProgressNotice({ pushNotice, setProgressHint });
|
|
121
135
|
if (!runtime.binary || !runtime.serverCmd) {
|
|
122
136
|
await fetcher.ensureWhisperRuntime(dataDir, onProgress);
|
|
123
137
|
}
|
|
@@ -147,7 +161,7 @@ export async function ensureVoiceRuntimeReady({ dataDir = resolvePluginData(), p
|
|
|
147
161
|
* Returns the NEW enabled state (true/false) on success, or null when the
|
|
148
162
|
* toggle could not run (install already in flight) or failed.
|
|
149
163
|
*/
|
|
150
|
-
export async function toggleVoice({ pushNotice } = {}) {
|
|
164
|
+
export async function toggleVoice({ pushNotice, setProgressHint } = {}) {
|
|
151
165
|
const dataDir = resolvePluginData();
|
|
152
166
|
if (isVoiceEnabled()) {
|
|
153
167
|
// Med-5: updateSection is a synchronous file write (readAllForRmW +
|
|
@@ -172,11 +186,13 @@ export async function toggleVoice({ pushNotice } = {}) {
|
|
|
172
186
|
}
|
|
173
187
|
_voiceInstallBusy = true;
|
|
174
188
|
try {
|
|
175
|
-
await ensureVoiceRuntimeReady({ dataDir, pushNotice });
|
|
189
|
+
await ensureVoiceRuntimeReady({ dataDir, pushNotice, setProgressHint });
|
|
176
190
|
updateSection('voice', (current) => ({ ...current, enabled: true }));
|
|
191
|
+
setProgressHint?.('');
|
|
177
192
|
pushNotice?.('Voice ON — Ctrl+Space to record', 'info');
|
|
178
193
|
return true;
|
|
179
194
|
} catch (err) {
|
|
195
|
+
setProgressHint?.('');
|
|
180
196
|
pushNotice?.(`Voice setup failed: ${err?.message || err}`, 'error');
|
|
181
197
|
return null;
|
|
182
198
|
} finally {
|
|
@@ -108,8 +108,8 @@ function decodeEntities(s) {
|
|
|
108
108
|
|
|
109
109
|
// ── Fenced code block rendering ─────────────────────────────────────────────
|
|
110
110
|
// Gutter-indented, syntax-highlighted body with NO background band and no ```
|
|
111
|
-
// fences
|
|
112
|
-
//
|
|
111
|
+
// fences. Diff/patch languages (and bodies that look like unified diffs) keep
|
|
112
|
+
// the diff highlighter; other languages use
|
|
113
113
|
// cli-highlight (highlight.js) themed from our syntax* palette; unknown
|
|
114
114
|
// languages fall back to flat `mdCodeBlock` body color.
|
|
115
115
|
const DIFF_LANGS = new Set(['diff', 'patch', 'udiff', 'git-diff', 'gitdiff']);
|
|
@@ -567,7 +567,7 @@ function collectHighlightedBodyLines(text, hljsLang, bandWidth) {
|
|
|
567
567
|
const CODE_GUTTER = ' ';
|
|
568
568
|
|
|
569
569
|
/**
|
|
570
|
-
* Render a fenced `code` token
|
|
570
|
+
* Render a fenced `code` token: an optional
|
|
571
571
|
* subtle language label row, then the wrapped, syntax/diff/flat-highlighted
|
|
572
572
|
* body. Every row is left-indented by a 2-col gutter. NO background band, no
|
|
573
573
|
* full-width padding, and no visible ``` fence lines.
|
|
@@ -653,7 +653,7 @@ export function formatToken(token, listBaseIndent = 0, orderedListNumber = null,
|
|
|
653
653
|
const ex = extraColorizers();
|
|
654
654
|
switch (token.type) {
|
|
655
655
|
case 'blockquote': {
|
|
656
|
-
// Block structure carries no color
|
|
656
|
+
// Block structure carries no color: a dim bar plus
|
|
657
657
|
// body-colored italic text. Color is reserved for inline codespan/link.
|
|
658
658
|
const bar = chalk.dim(BLOCKQUOTE_BAR);
|
|
659
659
|
const quotePrefix = `${BLOCKQUOTE_BAR} `;
|
|
@@ -673,12 +673,11 @@ export function formatToken(token, listBaseIndent = 0, orderedListNumber = null,
|
|
|
673
673
|
return renderCodeBlock(token, width);
|
|
674
674
|
case 'codespan':
|
|
675
675
|
// inline code — accent color only (no background tint; a bg box behind
|
|
676
|
-
// inline spans reads as awkward against body text
|
|
677
|
-
// codespan = color-only treatment).
|
|
676
|
+
// inline spans reads as awkward against body text).
|
|
678
677
|
return accent(decodeEntities(token.text));
|
|
679
678
|
case 'em':
|
|
680
|
-
// Italic only — no color tint (
|
|
681
|
-
//
|
|
679
|
+
// Italic only — no color tint (a colored em clashes per-theme and
|
|
680
|
+
// reads loud against body prose).
|
|
682
681
|
return chalk.italic((token.tokens ?? []).map((t) => formatToken(t, 0, null, parent)).join(''));
|
|
683
682
|
case 'strong':
|
|
684
683
|
// Bold only — no color tint (stays body-colored, just heavier weight).
|
|
@@ -723,6 +722,9 @@ export function formatToken(token, listBaseIndent = 0, orderedListNumber = null,
|
|
|
723
722
|
return `${OSC8_OPEN(href)}${ex.link(href)}${OSC8_CLOSE}`;
|
|
724
723
|
}
|
|
725
724
|
case 'list':
|
|
725
|
+
// Honor loose lists (blank line between items in the source markdown):
|
|
726
|
+
// items are joined with one extra EOL so multi-line items keep the
|
|
727
|
+
// author's breathing room. Tight lists render adjacent as before.
|
|
726
728
|
return token.items
|
|
727
729
|
.map((item, index) =>
|
|
728
730
|
formatToken(
|
|
@@ -734,7 +736,7 @@ export function formatToken(token, listBaseIndent = 0, orderedListNumber = null,
|
|
|
734
736
|
depth,
|
|
735
737
|
),
|
|
736
738
|
)
|
|
737
|
-
.join('');
|
|
739
|
+
.join(token.loose && depth === 0 ? EOL : '');
|
|
738
740
|
case 'list_item':
|
|
739
741
|
return formatListItem(token, listBaseIndent, orderedListNumber, parent, depth, width);
|
|
740
742
|
case 'paragraph':
|
|
@@ -67,6 +67,44 @@ export function imageReferenceIds(input) {
|
|
|
67
67
|
return new Set([...String(input || '').matchAll(re)].map((m) => Number(m[1]) || 0).filter(Boolean));
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
// --- Large pasted-text folding ------------------------------------------
|
|
71
|
+
// A big paste is replaced in the prompt by a compact token so the editor does
|
|
72
|
+
// not choke on thousands of raw characters; the original text is stored in an
|
|
73
|
+
// App-level map (mirroring pastedImages) and expanded back on submit.
|
|
74
|
+
const PASTE_TOKEN_MIN_LINES = 3;
|
|
75
|
+
const PASTE_TOKEN_MIN_CHARS = 200;
|
|
76
|
+
|
|
77
|
+
export function shouldFoldPastedText(text) {
|
|
78
|
+
const value = String(text ?? '');
|
|
79
|
+
if (!value) return false;
|
|
80
|
+
const lineCount = value.split('\n').length;
|
|
81
|
+
return lineCount >= PASTE_TOKEN_MIN_LINES || value.length > PASTE_TOKEN_MIN_CHARS;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function formatPastedTextRef(id, text) {
|
|
85
|
+
const lines = String(text ?? '').split('\n').length;
|
|
86
|
+
return `[Pasted text #${id} +${lines} lines]`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function pastedTextReferenceIds(input) {
|
|
90
|
+
const re = /\[Pasted text #(\d+) \+\d+ lines\]/g;
|
|
91
|
+
return new Set([...String(input || '').matchAll(re)].map((m) => Number(m[1]) || 0).filter(Boolean));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Expand every intact pasted-text token in `value` back to its original text.
|
|
95
|
+
// Broken / partially-deleted tokens simply do not match and are left as-is.
|
|
96
|
+
export function expandPastedTextTokens(value, pastedTexts = {}) {
|
|
97
|
+
let out = String(value ?? '');
|
|
98
|
+
if (!pastedTexts || typeof pastedTexts !== 'object') return out;
|
|
99
|
+
const re = /\[Pasted text #(\d+) \+\d+ lines\]/g;
|
|
100
|
+
out = out.replace(re, (match, idRaw) => {
|
|
101
|
+
const entry = pastedTexts[Number(idRaw)] || pastedTexts[idRaw];
|
|
102
|
+
if (entry && typeof entry.text === 'string') return entry.text;
|
|
103
|
+
return match;
|
|
104
|
+
});
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
|
|
70
108
|
export function buildPromptContentWithImages(text, pastedImages = {}) {
|
|
71
109
|
const value = String(text ?? '');
|
|
72
110
|
const refs = imageReferenceIds(value);
|
|
@@ -42,11 +42,16 @@ export function isResetStatsState(stats) {
|
|
|
42
42
|
/**
|
|
43
43
|
* True when the footer should snap local / invalidate cached async output.
|
|
44
44
|
* Keep in sync with `shouldSnapLocalStatusline` in StatusLine.jsx.
|
|
45
|
+
* `agentRevision` is intentionally NOT checked here: agent worker/job churn
|
|
46
|
+
* must only kick off the async full-render refresh (the render effect already
|
|
47
|
+
* depends on `agentRevision`), never snap the footer back to the usage-less
|
|
48
|
+
* `localBootStatusLine` before/after the first full render — that was the
|
|
49
|
+
* source of the boot-entry flicker. `agentRevision` still participates in
|
|
50
|
+
* `statuslineFooterCacheKey` below so the async cache key churns correctly.
|
|
45
51
|
*/
|
|
46
52
|
export function statuslineFooterIdentityChanged(args, lastArgs) {
|
|
47
53
|
if (!args) return false;
|
|
48
54
|
if (!lastArgs) return true;
|
|
49
|
-
if (args.agentRevision !== lastArgs.agentRevision) return true;
|
|
50
55
|
if (args.sessionId !== lastArgs.sessionId) return true;
|
|
51
56
|
if (args.provider !== lastArgs.provider || args.model !== lastArgs.model) return true;
|
|
52
57
|
if (args.effort !== lastArgs.effort || args.fast !== lastArgs.fast) return true;
|
|
@@ -60,8 +65,11 @@ export function statuslineFooterIdentityChanged(args, lastArgs) {
|
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* Cache key for the last async full statusline (identity-changing props only).
|
|
63
|
-
*
|
|
64
|
-
*
|
|
68
|
+
* Includes `agentRevision` as a churn key (so a stale async result is not kept
|
|
69
|
+
* forever), but `agentRevision` is deliberately excluded from
|
|
70
|
+
* `statuslineFooterIdentityChanged` above — it must not itself force a local
|
|
71
|
+
* snap-back. Stats reset is not keyed here; `statuslineFooterIdentityChanged`
|
|
72
|
+
* clears cache instead.
|
|
65
73
|
*/
|
|
66
74
|
export function statuslineFooterCacheKey({
|
|
67
75
|
agentRevision = '',
|
package/src/tui/theme.mjs
CHANGED
|
@@ -168,10 +168,16 @@ import {
|
|
|
168
168
|
BLACK_CIRCLE,
|
|
169
169
|
RESULT_GUTTER_GLYPH,
|
|
170
170
|
RESULT_GUTTER_CONT_GLYPH,
|
|
171
|
+
RIGHT_ARROW,
|
|
172
|
+
LEFT_ARROW,
|
|
171
173
|
} from './figures.mjs';
|
|
172
174
|
|
|
173
175
|
/** Turn marker — BLACK_CIRCLE (`⏺` on macOS; `●` elsewhere). */
|
|
174
176
|
export const TURN_MARKER = BLACK_CIRCLE;
|
|
177
|
+
/** Agent call marker — `←` (request going out to a sub-agent). */
|
|
178
|
+
export const AGENT_CALL_MARKER = LEFT_ARROW;
|
|
179
|
+
/** Agent response marker — `→` (result coming back in to the transcript). */
|
|
180
|
+
export const AGENT_RESPONSE_MARKER = RIGHT_ARROW;
|
|
175
181
|
/** Result-tree gutter — `└` (U+2514) padded to a 2-col hanging indent. */
|
|
176
182
|
export const RESULT_GUTTER = ` ${RESULT_GUTTER_GLYPH} `;
|
|
177
183
|
/**
|
package/src/tui/themes/base.mjs
CHANGED
|
@@ -35,8 +35,8 @@ export const basePalette = {
|
|
|
35
35
|
// Picker/list focus row. This is intentionally stronger than text selection.
|
|
36
36
|
selectionText: 'rgb(230,238,255)', // readable on selectionBackground
|
|
37
37
|
selectionBackground: 'rgb(38,79,120)', // classic dark-mode selection blue
|
|
38
|
-
// Mouse/drag text-selection overlay
|
|
39
|
-
// it replaces the cell background while text remains copy-highlight readable.
|
|
38
|
+
// Mouse/drag text-selection overlay: a separate role from selectionBackground
|
|
39
|
+
// so it replaces the cell background while text remains copy-highlight readable.
|
|
40
40
|
selectionHighlightText: 'rgb(255,255,255)',
|
|
41
41
|
selectionHighlightBackground: 'rgb(38,79,120)',
|
|
42
42
|
inactive: 'rgb(132,139,152)', // light_grey
|
|
@@ -13,10 +13,10 @@ import { basePalette } from './base.mjs';
|
|
|
13
13
|
export const kanagawaPalette = {
|
|
14
14
|
...basePalette,
|
|
15
15
|
background: 'transparent', // sumiInk3 #1F1F28
|
|
16
|
-
claude: 'rgb(210,126,153)', // sakuraPink accent
|
|
16
|
+
claude: 'rgb(210,126,153)', // sakuraPink accent
|
|
17
17
|
logo: 'rgb(232,156,182)', // welcome banner — brighter than accent
|
|
18
18
|
claudeShimmer: 'rgb(156,171,202)', // springViolet2 #9CABCA (lighter accent)
|
|
19
|
-
mixdogOrange: 'rgb(210,126,153)', // sakuraPink accent
|
|
19
|
+
mixdogOrange: 'rgb(210,126,153)', // sakuraPink accent
|
|
20
20
|
mixdogAmber: 'rgb(156,171,202)', // lighter accent
|
|
21
21
|
mixdogIvory: 'rgb(220,215,186)', // fujiWhite #DCD7BA
|
|
22
22
|
spinnerGlyph: 'rgb(228,104,118)', // most-emphasized: waveRed
|
|
@@ -57,7 +57,7 @@ export const kanagawaPalette = {
|
|
|
57
57
|
mdLink: 'rgb(126,156,216)', // crystalBlue
|
|
58
58
|
mdLinkText: 'rgb(118,148,106)', // waveAqua #76946A
|
|
59
59
|
mdStrong: 'rgb(215,166,87)', // roninYellow #D7A657
|
|
60
|
-
mdEmph: 'rgb(195,141,157)', // carpYellow
|
|
60
|
+
mdEmph: 'rgb(195,141,157)', // carpYellow #C38D9D
|
|
61
61
|
mdDiffAdded: 'rgb(152,187,108)', // springGreen
|
|
62
62
|
mdDiffRemoved: 'rgb(255,93,98)', // peachRed
|
|
63
63
|
mdDiffHunk: 'rgb(45,79,103)', // waveBlue #2D4F67
|
|
@@ -71,7 +71,7 @@ export const kanagawaPalette = {
|
|
|
71
71
|
syntaxVariable: 'rgb(220,215,186)', // fujiWhite
|
|
72
72
|
syntaxString: 'rgb(152,187,108)', // springGreen
|
|
73
73
|
syntaxNumber: 'rgb(215,166,87)', // roninYellow #D7A657
|
|
74
|
-
syntaxType: 'rgb(195,141,157)', // carpYellow
|
|
74
|
+
syntaxType: 'rgb(195,141,157)', // carpYellow #C38D9D
|
|
75
75
|
syntaxOperator: 'rgb(210,126,153)', // sakuraPink
|
|
76
76
|
syntaxPunctuation: 'rgb(220,215,186)', // fg
|
|
77
77
|
userMessageBackground: 'rgb(42,42,55)', // sumiInk4 #2A2A37
|
package/src/tui/themes/teal.mjs
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/tui/themes/teal.mjs — Teal
|
|
2
|
+
* src/tui/themes/teal.mjs — Teal.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* taken verbatim: accent teal #8abeb7, body text #d4d4d4, page bg #18181e,
|
|
4
|
+
* Values: accent teal #8abeb7, body text #d4d4d4, page bg #18181e,
|
|
6
5
|
* green #b5bd68, red #cc6666, warning pure yellow #ffff00, blue #81a2be,
|
|
7
|
-
* mdHeading #f0c674, gray #808080. Syntax uses
|
|
6
|
+
* mdHeading #f0c674, gray #808080. Syntax uses a VS Code Dark+-style palette
|
|
8
7
|
* (keyword #569CD6, string #CE9178, function #DCDCAA, etc.). Spreads
|
|
9
8
|
* `basePalette` first so the full key set is always present.
|
|
10
9
|
*/
|
|
11
10
|
import { basePalette } from './base.mjs';
|
|
12
11
|
|
|
13
|
-
/** Teal —
|
|
12
|
+
/** Teal — teal accent with soft body text. */
|
|
14
13
|
export const tealPalette = {
|
|
15
14
|
...basePalette,
|
|
16
15
|
background: 'transparent', // export.pageBg #18181e
|
package/src/tui/themes/utils.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/tui/themes/utils.mjs — palette post-processing helpers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Design goals:
|
|
5
5
|
* - keep body text neutral and highly readable;
|
|
6
6
|
* - keep strong live-state colors for spinners/status;
|
|
7
7
|
* - use accent colors sparingly for headings, links and syntax, preferably
|