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
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import React, { useEffect, useLayoutEffect, useState, useRef } from 'react';
|
|
19
19
|
import { Box, Text, useInput, usePaste, useStdin } from 'ink';
|
|
20
|
-
import
|
|
20
|
+
import { displayWidth } from '../display-width.mjs';
|
|
21
21
|
import { theme, surfaceBackground } from '../theme.mjs';
|
|
22
22
|
import {
|
|
23
23
|
caretPosition,
|
|
@@ -38,14 +38,21 @@ import {
|
|
|
38
38
|
replaceSelection,
|
|
39
39
|
selectionRange,
|
|
40
40
|
verticalOffset,
|
|
41
|
+
wordRangeAt,
|
|
41
42
|
} from '../input-editing.mjs';
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
import {
|
|
44
|
+
hintStyle,
|
|
45
|
+
insertText,
|
|
46
|
+
normalizePastedText,
|
|
47
|
+
singleTrailingLineBreakPrefix,
|
|
48
|
+
draftStateEqual,
|
|
49
|
+
isModifiedEnterSequence,
|
|
50
|
+
isAnyModifiedEnterSequence,
|
|
51
|
+
} from './prompt-input/edit-helpers.mjs';
|
|
52
|
+
import {
|
|
53
|
+
VOICE_TICK_MS,
|
|
54
|
+
voiceIndicatorTextFor,
|
|
55
|
+
} from './prompt-input/voice-indicator.mjs';
|
|
49
56
|
|
|
50
57
|
// Windows Terminal IME composition can clip a glyph that starts exactly at the
|
|
51
58
|
// left edge of the editable text node. The rounded prompt box already adds a
|
|
@@ -56,11 +63,6 @@ const IME_LEFT_GUARD_COLUMNS = 0;
|
|
|
56
63
|
// needs to immediate-render). Matches transcript selection paint cadence.
|
|
57
64
|
const MOUSE_EXTEND_COALESCE_MS = 24;
|
|
58
65
|
|
|
59
|
-
function insertText(draft, input) {
|
|
60
|
-
if (!input) return draft;
|
|
61
|
-
return replaceSelection(draft, input);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
66
|
function renderSelectedText(displayValue, range, trailingSpace = false) {
|
|
65
67
|
if (!range) return trailingSpace ? `${displayValue} ` : displayValue;
|
|
66
68
|
const start = Math.max(0, Math.min(displayValue.length, range.start));
|
|
@@ -77,57 +79,6 @@ function renderSelectedText(displayValue, range, trailingSpace = false) {
|
|
|
77
79
|
);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
function normalizePastedText(text) {
|
|
81
|
-
return String(text ?? '').replace(/\r\n?/g, '\n');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function singleTrailingLineBreakPrefix(text) {
|
|
85
|
-
const normalized = normalizePastedText(text);
|
|
86
|
-
if (!normalized.endsWith('\n')) return null;
|
|
87
|
-
const prefix = normalized.slice(0, -1);
|
|
88
|
-
return prefix.includes('\n') ? null : prefix;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function draftStateEqual(a, b) {
|
|
92
|
-
return (
|
|
93
|
-
a.value === b.value
|
|
94
|
-
&& a.cursor === b.cursor
|
|
95
|
-
&& a.selectionAnchor === b.selectionAnchor
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Recognize a MODIFIED Enter delivered via the kitty keyboard protocol
|
|
100
|
-
// (\x1b[13;<mod>u) or modifyOtherKeys (\x1b[27;<mod>;13~). The xterm modifier
|
|
101
|
-
// param is (1 + bitmask) where the bitmask bits are shift=1, alt=2, ctrl=4. We
|
|
102
|
-
// treat Ctrl+Enter AND Shift+Enter (the two common "insert newline" chords) as a
|
|
103
|
-
// newline, so we match when the shift OR ctrl bit is set. Ctrl+J is handled
|
|
104
|
-
// separately as the protocol-independent fallback.
|
|
105
|
-
const MODIFIED_ENTER_SHIFT_OR_CTRL = 1 | 4;
|
|
106
|
-
function isModifiedEnterSequence(input) {
|
|
107
|
-
const text = String(input ?? '');
|
|
108
|
-
const body = text.startsWith('\x1b[') ? text.slice(2) : text.startsWith('[') ? text.slice(1) : '';
|
|
109
|
-
if (!body) return false;
|
|
110
|
-
const kitty = /^13;(\d+)(?::\d+)?(?:;[\d:]+)?u$/.exec(body);
|
|
111
|
-
if (kitty) return ((Number(kitty[1]) - 1) & MODIFIED_ENTER_SHIFT_OR_CTRL) !== 0;
|
|
112
|
-
const modifyOtherKeys = /^27;(\d+);13~$/.exec(body);
|
|
113
|
-
return Boolean(modifyOtherKeys && (((Number(modifyOtherKeys[1]) - 1) & MODIFIED_ENTER_SHIFT_OR_CTRL) !== 0));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Recognize ANY modified Enter (any modifier bitmask, e.g. Alt+Enter \x1b[13;3u
|
|
117
|
-
// / \x1b[27;3;13~). Used to CONSUME modified-Enter sequences we don't map to a
|
|
118
|
-
// newline (Alt-only, etc.) so they aren't typed into the prompt as raw CSI text
|
|
119
|
-
// under modifyOtherKeys. Plain Enter (mod param = 1, bitmask 0) is intentionally
|
|
120
|
-
// NOT matched, so it still submits.
|
|
121
|
-
function isAnyModifiedEnterSequence(input) {
|
|
122
|
-
const text = String(input ?? '');
|
|
123
|
-
const body = text.startsWith('\x1b[') ? text.slice(2) : text.startsWith('[') ? text.slice(1) : '';
|
|
124
|
-
if (!body) return false;
|
|
125
|
-
const kitty = /^13;(\d+)(?::\d+)?(?:;[\d:]+)?u$/.exec(body);
|
|
126
|
-
if (kitty) return (Number(kitty[1]) - 1) !== 0;
|
|
127
|
-
const modifyOtherKeys = /^27;(\d+);13~$/.exec(body);
|
|
128
|
-
return Boolean(modifyOtherKeys && ((Number(modifyOtherKeys[1]) - 1) !== 0));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
82
|
export function PromptInput({
|
|
132
83
|
onSubmit,
|
|
133
84
|
disabled = false,
|
|
@@ -150,10 +101,12 @@ export function PromptInput({
|
|
|
150
101
|
onHistoryNavigate,
|
|
151
102
|
onPasteText,
|
|
152
103
|
onVoiceToggle,
|
|
104
|
+
voiceIndicatorMode = 'off',
|
|
153
105
|
selectionRef,
|
|
154
106
|
valueRef,
|
|
155
107
|
boxRectRef,
|
|
156
108
|
mouseSelectionRef,
|
|
109
|
+
suppressShiftNavRef,
|
|
157
110
|
}) {
|
|
158
111
|
const [draft, setDraft] = useState(() => {
|
|
159
112
|
const value = String(initialValue || '');
|
|
@@ -171,8 +124,32 @@ export function PromptInput({
|
|
|
171
124
|
const boxRef = useRef(null);
|
|
172
125
|
const cursorEnabledRef = useRef(false); // latest enabled state, read by the anchor fn at render time
|
|
173
126
|
const contentWidthRef = useRef(80);
|
|
127
|
+
// Cells reserved on the right for the voice indicator glyph(s) (0 when
|
|
128
|
+
// hidden, 1 idle/spinner-only, 2 recording's "◉"+braille). Read by the
|
|
129
|
+
// cursor-anchor closure below (installed ONCE, so it must read live state
|
|
130
|
+
// via a ref rather than close over a render-local variable) to keep the
|
|
131
|
+
// caret/wrap math in sync with the actual reserved column, same pattern as
|
|
132
|
+
// IME_LEFT_GUARD_COLUMNS on the left edge.
|
|
133
|
+
const voiceIndicatorWidthRef = useRef(0);
|
|
174
134
|
const preferredColumnRef = useRef(null);
|
|
175
135
|
const mouseExtendCoalesceRef = useRef({ pendingNext: null, timer: null, t: 0 });
|
|
136
|
+
// Voice indicator animation tick. Runs a single ~120ms interval ONLY while
|
|
137
|
+
// an ANIMATED mode is active (installing/recording/transcribing) — 'idle'
|
|
138
|
+
// renders a static glyph and 'off' renders nothing, so neither keeps a
|
|
139
|
+
// timer alive (the old idle "breathing" pulse spun constantly whenever
|
|
140
|
+
// voice was merely enabled, reading as phantom activity).
|
|
141
|
+
const [voiceTick, setVoiceTick] = useState(0);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (voiceIndicatorMode === 'off' || voiceIndicatorMode === 'idle') return undefined;
|
|
144
|
+
const timer = setInterval(() => setVoiceTick((t) => (t + 1) % 1000000), VOICE_TICK_MS);
|
|
145
|
+
timer.unref?.();
|
|
146
|
+
return () => clearInterval(timer);
|
|
147
|
+
}, [voiceIndicatorMode]);
|
|
148
|
+
// Undo/redo snapshot stack. Each entry is a { value, cursor, selectionAnchor }
|
|
149
|
+
// draft snapshot. Continuous typing coalesces (see UNDO_COALESCE_MS) so a run
|
|
150
|
+
// of characters collapses into one undo step; cursor-only moves are NOT
|
|
151
|
+
// snapshotted. Reset on submit / draftOverride.
|
|
152
|
+
const undoRef = useRef({ past: [], future: [], lastPushAt: 0, lastValue: null });
|
|
176
153
|
const { value, cursor } = draft;
|
|
177
154
|
draftRef.current = draft;
|
|
178
155
|
if (selectionRef) {
|
|
@@ -204,6 +181,7 @@ export function PromptInput({
|
|
|
204
181
|
const sameDraft = draftStateEqual(draftRef.current, next);
|
|
205
182
|
if (!options.keepPreferredColumn) preferredColumnRef.current = null;
|
|
206
183
|
if (sameDraft) return;
|
|
184
|
+
if (!options.skipHistory) recordUndoSnapshot(draftRef.current, next, options);
|
|
207
185
|
draftRef.current = next;
|
|
208
186
|
setDraft(next);
|
|
209
187
|
queueMicrotask(flushImmediate);
|
|
@@ -248,11 +226,20 @@ export function PromptInput({
|
|
|
248
226
|
const d = draftRef.current;
|
|
249
227
|
const w = yogaNode?.getComputedWidth?.() ?? 0;
|
|
250
228
|
const guardColumns = w > IME_LEFT_GUARD_COLUMNS ? IME_LEFT_GUARD_COLUMNS : 0;
|
|
251
|
-
|
|
229
|
+
// Reserve the voice-indicator's own cells (+1 gap column when visible)
|
|
230
|
+
// out of the SAME width the caret/wrap math uses, mirroring the left
|
|
231
|
+
// guard above — otherwise a visible indicator would silently overlap
|
|
232
|
+
// the caret/typed text at the right edge instead of pushing content in.
|
|
233
|
+
const voiceReserve = voiceIndicatorWidthRef.current > 0 ? voiceIndicatorWidthRef.current + 1 : 0;
|
|
234
|
+
const contentWidth = Math.max(1, (w ? w - guardColumns - voiceReserve : contentWidthRef.current) || 80);
|
|
252
235
|
contentWidthRef.current = contentWidth;
|
|
253
236
|
const caret = w > 0
|
|
254
|
-
|
|
255
|
-
|
|
237
|
+
// PromptInput renders a trailing space cell when the cursor is at
|
|
238
|
+
// end-of-input, so a caret flush on the last column there still has a
|
|
239
|
+
// following cell — pass hasTrailingContent=true so it rolls to row N+1
|
|
240
|
+
// exactly as ink wraps the trailing space.
|
|
241
|
+
? caretPosition(d.value, d.cursor, contentWidth, d.cursor >= d.value.length ? true : undefined)
|
|
242
|
+
: { row: 0, col: displayWidth(d.value.slice(0, d.cursor)) };
|
|
256
243
|
return w > 0
|
|
257
244
|
? { ...caret, col: caret.col + guardColumns }
|
|
258
245
|
: caret;
|
|
@@ -264,6 +251,70 @@ export function PromptInput({
|
|
|
264
251
|
commitDraft(fn(draftRef.current), options);
|
|
265
252
|
};
|
|
266
253
|
|
|
254
|
+
// --- undo/redo -----------------------------------------------------------
|
|
255
|
+
// Continuous-typing coalesce window: successive value-changing edits within
|
|
256
|
+
// this window collapse into a single undo step.
|
|
257
|
+
const UNDO_COALESCE_MS = 500;
|
|
258
|
+
const UNDO_MAX = 100;
|
|
259
|
+
|
|
260
|
+
const snapshotOf = (d) => ({ value: d.value, cursor: d.cursor, selectionAnchor: d.selectionAnchor ?? null });
|
|
261
|
+
|
|
262
|
+
const resetUndo = () => {
|
|
263
|
+
undoRef.current = { past: [], future: [], lastPushAt: 0, lastValue: null };
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Record a snapshot of the PREVIOUS state before applying `next`. Cursor-only
|
|
267
|
+
// moves (value unchanged) are never snapshotted. Consecutive value edits
|
|
268
|
+
// within UNDO_COALESCE_MS coalesce (we keep only the first snapshot of the
|
|
269
|
+
// run, so a single undo reverts the whole burst).
|
|
270
|
+
const recordUndoSnapshot = (prev, next, options = {}) => {
|
|
271
|
+
const stack = undoRef.current;
|
|
272
|
+
if (prev.value === next.value) {
|
|
273
|
+
// Cursor/selection-only move: don't snapshot, but BREAK the coalesce run
|
|
274
|
+
// so a following edit starts a fresh undo step (typing→move→typing must
|
|
275
|
+
// not collapse into one undo).
|
|
276
|
+
stack.lastPushAt = 0;
|
|
277
|
+
stack.lastValue = next.value;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const now = Date.now();
|
|
281
|
+
const coalesce = !options.undoBreak
|
|
282
|
+
&& stack.past.length > 0
|
|
283
|
+
&& (now - stack.lastPushAt) < UNDO_COALESCE_MS
|
|
284
|
+
&& stack.lastValue === prev.value;
|
|
285
|
+
if (!coalesce) {
|
|
286
|
+
stack.past.push(snapshotOf(prev));
|
|
287
|
+
if (stack.past.length > UNDO_MAX) stack.past.shift();
|
|
288
|
+
}
|
|
289
|
+
stack.lastPushAt = now;
|
|
290
|
+
stack.lastValue = next.value;
|
|
291
|
+
stack.future = [];
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const performUndo = () => {
|
|
295
|
+
const stack = undoRef.current;
|
|
296
|
+
if (stack.past.length === 0) return false;
|
|
297
|
+
const prev = stack.past.pop();
|
|
298
|
+
stack.future.push(snapshotOf(draftRef.current));
|
|
299
|
+
stack.lastPushAt = 0;
|
|
300
|
+
stack.lastValue = prev.value;
|
|
301
|
+
commitDraft(prev, { skipHistory: true });
|
|
302
|
+
return true;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const performRedo = () => {
|
|
306
|
+
const stack = undoRef.current;
|
|
307
|
+
if (stack.future.length === 0) return false;
|
|
308
|
+
const next = stack.future.pop();
|
|
309
|
+
stack.past.push(snapshotOf(draftRef.current));
|
|
310
|
+
if (stack.past.length > UNDO_MAX) stack.past.shift();
|
|
311
|
+
stack.lastPushAt = 0;
|
|
312
|
+
stack.lastValue = next.value;
|
|
313
|
+
commitDraft(next, { skipHistory: true });
|
|
314
|
+
return true;
|
|
315
|
+
};
|
|
316
|
+
// -------------------------------------------------------------------------
|
|
317
|
+
|
|
267
318
|
const cancelMouseExtendCoalesce = () => {
|
|
268
319
|
const state = mouseExtendCoalesceRef.current;
|
|
269
320
|
if (state.timer) {
|
|
@@ -332,6 +383,28 @@ export function PromptInput({
|
|
|
332
383
|
const anchor = Number.isFinite(d.selectionAnchor) ? d.selectionAnchor : d.cursor;
|
|
333
384
|
queueMouseExtendCommit({ ...d, cursor: off, selectionAnchor: anchor }, immediate);
|
|
334
385
|
},
|
|
386
|
+
hasSelection: () => selectionRange(draftRef.current) != null,
|
|
387
|
+
// Double-click word select: pick the word/punctuation run under the
|
|
388
|
+
// clicked offset and set selectionAnchor/cursor to its bounds so it
|
|
389
|
+
// paints via the normal selection highlight. Triple-click line select
|
|
390
|
+
// uses lineStart/lineEnd instead (see selectLineAt).
|
|
391
|
+
selectWordAt: (offset) => {
|
|
392
|
+
cancelMouseExtendCoalesce();
|
|
393
|
+
const value = draftRef.current.value;
|
|
394
|
+
const off = Math.max(0, Math.min(value.length, Math.floor(Number(offset) || 0)));
|
|
395
|
+
const { start, end } = wordRangeAt(value, off);
|
|
396
|
+
commitDraft({ ...draftRef.current, cursor: end, selectionAnchor: start });
|
|
397
|
+
mouseExtendCoalesceRef.current.t = Date.now();
|
|
398
|
+
},
|
|
399
|
+
selectLineAt: (offset) => {
|
|
400
|
+
cancelMouseExtendCoalesce();
|
|
401
|
+
const value = draftRef.current.value;
|
|
402
|
+
const off = Math.max(0, Math.min(value.length, Math.floor(Number(offset) || 0)));
|
|
403
|
+
const start = lineStart(value, off);
|
|
404
|
+
const end = lineEnd(value, off);
|
|
405
|
+
commitDraft({ ...draftRef.current, cursor: end, selectionAnchor: start });
|
|
406
|
+
mouseExtendCoalesceRef.current.t = Date.now();
|
|
407
|
+
},
|
|
335
408
|
clear: () => {
|
|
336
409
|
cancelMouseExtendCoalesce();
|
|
337
410
|
if (selectionRange(draftRef.current)) commitDraft(clearSelection(draftRef.current));
|
|
@@ -405,7 +478,8 @@ export function PromptInput({
|
|
|
405
478
|
|
|
406
479
|
useEffect(() => {
|
|
407
480
|
if (!draftOverride || typeof draftOverride.value !== 'string') return;
|
|
408
|
-
commitDraft({ value: draftOverride.value, cursor: draftOverride.value.length, selectionAnchor: null });
|
|
481
|
+
commitDraft({ value: draftOverride.value, cursor: draftOverride.value.length, selectionAnchor: null }, { skipHistory: true });
|
|
482
|
+
resetUndo();
|
|
409
483
|
}, [draftOverride?.id]);
|
|
410
484
|
|
|
411
485
|
useEffect(() => () => {
|
|
@@ -419,7 +493,8 @@ export function PromptInput({
|
|
|
419
493
|
commitDraft(next);
|
|
420
494
|
return;
|
|
421
495
|
}
|
|
422
|
-
commitDraft({ value: '', cursor: 0, selectionAnchor: null });
|
|
496
|
+
commitDraft({ value: '', cursor: 0, selectionAnchor: null }, { skipHistory: true });
|
|
497
|
+
resetUndo();
|
|
423
498
|
};
|
|
424
499
|
|
|
425
500
|
const submitEnterChunk = (prefix = '') => {
|
|
@@ -450,6 +525,21 @@ export function PromptInput({
|
|
|
450
525
|
const rawInput = String(input ?? '');
|
|
451
526
|
const inputKey = rawInput.toLowerCase();
|
|
452
527
|
|
|
528
|
+
// App owns Shift+Arrow when a transcript/status ink-grid selection is live.
|
|
529
|
+
// Because the parent (App) useInput handler fires AFTER this child handler
|
|
530
|
+
// for the same event, a flag SET in App's handler is always one event stale.
|
|
531
|
+
// Instead call a synchronous predicate derived from dragRef at event time.
|
|
532
|
+
const gridSelectionActive = typeof suppressShiftNavRef === 'function'
|
|
533
|
+
? suppressShiftNavRef()
|
|
534
|
+
: (typeof suppressShiftNavRef?.current === 'function'
|
|
535
|
+
? suppressShiftNavRef.current()
|
|
536
|
+
: Boolean(suppressShiftNavRef?.current));
|
|
537
|
+
if (gridSelectionActive) {
|
|
538
|
+
const isShiftArrow = key.shift
|
|
539
|
+
&& (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow || key.home || key.end);
|
|
540
|
+
if (isShiftArrow) return;
|
|
541
|
+
}
|
|
542
|
+
|
|
453
543
|
// Drop SGR mouse-tracking sequences (wheel/click). When app mouse tracking
|
|
454
544
|
// is explicitly enabled, App parses these off raw stdin itself;
|
|
455
545
|
// ink still forwards the bytes here as "input", which would otherwise type
|
|
@@ -482,7 +572,22 @@ export function PromptInput({
|
|
|
482
572
|
const rawShiftDown = rawInput === '\x1b[1;2B' || rawInput === '\x1b[b' || rawInput === '[1;2B';
|
|
483
573
|
const rawShiftRight = rawInput === '\x1b[1;2C' || rawInput === '\x1b[c' || rawInput === '[1;2C';
|
|
484
574
|
const rawShiftLeft = rawInput === '\x1b[1;2D' || rawInput === '\x1b[d' || rawInput === '[1;2D';
|
|
485
|
-
|
|
575
|
+
// Ctrl+Shift+Arrow modifier sequences: xterm mod=6 (1 + shift(1) + ctrl(4))
|
|
576
|
+
// arrives as `\x1b[1;6<dir>`; kitty keyboard protocol reports the same chord
|
|
577
|
+
// as `\x1b[<code>;6<dir>` (also mod=6). Ink decodes neither the `;6` for
|
|
578
|
+
// arrows, so the bytes arrive raw. Fold into a ctrlShiftHeld signal used to
|
|
579
|
+
// drive whole-word selection-extend below. `\x1b[1;6<dir>` covers both the
|
|
580
|
+
// classic xterm form and kitty's default (which emits the legacy arrow form
|
|
581
|
+
// with the CSI-u modifier param for arrow keys).
|
|
582
|
+
const rawCtrlShiftUp = rawInput === '\x1b[1;6A' || rawInput === '[1;6A';
|
|
583
|
+
const rawCtrlShiftDown = rawInput === '\x1b[1;6B' || rawInput === '[1;6B';
|
|
584
|
+
const rawCtrlShiftRight = rawInput === '\x1b[1;6C' || rawInput === '[1;6C';
|
|
585
|
+
const rawCtrlShiftLeft = rawInput === '\x1b[1;6D' || rawInput === '[1;6D';
|
|
586
|
+
const ctrlShiftHeld =
|
|
587
|
+
rawCtrlShiftUp || rawCtrlShiftDown || rawCtrlShiftLeft || rawCtrlShiftRight
|
|
588
|
+
|| (key.shift && key.ctrl && (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow));
|
|
589
|
+
const shiftHeld = key.shift || rawShiftUp || rawShiftDown || rawShiftLeft || rawShiftRight
|
|
590
|
+
|| rawCtrlShiftUp || rawCtrlShiftDown || rawCtrlShiftLeft || rawCtrlShiftRight;
|
|
486
591
|
const lineBreakIndex = rawInput.search(/[\r\n]/);
|
|
487
592
|
const rawEnter = rawInput === '\r' || rawInput === '\n' || rawInput === '\r\n';
|
|
488
593
|
const trailingEnterPrefix = singleTrailingLineBreakPrefix(rawInput);
|
|
@@ -566,25 +671,58 @@ export function PromptInput({
|
|
|
566
671
|
return;
|
|
567
672
|
}
|
|
568
673
|
|
|
569
|
-
|
|
674
|
+
// Ctrl+Shift+Left/Right → extend selection whole-word. Kept before the plain
|
|
675
|
+
// shift-arrow branches so the ctrl+shift chord never falls through to a
|
|
676
|
+
// char-wise extend. (Up/Down ctrl+shift extend to line-relative vertical
|
|
677
|
+
// move with extend — same as shift alone; handled in the vertical branch.)
|
|
678
|
+
if (ctrlShiftHeld && (rawCtrlShiftLeft || (key.ctrl && key.shift && key.leftArrow))) {
|
|
679
|
+
if (!commandPaletteActive) {
|
|
680
|
+
updateDraft((d) => moveCursor(d, previousWordOffset(d.value, d.cursor), { extend: true }));
|
|
681
|
+
}
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (ctrlShiftHeld && (rawCtrlShiftRight || (key.ctrl && key.shift && key.rightArrow))) {
|
|
685
|
+
if (!commandPaletteActive) {
|
|
686
|
+
updateDraft((d) => moveCursor(d, nextWordOffset(d.value, d.cursor), { extend: true }));
|
|
687
|
+
}
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (key.upArrow || rawUpArrow || rawShiftUp || rawCtrlShiftUp) {
|
|
570
692
|
if (commandPaletteActive) {
|
|
571
693
|
onCommandPaletteNavigate?.(-1);
|
|
572
694
|
} else {
|
|
573
|
-
|
|
574
|
-
if
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
695
|
+
// A Shift-held Up is a SELECTION gesture, never history navigation:
|
|
696
|
+
// extend the selection up one visual line, and if already on the first
|
|
697
|
+
// line extend all the way to document start (offset 0). History
|
|
698
|
+
// navigation (restoreQueued / applyHistoryNavigation) MUST NOT fire.
|
|
699
|
+
if (shiftHeld) {
|
|
700
|
+
if (!moveDraftVertically(-1, { extend: true })) {
|
|
701
|
+
updateDraft((d) => moveCursor(d, 0, { extend: true }));
|
|
702
|
+
}
|
|
703
|
+
} else {
|
|
704
|
+
const hasDraftText = String(draftRef.current.value || '').trim().length > 0;
|
|
705
|
+
if (!hasDraftText) {
|
|
706
|
+
if (!restoreQueuedToDraft()) applyHistoryNavigation('up', { emptyDraft: true });
|
|
707
|
+
} else if (!moveDraftVertically(-1, { extend: false })) {
|
|
708
|
+
applyHistoryNavigation('up', { emptyDraft: false });
|
|
709
|
+
}
|
|
578
710
|
}
|
|
579
711
|
}
|
|
580
712
|
return;
|
|
581
713
|
}
|
|
582
714
|
|
|
583
|
-
if (key.downArrow || rawDownArrow || rawShiftDown) {
|
|
715
|
+
if (key.downArrow || rawDownArrow || rawShiftDown || rawCtrlShiftDown) {
|
|
584
716
|
if (commandPaletteActive) {
|
|
585
717
|
onCommandPaletteNavigate?.(1);
|
|
586
718
|
} else {
|
|
587
|
-
|
|
719
|
+
// Shift-held Down: extend selection down one line, or to document end
|
|
720
|
+
// (value.length) when already on the last line. Never history nav.
|
|
721
|
+
if (shiftHeld) {
|
|
722
|
+
if (!moveDraftVertically(1, { extend: true })) {
|
|
723
|
+
updateDraft((d) => moveCursor(d, d.value.length, { extend: true }));
|
|
724
|
+
}
|
|
725
|
+
} else if (!moveDraftVertically(1, { extend: false })) {
|
|
588
726
|
applyHistoryNavigation('down', { emptyDraft: String(draftRef.current.value || '').trim().length === 0 });
|
|
589
727
|
}
|
|
590
728
|
}
|
|
@@ -641,8 +779,8 @@ export function PromptInput({
|
|
|
641
779
|
commitDraft({ value: '', cursor: 0, selectionAnchor: null });
|
|
642
780
|
return;
|
|
643
781
|
}
|
|
644
|
-
// Active turn takes precedence over queue restore
|
|
645
|
-
//
|
|
782
|
+
// Active turn takes precedence over queue restore: Esc during a
|
|
783
|
+
// running turn interrupts the
|
|
646
784
|
// turn and leaves queued steering prompts intact to run afterward. Queued
|
|
647
785
|
// messages only pop back into the draft when the turn is idle.
|
|
648
786
|
if (interruptActive) {
|
|
@@ -702,6 +840,27 @@ export function PromptInput({
|
|
|
702
840
|
|
|
703
841
|
const editingKey = String(input || '').toLowerCase();
|
|
704
842
|
|
|
843
|
+
// Undo / redo. Covered encodings:
|
|
844
|
+
// • kitty protocol: ctrl+z → input 'z' + key.ctrl; ctrl+y → 'y' + key.ctrl;
|
|
845
|
+
// ctrl+shift+z → 'z' + key.ctrl + key.shift (redo).
|
|
846
|
+
// • legacy control bytes: Ctrl+Z is \x1a (SUB, 0x1A), Ctrl+Y is \x19 (EM,
|
|
847
|
+
// 0x19). ink may deliver these as raw input without key.ctrl on some
|
|
848
|
+
// terminals, so match the byte directly too.
|
|
849
|
+
const isCtrlZ = (key.ctrl && editingKey === 'z') || rawInput === '\x1a';
|
|
850
|
+
const isCtrlY = (key.ctrl && editingKey === 'y') || rawInput === '\x19';
|
|
851
|
+
if (isCtrlZ && (key.shift || shiftHeld)) {
|
|
852
|
+
performRedo();
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (isCtrlZ) {
|
|
856
|
+
performUndo();
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (isCtrlY) {
|
|
860
|
+
performRedo();
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
705
864
|
// ctrl+a selects all like a normal text box; ctrl+e keeps readline line-end.
|
|
706
865
|
if (key.ctrl && editingKey === 'a') {
|
|
707
866
|
updateDraft((d) => (d.value ? { ...d, cursor: d.value.length, selectionAnchor: 0 } : clearSelection(d)));
|
|
@@ -834,6 +993,15 @@ export function PromptInput({
|
|
|
834
993
|
const displayValue = mask ? value.replace(/[^\n]/g, '*') : value;
|
|
835
994
|
const renderedValue = renderSelectedText(displayValue, selectionRange(draft), cursor === value.length);
|
|
836
995
|
const hintMeta = hintStyle(hintTone);
|
|
996
|
+
// Monotone voice indicator glyph text — zero-width (rendered as null) when
|
|
997
|
+
// mode is 'off'. Braille frame drives installing/recording/transcribing;
|
|
998
|
+
// 'idle' is a static ○ (no animation, no timer).
|
|
999
|
+
const voiceIndicatorText = voiceIndicatorTextFor(voiceIndicatorMode, voiceTick);
|
|
1000
|
+
// stringWidth is unnecessary here — every glyph above is a single
|
|
1001
|
+
// 1-column codepoint (○ ● and the braille frames), and 'recording'
|
|
1002
|
+
// concatenates exactly two 1-cell glyphs, so plain .length gives the
|
|
1003
|
+
// correct cell count without pulling in emoji-width special-casing.
|
|
1004
|
+
voiceIndicatorWidthRef.current = voiceIndicatorText.length;
|
|
837
1005
|
|
|
838
1006
|
return (
|
|
839
1007
|
<Box ref={boxRef} flexDirection="row" width="100%" flexGrow={1} flexShrink={1} backgroundColor={surfaceBackground()}>
|
|
@@ -844,6 +1012,17 @@ export function PromptInput({
|
|
|
844
1012
|
<Text color={hintMeta.textColor}>{hint}</Text>
|
|
845
1013
|
</Box>
|
|
846
1014
|
) : null}
|
|
1015
|
+
{voiceIndicatorText ? (
|
|
1016
|
+
<>
|
|
1017
|
+
{/* flexGrow spacer pins the indicator to the RIGHT edge of the row
|
|
1018
|
+
even when the typed text is short; without it the glyph would
|
|
1019
|
+
ride directly after the caret instead of the box edge. */}
|
|
1020
|
+
<Box flexGrow={1} backgroundColor={surfaceBackground()} />
|
|
1021
|
+
<Box flexShrink={0} width={voiceIndicatorText.length + 1} justifyContent="flex-end" backgroundColor={surfaceBackground()}>
|
|
1022
|
+
<Text color={theme.subtle}>{voiceIndicatorText}</Text>
|
|
1023
|
+
</Box>
|
|
1024
|
+
</>
|
|
1025
|
+
) : null}
|
|
847
1026
|
</Box>
|
|
848
1027
|
);
|
|
849
1028
|
}
|
|
@@ -60,7 +60,11 @@ export function SlashCommandPalette({ commands, selectedIndex = 0, title = 'Comm
|
|
|
60
60
|
start = Math.max(0, end - MAX_VISIBLE);
|
|
61
61
|
}
|
|
62
62
|
const visible = commands.slice(start, end);
|
|
63
|
-
|
|
63
|
+
// No-match frames keep the palette mounted at the SAME fixed height so the
|
|
64
|
+
// slash session never unmounts/remounts the 14-row panel per keystroke
|
|
65
|
+
// (Windows fullscreen repaint made that oscillation a hard flicker).
|
|
66
|
+
const noMatches = total === 0;
|
|
67
|
+
const blankRows = Math.max(0, MAX_VISIBLE - visible.length - (noMatches ? 1 : 0));
|
|
64
68
|
const labelWidth = Math.max(12, Math.min(COMMAND_LABEL_WIDTH, Math.max(12, Math.floor(columns * 0.45))));
|
|
65
69
|
const descriptionWidth = Math.max(0, columns - labelWidth - 12);
|
|
66
70
|
// Standard panel rhythm: title row, blank, description/hint row, blank, content.
|
|
@@ -84,6 +88,9 @@ export function SlashCommandPalette({ commands, selectedIndex = 0, title = 'Comm
|
|
|
84
88
|
<Text> </Text>
|
|
85
89
|
<Text color={theme.subtle}>{description || ' '}</Text>
|
|
86
90
|
<Text> </Text>
|
|
91
|
+
{noMatches ? (
|
|
92
|
+
<Text color={theme.subtle} wrap="truncate">No matching commands</Text>
|
|
93
|
+
) : null}
|
|
87
94
|
{visible.map((item, index) => (
|
|
88
95
|
<CommandRow
|
|
89
96
|
key={item.name}
|
|
@@ -482,6 +482,29 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
482
482
|
Number(activeTools.search?.startedAt) || 0,
|
|
483
483
|
].join('|')
|
|
484
484
|
: '';
|
|
485
|
+
// Stable primitive signatures so the render effect's dep array doesn't
|
|
486
|
+
// churn on new array/object identities carrying unchanged content — a new
|
|
487
|
+
// `agentWorkers`/`agentJobs` array (or `stats` object) reference on every
|
|
488
|
+
// render was clearing the pending 150ms debounce timer before it could
|
|
489
|
+
// fire, starving the boot full-render. Only tag|stage per worker/job and
|
|
490
|
+
// the numeric stats fields actually consumed here are covered.
|
|
491
|
+
const agentWorkersSignature = Array.isArray(agentWorkers)
|
|
492
|
+
? agentWorkers.map((w) => `${w?.tag || w?.agent || w?.name || ''}|${w?.stage || w?.status || ''}|${localTimeMs(w?.startedAt || w?.startTime || w?.createdAt)}`).join(',')
|
|
493
|
+
: '';
|
|
494
|
+
const agentJobsSignature = Array.isArray(agentJobs)
|
|
495
|
+
? agentJobs.map((j) => `${j?.tag || j?.agent || j?.name || ''}|${j?.status || j?.stage || ''}|${localTimeMs(j?.startedAt || j?.startTime || j?.createdAt)}`).join(',')
|
|
496
|
+
: '';
|
|
497
|
+
const statsForSignature = stats && typeof stats === 'object' ? stats : {};
|
|
498
|
+
const statsSignature = [
|
|
499
|
+
localNum(statsForSignature.currentContextTokens),
|
|
500
|
+
localNum(statsForSignature.currentEstimatedContextTokens),
|
|
501
|
+
localNum(statsForSignature.inputTokens),
|
|
502
|
+
localNum(statsForSignature.latestInputTokens),
|
|
503
|
+
localNum(statsForSignature.promptTokens),
|
|
504
|
+
localNum(statsForSignature.turns),
|
|
505
|
+
localNum(statsForSignature.contextTokens),
|
|
506
|
+
String(statsForSignature.currentContextSource || ''),
|
|
507
|
+
].join('|');
|
|
485
508
|
const refreshMs = hasActiveStatuslineWork(line, agentWorkers, agentJobs, activeTools) ? STATUSLINE_ACTIVE_REFRESH_MS : STATUSLINE_REFRESH_MS;
|
|
486
509
|
|
|
487
510
|
useEffect(() => {
|
|
@@ -498,6 +521,7 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
498
521
|
|
|
499
522
|
useEffect(() => {
|
|
500
523
|
let alive = true;
|
|
524
|
+
let bootRetryTimer = null;
|
|
501
525
|
const effectId = renderEffectIdRef.current + 1;
|
|
502
526
|
renderEffectIdRef.current = effectId;
|
|
503
527
|
const isCurrentEffect = () => alive && renderEffectIdRef.current === effectId;
|
|
@@ -508,23 +532,27 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
508
532
|
lastImmediateArgsRef.current,
|
|
509
533
|
);
|
|
510
534
|
// ROUTE identity = the subset that actually changes the async full line's L1
|
|
511
|
-
// usage/quota segment (provider/model/session/effort/fast
|
|
512
|
-
//
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
//
|
|
535
|
+
// usage/quota segment (provider/model/session/effort/fast). agentRevision,
|
|
536
|
+
// compactBoundaryTokens, autoCompactTokenLimit, stats-reset AND the context
|
|
537
|
+
// window fields (contextWindow/displayContextWindow/rawContextWindow) are
|
|
538
|
+
// NON-ROUTE: the first prompt's API response publishes displayContextWindow
|
|
539
|
+
// 0→N (engine syncContextDisplayFields), which only moves the ctx% bar —
|
|
540
|
+
// never the `5H …/7D …` usage windows. Treating it as route wiped the cached
|
|
541
|
+
// full line and snapped to the usage-less local line for one frame (the
|
|
542
|
+
// "usage blinks once on first prompt" bug). A real route switch that changes
|
|
543
|
+
// usage windows always changes provider/model/session anyway. Only a true
|
|
544
|
+
// route/session switch may wipe the cached full line; non-route churn keeps
|
|
545
|
+
// the last good full line (identityChanged still snaps, but via the cached
|
|
546
|
+
// graft path below, so usage survives and the async render refreshes ctx%
|
|
547
|
+
// within ~150ms). (lastImmediateArgsRef holds these fields, captured at the
|
|
548
|
+
// end of the previous effect run.)
|
|
518
549
|
const prevImmediate = lastImmediateArgsRef.current;
|
|
519
550
|
const routeChanged = !prevImmediate
|
|
520
551
|
|| prevImmediate.sessionId !== args.sessionId
|
|
521
552
|
|| prevImmediate.provider !== args.provider
|
|
522
553
|
|| prevImmediate.model !== args.model
|
|
523
554
|
|| prevImmediate.effort !== args.effort
|
|
524
|
-
|| prevImmediate.fast !== args.fast
|
|
525
|
-
|| prevImmediate.contextWindow !== args.contextWindow
|
|
526
|
-
|| prevImmediate.displayContextWindow !== args.displayContextWindow
|
|
527
|
-
|| prevImmediate.rawContextWindow !== args.rawContextWindow;
|
|
555
|
+
|| prevImmediate.fast !== args.fast;
|
|
528
556
|
// A theme switch must re-tone the footer immediately: the stored `line`
|
|
529
557
|
// holds already-normalized ANSI with the OLD palette, so re-running
|
|
530
558
|
// normalizeStatusLine on it is a no-op. Force a fresh local rebuild (new
|
|
@@ -607,9 +635,31 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
607
635
|
const timer = setTimeout(() => {
|
|
608
636
|
if (bootFullDoneRef.current !== true) {
|
|
609
637
|
if (!bootFullRenderEligible(mountAtRef.current, lineRef.current, args.agentWorkers, args.agentJobs, args.activeTools)) {
|
|
638
|
+
// Not eligible yet (boot delay not elapsed). Don't just bail and wait
|
|
639
|
+
// for the next refreshTick interval (250ms/2000ms) — arm a follow-up
|
|
640
|
+
// timeout for the REMAINING boot delay so the first full render still
|
|
641
|
+
// fires on schedule instead of stalling/flickering until the next tick.
|
|
642
|
+
const active = hasActiveStatuslineWork(lineRef.current, args.agentWorkers, args.agentJobs, args.activeTools);
|
|
643
|
+
const bootDelay = active ? STATUSLINE_BOOT_FULL_DELAY_ACTIVE_MS : STATUSLINE_BOOT_FULL_DELAY_MS;
|
|
644
|
+
const elapsed = Date.now() - mountAtRef.current;
|
|
645
|
+
const remaining = Math.max(150, bootDelay - elapsed);
|
|
646
|
+
bootRetryTimer = setTimeout(() => {
|
|
647
|
+
if (!isCurrentEffect()) return;
|
|
648
|
+
setRefreshTick((tick) => (tick + 1) % 1_000_000);
|
|
649
|
+
}, remaining);
|
|
650
|
+
bootRetryTimer.unref?.();
|
|
610
651
|
return;
|
|
611
652
|
}
|
|
612
653
|
if (!canAttemptBootFullRender(bootFullNextAttemptAtRef.current)) {
|
|
654
|
+
// Backoff window not elapsed yet — arm a follow-up timeout for exactly
|
|
655
|
+
// the remaining backoff so the retry fires on time instead of waiting
|
|
656
|
+
// for the next refreshTick interval.
|
|
657
|
+
const remaining = Math.max(150, bootFullNextAttemptAtRef.current - Date.now());
|
|
658
|
+
bootRetryTimer = setTimeout(() => {
|
|
659
|
+
if (!isCurrentEffect()) return;
|
|
660
|
+
setRefreshTick((tick) => (tick + 1) % 1_000_000);
|
|
661
|
+
}, remaining);
|
|
662
|
+
bootRetryTimer.unref?.();
|
|
613
663
|
return;
|
|
614
664
|
}
|
|
615
665
|
}
|
|
@@ -644,8 +694,9 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
644
694
|
return () => {
|
|
645
695
|
alive = false;
|
|
646
696
|
clearTimeout(timer);
|
|
697
|
+
clearTimeout(bootRetryTimer);
|
|
647
698
|
};
|
|
648
|
-
}, [sessionId, clientHostPid, provider, model, effort, fast, cwd,
|
|
699
|
+
}, [sessionId, clientHostPid, provider, model, effort, fast, cwd, statsSignature, contextWindow, displayContextWindow, compactBoundaryTokens, autoCompactTokenLimit, rawContextWindow, resizeEpoch, agentRevision, agentWorkersSignature, agentJobsSignature, activeToolsSignature, refreshTick, themeEpoch]);
|
|
649
700
|
|
|
650
701
|
const lines = line ? line.split('\n').slice(0, 2) : [' ', ' '];
|
|
651
702
|
const workflowLabel = workflowModeLabel(workflow, remoteEnabled);
|
|
@@ -159,6 +159,7 @@ function isAnyModifiedEnterSequence(input) {
|
|
|
159
159
|
export function TextEntryPanel({
|
|
160
160
|
title,
|
|
161
161
|
hint = '',
|
|
162
|
+
detail = '',
|
|
162
163
|
initialValue = '',
|
|
163
164
|
mask = false,
|
|
164
165
|
columns = 80,
|
|
@@ -465,6 +466,10 @@ export function TextEntryPanel({
|
|
|
465
466
|
// The hint is collapsed to one line and width-truncated so a long manual
|
|
466
467
|
// OAuth URL can never wrap and push the bordered title off the top.
|
|
467
468
|
const hintText = truncateText(singleLine(hint), Math.max(0, columns - 4));
|
|
469
|
+
// Optional wrapped detail block (e.g. the manual OAuth URL). Lives INSIDE the
|
|
470
|
+
// live panel — never pushed to the transcript — so it disappears completely
|
|
471
|
+
// when the panel closes instead of lingering in terminal scrollback.
|
|
472
|
+
const detailText = String(detail || '').trim();
|
|
468
473
|
|
|
469
474
|
return (
|
|
470
475
|
<Box flexDirection="column" flexShrink={0} width="100%">
|
|
@@ -475,6 +480,12 @@ export function TextEntryPanel({
|
|
|
475
480
|
</Box>
|
|
476
481
|
<Text> </Text>
|
|
477
482
|
<Text color={theme.subtle}>{hintText || ' '}</Text>
|
|
483
|
+
{detailText ? (
|
|
484
|
+
<Box flexDirection="column" width="100%">
|
|
485
|
+
<Text> </Text>
|
|
486
|
+
<Text color={theme.subtle} wrap="wrap">{detailText}</Text>
|
|
487
|
+
</Box>
|
|
488
|
+
) : null}
|
|
478
489
|
<Text> </Text>
|
|
479
490
|
<Box ref={boxRef} flexDirection="row" width="100%" backgroundColor={surfaceBackground()}>
|
|
480
491
|
<Text color={theme.inactive}>{promptLabel}</Text>
|