mixdog 1.1.1 → 1.2.0
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 +1 -0
- package/package.json +2 -2
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/release-paths.mjs +39 -2
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/references/actions.md +5 -2
- package/src/defaults/skills/visualize/SKILL.md +157 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/headless-role.mjs +2 -2
- package/src/help.mjs +2 -0
- package/src/lib/rules-builder.cjs +0 -23
- package/src/rules/lead/LEAD.md +2 -2
- package/src/rules/shared/70-delivery.md +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -1
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +47 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +51 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +25 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +348 -122
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +12 -4
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +67 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +521 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +15 -1
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +12 -11
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +11 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +11 -8
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +18 -11
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +17 -7
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +29 -19
- package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +23 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +28 -8
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +140 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +31 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -66
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -10
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/index.mjs +1 -5
- package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
- package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
- package/src/runtime/channels/lib/worker-main.mjs +4 -34
- package/src/runtime/shared/atomic-file.mjs +8 -2
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -118
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +156 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -2
- package/src/runtime/shared/provider-api-key.mjs +21 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/semver.d.mts +1 -0
- package/src/runtime/shared/semver.mjs +57 -0
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-surface.mjs +9 -2
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +161 -11
- package/src/runtime/shared/update-checker.mjs +2 -31
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/boot/apis.mjs +35 -0
- package/src/session-runtime/boot/begin.mjs +0 -2
- package/src/session-runtime/boot/config.mjs +2 -0
- package/src/session-runtime/boot/facade.mjs +20 -0
- package/src/session-runtime/boot/infrastructure.mjs +1 -0
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +9 -4
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +11 -1
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +11 -6
- package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -3
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/notification-bus.mjs +9 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/resource-extension-sync.mjs +30 -7
- package/src/session-runtime/resource-mcp-api.mjs +27 -10
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-bootstrap.mjs +0 -4
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-paths.mjs +0 -1
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-profile-api.mjs +27 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +11 -4
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +31 -3
- package/src/session-runtime/starter-guide-state.mjs +62 -0
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +13 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/standalone/channel-binding.mjs +1 -2
- package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-channels-loader.mjs +2 -3
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +4 -1
- package/src/standalone/daemon.mjs +139 -5
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +13 -78
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +17 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +11 -3
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +35 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/dist/index.mjs +332 -36
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +12 -8
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +18 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +2 -0
- package/src/tui/session/session-api/shared.mjs +10 -6
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +15 -1
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +39 -10
- package/src/runtime/channels/tool-defs.mjs +0 -17
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback client for the desktop app's visualize bridge. The desktop app
|
|
3
|
+
* advertises { port, token } through `visualize-bridge.json`; this runtime
|
|
4
|
+
* half is the sync availability probe and the executor behind tool calls.
|
|
5
|
+
* Request: POST /command { sessionId, cwd, action, input }.
|
|
6
|
+
* Response: { ok:true, text, tabs?, path? } | { ok:false, error }.
|
|
7
|
+
*/
|
|
8
|
+
import { bridgeDiscoveryChanged, readBridgeDiscovery, readBridgeDiscoveryDetail } from '../bridge-discovery.mjs';
|
|
9
|
+
|
|
10
|
+
const DISCOVERY_FILE = 'visualize-bridge.json';
|
|
11
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
12
|
+
/** `export` renders a page and writes a file; the desktop host's own deadline
|
|
13
|
+
* for it (45 s) is shorter, so a timeout is reported by the host, which then
|
|
14
|
+
* writes nothing, rather than raced by this client. */
|
|
15
|
+
const EXPORT_REQUEST_TIMEOUT_MS = 60_000;
|
|
16
|
+
const MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
17
|
+
const ACTIONS = new Set(['open', 'close', 'annotate', 'capture', 'export', 'check', 'inspect']);
|
|
18
|
+
const INLINE_KINDS = ['mermaid', 'svg', 'chart', 'html', 'markdown'];
|
|
19
|
+
const MAX_INLINE_BYTES = 512 * 1024;
|
|
20
|
+
const BRIDGE_UNAVAILABLE_MESSAGE = 'visualize is unavailable; the Mixdog desktop app must be running';
|
|
21
|
+
|
|
22
|
+
export function visualizeBridgeAvailableSync() {
|
|
23
|
+
return readBridgeDiscovery(DISCOVERY_FILE) !== null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function toolError(message) {
|
|
27
|
+
return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function unavailableMessage() {
|
|
31
|
+
const { reason } = readBridgeDiscoveryDetail(DISCOVERY_FILE);
|
|
32
|
+
return reason && reason !== 'missing'
|
|
33
|
+
? `${BRIDGE_UNAVAILABLE_MESSAGE} (discovery ${reason})`
|
|
34
|
+
: BRIDGE_UNAVAILABLE_MESSAGE;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function validateVisualizeArgs(args) {
|
|
38
|
+
if (!args || typeof args !== 'object' || Array.isArray(args)) {
|
|
39
|
+
return { ok: false, error: 'visualize arguments must be an object' };
|
|
40
|
+
}
|
|
41
|
+
if (!ACTIONS.has(args.action)) return { ok: false, error: 'action must be open, close, annotate, capture, export, check, or inspect' };
|
|
42
|
+
const input = args.input ?? {};
|
|
43
|
+
if (typeof input !== 'object' || Array.isArray(input)) return { ok: false, error: 'input must be an object' };
|
|
44
|
+
if (args.action === 'open' && input.content != null) {
|
|
45
|
+
const content = input.content;
|
|
46
|
+
if (input.targets != null) return { ok: false, error: 'open takes either input.targets or input.content, not both' };
|
|
47
|
+
if (typeof content !== 'object' || Array.isArray(content)) {
|
|
48
|
+
return { ok: false, error: 'open input.content must be { kind, source, title? }' };
|
|
49
|
+
}
|
|
50
|
+
if (!INLINE_KINDS.includes(content.kind)) {
|
|
51
|
+
return { ok: false, error: `open input.content.kind must be one of ${INLINE_KINDS.join(', ')}` };
|
|
52
|
+
}
|
|
53
|
+
if (typeof content.source !== 'string' || !content.source.trim()) {
|
|
54
|
+
return { ok: false, error: 'open input.content.source must be a non-empty string' };
|
|
55
|
+
}
|
|
56
|
+
if (Buffer.byteLength(content.source) > MAX_INLINE_BYTES) {
|
|
57
|
+
return { ok: false, error: `open input.content.source must be at most ${MAX_INLINE_BYTES / 1024} KiB` };
|
|
58
|
+
}
|
|
59
|
+
if (content.title != null && typeof content.title !== 'string') {
|
|
60
|
+
return { ok: false, error: 'open input.content.title must be a string' };
|
|
61
|
+
}
|
|
62
|
+
if (input.version != null && !(Number.isInteger(input.version) && input.version >= 1)) {
|
|
63
|
+
return { ok: false, error: 'open input.version must be a positive integer (a kept version number)' };
|
|
64
|
+
}
|
|
65
|
+
} else if (args.action === 'open') {
|
|
66
|
+
const targets = input.targets;
|
|
67
|
+
if (
|
|
68
|
+
!Array.isArray(targets) ||
|
|
69
|
+
targets.length < 1 ||
|
|
70
|
+
targets.length > 3 ||
|
|
71
|
+
targets.some((target) => typeof target !== 'string' || !target.trim())
|
|
72
|
+
) {
|
|
73
|
+
return { ok: false, error: 'open requires input.targets: 1-3 non-empty strings' };
|
|
74
|
+
}
|
|
75
|
+
if (targets.some((target) => /^https?:\/\//i.test(target.trim())) && targets.length > 1) {
|
|
76
|
+
return { ok: false, error: 'a web URL must be the only target; do not mix it with files or other URLs' };
|
|
77
|
+
}
|
|
78
|
+
if (input.version != null && !(Number.isInteger(input.version) && input.version >= 1)) {
|
|
79
|
+
return { ok: false, error: 'open input.version must be a positive integer (a kept version number)' };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (['annotate', 'capture', 'export', 'check', 'inspect'].includes(args.action) && (typeof input.tab !== 'string' || !input.tab)) {
|
|
83
|
+
return { ok: false, error: `${args.action} requires input.tab` };
|
|
84
|
+
}
|
|
85
|
+
if (args.action === 'inspect') {
|
|
86
|
+
const { viewport, selector } = input;
|
|
87
|
+
if (viewport != null) {
|
|
88
|
+
const sized = (value, min, max) => Number.isInteger(value) && value >= min && value <= max;
|
|
89
|
+
if (
|
|
90
|
+
typeof viewport !== 'object' ||
|
|
91
|
+
Array.isArray(viewport) ||
|
|
92
|
+
!sized(viewport.width, 200, 3000) ||
|
|
93
|
+
(viewport.height != null && !sized(viewport.height, 200, 4000))
|
|
94
|
+
) {
|
|
95
|
+
return { ok: false, error: 'inspect input.viewport must be { width: 200-3000, height?: 200-4000 } in whole pixels' };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (selector != null && (typeof selector !== 'string' || !selector.trim() || selector.length > 500)) {
|
|
99
|
+
return { ok: false, error: 'inspect input.selector must be a non-empty CSS selector of at most 500 characters' };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (args.action === 'capture' && !(typeof input.output === 'string' && /\.png$/i.test(input.output.trim()))) {
|
|
103
|
+
return { ok: false, error: 'capture requires input.output ending in .png' };
|
|
104
|
+
}
|
|
105
|
+
if (args.action === 'export') {
|
|
106
|
+
if (input.format !== 'png' && input.format !== 'pdf') return { ok: false, error: 'export requires input.format png or pdf' };
|
|
107
|
+
if (typeof input.output !== 'string' || !new RegExp('\\.' + input.format + '$', 'i').test(input.output.trim())) {
|
|
108
|
+
return { ok: false, error: `export requires input.output ending in .${input.format}` };
|
|
109
|
+
}
|
|
110
|
+
if (input.version != null && !(Number.isInteger(input.version) && input.version >= 1)) {
|
|
111
|
+
return { ok: false, error: 'export input.version must be a positive integer (a kept version number)' };
|
|
112
|
+
}
|
|
113
|
+
if (input.overwrite != null && typeof input.overwrite !== 'boolean') {
|
|
114
|
+
return { ok: false, error: 'export input.overwrite must be a boolean' };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { ok: true, action: args.action, input };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function requestBridge(discovery, payload, signal) {
|
|
121
|
+
const response = await fetch(`http://127.0.0.1:${discovery.port}/command`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${discovery.token}` },
|
|
124
|
+
body: JSON.stringify(payload),
|
|
125
|
+
signal,
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
const text = await response.text();
|
|
129
|
+
if (text.length > MAX_RESPONSE_BYTES) throw new Error('too large');
|
|
130
|
+
return { body: JSON.parse(text), status: response.status };
|
|
131
|
+
} catch {
|
|
132
|
+
throw new Error(`visualize bridge returned an invalid response (HTTP ${response.status})`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function formatSuccess(body) {
|
|
137
|
+
const lines = [String(body.text ?? '')];
|
|
138
|
+
if (Array.isArray(body.tabs) && body.tabs.length) {
|
|
139
|
+
lines.push(...body.tabs.map((tab) => `${tab.id} ${tab.kind} ${tab.label} → ${tab.target}`));
|
|
140
|
+
}
|
|
141
|
+
if (typeof body.path === 'string' && body.path) lines.push(`path: ${body.path}`);
|
|
142
|
+
return lines.filter(Boolean).join('\n');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Execute one `visualize` tool call. */
|
|
146
|
+
export async function executeVisualizeTool(args, options = {}) {
|
|
147
|
+
const validated = validateVisualizeArgs(args);
|
|
148
|
+
if (!validated.ok) return toolError(validated.error);
|
|
149
|
+
const sessionId = String(options.sessionId || '').trim();
|
|
150
|
+
if (!sessionId) return toolError('visualize session context is unavailable');
|
|
151
|
+
const payload = {
|
|
152
|
+
sessionId,
|
|
153
|
+
cwd: options.cwd || process.cwd(),
|
|
154
|
+
action: validated.action,
|
|
155
|
+
input: validated.input,
|
|
156
|
+
};
|
|
157
|
+
let discovery = readBridgeDiscovery(DISCOVERY_FILE);
|
|
158
|
+
if (!discovery) return toolError(unavailableMessage());
|
|
159
|
+
let result;
|
|
160
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
161
|
+
const timeout = AbortSignal.timeout(validated.action === 'export' ? EXPORT_REQUEST_TIMEOUT_MS : REQUEST_TIMEOUT_MS);
|
|
162
|
+
try {
|
|
163
|
+
result = await requestBridge(discovery, payload, options.signal ? AbortSignal.any([timeout, options.signal]) : timeout);
|
|
164
|
+
break;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
if (error?.name === 'TimeoutError') return toolError('visualize bridge timed out');
|
|
167
|
+
if (options.signal?.aborted) return toolError('visualize command cancelled');
|
|
168
|
+
const replacement = readBridgeDiscovery(DISCOVERY_FILE);
|
|
169
|
+
if (attempt === 0 && bridgeDiscoveryChanged(discovery, replacement)) {
|
|
170
|
+
discovery = replacement;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
return toolError(error?.message?.startsWith('visualize bridge') ? error.message : unavailableMessage());
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const { body, status } = result;
|
|
177
|
+
if (!body?.ok) {
|
|
178
|
+
return toolError(String(body?.error || `visualize bridge request failed (HTTP ${status})`).replace(/^Error:\s*/, ''));
|
|
179
|
+
}
|
|
180
|
+
return { content: [{ type: 'text', text: formatSuccess(body) }] };
|
|
181
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { TOOL_SYNC_EXECUTION_CONTRACT } from '../shared/tool-execution-contract.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `visualize` shows produced artifacts in the Mixdog desktop app's side panel
|
|
5
|
+
* over a loopback bridge. Deferred, and present only while the desktop
|
|
6
|
+
* Visualize bridge is up and the feature is on.
|
|
7
|
+
*/
|
|
8
|
+
export const TOOL_DEFS = [
|
|
9
|
+
{
|
|
10
|
+
name: 'visualize',
|
|
11
|
+
title: 'Mixdog Visualize',
|
|
12
|
+
description:
|
|
13
|
+
"Show the user any artifact you produced or changed (mockups, docs, decks, sheets, PDFs, images, video, local dev pages) in the desktop side panel beside the chat. Panel tabs auto-reload when a file changes, so re-opening after edits is unnecessary. " +
|
|
14
|
+
'open: input.targets (1-3 file paths, 2-3 = side-by-side comparison; or a single http(s) URL alone, never mixed with files or other URLs), optional label, optional version (show that kept version of a file tab read-only; each open/reload that changes the files keeps a version). close: optional tab (omit = all). ' +
|
|
15
|
+
'annotate: tab + add [{selector,label?}] | remove [selector] | clear; HTML pages only, never edits the file. capture: tab + output (.png path). ' +
|
|
16
|
+
'export: tab + format (png|pdf) + output (project-relative path ending .png/.pdf) + optional version (a kept version of the tab) + optional overwrite (true replaces an existing file, only under design/; otherwise an existing file is refused). Writes a record of the approved page inside the project and returns its project-relative path, size/dimensions and the page problems seen while rendering. A single HTML page (file, or inline html/chart) is rendered whole in a hidden window, so the tab need not be on screen (PDF: HTML pages only; PNG has a pixel cap, over it export pdf or shorten the page); any other tab exports as capture does (the displayed tab, PNG only). Same project containment as capture: links and paths outside the working directory are refused. ' +
|
|
17
|
+
'check: tab; HTML pages only; waits briefly for the page to settle and returns only its runtime problems (uncaught errors, unhandled rejections, CSP violations, console errors/warnings, missing local assets). ' +
|
|
18
|
+
'inspect: tab + optional viewport {width,height} (e.g. 375x812 to look at a mobile layout) + optional selector (scope); HTML pages with scripts/styles only (not static mermaid/svg/markdown). Returns a compact text read-out of the rendered page without a screenshot: viewport, landmarks and headings, interactive elements (role, accessible name, selector, x/y/w/h), and layout findings (horizontal overflow, clipped text, overlapping or tiny tap targets, low text contrast, images without alt, unlabeled inputs). Run it (desktop width and, for responsive designs, a mobile width) and fix the findings before reporting; `check` also says when findings exist. Secret field values are never included. ' +
|
|
19
|
+
'Any result about a tab ends with a "Page problems" section when the page has problems: fix them in the file and re-check before reporting; never report a page with unresolved problems as done. ' +
|
|
20
|
+
'HTML pages run their scripts, but outside network (CDNs, web fonts, APIs, any other http(s)/ws address) is blocked unless the user allows it for the tab, so mockups must be self-contained; a blocked request appears as a "blocked-network" page problem on open/check. ' +
|
|
21
|
+
'Direct renderers: instead of targets, open takes input.content {kind: mermaid|svg|chart|html|markdown, source, title?} (max 512 KiB; chart = an ECharts option as JSON) and shows it with no file written. ' +
|
|
22
|
+
'Prefer inline content for diagrams, charts and quick sketches; write a file for real mockups that need iteration, scripts or versioning on disk. ' +
|
|
23
|
+
'mermaid, svg and markdown render as static, sanitized output (no scripts, no network). chart (ECharts option JSON, interactive: tooltips, zoom) and html (scripts run) open as in-memory pages with the same protections as HTML file pages (outside network blocked, page problems captured); html content must be self-contained. Re-open with the same title to add a version. A render error (Mermaid parse error, bad chart JSON) is returned as a page problem: fix the source and open again. ' +
|
|
24
|
+
'Tweaks: an HTML page (file or inline html/chart) can declare adjustable parameters in <script type="application/json" id="mx-tweaks">[{id,label,type:color|range|select|toggle,var:"--name" or attr:"data-name",min,max,step,unit,options:[{value,label}],default}] (max 16, keep it to a few meaningful ones). The panel shows a Tweaks drawer and applies changes live to the page root (CSS variables / data attributes your CSS and script read); the chosen values come back with their next message under tweaks: to bake into the file. A malformed block is a "tweaks: <reason>" page problem. ' +
|
|
25
|
+
'Does not replace browser for clicking/typing. ' +
|
|
26
|
+
TOOL_SYNC_EXECUTION_CONTRACT,
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
action: { type: 'string', enum: ['open', 'close', 'annotate', 'capture', 'export', 'check', 'inspect'] },
|
|
31
|
+
input: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
targets: {
|
|
35
|
+
type: 'array',
|
|
36
|
+
items: { type: 'string' },
|
|
37
|
+
minItems: 1,
|
|
38
|
+
maxItems: 3,
|
|
39
|
+
description: 'open: 1-3 project-relative/absolute file paths (side by side), or exactly one http(s) URL as the only target.',
|
|
40
|
+
},
|
|
41
|
+
content: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
kind: { type: 'string', enum: ['mermaid', 'svg', 'chart', 'html', 'markdown'] },
|
|
45
|
+
source: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
maxLength: 524288,
|
|
48
|
+
description: 'Diagram text, SVG/HTML/Markdown source, or an ECharts option as JSON (chart).',
|
|
49
|
+
},
|
|
50
|
+
title: { type: 'string', description: 'Tab title; re-opening the same title adds a version.' },
|
|
51
|
+
},
|
|
52
|
+
required: ['kind', 'source'],
|
|
53
|
+
additionalProperties: false,
|
|
54
|
+
description: 'open: render this directly instead of files. Exclusive with targets.',
|
|
55
|
+
},
|
|
56
|
+
label: { type: 'string', description: 'open: tab label.' },
|
|
57
|
+
version: {
|
|
58
|
+
type: 'integer',
|
|
59
|
+
minimum: 1,
|
|
60
|
+
description: 'open (file tabs): kept version number to show read-only instead of the live files. export: kept version number to render instead of the live page.',
|
|
61
|
+
},
|
|
62
|
+
tab: { type: 'string', description: 'Tab id from open (close: omit for all).' },
|
|
63
|
+
add: {
|
|
64
|
+
type: 'array',
|
|
65
|
+
items: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: { selector: { type: 'string' }, label: { type: 'string' } },
|
|
68
|
+
required: ['selector'],
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
},
|
|
71
|
+
description: 'annotate: CSS selectors to outline.',
|
|
72
|
+
},
|
|
73
|
+
remove: { type: 'array', items: { type: 'string' }, description: 'annotate: selectors to un-outline.' },
|
|
74
|
+
clear: { type: 'boolean', description: 'annotate: remove all outlines.' },
|
|
75
|
+
output: { type: 'string', description: 'capture: project path ending .png. export: project path ending .png or .pdf (matching format).' },
|
|
76
|
+
format: { type: 'string', enum: ['png', 'pdf'], description: 'export: file format.' },
|
|
77
|
+
overwrite: { type: 'boolean', description: 'export: replace an existing output file; honored only for paths under design/.' },
|
|
78
|
+
viewport: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
width: { type: 'integer', minimum: 200, maximum: 3000 },
|
|
82
|
+
height: { type: 'integer', minimum: 200, maximum: 4000 },
|
|
83
|
+
},
|
|
84
|
+
required: ['width'],
|
|
85
|
+
additionalProperties: false,
|
|
86
|
+
description: 'inspect: size to lay the page out at (height defaults to 800); omit for the panel\'s current size.',
|
|
87
|
+
},
|
|
88
|
+
selector: { type: 'string', maxLength: 500, description: 'inspect: CSS selector scoping the read-out to one subtree.' },
|
|
89
|
+
},
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: ['action'],
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
];
|
|
@@ -58,6 +58,8 @@ import { loadInstructionFilesContext } from '../../runtime/agent/orchestrator/co
|
|
|
58
58
|
import { createSessionTurnApi } from '../session-turn-api.mjs';
|
|
59
59
|
import { createGoalFacadeApi } from '../goal-facade-api.mjs';
|
|
60
60
|
import { createRuntimeReviewApi } from '../runtime-review-api.mjs';
|
|
61
|
+
import { createWorkspaceTransitions, refreshWorkspacePrompt } from '../workspace/transitions.mjs';
|
|
62
|
+
import { saveSession } from '../../runtime/agent/orchestrator/session/store.mjs';
|
|
61
63
|
import { bootProfile } from '../boot-profile.mjs';
|
|
62
64
|
import { STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
|
|
63
65
|
import {
|
|
@@ -98,6 +100,7 @@ export function wireApis(boot) {
|
|
|
98
100
|
};
|
|
99
101
|
boot.modelRouteApi = modelRouteApiFor(boot);
|
|
100
102
|
boot.workflowAgentsApi = workflowAgentsApiFor(boot);
|
|
103
|
+
boot.workspaceApi = workspaceApiFor(boot);
|
|
101
104
|
boot.sessionTurnApi = sessionTurnApiFor(boot);
|
|
102
105
|
const getFacadeSessionId = () => boot.rt.session?.id || boot.rt.reservedSessionId || null;
|
|
103
106
|
boot.runtimeReviewApi = createRuntimeReviewApi({
|
|
@@ -364,6 +367,8 @@ function lifecycleApiFor(boot) {
|
|
|
364
367
|
invalidateContextStatusCache: boot.invalidateContextStatusCache,
|
|
365
368
|
invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
|
|
366
369
|
applyResolvedCwd: boot.applyResolvedCwd,
|
|
370
|
+
// Created after the lifecycle API, so resolved at call time.
|
|
371
|
+
cleanupDeletedSessionWorktree: (sessionId) => boot.workspaceApi?.cleanupDeletedSession?.(sessionId),
|
|
367
372
|
resolveRoute,
|
|
368
373
|
applyDeferredToolSurface,
|
|
369
374
|
beginRoutePreparation: (task) => routePreparation.start(task),
|
|
@@ -486,9 +491,39 @@ function workflowAgentsApiFor(boot) {
|
|
|
486
491
|
});
|
|
487
492
|
}
|
|
488
493
|
|
|
494
|
+
// The Lead workspace transition authority (worktree / project branch / remove)
|
|
495
|
+
// and the turn-start workspace gate. Facade-facing methods are spread by
|
|
496
|
+
// buildFacade; prepareTurnWorkspace feeds the turn runner.
|
|
497
|
+
function workspaceApiFor(boot) {
|
|
498
|
+
const { rt } = boot;
|
|
499
|
+
return createWorkspaceTransitions({
|
|
500
|
+
getSession: () => rt.session,
|
|
501
|
+
getReservedSessionId: () => rt.reservedSessionId,
|
|
502
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
503
|
+
getDesktopSession: () => rt.desktopSession,
|
|
504
|
+
getConfig: () => rt.config,
|
|
505
|
+
createCurrentSession: (reason) => boot.createCurrentSession(reason),
|
|
506
|
+
applyResolvedCwd: (cwd) => boot.applyResolvedCwd(cwd),
|
|
507
|
+
persistSession: (session) => saveSession(session, { immediate: true }),
|
|
508
|
+
refreshWorkspaceContext: (session, cwd) =>
|
|
509
|
+
refreshWorkspacePrompt(session, cwd, { loadCoreMemoryContext: boot.loadCoreMemoryContext }),
|
|
510
|
+
invalidateCaches: () => {
|
|
511
|
+
boot.invalidateContextStatusCache();
|
|
512
|
+
boot.invalidatePreSessionToolSurface();
|
|
513
|
+
},
|
|
514
|
+
getActiveTurnCount: () => rt.activeTurnCount || 0,
|
|
515
|
+
agentStatusState: boot.agentStatusState,
|
|
516
|
+
updateLeadWorkerCwd: (sessionId, cwd) => boot.agentTool?.updateLeadWorkerCwd?.(sessionId, cwd),
|
|
517
|
+
sessionManager: boot.mgr,
|
|
518
|
+
initialIntent: boot.params.worktree,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
489
522
|
function sessionTurnApiFor(boot) {
|
|
490
523
|
const { rt, tunables, remoteTranscript, routePreparation } = boot;
|
|
491
524
|
return createSessionTurnApi({
|
|
525
|
+
prepareTurnWorkspace: boot.workspaceApi.prepareTurnWorkspace,
|
|
526
|
+
admitTurnWorkspace: boot.workspaceApi.admitTurnWorkspace,
|
|
492
527
|
getSession: () => rt.session,
|
|
493
528
|
setSession: boot.adoptSession,
|
|
494
529
|
getCurrentCwd: () => rt.currentCwd,
|
|
@@ -74,7 +74,6 @@ export async function loadModules(boot) {
|
|
|
74
74
|
statusRoutes,
|
|
75
75
|
webSearchToolDefs,
|
|
76
76
|
memoryToolDefs,
|
|
77
|
-
channelToolDefs,
|
|
78
77
|
codeGraphToolDefs,
|
|
79
78
|
} = await loadRuntimeModules();
|
|
80
79
|
const {
|
|
@@ -103,7 +102,6 @@ export async function loadModules(boot) {
|
|
|
103
102
|
statusRoutes,
|
|
104
103
|
webSearchToolDefs,
|
|
105
104
|
memoryToolDefs,
|
|
106
|
-
channelToolDefs,
|
|
107
105
|
codeGraphToolDefs,
|
|
108
106
|
recapEnabledFn,
|
|
109
107
|
memoryToolsEnabledFn,
|
|
@@ -30,8 +30,10 @@ export async function resolveConfig(boot) {
|
|
|
30
30
|
model: params.model,
|
|
31
31
|
effort: params.effort,
|
|
32
32
|
fast: params.fast,
|
|
33
|
+
autoEffort: params.autoEffort,
|
|
33
34
|
modelParameters: params.modelParameters,
|
|
34
35
|
contextPercent: params.contextPercent,
|
|
36
|
+
contextTokens: params.contextTokens,
|
|
35
37
|
selectedContextWindow: params.selectedContextWindow,
|
|
36
38
|
}));
|
|
37
39
|
configureLocalProviderIdleTtl(rt.config.providers?.[LOCAL_PROVIDER_ID]?.idleTtlSeconds);
|
|
@@ -27,6 +27,26 @@ export function buildFacade(boot) {
|
|
|
27
27
|
...boot.modelRouteApi,
|
|
28
28
|
...boot.workflowAgentsApi,
|
|
29
29
|
...boot.sessionTurnApi,
|
|
30
|
+
// A surface registers its idle auto-clear here; returns an unregister.
|
|
31
|
+
setCompletionWakeGate: (gate) => {
|
|
32
|
+
const next = typeof gate === 'function' ? gate : null;
|
|
33
|
+
boot.completionWakeGate = next;
|
|
34
|
+
return () => {
|
|
35
|
+
if (boot.completionWakeGate === next) boot.completionWakeGate = null;
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
applyWorkspaceTransition: boot.workspaceApi.applyWorkspaceTransition,
|
|
39
|
+
getWorkspace: boot.workspaceApi.getWorkspace,
|
|
40
|
+
getBranchMenu: boot.workspaceApi.getBranchMenu,
|
|
41
|
+
getProjectBranchMenu: boot.workspaceApi.getProjectBranchMenu,
|
|
42
|
+
selectBranch: boot.workspaceApi.selectBranch,
|
|
43
|
+
selectProjectBranch: boot.workspaceApi.selectProjectBranch,
|
|
44
|
+
mergeProjectBranch: boot.workspaceApi.mergeProjectBranch,
|
|
45
|
+
requestWorktree: boot.workspaceApi.requestWorktree,
|
|
46
|
+
mergeWorkspace: boot.workspaceApi.mergeWorkspace,
|
|
47
|
+
removeWorktree: boot.workspaceApi.removeWorktree,
|
|
48
|
+
listWorktrees: boot.workspaceApi.listWorktrees,
|
|
49
|
+
pruneWorktrees: boot.workspaceApi.pruneWorktrees,
|
|
30
50
|
claimSetupRequest: setupTool.claimSetupRequest,
|
|
31
51
|
isSetupRequestActive: setupTool.isSetupRequestActive,
|
|
32
52
|
completeSetupRequest: setupTool.completeSetupRequest,
|
|
@@ -119,6 +119,7 @@ function wireNotifications(boot) {
|
|
|
119
119
|
getCurrentSessionId: () => rt.session?.id || rt.reservedSessionId || null,
|
|
120
120
|
getTurnApi: () => boot.sessionTurnApi,
|
|
121
121
|
getOwnerLifecycle: (sessionId) => ownerLifecycle(rt, sessionId),
|
|
122
|
+
beforeWake: (sessionId) => boot.completionWakeGate?.(sessionId),
|
|
122
123
|
});
|
|
123
124
|
const {
|
|
124
125
|
emitRuntimeNotification,
|
|
@@ -20,6 +20,7 @@ export function wireSessionLifecycle(boot) {
|
|
|
20
20
|
params.model !== undefined ||
|
|
21
21
|
params.effort !== undefined ||
|
|
22
22
|
params.fast !== undefined ||
|
|
23
|
+
params.autoEffort !== undefined ||
|
|
23
24
|
params.modelParameters !== undefined,
|
|
24
25
|
invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
|
|
25
26
|
invalidateOutputStyleStatusCache: boot.invalidateOutputStyleStatusCache,
|
|
@@ -20,6 +20,7 @@ import { TOOL_DEFS as TERMINAL_BRIDGE_TOOL_DEFS } from '../../runtime/terminal-b
|
|
|
20
20
|
import { TOOL_DEFS as OFFICE_TOOL_DEFS } from '../../runtime/office/tool-defs.mjs';
|
|
21
21
|
import { TOOL_DEFS as MEDIA_TOOL_DEFS } from '../../runtime/media/tool-defs.mjs';
|
|
22
22
|
import { TOOL_DEFS as TIDY_TOOL_DEFS } from '../../runtime/tidy/tool-defs.mjs';
|
|
23
|
+
import { TOOL_DEFS as VISUALIZE_BRIDGE_TOOL_DEFS } from '../../runtime/visualize-bridge/tool-defs.mjs';
|
|
23
24
|
import { SETUP_TOOL_DEFS } from '../setup-tool/tool-defs.mjs';
|
|
24
25
|
import { createSetupToolExecutor } from '../setup-tool/executor.mjs';
|
|
25
26
|
import { bootProfile } from '../boot-profile.mjs';
|
|
@@ -27,7 +28,10 @@ import { createRoutedAgentTool } from '../agent-tool-routing.mjs';
|
|
|
27
28
|
import { createGoalRuntime } from '../../runtime/agent/orchestrator/runtime-core/goal-runtime.mjs';
|
|
28
29
|
import { collectStandaloneToolDefs } from '../../runtime/agent/orchestrator/runtime-core/tool-defs.mjs';
|
|
29
30
|
import { createToolSurface } from '../tool-surface.mjs';
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
contextStatusForSession,
|
|
33
|
+
createContextStatus,
|
|
34
|
+
} from '../../runtime/agent/orchestrator/runtime-core/context-status.mjs';
|
|
31
35
|
import { createInternalToolExecutor } from '../internal-tool-executor.mjs';
|
|
32
36
|
import { STANDALONE_ROOT, STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
|
|
33
37
|
import { dataDirOf, workflowHelpers } from './shared.mjs';
|
|
@@ -141,12 +145,11 @@ function wireChannels(boot) {
|
|
|
141
145
|
// Tool definitions, then the lead tool surface (workflow-gated agent tool,
|
|
142
146
|
// pre-session preview, deferred replay) from tool-surface.mjs.
|
|
143
147
|
function wireToolSurface(boot) {
|
|
144
|
-
const { rt, cfgMod, mgr, goalRuntime, agentTool,
|
|
148
|
+
const { rt, cfgMod, mgr, goalRuntime, agentTool, featureDisallowedTools } = boot;
|
|
145
149
|
const toolsStartedAt = performance.now();
|
|
146
150
|
const { standaloneTools, internalToolDefs, agentToolNames } = collectStandaloneToolDefs({
|
|
147
151
|
webSearchToolDefs: boot.webSearchToolDefs,
|
|
148
152
|
memoryToolDefs: boot.memoryToolDefs,
|
|
149
|
-
channelToolDefs: boot.channelToolDefs,
|
|
150
153
|
codeGraphToolDefs: boot.codeGraphToolDefs,
|
|
151
154
|
browserToolDefs: BROWSER_BRIDGE_TOOL_DEFS,
|
|
152
155
|
computerToolDefs: COMPUTER_BRIDGE_TOOL_DEFS,
|
|
@@ -154,10 +157,10 @@ function wireToolSurface(boot) {
|
|
|
154
157
|
officeToolDefs: OFFICE_TOOL_DEFS,
|
|
155
158
|
mediaToolDefs: MEDIA_TOOL_DEFS,
|
|
156
159
|
tidyToolDefs: TIDY_TOOL_DEFS,
|
|
160
|
+
visualizeToolDefs: VISUALIZE_BRIDGE_TOOL_DEFS,
|
|
157
161
|
setupToolDefs: SETUP_TOOL_DEFS,
|
|
158
162
|
goalTools: goalRuntime.tools,
|
|
159
163
|
agentTools: agentTool.tools,
|
|
160
|
-
isChannelTool: (name) => channels.isChannelTool(name),
|
|
161
164
|
skillToolEnabled: !envFlag('MIXDOG_DISABLE_SKILLS'),
|
|
162
165
|
});
|
|
163
166
|
bootProfile('tools:ready', { ms: (performance.now() - toolsStartedAt).toFixed(1), count: standaloneTools.length });
|
|
@@ -245,6 +248,8 @@ function wireInternalTools(boot) {
|
|
|
245
248
|
mcpStatus: boot.mcpStatus,
|
|
246
249
|
applyResolvedCwd: boot.applyResolvedCwd,
|
|
247
250
|
skillToolContent: boot.skillToolContent,
|
|
251
|
+
// Late-bound: the workspace authority is built with the API slices.
|
|
252
|
+
getWorkspaceApi: () => boot.workspaceApi || null,
|
|
248
253
|
}),
|
|
249
254
|
});
|
|
250
255
|
Object.assign(boot, { setupTool, disposeInternalTools });
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { withGrandfatheredBuiltins } from '../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
|
|
11
11
|
import { webSearchRouteOrDefault } from '../runtime/agent/orchestrator/runtime-core/workflow.mjs';
|
|
12
12
|
import { applyConfigPatch } from '../runtime/shared/config-patch.mjs';
|
|
13
|
+
import { normalizeContextTokens } from '../runtime/shared/llm/default-context-window.mjs';
|
|
13
14
|
import { withEffectiveDisabledAgents, withStoredDisabledAgents } from '../runtime/shared/agent-route-config.mjs';
|
|
14
15
|
import {
|
|
15
16
|
DEFAULT_MAINTENANCE,
|
|
@@ -40,19 +41,27 @@ export function resolveInitialConfigState({
|
|
|
40
41
|
model,
|
|
41
42
|
effort,
|
|
42
43
|
fast,
|
|
44
|
+
autoEffort,
|
|
43
45
|
modelParameters,
|
|
44
46
|
contextPercent,
|
|
47
|
+
contextTokens,
|
|
45
48
|
selectedContextWindow,
|
|
46
49
|
}) {
|
|
47
50
|
const config = withGrandfatheredBuiltins(
|
|
48
51
|
initialConfig && typeof initialConfig === 'object' ? initialConfig : loadConfig()
|
|
49
52
|
);
|
|
53
|
+
// A requested percent replaces a saved token window; requested tokens
|
|
54
|
+
// replace both (resolveRouteContextState lets tokens win).
|
|
55
|
+
const { contextTokens: savedTokens, ...resolved } = resolveRoute(config, { provider, model });
|
|
56
|
+
const tokens = normalizeContextTokens(contextTokens) || (contextPercent > 0 ? undefined : savedTokens);
|
|
50
57
|
return {
|
|
51
58
|
config,
|
|
52
59
|
route: {
|
|
53
|
-
...
|
|
60
|
+
...resolved,
|
|
61
|
+
...(tokens ? { contextTokens: tokens } : {}),
|
|
54
62
|
...(effort === undefined ? {} : { effort: effort || null }),
|
|
55
63
|
...(fast === true || fast === false ? { fast } : {}),
|
|
64
|
+
...(autoEffort === true || autoEffort === false ? { autoEffort } : {}),
|
|
56
65
|
...(modelParameters && typeof modelParameters === 'object' ? { modelParameters: { ...modelParameters } } : {}),
|
|
57
66
|
...(contextPercent > 0 ? { contextPercent } : {}),
|
|
58
67
|
...(selectedContextWindow > 0 ? { selectedContextWindow } : {}),
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// cwd-plugins/cwd-apply.mjs — resolving a requested cwd and retargeting the
|
|
2
2
|
// live session to it (project selection persistence, code-graph prewarm,
|
|
3
3
|
// CwdChanged hook) without recreating the session.
|
|
4
|
+
import { setWorkspaceCwdLock } from '../workspace/admission-gate.mjs';
|
|
5
|
+
import { workspaceForProjectSelection } from '../workspace/workspace-state.mjs';
|
|
6
|
+
|
|
4
7
|
export function createCwdApply({
|
|
5
8
|
getCurrentCwd,
|
|
6
9
|
setCurrentCwd,
|
|
@@ -31,9 +34,16 @@ export function createCwdApply({
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
function persistProjectSelection(session, currentCwd) {
|
|
37
|
+
// A Lead in its worktree keeps the registered Project as projectPath; a
|
|
38
|
+
// switch elsewhere leaves the worktree at a new workspace revision.
|
|
39
|
+
const { workspace, projectPath } = workspaceForProjectSelection(session, currentCwd);
|
|
40
|
+
if (session && workspace !== (session.workspace ?? null)) {
|
|
41
|
+
session.workspace = workspace;
|
|
42
|
+
setWorkspaceCwdLock(session.id, null);
|
|
43
|
+
}
|
|
34
44
|
const desktop = getDesktopSession?.() || session?.desktopSession;
|
|
35
45
|
if (desktop && typeof desktop === 'object') {
|
|
36
|
-
const nextDesktop = { classification: 'project', projectPath
|
|
46
|
+
const nextDesktop = { classification: 'project', projectPath };
|
|
37
47
|
setDesktopSession?.(nextDesktop);
|
|
38
48
|
if (session) session.desktopSession = nextDesktop;
|
|
39
49
|
}
|
|
@@ -16,11 +16,11 @@ export function subscribeGlobalExtensionChanges(listener) {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export async function publishGlobalExtensionChange(kind, originId = null) {
|
|
19
|
+
export async function publishGlobalExtensionChange(kind, originId = null, detail = null) {
|
|
20
20
|
const jobs = [];
|
|
21
21
|
for (const [id, listener] of subscribers) {
|
|
22
22
|
if (id === originId) continue;
|
|
23
|
-
jobs.push(Promise.resolve().then(() => listener(kind)));
|
|
23
|
+
jobs.push(Promise.resolve().then(() => listener(kind, detail)));
|
|
24
24
|
}
|
|
25
25
|
await Promise.allSettled(jobs);
|
|
26
26
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import { executeBrowserTool } from '../../runtime/browser-bridge/client.mjs';
|
|
4
4
|
import { executeComputerTool } from '../../runtime/computer-bridge/client.mjs';
|
|
5
5
|
import { executeTerminalTool } from '../../runtime/terminal-bridge/client.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import { executeVisualizeTool } from '../../runtime/visualize-bridge/client.mjs';
|
|
7
|
+
import { featureEnvOverride, moduleEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
7
8
|
import { createCallerContextResolvers } from './caller-context.mjs';
|
|
8
9
|
|
|
9
10
|
export function createBridgeToolHandlers({ rt }) {
|
|
@@ -50,5 +51,29 @@ export function createBridgeToolHandlers({ rt }) {
|
|
|
50
51
|
});
|
|
51
52
|
};
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
const visualize = async (args, { callerCtx, callerCwd }) => {
|
|
55
|
+
if (environmentDisabled(callerCtx, 'MIXDOG_FEATURE_VISUALIZE')) {
|
|
56
|
+
throw new Error('the visualize tool is disabled in this environment');
|
|
57
|
+
}
|
|
58
|
+
// A conversation keeps its frozen tool list, so honor the Settings toggle
|
|
59
|
+
// at execution time too (an env override wins, as in builtinFeatureActive).
|
|
60
|
+
if (!(featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ?? moduleEnabled(rt.config, 'visualize', true))) {
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: 'text',
|
|
65
|
+
text: 'Error: Visualize is turned off in Settings by the user. Do not turn it back on unless the user asks; give the file path instead.',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return await executeVisualizeTool(args, {
|
|
72
|
+
sessionId: sessionIdFor(callerCtx),
|
|
73
|
+
cwd: callerCwd,
|
|
74
|
+
signal: signalFor(callerCtx),
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return { browser, browser_devtools: browser, computer, terminal, visualize };
|
|
54
79
|
}
|
|
@@ -5,9 +5,40 @@ import { resolve } from 'node:path';
|
|
|
5
5
|
import { listProjects } from '../../runtime/shared/projects.mjs';
|
|
6
6
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
7
7
|
|
|
8
|
-
export function createWorkspaceToolHandlers({
|
|
8
|
+
export function createWorkspaceToolHandlers({
|
|
9
|
+
rt,
|
|
10
|
+
goalRuntime,
|
|
11
|
+
agentTool,
|
|
12
|
+
notifyFnForSession,
|
|
13
|
+
applyResolvedCwd,
|
|
14
|
+
getWorkspaceApi = () => null,
|
|
15
|
+
}) {
|
|
16
|
+
// The Lead's own worktree, on the user's request: the same authority the
|
|
17
|
+
// branch chip menu drives, tolerating the calling turn but nothing else
|
|
18
|
+
// running (workers, background shells).
|
|
19
|
+
const worktreeAction = async (action, args, callerCtx) => {
|
|
20
|
+
const workspace = getWorkspaceApi();
|
|
21
|
+
if (!workspace) throw new Error(`cwd: ${action} is unavailable in this runtime`);
|
|
22
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
23
|
+
if (!rt.session?.id || callerSessionId !== rt.session.id) {
|
|
24
|
+
throw new Error(`cwd: ${action} belongs to the Lead session`);
|
|
25
|
+
}
|
|
26
|
+
const result =
|
|
27
|
+
action === 'merge-worktree'
|
|
28
|
+
? await workspace.mergeWorkspace({ confirmed: true }, { ownTurn: true })
|
|
29
|
+
: action === 'remove-worktree'
|
|
30
|
+
? await workspace.removeWorktree({ force: args?.force === true }, { ownTurn: true })
|
|
31
|
+
: action === 'list-worktrees'
|
|
32
|
+
? await workspace.listWorktrees()
|
|
33
|
+
: await workspace.pruneWorktrees();
|
|
34
|
+
return JSON.stringify({ action, cwd: rt.currentCwd, result }, null, 2);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const WORKTREE_ACTIONS = new Set(['merge-worktree', 'remove-worktree', 'list-worktrees', 'prune-worktrees']);
|
|
38
|
+
|
|
9
39
|
const cwd = async (args, { callerCtx, callerCwd }) => {
|
|
10
40
|
const action = clean(args?.action || (args?.path ? 'set' : 'get')).toLowerCase();
|
|
41
|
+
if (WORKTREE_ACTIONS.has(action)) return worktreeAction(action, args, callerCtx);
|
|
11
42
|
let currentCwd = callerCwd;
|
|
12
43
|
if (action === 'list') {
|
|
13
44
|
return JSON.stringify(
|