mixdog 1.3.1 → 1.3.3
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 +8 -4
- package/package.json +8 -8
- package/scripts/native-release-status.mjs +238 -0
- package/scripts/verify-runtime-dependency-versions.mjs +158 -0
- package/src/agents/reviewer/AGENT.md +5 -7
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +9 -1
- package/src/defaults/skills/browser-use/SKILL.md +6 -3
- package/src/defaults/skills/local-provider/SKILL.md +33 -6
- package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
- package/src/defaults/skills/setup/SKILL.md +23 -1
- package/src/defaults/skills/setup/references/actions.md +50 -3
- package/src/defaults/skills/setup/references/surfaces.md +23 -3
- package/src/defaults/skills/setup-guide/SKILL.md +7 -3
- package/src/defaults/skills/visualize/SKILL.md +9 -6
- package/src/defaults/skills/visualize/references/craft.md +8 -1
- package/src/help.mjs +1 -1
- package/src/lib/keychain-cjs.cjs +4 -1
- package/src/lib/rules-builder.cjs +14 -7
- package/src/rules/agent-mode/AGENT_MODE.md +8 -5
- package/src/rules/agent-mode/MIXY.md +6 -8
- package/src/rules/lead/LEAD.md +7 -10
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/20-research.md +2 -0
- package/src/rules/shared/45-destructive.md +5 -1
- package/src/rules/shared/60-verification.md +6 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
- package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
- package/src/runtime/agent/orchestrator/config.mjs +11 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
- package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
- package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
- package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
- package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
- package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
- package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
- package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
- package/src/runtime/attachments/attachment-reference.mjs +155 -0
- package/src/runtime/attachments/legacy-office.mjs +110 -0
- package/src/runtime/attachments/limits.mjs +5 -1
- package/src/runtime/attachments/office-extract.mjs +3 -6
- package/src/runtime/attachments/pdf-extract.mjs +84 -10
- package/src/runtime/attachments/prompt-restore.mjs +70 -0
- package/src/runtime/attachments/store.mjs +109 -33
- package/src/runtime/browser-bridge/client.mjs +73 -20
- package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
- package/src/runtime/channels/lib/scheduler.mjs +14 -7
- package/src/runtime/channels/lib/state-file.mjs +15 -15
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
- package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
- package/src/runtime/channels/lib/worker-main.mjs +3 -2
- package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
- package/src/runtime/effort-judge/judge-client.mjs +66 -16
- package/src/runtime/effort-judge/model-install.mjs +47 -7
- package/src/runtime/local-provider/asset-installer.mjs +10 -4
- package/src/runtime/local-provider/catalog.mjs +5 -2
- package/src/runtime/local-provider/registered-models.mjs +64 -27
- package/src/runtime/local-provider/server.mjs +2 -3
- package/src/runtime/media/adapters/image-api.mjs +89 -0
- package/src/runtime/media/auth.mjs +64 -1
- package/src/runtime/media/catalog.mjs +66 -4
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +46 -5
- package/src/runtime/media/media-usage.mjs +17 -0
- package/src/runtime/media/renditions.mjs +15 -15
- package/src/runtime/media/store.mjs +36 -35
- package/src/runtime/media/tool.mjs +8 -1
- package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
- package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
- package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
- package/src/runtime/memory/lib/pg/process.mjs +8 -4
- package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
- package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
- package/src/runtime/office/core/journal.mjs +7 -14
- package/src/runtime/office/design/library/design-library-core.mjs +4 -16
- package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
- package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
- package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
- package/src/runtime/office/pdf/pdf-render.mjs +30 -18
- package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +135 -29
- package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
- package/src/runtime/shared/agent-avatar.mjs +3 -2
- package/src/runtime/shared/agent-kind-migration.mjs +200 -0
- package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
- package/src/runtime/shared/agent-message-envelope.mjs +44 -0
- package/src/runtime/shared/agent-reap-state.d.mts +4 -0
- package/src/runtime/shared/agent-room-host.d.mts +31 -0
- package/src/runtime/shared/agent-room-host.mjs +58 -0
- package/src/runtime/shared/agent-store.mjs +24 -7
- package/src/runtime/shared/agent-task-starts.mjs +27 -0
- package/src/runtime/shared/atomic-file.d.mts +50 -0
- package/src/runtime/shared/atomic-file.mjs +394 -132
- package/src/runtime/shared/background-tasks.mjs +7 -4
- package/src/runtime/shared/bot-hops.mjs +35 -0
- package/src/runtime/shared/bot-split-migration.mjs +1 -1
- package/src/runtime/shared/bounded-download.mjs +4 -2
- package/src/runtime/shared/config.mjs +39 -9
- package/src/runtime/shared/defaults-separation.mjs +6 -2
- package/src/runtime/shared/delegated-approval-host.mjs +74 -0
- package/src/runtime/shared/err-text.mjs +4 -0
- package/src/runtime/shared/error-presentation.mjs +22 -4
- package/src/runtime/shared/file-has-data.mjs +28 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
- package/src/runtime/shared/pending-agent-work.mjs +54 -0
- package/src/runtime/shared/process-lifecycle.mjs +8 -12
- package/src/runtime/shared/projects.mjs +50 -41
- package/src/runtime/shared/provider-accounts.mjs +17 -12
- package/src/runtime/shared/redact-directories.mjs +18 -0
- package/src/runtime/shared/remove-directory.mjs +28 -0
- package/src/runtime/shared/routine-trigger.d.mts +6 -1
- package/src/runtime/shared/routine-trigger.mjs +14 -6
- package/src/runtime/shared/run-child.d.mts +3 -0
- package/src/runtime/shared/run-child.mjs +24 -3
- package/src/runtime/shared/session-baseline-store.mjs +8 -2
- package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
- package/src/runtime/shared/session-sidebar-host.mjs +16 -3
- package/src/runtime/shared/singleton-owner.mjs +7 -6
- package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
- package/src/runtime/shared/staged-update.mjs +4 -2
- package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
- package/src/runtime/shared/tool-execution-contract.mjs +10 -0
- package/src/runtime/shared/tool-work-units.mjs +12 -9
- package/src/runtime/shared/transcript-metadata.mjs +28 -3
- package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
- package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
- package/src/runtime/shared/turn-snapshot.mjs +277 -124
- package/src/runtime/shared/user-cwd.mjs +9 -4
- package/src/runtime/shared/user-data-guard.mjs +92 -61
- package/src/runtime/shared/utf8-cut.mjs +10 -0
- package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
- package/src/runtime/shared/worktrees/contract.mjs +11 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
- package/src/runtime/shared/worktrees/manager.mjs +419 -52
- package/src/runtime/shared/worktrees/registry.mjs +29 -27
- package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
- package/src/runtime/tidy/core-install.mjs +26 -4
- package/src/runtime/tidy/install.mjs +91 -23
- package/src/runtime/tidy/process.mjs +41 -8
- package/src/runtime/tidy/resolve.mjs +10 -7
- package/src/runtime/web-search/lib/config.mjs +4 -16
- package/src/session-runtime/agent-home-delivery.mjs +542 -204
- package/src/session-runtime/agent-profile-changes.mjs +16 -0
- package/src/session-runtime/agent-room-delivery.mjs +81 -35
- package/src/session-runtime/agent-task-sessions.mjs +811 -179
- package/src/session-runtime/agent-tool-routing.mjs +1 -0
- package/src/session-runtime/boot/apis.mjs +48 -24
- package/src/session-runtime/boot/config.mjs +1 -0
- package/src/session-runtime/boot/facade.mjs +16 -7
- package/src/session-runtime/boot/infrastructure.mjs +14 -2
- package/src/session-runtime/boot/providers.mjs +2 -2
- package/src/session-runtime/boot/shared.mjs +12 -6
- package/src/session-runtime/boot/tools.mjs +23 -3
- package/src/session-runtime/bot-status.mjs +432 -0
- package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
- package/src/session-runtime/config-lifecycle.mjs +60 -55
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
- package/src/session-runtime/held-requests.mjs +31 -33
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown.mjs +11 -3
- package/src/session-runtime/mcp-glue/connect.mjs +100 -186
- package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
- package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
- package/src/session-runtime/media-api.mjs +5 -2
- package/src/session-runtime/model-recency.mjs +1 -0
- package/src/session-runtime/model-route/route-tuning.mjs +4 -1
- package/src/session-runtime/model-route/set-route.mjs +61 -1
- package/src/session-runtime/owned-automation-run.mjs +8 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
- package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
- package/src/session-runtime/route-resolution.mjs +16 -8
- package/src/session-runtime/route-state.mjs +1 -59
- package/src/session-runtime/routine-home-turn.mjs +5 -3
- package/src/session-runtime/runtime-bootstrap.mjs +5 -3
- package/src/session-runtime/runtime-paths.mjs +11 -5
- package/src/session-runtime/runtime-review-api.mjs +4 -1
- package/src/session-runtime/runtime-tunables.mjs +6 -0
- package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
- package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
- package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
- package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
- package/src/session-runtime/services/agent-tool.mjs +6 -0
- package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
- package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
- package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
- package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
- package/src/session-runtime/services/plugin-admin.mjs +26 -26
- package/src/session-runtime/services/provider-admin.mjs +26 -6
- package/src/session-runtime/services/schedule-access.mjs +33 -4
- package/src/session-runtime/services/seeds.mjs +4 -3
- package/src/session-runtime/services/usage-dashboard.mjs +1 -1
- package/src/session-runtime/services/usage-stats-model.mjs +2 -1
- package/src/session-runtime/services/worktree-access.mjs +27 -0
- package/src/session-runtime/session-create/materialize.mjs +2 -3
- package/src/session-runtime/session-create/session-options.mjs +15 -2
- package/src/session-runtime/session-hooks.mjs +9 -17
- package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
- package/src/session-runtime/settings-compaction-api.mjs +86 -39
- package/src/session-runtime/setup-tool/executor.mjs +12 -2
- package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
- package/src/session-runtime/skills-api.mjs +12 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
- package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
- package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
- package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
- package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
- package/src/session-runtime/tool-policy-refresh.mjs +48 -369
- package/src/session-runtime/tool-surface.mjs +10 -4
- package/src/session-runtime/turn/session-ops.mjs +8 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +7 -2
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
- package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
- package/src/session-runtime/workspace/transition-steps.mjs +1 -18
- package/src/session-runtime/workspace/transitions.mjs +279 -67
- package/src/session-runtime/workspace/workspace-state.mjs +7 -6
- package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
- package/src/standalone/daemon-agent-control.mjs +4 -1
- package/src/standalone/daemon-lag-profiler.mjs +3 -1
- package/src/standalone/daemon-session-control.mjs +173 -15
- package/src/standalone/daemon-telemetry.mjs +9 -0
- package/src/standalone/daemon.mjs +33 -10
- package/src/standalone/local-session-runtime.mjs +33 -16
- package/src/standalone/session-client.mjs +7 -2
- package/src/standalone/session-protocol.mjs +8 -0
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
- package/src/standalone/session-service/agent-route-follow.mjs +69 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
- package/src/standalone/session-service.mjs +21 -0
- package/src/standalone/session-state-projection.mjs +19 -9
- package/src/standalone/session-tool-output.mjs +36 -9
- package/src/standalone/staged-import.mjs +47 -0
- package/src/tui/App.jsx +2 -0
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
- package/src/tui/app/message-selector.mjs +15 -2
- package/src/tui/app/model-options.mjs +16 -2
- package/src/tui/app/model-picker.mjs +2 -1
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
- package/src/tui/dist/index.mjs +617 -369
- package/src/tui/paste-attachments.mjs +0 -1
- package/src/tui/paste-text-policy.mjs +4 -5
- package/src/tui/prompt-history-store.mjs +16 -21
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
- package/src/tui/session/context-state.mjs +4 -0
- package/src/tui/session/notice-surface.mjs +3 -0
- package/src/tui/session/session-api/intake/abort.mjs +52 -18
- package/src/tui/session/session-api/intake/rewind.mjs +39 -4
- package/src/tui/session/session-api/media.mjs +2 -1
- package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
- package/src/tui/session/session-api/routes-write.mjs +203 -0
- package/src/tui/session/session-api/routes.mjs +23 -247
- package/src/tui/session/session-api/settings.mjs +14 -1
- package/src/tui/session/session-flow/drain.mjs +28 -3
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
- package/src/tui/session/session-flow/steering.mjs +8 -3
- package/src/tui/session/tool-approval.mjs +142 -14
- package/src/tui/session/transcript-intake.mjs +2 -0
- package/src/tui/session/turn.mjs +23 -14
- package/src/tui/transcript-tool-failures.mjs +8 -2
- package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
- package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
- package/src/rules/agent-mode/ROUTINE.md +0 -6
- package/src/session-runtime/room-member-gate.mjs +0 -48
|
@@ -6,15 +6,18 @@
|
|
|
6
6
|
* transaction ledger that makes creation rollback and deletion resumable:
|
|
7
7
|
* which resources this transaction created ('pending' is written before a
|
|
8
8
|
* resource is made, 'created' after), the deletion decision and any recovery
|
|
9
|
-
* diagnostics. Writes are atomic (temp file + rename) under a file lock
|
|
10
|
-
*
|
|
9
|
+
* diagnostics. Writes are atomic (temp file + rename) under a file lock and keep
|
|
10
|
+
* the replaced version as registry.json.bak. A damaged registry (NUL, truncated,
|
|
11
|
+
* malformed) never blocks worktree operations: its bytes are kept aside as
|
|
12
|
+
* registry.json.corrupt-<ts>, the last good copy is restored when there is one,
|
|
13
|
+
* and otherwise the registry starts empty (worktree folders on disk are never
|
|
14
|
+
* touched by that).
|
|
11
15
|
*/
|
|
12
|
-
import { readFile } from 'node:fs/promises';
|
|
13
16
|
import { join } from 'node:path';
|
|
14
|
-
import {
|
|
17
|
+
import { readJsonState, readJsonStateSync, updateJsonAtomic } from '../atomic-file.mjs';
|
|
15
18
|
import { isPlainObject } from '../object.mjs';
|
|
16
19
|
import { resolvePluginData } from '../plugin-paths.mjs';
|
|
17
|
-
import { normalizeWorktreeEntry, WORKTREE_STORE_DIR } from './contract.mjs';
|
|
20
|
+
import { normalizeWorktreeEntry, WORKTREE_SNAPSHOT_KIND, WORKTREE_STORE_DIR } from './contract.mjs';
|
|
18
21
|
|
|
19
22
|
const REGISTRY_VERSION = 1;
|
|
20
23
|
|
|
@@ -30,24 +33,21 @@ function emptyDoc() {
|
|
|
30
33
|
return { version: REGISTRY_VERSION, entries: {} };
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (error?.code === 'ENOENT') return emptyDoc();
|
|
39
|
-
throw error;
|
|
40
|
-
}
|
|
41
|
-
const value = parseJsonText(text);
|
|
42
|
-
if (!isPlainObject(value) || !isPlainObject(value.entries)) {
|
|
43
|
-
throw new Error(`Worktree registry ${path} is malformed.`);
|
|
44
|
-
}
|
|
36
|
+
const isRegistryDoc = (value) => isPlainObject(value) && isPlainObject(value.entries);
|
|
37
|
+
|
|
38
|
+
function validateDoc(value) {
|
|
39
|
+
if (!isRegistryDoc(value)) throw new Error('Worktree registry is malformed.');
|
|
45
40
|
return value;
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
/** Snapshot of every record (plain clones). */
|
|
49
44
|
export async function readRegistry() {
|
|
50
|
-
return
|
|
45
|
+
return (await readJsonState(registryPath(), { validate: validateDoc })) ?? emptyDoc();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** readRegistry for synchronous callers (the workspace admission gate). */
|
|
49
|
+
export function readRegistrySync() {
|
|
50
|
+
return readJsonStateSync(registryPath(), { validate: validateDoc }) ?? emptyDoc();
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -58,17 +58,17 @@ export async function readRegistry() {
|
|
|
58
58
|
* @returns {Promise<T>}
|
|
59
59
|
*/
|
|
60
60
|
export async function mutateRegistry(mutate) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const doc =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return result;
|
|
61
|
+
let result;
|
|
62
|
+
await updateJsonAtomic(
|
|
63
|
+
registryPath(),
|
|
64
|
+
(current) => {
|
|
65
|
+
const doc = isRegistryDoc(current) ? current : emptyDoc();
|
|
66
|
+
result = mutate(doc);
|
|
67
|
+
return doc;
|
|
69
68
|
},
|
|
70
|
-
{ timeoutMs: 15_000 }
|
|
69
|
+
{ timeoutMs: 15_000, backup: true }
|
|
71
70
|
);
|
|
71
|
+
return result;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export async function getRecord(id) {
|
|
@@ -106,5 +106,7 @@ export function entryView(record) {
|
|
|
106
106
|
mergeTarget: record.mergeTarget ?? null,
|
|
107
107
|
warnings: Array.isArray(record.warnings) ? [...record.warnings] : [],
|
|
108
108
|
disposable: record.disposable === true,
|
|
109
|
+
// The session whose turn made a snapshot (it is removed when that turn settles).
|
|
110
|
+
...(record.kind === WORKTREE_SNAPSHOT_KIND ? { holderSessionId: record.holderSessionId ?? null } : {}),
|
|
109
111
|
};
|
|
110
112
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Agent tool → session runtime channel for managed-worktree operations. The
|
|
2
|
+
// `git` tool (a builtin with no workspace authority of its own) reaches the
|
|
3
|
+
// runtime that hosts the calling Lead session through the host that runtime
|
|
4
|
+
// registers here; a caller no host claims (a delegated worker, a bare tool
|
|
5
|
+
// run) is no Lead and gets no worktree authority.
|
|
6
|
+
//
|
|
7
|
+
// Dependency-free, and shared through a global symbol like the sidebar host,
|
|
8
|
+
// so every bundle copy of this module sees one registry.
|
|
9
|
+
|
|
10
|
+
const HOSTS_KEY = Symbol.for('mixdog.worktreeToolHosts.v1');
|
|
11
|
+
|
|
12
|
+
function hosts() {
|
|
13
|
+
globalThis[HOSTS_KEY] ??= [];
|
|
14
|
+
return globalThis[HOSTS_KEY];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register a runtime's host: `sessionId()` is the Lead session it hosts now
|
|
19
|
+
* (null before one exists); `run(request, context)` performs one operation
|
|
20
|
+
* (session-runtime/workspace/worktree-tool.mjs).
|
|
21
|
+
* @param {{ sessionId: () => string|null, run: (request: object, context: object) => Promise<object> }} host
|
|
22
|
+
* @returns {() => void} Unregister.
|
|
23
|
+
*/
|
|
24
|
+
export function registerWorktreeToolHost(host) {
|
|
25
|
+
const entry = { sessionId: host.sessionId, run: host.run };
|
|
26
|
+
hosts().push(entry);
|
|
27
|
+
return () => {
|
|
28
|
+
const list = hosts();
|
|
29
|
+
const index = list.indexOf(entry);
|
|
30
|
+
if (index >= 0) list.splice(index, 1);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The host of Lead session `sessionId` (the most recently registered), or null. */
|
|
35
|
+
export function worktreeToolHost(sessionId) {
|
|
36
|
+
const id = String(sessionId || '').trim();
|
|
37
|
+
if (!id) return null;
|
|
38
|
+
return hosts().findLast((entry) => entry.sessionId() === id) ?? null;
|
|
39
|
+
}
|
|
@@ -76,6 +76,11 @@ function powerShellHost(env) {
|
|
|
76
76
|
return false;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function coreInstallable(id, manifest, env) {
|
|
80
|
+
if (!TIDY_CORE_ENGINE_IDS.includes(id) || !manifestAsset(manifest, id)) return false;
|
|
81
|
+
return id !== 'psscriptanalyzer' || powerShellHost(env);
|
|
82
|
+
}
|
|
83
|
+
|
|
79
84
|
let installJob = null;
|
|
80
85
|
let installInFlight = null;
|
|
81
86
|
// Bumped on every job mutation and every refreshed host-module probe, so the
|
|
@@ -118,6 +123,14 @@ async function cachedHostModuleProbe(engine) {
|
|
|
118
123
|
return entry?.version || '';
|
|
119
124
|
}
|
|
120
125
|
|
|
126
|
+
/** A host-module engine (PSScriptAnalyzer) whose host exists but whose first
|
|
127
|
+
* probe has not answered yet. Until then "module missing" only means "not
|
|
128
|
+
* looked at yet", so the status reports it as pending instead. */
|
|
129
|
+
function hostModuleProbing(catalog, engine) {
|
|
130
|
+
if (!catalog.managedModule || engine.source !== 'missing' || !engine.command) return false;
|
|
131
|
+
return (hostModuleCache.get(String(engine.command))?.at || 0) === 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
121
134
|
function clampPercent(value) {
|
|
122
135
|
return Math.max(0, Math.min(100, Math.round(value)));
|
|
123
136
|
}
|
|
@@ -222,14 +235,17 @@ async function runCoreInstall({
|
|
|
222
235
|
signal = null,
|
|
223
236
|
installEngines = installEnginesImpl,
|
|
224
237
|
resolveEngines = resolveEnginesImpl,
|
|
238
|
+
engines: only = null,
|
|
225
239
|
} = {}) {
|
|
226
240
|
const loaded = manifest || readEnginesManifest();
|
|
227
241
|
const toolsDir = pluginData ? managedToolsDir(pluginData) : '';
|
|
228
|
-
|
|
242
|
+
// A retry for named engines installs just those; the default is the set.
|
|
243
|
+
const wanted = Array.isArray(only) ? TIDY_CORE_ENGINE_IDS.filter((id) => only.includes(id)) : TIDY_CORE_ENGINE_IDS;
|
|
244
|
+
const job = startJob(wanted);
|
|
229
245
|
try {
|
|
230
246
|
const resolution = await resolveEngines({
|
|
231
247
|
cwd: resolutionScope(toolsDir),
|
|
232
|
-
engineIds: [...
|
|
248
|
+
engineIds: [...wanted],
|
|
233
249
|
pluginData,
|
|
234
250
|
manifest: loaded,
|
|
235
251
|
env,
|
|
@@ -238,7 +254,7 @@ async function runCoreInstall({
|
|
|
238
254
|
});
|
|
239
255
|
const resolved = new Map((resolution.engines || []).map((engine) => [engine.id, engine]));
|
|
240
256
|
const pending = [];
|
|
241
|
-
for (const id of
|
|
257
|
+
for (const id of wanted) {
|
|
242
258
|
const catalog = ENGINE_CATALOG[id];
|
|
243
259
|
const engine = resolved.get(id);
|
|
244
260
|
if (id === 'psscriptanalyzer' && !powerShellHost(env)) {
|
|
@@ -400,6 +416,7 @@ function withInstallStatus(inventory) {
|
|
|
400
416
|
toolsDir: inventory.toolsDir,
|
|
401
417
|
core: [...inventory.core],
|
|
402
418
|
engines: inventory.engines.map((engine) => ({ ...engine })),
|
|
419
|
+
probing: inventory.probing,
|
|
403
420
|
installing: tidyInstallStatus(),
|
|
404
421
|
};
|
|
405
422
|
}
|
|
@@ -424,6 +441,7 @@ async function buildEngineInventory({ pluginData, manifest, env, probeVersions,
|
|
|
424
441
|
const source = statusSource(engine.source);
|
|
425
442
|
const version = engine.version || '';
|
|
426
443
|
const installHint = engine.installHint || catalog.installHint || '';
|
|
444
|
+
const probing = hostModuleProbing(catalog, engine);
|
|
427
445
|
return {
|
|
428
446
|
id,
|
|
429
447
|
title: catalog.title || id,
|
|
@@ -437,7 +455,11 @@ async function buildEngineInventory({ pluginData, manifest, env, probeVersions,
|
|
|
437
455
|
// Managed engines carry their on-disk cost; a host binary is the user's.
|
|
438
456
|
...(source === 'managed' ? { bytes: managedEngineBytes(pluginData, id, version) } : {}),
|
|
439
457
|
...(source === 'missing' && installHint ? { installHint } : {}),
|
|
458
|
+
// The host probe has not answered: pending, neither installed nor missing.
|
|
459
|
+
...(probing ? { probing: true } : {}),
|
|
460
|
+
// A missing core engine the Install/Retry control can fetch right now.
|
|
461
|
+
...(source === 'missing' && !probing && coreInstallable(id, loaded, env) ? { installable: true } : {}),
|
|
440
462
|
};
|
|
441
463
|
});
|
|
442
|
-
return { toolsDir, core: [...TIDY_CORE_ENGINE_IDS], engines };
|
|
464
|
+
return { toolsDir, core: [...TIDY_CORE_ENGINE_IDS], engines, probing: engines.some((engine) => engine.probing) };
|
|
443
465
|
}
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
// Policy gating happens BEFORE any network call: 'never' refuses, 'ask' returns
|
|
5
5
|
// needsApproval unless the call carried approveDownloads:true, 'auto' proceeds.
|
|
6
6
|
// Toolchain engines (rustfmt, gofmt, zig fmt, ...) are never downloadable.
|
|
7
|
-
import { chmodSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from 'node:fs';
|
|
7
|
+
import { chmodSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from 'node:fs';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { MAX_NATIVE_BINARY_DOWNLOAD_BYTES, streamResponseToFile } from '../shared/bounded-download.mjs';
|
|
11
11
|
import { platformEntryKey, platformKey, sha256File } from '../shared/native-asset.mjs';
|
|
12
|
+
import { fileHasData } from '../shared/file-has-data.mjs';
|
|
12
13
|
import { readJsonSafe } from '../shared/json-file.mjs';
|
|
13
14
|
import { ENGINE_CATALOG } from './engines.mjs';
|
|
14
15
|
import { extractArchive } from './extract.mjs';
|
|
@@ -51,7 +52,7 @@ export function managedEngineBinary({ id, manifest, pluginData }) {
|
|
|
51
52
|
const binPath = String(found.asset.binPath || '');
|
|
52
53
|
if (!binPath) return null;
|
|
53
54
|
const full = join(managedEngineDir(pluginData, id, found.version), binPath);
|
|
54
|
-
return
|
|
55
|
+
return fileHasData(full) ? { path: full, version: found.version } : null;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
/** Throws unless `filePath` hashes to `expected`. */
|
|
@@ -148,44 +149,109 @@ export function planInstall({ ids = [], manifest, pluginData = '', policy = 'aut
|
|
|
148
149
|
return { targets, present, errors, needsApproval: null };
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
// A network switch can leave a socket open that never delivers another byte;
|
|
153
|
+
// without a deadline the install (and the single in-flight job behind it)
|
|
154
|
+
// would wait forever.
|
|
155
|
+
const DEFAULT_STALL_TIMEOUT_MS = 60_000;
|
|
156
|
+
// A `.tmp-<pid>-<ms>-…` staging dir whose owner crashed is never cleaned by
|
|
157
|
+
// that owner; an hour is far beyond any live download.
|
|
158
|
+
const STALE_TMP_AGE_MS = 60 * 60 * 1000;
|
|
159
|
+
|
|
160
|
+
function pidAlive(pid) {
|
|
161
|
+
try {
|
|
162
|
+
process.kill(pid, 0);
|
|
163
|
+
return true;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
return error?.code === 'EPERM';
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Remove staging dirs a killed install left behind (dead owner, or ancient). */
|
|
170
|
+
function reclaimStaleStaging(engineRoot) {
|
|
171
|
+
let names;
|
|
172
|
+
try {
|
|
173
|
+
names = readdirSync(engineRoot);
|
|
174
|
+
} catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (const name of names) {
|
|
178
|
+
const match = /^\.tmp-(\d+)-(\d+)-/.exec(name);
|
|
179
|
+
if (!match) continue;
|
|
180
|
+
const pid = Number(match[1]);
|
|
181
|
+
const ownerGone = pid !== process.pid && !pidAlive(pid);
|
|
182
|
+
if (!ownerGone && Date.now() - Number(match[2]) < STALE_TMP_AGE_MS) continue;
|
|
183
|
+
try {
|
|
184
|
+
rmSync(join(engineRoot, name), { recursive: true, force: true });
|
|
185
|
+
} catch {
|
|
186
|
+
/* best-effort */
|
|
187
|
+
}
|
|
156
188
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function downloadAsset(target, tmpDir, fetchFn, signal, onProgress, stallTimeoutMs) {
|
|
192
|
+
const archiveFile = join(tmpDir, `asset-${target.id}`);
|
|
193
|
+
const stalled = new AbortController();
|
|
194
|
+
const merged = signal ? AbortSignal.any([signal, stalled.signal]) : stalled.signal;
|
|
195
|
+
let timer;
|
|
196
|
+
const arm = () => {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
timer = setTimeout(() => stalled.abort(), stallTimeoutMs);
|
|
199
|
+
};
|
|
200
|
+
arm();
|
|
201
|
+
try {
|
|
202
|
+
const response = await fetchFn(target.url, { signal: merged, redirect: 'follow' });
|
|
203
|
+
if (!response?.ok) {
|
|
204
|
+
throw new Error(`${LABEL} ${target.id}: HTTP ${response?.status || 0} for ${target.url}`);
|
|
205
|
+
}
|
|
206
|
+
await streamResponseToFile(response, archiveFile, {
|
|
207
|
+
maxBytes: MAX_NATIVE_BINARY_DOWNLOAD_BYTES,
|
|
208
|
+
label: `${LABEL} ${target.id}`,
|
|
209
|
+
signal: merged,
|
|
210
|
+
// Byte progress per engine: the desktop card renders it live, so the
|
|
211
|
+
// callback carries the engine id the bytes belong to. Any bytes also
|
|
212
|
+
// prove the connection is alive.
|
|
213
|
+
onProgress: ({ downloaded, total }) => {
|
|
214
|
+
arm();
|
|
215
|
+
if (typeof onProgress === 'function') {
|
|
216
|
+
onProgress({ id: target.id, receivedBytes: downloaded, totalBytes: total });
|
|
167
217
|
}
|
|
168
|
-
|
|
169
|
-
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (stalled.signal.aborted && !signal?.aborted) {
|
|
222
|
+
throw new Error(`${LABEL} ${target.id}: download stalled (no data for ${stallTimeoutMs / 1000}s)`);
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
225
|
+
} finally {
|
|
226
|
+
clearTimeout(timer);
|
|
227
|
+
}
|
|
170
228
|
return archiveFile;
|
|
171
229
|
}
|
|
172
230
|
|
|
173
231
|
/** Download + verify + extract one engine into its versioned managed dir. */
|
|
174
232
|
export async function installEngine(
|
|
175
233
|
target,
|
|
176
|
-
{
|
|
234
|
+
{
|
|
235
|
+
pluginData,
|
|
236
|
+
fetchFn = globalThis.fetch,
|
|
237
|
+
signal = null,
|
|
238
|
+
onProgress = null,
|
|
239
|
+
stallTimeoutMs = DEFAULT_STALL_TIMEOUT_MS,
|
|
240
|
+
} = {}
|
|
177
241
|
) {
|
|
178
242
|
const engineRoot = join(managedToolsDir(pluginData), target.id);
|
|
179
243
|
const finalDir = managedEngineDir(pluginData, target.id, target.version);
|
|
180
244
|
const binFull = join(finalDir, target.binPath);
|
|
181
|
-
|
|
245
|
+
// An engine binary a crash left NUL-filled is not present: it is installed again.
|
|
246
|
+
if (fileHasData(binFull)) {
|
|
182
247
|
return { id: target.id, version: target.version, path: binFull, bytes: 0, status: 'present' };
|
|
183
248
|
}
|
|
184
249
|
mkdirSync(engineRoot, { recursive: true });
|
|
250
|
+
reclaimStaleStaging(engineRoot);
|
|
185
251
|
const tmpDir = join(engineRoot, `.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
186
252
|
mkdirSync(tmpDir, { recursive: true });
|
|
187
253
|
try {
|
|
188
|
-
const archiveFile = await downloadAsset(target, tmpDir, fetchFn, signal, onProgress);
|
|
254
|
+
const archiveFile = await downloadAsset(target, tmpDir, fetchFn, signal, onProgress, stallTimeoutMs);
|
|
189
255
|
await verifyDownloadDigest(archiveFile, target.sha256);
|
|
190
256
|
const bytes = statSync(archiveFile).size;
|
|
191
257
|
const stageDir = join(tmpDir, 'stage');
|
|
@@ -207,6 +273,7 @@ export async function installEngine(
|
|
|
207
273
|
}
|
|
208
274
|
}
|
|
209
275
|
try {
|
|
276
|
+
if (existsSync(finalDir) && !fileHasData(binFull)) rmSync(finalDir, { recursive: true, force: true });
|
|
210
277
|
renameSync(stageDir, finalDir);
|
|
211
278
|
} catch (error) {
|
|
212
279
|
// A concurrent install won the race; its verified copy is equivalent.
|
|
@@ -235,6 +302,7 @@ export async function installEngines({
|
|
|
235
302
|
fetchFn = globalThis.fetch,
|
|
236
303
|
signal = null,
|
|
237
304
|
onProgress = null,
|
|
305
|
+
stallTimeoutMs = undefined,
|
|
238
306
|
} = {}) {
|
|
239
307
|
const loaded = manifest || readEnginesManifest();
|
|
240
308
|
const plan = planInstall({ ids, manifest: loaded, pluginData, policy, approveDownloads });
|
|
@@ -249,7 +317,7 @@ export async function installEngines({
|
|
|
249
317
|
}
|
|
250
318
|
for (const target of plan.targets) {
|
|
251
319
|
try {
|
|
252
|
-
installed.push(await installEngine(target, { pluginData, fetchFn, signal, onProgress }));
|
|
320
|
+
installed.push(await installEngine(target, { pluginData, fetchFn, signal, onProgress, stallTimeoutMs }));
|
|
253
321
|
} catch (error) {
|
|
254
322
|
errors.push({ id: target.id, error: error?.message || String(error) });
|
|
255
323
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// binaries and file paths are untrusted input, so no value ever reaches a shell
|
|
5
5
|
// parser.
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
|
-
import { existsSync, statSync } from 'node:fs';
|
|
7
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
8
8
|
import { delimiter, extname, isAbsolute, join } from 'node:path';
|
|
9
9
|
|
|
10
10
|
const DEFAULT_PROCESS_TIMEOUT_MS = 90_000;
|
|
@@ -36,19 +36,52 @@ function isExecutableFile(candidate) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const pathDirs = (env) =>
|
|
40
|
+
String(env.PATH || env.Path || '')
|
|
41
|
+
.split(delimiter)
|
|
42
|
+
.map((value) => value.trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
|
|
45
|
+
const nameKey = (value) => (process.platform === 'win32' ? value.toLowerCase() : value);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* One PATH's directory listings, read once per directory on first use, for a
|
|
49
|
+
* batch of `which` lookups. Resolving the whole engine catalog probed every
|
|
50
|
+
* PATH dir × PATHEXT extension × engine name with its own statSync (≈17k
|
|
51
|
+
* synchronous stats on a 65-entry PATH); with an index a name that is on no
|
|
52
|
+
* directory costs no filesystem call at all.
|
|
53
|
+
*/
|
|
54
|
+
export function createPathIndex(env = process.env) {
|
|
55
|
+
const listings = new Map(); // dir -> Set of entry names, or null (unlistable)
|
|
56
|
+
return {
|
|
57
|
+
dirs: pathDirs(env),
|
|
58
|
+
has(dir, candidate) {
|
|
59
|
+
if (!listings.has(dir)) {
|
|
60
|
+
let names = null;
|
|
61
|
+
try {
|
|
62
|
+
names = new Set(readdirSync(dir).map(nameKey));
|
|
63
|
+
} catch {
|
|
64
|
+
// Unlistable (no list permission, gone): probe it per candidate.
|
|
65
|
+
}
|
|
66
|
+
listings.set(dir, names);
|
|
67
|
+
}
|
|
68
|
+
const names = listings.get(dir);
|
|
69
|
+
return names ? names.has(nameKey(candidate)) : true;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** First match for `name` on PATH, or null. Pure filesystem lookup, no spawn.
|
|
75
|
+
* `index` (createPathIndex over the same env) answers from directory listings. */
|
|
76
|
+
export function which(name, { env = process.env, index = null } = {}) {
|
|
41
77
|
const bin = String(name || '').trim();
|
|
42
78
|
if (!bin) return null;
|
|
43
79
|
if (isAbsolute(bin) || bin.includes('/') || bin.includes('\\')) {
|
|
44
80
|
return existsSync(bin) ? bin : null;
|
|
45
81
|
}
|
|
46
|
-
const
|
|
47
|
-
.split(delimiter)
|
|
48
|
-
.map((value) => value.trim())
|
|
49
|
-
.filter(Boolean);
|
|
50
|
-
for (const dir of dirs) {
|
|
82
|
+
for (const dir of index ? index.dirs : pathDirs(env)) {
|
|
51
83
|
for (const candidate of executableCandidates(bin)) {
|
|
84
|
+
if (index && !index.has(dir, candidate)) continue;
|
|
52
85
|
const full = join(dir, candidate);
|
|
53
86
|
if (isExecutableFile(full)) return full;
|
|
54
87
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { existsSync, readFileSync } from 'node:fs';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { ENGINE_CATALOG, ENGINE_IDS, engineEntry, enginesForLanguages } from './engines.mjs';
|
|
10
|
-
import { mapLimit, runProcess, which } from './process.mjs';
|
|
10
|
+
import { createPathIndex, mapLimit, runProcess, which } from './process.mjs';
|
|
11
11
|
import { managedEngineBinary, manifestAsset, readEnginesManifest } from './install.mjs';
|
|
12
12
|
|
|
13
13
|
const DOWNLOAD_POLICIES = Object.freeze(['ask', 'auto', 'never']);
|
|
@@ -113,9 +113,9 @@ export function projectConfigFileFor(cwd, entry) {
|
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function pathBinary(entry, env) {
|
|
116
|
+
function pathBinary(entry, env, index) {
|
|
117
117
|
for (const name of [entry.bin, ...(entry.altBins || [])]) {
|
|
118
|
-
const found = which(name, { env });
|
|
118
|
+
const found = which(name, { env, index });
|
|
119
119
|
if (found) return { path: found, bin: name };
|
|
120
120
|
}
|
|
121
121
|
return null;
|
|
@@ -135,7 +135,7 @@ function installableFlag(entry, manifest) {
|
|
|
135
135
|
return entry.managed && manifestAsset(manifest, entry.id) ? { installable: true } : {};
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function resolveOne({ cwd, entry, config, env, manifest, pluginData }) {
|
|
138
|
+
function resolveOne({ cwd, entry, config, env, manifest, pluginData, pathIndex }) {
|
|
139
139
|
const base = {
|
|
140
140
|
id: entry.id,
|
|
141
141
|
kind: entry.kind,
|
|
@@ -159,7 +159,7 @@ function resolveOne({ cwd, entry, config, env, manifest, pluginData }) {
|
|
|
159
159
|
return { ...base, source: 'missing', path: '', missing: true };
|
|
160
160
|
}
|
|
161
161
|
if (entry.managedModule) {
|
|
162
|
-
const host = pathBinary(entry, env);
|
|
162
|
+
const host = pathBinary(entry, env, pathIndex);
|
|
163
163
|
if (!host) {
|
|
164
164
|
return {
|
|
165
165
|
...base,
|
|
@@ -191,7 +191,7 @@ function resolveOne({ cwd, entry, config, env, manifest, pluginData }) {
|
|
|
191
191
|
...installableFlag(entry, manifest),
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
|
-
const onPath = pathBinary(entry, env);
|
|
194
|
+
const onPath = pathBinary(entry, env, pathIndex);
|
|
195
195
|
if (onPath) return { ...base, source: 'path', path: onPath.path, ...commandFor(entry, onPath.path) };
|
|
196
196
|
if (entry.managed && pluginData) {
|
|
197
197
|
const managed = managedEngineBinary({ id: entry.id, manifest, pluginData });
|
|
@@ -282,9 +282,12 @@ export async function resolveEngines({
|
|
|
282
282
|
else if (languages.length > 0) requested = enginesForLanguages(languages);
|
|
283
283
|
const unknown = engineIds.filter((id) => !ENGINE_IDS.includes(id));
|
|
284
284
|
|
|
285
|
+
// One listing per PATH dir for the whole batch: this runs synchronously on
|
|
286
|
+
// the host loop (the desktop's Code Tidy status read at startup).
|
|
287
|
+
const pathIndex = createPathIndex(env);
|
|
285
288
|
const resolved = requested.map((id) => {
|
|
286
289
|
const entry = ENGINE_CATALOG[id];
|
|
287
|
-
const result = resolveOne({ cwd, entry, config, env, manifest: loadedManifest, pluginData });
|
|
290
|
+
const result = resolveOne({ cwd, entry, config, env, manifest: loadedManifest, pluginData, pathIndex });
|
|
288
291
|
const configFile = projectConfigFileFor(cwd, entry);
|
|
289
292
|
if (configFile) result.configFile = configFile;
|
|
290
293
|
if (entry.requiresConfig && !configFile && result.source !== 'project-config') {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { resolvePluginData } from '../../shared/plugin-paths.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { readJsonStateSync, writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
|
|
5
5
|
|
|
6
6
|
// Unified mode: web search shares the plugin data dir with the rest of mixdog.
|
|
7
7
|
const SHARED_DATA_DIR = resolvePluginData();
|
|
@@ -19,22 +19,10 @@ export function ensureDataDir() {
|
|
|
19
19
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// usage/cache state: a damaged file (empty, NUL-filled, truncated) is kept as
|
|
23
|
+
// <file>.corrupt-<ts> and reads as `fallback`, so search/fetch keep working.
|
|
22
24
|
export function readJson(filePath, fallback) {
|
|
23
|
-
|
|
24
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(raw);
|
|
27
|
-
} catch (parseErr) {
|
|
28
|
-
try {
|
|
29
|
-
renameWithRetrySync(filePath, `${filePath}.corrupt.${Date.now()}`);
|
|
30
|
-
} catch {}
|
|
31
|
-
process.stderr.write(`[web-search-config] corrupt JSON backed up: ${filePath}\n`);
|
|
32
|
-
throw parseErr;
|
|
33
|
-
}
|
|
34
|
-
} catch (err) {
|
|
35
|
-
if (err.code === 'ENOENT') return fallback;
|
|
36
|
-
throw err;
|
|
37
|
-
}
|
|
25
|
+
return readJsonStateSync(filePath, { fallback });
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
export function writeJson(filePath, value) {
|