mixdog 1.4.1 → 1.4.2
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 +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/gate-local.mjs +172 -61
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +54 -29
- package/scripts/lib/scoped-temp.mjs +1 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/pptx-kit-index.mjs +5 -6
- package/scripts/suite-health.mjs +110 -28
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +18 -2
- package/src/defaults/skills/browser-use/SKILL.md +4 -1
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +12 -2
- package/src/defaults/skills/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/rules-builder.cjs +6 -30
- package/src/rules/lead/LEAD.md +3 -3
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +4 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +130 -21
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +7 -7
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +43 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +30 -28
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +48 -31
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +26 -28
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +33 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +94 -10
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +10 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -14
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +17 -14
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +47 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +243 -38
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +36 -26
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/whisper-port.mjs +19 -10
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
- package/src/runtime/office/quality/assurance-structure-docx.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +13 -15
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/llm/cost.mjs +6 -1
- package/src/runtime/shared/llm/http-agent.mjs +313 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +452 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-surface.mjs +110 -36
- package/src/runtime/shared/tool-work-units.mjs +4 -0
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
- package/src/runtime/shared/worktrees/git.mjs +57 -0
- package/src/runtime/shared/worktrees/manager.mjs +205 -137
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +4 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +1 -1
- package/src/session-runtime/agent-task-sessions.mjs +410 -353
- package/src/session-runtime/boot/tools.mjs +2 -0
- package/src/session-runtime/bot-status.mjs +103 -103
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/discovery.mjs +159 -40
- package/src/session-runtime/conversation-import/service.mjs +9 -3
- package/src/session-runtime/handoff-attachments.mjs +1 -1
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +143 -116
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +281 -10
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +10 -1
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +108 -1066
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +54 -54
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +151 -136
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/tui/App.jsx +154 -164
- package/src/tui/app/app-view/floating-panel.jsx +3 -0
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +21 -10
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +25 -6
- package/src/tui/app/provider-setup-picker.mjs +20 -13
- package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +3 -2
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +47 -32
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1896 -1341
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
package/src/tui/App.jsx
CHANGED
|
@@ -16,13 +16,15 @@
|
|
|
16
16
|
* layout, which <Static> collapses. The terminal handles scrollback itself as
|
|
17
17
|
* the transcript column grows past the screen height.
|
|
18
18
|
*/
|
|
19
|
-
import { useState, useCallback, useEffect,
|
|
19
|
+
import { useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
20
20
|
import { useApp, useStdin, useStdout } from 'ink';
|
|
21
21
|
import { useSession } from './hooks/useSession.mjs';
|
|
22
22
|
import { pickFolder } from '../standalone/folder-dialog.mjs';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
23
|
+
import { useSlashPalette, useSlashPaletteState } from './app/use-slash-palette.mjs';
|
|
24
|
+
import { useBtwPanel } from './app/use-btw-panel.mjs';
|
|
25
|
+
import { useTuiReady } from './app/use-tui-ready.mjs';
|
|
26
|
+
import { useTranscriptSwapReset } from './app/use-transcript-swap-reset.mjs';
|
|
27
|
+
import { useContextPanelRefresh } from './app/use-context-panel-refresh.mjs';
|
|
26
28
|
import { shouldSupersedePanelEpoch, supersedePanelEpoch } from './app/panel-epoch.mjs';
|
|
27
29
|
import { createPanelSurface } from './app/panel-surface.mjs';
|
|
28
30
|
import { resolvePickerState } from './app/picker-index-mode.mjs';
|
|
@@ -50,7 +52,6 @@ import { usePromptQueueHistory } from './app/use-prompt-queue-history.mjs';
|
|
|
50
52
|
import { useMessageSelector } from './app/message-selector.mjs';
|
|
51
53
|
import { useTerminalChrome } from './app/use-terminal-chrome.mjs';
|
|
52
54
|
import { useTranscriptWindow } from './app/use-transcript-window.mjs';
|
|
53
|
-
import { transcriptSwapReturnsToTail } from './app/transcript-window.mjs';
|
|
54
55
|
import { terminalSize, projectNameFromPath } from './app/app-format.mjs';
|
|
55
56
|
import { useWorkflowTabCycle } from './app/use-workflow-tab-cycle.mjs';
|
|
56
57
|
import { createProjectPicker } from './app/project-picker.mjs';
|
|
@@ -59,67 +60,25 @@ import { useModelCatalogCache } from './app/use-model-catalog-cache.mjs';
|
|
|
59
60
|
import { useDisabledSkills } from './app/use-disabled-skills.mjs';
|
|
60
61
|
import { resolveOnboardingCompleted, useOnboardingStart } from './app/use-onboarding-start.mjs';
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// so a plain stdin.on('data') listener of ours never sees mouse bytes. Instead
|
|
73
|
-
// we subscribe to ink's 'input' events, which carry every parsed sequence —
|
|
74
|
-
// including raw SGR mouse sequences (\x1b[<…M/m), since ink's input-parser
|
|
75
|
-
// passes CSI sequences through untouched and emitInput forwards them verbatim.
|
|
76
|
-
const { isRawModeSupported, internal_eventEmitter: inkInput } = useStdin();
|
|
77
|
-
const { stdout } = useStdout();
|
|
78
|
-
const [exiting, setExiting] = useState(false);
|
|
79
|
-
// tuiReady stays false across the first render + commit. A setTimeout(0) in
|
|
80
|
-
// the first effect defers the flip until one event-loop poll has drained any
|
|
81
|
-
// keystrokes that the OS buffered during terminal setup / initial mount.
|
|
82
|
-
const [tuiReady, setTuiReady] = useState(false);
|
|
83
|
-
const exitRequestedRef = useRef(false);
|
|
84
|
-
const [resizeState, setResizeState] = useState(() => ({ ...terminalSize(stdout), epoch: 0 }));
|
|
85
|
-
const [panelTransitionEpoch, setPanelTransitionEpoch] = useState(0);
|
|
86
|
-
const [panelInkMaskEpoch, setPanelInkMaskEpoch] = useState(0);
|
|
87
|
-
// Keep a universal one-cell margin at the right edge: terminals may clip or
|
|
88
|
-
// wrap their final cell, including macOS terminals rendering rounded borders.
|
|
89
|
-
const rightSafetyColumns = 1;
|
|
90
|
-
const frameColumns = Math.max(1, resizeState.columns - rightSafetyColumns);
|
|
91
|
-
const {
|
|
92
|
-
scrollOffset,
|
|
93
|
-
setScrollOffset,
|
|
94
|
-
scrollPositionRef,
|
|
95
|
-
scrollTargetRef,
|
|
96
|
-
maxScrollRowsRef,
|
|
97
|
-
transcriptBottomSlackRowsRef,
|
|
98
|
-
transcriptAnchorRef,
|
|
99
|
-
transcriptAnchorDirtyRef,
|
|
100
|
-
transcriptGeomRef,
|
|
101
|
-
measuredRowsVersion,
|
|
102
|
-
setMeasuredRowsVersion,
|
|
103
|
-
followingRef,
|
|
104
|
-
lastItemsCountRef,
|
|
105
|
-
lastFirstItemIdRef,
|
|
106
|
-
} = useTranscriptScrollState();
|
|
63
|
+
// Statusline band height (bottom rows); mirrors the layout reserve.
|
|
64
|
+
const STATUSLINE_BAND_ROWS = 3;
|
|
65
|
+
|
|
66
|
+
// OWNING LAYER for the raw panel sinks (with app/panel-surface.mjs): the picker
|
|
67
|
+
// and the context/usage panel setters are handed to the surface exactly once
|
|
68
|
+
// and never leave this file. Every panel factory gets `surface` instead, so
|
|
69
|
+
// painting or delegating without a claim that proves ownership is not
|
|
70
|
+
// expressible elsewhere. The surface is stable across renders — the usage
|
|
71
|
+
// generation lives inside it.
|
|
72
|
+
function usePanelOwnership() {
|
|
107
73
|
// picker = null | { type, title, items, onSelect }
|
|
108
74
|
// Rendered as an option panel attached directly above the bottom prompt.
|
|
109
75
|
const pickerOpenedFromEnterRef = useRef(false);
|
|
110
76
|
const pickerOpenedFromEnterTimerRef = useRef(null);
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
const projectPickerRef = useRef(null);
|
|
117
|
-
// NOTE: the initial project-picker state CANNOT be built inside this
|
|
118
|
-
// useState initializer — it runs before projectPickerRef is populated
|
|
119
|
-
// (createProjectPicker below), so buildProjectPickerState would deref null.
|
|
120
|
-
// The first-mount build happens as a render-phase update right after the
|
|
121
|
-
// factory is instantiated (see initialPickerBuiltRef below), which React
|
|
122
|
-
// applies before the first commit — no picker-less flash frame.
|
|
77
|
+
// NOTE: the initial project-picker state is NOT built in this useState
|
|
78
|
+
// initializer — the project-picker factory does not exist yet. The
|
|
79
|
+
// first-mount build happens as a render-phase update right after the factory
|
|
80
|
+
// is instantiated (useProjectPickerBoot), which React applies before the
|
|
81
|
+
// first commit — no picker-less flash frame.
|
|
123
82
|
const [picker, setPickerState] = useState(null);
|
|
124
83
|
// Live handle to the current picker state so async callbacks (e.g. the MCP
|
|
125
84
|
// toggle settle guard in extension-pickers) read the picker actually on
|
|
@@ -153,52 +112,41 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
153
112
|
livePickerRef.current = picker;
|
|
154
113
|
const [contextPanel, setContextPanel] = useState(null);
|
|
155
114
|
const [usagePanel, setUsagePanel] = useState(null);
|
|
156
|
-
const [btwPanel, setBtwPanel] = useState(null);
|
|
157
|
-
const btwRef = useRef(null);
|
|
158
|
-
const sessionIdRef = useRef('');
|
|
159
|
-
sessionIdRef.current = state.sessionId || '';
|
|
160
|
-
if (!btwRef.current) {
|
|
161
|
-
btwRef.current = createBtwController({
|
|
162
|
-
getStore: () => store,
|
|
163
|
-
setPanel: setBtwPanel,
|
|
164
|
-
getSessionId: () => sessionIdRef.current,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
const btw = btwRef.current;
|
|
168
|
-
// A side answer belongs to the conversation it was asked in: drop it on a
|
|
169
|
-
// session change (new/resume/clear/switch) and cancel it on unmount.
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
btw.sync();
|
|
172
|
-
}, [btw, state.sessionId]);
|
|
173
|
-
useEffect(() => () => btw.dispose(), [btw]);
|
|
174
|
-
// OWNING LAYER (app/panel-surface.mjs): the three sinks above are handed over
|
|
175
|
-
// exactly once and never leave that module. Every panel factory below gets
|
|
176
|
-
// `surface` instead, so painting or delegating without a claim that proves
|
|
177
|
-
// ownership is not expressible outside this file. Stable across renders — the
|
|
178
|
-
// usage generation lives inside it.
|
|
179
115
|
const surfaceRef = useRef(null);
|
|
180
116
|
if (!surfaceRef.current) {
|
|
181
117
|
surfaceRef.current = createPanelSurface({ setPicker, setContextPanel, setUsagePanel });
|
|
182
118
|
}
|
|
183
119
|
const surface = surfaceRef.current;
|
|
184
|
-
// Cache of the last computed heavy settings-picker status objects (MCP,
|
|
185
|
-
// plugins, skills, channel provider). ←/→ cycle/toggle handlers in
|
|
186
|
-
// openSettingsPicker() pass { light: true } to reuse this cache instead of
|
|
187
|
-
// re-querying these heavy getters on every keystroke; only a full open
|
|
188
|
-
// (initial /config or Esc-return) recomputes them.
|
|
189
|
-
const settingsHeavyCacheRef = useRef(null);
|
|
190
|
-
// Settings build generation: every open/refresh takes a ticket and Esc bumps
|
|
191
|
-
// it, so a slow (daemon) snapshot from a superseded build cannot re-open the
|
|
192
|
-
// panel after it was closed.
|
|
193
|
-
const settingsRequestRef = useRef(0);
|
|
194
120
|
const closeUsagePanel = useCallback(() => surface.closeUsage(), [surface]);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
121
|
+
return {
|
|
122
|
+
picker,
|
|
123
|
+
setPicker,
|
|
124
|
+
livePickerRef,
|
|
125
|
+
pickerOpenedFromEnterRef,
|
|
126
|
+
pickerOpenedFromEnterTimerRef,
|
|
127
|
+
contextPanel,
|
|
128
|
+
usagePanel,
|
|
129
|
+
surface,
|
|
130
|
+
closeUsagePanel,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Project-picker cluster plus its first-mount entry. The factory's
|
|
135
|
+
// onSelect/onKey/onCancel closures resolve the builders at call time; the
|
|
136
|
+
// initial open goes through projectPickerRef so it reaches the latest ones.
|
|
137
|
+
function useProjectPickerBoot({
|
|
138
|
+
state,
|
|
139
|
+
store,
|
|
140
|
+
surface,
|
|
141
|
+
setProviderPrompt,
|
|
142
|
+
setSettingsPrompt,
|
|
143
|
+
closeUsagePanel,
|
|
144
|
+
setPicker,
|
|
145
|
+
livePickerRef,
|
|
146
|
+
forceOnboarding,
|
|
147
|
+
onboardingCompleted,
|
|
148
|
+
}) {
|
|
149
|
+
const projectPickerRef = useRef(null);
|
|
202
150
|
const projectPicker = createProjectPicker({
|
|
203
151
|
state,
|
|
204
152
|
store,
|
|
@@ -221,11 +169,96 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
221
169
|
setPicker(projectPicker.buildProjectPickerState({ initialEntry: true, loading: true }));
|
|
222
170
|
}
|
|
223
171
|
}
|
|
172
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(store): a new store re-runs the pending initial open
|
|
224
173
|
useEffect(() => {
|
|
225
174
|
if (livePickerRef.current?._projectInitialPending !== true) return;
|
|
226
175
|
void projectPickerRef.current?.openProjectPicker({ initialEntry: true });
|
|
227
|
-
}, [store]);
|
|
228
|
-
|
|
176
|
+
}, [store, livePickerRef]);
|
|
177
|
+
return projectPicker;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function App({ store, initialStatusLine = '', forceOnboarding = false, onboardingCompleted = undefined }) {
|
|
181
|
+
const state = useSession(store);
|
|
182
|
+
const [toolOutputExpanded, setToolOutputExpanded] = useState(false);
|
|
183
|
+
// True for the entire first-run onboarding wizard (every step + nested depth)
|
|
184
|
+
// so the welcome banner stays reserved and the layout doesn't jump when the
|
|
185
|
+
// step pickers mount. Cleared on finish/cancel.
|
|
186
|
+
const [onboardingActive, setOnboardingActive] = useState(false);
|
|
187
|
+
const { exit } = useApp();
|
|
188
|
+
// internal_eventEmitter is ink's parsed-input bus. ink 7 consumes stdin via
|
|
189
|
+
// the 'readable' event + stdin.read() (see ink's App.js), draining the buffer
|
|
190
|
+
// so a plain stdin.on('data') listener of ours never sees mouse bytes. Instead
|
|
191
|
+
// we subscribe to ink's 'input' events, which carry every parsed sequence —
|
|
192
|
+
// including raw SGR mouse sequences (\x1b[<…M/m), since ink's input-parser
|
|
193
|
+
// passes CSI sequences through untouched and emitInput forwards them verbatim.
|
|
194
|
+
const { isRawModeSupported, internal_eventEmitter: inkInput } = useStdin();
|
|
195
|
+
const { stdout } = useStdout();
|
|
196
|
+
const [exiting, setExiting] = useState(false);
|
|
197
|
+
const exitRequestedRef = useRef(false);
|
|
198
|
+
const [resizeState, setResizeState] = useState(() => ({ ...terminalSize(stdout), epoch: 0 }));
|
|
199
|
+
const [panelTransitionEpoch, setPanelTransitionEpoch] = useState(0);
|
|
200
|
+
const [panelInkMaskEpoch, setPanelInkMaskEpoch] = useState(0);
|
|
201
|
+
// Keep a universal one-cell margin at the right edge: terminals may clip or
|
|
202
|
+
// wrap their final cell, including macOS terminals rendering rounded borders.
|
|
203
|
+
const rightSafetyColumns = 1;
|
|
204
|
+
const frameColumns = Math.max(1, resizeState.columns - rightSafetyColumns);
|
|
205
|
+
const {
|
|
206
|
+
scrollOffset,
|
|
207
|
+
setScrollOffset,
|
|
208
|
+
scrollPositionRef,
|
|
209
|
+
scrollTargetRef,
|
|
210
|
+
maxScrollRowsRef,
|
|
211
|
+
transcriptBottomSlackRowsRef,
|
|
212
|
+
transcriptAnchorRef,
|
|
213
|
+
transcriptAnchorDirtyRef,
|
|
214
|
+
transcriptGeomRef,
|
|
215
|
+
measuredRowsVersion,
|
|
216
|
+
setMeasuredRowsVersion,
|
|
217
|
+
followingRef,
|
|
218
|
+
lastItemsCountRef,
|
|
219
|
+
lastFirstItemIdRef,
|
|
220
|
+
} = useTranscriptScrollState();
|
|
221
|
+
// Picker + context/usage panels and the surface that owns them (owning layer).
|
|
222
|
+
const {
|
|
223
|
+
picker,
|
|
224
|
+
setPicker,
|
|
225
|
+
livePickerRef,
|
|
226
|
+
pickerOpenedFromEnterRef,
|
|
227
|
+
pickerOpenedFromEnterTimerRef,
|
|
228
|
+
contextPanel,
|
|
229
|
+
usagePanel,
|
|
230
|
+
surface,
|
|
231
|
+
closeUsagePanel,
|
|
232
|
+
} = usePanelOwnership();
|
|
233
|
+
// /btw side-question panel + controller lifecycle: app/use-btw-panel.mjs.
|
|
234
|
+
const { btwPanel, btw } = useBtwPanel({ store, sessionId: state.sessionId });
|
|
235
|
+
// Cache of the last computed heavy settings-picker status objects (MCP,
|
|
236
|
+
// plugins, skills, channel provider). ←/→ cycle/toggle handlers in
|
|
237
|
+
// openSettingsPicker() pass { light: true } to reuse this cache instead of
|
|
238
|
+
// re-querying these heavy getters on every keystroke; only a full open
|
|
239
|
+
// (initial /config or Esc-return) recomputes them.
|
|
240
|
+
const settingsHeavyCacheRef = useRef(null);
|
|
241
|
+
// Settings build generation: every open/refresh takes a ticket and Esc bumps
|
|
242
|
+
// it, so a slow (daemon) snapshot from a superseded build cannot re-open the
|
|
243
|
+
// panel after it was closed.
|
|
244
|
+
const settingsRequestRef = useRef(0);
|
|
245
|
+
const [providerPrompt, setProviderPrompt] = useState(null);
|
|
246
|
+
const oauthSubmitRef = useRef(false);
|
|
247
|
+
const [settingsPrompt, setSettingsPrompt] = useState(null);
|
|
248
|
+
// Project-picker cluster, instantiated now that the surface + every prompt
|
|
249
|
+
// setter and the usage-panel closer exist, plus its first-mount entry.
|
|
250
|
+
const { registerProject, enterProject, openProjectPicker } = useProjectPickerBoot({
|
|
251
|
+
state,
|
|
252
|
+
store,
|
|
253
|
+
surface,
|
|
254
|
+
setProviderPrompt,
|
|
255
|
+
setSettingsPrompt,
|
|
256
|
+
closeUsagePanel,
|
|
257
|
+
setPicker,
|
|
258
|
+
livePickerRef,
|
|
259
|
+
forceOnboarding,
|
|
260
|
+
onboardingCompleted,
|
|
261
|
+
});
|
|
229
262
|
// Disabled-Skill set (async load + persist on change):
|
|
230
263
|
// app/use-disabled-skills.mjs.
|
|
231
264
|
const { disabledSkills, setDisabledSkills } = useDisabledSkills({ store });
|
|
@@ -287,11 +320,10 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
287
320
|
welcomePromptHintVisibleRef,
|
|
288
321
|
dismissWelcomePromptHint,
|
|
289
322
|
} = useWelcomePromptHint({ store, state, toastErrorSignature });
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
323
|
+
// Slash palette index / dismissal state: app/use-slash-palette.mjs.
|
|
324
|
+
const { slashIndex, setSlashIndex, slashDismissedFor, setSlashDismissedFor, slashPaletteRef } =
|
|
325
|
+
useSlashPaletteState();
|
|
293
326
|
const scrollFocusRef = useRef({});
|
|
294
|
-
const onboardingStartedRef = useRef(false);
|
|
295
327
|
const onboardingRef = useRef({
|
|
296
328
|
defaultRoute: null,
|
|
297
329
|
webSearchRoute: null,
|
|
@@ -365,7 +397,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
365
397
|
selectionTextRef,
|
|
366
398
|
lastClickRef,
|
|
367
399
|
} = useInteractionRefs();
|
|
368
|
-
const STATUSLINE_BAND_ROWS = 3;
|
|
369
400
|
const promptContentColumns = Math.max(1, frameColumns - 4);
|
|
370
401
|
// Prompt + text-entry layout rows: app/use-prompt-layout-rows.mjs.
|
|
371
402
|
const syncPromptLayoutRows = usePromptLayoutRows({
|
|
@@ -377,14 +408,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
377
408
|
setTextEntryLayoutRows,
|
|
378
409
|
});
|
|
379
410
|
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
// PromptInput is still disabled) any keystrokes queued during boot/first
|
|
383
|
-
// render. After the tick, enable the input — new keystrokes land normally.
|
|
384
|
-
useEffect(() => {
|
|
385
|
-
const timer = setTimeout(() => setTuiReady(true), 0);
|
|
386
|
-
return () => clearTimeout(timer);
|
|
387
|
-
}, []);
|
|
411
|
+
// Post-mount input gate: app/use-tui-ready.mjs.
|
|
412
|
+
const tuiReady = useTuiReady();
|
|
388
413
|
|
|
389
414
|
// Resize debounce + extended-keyboard enable: app/use-terminal-chrome.mjs.
|
|
390
415
|
useTerminalChrome({ stdout, isRawModeSupported, setResizeState });
|
|
@@ -471,23 +496,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
471
496
|
clearStitchBuffer,
|
|
472
497
|
});
|
|
473
498
|
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
// only prompt submit, session reset, or an explicit return to bottom arms
|
|
477
|
-
// follow. Pure streaming height growth is handled in the row-delta effect.
|
|
478
|
-
useLayoutEffect(() => {
|
|
479
|
-
const count = state.items.length;
|
|
480
|
-
const previousCount = lastItemsCountRef.current;
|
|
481
|
-
lastItemsCountRef.current = count;
|
|
482
|
-
const firstId = count > 0 ? (state.items[0]?.id ?? null) : null;
|
|
483
|
-
const previousFirstId = lastFirstItemIdRef.current;
|
|
484
|
-
lastFirstItemIdRef.current = firstId;
|
|
485
|
-
// A bulk swap (compaction included) is an explicit return to the tail: the
|
|
486
|
-
// rows the reader was anchored to no longer exist.
|
|
487
|
-
if (transcriptSwapReturnsToTail({ count, previousCount, firstId, previousFirstId })) {
|
|
488
|
-
resetTranscriptScroll();
|
|
489
|
-
}
|
|
490
|
-
}, [state.items, resetTranscriptScroll]);
|
|
499
|
+
// Bulk transcript swap → back to the tail: app/use-transcript-swap-reset.mjs.
|
|
500
|
+
useTranscriptSwapReset({ items: state.items, lastItemsCountRef, lastFirstItemIdRef, resetTranscriptScroll });
|
|
491
501
|
|
|
492
502
|
// Exit + queued-restore + prompt history: app/use-prompt-queue-history.mjs.
|
|
493
503
|
const { requestExit, restoreQueuedToPrompt, recentPromptHistory, resetPromptHistoryNav } = usePromptQueueHistory({
|
|
@@ -594,31 +604,14 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
594
604
|
closeUsagePanel,
|
|
595
605
|
});
|
|
596
606
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}, [
|
|
600
|
-
contextPanel?.kind,
|
|
601
|
-
state.stats,
|
|
602
|
-
state.contextWindow,
|
|
603
|
-
state.rawContextWindow,
|
|
604
|
-
state.sessionId,
|
|
605
|
-
state.toolMode,
|
|
606
|
-
state.agentWorkers,
|
|
607
|
-
state.agentJobs,
|
|
608
|
-
state.provider,
|
|
609
|
-
state.model,
|
|
610
|
-
state.effort,
|
|
611
|
-
state.fast,
|
|
612
|
-
state.cwd,
|
|
613
|
-
state.clientHostPid,
|
|
614
|
-
]);
|
|
607
|
+
// Open /context breakdown stays current: app/use-context-panel-refresh.mjs.
|
|
608
|
+
useContextPanelRefresh({ contextPanelKind: contextPanel?.kind, state, openContextPicker });
|
|
615
609
|
|
|
616
610
|
// First-run onboarding wizard launch: app/use-onboarding-start.mjs.
|
|
617
611
|
useOnboardingStart({
|
|
618
612
|
store,
|
|
619
613
|
forceOnboarding,
|
|
620
614
|
onboardingCompleted,
|
|
621
|
-
onboardingStartedRef,
|
|
622
615
|
setOnboardingActive,
|
|
623
616
|
openOnboardingAuthStep,
|
|
624
617
|
});
|
|
@@ -652,8 +645,14 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
652
645
|
pastedTextsRef,
|
|
653
646
|
});
|
|
654
647
|
|
|
655
|
-
|
|
656
|
-
const { activeSlashQuery, slashCommands, slashPaletteOpen, paletteKind } =
|
|
648
|
+
// Slash / `$skill` palette for this render: app/use-slash-palette.mjs.
|
|
649
|
+
const { activeSlashQuery, slashCommands, slashPaletteOpen, paletteKind } = useSlashPalette({
|
|
650
|
+
store,
|
|
651
|
+
slashPaletteRef,
|
|
652
|
+
setSlashIndex,
|
|
653
|
+
slashDismissedFor,
|
|
654
|
+
promptDraft,
|
|
655
|
+
promptCaret,
|
|
657
656
|
providerPrompt,
|
|
658
657
|
settingsPrompt,
|
|
659
658
|
toolApproval,
|
|
@@ -662,12 +661,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
662
661
|
picker,
|
|
663
662
|
exiting,
|
|
664
663
|
commandBusy: state.commandBusy,
|
|
665
|
-
promptDraft,
|
|
666
|
-
slashDismissedFor,
|
|
667
|
-
skills: mentionSkills,
|
|
668
|
-
promptCaret,
|
|
669
664
|
});
|
|
670
|
-
slashPaletteRef.current = { open: slashPaletteOpen, count: slashCommands.length };
|
|
671
665
|
scrollFocusRef.current = {
|
|
672
666
|
slashPaletteOpen,
|
|
673
667
|
picker: !!picker,
|
|
@@ -679,10 +673,6 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
679
673
|
settingsPrompt: !!settingsPrompt,
|
|
680
674
|
};
|
|
681
675
|
|
|
682
|
-
useEffect(() => {
|
|
683
|
-
setSlashIndex((index) => Math.min(index, Math.max(0, slashCommands.length - 1)));
|
|
684
|
-
}, [slashCommands.length, activeSlashQuery]);
|
|
685
|
-
|
|
686
676
|
// Draft/slash flow (draft sync, prompt cancels, palette accept/cancel):
|
|
687
677
|
// app/use-prompt-draft-flow.mjs.
|
|
688
678
|
const {
|
|
@@ -124,6 +124,7 @@ function renderPicker({
|
|
|
124
124
|
onHighlight={picker.onHighlight}
|
|
125
125
|
title={picker.title}
|
|
126
126
|
description={picker.description}
|
|
127
|
+
detail={picker.detail}
|
|
127
128
|
footer={picker.footer}
|
|
128
129
|
footerGapRows={picker.footerGapRows}
|
|
129
130
|
help={picker.help}
|
|
@@ -159,6 +160,7 @@ export function renderFloatingPanel(ctx) {
|
|
|
159
160
|
slashCommands,
|
|
160
161
|
slashIndex,
|
|
161
162
|
slashPaletteOpen,
|
|
163
|
+
textEntryUrlMaxRows,
|
|
162
164
|
toolApproval,
|
|
163
165
|
usagePanel,
|
|
164
166
|
btwPanel,
|
|
@@ -212,6 +214,7 @@ export function renderFloatingPanel(ctx) {
|
|
|
212
214
|
title={title}
|
|
213
215
|
hint={hint}
|
|
214
216
|
detail={providerPrompt.detail || ''}
|
|
217
|
+
urlMaxRows={textEntryUrlMaxRows}
|
|
215
218
|
mask={providerPrompt.kind === 'api-key' || providerPrompt.kind === 'openai-usage-session'}
|
|
216
219
|
columns={frameColumns}
|
|
217
220
|
// Restored after a REJECTED daemon save so the entered secret
|
|
@@ -17,6 +17,7 @@ export function providerPromptFields(providerPrompt) {
|
|
|
17
17
|
providerPrompt.envName ? `Env: ${providerPrompt.envName}` : '',
|
|
18
18
|
providerPrompt.source ? `Current: ${providerPrompt.source}` : '',
|
|
19
19
|
providerPrompt.keyUrl ? `Get a key: ${providerPrompt.keyUrl}` : '',
|
|
20
|
+
providerPrompt.hint || '',
|
|
20
21
|
'Stored in the OS keychain.',
|
|
21
22
|
]
|
|
22
23
|
.filter(Boolean)
|
|
@@ -71,11 +71,21 @@ function nativeClipboardCommand(text) {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
if (process.platform === 'darwin') return { cmd: 'pbcopy', args: [], input: value };
|
|
74
|
+
// WAYLAND_DISPLAY is the variable a Wayland client connects by; set means a
|
|
75
|
+
// Wayland session. A helper that is not installed fails with ENOENT, which
|
|
76
|
+
// clipboardHelperError turns into the install hint.
|
|
74
77
|
if (process.env.WAYLAND_DISPLAY) return { cmd: 'wl-copy', args: [], input: value };
|
|
75
78
|
return { cmd: 'xclip', args: ['-selection', 'clipboard'], input: value };
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Resolves 'copied' once a native helper confirmed the write, or 'terminal' when
|
|
83
|
+
* only OSC 52 was emitted (the terminal may ignore it, so it is unconfirmed).
|
|
84
|
+
* Rejects when no path was available. By default a successful OSC 52 resolves
|
|
85
|
+
* at once without awaiting the helper; `confirm` waits for the helper so the
|
|
86
|
+
* caller can tell which of the two happened.
|
|
87
|
+
*/
|
|
88
|
+
export function copyToClipboard(text, { confirm = false } = {}) {
|
|
79
89
|
const value = String(text ?? '');
|
|
80
90
|
const wroteOsc52 = writeOsc52Clipboard(value);
|
|
81
91
|
// When OSC 52 already wrote the clipboard, fire-and-forget the native helper
|
|
@@ -84,24 +94,25 @@ export function copyToClipboard(text) {
|
|
|
84
94
|
// skipped (payload too large), the native helper is the ONLY writer, so we
|
|
85
95
|
// must await its exit to report real success/failure instead of a false
|
|
86
96
|
// "copied" hint.
|
|
97
|
+
const waitForHelper = !wroteOsc52 || confirm;
|
|
87
98
|
return new Promise((resolve, reject) => {
|
|
99
|
+
// A helper failure only matters when OSC 52 did not cover us.
|
|
100
|
+
const helperFailed = (error) => (wroteOsc52 ? resolve('terminal') : reject(error));
|
|
88
101
|
const { cmd, args, input } = nativeClipboardCommand(value);
|
|
89
102
|
let child;
|
|
90
103
|
try {
|
|
91
104
|
child = spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore'], windowsHide: true });
|
|
92
105
|
} catch (e) {
|
|
93
|
-
|
|
94
|
-
else reject(clipboardHelperError(cmd, e));
|
|
106
|
+
helperFailed(clipboardHelperError(cmd, e));
|
|
95
107
|
return;
|
|
96
108
|
}
|
|
97
109
|
child.on('error', (e) => {
|
|
98
|
-
|
|
99
|
-
if (!wroteOsc52) reject(clipboardHelperError(cmd, e));
|
|
110
|
+
if (waitForHelper) helperFailed(clipboardHelperError(cmd, e));
|
|
100
111
|
});
|
|
101
|
-
if (
|
|
112
|
+
if (waitForHelper) {
|
|
102
113
|
child.on('close', (code) => {
|
|
103
|
-
if (code === 0) resolve();
|
|
104
|
-
else
|
|
114
|
+
if (code === 0) resolve('copied');
|
|
115
|
+
else helperFailed(new Error(`${cmd} exited with code ${code}`));
|
|
105
116
|
});
|
|
106
117
|
}
|
|
107
118
|
child.stdin.on('error', () => {
|
|
@@ -109,9 +120,9 @@ export function copyToClipboard(text) {
|
|
|
109
120
|
});
|
|
110
121
|
child.stdin.end(input);
|
|
111
122
|
// OSC 52 covered us: resolve now, don't await the child's exit.
|
|
112
|
-
if (
|
|
123
|
+
if (!waitForHelper) {
|
|
113
124
|
child.unref?.();
|
|
114
|
-
resolve();
|
|
125
|
+
resolve('terminal');
|
|
115
126
|
}
|
|
116
127
|
});
|
|
117
128
|
}
|
|
@@ -26,6 +26,6 @@ export function usePromptHistoryNavigation({
|
|
|
26
26
|
if (outcome.draftChanged) promptHistoryDraftChangeRef.current = true;
|
|
27
27
|
return outcome.value;
|
|
28
28
|
},
|
|
29
|
-
[recentPromptHistory, resetPromptHistoryNav, clearPromptHint]
|
|
29
|
+
[promptHistoryNavRef, promptHistoryDraftChangeRef, recentPromptHistory, resetPromptHistoryNav, clearPromptHint]
|
|
30
30
|
);
|
|
31
31
|
}
|
|
@@ -47,6 +47,9 @@ export function usePromptInterrupt({
|
|
|
47
47
|
[store, promptValueRef, restoreAttachments, clearPromptHint, syncPromptLayoutRows, setPromptDraftOverride]
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
+
// `busy` is the trigger, not a read: a parked restore re-checks the store
|
|
51
|
+
// each time the turn's busy flag flips.
|
|
52
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(busy): busy-flip trigger for the parked restore
|
|
50
53
|
useEffect(() => {
|
|
51
54
|
const pending = pendingRestoreRef.current;
|
|
52
55
|
if (!pending) return;
|
|
@@ -36,7 +36,9 @@ function saveApiKey(ctx, target, commandText) {
|
|
|
36
36
|
requiredNotice: `API key is required for ${target.providerId}`,
|
|
37
37
|
busyNotice: 'API key is already being saved',
|
|
38
38
|
method: 'saveProviderApiKey',
|
|
39
|
-
args:
|
|
39
|
+
args: target.authMode
|
|
40
|
+
? [target.providerId, commandText, { authMode: target.authMode }]
|
|
41
|
+
: [target.providerId, commandText],
|
|
40
42
|
onSaved: () => ctx.clearModelCaches('all'),
|
|
41
43
|
failurePrefix: 'api key save failed',
|
|
42
44
|
});
|
|
@@ -6,9 +6,13 @@ import { openInBrowser } from '../../../runtime/shared/open-url.mjs';
|
|
|
6
6
|
import { providerDetailText } from '../app-format.mjs';
|
|
7
7
|
import { forgetProviderAuth, keyConsoleUrl, providerStatusFooter } from './provider-items.mjs';
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const VERTEX_EXPRESS_HINT =
|
|
10
|
+
'Express mode API keys serve Gemini models on the global location only. Claude and MaaS models need a service-account key or the gcloud login.';
|
|
11
|
+
|
|
12
|
+
const setApiKeyPrompt = (flow, providerItem, extra = {}) => {
|
|
10
13
|
if (!flow.ownsSurface()) return;
|
|
11
14
|
flow.setProviderPrompt({
|
|
15
|
+
...extra,
|
|
12
16
|
kind: 'api-key',
|
|
13
17
|
providerId: providerItem._providerId,
|
|
14
18
|
label: providerItem._providerName,
|
|
@@ -51,6 +55,28 @@ function apiKeyActions(providerItem, provider) {
|
|
|
51
55
|
return { apiActions, keyUrl };
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
// Vertex AI takes either a service-account key or an express-mode API key.
|
|
59
|
+
function openVertexModePicker(flow, providerItem) {
|
|
60
|
+
flow.paint({
|
|
61
|
+
title: `Provider · ${providerItem._providerName}`,
|
|
62
|
+
description: 'Choose the Vertex AI key type.',
|
|
63
|
+
help: '↑/↓ Select · Enter Choose · Esc Back',
|
|
64
|
+
indexMode: 'always',
|
|
65
|
+
labelWidth: 22,
|
|
66
|
+
pickerKey: `providers-vertex-mode:${providerItem.value}`,
|
|
67
|
+
initialIndex: 0,
|
|
68
|
+
items: [
|
|
69
|
+
{ value: 'service-account', label: 'Service account key', description: 'service-account JSON' },
|
|
70
|
+
{ value: 'api-key', label: 'API key (express mode)', description: 'Gemini only · global location' },
|
|
71
|
+
],
|
|
72
|
+
onSelect: (mode) => {
|
|
73
|
+
flow.releaseSurface();
|
|
74
|
+
setApiKeyPrompt(flow, providerItem, mode === 'api-key' ? { authMode: 'api-key', hint: VERTEX_EXPRESS_HINT } : {});
|
|
75
|
+
},
|
|
76
|
+
onCancel: () => openApiProviderActions(flow, providerItem),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
54
80
|
export function openApiProviderActions(flow, providerItem) {
|
|
55
81
|
// Reached from acks (a failed forget-key) as well as key presses: prove
|
|
56
82
|
// ownership at the sink so every caller is covered.
|
|
@@ -69,6 +95,10 @@ export function openApiProviderActions(flow, providerItem) {
|
|
|
69
95
|
initialIndex: 0,
|
|
70
96
|
items: apiActions,
|
|
71
97
|
onSelect: (_detailValue, detail) => {
|
|
98
|
+
if (detail._action === 'set-key' && providerItem._providerId === 'vertex-ai') {
|
|
99
|
+
openVertexModePicker(flow, providerItem);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
72
102
|
flow.releaseSurface();
|
|
73
103
|
if (detail._action === 'set-key') {
|
|
74
104
|
setApiKeyPrompt(flow, providerItem);
|