mixdog 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/package.json +1 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +13 -3
- package/src/defaults/skills/visualize/SKILL.md +12 -9
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config.mjs +10 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +122 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
- package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +447 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +41 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +34 -24
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/atomic-file.mjs +25 -4
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/err-text.mjs +25 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -52
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +124 -20
- package/src/runtime/shared/provider-api-key.mjs +12 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/turn-changes.mjs +447 -219
- package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
- package/src/runtime/shared/turn-snapshot.mjs +170 -48
- package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/contract.mjs +0 -24
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +61 -0
- package/src/session-runtime/boot/config.mjs +5 -2
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +35 -18
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/connect.mjs +83 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
- package/src/session-runtime/mcp-glue.mjs +25 -2
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +83 -15
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +9 -2
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +24 -5
- package/src/session-runtime/resource-mcp-api.mjs +198 -78
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/runtime-review-api.mjs +13 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +12 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-stored-session-views.mjs +11 -3
- package/src/standalone/daemon-telemetry.mjs +55 -0
- package/src/standalone/daemon.mjs +44 -11
- package/src/standalone/session-protocol.mjs +12 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
- package/src/standalone/session-service-api.mjs +5 -0
- package/src/standalone/session-service.mjs +1 -0
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +7 -0
- package/src/tui/app/create-app-pickers.mjs +4 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +13 -16
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-commands.mjs +7 -2
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +1323 -214
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +33 -24
- package/src/tui/session/session-api/extensions.mjs +26 -2
- package/src/tui/session/session-api/integrations.mjs +7 -0
- package/src/tui/session/session-api/lifecycle.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +12 -14
- package/src/tui/session/session-flow/drain.mjs +21 -5
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn.mjs +55 -15
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { createHash } from 'node:crypto';
|
|
16
16
|
import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
17
17
|
import { join, resolve } from 'node:path';
|
|
18
|
+
import { withFileLock } from './atomic-file.mjs';
|
|
18
19
|
import { cleanString as clean } from './clean.mjs';
|
|
19
20
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
20
21
|
|
|
@@ -37,6 +38,12 @@ let recordRootOverride = '';
|
|
|
37
38
|
function recordRoot() {
|
|
38
39
|
return recordRootOverride || join(resolvePluginData(), 'turn-snapshots');
|
|
39
40
|
}
|
|
41
|
+
|
|
42
|
+
/** The directory every turn-snapshot store lives under (follows the test
|
|
43
|
+
* override and a runtime MIXDOG_DATA_DIR retarget). */
|
|
44
|
+
export function turnSnapshotStoreRoot() {
|
|
45
|
+
return recordRoot();
|
|
46
|
+
}
|
|
40
47
|
// Per-session write chain. Two turns of one session never race their own file,
|
|
41
48
|
// and a read never observes a partially written record (writes land by rename).
|
|
42
49
|
const writeChains = new Map();
|
|
@@ -133,7 +140,7 @@ async function readRecordFile(path) {
|
|
|
133
140
|
|
|
134
141
|
/** Oldest-first sweep of one record directory so a long-lived install cannot
|
|
135
142
|
* accumulate records. */
|
|
136
|
-
async function sweepDirectory(directory, ttlMs, maxFiles) {
|
|
143
|
+
export async function sweepDirectory(directory, ttlMs, maxFiles) {
|
|
137
144
|
try {
|
|
138
145
|
const names = await readdir(directory);
|
|
139
146
|
const now = Date.now();
|
|
@@ -231,6 +238,7 @@ export async function saveTurnSnapshotRecord(sessionId, turn) {
|
|
|
231
238
|
.map(serializeScope),
|
|
232
239
|
};
|
|
233
240
|
await writeJsonAtomically(path, payload);
|
|
241
|
+
await endClearedHistory(id);
|
|
234
242
|
return true;
|
|
235
243
|
} catch {
|
|
236
244
|
return false;
|
|
@@ -257,6 +265,108 @@ export async function loadTurnSnapshotRecord(sessionId) {
|
|
|
257
265
|
return null;
|
|
258
266
|
}
|
|
259
267
|
|
|
268
|
+
// ── Last computed review summary ────────────────────────────────────────────
|
|
269
|
+
//
|
|
270
|
+
// Rebuilding a recorded review rescans the whole worktree behind every other
|
|
271
|
+
// capture of that repository. Entering a session reads the last computed
|
|
272
|
+
// files and counts instead and lets the refreshing read follow. Keyed to the
|
|
273
|
+
// turn record it was computed from, so a newer turn never shows an older one.
|
|
274
|
+
|
|
275
|
+
let summarySweepDone = false;
|
|
276
|
+
|
|
277
|
+
function summariesRoot() {
|
|
278
|
+
return join(recordRoot(), 'review-summaries');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Identity of the paths a record turn scopes its review to. */
|
|
282
|
+
function summaryToolFilesKey(turn) {
|
|
283
|
+
return (Array.isArray(turn?.toolFiles) ? turn.toolFiles : []).map(rootKey).sort().join('\0');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function summaryPath(sessionId) {
|
|
287
|
+
const hash = createHash('sha256').update(sessionId).digest('hex').slice(0, 24);
|
|
288
|
+
return join(summariesRoot(), `${hash}.json`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Test hook: runs inside the summary critical section, between the cleared
|
|
292
|
+
// check and the write.
|
|
293
|
+
let summaryWriteHookForTest = null;
|
|
294
|
+
export function _setSummaryWriteHookForTest(hook) {
|
|
295
|
+
summaryWriteHookForTest = typeof hook === 'function' ? hook : null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const SUMMARY_LOCK_TIMEOUT_MS = 10_000;
|
|
299
|
+
const withSummaryLock = (sessionId, task) =>
|
|
300
|
+
withFileLock(`${summaryPath(sessionId)}.lock`, task, { timeoutMs: SUMMARY_LOCK_TIMEOUT_MS });
|
|
301
|
+
|
|
302
|
+
// Summary saves still running. Callers fire them and move on, so a test that
|
|
303
|
+
// removes the store root settles them first instead of racing their writes.
|
|
304
|
+
const pendingSummarySaves = new Set();
|
|
305
|
+
export async function _settleSummarySavesForTest() {
|
|
306
|
+
while (pendingSummarySaves.size) await Promise.allSettled([...pendingSummarySaves]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Keep the files and counts last computed for `turn` (a loaded record turn). */
|
|
310
|
+
export function saveTurnReviewSummary(sessionId, turn, review) {
|
|
311
|
+
const saving = saveTurnReviewSummaryNow(sessionId, turn, review);
|
|
312
|
+
pendingSummarySaves.add(saving);
|
|
313
|
+
const done = () => pendingSummarySaves.delete(saving);
|
|
314
|
+
saving.then(done, done);
|
|
315
|
+
return saving;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function saveTurnReviewSummaryNow(sessionId, turn, review) {
|
|
319
|
+
const id = clean(sessionId);
|
|
320
|
+
if (!id || !turn?.baselineTree || !Array.isArray(review?.files)) return false;
|
|
321
|
+
return await chain(`summary:${id}`, async () => {
|
|
322
|
+
try {
|
|
323
|
+
await mkdir(summariesRoot(), { recursive: true });
|
|
324
|
+
if (!summarySweepDone) {
|
|
325
|
+
summarySweepDone = true;
|
|
326
|
+
void sweepDirectory(summariesRoot(), RECORD_TTL_MS, MAX_RECORD_FILES);
|
|
327
|
+
}
|
|
328
|
+
return await withSummaryLock(id, async () => {
|
|
329
|
+
// Deleted history stays deleted: a summary computed before the delete
|
|
330
|
+
// must not bring it back (a newly recorded turn ends the marker). The
|
|
331
|
+
// check and the write are one step against a delete in any process.
|
|
332
|
+
if (await sessionChangeHistoryCleared(id)) return false;
|
|
333
|
+
await summaryWriteHookForTest?.(id);
|
|
334
|
+
await writeJsonAtomically(summaryPath(id), {
|
|
335
|
+
version: RECORD_VERSION,
|
|
336
|
+
sessionId: id,
|
|
337
|
+
generation: Number(turn.generation) || 0,
|
|
338
|
+
baselineTree: clean(turn.baselineTree),
|
|
339
|
+
checkpointId: clean(turn.checkpointId),
|
|
340
|
+
toolFilesKey: summaryToolFilesKey(turn),
|
|
341
|
+
files: review.files,
|
|
342
|
+
patchTruncated: review.patchTruncated === true,
|
|
343
|
+
savedAt: Date.now(),
|
|
344
|
+
});
|
|
345
|
+
return true;
|
|
346
|
+
});
|
|
347
|
+
} catch {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** The summary saved for exactly this record turn, else null. */
|
|
354
|
+
export async function loadTurnReviewSummary(sessionId, turn) {
|
|
355
|
+
const id = clean(sessionId);
|
|
356
|
+
if (!id || !turn?.baselineTree) return null;
|
|
357
|
+
try {
|
|
358
|
+
const parsed = JSON.parse(await readFile(summaryPath(id), 'utf8'));
|
|
359
|
+
if (parsed?.version !== RECORD_VERSION || parsed.sessionId !== id || !Array.isArray(parsed.files)) return null;
|
|
360
|
+
if (parsed.baselineTree !== clean(turn.baselineTree)) return null;
|
|
361
|
+
if (parsed.generation !== (Number(turn.generation) || 0)) return null;
|
|
362
|
+
if (parsed.checkpointId !== clean(turn.checkpointId)) return null;
|
|
363
|
+
if (parsed.toolFilesKey !== summaryToolFilesKey(turn)) return null;
|
|
364
|
+
return parsed;
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
260
370
|
/** Earliest retained baseline plus every path attributed to this session,
|
|
261
371
|
* grouped by repository root. This is the durable source for the pane's
|
|
262
372
|
* session-wide Diff and is intentionally separate from latest-turn Undo. */
|
|
@@ -268,6 +378,75 @@ export async function loadSessionSnapshotRecords(sessionId) {
|
|
|
268
378
|
return sessionScopes(record).map(serializeScope);
|
|
269
379
|
}
|
|
270
380
|
|
|
381
|
+
/**
|
|
382
|
+
* Permanently delete one session's retained change history: its turn
|
|
383
|
+
* baselines and session scopes, and every per-turn change record (stored
|
|
384
|
+
* patches, file metadata, applied/reverted state). The baseline index keeps a
|
|
385
|
+
* `historyCleared` marker so the session review answers an authoritative
|
|
386
|
+
* empty set instead of falling back to older sources; the next recorded turn
|
|
387
|
+
* starts a fresh scope. Each half is serialized with the other writes of its
|
|
388
|
+
* file. Resolves to the number of turn change records deleted.
|
|
389
|
+
*/
|
|
390
|
+
export async function clearSessionChangeHistory(sessionId) {
|
|
391
|
+
const id = clean(sessionId);
|
|
392
|
+
if (!id) return 0;
|
|
393
|
+
await chain(id, async () => {
|
|
394
|
+
// The marker lands before the records go: a failure in between leaves
|
|
395
|
+
// the session answering empty, never resurrecting what was asked gone.
|
|
396
|
+
await mkdir(clearedRoot(), { recursive: true });
|
|
397
|
+
// Under the summary's file lock, shared with every process: a summary
|
|
398
|
+
// write that passed its marker check finishes before the marker lands (and
|
|
399
|
+
// is removed with it); one that starts later sees the marker and stays away.
|
|
400
|
+
await withSummaryLock(id, async () => {
|
|
401
|
+
await writeJsonAtomically(clearedPath(id), { version: RECORD_VERSION, sessionId: id, clearedAt: Date.now() });
|
|
402
|
+
await rm(summaryPath(id), { force: true });
|
|
403
|
+
});
|
|
404
|
+
await rm(recordPath(id), { force: true });
|
|
405
|
+
});
|
|
406
|
+
return await chain(`changes:${id}`, async () => {
|
|
407
|
+
const records = (await readChangesFile(id))?.records || [];
|
|
408
|
+
await rm(changesPath(id), { force: true });
|
|
409
|
+
changeRecordsCache.delete(id);
|
|
410
|
+
return records.length;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// "History deleted" markers live in their own directory: neither record
|
|
415
|
+
// sweep (age or count) reaches it, so a deletion stays in force for as long
|
|
416
|
+
// as the session exists. The next recorded turn ends it, and deleting the
|
|
417
|
+
// session removes it (forgetSessionChangeHistoryMarker).
|
|
418
|
+
function clearedRoot() {
|
|
419
|
+
return join(recordRoot(), 'cleared');
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function clearedPath(sessionId) {
|
|
423
|
+
const hash = createHash('sha256').update(sessionId).digest('hex').slice(0, 24);
|
|
424
|
+
return join(clearedRoot(), `${hash}.json`);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** End the cleared state: a new turn record of the session was written. */
|
|
428
|
+
async function endClearedHistory(sessionId) {
|
|
429
|
+
await rm(clearedPath(sessionId), { force: true });
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** The session's history was deleted and no turn has been recorded since. */
|
|
433
|
+
export async function sessionChangeHistoryCleared(sessionId) {
|
|
434
|
+
const id = clean(sessionId);
|
|
435
|
+
if (!id) return false;
|
|
436
|
+
try {
|
|
437
|
+
return JSON.parse(await readFile(clearedPath(id), 'utf8'))?.sessionId === id;
|
|
438
|
+
} catch {
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** The session itself was deleted: its history marker goes with it. */
|
|
444
|
+
export async function forgetSessionChangeHistoryMarker(sessionId) {
|
|
445
|
+
const id = clean(sessionId);
|
|
446
|
+
if (!id) return;
|
|
447
|
+
await chain(id, () => rm(clearedPath(id), { force: true })).catch(() => {});
|
|
448
|
+
}
|
|
449
|
+
|
|
271
450
|
// Absolute, slash-normalized and (on Windows) case-folded: equal for every
|
|
272
451
|
// spelling of one folder, so a stored root and a caller's root compare equal.
|
|
273
452
|
function matchKey(value) {
|
|
@@ -520,6 +699,8 @@ export async function upsertTurnChangeRecord(sessionId, next) {
|
|
|
520
699
|
else records.push(record);
|
|
521
700
|
boundChangeRecords(records, checkpointId);
|
|
522
701
|
await writeChangeRecords(id, records);
|
|
702
|
+
// Any new turn record (Git or not) starts the history again.
|
|
703
|
+
await endClearedHistory(id);
|
|
523
704
|
return records.find((entry) => entry.checkpointId === checkpointId) || null;
|
|
524
705
|
});
|
|
525
706
|
}
|
|
@@ -4,6 +4,7 @@ import { dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
|
4
4
|
import {
|
|
5
5
|
_resetTurnWorktreeSnapshotsForTest,
|
|
6
6
|
createTurnWorktreeSnapshot,
|
|
7
|
+
diffTurnWorktreeHead,
|
|
7
8
|
freezeTurnWorktreeChanges,
|
|
8
9
|
noteTurnWorktreeChange,
|
|
9
10
|
refreshTurnWorktreeSnapshot,
|
|
@@ -14,11 +15,15 @@ import {
|
|
|
14
15
|
revertTurnWorktreeSnapshot,
|
|
15
16
|
} from './turn-worktree-snapshot.mjs';
|
|
16
17
|
import {
|
|
18
|
+
clearSessionChangeHistory as clearStoredChangeHistory,
|
|
17
19
|
loadSessionSnapshotRecords,
|
|
18
20
|
loadTurnChangeRecord,
|
|
19
21
|
loadTurnChangeRecords,
|
|
22
|
+
loadTurnReviewSummary,
|
|
20
23
|
loadTurnSnapshotRecord,
|
|
24
|
+
saveTurnReviewSummary,
|
|
21
25
|
saveTurnSnapshotRecord,
|
|
26
|
+
sessionChangeHistoryCleared,
|
|
22
27
|
updateTurnChangeRecord,
|
|
23
28
|
upsertTurnChangeRecord,
|
|
24
29
|
} from './turn-snapshot-store.mjs';
|
|
@@ -27,13 +32,21 @@ import {
|
|
|
27
32
|
changePathKey,
|
|
28
33
|
findChangeFile,
|
|
29
34
|
measureLegacyChangeFile,
|
|
30
|
-
mergeRecordedSessionChanges,
|
|
31
35
|
namedChangePatch,
|
|
36
|
+
sessionChangeReview,
|
|
32
37
|
storedChangeFile,
|
|
33
38
|
trackedChangeFile,
|
|
34
39
|
turnChangesReply,
|
|
35
40
|
unknownStateChangeFile,
|
|
36
41
|
} from './turn-changes.mjs';
|
|
42
|
+
import {
|
|
43
|
+
_resetSessionBaselinesForTest,
|
|
44
|
+
captureSessionBaselines,
|
|
45
|
+
clearSessionBaselines,
|
|
46
|
+
loadSessionBaselines,
|
|
47
|
+
primeReviewOwner,
|
|
48
|
+
recordReviewOwner,
|
|
49
|
+
} from './session-baseline-store.mjs';
|
|
37
50
|
import { boundReviewPatch } from './review-diff.mjs';
|
|
38
51
|
import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
|
|
39
52
|
import { cleanString as clean } from './clean.mjs';
|
|
@@ -473,13 +486,48 @@ export function recordTurnDiffChanges(sessionId, changes = []) {
|
|
|
473
486
|
const id = clean(sessionId);
|
|
474
487
|
if (!id) return '';
|
|
475
488
|
const tracker = _diffTrackersBySession.get(id) || resetDiffTracker(id);
|
|
489
|
+
const list = Array.isArray(changes) ? changes : [];
|
|
490
|
+
const ownerTracker = tracker?.ownerSessionId ? _diffTrackersBySession.get(tracker.ownerSessionId) : tracker;
|
|
491
|
+
const snapshot = ownerTracker?.worktreeSnapshot;
|
|
492
|
+
// Independent of this tracker's validity: the session review needs every
|
|
493
|
+
// write's first-seen state even once the turn's buffers are gone.
|
|
494
|
+
captureReviewBaselines(id, tracker, ownerTracker, list, snapshot);
|
|
476
495
|
if (!tracker?.valid || tracker.sealed) return tracker?.unifiedDiff || '';
|
|
477
|
-
|
|
478
|
-
?.worktreeSnapshot;
|
|
479
|
-
foldTrackedChanges(tracker, Array.isArray(changes) ? changes : [], snapshot);
|
|
496
|
+
foldTrackedChanges(tracker, list, snapshot);
|
|
480
497
|
return tracker.unifiedDiff;
|
|
481
498
|
}
|
|
482
499
|
|
|
500
|
+
const insidePath = (root, path) => {
|
|
501
|
+
const rel = relative(root, path);
|
|
502
|
+
return Boolean(rel) && !isAbsolute(rel) && rel.split(/[\\/]+/)[0] !== '..';
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
/** Hand each change's first-seen state to the session baselines of `sessionId`
|
|
506
|
+
* and the Lead it works for. A path inside the turn's Git root is skipped: the
|
|
507
|
+
* shadow baseline tree already vouches for it. */
|
|
508
|
+
function captureReviewBaselines(sessionId, tracker, ownerTracker, list, snapshot) {
|
|
509
|
+
const gitRoot = clean(snapshot?.root);
|
|
510
|
+
const root = clean(tracker?.worktreeRoot) || clean(ownerTracker?.worktreeRoot);
|
|
511
|
+
const changes = [];
|
|
512
|
+
for (const raw of list) {
|
|
513
|
+
const path = clean(raw?.path);
|
|
514
|
+
if (!path || !isAbsolute(path)) continue;
|
|
515
|
+
const newPath = isAbsolute(clean(raw?.newPath)) ? resolve(clean(raw.newPath)) : '';
|
|
516
|
+
if (gitRoot && insidePath(gitRoot, path) && (!newPath || insidePath(gitRoot, newPath))) continue;
|
|
517
|
+
const before = raw?.before ?? null;
|
|
518
|
+
const after = raw?.after ?? null;
|
|
519
|
+
if (!newPath && before === null && after === null) continue;
|
|
520
|
+
changes.push({
|
|
521
|
+
path: resolve(path),
|
|
522
|
+
root,
|
|
523
|
+
before,
|
|
524
|
+
...(newPath ? { newPath } : {}),
|
|
525
|
+
replaced: (before === null) !== (after === null),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
captureSessionBaselines(sessionId, changes, { owner: tracker?.ownerSessionId || '' });
|
|
529
|
+
}
|
|
530
|
+
|
|
483
531
|
/** Fold exact before/after states into `tracker`. `snapshot` — the owning
|
|
484
532
|
* Lead turn's Git baseline, when tools are writing — learns each written
|
|
485
533
|
* path's first-before bytes. */
|
|
@@ -626,6 +674,9 @@ function continueTurnSnapshot(ownerSessionId) {
|
|
|
626
674
|
export async function beginTurnSnapshot(worktree, sessionId, options = {}) {
|
|
627
675
|
const ownerSessionId = clean(sessionId);
|
|
628
676
|
if (DISABLED || !ownerSessionId) return undefined;
|
|
677
|
+
// A worker resumed after a restart still credits its Lead's review (a
|
|
678
|
+
// capture racing this lookup waits for it).
|
|
679
|
+
void primeReviewOwner(ownerSessionId);
|
|
629
680
|
// Turn start and Undo/Reapply share the per-session lock: a start waits for
|
|
630
681
|
// an in-flight mutation, and a mutation queued after this start re-checks
|
|
631
682
|
// under the lock and finds the turn running.
|
|
@@ -701,6 +752,9 @@ export function beginAgentTurnReview(ownerSessionId, childSessionId, meta = {})
|
|
|
701
752
|
if (DISABLED) return null;
|
|
702
753
|
const owner = clean(ownerSessionId);
|
|
703
754
|
const child = clean(childSessionId);
|
|
755
|
+
// Durable: the worker's edits join the Lead's session review even when it
|
|
756
|
+
// runs its own session turn, and whatever the turn's agent list keeps.
|
|
757
|
+
recordReviewOwner(child, owner);
|
|
704
758
|
const turn = _turnsBySession.get(owner);
|
|
705
759
|
if (!owner || !child || !turn) return null;
|
|
706
760
|
const existingTracker = _diffTrackersBySession.get(child);
|
|
@@ -735,11 +789,15 @@ export function completeAgentTurnReview(handle, patches = []) {
|
|
|
735
789
|
const tracker = _diffTrackersBySession.get(key);
|
|
736
790
|
const matchingTracker =
|
|
737
791
|
tracker && tracker.ownerSessionId === clean(handle.ownerSessionId) && tracker.ownerGeneration === handle.generation;
|
|
792
|
+
// A worker that runs its own session turn replaces the bound tracker with
|
|
793
|
+
// its turn's own one (no owner). beginAgentTurnReview reset any older
|
|
794
|
+
// tracker, so an unowned one here holds this execution's edits.
|
|
795
|
+
const source = matchingTracker || (tracker && !tracker.ownerSessionId) ? tracker : null;
|
|
738
796
|
try {
|
|
739
797
|
if (!turn || turn.generation !== handle.generation) return false;
|
|
740
798
|
const prior = turn.agents.get(key);
|
|
741
|
-
const tracked =
|
|
742
|
-
const patch = tracked ?
|
|
799
|
+
const tracked = Boolean(source) && source.revision > 0;
|
|
800
|
+
const patch = tracked ? source.unifiedDiff : mergePatches(patches);
|
|
743
801
|
if (!patch) {
|
|
744
802
|
if (tracked && prior) {
|
|
745
803
|
turn.agents.delete(key);
|
|
@@ -755,8 +813,8 @@ export function completeAgentTurnReview(handle, patches = []) {
|
|
|
755
813
|
patch: tracked ? patch : mergePatches([prior?.patch, patch]),
|
|
756
814
|
});
|
|
757
815
|
const ownerTracker = _diffTrackersBySession.get(clean(handle.ownerSessionId));
|
|
758
|
-
if (
|
|
759
|
-
for (const path of
|
|
816
|
+
if (source && ownerTracker && ownerTracker !== source) {
|
|
817
|
+
for (const path of source.ownedPaths) ownerTracker.ownedPaths.add(path);
|
|
760
818
|
}
|
|
761
819
|
trimAgentReviews(turn);
|
|
762
820
|
// A worker that finishes after its Lead turn sealed re-seals that turn's
|
|
@@ -780,7 +838,7 @@ export function completeAgentTurnReview(handle, patches = []) {
|
|
|
780
838
|
* are precisely the attribution that contention destroys. */
|
|
781
839
|
async function persistTurnSnapshotRecord(ownerSessionId, tracker) {
|
|
782
840
|
const snapshot = tracker?.worktreeSnapshot;
|
|
783
|
-
if (!ownerSessionId || !snapshot?.baselineTree || !snapshot.root) return;
|
|
841
|
+
if (!ownerSessionId || tracker.discarded || !snapshot?.baselineTree || !snapshot.root) return;
|
|
784
842
|
const owned = [...sessionAttributedPaths(snapshot.root, tracker)];
|
|
785
843
|
if (owned.length === 0) return;
|
|
786
844
|
try {
|
|
@@ -937,6 +995,8 @@ const pendingFreezeKey = (ownerSessionId, checkpointId) => `${ownerSessionId}\0$
|
|
|
937
995
|
* re-freezes a turn that is still sealed.
|
|
938
996
|
*/
|
|
939
997
|
function sealTurn(ownerSessionId, tracker, { reseal = false } = {}) {
|
|
998
|
+
// Its history was deleted (clearSessionChangeHistory): never written again.
|
|
999
|
+
if (tracker.discarded) return Promise.resolve();
|
|
940
1000
|
if (tracker.freezing) {
|
|
941
1001
|
if (reseal) return tracker.freezing;
|
|
942
1002
|
return tracker.freezing.then(() => (tracker.sealed ? undefined : sealTurn(ownerSessionId, tracker)));
|
|
@@ -953,11 +1013,11 @@ function sealTurn(ownerSessionId, tracker, { reseal = false } = {}) {
|
|
|
953
1013
|
// Best-effort refresh: seal with the last captured worktree state.
|
|
954
1014
|
}
|
|
955
1015
|
}
|
|
956
|
-
if (tracker.sealEpoch !== epoch || (reseal && !tracker.sealed)) return;
|
|
1016
|
+
if (tracker.discarded || tracker.sealEpoch !== epoch || (reseal && !tracker.sealed)) return;
|
|
957
1017
|
tracker.sealed = true;
|
|
958
1018
|
await persistTurnSnapshotRecord(ownerSessionId, tracker);
|
|
959
1019
|
try {
|
|
960
|
-
await freezeTurnChanges(ownerSessionId, tracker, agents);
|
|
1020
|
+
if (!tracker.discarded) await freezeTurnChanges(ownerSessionId, tracker, agents);
|
|
961
1021
|
} catch {
|
|
962
1022
|
/* the change record adds Undo history; it never fails a turn */
|
|
963
1023
|
}
|
|
@@ -1055,6 +1115,7 @@ async function resumeRecordedSnapshot(ownerSessionId, { omitPatch = false } = {}
|
|
|
1055
1115
|
async function scopedReviewFromRecord(ownerSessionId, { omitPatch = false } = {}) {
|
|
1056
1116
|
const resumed = await resumeRecordedSnapshot(ownerSessionId, { omitPatch });
|
|
1057
1117
|
if (!resumed) return null;
|
|
1118
|
+
void saveTurnReviewSummary(ownerSessionId, resumed.record, resumed.snapshot);
|
|
1058
1119
|
return {
|
|
1059
1120
|
supported: true,
|
|
1060
1121
|
files: resumed.snapshot.files || [],
|
|
@@ -1070,6 +1131,28 @@ async function scopedReviewFromRecord(ownerSessionId, { omitPatch = false } = {}
|
|
|
1070
1131
|
};
|
|
1071
1132
|
}
|
|
1072
1133
|
|
|
1134
|
+
/** The files and counts last computed from the durable record, without
|
|
1135
|
+
* rescanning the worktree. `cached` (not `recorded`): the caller's
|
|
1136
|
+
* refreshing read still follows and replaces it. */
|
|
1137
|
+
async function cachedReviewFromRecord(ownerSessionId) {
|
|
1138
|
+
const record = await loadTurnSnapshotRecord(ownerSessionId).catch(() => null);
|
|
1139
|
+
const cached = record ? await loadTurnReviewSummary(ownerSessionId, record) : null;
|
|
1140
|
+
if (!cached) return null;
|
|
1141
|
+
return {
|
|
1142
|
+
supported: true,
|
|
1143
|
+
files: cached.files,
|
|
1144
|
+
patch: '',
|
|
1145
|
+
patchOmitted: true,
|
|
1146
|
+
snapshotKind: 'scoped',
|
|
1147
|
+
revertMode: 'scoped',
|
|
1148
|
+
checkpointId: clean(record.checkpointId),
|
|
1149
|
+
patchTruncated: cached.patchTruncated === true,
|
|
1150
|
+
authoritative: true,
|
|
1151
|
+
agents: publicAgentReviews(ownerSessionId),
|
|
1152
|
+
reason: 'cached',
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1073
1156
|
function assertCheckpointMatches(expectedCheckpointId, actualCheckpointId) {
|
|
1074
1157
|
const expected = clean(expectedCheckpointId);
|
|
1075
1158
|
if (!expected) return;
|
|
@@ -1123,6 +1206,12 @@ export async function getTurnReviewDiff(_worktree, sessionId, options = {}) {
|
|
|
1123
1206
|
// turn cache evicted it, or the runtime restarted. The recorded baseline is
|
|
1124
1207
|
// still on disk and still exact. Rebuilt per read and then discarded, so a
|
|
1125
1208
|
// summary read skips its patch text instead of dropping it afterwards.
|
|
1209
|
+
// A collapsed read that asked for no refresh (entering the session) takes
|
|
1210
|
+
// the last computed summary instead of queueing a rescan.
|
|
1211
|
+
if (options.refresh === false && options.summary === true) {
|
|
1212
|
+
const cached = await cachedReviewFromRecord(ownerSessionId);
|
|
1213
|
+
if (cached) return cached;
|
|
1214
|
+
}
|
|
1126
1215
|
const recorded = await scopedReviewFromRecord(ownerSessionId, { omitPatch: options.summary === true });
|
|
1127
1216
|
if (recorded) return recorded;
|
|
1128
1217
|
}
|
|
@@ -1152,44 +1241,27 @@ function scopeMatchesWorktree(scope, worktree, tracker) {
|
|
|
1152
1241
|
return requested === root || requested.startsWith(`${root}${process.platform === 'win32' ? '\\' : '/'}`);
|
|
1153
1242
|
}
|
|
1154
1243
|
|
|
1155
|
-
/** Return the net
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1244
|
+
/** Return the session's net change: inside a Git root, the diff from its
|
|
1245
|
+
* earliest retained baseline tree to the worktree, scoped to the paths it
|
|
1246
|
+
* changed; everywhere else (a non-Git root, files outside the Git root) each
|
|
1247
|
+
* file's FIRST state observed in the session — by the Lead or a worker
|
|
1248
|
+
* attributed to it — against the disk now. Unlike getTurnReviewDiff this
|
|
1249
|
+
* survives user-turn boundaries and has no revert contract; the composer
|
|
1250
|
+
* keeps owning latest-turn Undo. */
|
|
1159
1251
|
export async function getSessionReviewDiff(worktree, sessionId) {
|
|
1160
1252
|
if (DISABLED) return { supported: false, files: [], patch: '' };
|
|
1161
1253
|
const ownerSessionId = clean(sessionId);
|
|
1162
1254
|
if (!ownerSessionId) return { supported: true, files: [], patch: '', authoritative: true };
|
|
1163
|
-
const review = await
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
const LIVE_RECORD_TTL_MS = 1000;
|
|
1175
|
-
const _liveRecords = new WeakMap();
|
|
1176
|
-
|
|
1177
|
-
/** The running non-Git turn as a record, so its edits join the net review
|
|
1178
|
-
* before the turn seals. Rebuilt at most once a second. */
|
|
1179
|
-
function liveTrackedRecord(tracker) {
|
|
1180
|
-
if (!tracker || tracker.worktreeSnapshot || !tracker.worktreeRoot || !clean(tracker.checkpointId)) return null;
|
|
1181
|
-
const cached = _liveRecords.get(tracker);
|
|
1182
|
-
if (cached && Date.now() - cached.at < LIVE_RECORD_TTL_MS && cached.checkpointId === tracker.checkpointId) {
|
|
1183
|
-
return cached.record;
|
|
1184
|
-
}
|
|
1185
|
-
const record = {
|
|
1186
|
-
checkpointId: clean(tracker.checkpointId),
|
|
1187
|
-
kind: 'tracked',
|
|
1188
|
-
root: tracker.worktreeRoot,
|
|
1189
|
-
files: trackedRecordFiles(tracker, tracker.worktreeRoot).map(storedChangeFile),
|
|
1190
|
-
};
|
|
1191
|
-
_liveRecords.set(tracker, { at: Date.now(), checkpointId: record.checkpointId, record });
|
|
1192
|
-
return record;
|
|
1255
|
+
const [review, baselines, records] = await Promise.all([
|
|
1256
|
+
baselineSessionReviewDiff(worktree, ownerSessionId),
|
|
1257
|
+
loadSessionBaselines(ownerSessionId).catch(() => null),
|
|
1258
|
+
// Read through the mtime-keyed cache: only files no baseline knows (a
|
|
1259
|
+
// session recorded before baselines existed) come from them.
|
|
1260
|
+
loadTurnChangeRecords(ownerSessionId).catch(() => []),
|
|
1261
|
+
]);
|
|
1262
|
+
const merged = await sessionChangeReview(review, { baselines, records });
|
|
1263
|
+
// `historyTurns`: the retained turn records clearSessionChangeHistory deletes.
|
|
1264
|
+
return { ...merged, historyTurns: records.length };
|
|
1193
1265
|
}
|
|
1194
1266
|
|
|
1195
1267
|
async function baselineSessionReviewDiff(worktree, ownerSessionId) {
|
|
@@ -1211,8 +1283,9 @@ async function baselineSessionReviewDiff(worktree, ownerSessionId) {
|
|
|
1211
1283
|
for (const path of sessionAttributedPaths(root, tracker)) ownedPaths.add(path);
|
|
1212
1284
|
if (ownedPaths.size === 0) {
|
|
1213
1285
|
// Authoritatively empty only when the session left ownership metadata
|
|
1214
|
-
// behind (a stored scope or a
|
|
1215
|
-
// change records are the only memory of what it changed.
|
|
1286
|
+
// behind (a stored scope, a live tracker, or a deleted history); with
|
|
1287
|
+
// none, the retained change records are the only memory of what it changed.
|
|
1288
|
+
const known = Boolean(stored || tracker) || (await sessionChangeHistoryCleared(ownerSessionId).catch(() => false));
|
|
1216
1289
|
return {
|
|
1217
1290
|
supported: true,
|
|
1218
1291
|
root,
|
|
@@ -1220,7 +1293,7 @@ async function baselineSessionReviewDiff(worktree, ownerSessionId) {
|
|
|
1220
1293
|
patch: '',
|
|
1221
1294
|
snapshotKind: 'session',
|
|
1222
1295
|
authoritative: true,
|
|
1223
|
-
source:
|
|
1296
|
+
source: known ? 'empty' : 'absent',
|
|
1224
1297
|
};
|
|
1225
1298
|
}
|
|
1226
1299
|
const baselineTree = stored?.baselineTree || tracker?.worktreeSnapshot?.baselineTree;
|
|
@@ -1235,6 +1308,9 @@ async function baselineSessionReviewDiff(worktree, ownerSessionId) {
|
|
|
1235
1308
|
baselineFiles: [...baselineFiles.values()],
|
|
1236
1309
|
}).catch(() => null);
|
|
1237
1310
|
if (snapshot) {
|
|
1311
|
+
// The island counts only what is still uncommitted: the owned files
|
|
1312
|
+
// that differ from HEAD now, measured against HEAD.
|
|
1313
|
+
const uncommittedFiles = await diffTurnWorktreeHead(snapshot, [...ownedPaths]).catch(() => null);
|
|
1238
1314
|
return {
|
|
1239
1315
|
supported: true,
|
|
1240
1316
|
root,
|
|
@@ -1244,31 +1320,37 @@ async function baselineSessionReviewDiff(worktree, ownerSessionId) {
|
|
|
1244
1320
|
snapshotKind: 'session',
|
|
1245
1321
|
authoritative: true,
|
|
1246
1322
|
source: 'git',
|
|
1323
|
+
...(uncommittedFiles ? { uncommittedFiles } : {}),
|
|
1247
1324
|
};
|
|
1248
1325
|
}
|
|
1249
1326
|
}
|
|
1250
1327
|
// A non-Git first turn still has an exact apply_patch tracker. It cannot
|
|
1251
1328
|
// cross a runtime restart, but remains preferable to widening into unrelated
|
|
1252
1329
|
// files from the surrounding directory.
|
|
1330
|
+
// `owned` lets the session review flag an owned file no other source knows.
|
|
1253
1331
|
if (tracker?.valid) {
|
|
1254
1332
|
return {
|
|
1255
1333
|
supported: true,
|
|
1334
|
+
root,
|
|
1256
1335
|
files: [],
|
|
1257
1336
|
patch: tracker.unifiedDiff || '',
|
|
1258
1337
|
patchTruncated: tracker.patchTruncated === true,
|
|
1259
1338
|
snapshotKind: 'session-tool',
|
|
1260
1339
|
authoritative: true,
|
|
1261
1340
|
source: 'tool',
|
|
1341
|
+
owned: [...ownedPaths],
|
|
1262
1342
|
};
|
|
1263
1343
|
}
|
|
1264
1344
|
return {
|
|
1265
1345
|
supported: false,
|
|
1346
|
+
root,
|
|
1266
1347
|
files: [],
|
|
1267
1348
|
patch: '',
|
|
1268
1349
|
snapshotKind: 'session',
|
|
1269
1350
|
authoritative: false,
|
|
1270
1351
|
reason: 'session baseline is unavailable',
|
|
1271
1352
|
source: 'unavailable',
|
|
1353
|
+
owned: [...ownedPaths],
|
|
1272
1354
|
};
|
|
1273
1355
|
}
|
|
1274
1356
|
|
|
@@ -1625,6 +1707,45 @@ export function reapplyTurnChanges(sessionId, checkpointId, file, revision) {
|
|
|
1625
1707
|
return mutateTurnChanges(sessionId, checkpointId, file, revision, false);
|
|
1626
1708
|
}
|
|
1627
1709
|
|
|
1710
|
+
/** Permanently delete this session's retained change history — every turn's
|
|
1711
|
+
* change record (patches, file metadata, applied/reverted state), its stored
|
|
1712
|
+
* Git baselines, and the live and latest-turn trackers that would resurface
|
|
1713
|
+
* them — so its reviews answer empty, across a restart too. Edits from the
|
|
1714
|
+
* next turn on are tracked again. Refused while a turn runs or seals; on the
|
|
1715
|
+
* per-session lock, behind any Undo/Reapply. Files, the Git index, commits
|
|
1716
|
+
* and the conversation are never touched. Resolves to `{ turns }`, the
|
|
1717
|
+
* number of turn records deleted. */
|
|
1718
|
+
export async function clearSessionChangeHistory(sessionId) {
|
|
1719
|
+
const ownerSessionId = clean(sessionId);
|
|
1720
|
+
if (!ownerSessionId) throw new Error('change history is unavailable without a session');
|
|
1721
|
+
return await serializeChangeMutation(ownerSessionId, async () => {
|
|
1722
|
+
if (sessionTurnBusy(ownerSessionId)) {
|
|
1723
|
+
throw new Error('change history cannot be deleted while this session is running a turn');
|
|
1724
|
+
}
|
|
1725
|
+
// Invalidate every producer of the old history BEFORE the awaited
|
|
1726
|
+
// deletion: a worker finishing meanwhile finds no turn to fold into, and
|
|
1727
|
+
// a re-seal it (or anything earlier) already queued finds its tracker
|
|
1728
|
+
// discarded, so nothing begun before the delete can write it back.
|
|
1729
|
+
for (const [trackedSessionId, tracker] of _diffTrackersBySession) {
|
|
1730
|
+
if (trackedSessionId !== ownerSessionId && tracker.ownerSessionId !== ownerSessionId) continue;
|
|
1731
|
+
tracker.discarded = true;
|
|
1732
|
+
tracker.resealQueued = false;
|
|
1733
|
+
releaseDiffTrackerContent(tracker);
|
|
1734
|
+
_diffTrackersBySession.delete(trackedSessionId);
|
|
1735
|
+
}
|
|
1736
|
+
_turnsBySession.delete(ownerSessionId);
|
|
1737
|
+
for (const key of _unknownChangeStates.keys()) {
|
|
1738
|
+
if (key.startsWith(`${ownerSessionId}\0`)) _unknownChangeStates.delete(key);
|
|
1739
|
+
}
|
|
1740
|
+
// The session's first-seen baselines go too: later edits start fresh ones.
|
|
1741
|
+
const [turns] = await Promise.all([
|
|
1742
|
+
clearStoredChangeHistory(ownerSessionId),
|
|
1743
|
+
clearSessionBaselines(ownerSessionId),
|
|
1744
|
+
]);
|
|
1745
|
+
return { turns };
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1628
1749
|
export function _resetTurnSnapshotForTest() {
|
|
1629
1750
|
_turnsBySession.clear();
|
|
1630
1751
|
_diffTrackersBySession.clear();
|
|
@@ -1634,4 +1755,5 @@ export function _resetTurnSnapshotForTest() {
|
|
|
1634
1755
|
_turnOpenedHookForTest = null;
|
|
1635
1756
|
_agentTurnSeq = 0;
|
|
1636
1757
|
_resetTurnWorktreeSnapshotsForTest();
|
|
1758
|
+
_resetSessionBaselinesForTest();
|
|
1637
1759
|
}
|