mixdog 0.9.3 → 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 +7 -3
- 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/freevar-smoke.mjs +95 -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/provider-toolcall-test.mjs +7 -3
- 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 +23 -63
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +6 -5
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/worker/AGENT.md +8 -4
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +632 -2042
- 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/rules/agent/00-common.md +2 -0
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +0 -11
- 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/providers/anthropic-effort.mjs +62 -20
- 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 +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- 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 +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- 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/openai-codex-model.mjs +108 -0
- 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 +40 -1143
- 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 +297 -2123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
- 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/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-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- 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/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -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/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- 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/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 +32 -41
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- 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 -92
- 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 +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- 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 +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- 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 +229 -663
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -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/output-forwarder.mjs +1 -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/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/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +201 -1948
- 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/embedding-warmup.mjs +58 -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 +24 -842
- 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-store.mjs +22 -2
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -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 +5 -13
- 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/atomic-file.mjs +26 -1
- package/src/runtime/shared/launcher-control.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 +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +84 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -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 +11 -9
- 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/settings-api.mjs +319 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -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 +110 -671
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +30 -9
- 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 +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +945 -8094
- 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 +23 -101
- 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 +52 -594
- 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/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +19652 -18630
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -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.mjs +122 -562
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- 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 +67 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +1 -1
- package/src/workflows/sequential/WORKFLOW.md +1 -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/external-tool-adapters.test.mjs +0 -162
- 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/task-notification-envelope.test.mjs +0 -107
- 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/input-editing.selection.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/paste-fix.test.mjs +0 -119
- 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
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* use-prompt-handlers.mjs — the PROMPT HANDLER cluster extracted from App.jsx
|
|
3
|
+
* (pass-7 split).
|
|
4
|
+
*
|
|
5
|
+
* Exports usePromptHandlers(), which owns the five useCallback handlers wired to
|
|
6
|
+
* PromptInput: handleVoiceToggle / handlePromptPaste / handlePromptHistoryNavigate
|
|
7
|
+
* / handlePromptEscape / handlePromptInterrupt. Every ref, setter, store value and
|
|
8
|
+
* derived value the handlers close over is threaded in explicitly so the deps
|
|
9
|
+
* arrays stay byte-identical to the original inline hooks. Import-level helpers
|
|
10
|
+
* (voice recorder + paste attachments) are imported directly here.
|
|
11
|
+
*/
|
|
12
|
+
import { useCallback } from 'react';
|
|
13
|
+
import {
|
|
14
|
+
readClipboardImageAttachment,
|
|
15
|
+
readImageAttachmentFromPath,
|
|
16
|
+
splitPastedImagePathCandidates,
|
|
17
|
+
} from '../paste-attachments.mjs';
|
|
18
|
+
import {
|
|
19
|
+
shouldFoldPastedText,
|
|
20
|
+
} from '../paste-attachments.mjs';
|
|
21
|
+
import {
|
|
22
|
+
isVoiceEnabled,
|
|
23
|
+
getRecorderState,
|
|
24
|
+
startRecording,
|
|
25
|
+
stopRecording,
|
|
26
|
+
cancelRecording,
|
|
27
|
+
} from '../lib/voice-recorder.mjs';
|
|
28
|
+
import { promptHistoryKey } from '../prompt-history-store.mjs';
|
|
29
|
+
|
|
30
|
+
export function usePromptHandlers({
|
|
31
|
+
store,
|
|
32
|
+
state,
|
|
33
|
+
// refs
|
|
34
|
+
promptValueRef,
|
|
35
|
+
pastedImagesRef,
|
|
36
|
+
nextPastedImageIdRef,
|
|
37
|
+
pastedTextsRef,
|
|
38
|
+
nextPastedTextIdRef,
|
|
39
|
+
promptHistoryNavRef,
|
|
40
|
+
promptHistoryDraftChangeRef,
|
|
41
|
+
// setters
|
|
42
|
+
setPastedImages,
|
|
43
|
+
setPastedTexts,
|
|
44
|
+
setPromptDraftOverride,
|
|
45
|
+
setVoiceRecPhase,
|
|
46
|
+
setContextPanel,
|
|
47
|
+
// derived / helper values + callbacks
|
|
48
|
+
setVoiceStatusHint,
|
|
49
|
+
syncPromptLayoutRows,
|
|
50
|
+
showPromptHint,
|
|
51
|
+
clearPromptHint,
|
|
52
|
+
recentPromptHistory,
|
|
53
|
+
resetPromptHistoryNav,
|
|
54
|
+
restoreQueuedToPrompt,
|
|
55
|
+
usagePanel,
|
|
56
|
+
closeUsagePanel,
|
|
57
|
+
contextPanel,
|
|
58
|
+
// paste-attachment helpers (owned by App, threaded in explicitly)
|
|
59
|
+
installPastedImages,
|
|
60
|
+
clearPastedImagesSnapshot,
|
|
61
|
+
registerPastedImage,
|
|
62
|
+
installPastedTexts,
|
|
63
|
+
clearPastedTextsSnapshot,
|
|
64
|
+
registerPastedText,
|
|
65
|
+
}) {
|
|
66
|
+
// Ctrl+Space handler wired to PromptInput's onVoiceToggle. Dispatches on the
|
|
67
|
+
// recorder's CURRENT state (idle/recording/transcribing) — see
|
|
68
|
+
// src/tui/lib/voice-recorder.mjs for the state machine itself.
|
|
69
|
+
const handleVoiceToggle = useCallback(async () => {
|
|
70
|
+
if (!isVoiceEnabled()) {
|
|
71
|
+
store.pushNotice('Voice is off — run /voice to turn it on', 'warn');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const recState = getRecorderState();
|
|
75
|
+
if (recState === 'transcribing') {
|
|
76
|
+
store.pushNotice('Voice: already transcribing…', 'info');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (recState === 'recording') {
|
|
80
|
+
setVoiceStatusHint('… transcribing', 'info');
|
|
81
|
+
setVoiceRecPhase('transcribing');
|
|
82
|
+
let result;
|
|
83
|
+
try {
|
|
84
|
+
result = await stopRecording();
|
|
85
|
+
} catch (e) {
|
|
86
|
+
result = { ok: false, reason: e?.message || String(e) };
|
|
87
|
+
}
|
|
88
|
+
setVoiceStatusHint('', 'info');
|
|
89
|
+
setVoiceRecPhase('idle');
|
|
90
|
+
if (!result) return; // race: recorder was no longer RECORDING
|
|
91
|
+
if (!result.ok) {
|
|
92
|
+
store.pushNotice(`Voice: ${result.reason || 'transcription failed'}`, 'error');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const text = String(result.text || '').trim();
|
|
96
|
+
if (!text) {
|
|
97
|
+
store.pushNotice('Voice: no speech detected', 'warn');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// End-of-draft insertion via promptDraftOverride (approved design —
|
|
101
|
+
// no cursor-position insert; see gamerscroll skill's out-of-scope note
|
|
102
|
+
// on PromptInput's imperative surface).
|
|
103
|
+
// Med-4: promptValueRef.current is read HERE — after `await
|
|
104
|
+
// stopRecording()` has already resolved — not captured earlier before
|
|
105
|
+
// the await. PromptInput keeps valueRef.current live on every
|
|
106
|
+
// keystroke (commitDraft), so this always reflects whatever the user
|
|
107
|
+
// typed during the recording+transcribe window; nothing typed in that
|
|
108
|
+
// gap is overwritten.
|
|
109
|
+
const current = promptValueRef.current || '';
|
|
110
|
+
const next = current ? `${current}${/\s$/.test(current) ? '' : ' '}${text}` : text;
|
|
111
|
+
syncPromptLayoutRows(next);
|
|
112
|
+
setPromptDraftOverride({ id: Date.now(), value: next });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// idle -> recording
|
|
116
|
+
let result;
|
|
117
|
+
try {
|
|
118
|
+
result = await startRecording();
|
|
119
|
+
} catch (e) {
|
|
120
|
+
result = { ok: false, reason: e?.message || String(e) };
|
|
121
|
+
}
|
|
122
|
+
if (!result?.ok) {
|
|
123
|
+
store.pushNotice(`Voice: ${result?.reason || 'failed to start recording'}`, 'error');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
setVoiceStatusHint('● REC', 'error');
|
|
127
|
+
setVoiceRecPhase('recording');
|
|
128
|
+
}, [store, setVoiceStatusHint, syncPromptLayoutRows]);
|
|
129
|
+
|
|
130
|
+
const handlePromptPaste = useCallback((text, meta = {}) => {
|
|
131
|
+
const source = String(meta?.source || 'paste');
|
|
132
|
+
const value = String(text ?? '');
|
|
133
|
+
if (source === 'clipboard-image-shortcut' && !value) {
|
|
134
|
+
return readClipboardImageAttachment()
|
|
135
|
+
.then((image) => {
|
|
136
|
+
if (!image) {
|
|
137
|
+
showPromptHint('no image found on clipboard', 'plain');
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
const ref = registerPastedImage(image);
|
|
141
|
+
showPromptHint(`attached ${image.filename || 'clipboard image'}`, 'plain');
|
|
142
|
+
return ref;
|
|
143
|
+
})
|
|
144
|
+
.catch((e) => {
|
|
145
|
+
showPromptHint(`image paste failed: ${e?.message || e}`, 'warn');
|
|
146
|
+
return false;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const chunks = splitPastedImagePathCandidates(value);
|
|
151
|
+
const hasImagePath = chunks.some((chunk) => chunk.imagePath);
|
|
152
|
+
// No image paths in the paste: fold the whole text into a token when it is
|
|
153
|
+
// large, otherwise let PromptInput insert it raw (return undefined).
|
|
154
|
+
if (!hasImagePath) {
|
|
155
|
+
if (shouldFoldPastedText(value)) return registerPastedText(value);
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
// Mixed paste: resolve each image chunk to an image ref, then fold each
|
|
159
|
+
// CONTIGUOUS run of non-image text into its own token (only if over
|
|
160
|
+
// threshold) so content order around image refs is preserved. '\n'
|
|
161
|
+
// separator chunks are plain text and stay inside their surrounding run.
|
|
162
|
+
return Promise.all(chunks.map(async (chunk) => {
|
|
163
|
+
if (!chunk.imagePath) return chunk.text;
|
|
164
|
+
try {
|
|
165
|
+
const image = await readImageAttachmentFromPath(chunk.text, state.cwd || process.cwd());
|
|
166
|
+
if (!image) return chunk.text;
|
|
167
|
+
const ref = registerPastedImage(image);
|
|
168
|
+
showPromptHint(`attached ${image.filename || 'image'}`, 'plain');
|
|
169
|
+
return ref;
|
|
170
|
+
} catch (e) {
|
|
171
|
+
showPromptHint(`image attach failed: ${e?.message || e}`, 'warn');
|
|
172
|
+
return chunk.text;
|
|
173
|
+
}
|
|
174
|
+
})).then((parts) => {
|
|
175
|
+
let out = '';
|
|
176
|
+
let run = '';
|
|
177
|
+
const flushRun = () => {
|
|
178
|
+
if (!run) return;
|
|
179
|
+
out += shouldFoldPastedText(run) ? registerPastedText(run) : run;
|
|
180
|
+
run = '';
|
|
181
|
+
};
|
|
182
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
183
|
+
if (chunks[i].imagePath) {
|
|
184
|
+
flushRun();
|
|
185
|
+
out += parts[i];
|
|
186
|
+
} else {
|
|
187
|
+
run += parts[i];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
flushRun();
|
|
191
|
+
return out;
|
|
192
|
+
});
|
|
193
|
+
}, [registerPastedImage, registerPastedText, showPromptHint, state.cwd]);
|
|
194
|
+
|
|
195
|
+
const handlePromptHistoryNavigate = useCallback((direction, currentText = '', meta = {}) => {
|
|
196
|
+
const currentValue = String(currentText || '');
|
|
197
|
+
const currentKey = promptHistoryKey(currentValue);
|
|
198
|
+
const nav = promptHistoryNavRef.current || { active: false, index: -1, seed: '', lastValue: '' };
|
|
199
|
+
|
|
200
|
+
if (meta.emptyDraft && direction === 'down') {
|
|
201
|
+
resetPromptHistoryNav();
|
|
202
|
+
clearPromptHint();
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (recentPromptHistory.length === 0) {
|
|
207
|
+
resetPromptHistoryNav();
|
|
208
|
+
clearPromptHint();
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (direction === 'down' && !nav.active) {
|
|
213
|
+
clearPromptHint();
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const active = nav.active && (currentValue === nav.lastValue || currentValue === nav.seed);
|
|
218
|
+
const seed = active ? nav.seed : currentValue;
|
|
219
|
+
const step = direction === 'down' ? -1 : 1;
|
|
220
|
+
let nextIndex = (active ? nav.index : -1) + step;
|
|
221
|
+
|
|
222
|
+
if (nextIndex < 0) {
|
|
223
|
+
resetPromptHistoryNav();
|
|
224
|
+
clearPromptHint();
|
|
225
|
+
promptHistoryDraftChangeRef.current = true;
|
|
226
|
+
return seed;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
while (nextIndex >= 0 && nextIndex < recentPromptHistory.length && promptHistoryKey(recentPromptHistory[nextIndex]) === currentKey) {
|
|
230
|
+
nextIndex += step;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (nextIndex < 0) {
|
|
234
|
+
resetPromptHistoryNav();
|
|
235
|
+
clearPromptHint();
|
|
236
|
+
promptHistoryDraftChangeRef.current = true;
|
|
237
|
+
return seed;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (nextIndex >= recentPromptHistory.length) {
|
|
241
|
+
clearPromptHint();
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const nextValue = recentPromptHistory[nextIndex];
|
|
246
|
+
promptHistoryNavRef.current = { active: true, index: nextIndex, seed, lastValue: nextValue };
|
|
247
|
+
clearPromptHint();
|
|
248
|
+
promptHistoryDraftChangeRef.current = true;
|
|
249
|
+
return nextValue;
|
|
250
|
+
}, [recentPromptHistory, resetPromptHistoryNav, clearPromptHint]);
|
|
251
|
+
|
|
252
|
+
// ESC / Up handling (prompt input):
|
|
253
|
+
// - prompt-local overlays such as the slash palette close first.
|
|
254
|
+
// - queued editable messages pop back into the prompt before clear/interrupt.
|
|
255
|
+
// - non-empty prompt text is cleared by PromptInput and must never interrupt
|
|
256
|
+
// the active turn on the same Esc press.
|
|
257
|
+
// - empty prompt + active turn interrupts the active turn.
|
|
258
|
+
const handlePromptEscape = useCallback((text = '', meta = {}) => {
|
|
259
|
+
// Recording takes priority over every other Esc branch (usage/context
|
|
260
|
+
// panels, slash-clear, queue-restore, turn-interrupt): a live mic capture
|
|
261
|
+
// must never silently keep running because Esc was consumed by something
|
|
262
|
+
// else first. Only consumes Esc while actually RECORDING — transcribing
|
|
263
|
+
// (already stopped, awaiting the HTTP round-trip) and idle fall through
|
|
264
|
+
// to the existing branches unchanged.
|
|
265
|
+
if (getRecorderState() === 'recording') {
|
|
266
|
+
cancelRecording();
|
|
267
|
+
setVoiceStatusHint('', 'info');
|
|
268
|
+
setVoiceRecPhase('idle');
|
|
269
|
+
store.pushNotice('Voice: recording cancelled', 'plain');
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
if (usagePanel) { closeUsagePanel(); return true; }
|
|
273
|
+
if (contextPanel) { setContextPanel(null); return true; }
|
|
274
|
+
|
|
275
|
+
if (meta.phase === 'clear') {
|
|
276
|
+
clearPastedImagesSnapshot();
|
|
277
|
+
clearPromptHint();
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
if (meta.phase === 'empty') {
|
|
281
|
+
return restoreQueuedToPrompt({ restoreDraft: true, showHint: false, currentText: text });
|
|
282
|
+
}
|
|
283
|
+
// Idle + empty + nothing to restore: nothing (double-press from empty
|
|
284
|
+
// opens message selector, but we don't have that feature yet).
|
|
285
|
+
return false;
|
|
286
|
+
}, [contextPanel, usagePanel, closeUsagePanel, restoreQueuedToPrompt, clearPromptHint, clearPastedImagesSnapshot, setVoiceStatusHint, store]);
|
|
287
|
+
|
|
288
|
+
const handlePromptInterrupt = useCallback((currentText = '') => {
|
|
289
|
+
const result = store.abort?.();
|
|
290
|
+
if (result?.aborted === false) return undefined;
|
|
291
|
+
if (result?.pastedImages) installPastedImages(result.pastedImages, { merge: true });
|
|
292
|
+
if (result?.discardPastedImages) clearPastedImagesSnapshot(result.discardPastedImages);
|
|
293
|
+
if (result?.pastedTexts) installPastedTexts(result.pastedTexts, { merge: true });
|
|
294
|
+
if (result?.discardPastedTexts) clearPastedTextsSnapshot(result.discardPastedTexts);
|
|
295
|
+
const restoreText = String(result?.restoreText || '').trim();
|
|
296
|
+
if (!restoreText) return undefined;
|
|
297
|
+
const existingText = String(currentText || '').trim();
|
|
298
|
+
const nextText = [restoreText, existingText].filter(Boolean).join('\n');
|
|
299
|
+
clearPromptHint();
|
|
300
|
+
return nextText;
|
|
301
|
+
}, [store, clearPromptHint, installPastedImages, clearPastedImagesSnapshot]);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
handleVoiceToggle,
|
|
305
|
+
handlePromptPaste,
|
|
306
|
+
handlePromptHistoryNavigate,
|
|
307
|
+
handlePromptEscape,
|
|
308
|
+
handlePromptInterrupt,
|
|
309
|
+
};
|
|
310
|
+
}
|