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
|
@@ -6,10 +6,14 @@ import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
|
|
|
6
6
|
import { cleanString as clean } from './clean.mjs';
|
|
7
7
|
import { runGitOffThread } from './git-runner.mjs';
|
|
8
8
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
9
|
+
import { normalizeChangeStatus, splitPatchSections } from './turn-changes.mjs';
|
|
9
10
|
|
|
10
11
|
const COMMAND_TIMEOUT_MS = 30_000;
|
|
11
12
|
const COMMAND_MAX_BYTES = 8 * 1024 * 1024;
|
|
12
13
|
const PATCH_MAX_BYTES = 2 * 1024 * 1024;
|
|
14
|
+
// The complete (replay-safe) patch a sealing turn records; per-file and
|
|
15
|
+
// per-session bounds are applied on top of this by the change record.
|
|
16
|
+
const FREEZE_PATCH_MAX_BYTES = 64 * 1024 * 1024;
|
|
13
17
|
const MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
|
|
14
18
|
// Largest tool-supplied baseline kept for `hash-object`: the apply_patch edit
|
|
15
19
|
// limit. (2–64 MiB explicit untracked edits keep their exact revert.)
|
|
@@ -70,9 +74,16 @@ function commandError(args, stderr, code) {
|
|
|
70
74
|
// call that would otherwise stall the event loop each time.
|
|
71
75
|
async function runGit(
|
|
72
76
|
args,
|
|
73
|
-
{
|
|
77
|
+
{
|
|
78
|
+
cwd,
|
|
79
|
+
input = '',
|
|
80
|
+
timeoutMs = COMMAND_TIMEOUT_MS,
|
|
81
|
+
maxBytes = COMMAND_MAX_BYTES,
|
|
82
|
+
allowFailure = false,
|
|
83
|
+
stdoutEncoding = 'utf8',
|
|
84
|
+
} = {}
|
|
74
85
|
) {
|
|
75
|
-
const result = await runGitOffThread(args, { cwd, input, timeoutMs, maxBytes });
|
|
86
|
+
const result = await runGitOffThread(args, { cwd, input, timeoutMs, maxBytes, stdoutEncoding });
|
|
76
87
|
if (result.code !== 0 && !allowFailure) throw commandError(args, result.stderr, result.code);
|
|
77
88
|
return result;
|
|
78
89
|
}
|
|
@@ -94,11 +105,14 @@ async function repositoryRoot(worktree) {
|
|
|
94
105
|
return root ? resolve(root) : null;
|
|
95
106
|
}
|
|
96
107
|
|
|
108
|
+
function shadowDirForKey(key) {
|
|
109
|
+
return join(snapshotRoot(), createHash('sha256').update(key).digest('hex').slice(0, 24));
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
function stateForRoot(root) {
|
|
98
113
|
const key = pathKey(root);
|
|
99
114
|
let state = states.get(key);
|
|
100
|
-
const
|
|
101
|
-
const gitDir = join(snapshotRoot(), hash);
|
|
115
|
+
const gitDir = shadowDirForKey(key);
|
|
102
116
|
if (state && state.gitDir === gitDir) return state;
|
|
103
117
|
state = {
|
|
104
118
|
root,
|
|
@@ -112,6 +126,11 @@ function stateForRoot(root) {
|
|
|
112
126
|
// Bumped by every reported write under `root`; a capture records the
|
|
113
127
|
// value it started from.
|
|
114
128
|
changeStamp: 0,
|
|
129
|
+
// Set by forgetTurnWorktreeRoot(): the shadow repository must never be
|
|
130
|
+
// recreated for this state. A later snapshot of the same path gets a new one.
|
|
131
|
+
forgotten: false,
|
|
132
|
+
gcTimer: null,
|
|
133
|
+
gcRun: null,
|
|
115
134
|
};
|
|
116
135
|
states.set(key, state);
|
|
117
136
|
return state;
|
|
@@ -156,7 +175,19 @@ async function sourceLineEndingArgs(root) {
|
|
|
156
175
|
return [...values].flatMap(([key, value]) => ['-c', `${key}=${value}`]);
|
|
157
176
|
}
|
|
158
177
|
|
|
178
|
+
// A forgotten root's shadow repository is gone for good. Every git command and
|
|
179
|
+
// every file write on it goes through shadowArgs/ensureState, so refusing there
|
|
180
|
+
// keeps a snapshot object that outlived forgetTurnWorktreeRoot() from
|
|
181
|
+
// recreating the directory.
|
|
182
|
+
function assertStateLive(state) {
|
|
183
|
+
if (!state.forgotten) return;
|
|
184
|
+
const error = new Error(`turn worktree snapshot for ${state.root} was removed`);
|
|
185
|
+
error.code = 'TURN_WORKTREE_SNAPSHOT_REMOVED';
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
|
|
159
189
|
function shadowArgs(state, args) {
|
|
190
|
+
assertStateLive(state);
|
|
160
191
|
return [
|
|
161
192
|
...state.lineEndingArgs,
|
|
162
193
|
// A conversion warning must never fail the snapshot `add`.
|
|
@@ -239,6 +270,7 @@ async function refreshSourceTracked(state, { force = false } = {}) {
|
|
|
239
270
|
}
|
|
240
271
|
|
|
241
272
|
async function ensureState(state) {
|
|
273
|
+
assertStateLive(state);
|
|
242
274
|
if (state.initialized && (await exists(join(state.gitDir, 'config')))) return;
|
|
243
275
|
await mkdir(dirname(state.gitDir), { recursive: true });
|
|
244
276
|
if (!(await exists(join(state.gitDir, 'config')))) {
|
|
@@ -643,24 +675,141 @@ async function diffTreesUnlocked(state, baselineTree, currentTree, paths = null,
|
|
|
643
675
|
return { patch, files, patchTruncated, currentTree, ...(omitPatch ? { patchOmitted: true } : {}) };
|
|
644
676
|
}
|
|
645
677
|
|
|
678
|
+
/**
|
|
679
|
+
* Freeze the snapshot's last captured net diff into replayable per-file
|
|
680
|
+
* patches: one complete `--binary --full-index` diff (independent of the
|
|
681
|
+
* bounded display patch), split back into its `diff --git` sections. `paths`
|
|
682
|
+
* scopes a contended worktree to the session-owned files. Callers refresh the
|
|
683
|
+
* snapshot first; this never captures. Files whose bytes cannot be replayed
|
|
684
|
+
* (an uncaptured baseline, a type change, an over-limit diff) stay in the
|
|
685
|
+
* record as non-revertable with a reason.
|
|
686
|
+
*/
|
|
687
|
+
export async function freezeTurnWorktreeChanges(snapshot, paths = null) {
|
|
688
|
+
if (!snapshot?.state || !snapshot.baselineTree) return null;
|
|
689
|
+
const scope = Array.isArray(paths) ? [...new Set(paths.filter(Boolean))] : null;
|
|
690
|
+
if (scope && scope.length === 0) return { root: snapshot.root, files: [] };
|
|
691
|
+
return await withStateLock(snapshot.state, async () => {
|
|
692
|
+
const { state, baselineTree, currentTree } = snapshot;
|
|
693
|
+
let review = { files: [], patch: '', patchTruncated: false };
|
|
694
|
+
let sections = [];
|
|
695
|
+
let unavailable = '';
|
|
696
|
+
if (currentTree && currentTree !== baselineTree) {
|
|
697
|
+
review = await diffTreesUnlocked(state, baselineTree, currentTree, scope, { omitPatch: true });
|
|
698
|
+
const args = [
|
|
699
|
+
'diff',
|
|
700
|
+
'--binary',
|
|
701
|
+
'--full-index',
|
|
702
|
+
'--no-color',
|
|
703
|
+
'--no-ext-diff',
|
|
704
|
+
'--no-textconv',
|
|
705
|
+
'--src-prefix=a/',
|
|
706
|
+
'--dst-prefix=b/',
|
|
707
|
+
'--find-renames',
|
|
708
|
+
baselineTree,
|
|
709
|
+
currentTree,
|
|
710
|
+
'--',
|
|
711
|
+
...(scope || ['.']),
|
|
712
|
+
];
|
|
713
|
+
try {
|
|
714
|
+
const result = await runGit(shadowArgs(state, args), {
|
|
715
|
+
cwd: state.root,
|
|
716
|
+
maxBytes: FREEZE_PATCH_MAX_BYTES,
|
|
717
|
+
stdoutEncoding: 'base64',
|
|
718
|
+
});
|
|
719
|
+
sections = splitPatchSections(Buffer.from(result.stdout, 'base64'));
|
|
720
|
+
} catch (error) {
|
|
721
|
+
if (error?.code !== 'EMAXBUFFER') throw error;
|
|
722
|
+
unavailable = 'the turn changed too much to keep its patches for undo';
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const listed = withOmittedBaselines({ ...snapshot, scopePaths: scope }, { ...review, patchOmitted: true });
|
|
726
|
+
const byPath = new Map();
|
|
727
|
+
for (const section of sections) {
|
|
728
|
+
const key = pathKey(section.newPath ?? section.oldPath ?? '');
|
|
729
|
+
// A type change diffs as a delete plus an add of one path: not one patch.
|
|
730
|
+
byPath.set(key, byPath.has(key) ? null : section.patch);
|
|
731
|
+
}
|
|
732
|
+
const files = (listed.files || []).map((file) => {
|
|
733
|
+
const entry = {
|
|
734
|
+
path: file.path,
|
|
735
|
+
...(file.oldPath ? { oldPath: file.oldPath } : {}),
|
|
736
|
+
status: normalizeChangeStatus(file.status),
|
|
737
|
+
additions: Number(file.additions) || 0,
|
|
738
|
+
deletions: Number(file.deletions) || 0,
|
|
739
|
+
...(file.binary ? { binary: true } : {}),
|
|
740
|
+
};
|
|
741
|
+
if (entry.status !== 'R') delete entry.oldPath;
|
|
742
|
+
if (file.omitted === true) {
|
|
743
|
+
return { ...entry, revertable: false, reason: 'binary/large file content was not captured' };
|
|
744
|
+
}
|
|
745
|
+
if (file.status === 'T') return { ...entry, revertable: false, reason: 'the file type changed' };
|
|
746
|
+
const patch = byPath.get(pathKey(file.path));
|
|
747
|
+
if (!patch) return { ...entry, revertable: false, reason: unavailable || 'no replayable patch was recorded' };
|
|
748
|
+
return { ...entry, revertable: true, patch };
|
|
749
|
+
});
|
|
750
|
+
return { root: snapshot.root, files };
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
646
754
|
// One maintenance pass per shadow repository per process, off the turn's
|
|
647
755
|
// critical path. `--auto` does nothing until the repository actually needs it,
|
|
648
756
|
// so the common case costs a process spawn and nothing else.
|
|
649
757
|
const maintainedShadowRepositories = new Set();
|
|
650
758
|
|
|
651
759
|
function scheduleShadowGc(state) {
|
|
652
|
-
if (maintainedShadowRepositories.has(state.gitDir)) return;
|
|
760
|
+
if (state.forgotten || maintainedShadowRepositories.has(state.gitDir)) return;
|
|
653
761
|
maintainedShadowRepositories.add(state.gitDir);
|
|
654
762
|
const timer = setTimeout(() => {
|
|
763
|
+
state.gcTimer = null;
|
|
655
764
|
// Deliberately outside the state lock: collection can run for minutes and
|
|
656
765
|
// git serializes itself through gc.lock, so a clash just retries later.
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
766
|
+
// forgetTurnWorktreeRoot() waits for `gcRun` before deleting the repository.
|
|
767
|
+
state.gcRun = Promise.resolve()
|
|
768
|
+
.then(() =>
|
|
769
|
+
runGit(shadowArgs(state, ['gc', '--auto', `--prune=${SHADOW_GC_PRUNE}`, '--quiet']), {
|
|
770
|
+
cwd: state.root,
|
|
771
|
+
allowFailure: true,
|
|
772
|
+
timeoutMs: 300_000,
|
|
773
|
+
})
|
|
774
|
+
)
|
|
775
|
+
.catch(() => {});
|
|
662
776
|
}, 60_000);
|
|
663
777
|
timer.unref?.();
|
|
778
|
+
state.gcTimer = timer;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
async function removeShadowDir(gitDir, removed, kept) {
|
|
782
|
+
if (!(await exists(gitDir))) return;
|
|
783
|
+
await rm(gitDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch(() => {});
|
|
784
|
+
(await exists(gitDir) ? kept : removed).push(gitDir);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/** Drop every trace of one worktree root: serialize behind its pending capture
|
|
788
|
+
* work, remove its shadow repository and in-memory state, and refuse any write
|
|
789
|
+
* a snapshot object that still holds the old state attempts afterwards.
|
|
790
|
+
* Only the exact root is touched. Returns `{ removed, kept }` shadow paths;
|
|
791
|
+
* `kept` lists a shadow repository that could not be deleted. */
|
|
792
|
+
export async function forgetTurnWorktreeRoot(root) {
|
|
793
|
+
const text = clean(root);
|
|
794
|
+
const removed = [];
|
|
795
|
+
const kept = [];
|
|
796
|
+
if (!text) return { removed, kept };
|
|
797
|
+
const keys = new Set([pathKey(resolve(text)), pathKey(canonicalPath(text))]);
|
|
798
|
+
const gitDirs = new Set([...keys].map(shadowDirForKey));
|
|
799
|
+
for (const [key, state] of [...states]) {
|
|
800
|
+
if (!keys.has(key) && !keys.has(pathKey(state.root))) continue;
|
|
801
|
+
gitDirs.add(state.gitDir);
|
|
802
|
+
await withStateLock(state, async () => {
|
|
803
|
+
state.forgotten = true;
|
|
804
|
+
if (states.get(key) === state) states.delete(key);
|
|
805
|
+
if (state.gcTimer) clearTimeout(state.gcTimer);
|
|
806
|
+
state.gcTimer = null;
|
|
807
|
+
await state.gcRun;
|
|
808
|
+
maintainedShadowRepositories.delete(state.gitDir);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
for (const gitDir of gitDirs) await removeShadowDir(gitDir, removed, kept);
|
|
812
|
+
return { removed, kept };
|
|
664
813
|
}
|
|
665
814
|
|
|
666
815
|
export async function createTurnWorktreeSnapshot(worktree) {
|
|
@@ -847,7 +996,8 @@ async function revertPathsUnlocked(snapshot, targets) {
|
|
|
847
996
|
await diffTreesUnlocked(snapshot.state, snapshot.baselineTree, currentTree, snapshot.scopePaths)
|
|
848
997
|
)
|
|
849
998
|
);
|
|
850
|
-
|
|
999
|
+
// The absolute paths now back at their baseline bytes.
|
|
1000
|
+
return targets.map((target) => resolve(snapshot.root, target));
|
|
851
1001
|
}
|
|
852
1002
|
|
|
853
1003
|
export async function revertTurnWorktreeFile(snapshot, value) {
|
|
@@ -15,6 +15,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
15
15
|
import { writeJsonAtomicSync } from './atomic-file.mjs';
|
|
16
16
|
import { readJsonSafe } from './json-file.mjs';
|
|
17
17
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
18
|
+
import { compareSemver } from './semver.mjs';
|
|
18
19
|
|
|
19
20
|
const PACKAGE_NAME = 'mixdog';
|
|
20
21
|
const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
@@ -84,37 +85,7 @@ function writeCache(dataDir, payload) {
|
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
function parseSemver(value) {
|
|
89
|
-
const text = String(value || '')
|
|
90
|
-
.trim()
|
|
91
|
-
.replace(/^v/i, '');
|
|
92
|
-
const [core, ...preParts] = text.split('-');
|
|
93
|
-
const parts = core.split('.').map((n) => {
|
|
94
|
-
const num = Number.parseInt(n, 10);
|
|
95
|
-
return Number.isFinite(num) ? num : 0;
|
|
96
|
-
});
|
|
97
|
-
while (parts.length < 3) parts.push(0);
|
|
98
|
-
return { parts, prerelease: preParts.join('-') };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Compare two semver strings. Returns positive if `a` > `b`, negative if
|
|
103
|
-
* `a` < `b`, 0 if equal. A version with a prerelease tag is considered
|
|
104
|
-
* lower than the same core version without one (standard semver rule).
|
|
105
|
-
*/
|
|
106
|
-
export function compareSemver(a, b) {
|
|
107
|
-
const pa = parseSemver(a);
|
|
108
|
-
const pb = parseSemver(b);
|
|
109
|
-
for (let i = 0; i < 3; i++) {
|
|
110
|
-
const diff = (pa.parts[i] || 0) - (pb.parts[i] || 0);
|
|
111
|
-
if (diff !== 0) return diff;
|
|
112
|
-
}
|
|
113
|
-
if (pa.prerelease && !pb.prerelease) return -1;
|
|
114
|
-
if (!pa.prerelease && pb.prerelease) return 1;
|
|
115
|
-
if (pa.prerelease !== pb.prerelease) return pa.prerelease < pb.prerelease ? -1 : 1;
|
|
116
|
-
return 0;
|
|
117
|
-
}
|
|
88
|
+
export { compareSemver };
|
|
118
89
|
|
|
119
90
|
export function isNewerVersion(latest, current) {
|
|
120
91
|
if (!latest || !current) return false;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root cleanups for Mixdog's path-keyed caches: when a managed worktree is
|
|
3
|
+
* removed these drop everything the runtime cached under that exact root, so
|
|
4
|
+
* the removal leaves no turn snapshots, snapshot-store scopes or code-graph
|
|
5
|
+
* state behind. Each returns `{ removed, kept }` and never touches other roots.
|
|
6
|
+
* Modules load lazily: registering costs nothing until a worktree is removed.
|
|
7
|
+
*/
|
|
8
|
+
import { registerRootCleanup } from './contract.mjs';
|
|
9
|
+
|
|
10
|
+
const merge = (target, part) => {
|
|
11
|
+
target.removed.push(...part.removed);
|
|
12
|
+
target.kept.push(...part.kept);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** @type {Record<string, (root: string) => Promise<{removed: string[], kept: string[]}>>} */
|
|
16
|
+
export const WORKTREE_CACHE_CLEANUPS = Object.freeze({
|
|
17
|
+
'turn-worktree-snapshots': async (root) => {
|
|
18
|
+
const { forgetTurnWorktreeRoot } = await import('../turn-worktree-snapshot.mjs');
|
|
19
|
+
return await forgetTurnWorktreeRoot(root);
|
|
20
|
+
},
|
|
21
|
+
'turn-snapshot-store': async (root) => {
|
|
22
|
+
const { forgetTurnSnapshotRoot } = await import('../turn-snapshot-store.mjs');
|
|
23
|
+
return await forgetTurnSnapshotRoot(root);
|
|
24
|
+
},
|
|
25
|
+
'code-graph': async (root) => {
|
|
26
|
+
const [state, disk, trusted] = await Promise.all([
|
|
27
|
+
import('../code-graph-state.mjs'),
|
|
28
|
+
import('../../agent/orchestrator/tools/code-graph/disk-cache.mjs'),
|
|
29
|
+
import('../../agent/orchestrator/tools/code-graph/trusted-roots.mjs'),
|
|
30
|
+
]);
|
|
31
|
+
const result = { removed: state.forgetCodeGraphStateRoot(root).map((name) => `${name}:${root}`), kept: [] };
|
|
32
|
+
merge(result, disk.forgetDiskCodeGraphRoot(root));
|
|
33
|
+
merge(result, trusted.forgetTrustedCodeGraphRoot(root));
|
|
34
|
+
return result;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const registrations = new WeakSet();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Register every cache cleanup once per registry function. Idempotent: calling
|
|
42
|
+
* it again (or from several hosts) neither throws nor registers duplicates.
|
|
43
|
+
* @param {typeof registerRootCleanup} [register] Defaults to the process registry.
|
|
44
|
+
* @returns {string[]} Cleanup names.
|
|
45
|
+
*/
|
|
46
|
+
export function registerWorktreeCacheCleanups(register = registerRootCleanup) {
|
|
47
|
+
if (!registrations.has(register)) {
|
|
48
|
+
registrations.add(register);
|
|
49
|
+
for (const [name, cleanup] of Object.entries(WORKTREE_CACHE_CLEANUPS)) register(name, cleanup);
|
|
50
|
+
}
|
|
51
|
+
return Object.keys(WORKTREE_CACHE_CLEANUPS);
|
|
52
|
+
}
|