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
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
reviewSectionTruncated,
|
|
55
55
|
} from './review-diff.mjs';
|
|
56
56
|
import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
|
|
57
|
+
import { runGitOffThread } from './git-runner.mjs';
|
|
57
58
|
import { cleanString as clean } from './clean.mjs';
|
|
58
59
|
import { createLineDiffBudget, lineDiffHunks, unifiedHunksBody } from './bounded-line-diff.mjs';
|
|
59
60
|
|
|
@@ -74,7 +75,11 @@ import { createLineDiffBudget, lineDiffHunks, unifiedHunksBody } from './bounded
|
|
|
74
75
|
// baseline of the session-wide review.
|
|
75
76
|
//
|
|
76
77
|
// Worker trackers remain separate until the worker completes: then its
|
|
77
|
-
// segments, owned paths and counts join the
|
|
78
|
+
// segments, owned paths and counts join the Lead turn that is running at that
|
|
79
|
+
// moment, or — when the Lead's turn already ended (sealed or sealing) — the
|
|
80
|
+
// next Lead turn that opens or reopens: the turn that receives the worker's
|
|
81
|
+
// completion and reports it. A worker's edits so land in exactly one turn
|
|
82
|
+
// record, and never in a record that was already frozen.
|
|
78
83
|
|
|
79
84
|
const DISABLED = /^(0|false|off)$/i.test(String(process.env.MIXDOG_TURN_SNAPSHOT || ''));
|
|
80
85
|
const TURN_CACHE_MAX = 32;
|
|
@@ -98,18 +103,46 @@ const _changeMutations = new Map();
|
|
|
98
103
|
// `${sessionId}\0${checkpointId}` → path keys whose worktree state is unknown
|
|
99
104
|
// and whose record could not even be marked so (the store refused every write).
|
|
100
105
|
const _unknownChangeStates = new Map();
|
|
106
|
+
// Owner session → worker key → completed worker review waiting for the
|
|
107
|
+
// owner's next turn (its turn had ended when they completed), in completion
|
|
108
|
+
// order. Bounded like a turn's agents (trimWorkerReviews).
|
|
109
|
+
const _parkedWorkerReviews = new Map();
|
|
110
|
+
// Owner session → how many times its change history was deleted: a worker
|
|
111
|
+
// bound before a delete never writes into the history after it.
|
|
112
|
+
const _historyEpochs = new Map();
|
|
113
|
+
|
|
114
|
+
/** A parked review owns its worker's bound tracker content (`release`). */
|
|
115
|
+
function releaseParkedReview(review) {
|
|
116
|
+
if (review.release) releaseDiffTrackerContent(review.source);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function dropParkedWorkerReviews(ownerSessionId) {
|
|
120
|
+
for (const review of _parkedWorkerReviews.get(ownerSessionId)?.values() || []) releaseParkedReview(review);
|
|
121
|
+
_parkedWorkerReviews.delete(ownerSessionId);
|
|
122
|
+
}
|
|
101
123
|
|
|
102
124
|
function trimTurnCache() {
|
|
103
125
|
while (_turnsBySession.size > TURN_CACHE_MAX) {
|
|
104
126
|
const oldest = _turnsBySession.keys().next().value;
|
|
105
127
|
if (oldest === undefined) break;
|
|
106
128
|
_turnsBySession.delete(oldest);
|
|
129
|
+
dropParkedWorkerReviews(oldest);
|
|
107
130
|
for (const [sessionId, tracker] of _diffTrackersBySession) {
|
|
108
131
|
if (sessionId === oldest || tracker.ownerSessionId === oldest) {
|
|
109
132
|
_diffTrackersBySession.delete(sessionId);
|
|
133
|
+
// A bound worker's content is read by nothing else; the Lead's own
|
|
134
|
+
// tracker may still be sealing, so it is only unregistered.
|
|
135
|
+
if (sessionId !== oldest) releaseDiffTrackerContent(tracker);
|
|
110
136
|
}
|
|
111
137
|
}
|
|
112
138
|
}
|
|
139
|
+
// Parked reviews of a Lead with no turn in memory (a worker resumed after a
|
|
140
|
+
// restart) are bounded on their own: oldest owner first.
|
|
141
|
+
while (_parkedWorkerReviews.size > TURN_CACHE_MAX) {
|
|
142
|
+
const oldest = _parkedWorkerReviews.keys().next().value;
|
|
143
|
+
if (oldest === undefined) break;
|
|
144
|
+
dropParkedWorkerReviews(oldest);
|
|
145
|
+
}
|
|
113
146
|
}
|
|
114
147
|
|
|
115
148
|
function mergePatches(values) {
|
|
@@ -694,13 +727,17 @@ function mergeTrackedContent(target, source) {
|
|
|
694
727
|
}
|
|
695
728
|
}
|
|
696
729
|
|
|
697
|
-
|
|
730
|
+
/** Bound worker reviews keyed by worker (a turn's agents, an owner's parked
|
|
731
|
+
* reviews): oldest out first, by count and retained patch bytes. */
|
|
732
|
+
function trimWorkerReviews(reviews, evicted = () => {}) {
|
|
698
733
|
const retainedBytes = () =>
|
|
699
|
-
[...
|
|
700
|
-
while (
|
|
701
|
-
const oldest =
|
|
734
|
+
[...reviews.values()].reduce((total, review) => total + Buffer.byteLength(review.patch || '', 'utf8'), 0);
|
|
735
|
+
while (reviews.size > MAX_AGENT_REVIEWS_PER_TURN || retainedBytes() > MAX_AGENT_REVIEW_BYTES_PER_TURN) {
|
|
736
|
+
const oldest = reviews.keys().next().value;
|
|
702
737
|
if (oldest === undefined) break;
|
|
703
|
-
|
|
738
|
+
const review = reviews.get(oldest);
|
|
739
|
+
reviews.delete(oldest);
|
|
740
|
+
evicted(review);
|
|
704
741
|
}
|
|
705
742
|
}
|
|
706
743
|
|
|
@@ -740,6 +777,7 @@ function continueTurnSnapshot(ownerSessionId) {
|
|
|
740
777
|
tracker.sealed = false;
|
|
741
778
|
tracker.sealEpoch += 1;
|
|
742
779
|
markWorktreeContention(ownerSessionId, tracker);
|
|
780
|
+
adoptParkedWorkerReviews(ownerSessionId, turn, tracker);
|
|
743
781
|
return true;
|
|
744
782
|
}
|
|
745
783
|
|
|
@@ -756,16 +794,8 @@ export async function beginTurnSnapshot(worktree, sessionId, options = {}) {
|
|
|
756
794
|
// Turn start and Undo/Reapply share the per-session lock: a start waits for
|
|
757
795
|
// an in-flight mutation, and a mutation queued after this start re-checks
|
|
758
796
|
// under the lock and finds the turn running.
|
|
759
|
-
// Late-worker re-seals still queued for the previous turn run first, in
|
|
760
|
-
// this same step — including one a worker queued while an earlier one ran —
|
|
761
|
-
// so the workers' edits stay in that turn's record and nothing the new turn
|
|
762
|
-
// writes can reach it. (A queued re-seal implies a chain.)
|
|
763
797
|
const { token, tracker } = _changeMutations.has(ownerSessionId)
|
|
764
|
-
? await serializeChangeMutation(ownerSessionId, async () =>
|
|
765
|
-
const previous = _diffTrackersBySession.get(ownerSessionId);
|
|
766
|
-
while (previous?.resealQueued) await runQueuedReseal(ownerSessionId, previous).catch(() => {});
|
|
767
|
-
return openTurnSnapshot(worktree, ownerSessionId, options);
|
|
768
|
-
})
|
|
798
|
+
? await serializeChangeMutation(ownerSessionId, async () => openTurnSnapshot(worktree, ownerSessionId, options))
|
|
769
799
|
: openTurnSnapshot(worktree, ownerSessionId, options);
|
|
770
800
|
if (!tracker) return token;
|
|
771
801
|
try {
|
|
@@ -795,15 +825,15 @@ function openTurnSnapshot(worktree, ownerSessionId, options) {
|
|
|
795
825
|
// worktree lock ahead of this turn's baseline.
|
|
796
826
|
const prior = _diffTrackersBySession.get(ownerSessionId);
|
|
797
827
|
if (prior && prior.ownerGeneration > 0 && !prior.sealed) void sealTurn(ownerSessionId, prior).catch(() => {});
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}
|
|
828
|
+
// Workers still running keep their bound trackers across the new turn:
|
|
829
|
+
// their edits join whichever turn is running when they complete.
|
|
801
830
|
_turnsBySession.delete(ownerSessionId);
|
|
802
|
-
|
|
831
|
+
const turn = {
|
|
803
832
|
generation,
|
|
804
833
|
checkpointId,
|
|
805
834
|
agents: new Map(),
|
|
806
|
-
}
|
|
835
|
+
};
|
|
836
|
+
_turnsBySession.set(ownerSessionId, turn);
|
|
807
837
|
const worktreeRequest = pathKey(worktree);
|
|
808
838
|
const tracker = resetDiffTracker(ownerSessionId, {
|
|
809
839
|
ownerGeneration: generation,
|
|
@@ -812,7 +842,10 @@ function openTurnSnapshot(worktree, ownerSessionId, options) {
|
|
|
812
842
|
worktreeRoot: resolve(clean(worktree) || '.'),
|
|
813
843
|
});
|
|
814
844
|
trimTurnCache();
|
|
815
|
-
if (tracker)
|
|
845
|
+
if (tracker) {
|
|
846
|
+
markWorktreeContention(ownerSessionId, tracker);
|
|
847
|
+
adoptParkedWorkerReviews(ownerSessionId, turn, tracker);
|
|
848
|
+
}
|
|
816
849
|
_turnOpenedHookForTest?.(ownerSessionId, checkpointId);
|
|
817
850
|
return { token: { generation, checkpointId }, tracker };
|
|
818
851
|
}
|
|
@@ -824,7 +857,9 @@ export function _setTurnOpenedHookForTest(hook) {
|
|
|
824
857
|
_turnOpenedHookForTest = typeof hook === 'function' ? hook : null;
|
|
825
858
|
}
|
|
826
859
|
|
|
827
|
-
/** Bind one worker execution to
|
|
860
|
+
/** Bind one worker execution to its Lead. A Lead with no turn in memory (a
|
|
861
|
+
* worker resumed after a restart) binds generation 0: its completion waits
|
|
862
|
+
* for the Lead's next turn. */
|
|
828
863
|
export function beginAgentTurnReview(ownerSessionId, childSessionId, meta = {}) {
|
|
829
864
|
if (DISABLED) return null;
|
|
830
865
|
const owner = clean(ownerSessionId);
|
|
@@ -832,24 +867,21 @@ export function beginAgentTurnReview(ownerSessionId, childSessionId, meta = {})
|
|
|
832
867
|
// Durable: the worker's edits join the Lead's session review even when it
|
|
833
868
|
// runs its own session turn, and whatever the turn's agent list keeps.
|
|
834
869
|
recordReviewOwner(child, owner);
|
|
835
|
-
|
|
836
|
-
|
|
870
|
+
if (!owner || !child) return null;
|
|
871
|
+
const generation = _turnsBySession.get(owner)?.generation || 0;
|
|
837
872
|
const existingTracker = _diffTrackersBySession.get(child);
|
|
838
|
-
if (
|
|
839
|
-
!existingTracker ||
|
|
840
|
-
existingTracker.ownerSessionId !== owner ||
|
|
841
|
-
existingTracker.ownerGeneration !== turn.generation
|
|
842
|
-
) {
|
|
873
|
+
if (!existingTracker || existingTracker.ownerSessionId !== owner || existingTracker.ownerGeneration !== generation) {
|
|
843
874
|
resetDiffTracker(child, {
|
|
844
875
|
ownerSessionId: owner,
|
|
845
|
-
ownerGeneration:
|
|
876
|
+
ownerGeneration: generation,
|
|
846
877
|
});
|
|
847
878
|
}
|
|
848
879
|
_agentTurnSeq += 1;
|
|
849
880
|
return {
|
|
850
881
|
id: `agent-review-${_agentTurnSeq}`,
|
|
851
882
|
ownerSessionId: owner,
|
|
852
|
-
generation
|
|
883
|
+
generation,
|
|
884
|
+
historyEpoch: _historyEpochs.get(owner) || 0,
|
|
853
885
|
sessionId: child,
|
|
854
886
|
agent: clean(meta.agent) || null,
|
|
855
887
|
tag: clean(meta.tag) || null,
|
|
@@ -857,62 +889,145 @@ export function beginAgentTurnReview(ownerSessionId, childSessionId, meta = {})
|
|
|
857
889
|
};
|
|
858
890
|
}
|
|
859
891
|
|
|
860
|
-
/**
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
892
|
+
/** The Lead turn is running: not sealed, not sealing. */
|
|
893
|
+
const turnRunning = (turn, tracker) =>
|
|
894
|
+
Boolean(turn && tracker) &&
|
|
895
|
+
tracker.ownerGeneration === turn.generation &&
|
|
896
|
+
!tracker.sealed &&
|
|
897
|
+
!tracker.freezing &&
|
|
898
|
+
!tracker.discarded;
|
|
899
|
+
|
|
900
|
+
/** Attribute one completed worker review to `turn` and fold its own segments,
|
|
901
|
+
* owned paths and counts into the turn's tracker. */
|
|
902
|
+
function joinWorkerReview(turn, ownerTracker, review) {
|
|
903
|
+
const { key, source, tracked, patch } = review;
|
|
904
|
+
const prior = turn.agents.get(key);
|
|
905
|
+
if (!patch) {
|
|
906
|
+
if (!(tracked && prior)) return false;
|
|
907
|
+
turn.agents.delete(key);
|
|
908
|
+
return true;
|
|
909
|
+
}
|
|
910
|
+
turn.agents.delete(key);
|
|
911
|
+
turn.agents.set(key, {
|
|
912
|
+
sessionId: key,
|
|
913
|
+
agent: review.agent || prior?.agent || null,
|
|
914
|
+
tag: review.tag || prior?.tag || null,
|
|
915
|
+
patch: tracked ? patch : mergePatches([prior?.patch, patch]),
|
|
916
|
+
});
|
|
917
|
+
if (source && ownerTracker !== source) foldWorkerTracker(ownerTracker, source);
|
|
918
|
+
trimWorkerReviews(turn.agents);
|
|
919
|
+
return true;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/** Fold a finished worker's owned paths, counts and own segments into
|
|
923
|
+
* `target`. */
|
|
924
|
+
function foldWorkerTracker(target, source) {
|
|
925
|
+
for (const path of source.ownedPaths) target.ownedPaths.add(path);
|
|
926
|
+
mergeOwnStats(target.ownStats, source.ownStats);
|
|
927
|
+
mergeTrackedContent(target, source);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/** One parked review of a worker that completed again before its Lead's next
|
|
931
|
+
* turn: both runs' sources fold into one tracker this review owns — the
|
|
932
|
+
* older run's own bound tracker, else a fresh one, never a worker session's
|
|
933
|
+
* own tracker — exactly as both would have joined a running turn. The patch
|
|
934
|
+
* is the merged tracker's cumulative view when tracked, else the merged
|
|
935
|
+
* apply_patch diffs. */
|
|
936
|
+
function mergeParkedReview(prior, review) {
|
|
937
|
+
const sources = [prior.source, review.source].filter(Boolean);
|
|
938
|
+
let source = null;
|
|
939
|
+
if (sources.length > 0) {
|
|
940
|
+
source = prior.release && prior.source ? prior.source : createDiffTracker();
|
|
941
|
+
for (const other of sources) if (other !== source) foldWorkerTracker(source, other);
|
|
942
|
+
}
|
|
943
|
+
releaseParkedReview(review);
|
|
944
|
+
const tracked = prior.tracked || review.tracked;
|
|
945
|
+
return {
|
|
946
|
+
key: review.key,
|
|
947
|
+
source,
|
|
948
|
+
tracked,
|
|
949
|
+
patch: tracked ? trackerView(source).patch : mergePatches([prior.patch, review.patch]),
|
|
950
|
+
agent: review.agent || prior.agent,
|
|
951
|
+
tag: review.tag || prior.tag,
|
|
952
|
+
release: Boolean(source),
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/** Park a completed worker review for its Lead's next turn. A newer
|
|
957
|
+
* completion of the same worker merges into the parked one (every run's
|
|
958
|
+
* edits are kept, as a running turn keeps them); the parked reviews are
|
|
959
|
+
* bounded by the turn's own limits, and an evicted one releases its tracker
|
|
960
|
+
* content. */
|
|
961
|
+
function parkWorkerReview(ownerSessionId, review) {
|
|
962
|
+
const parked = _parkedWorkerReviews.get(ownerSessionId) || new Map();
|
|
963
|
+
const prior = parked.get(review.key);
|
|
964
|
+
if (!prior && !review.patch) {
|
|
965
|
+
releaseParkedReview(review);
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
parked.delete(review.key);
|
|
969
|
+
parked.set(review.key, prior ? mergeParkedReview(prior, review) : review);
|
|
970
|
+
trimWorkerReviews(parked, releaseParkedReview);
|
|
971
|
+
_parkedWorkerReviews.set(ownerSessionId, parked);
|
|
972
|
+
trimTurnCache();
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/** A turn that just opened (or reopened) takes the worker reviews that
|
|
977
|
+
* completed while its Lead had no running turn. */
|
|
978
|
+
function adoptParkedWorkerReviews(ownerSessionId, turn, tracker) {
|
|
979
|
+
const parked = _parkedWorkerReviews.get(ownerSessionId);
|
|
980
|
+
if (!parked) return;
|
|
981
|
+
_parkedWorkerReviews.delete(ownerSessionId);
|
|
982
|
+
for (const review of parked.values()) {
|
|
983
|
+
joinWorkerReview(turn, tracker, review);
|
|
984
|
+
releaseParkedReview(review);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/** Hand a finished worker's own edits (its tracker, else its apply_patch
|
|
989
|
+
* diffs) to its Lead: into the Lead turn running now, or — when none runs —
|
|
990
|
+
* parked for the Lead's next turn to open or reopen. Everything a seal
|
|
991
|
+
* reads is published before this returns, so a seal started right after it
|
|
992
|
+
* already includes the worker. */
|
|
864
993
|
export function completeAgentTurnReview(handle, patches = []) {
|
|
865
994
|
if (!handle || handle.completed === true) return false;
|
|
866
995
|
handle.completed = true;
|
|
867
996
|
const ownerSessionId = clean(handle.ownerSessionId);
|
|
868
997
|
const key = clean(handle.sessionId) || handle.id;
|
|
869
|
-
const turn = _turnsBySession.get(ownerSessionId);
|
|
870
998
|
const tracker = _diffTrackersBySession.get(key);
|
|
871
999
|
const matchingTracker =
|
|
872
1000
|
tracker && tracker.ownerSessionId === ownerSessionId && tracker.ownerGeneration === handle.generation;
|
|
1001
|
+
// This execution is over: its bound tracker leaves the registry, so a next
|
|
1002
|
+
// execution of the worker starts its own.
|
|
1003
|
+
if (matchingTracker) _diffTrackersBySession.delete(key);
|
|
1004
|
+
// The Lead's change history was deleted after this worker was bound.
|
|
1005
|
+
if ((_historyEpochs.get(ownerSessionId) || 0) !== (handle.historyEpoch || 0)) {
|
|
1006
|
+
if (matchingTracker) releaseDiffTrackerContent(tracker);
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
873
1009
|
// A worker that runs its own session turn replaces the bound tracker with
|
|
874
1010
|
// its turn's own one (no owner). beginAgentTurnReview reset any older
|
|
875
1011
|
// tracker, so an unowned one here holds this execution's edits.
|
|
876
1012
|
const source = matchingTracker || (tracker && !tracker.ownerSessionId) ? tracker : null;
|
|
1013
|
+
const tracked = Boolean(source) && source.revision > 0;
|
|
1014
|
+
const review = {
|
|
1015
|
+
key,
|
|
1016
|
+
source,
|
|
1017
|
+
tracked,
|
|
1018
|
+
patch: tracked ? trackerView(source).patch : mergePatches(patches),
|
|
1019
|
+
agent: handle.agent,
|
|
1020
|
+
tag: handle.tag,
|
|
1021
|
+
release: Boolean(matchingTracker),
|
|
1022
|
+
};
|
|
1023
|
+
const turn = _turnsBySession.get(ownerSessionId);
|
|
877
1024
|
const ownerTracker = _diffTrackersBySession.get(ownerSessionId);
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
const patch = tracked ? trackerView(source).patch : mergePatches(patches);
|
|
883
|
-
if (!patch) {
|
|
884
|
-
if (tracked && prior) {
|
|
885
|
-
turn.agents.delete(key);
|
|
886
|
-
return true;
|
|
887
|
-
}
|
|
888
|
-
return false;
|
|
889
|
-
}
|
|
890
|
-
turn.agents.delete(key);
|
|
891
|
-
turn.agents.set(key, {
|
|
892
|
-
sessionId: key,
|
|
893
|
-
agent: handle.agent || prior?.agent || null,
|
|
894
|
-
tag: handle.tag || prior?.tag || null,
|
|
895
|
-
patch: tracked ? patch : mergePatches([prior?.patch, patch]),
|
|
896
|
-
});
|
|
897
|
-
if (source && ownerTracker && ownerTracker !== source) {
|
|
898
|
-
for (const path of source.ownedPaths) ownerTracker.ownedPaths.add(path);
|
|
899
|
-
mergeOwnStats(ownerTracker.ownStats, source.ownStats);
|
|
900
|
-
mergeTrackedContent(ownerTracker, source);
|
|
901
|
-
}
|
|
902
|
-
trimAgentReviews(turn);
|
|
903
|
-
// A worker that finishes after its Lead turn sealed (or while it seals)
|
|
904
|
-
// re-seals that turn's change record (its edits are part of the turn's net
|
|
905
|
-
// worktree diff).
|
|
906
|
-
if (ownerTracker && ownerTracker !== tracker && ownerTracker.ownerGeneration === handle.generation) {
|
|
907
|
-
resealTurn(ownerSessionId, ownerTracker);
|
|
908
|
-
}
|
|
909
|
-
return true;
|
|
910
|
-
} finally {
|
|
911
|
-
if (matchingTracker && _diffTrackersBySession.get(key) === tracker) {
|
|
912
|
-
releaseDiffTrackerContent(tracker);
|
|
913
|
-
_diffTrackersBySession.delete(key);
|
|
914
|
-
}
|
|
1025
|
+
if (turnRunning(turn, ownerTracker)) {
|
|
1026
|
+
const joined = joinWorkerReview(turn, ownerTracker, review);
|
|
1027
|
+
releaseParkedReview(review);
|
|
1028
|
+
return joined;
|
|
915
1029
|
}
|
|
1030
|
+
return parkWorkerReview(ownerSessionId, review);
|
|
916
1031
|
}
|
|
917
1032
|
|
|
918
1033
|
/** Persist the durable half of a finished review: the baseline tree hash plus
|
|
@@ -1091,15 +1206,12 @@ const pendingFreezeKey = (ownerSessionId, checkpointId) => `${ownerSessionId}\0$
|
|
|
1091
1206
|
* Seal a Lead turn: capture the worktree one last time, persist the review
|
|
1092
1207
|
* baseline and freeze the change record. Single-flight per tracker; a seal
|
|
1093
1208
|
* requested while another runs waits for it and then seals again only if a
|
|
1094
|
-
* completion wake reopened the turn meanwhile.
|
|
1095
|
-
* re-freezes a turn that is still sealed, after any seal in flight: that one
|
|
1096
|
-
* captured its agents before the worker was attributed.
|
|
1209
|
+
* completion wake reopened the turn meanwhile.
|
|
1097
1210
|
*/
|
|
1098
|
-
function sealTurn(ownerSessionId, tracker
|
|
1211
|
+
function sealTurn(ownerSessionId, tracker) {
|
|
1099
1212
|
// Its history was deleted (clearSessionChangeHistory): never written again.
|
|
1100
1213
|
if (tracker.discarded) return Promise.resolve();
|
|
1101
1214
|
if (tracker.freezing) {
|
|
1102
|
-
if (reseal) return tracker.freezing.then(() => sealTurn(ownerSessionId, tracker, { reseal: true }));
|
|
1103
1215
|
return tracker.freezing.then(() => (tracker.sealed ? undefined : sealTurn(ownerSessionId, tracker)));
|
|
1104
1216
|
}
|
|
1105
1217
|
const epoch = tracker.sealEpoch;
|
|
@@ -1114,9 +1226,7 @@ function sealTurn(ownerSessionId, tracker, { reseal = false } = {}) {
|
|
|
1114
1226
|
// Best-effort refresh: seal with the last captured worktree state.
|
|
1115
1227
|
}
|
|
1116
1228
|
}
|
|
1117
|
-
if (tracker.discarded || tracker.sealEpoch !== epoch
|
|
1118
|
-
// A re-seal never rewrites a turn once the next one opened.
|
|
1119
|
-
if (reseal && turnSuperseded(ownerSessionId, tracker)) return;
|
|
1229
|
+
if (tracker.discarded || tracker.sealEpoch !== epoch) return;
|
|
1120
1230
|
tracker.sealed = true;
|
|
1121
1231
|
await persistTurnSnapshotRecord(ownerSessionId, tracker);
|
|
1122
1232
|
try {
|
|
@@ -1124,8 +1234,8 @@ function sealTurn(ownerSessionId, tracker, { reseal = false } = {}) {
|
|
|
1124
1234
|
} catch {
|
|
1125
1235
|
/* the change record adds Undo history; it never fails a turn */
|
|
1126
1236
|
}
|
|
1127
|
-
// The bounded own before/after content stays until the next turn: a
|
|
1128
|
-
//
|
|
1237
|
+
// The bounded own before/after content stays until the next turn: a
|
|
1238
|
+
// completion wake reopening it and an Undo/Reapply of this turn fold into it.
|
|
1129
1239
|
})().finally(() => {
|
|
1130
1240
|
if (tracker.freezing === run) tracker.freezing = null;
|
|
1131
1241
|
const count = (_pendingFreezes.get(key) || 1) - 1;
|
|
@@ -1136,31 +1246,6 @@ function sealTurn(ownerSessionId, tracker, { reseal = false } = {}) {
|
|
|
1136
1246
|
return run;
|
|
1137
1247
|
}
|
|
1138
1248
|
|
|
1139
|
-
function resealTurn(ownerSessionId, tracker) {
|
|
1140
|
-
// A turn still running seals with every published agent; one sealed or
|
|
1141
|
-
// sealing queues its re-seal (sealTurn runs it after a seal in flight).
|
|
1142
|
-
if (!(tracker.sealed || tracker.freezing) || !clean(tracker.checkpointId) || tracker.resealQueued) return;
|
|
1143
|
-
// On the session lock: an in-flight Undo/Reapply lands before the re-freeze
|
|
1144
|
-
// reads the worktree. A turn start queued ahead of it runs it before opening
|
|
1145
|
-
// (beginTurnSnapshot), so one queued re-seal covers every late worker.
|
|
1146
|
-
tracker.resealQueued = true;
|
|
1147
|
-
void serializeChangeMutation(ownerSessionId, () => runQueuedReseal(ownerSessionId, tracker)).catch(() => {});
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
/** True once a newer turn of the session opened over `tracker`'s. */
|
|
1151
|
-
function turnSuperseded(ownerSessionId, tracker) {
|
|
1152
|
-
const turn = _turnsBySession.get(ownerSessionId);
|
|
1153
|
-
return Boolean(turn) && turn.generation !== tracker.ownerGeneration;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
/** Run `tracker`'s queued re-seal once, whichever lock step reaches it first. */
|
|
1157
|
-
function runQueuedReseal(ownerSessionId, tracker) {
|
|
1158
|
-
if (!tracker.resealQueued) return undefined;
|
|
1159
|
-
tracker.resealQueued = false;
|
|
1160
|
-
if (turnSuperseded(ownerSessionId, tracker)) return undefined;
|
|
1161
|
-
return sealTurn(ownerSessionId, tracker, { reseal: true });
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
1249
|
/** Freeze a completed (or cancelled) Lead turn while retaining only its
|
|
1165
1250
|
* bounded review data. `expected` — the `{ generation }` beginTurnSnapshot
|
|
1166
1251
|
* resolved to — makes a delayed call a no-op once a newer turn began. */
|
|
@@ -1718,13 +1803,52 @@ export async function getTurnChanges(sessionId, options = {}) {
|
|
|
1718
1803
|
let status = 'missing';
|
|
1719
1804
|
if (ownerSessionId && turnChangesPending(ownerSessionId, checkpointId)) status = 'pending';
|
|
1720
1805
|
else if (record) status = record.files?.length || record.outside?.length ? 'ready' : 'none';
|
|
1721
|
-
|
|
1806
|
+
const reply = turnChangesReply({
|
|
1722
1807
|
status,
|
|
1723
1808
|
checkpointId,
|
|
1724
1809
|
record,
|
|
1725
1810
|
patch: options?.patch === true,
|
|
1726
1811
|
file: clean(options?.file) || null,
|
|
1727
1812
|
});
|
|
1813
|
+
return status === 'ready' ? withIgnoredChangeFiles(ownerSessionId, reply) : reply;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// The gitignored files of each answered record revision (build output a file
|
|
1817
|
+
// tool wrote): flagged `ignored` so a viewer can leave them out of its turn
|
|
1818
|
+
// previews. Asked of Git once per revision; bounded.
|
|
1819
|
+
const IGNORED_CHANGE_CACHE_LIMIT = 200;
|
|
1820
|
+
const _ignoredChangeFiles = new Map();
|
|
1821
|
+
|
|
1822
|
+
async function ignoredChangePaths(key, root, paths) {
|
|
1823
|
+
const cached = _ignoredChangeFiles.get(key);
|
|
1824
|
+
if (cached) return cached;
|
|
1825
|
+
let ignored = new Set();
|
|
1826
|
+
try {
|
|
1827
|
+
const result = await runGitOffThread(['check-ignore', '-z', '--stdin'], {
|
|
1828
|
+
cwd: root,
|
|
1829
|
+
input: `${paths.join('\0')}\0`,
|
|
1830
|
+
timeoutMs: 10_000,
|
|
1831
|
+
maxBytes: 4 * 1024 * 1024,
|
|
1832
|
+
});
|
|
1833
|
+
// 0: some are ignored, 1: none; anything else (no repository) flags none.
|
|
1834
|
+
if (result.code === 0) ignored = new Set(result.stdout.split('\0').filter(Boolean));
|
|
1835
|
+
} catch {
|
|
1836
|
+
// No Git to ask: nothing is flagged.
|
|
1837
|
+
}
|
|
1838
|
+
_ignoredChangeFiles.set(key, ignored);
|
|
1839
|
+
while (_ignoredChangeFiles.size > IGNORED_CHANGE_CACHE_LIMIT) {
|
|
1840
|
+
_ignoredChangeFiles.delete(_ignoredChangeFiles.keys().next().value);
|
|
1841
|
+
}
|
|
1842
|
+
return ignored;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
async function withIgnoredChangeFiles(ownerSessionId, reply) {
|
|
1846
|
+
const paths = reply.files.map((file) => file.path).filter((path) => path && !isAbsolute(path));
|
|
1847
|
+
if (!reply.root || paths.length === 0) return reply;
|
|
1848
|
+
const key = `${ownerSessionId}\0${reply.checkpointId}\0${reply.revision}\0${reply.root}`;
|
|
1849
|
+
const ignored = await ignoredChangePaths(key, reply.root, paths);
|
|
1850
|
+
if (ignored.size === 0) return reply;
|
|
1851
|
+
return { ...reply, files: reply.files.map((file) => (ignored.has(file.path) ? { ...file, ignored: true } : file)) };
|
|
1728
1852
|
}
|
|
1729
1853
|
|
|
1730
1854
|
function serializeChangeMutation(ownerSessionId, task) {
|
|
@@ -1871,11 +1995,13 @@ async function mutateTurnChanges(sessionId, checkpointIdValue, fileValue, revisi
|
|
|
1871
1995
|
if (targets.length === 0) throw new Error(`this turn has no changes to be ${verb}`);
|
|
1872
1996
|
}
|
|
1873
1997
|
const updated = await mutateRecordFiles(ownerSessionId, record, targets, reverse);
|
|
1874
|
-
|
|
1998
|
+
const reply = turnChangesReply({
|
|
1875
1999
|
status: updated ? 'ready' : 'missing',
|
|
1876
2000
|
checkpointId,
|
|
1877
2001
|
record: withUnknownChangeStates(ownerSessionId, updated),
|
|
1878
2002
|
});
|
|
2003
|
+
// The viewer caches this reply as it caches a read: the same `ignored` flags.
|
|
2004
|
+
return updated ? withIgnoredChangeFiles(ownerSessionId, reply) : reply;
|
|
1879
2005
|
});
|
|
1880
2006
|
}
|
|
1881
2007
|
|
|
@@ -1905,20 +2031,8 @@ export async function clearSessionChangeHistory(sessionId) {
|
|
|
1905
2031
|
throw new Error('change history cannot be deleted while this session is running a turn');
|
|
1906
2032
|
}
|
|
1907
2033
|
// Invalidate every producer of the old history BEFORE the awaited
|
|
1908
|
-
// deletion
|
|
1909
|
-
|
|
1910
|
-
// discarded, so nothing begun before the delete can write it back.
|
|
1911
|
-
for (const [trackedSessionId, tracker] of _diffTrackersBySession) {
|
|
1912
|
-
if (trackedSessionId !== ownerSessionId && tracker.ownerSessionId !== ownerSessionId) continue;
|
|
1913
|
-
tracker.discarded = true;
|
|
1914
|
-
tracker.resealQueued = false;
|
|
1915
|
-
releaseDiffTrackerContent(tracker);
|
|
1916
|
-
_diffTrackersBySession.delete(trackedSessionId);
|
|
1917
|
-
}
|
|
1918
|
-
_turnsBySession.delete(ownerSessionId);
|
|
1919
|
-
for (const key of _unknownChangeStates.keys()) {
|
|
1920
|
-
if (key.startsWith(`${ownerSessionId}\0`)) _unknownChangeStates.delete(key);
|
|
1921
|
-
}
|
|
2034
|
+
// deletion, so nothing begun before the delete can write it back.
|
|
2035
|
+
discardSessionChangeState(ownerSessionId);
|
|
1922
2036
|
// The session's first-seen baselines go too: later edits start fresh ones.
|
|
1923
2037
|
const [turns] = await Promise.all([
|
|
1924
2038
|
clearStoredChangeHistory(ownerSessionId),
|
|
@@ -1928,12 +2042,51 @@ export async function clearSessionChangeHistory(sessionId) {
|
|
|
1928
2042
|
});
|
|
1929
2043
|
}
|
|
1930
2044
|
|
|
2045
|
+
/** Release a Lead session's in-memory change state: a worker bound before
|
|
2046
|
+
* this finds its history epoch moved on (its completion parks nothing), its
|
|
2047
|
+
* parked worker reviews are dropped, and its own and bound worker trackers
|
|
2048
|
+
* are discarded (anything earlier that seals writes nothing). */
|
|
2049
|
+
function discardSessionChangeState(ownerSessionId) {
|
|
2050
|
+
_historyEpochs.set(ownerSessionId, (_historyEpochs.get(ownerSessionId) || 0) + 1);
|
|
2051
|
+
dropParkedWorkerReviews(ownerSessionId);
|
|
2052
|
+
for (const [trackedSessionId, tracker] of _diffTrackersBySession) {
|
|
2053
|
+
if (trackedSessionId !== ownerSessionId && tracker.ownerSessionId !== ownerSessionId) continue;
|
|
2054
|
+
tracker.discarded = true;
|
|
2055
|
+
releaseDiffTrackerContent(tracker);
|
|
2056
|
+
_diffTrackersBySession.delete(trackedSessionId);
|
|
2057
|
+
}
|
|
2058
|
+
_turnsBySession.delete(ownerSessionId);
|
|
2059
|
+
for (const key of _unknownChangeStates.keys()) {
|
|
2060
|
+
if (key.startsWith(`${ownerSessionId}\0`)) _unknownChangeStates.delete(key);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
/** The session was deleted: release its turn, parked worker reviews and
|
|
2065
|
+
* bound worker trackers (its stored records go with the session purge). */
|
|
2066
|
+
export function forgetTurnSnapshotSession(sessionId) {
|
|
2067
|
+
const ownerSessionId = clean(sessionId);
|
|
2068
|
+
if (ownerSessionId) discardSessionChangeState(ownerSessionId);
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
export function _diffTrackerForTest(sessionId) {
|
|
2072
|
+
return _diffTrackersBySession.get(clean(sessionId));
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
export function _parkedWorkerReviewsForTest(ownerSessionId) {
|
|
2076
|
+
return [...(_parkedWorkerReviews.get(clean(ownerSessionId))?.values() || [])].map(({ key, patch }) => ({
|
|
2077
|
+
key,
|
|
2078
|
+
patch,
|
|
2079
|
+
}));
|
|
2080
|
+
}
|
|
2081
|
+
|
|
1931
2082
|
export function _resetTurnSnapshotForTest() {
|
|
1932
2083
|
_turnsBySession.clear();
|
|
1933
2084
|
_diffTrackersBySession.clear();
|
|
1934
2085
|
_pendingFreezes.clear();
|
|
1935
2086
|
_changeMutations.clear();
|
|
1936
2087
|
_unknownChangeStates.clear();
|
|
2088
|
+
_parkedWorkerReviews.clear();
|
|
2089
|
+
_historyEpochs.clear();
|
|
1937
2090
|
_turnOpenedHookForTest = null;
|
|
1938
2091
|
_agentTurnSeq = 0;
|
|
1939
2092
|
_resetTurnWorktreeSnapshotsForTest();
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
23
|
-
import { readFileSync, statSync
|
|
23
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
24
24
|
import { join, resolve } from 'node:path';
|
|
25
25
|
import { homedir } from 'node:os';
|
|
26
|
+
import { holdsNoData, writeFileAtomicSync } from './atomic-file.mjs';
|
|
26
27
|
import { resolvePluginData, mixdogRoot } from './plugin-paths.mjs';
|
|
27
28
|
|
|
28
29
|
const _cwdOverride = new AsyncLocalStorage();
|
|
@@ -112,7 +113,10 @@ export function explicitSessionCwd() {
|
|
|
112
113
|
if (sessionDir) return sessionDir;
|
|
113
114
|
}
|
|
114
115
|
try {
|
|
115
|
-
const
|
|
116
|
+
const raw = readFileSync(_dataFile('user-cwd.txt'), 'utf8');
|
|
117
|
+
// A crash can leave NUL bytes in place of the path: that is no path.
|
|
118
|
+
if (holdsNoData(raw)) return null;
|
|
119
|
+
const txt = raw.trim();
|
|
116
120
|
return (txt && _normalizePlatformCwd(txt)) || null;
|
|
117
121
|
} catch {
|
|
118
122
|
return null;
|
|
@@ -173,7 +177,7 @@ function _lastSessionCwdFile(keyPid) {
|
|
|
173
177
|
*/
|
|
174
178
|
export function writeLastSessionCwd(cwd, keyPid) {
|
|
175
179
|
try {
|
|
176
|
-
|
|
180
|
+
writeFileAtomicSync(_lastSessionCwdFile(keyPid), String(cwd));
|
|
177
181
|
} catch {
|
|
178
182
|
/* best-effort */
|
|
179
183
|
}
|
|
@@ -188,7 +192,8 @@ export function writeLastSessionCwd(cwd, keyPid) {
|
|
|
188
192
|
*/
|
|
189
193
|
export function readLastSessionCwd(keyPid) {
|
|
190
194
|
try {
|
|
191
|
-
|
|
195
|
+
const raw = readFileSync(_lastSessionCwdFile(keyPid), 'utf8');
|
|
196
|
+
return holdsNoData(raw) ? null : _existingDirectory(raw.trim());
|
|
192
197
|
} catch {
|
|
193
198
|
return null;
|
|
194
199
|
}
|