mixdog 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -38
- package/package.json +10 -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/r5-orchestrated-task.json +7 -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-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -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 +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- 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-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- 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 +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- 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 +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -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 +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -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/provider-catalog-cache.mjs +80 -0
- 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 +275 -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 +278 -402
- 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/stall-policy.mjs +20 -1
- 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 +35 -44
- 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 +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- 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/scheduler.mjs +27 -113
- 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 +313 -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 +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -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/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -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 +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -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/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- 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 +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -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 +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -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/session-runtime/workflow.mjs +1 -1
- 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 +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- 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 +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -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 +356 -0
- package/src/tui/app/model-options.mjs +334 -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 +489 -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 +677 -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 +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- 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 +13553 -12384
- 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 +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- 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 +77 -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 +16 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- 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/channels/lib/holidays.mjs +0 -138
- 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,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* onboarding-steps.mjs — the first-run onboarding wizard step cluster.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from App.jsx behavior-preservingly as a dependency-injection
|
|
5
|
+
* factory. Every function body is the original App logic verbatim, with closure
|
|
6
|
+
* identifiers threaded through the factory argument. Cross-references between
|
|
7
|
+
* steps stay inside this factory; deps pointing at later-defined App fns
|
|
8
|
+
* (openProviderSetupPicker, openThemePicker, openOutputStylePicker) thread as
|
|
9
|
+
* lazy getter wrappers so they resolve the live opener at call time.
|
|
10
|
+
*/
|
|
11
|
+
import { theme } from '../theme.mjs';
|
|
12
|
+
import { SEARCH_DEFAULT_ROUTE, isSearchDefaultRoute } from './app-format.mjs';
|
|
13
|
+
import {
|
|
14
|
+
normalizeModelOptions,
|
|
15
|
+
modelDescription,
|
|
16
|
+
agentModelParts,
|
|
17
|
+
routeFromModel,
|
|
18
|
+
} from './model-options.mjs';
|
|
19
|
+
|
|
20
|
+
export function createOnboardingSteps({
|
|
21
|
+
store,
|
|
22
|
+
setPicker,
|
|
23
|
+
setProviderPrompt,
|
|
24
|
+
setChannelPrompt,
|
|
25
|
+
setHookPrompt,
|
|
26
|
+
setSettingsPrompt,
|
|
27
|
+
setOnboardingActive,
|
|
28
|
+
onboardingRef,
|
|
29
|
+
providerModelsCacheRef,
|
|
30
|
+
onboardingPrefetchSeqRef,
|
|
31
|
+
openProviderSetupPicker,
|
|
32
|
+
openThemePicker,
|
|
33
|
+
openOutputStylePicker,
|
|
34
|
+
}) {
|
|
35
|
+
// First-run onboarding is a 4-step wizard. Each step's ROOT screen carries a
|
|
36
|
+
// ConfirmBar (Back/Next, Finish on the last step); the Picker owns the bar
|
|
37
|
+
// focus and only fires onConfirm from the bar. Nested depths (API-key entry,
|
|
38
|
+
// model route picker) render without a ConfirmBar so
|
|
39
|
+
// their own key semantics are untouched and step-switching is disabled there.
|
|
40
|
+
// Esc/cancel during onboarding = confirm skip. Mark onboarding complete
|
|
41
|
+
// (routes/agents/provider untouched) so it does not reopen next launch;
|
|
42
|
+
// `mixdog --onboarding` (forceOnboarding) still reopens regardless.
|
|
43
|
+
const onboardingWarnReopen = () => {
|
|
44
|
+
setOnboardingActive(false);
|
|
45
|
+
try {
|
|
46
|
+
store.skipOnboarding?.();
|
|
47
|
+
store.pushNotice('Setup skipped. Run `mixdog --onboarding` to set up later.', 'info');
|
|
48
|
+
} catch (e) {
|
|
49
|
+
store.pushNotice(`Couldn’t save skip: ${e?.message || e}`, 'error');
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Warm the Step 2 data (provider models + agent roster) in the background as
|
|
54
|
+
// soon as Step 1 opens, so advancing to Step 2 renders instantly instead of
|
|
55
|
+
// flashing an empty panel while the async load runs.
|
|
56
|
+
const prefetchOnboardingStep2 = () => {
|
|
57
|
+
if (!Array.isArray(onboardingRef.current.providerModels) || onboardingRef.current.providerModels.length === 0) {
|
|
58
|
+
const seq = onboardingPrefetchSeqRef.current;
|
|
59
|
+
void Promise.resolve(store.listProviderModels?.())
|
|
60
|
+
.then((models) => {
|
|
61
|
+
// Drop a stale result: if the provider cache was invalidated (auth
|
|
62
|
+
// change) while this load was in flight, its generation moved on.
|
|
63
|
+
if (seq !== onboardingPrefetchSeqRef.current) return;
|
|
64
|
+
if (Array.isArray(models) && models.length) {
|
|
65
|
+
onboardingRef.current.providerModels = models;
|
|
66
|
+
providerModelsCacheRef.current = { models, at: Date.now() };
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.catch(() => { /* Step 2 falls back to its own load on entry. */ });
|
|
70
|
+
}
|
|
71
|
+
if (!Array.isArray(onboardingRef.current.agents) || onboardingRef.current.agents.length === 0) {
|
|
72
|
+
try {
|
|
73
|
+
const roster = (store.listAgents?.() || []).map((a) => ({ id: a.id, label: a.label || a.id, description: a.description || '' }));
|
|
74
|
+
if (roster.length) onboardingRef.current.agents = roster;
|
|
75
|
+
} catch { /* Step 2 retries on entry. */ }
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const openOnboardingAuthStep = async () => {
|
|
80
|
+
prefetchOnboardingStep2();
|
|
81
|
+
// Load the provider setup BEFORE opening the picker so Step 1 renders the
|
|
82
|
+
// real list in one frame instead of flashing the "Checking Providers"
|
|
83
|
+
// placeholder (that swap is what looked like a jump on entry). On failure,
|
|
84
|
+
// fall back to the picker's own in-panel loading path.
|
|
85
|
+
let preloadedSetup = null;
|
|
86
|
+
try {
|
|
87
|
+
preloadedSetup = await store.getProviderSetup?.();
|
|
88
|
+
} catch { /* openProviderSetupPicker will show its loading frame + error. */ }
|
|
89
|
+
void openProviderSetupPicker({
|
|
90
|
+
title: 'First Run · Step 1/4 · Provider Auth',
|
|
91
|
+
returnTo: () => openOnboardingAuthStep(),
|
|
92
|
+
preloadedSetup,
|
|
93
|
+
confirmBar: {
|
|
94
|
+
buttons: [{ value: 'next', label: 'Next ▶' }],
|
|
95
|
+
// Keep Step 1 visible while Step 2's async model load runs; the next
|
|
96
|
+
// step replaces the picker itself, so no blank frame in between.
|
|
97
|
+
onConfirm: () => { void openOnboardingWorkflowStep(); },
|
|
98
|
+
},
|
|
99
|
+
onCancel: onboardingWarnReopen,
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const openOnboardingThemeStep = () => {
|
|
104
|
+
openThemePicker({
|
|
105
|
+
onboarding: {
|
|
106
|
+
onAdvance: () => openOnboardingOutputStyleStep(),
|
|
107
|
+
onBack: () => void openOnboardingWorkflowStep(),
|
|
108
|
+
onCancel: onboardingWarnReopen,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const openOnboardingOutputStyleStep = () => {
|
|
114
|
+
openOutputStylePicker({
|
|
115
|
+
onboarding: {
|
|
116
|
+
isLastStep: true,
|
|
117
|
+
onAdvance: () => finishOnboarding(),
|
|
118
|
+
onBack: () => openOnboardingThemeStep(),
|
|
119
|
+
onCancel: onboardingWarnReopen,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const finishOnboarding = () => {
|
|
125
|
+
const defaultRoute = onboardingRef.current.defaultRoute;
|
|
126
|
+
const searchRoute = onboardingRef.current.searchRoute || null;
|
|
127
|
+
const overrides = onboardingRef.current.agentRoutes || {};
|
|
128
|
+
const hasOverrides = Object.keys(overrides).length > 0;
|
|
129
|
+
setPicker(null);
|
|
130
|
+
setOnboardingActive(false);
|
|
131
|
+
const done = () => store.pushNotice('First-run setup complete.', 'info');
|
|
132
|
+
const failed = (e) => store.pushNotice(`Couldn’t save setup: ${e?.message || e}`, 'error');
|
|
133
|
+
// Branch 1 — Main Model set: full persist. Agents without an explicit
|
|
134
|
+
// override are sent; untouched agents are left out so the backend never
|
|
135
|
+
// overwrites them (they follow the Main Model dynamically at runtime).
|
|
136
|
+
if (defaultRoute) {
|
|
137
|
+
void store.completeOnboarding?.({
|
|
138
|
+
defaultRoute,
|
|
139
|
+
defaultProvider: defaultRoute.provider,
|
|
140
|
+
...(hasOverrides ? { agentRoutes: { ...overrides } } : {}),
|
|
141
|
+
...(searchRoute ? { searchRoute } : {}),
|
|
142
|
+
}).then(done).catch(failed);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Branch 2 — Main unset but some Search/agent picks exist: partial persist.
|
|
146
|
+
// Only the explicit overrides are sent (no defaultRoute/defaultProvider); the
|
|
147
|
+
// backend skips agents lacking a route and marks onboarding complete.
|
|
148
|
+
if (hasOverrides || searchRoute) {
|
|
149
|
+
void store.completeOnboarding?.({
|
|
150
|
+
...(hasOverrides ? { agentRoutes: { ...overrides } } : {}),
|
|
151
|
+
...(searchRoute ? { searchRoute } : {}),
|
|
152
|
+
}).then(done).catch(failed);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Branch 3 — nothing configured: mark done only, leave config untouched.
|
|
156
|
+
try {
|
|
157
|
+
store.skipOnboarding?.();
|
|
158
|
+
} catch (e) {
|
|
159
|
+
failed(e);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
done();
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Onboarding Step 2 per-target model picker. `target` is either the pseudo
|
|
166
|
+
// slot 'lead' (Main Model) or a real agent id from listAgents(). No
|
|
167
|
+
// recommendation logic: the current effective route is pre-highlighted, and
|
|
168
|
+
// the plain model list is shown. Selecting Main Model updates defaultRoute;
|
|
169
|
+
// agents that have no explicit override keep inheriting it.
|
|
170
|
+
const openOnboardingRoleModelPicker = async (target) => {
|
|
171
|
+
const isLead = target === 'lead';
|
|
172
|
+
const isSearch = target === 'search';
|
|
173
|
+
// Search uses the search-capable model list; lead/agent use provider models.
|
|
174
|
+
let models;
|
|
175
|
+
if (isSearch) {
|
|
176
|
+
let searchModels = [];
|
|
177
|
+
try {
|
|
178
|
+
searchModels = await Promise.resolve(store.listSearchModels?.() || []);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
store.pushNotice(`could not list search models: ${e?.message || e}`, 'warn');
|
|
181
|
+
}
|
|
182
|
+
models = normalizeModelOptions(searchModels || []);
|
|
183
|
+
if (models.length === 0) {
|
|
184
|
+
store.pushNotice('no native search models available; connect OpenAI, Grok, Gemini, or Anthropic', 'warn');
|
|
185
|
+
void openOnboardingWorkflowStep();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
models = normalizeModelOptions(onboardingRef.current.providerModels || []);
|
|
190
|
+
if (models.length === 0) {
|
|
191
|
+
store.pushNotice('no provider models available; open /providers to sign in', 'warn');
|
|
192
|
+
openOnboardingAuthStep();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const overrides = onboardingRef.current.agentRoutes || {};
|
|
197
|
+
// Current effective route for pre-marking: Main/Search show their own stored
|
|
198
|
+
// route (or none); agents show their explicit override only (unset = none,
|
|
199
|
+
// so we don't falsely mark the Main Model row on an untouched agent).
|
|
200
|
+
const currentRoute = isLead
|
|
201
|
+
? (onboardingRef.current.defaultRoute || null)
|
|
202
|
+
: isSearch
|
|
203
|
+
? (onboardingRef.current.searchRoute || null)
|
|
204
|
+
: (overrides[target] || null);
|
|
205
|
+
const routeMatchesModel = (route, m) => route?.provider === m.provider && route?.model === m.id;
|
|
206
|
+
// Non-lead targets get a leading "Default" row that makes the target follow
|
|
207
|
+
// the Main Model at runtime. For agents this clears the override (null);
|
|
208
|
+
// for search this stores the SEARCH_DEFAULT marker route. "Default" is the
|
|
209
|
+
// pre-marked row when the target is unset (agent) or on the marker (search).
|
|
210
|
+
const isDefaultSelected = isSearch
|
|
211
|
+
? (!currentRoute || isSearchDefaultRoute(currentRoute))
|
|
212
|
+
: !currentRoute;
|
|
213
|
+
const isUnset = isDefaultSelected;
|
|
214
|
+
const modelItems = models.map((m) => ({
|
|
215
|
+
value: `${m.provider}:${m.id}`,
|
|
216
|
+
label: m.display || m.id,
|
|
217
|
+
marker: routeMatchesModel(currentRoute, m) ? '✓' : '',
|
|
218
|
+
markerColor: theme.success,
|
|
219
|
+
description: modelDescription(m),
|
|
220
|
+
_model: m,
|
|
221
|
+
}));
|
|
222
|
+
const items = isLead
|
|
223
|
+
? modelItems
|
|
224
|
+
: [
|
|
225
|
+
{
|
|
226
|
+
value: '__default__',
|
|
227
|
+
label: 'Default',
|
|
228
|
+
marker: isUnset ? '✓' : '',
|
|
229
|
+
markerColor: theme.success,
|
|
230
|
+
description: 'follows Main Model',
|
|
231
|
+
_default: true,
|
|
232
|
+
},
|
|
233
|
+
...modelItems,
|
|
234
|
+
];
|
|
235
|
+
const matchIdx = models.findIndex((m) => routeMatchesModel(currentRoute, m));
|
|
236
|
+
const initialIndex = isLead
|
|
237
|
+
? Math.max(0, matchIdx)
|
|
238
|
+
: (isUnset || matchIdx < 0 ? 0 : matchIdx + 1);
|
|
239
|
+
const label = isLead
|
|
240
|
+
? 'Main'
|
|
241
|
+
: isSearch
|
|
242
|
+
? 'Search'
|
|
243
|
+
: (onboardingRef.current.agents || []).find((a) => a.id === target)?.label || target;
|
|
244
|
+
setPicker({
|
|
245
|
+
title: `First Run · ${label}`,
|
|
246
|
+
description: isLead
|
|
247
|
+
? 'Pick the main model. Agents inherit this unless individually changed.'
|
|
248
|
+
: isSearch
|
|
249
|
+
? 'Pick the native web-search model, or Default to follow the Main Model.'
|
|
250
|
+
: `Pick the model for ${label}, or Default to follow the Main Model.`,
|
|
251
|
+
initialIndex,
|
|
252
|
+
items,
|
|
253
|
+
onSelect: (_value, item) => {
|
|
254
|
+
// "Default" → clear the override so this target follows the Main Model.
|
|
255
|
+
if (item?._default) {
|
|
256
|
+
if (isSearch) {
|
|
257
|
+
// Store the SEARCH_DEFAULT marker so finish persists it and the
|
|
258
|
+
// runtime follows the Main Model (not a null that drops the field).
|
|
259
|
+
onboardingRef.current.searchRoute = { ...SEARCH_DEFAULT_ROUTE };
|
|
260
|
+
} else {
|
|
261
|
+
const nextOverrides = { ...(onboardingRef.current.agentRoutes || {}) };
|
|
262
|
+
delete nextOverrides[target];
|
|
263
|
+
onboardingRef.current.agentRoutes = nextOverrides;
|
|
264
|
+
}
|
|
265
|
+
void openOnboardingWorkflowStep();
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const next = item?._model ? routeFromModel(item._model) : null;
|
|
269
|
+
if (!next) {
|
|
270
|
+
store.pushNotice('select a provider model first', 'warn');
|
|
271
|
+
void openOnboardingWorkflowStep();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (isLead) {
|
|
275
|
+
onboardingRef.current.defaultRoute = next;
|
|
276
|
+
} else if (isSearch) {
|
|
277
|
+
onboardingRef.current.searchRoute = next;
|
|
278
|
+
} else {
|
|
279
|
+
onboardingRef.current.agentRoutes = {
|
|
280
|
+
...(onboardingRef.current.agentRoutes || {}),
|
|
281
|
+
[target]: next,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
void openOnboardingWorkflowStep();
|
|
285
|
+
},
|
|
286
|
+
onCancel: () => {
|
|
287
|
+
void openOnboardingWorkflowStep();
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const openOnboardingWorkflowStep = async () => {
|
|
293
|
+
if (!Array.isArray(onboardingRef.current.providerModels) || onboardingRef.current.providerModels.length === 0) {
|
|
294
|
+
try {
|
|
295
|
+
onboardingRef.current.providerModels = await store.listProviderModels();
|
|
296
|
+
providerModelsCacheRef.current = { models: onboardingRef.current.providerModels, at: Date.now() };
|
|
297
|
+
} catch (e) {
|
|
298
|
+
onboardingRef.current.providerModels = [];
|
|
299
|
+
store.pushNotice(`could not list models: ${e?.message || e}`, 'warn');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const models = onboardingRef.current.providerModels || [];
|
|
303
|
+
if (models.length === 0) {
|
|
304
|
+
onboardingRef.current.defaultRoute = null;
|
|
305
|
+
onboardingRef.current.agentRoutes = {};
|
|
306
|
+
store.pushNotice('no provider models available; open /providers to sign in', 'warn');
|
|
307
|
+
openOnboardingAuthStep();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
// Main Model stays unset until the user picks one; no auto-recommendation.
|
|
311
|
+
// Load the real agent roster once (explore/maintainer/worker/heavy-worker/
|
|
312
|
+
// reviewer/debugger). Each agent defaults to the Main Model unless the user
|
|
313
|
+
// set an explicit override in agentRoutes.
|
|
314
|
+
if (!Array.isArray(onboardingRef.current.agents) || onboardingRef.current.agents.length === 0) {
|
|
315
|
+
try {
|
|
316
|
+
onboardingRef.current.agents = (store.listAgents?.() || []).map((a) => ({ id: a.id, label: a.label || a.id, description: a.description || '' }));
|
|
317
|
+
} catch (e) {
|
|
318
|
+
onboardingRef.current.agents = [];
|
|
319
|
+
store.pushNotice(`could not list agents: ${e?.message || e}`, 'warn');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const defaultRoute = onboardingRef.current.defaultRoute;
|
|
323
|
+
const searchRoute = onboardingRef.current.searchRoute || null;
|
|
324
|
+
const overrides = onboardingRef.current.agentRoutes || {};
|
|
325
|
+
const agents = onboardingRef.current.agents || [];
|
|
326
|
+
setProviderPrompt(null);
|
|
327
|
+
setChannelPrompt(null);
|
|
328
|
+
setHookPrompt(null);
|
|
329
|
+
setSettingsPrompt(null);
|
|
330
|
+
setPicker({
|
|
331
|
+
title: 'First Run · Step 2/4 · Models',
|
|
332
|
+
description: 'Set the Main Model; each agent inherits it unless changed.',
|
|
333
|
+
indexMode: 'always',
|
|
334
|
+
labelWidth: 18,
|
|
335
|
+
metaWidth: 33,
|
|
336
|
+
items: [
|
|
337
|
+
{
|
|
338
|
+
value: 'main-model',
|
|
339
|
+
label: 'Main',
|
|
340
|
+
metaParts: agentModelParts(defaultRoute),
|
|
341
|
+
description: 'main chat, planning, and agent default',
|
|
342
|
+
_action: 'slot',
|
|
343
|
+
_target: 'lead',
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
value: 'search-model',
|
|
347
|
+
label: 'Search',
|
|
348
|
+
// Marker route = follow Main Model → show a hint, not 'default/default'.
|
|
349
|
+
metaParts: isSearchDefaultRoute(searchRoute)
|
|
350
|
+
? [{ text: '(follows main)', width: 17 }, { text: '', width: 6 }, { text: '', width: 4 }]
|
|
351
|
+
: agentModelParts(searchRoute),
|
|
352
|
+
description: 'native search model',
|
|
353
|
+
_action: 'slot',
|
|
354
|
+
_target: 'search',
|
|
355
|
+
},
|
|
356
|
+
...agents.map((agent) => ({
|
|
357
|
+
value: `agent:${agent.id}`,
|
|
358
|
+
label: agent.label,
|
|
359
|
+
metaParts: agentModelParts(overrides[agent.id] || null),
|
|
360
|
+
description: agent.description || '',
|
|
361
|
+
_action: 'slot',
|
|
362
|
+
_target: agent.id,
|
|
363
|
+
})),
|
|
364
|
+
],
|
|
365
|
+
confirmBar: {
|
|
366
|
+
buttons: [
|
|
367
|
+
{ value: 'back', label: '◀ Back' },
|
|
368
|
+
{ value: 'next', label: 'Next ▶' },
|
|
369
|
+
],
|
|
370
|
+
onConfirm: (button) => {
|
|
371
|
+
// Both neighbors are async (Step 1 preloads provider setup; theme
|
|
372
|
+
// list is sync but keep symmetric) — leave Step 2 visible until the
|
|
373
|
+
// next picker replaces it to avoid a blank frame.
|
|
374
|
+
if (button.value === 'back') openOnboardingAuthStep();
|
|
375
|
+
else openOnboardingThemeStep();
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
onSelect: (_value, item) => {
|
|
379
|
+
if (item._action === 'slot') {
|
|
380
|
+
openOnboardingRoleModelPicker(item._target);
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
onCancel: () => {
|
|
384
|
+
setPicker(null);
|
|
385
|
+
onboardingWarnReopen();
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
onboardingWarnReopen,
|
|
392
|
+
prefetchOnboardingStep2,
|
|
393
|
+
openOnboardingAuthStep,
|
|
394
|
+
openOnboardingThemeStep,
|
|
395
|
+
openOnboardingOutputStyleStep,
|
|
396
|
+
finishOnboarding,
|
|
397
|
+
openOnboardingRoleModelPicker,
|
|
398
|
+
openOnboardingWorkflowStep,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-picker.mjs — the project selector / create / rename / enter cluster.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from App.jsx behavior-preservingly. This cluster is ref/state
|
|
5
|
+
* coupled (it drives setPicker + a fan of prompt setters), so it's delivered as
|
|
6
|
+
* a dependency-injection factory rather than pure functions: App calls
|
|
7
|
+
* createProjectPicker({...}) once and destructures the returned builders. Every
|
|
8
|
+
* function body is the original App logic, verbatim, with closure identifiers
|
|
9
|
+
* (state, store, setPicker, the prompt setters, and the projects.mjs helpers)
|
|
10
|
+
* threaded in through the factory argument.
|
|
11
|
+
*/
|
|
12
|
+
export function createProjectPicker({
|
|
13
|
+
state,
|
|
14
|
+
store,
|
|
15
|
+
setPicker,
|
|
16
|
+
setProviderPrompt,
|
|
17
|
+
setChannelPrompt,
|
|
18
|
+
setHookPrompt,
|
|
19
|
+
setSettingsPrompt,
|
|
20
|
+
setContextPanel,
|
|
21
|
+
closeUsagePanel,
|
|
22
|
+
listProjects,
|
|
23
|
+
addProject,
|
|
24
|
+
touchProjectSelected,
|
|
25
|
+
resolveProjectPath,
|
|
26
|
+
projectNameFromPath,
|
|
27
|
+
pickFolder,
|
|
28
|
+
}) {
|
|
29
|
+
const buildProjectPickerState = ({ initialEntry = false } = {}) => {
|
|
30
|
+
let projects = [];
|
|
31
|
+
try {
|
|
32
|
+
projects = listProjects() || [];
|
|
33
|
+
} catch {
|
|
34
|
+
projects = [];
|
|
35
|
+
}
|
|
36
|
+
const currentPath = String(state.cwd || process.cwd() || '');
|
|
37
|
+
const items = [];
|
|
38
|
+
// Registered projects (store order via listProjects).
|
|
39
|
+
for (const project of projects) {
|
|
40
|
+
if (!project?.path) continue;
|
|
41
|
+
items.push({
|
|
42
|
+
value: project.path,
|
|
43
|
+
label: project.name || project.path,
|
|
44
|
+
meta: project.path,
|
|
45
|
+
_project: project,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Last row: implicit current-directory shortcut (not persisted).
|
|
49
|
+
items.push({
|
|
50
|
+
value: '__use_current__',
|
|
51
|
+
label: 'Current Path',
|
|
52
|
+
meta: currentPath,
|
|
53
|
+
_action: 'current',
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
kind: 'project',
|
|
57
|
+
title: 'Project',
|
|
58
|
+
description: 'Choose a project.',
|
|
59
|
+
help: initialEntry
|
|
60
|
+
? '↑/↓ Select · Enter Open · c Create · r Rename'
|
|
61
|
+
: '↑/↓ Select · Enter Open · c Create · r Rename · Esc Back',
|
|
62
|
+
indexMode: 'always',
|
|
63
|
+
labelWidth: 18,
|
|
64
|
+
metaWidth: 40,
|
|
65
|
+
items,
|
|
66
|
+
onSelect: (_value, item) => {
|
|
67
|
+
if (item?._action === 'new') {
|
|
68
|
+
beginNewProject();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (item?._action === 'current') {
|
|
72
|
+
setPicker(null);
|
|
73
|
+
try {
|
|
74
|
+
store.setCwd?.(currentPath, {
|
|
75
|
+
notice: !initialEntry,
|
|
76
|
+
message: `Project set: ${projectNameFromPath(currentPath)}`,
|
|
77
|
+
});
|
|
78
|
+
} catch (e) {
|
|
79
|
+
store.pushNotice(`project switch failed: ${e?.message || e}`, 'error');
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
setPicker(null);
|
|
84
|
+
const project = item?._project;
|
|
85
|
+
if (project?.path) enterProject(project.path, { notice: !initialEntry });
|
|
86
|
+
},
|
|
87
|
+
onKey: (input, _key, item) => {
|
|
88
|
+
if (input === 'c' || input === 'C') {
|
|
89
|
+
beginNewProject();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// 'r' renames the highlighted registered project (not the current-dir
|
|
93
|
+
// shortcut or the create row).
|
|
94
|
+
if ((input === 'r' || input === 'R') && item?._project?.path) {
|
|
95
|
+
beginRenameProject(item._project);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
onCancel: () => {
|
|
99
|
+
setPicker(null);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Open the manual path-entry flow. The user types a directory path; on submit
|
|
105
|
+
// we register it (and offer to create it if missing). Used as a
|
|
106
|
+
// fallback when no native folder dialog is available.
|
|
107
|
+
const beginNewProjectManual = () => {
|
|
108
|
+
setPicker(null);
|
|
109
|
+
setProviderPrompt(null);
|
|
110
|
+
setChannelPrompt(null);
|
|
111
|
+
setHookPrompt(null);
|
|
112
|
+
setContextPanel(null);
|
|
113
|
+
closeUsagePanel();
|
|
114
|
+
setSettingsPrompt({
|
|
115
|
+
kind: 'project-new',
|
|
116
|
+
label: 'New project · Path',
|
|
117
|
+
hint: 'Type a directory path. The folder name becomes the project name.',
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Begin "create project": open the OS-native folder picker. The project picker
|
|
122
|
+
// stays mounted (swapped to a non-interactive "Opening folder picker…" panel)
|
|
123
|
+
// while the native dialog is open, so the welcome banner/layout stay put and
|
|
124
|
+
// the prompt remains disabled (input is gated on `!!picker`). On a chosen
|
|
125
|
+
// folder we register; on cancel we return to the project picker;
|
|
126
|
+
// when no dialog tool exists we fall back to manual path typing.
|
|
127
|
+
const beginNewProject = () => {
|
|
128
|
+
setProviderPrompt(null);
|
|
129
|
+
setChannelPrompt(null);
|
|
130
|
+
setHookPrompt(null);
|
|
131
|
+
setContextPanel(null);
|
|
132
|
+
closeUsagePanel();
|
|
133
|
+
// Keep an overlay up (kind:'project' so the banner/height stay reserved) but
|
|
134
|
+
// make it inert: no selectable items, navigation is a no-op until resolve.
|
|
135
|
+
setPicker({
|
|
136
|
+
kind: 'project',
|
|
137
|
+
title: 'Project',
|
|
138
|
+
description: 'Opening folder picker… choose a folder in the dialog window.',
|
|
139
|
+
help: 'Waiting for the system folder dialog…',
|
|
140
|
+
indexMode: 'never',
|
|
141
|
+
items: [],
|
|
142
|
+
onSelect: () => {},
|
|
143
|
+
onCancel: () => {},
|
|
144
|
+
});
|
|
145
|
+
void pickFolder({
|
|
146
|
+
title: 'Select a project folder',
|
|
147
|
+
initialPath: String(state.cwd || process.cwd() || ''),
|
|
148
|
+
})
|
|
149
|
+
.then((result) => {
|
|
150
|
+
if (!result || result.available === false) {
|
|
151
|
+
// No native dialog on this system → manual typing.
|
|
152
|
+
beginNewProjectManual();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!result.path) {
|
|
156
|
+
// User cancelled the dialog → back to the project list.
|
|
157
|
+
openProjectPicker();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
registerProject(result.path);
|
|
161
|
+
})
|
|
162
|
+
.catch(() => {
|
|
163
|
+
beginNewProjectManual();
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Register a project in the picker list without switching this session's cwd.
|
|
168
|
+
const registerProject = (rawPath) => {
|
|
169
|
+
const path = resolveProjectPath(rawPath);
|
|
170
|
+
if (!path) {
|
|
171
|
+
store.pushNotice('project path is required', 'warn');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const project = addProject(path);
|
|
176
|
+
if (project?.name) store.pushNotice(`project added: ${project.name}`, 'info');
|
|
177
|
+
openProjectPicker();
|
|
178
|
+
} catch (e) {
|
|
179
|
+
store.pushNotice(`project add failed: ${e?.message || e}`, 'error');
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Switch the active working directory to a registered/created project path.
|
|
184
|
+
const enterProject = (rawPath, options = {}) => {
|
|
185
|
+
const path = resolveProjectPath(rawPath);
|
|
186
|
+
if (!path) {
|
|
187
|
+
store.pushNotice('project path is required', 'warn');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
// Switch cwd first; only persist the project once the runtime accepts it,
|
|
192
|
+
// so an invalid/missing path can never be written to projects.json.
|
|
193
|
+
store.setCwd?.(path, {
|
|
194
|
+
notice: options?.notice !== false,
|
|
195
|
+
message: `Project set: ${projectNameFromPath(path)}`,
|
|
196
|
+
});
|
|
197
|
+
addProject(path);
|
|
198
|
+
touchProjectSelected(path);
|
|
199
|
+
} catch (e) {
|
|
200
|
+
store.pushNotice(`project switch failed: ${e?.message || e}`, 'error');
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Begin renaming a registered project's display name. Opens a text prompt
|
|
205
|
+
// seeded with the current name; submitting persists via renameProject and
|
|
206
|
+
// returns to the project picker. The path is never changed.
|
|
207
|
+
const beginRenameProject = (project) => {
|
|
208
|
+
if (!project?.path) return;
|
|
209
|
+
setPicker(null);
|
|
210
|
+
setProviderPrompt(null);
|
|
211
|
+
setChannelPrompt(null);
|
|
212
|
+
setHookPrompt(null);
|
|
213
|
+
setContextPanel(null);
|
|
214
|
+
closeUsagePanel();
|
|
215
|
+
setSettingsPrompt({
|
|
216
|
+
kind: 'project-rename',
|
|
217
|
+
label: 'Rename project',
|
|
218
|
+
hint: 'Set a display name. Leave blank to reset to the folder name.',
|
|
219
|
+
projectPath: project.path,
|
|
220
|
+
initialValue: project.name || '',
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Open the project selector, styled like the Model picker: numbered rows with
|
|
225
|
+
// a Name column + Path column. The list always opens (even when empty) and
|
|
226
|
+
// lists registered projects first, then a trailing "Current Path" shortcut.
|
|
227
|
+
// Creating a new project is available via the picker-level c shortcut.
|
|
228
|
+
const openProjectPicker = () => {
|
|
229
|
+
setProviderPrompt(null);
|
|
230
|
+
setChannelPrompt(null);
|
|
231
|
+
setHookPrompt(null);
|
|
232
|
+
setSettingsPrompt(null);
|
|
233
|
+
setContextPanel(null);
|
|
234
|
+
closeUsagePanel();
|
|
235
|
+
setPicker(buildProjectPickerState());
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
buildProjectPickerState,
|
|
240
|
+
beginNewProjectManual,
|
|
241
|
+
beginNewProject,
|
|
242
|
+
registerProject,
|
|
243
|
+
enterProject,
|
|
244
|
+
beginRenameProject,
|
|
245
|
+
openProjectPicker,
|
|
246
|
+
};
|
|
247
|
+
}
|