mixdog 1.2.1 → 1.2.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 +18 -0
- package/package.json +2 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- 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/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
- 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/providers/account-pool.mjs +188 -29
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -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 +48 -37
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
- 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 +102 -45
- 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-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
- 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 +88 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
- 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 +103 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -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-request-body.mjs +41 -5
- 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 +369 -112
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- 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-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- 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 +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
- 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 +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
- 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/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -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 +7 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- 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-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- 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-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -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/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- 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 +1 -97
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- 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-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +65 -50
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +2 -2
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +31 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -40
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +670 -274
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
- package/src/runtime/shared/turn-snapshot.mjs +986 -690
- package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -24
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +19 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +12 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +73 -8
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +46 -14
- package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
- package/src/session-runtime/mcp-glue.mjs +3 -0
- 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 +85 -17
- 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/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- 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 +4 -0
- package/src/session-runtime/resource-mcp-api.mjs +98 -2
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +21 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- 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/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- 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/tag-registry.mjs +15 -1
- 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-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +74 -0
- package/src/standalone/daemon.mjs +64 -8
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +14 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -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 +18 -3
- package/src/standalone/session-service-api.mjs +11 -1
- package/src/standalone/session-service.mjs +1 -0
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +6 -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.mjs +2 -1
- package/src/tui/app/model-options.mjs +11 -16
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +20 -5
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- 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/dist/index.mjs +3221 -1500
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +43 -24
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/extensions.mjs +22 -0
- package/src/tui/session/session-api/integrations.mjs +16 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +7 -2
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +11 -14
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/session-flow/drain.mjs +7 -1
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +46 -16
- 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/workflows/default/WORKFLOW.md +3 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -6,14 +6,22 @@ import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
|
|
|
6
6
|
import { cleanString as clean } from './clean.mjs';
|
|
7
7
|
import { runGitOffThread } from './git-runner.mjs';
|
|
8
8
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
createReviewPatchBudget,
|
|
11
|
+
PATCH_TOO_LARGE,
|
|
12
|
+
patchOmittedFile,
|
|
13
|
+
REVIEW_FILE_PATCH_MAX_BYTES,
|
|
14
|
+
REVIEW_PATCH_MAX_BYTES,
|
|
15
|
+
reviewDiffTooLarge,
|
|
16
|
+
} from './review-diff.mjs';
|
|
17
|
+
import { splitPatchSections } from './turn-changes.mjs';
|
|
10
18
|
|
|
11
19
|
const COMMAND_TIMEOUT_MS = 30_000;
|
|
12
20
|
const COMMAND_MAX_BYTES = 8 * 1024 * 1024;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// per
|
|
16
|
-
const
|
|
21
|
+
// Room for git's diagnostics beside a patch read up to its byte cap.
|
|
22
|
+
const PATCH_OUTPUT_SLACK_BYTES = 64 * 1024;
|
|
23
|
+
// Paths per patch read once the whole range overflowed (bounded command line).
|
|
24
|
+
const PATCH_BATCH_FILES = 32;
|
|
17
25
|
const MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
|
|
18
26
|
// Largest tool-supplied baseline kept for `hash-object`: the apply_patch edit
|
|
19
27
|
// limit. (2–64 MiB explicit untracked edits keep their exact revert.)
|
|
@@ -32,9 +40,6 @@ const SHADOW_GC_PRUNE = '7.days.ago';
|
|
|
32
40
|
// signal reports (an external editor, a command the shell classifies
|
|
33
41
|
// read-only) still show within this gap.
|
|
34
42
|
const MIN_RECAPTURE_GAP_MS = 15_000;
|
|
35
|
-
// The same gap for a timer poll of a review the user has open. Shorter than
|
|
36
|
-
// the desktop bar's 6 s poll, so each of those polls still captures.
|
|
37
|
-
const VISIBLE_RECAPTURE_GAP_MS = 5_000;
|
|
38
43
|
const states = new Map();
|
|
39
44
|
|
|
40
45
|
function pathKey(value) {
|
|
@@ -44,7 +49,7 @@ function pathKey(value) {
|
|
|
44
49
|
|
|
45
50
|
/** `resolve` plus symlinks of the nearest existing ancestor: git reports the
|
|
46
51
|
* worktree root canonically (macOS /var -> /private/var), callers may not. */
|
|
47
|
-
function canonicalPath(value, dirs = null) {
|
|
52
|
+
export function canonicalPath(value, dirs = null) {
|
|
48
53
|
const full = resolve(value);
|
|
49
54
|
if (dirs) {
|
|
50
55
|
// Same result as the walk below without repeating it per file: the parent
|
|
@@ -169,8 +174,30 @@ export function noteTurnWorktreeChange(paths = null) {
|
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
// A lock hold (queue wait plus work) at least this long is logged, so a slow
|
|
178
|
+
// review bar can be told apart as waiting behind other captures or slow git.
|
|
179
|
+
const SLOW_LOCK_LOG_MS = 1_000;
|
|
180
|
+
|
|
181
|
+
function withStateLock(state, task, op = 'task') {
|
|
182
|
+
const queuedAt = Date.now();
|
|
183
|
+
const queuedBehind = state.lockDepth || 0;
|
|
184
|
+
state.lockDepth = queuedBehind + 1;
|
|
185
|
+
let startedAt = 0;
|
|
186
|
+
const timed = async () => {
|
|
187
|
+
startedAt = Date.now();
|
|
188
|
+
return await task();
|
|
189
|
+
};
|
|
190
|
+
const next = state.lock.then(timed, timed).finally(() => {
|
|
191
|
+
state.lockDepth -= 1;
|
|
192
|
+
const finishedAt = Date.now();
|
|
193
|
+
const waitMs = (startedAt || finishedAt) - queuedAt;
|
|
194
|
+
if (finishedAt - queuedAt >= SLOW_LOCK_LOG_MS) {
|
|
195
|
+
console.info(
|
|
196
|
+
`[turn-snapshot-lock] op=${op} waitMs=${waitMs} workMs=${finishedAt - (startedAt || finishedAt)} ` +
|
|
197
|
+
`queuedBehind=${queuedBehind} root=${state.root}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
174
201
|
state.lock = next.then(
|
|
175
202
|
() => undefined,
|
|
176
203
|
() => undefined
|
|
@@ -338,6 +365,7 @@ async function ensureState(state) {
|
|
|
338
365
|
}
|
|
339
366
|
|
|
340
367
|
async function resetShadowIndex(state) {
|
|
368
|
+
state.lastCapture = null;
|
|
341
369
|
state.sourceIndexPath ??= await sourceGitPath(state.root, 'index');
|
|
342
370
|
const sourceIndex = state.sourceIndexPath;
|
|
343
371
|
const target = join(state.gitDir, 'index');
|
|
@@ -400,10 +428,33 @@ async function changedWorktreePaths(state) {
|
|
|
400
428
|
};
|
|
401
429
|
}
|
|
402
430
|
|
|
403
|
-
|
|
431
|
+
/** The capture sequence a caller observes before queueing on the lock. */
|
|
432
|
+
function captureSequence(state) {
|
|
433
|
+
return state.captureSeq || 0;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** `after` (a captureSequence() read before queueing): a capture of this root
|
|
437
|
+
* that began after that point, with no reported write since and covering
|
|
438
|
+
* every explicit path, already is a capture that began after the call. Many
|
|
439
|
+
* sessions of one repository queue reads together; they share that capture
|
|
440
|
+
* instead of each re-scanning the worktree in turn. */
|
|
441
|
+
async function captureTreeUnlocked(state, toolPaths = [], { after = -1 } = {}) {
|
|
404
442
|
await ensureState(state);
|
|
405
|
-
const { paths, excludedUntracked } = await changedWorktreePaths(state);
|
|
406
443
|
const explicit = new Set(toolPaths);
|
|
444
|
+
const last = state.lastCapture;
|
|
445
|
+
if (
|
|
446
|
+
after >= 0 &&
|
|
447
|
+
last &&
|
|
448
|
+
last.seq > after &&
|
|
449
|
+
last.stamp === state.changeStamp &&
|
|
450
|
+
[...explicit].every((rel) => last.explicit.has(rel))
|
|
451
|
+
) {
|
|
452
|
+
return last.tree;
|
|
453
|
+
}
|
|
454
|
+
state.captureSeq = captureSequence(state) + 1;
|
|
455
|
+
const capture = { seq: state.captureSeq, stamp: state.changeStamp, explicit, tree: '' };
|
|
456
|
+
state.lastCapture = null;
|
|
457
|
+
const { paths, excludedUntracked } = await changedWorktreePaths(state);
|
|
407
458
|
for (const rel of explicit) {
|
|
408
459
|
try {
|
|
409
460
|
const stat = await lstat(resolve(state.root, rel));
|
|
@@ -432,7 +483,9 @@ async function captureTreeUnlocked(state, toolPaths = []) {
|
|
|
432
483
|
});
|
|
433
484
|
}
|
|
434
485
|
const tree = await runGit(shadowArgs(state, ['write-tree']), { cwd: state.root });
|
|
435
|
-
|
|
486
|
+
capture.tree = clean(tree.stdout);
|
|
487
|
+
state.lastCapture = capture;
|
|
488
|
+
return capture.tree;
|
|
436
489
|
}
|
|
437
490
|
|
|
438
491
|
/** Only explicit tool targets bypass ignore/size filters. Their first-before
|
|
@@ -481,7 +534,6 @@ function withOmittedBaselines(snapshot, review) {
|
|
|
481
534
|
omitted: true,
|
|
482
535
|
size,
|
|
483
536
|
});
|
|
484
|
-
if (review.patchOmitted) continue;
|
|
485
537
|
const newHeader = present ? `b/${entry.path}` : '/dev/null';
|
|
486
538
|
patch +=
|
|
487
539
|
`diff --git a/${entry.path} b/${entry.path}\n` +
|
|
@@ -490,19 +542,7 @@ function withOmittedBaselines(snapshot, review) {
|
|
|
490
542
|
`Binary files a/${entry.path} and ${newHeader} differ\n`;
|
|
491
543
|
}
|
|
492
544
|
files.sort((left, right) => left.path.localeCompare(right.path));
|
|
493
|
-
return { ...review, files, patch };
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/** Revert never claims success for a path whose baseline bytes were not captured. */
|
|
497
|
-
function assertRevertable(snapshot, targets) {
|
|
498
|
-
for (const target of targets) {
|
|
499
|
-
const entry = snapshot.baselineFiles.get(pathKey(target));
|
|
500
|
-
if (entry?.omitted === true) {
|
|
501
|
-
throw new Error(
|
|
502
|
-
`turn review revert is unavailable for ${target}: binary/large file content was not captured (${Number(entry.size) || 0} bytes)`
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
545
|
+
return { ...review, files, patch, diffTooLarge: reviewDiffTooLarge(files, patch) };
|
|
506
546
|
}
|
|
507
547
|
|
|
508
548
|
async function applyBaselineFilesUnlocked(state, tree, allEntries) {
|
|
@@ -646,9 +686,98 @@ function parseNumstat(text) {
|
|
|
646
686
|
return out;
|
|
647
687
|
}
|
|
648
688
|
|
|
649
|
-
|
|
689
|
+
const literalPathspec = (path) => `:(literal)${path}`;
|
|
690
|
+
|
|
691
|
+
/** The `diff --git` sections of one patch read, keyed by path; null once its
|
|
692
|
+
* output passed `maxBytes`. */
|
|
693
|
+
async function patchSectionsByPath(state, baselineTree, currentTree, pathspec, maxBytes) {
|
|
694
|
+
let result;
|
|
695
|
+
try {
|
|
696
|
+
result = await runGit(
|
|
697
|
+
shadowArgs(state, [
|
|
698
|
+
'diff',
|
|
699
|
+
'--no-ext-diff',
|
|
700
|
+
'--unified=3',
|
|
701
|
+
'--find-renames',
|
|
702
|
+
baselineTree,
|
|
703
|
+
currentTree,
|
|
704
|
+
'--',
|
|
705
|
+
...pathspec,
|
|
706
|
+
]),
|
|
707
|
+
{ cwd: state.root, maxBytes: maxBytes + PATCH_OUTPUT_SLACK_BYTES }
|
|
708
|
+
);
|
|
709
|
+
} catch (error) {
|
|
710
|
+
if (error?.code === 'EMAXBUFFER') return null;
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
713
|
+
const sections = new Map();
|
|
714
|
+
for (const section of splitPatchSections(Buffer.from(result.stdout, 'utf8'))) {
|
|
715
|
+
const path = section.newPath ?? section.oldPath;
|
|
716
|
+
if (path) sections.set(pathKey(path), section.patch);
|
|
717
|
+
}
|
|
718
|
+
return sections;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/** After the whole range overflowed: patches read in path batches, the
|
|
722
|
+
* smallest changes first, until the reply's total is spent. A batch that
|
|
723
|
+
* overflows is halved; a single file that does is too large. */
|
|
724
|
+
async function batchedPatchSections(state, baselineTree, currentTree, files, sections) {
|
|
725
|
+
const lines = (file) => (Number(file.additions) || 0) + (Number(file.deletions) || 0);
|
|
726
|
+
let remaining = REVIEW_PATCH_MAX_BYTES;
|
|
727
|
+
const read = async (batch) => {
|
|
728
|
+
if (batch.length === 0 || remaining <= 0) return;
|
|
729
|
+
const pathspec = batch.flatMap((file) => [file.path, file.oldPath].filter(Boolean).map(literalPathspec));
|
|
730
|
+
const found = await patchSectionsByPath(state, baselineTree, currentTree, pathspec, remaining);
|
|
731
|
+
if (!found) {
|
|
732
|
+
if (batch.length === 1) return;
|
|
733
|
+
const half = Math.ceil(batch.length / 2);
|
|
734
|
+
await read(batch.slice(0, half));
|
|
735
|
+
await read(batch.slice(half));
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
for (const [key, section] of found) {
|
|
739
|
+
sections.set(key, section);
|
|
740
|
+
if (section.length <= REVIEW_FILE_PATCH_MAX_BYTES) remaining -= section.length;
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
// Every changed line costs at least its marker and newline: a file whose
|
|
744
|
+
// counts alone pass the per-file cap is never read.
|
|
745
|
+
const queue = files
|
|
746
|
+
.filter((file) => 2 * lines(file) <= REVIEW_FILE_PATCH_MAX_BYTES)
|
|
747
|
+
.sort((left, right) => lines(left) - lines(right));
|
|
748
|
+
for (let index = 0; index < queue.length && remaining > 0; index += PATCH_BATCH_FILES) {
|
|
749
|
+
await read(queue.slice(index, index + PATCH_BATCH_FILES));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** Every file's patch within the reply caps. A file past the per-file cap or
|
|
754
|
+
* the remaining total is listed without one (`patchOmitted`, too large). */
|
|
755
|
+
async function boundedTreePatch(state, baselineTree, currentTree, pathspec, files) {
|
|
756
|
+
let sections = await patchSectionsByPath(state, baselineTree, currentTree, pathspec, COMMAND_MAX_BYTES);
|
|
757
|
+
// Read whole, every file has its section; read in batches, a file without
|
|
758
|
+
// one was too large or past the total.
|
|
759
|
+
const whole = Boolean(sections);
|
|
760
|
+
if (!sections) {
|
|
761
|
+
sections = new Map();
|
|
762
|
+
await batchedPatchSections(state, baselineTree, currentTree, files, sections);
|
|
763
|
+
}
|
|
764
|
+
const budget = createReviewPatchBudget();
|
|
765
|
+
const kept = [];
|
|
766
|
+
const bounded = files.map((file) => {
|
|
767
|
+
const section = sections.get(pathKey(file.path));
|
|
768
|
+
if (!section && whole) return file;
|
|
769
|
+
if (section && budget.admit(section.length)) {
|
|
770
|
+
kept.push(section);
|
|
771
|
+
return file;
|
|
772
|
+
}
|
|
773
|
+
return patchOmittedFile(file);
|
|
774
|
+
});
|
|
775
|
+
return { patch: Buffer.concat(kept).toString('utf8'), files: bounded };
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
async function diffTreesUnlocked(state, baselineTree, currentTree, paths = null) {
|
|
650
779
|
if (!baselineTree || !currentTree || baselineTree === currentTree) {
|
|
651
|
-
return { patch: '', files: [], patchTruncated: false, currentTree };
|
|
780
|
+
return { patch: '', files: [], patchTruncated: false, diffTooLarge: false, currentTree };
|
|
652
781
|
}
|
|
653
782
|
// A scoped review restricts the range to the paths its session owns: the
|
|
654
783
|
// baseline describes the whole worktree, concurrent sessions included.
|
|
@@ -658,23 +787,6 @@ async function diffTreesUnlocked(state, baselineTree, currentTree, paths = null,
|
|
|
658
787
|
runGit(shadowArgs(state, ['diff', '--name-status', '-z', ...rangeArgs]), { cwd: state.root }),
|
|
659
788
|
runGit(shadowArgs(state, ['diff', '--numstat', '-z', ...rangeArgs]), { cwd: state.root }),
|
|
660
789
|
]);
|
|
661
|
-
let patch = '';
|
|
662
|
-
let patchTruncated = false;
|
|
663
|
-
// A files-and-counts read skips the patch text, the one part whose cost
|
|
664
|
-
// grows with the size of the change.
|
|
665
|
-
if (!omitPatch) {
|
|
666
|
-
try {
|
|
667
|
-
const result = await runGit(shadowArgs(state, ['diff', '--no-ext-diff', '--unified=3', ...rangeArgs]), {
|
|
668
|
-
cwd: state.root,
|
|
669
|
-
maxBytes: PATCH_MAX_BYTES + 256 * 1024,
|
|
670
|
-
});
|
|
671
|
-
patch = result.stdout.length <= PATCH_MAX_BYTES ? result.stdout : result.stdout.slice(0, PATCH_MAX_BYTES);
|
|
672
|
-
patchTruncated = result.stdout.length > PATCH_MAX_BYTES;
|
|
673
|
-
} catch (error) {
|
|
674
|
-
if (error?.code !== 'EMAXBUFFER') throw error;
|
|
675
|
-
patchTruncated = true;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
790
|
const statuses = parseNameStatus(nameStatus.stdout);
|
|
679
791
|
const stats = parseNumstat(numstat.stdout);
|
|
680
792
|
const keys = [...new Set([...statuses.keys(), ...stats.keys()])];
|
|
@@ -692,83 +804,27 @@ async function diffTreesUnlocked(state, baselineTree, currentTree, paths = null,
|
|
|
692
804
|
};
|
|
693
805
|
})
|
|
694
806
|
.sort((left, right) => left.path.localeCompare(right.path));
|
|
695
|
-
|
|
807
|
+
const bounded = await boundedTreePatch(state, baselineTree, currentTree, pathspec, files);
|
|
808
|
+
return {
|
|
809
|
+
patch: bounded.patch,
|
|
810
|
+
files: bounded.files,
|
|
811
|
+
patchTruncated: false,
|
|
812
|
+
diffTooLarge: reviewDiffTooLarge(bounded.files, bounded.patch),
|
|
813
|
+
currentTree,
|
|
814
|
+
};
|
|
696
815
|
}
|
|
697
816
|
|
|
698
|
-
/**
|
|
699
|
-
*
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const scope = Array.isArray(paths) ? [...new Set(paths.filter(Boolean))] : null;
|
|
710
|
-
if (scope && scope.length === 0) return { root: snapshot.root, files: [] };
|
|
711
|
-
return await withStateLock(snapshot.state, async () => {
|
|
712
|
-
const { state, baselineTree, currentTree } = snapshot;
|
|
713
|
-
let review = { files: [], patch: '', patchTruncated: false };
|
|
714
|
-
let sections = [];
|
|
715
|
-
let unavailable = '';
|
|
716
|
-
if (currentTree && currentTree !== baselineTree) {
|
|
717
|
-
review = await diffTreesUnlocked(state, baselineTree, currentTree, scope, { omitPatch: true });
|
|
718
|
-
const args = [
|
|
719
|
-
'diff',
|
|
720
|
-
'--binary',
|
|
721
|
-
'--full-index',
|
|
722
|
-
'--no-color',
|
|
723
|
-
'--no-ext-diff',
|
|
724
|
-
'--no-textconv',
|
|
725
|
-
'--src-prefix=a/',
|
|
726
|
-
'--dst-prefix=b/',
|
|
727
|
-
'--find-renames',
|
|
728
|
-
baselineTree,
|
|
729
|
-
currentTree,
|
|
730
|
-
'--',
|
|
731
|
-
...(scope || ['.']),
|
|
732
|
-
];
|
|
733
|
-
try {
|
|
734
|
-
const result = await runGit(shadowArgs(state, args), {
|
|
735
|
-
cwd: state.root,
|
|
736
|
-
maxBytes: FREEZE_PATCH_MAX_BYTES,
|
|
737
|
-
stdoutEncoding: 'base64',
|
|
738
|
-
});
|
|
739
|
-
sections = splitPatchSections(Buffer.from(result.stdout, 'base64'));
|
|
740
|
-
} catch (error) {
|
|
741
|
-
if (error?.code !== 'EMAXBUFFER') throw error;
|
|
742
|
-
unavailable = 'the turn changed too much to keep its patches for undo';
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
const listed = withOmittedBaselines({ ...snapshot, scopePaths: scope }, { ...review, patchOmitted: true });
|
|
746
|
-
const byPath = new Map();
|
|
747
|
-
for (const section of sections) {
|
|
748
|
-
const key = pathKey(section.newPath ?? section.oldPath ?? '');
|
|
749
|
-
// A type change diffs as a delete plus an add of one path: not one patch.
|
|
750
|
-
byPath.set(key, byPath.has(key) ? null : section.patch);
|
|
751
|
-
}
|
|
752
|
-
const files = (listed.files || []).map((file) => {
|
|
753
|
-
const entry = {
|
|
754
|
-
path: file.path,
|
|
755
|
-
...(file.oldPath ? { oldPath: file.oldPath } : {}),
|
|
756
|
-
status: normalizeChangeStatus(file.status),
|
|
757
|
-
additions: Number(file.additions) || 0,
|
|
758
|
-
deletions: Number(file.deletions) || 0,
|
|
759
|
-
...(file.binary ? { binary: true } : {}),
|
|
760
|
-
};
|
|
761
|
-
if (entry.status !== 'R') delete entry.oldPath;
|
|
762
|
-
if (file.omitted === true) {
|
|
763
|
-
return { ...entry, revertable: false, reason: 'binary/large file content was not captured' };
|
|
764
|
-
}
|
|
765
|
-
if (file.status === 'T') return { ...entry, revertable: false, reason: 'the file type changed' };
|
|
766
|
-
const patch = byPath.get(pathKey(file.path));
|
|
767
|
-
if (!patch) return { ...entry, revertable: false, reason: unavailable || 'no replayable patch was recorded' };
|
|
768
|
-
return { ...entry, revertable: true, patch };
|
|
769
|
-
});
|
|
770
|
-
return { root: snapshot.root, files };
|
|
771
|
-
});
|
|
817
|
+
/** One file's patch between two trees (a rename read with its old path), up
|
|
818
|
+
* to `maxBytes`; past it, the file is too large. */
|
|
819
|
+
async function filePatchUnlocked(state, baselineTree, currentTree, path, oldPath, maxBytes) {
|
|
820
|
+
if (!baselineTree || !currentTree || baselineTree === currentTree) return { patch: '' };
|
|
821
|
+
const pathspec = [path, oldPath].filter(Boolean).map(literalPathspec);
|
|
822
|
+
const sections = await patchSectionsByPath(state, baselineTree, currentTree, pathspec, maxBytes);
|
|
823
|
+
const section = sections?.get(pathKey(path)) ?? (oldPath ? sections?.get(pathKey(oldPath)) : undefined);
|
|
824
|
+
if (!sections || (section && section.length > maxBytes)) {
|
|
825
|
+
return { patch: '', patchOmitted: true, reason: PATCH_TOO_LARGE };
|
|
826
|
+
}
|
|
827
|
+
return { patch: section ? section.toString('utf8') : '' };
|
|
772
828
|
}
|
|
773
829
|
|
|
774
830
|
// One maintenance pass per shadow repository per process, off the turn's
|
|
@@ -862,53 +918,93 @@ export async function createTurnWorktreeSnapshot(worktree) {
|
|
|
862
918
|
|
|
863
919
|
/** Re-open a recorded baseline after the runtime lost its in-memory tracker.
|
|
864
920
|
* The tree object is the durable half of a review; `paths` scopes both this
|
|
865
|
-
* diff and any later revert to what the recording session actually owns.
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
921
|
+
* diff and any later revert to what the recording session actually owns. */
|
|
922
|
+
export async function resumeTurnWorktreeSnapshot(worktree, baselineTree, { paths = null, baselineFiles = [] } = {}) {
|
|
923
|
+
const tree = clean(baselineTree);
|
|
924
|
+
if (!tree) return null;
|
|
925
|
+
const root = await repositoryRoot(worktree).catch(() => null);
|
|
926
|
+
if (!root) return null;
|
|
927
|
+
const state = stateForRoot(root);
|
|
928
|
+
const after = captureSequence(state);
|
|
929
|
+
return await withStateLock(
|
|
930
|
+
state,
|
|
931
|
+
async () => {
|
|
932
|
+
if (!(await recordedTreeUnlocked(state, tree))) return null;
|
|
933
|
+
const scopePaths = Array.isArray(paths) && paths.length > 0 ? [...new Set(paths)] : null;
|
|
934
|
+
const applied = await applyBaselineFilesUnlocked(state, tree, baselineFiles || []);
|
|
935
|
+
const snapshot = {
|
|
936
|
+
state,
|
|
937
|
+
root,
|
|
938
|
+
baselineTree: applied.tree,
|
|
939
|
+
currentTree: tree,
|
|
940
|
+
patch: '',
|
|
941
|
+
files: [],
|
|
942
|
+
patchTruncated: false,
|
|
943
|
+
scopePaths,
|
|
944
|
+
toolPaths: new Set(scopePaths || []),
|
|
945
|
+
pendingBaselines: new Map(),
|
|
946
|
+
baselineFiles: new Map(applied.files.map((entry) => [pathKey(entry.path), entry])),
|
|
947
|
+
};
|
|
948
|
+
const currentTree = await captureTreeUnlocked(state, snapshot.toolPaths, { after });
|
|
949
|
+
Object.assign(
|
|
950
|
+
snapshot,
|
|
951
|
+
withOmittedBaselines(snapshot, await diffTreesUnlocked(state, snapshot.baselineTree, currentTree, scopePaths))
|
|
952
|
+
);
|
|
953
|
+
return snapshot;
|
|
954
|
+
},
|
|
955
|
+
'resume'
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/** A collected or never-written baseline restores nothing: false reports it
|
|
960
|
+
* as "no snapshot" instead of failing every later call made against it. */
|
|
961
|
+
async function recordedTreeUnlocked(state, tree) {
|
|
962
|
+
await ensureState(state);
|
|
963
|
+
const kind = await runGit(shadowArgs(state, ['cat-file', '-t', tree]), {
|
|
964
|
+
cwd: state.root,
|
|
965
|
+
allowFailure: true,
|
|
966
|
+
});
|
|
967
|
+
return kind.code === 0 && clean(kind.stdout) === 'tree';
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/** One file's patch of a recorded baseline against the worktree now, up to
|
|
971
|
+
* `maxBytes`. `paths` is the recording session's scope, every target checked
|
|
972
|
+
* on its own: a file outside it answers with no patch, a rename source
|
|
973
|
+
* outside it is left out of the diff. Null when the baseline is gone. */
|
|
974
|
+
export async function resumeTurnWorktreeFilePatch(
|
|
869
975
|
worktree,
|
|
870
976
|
baselineTree,
|
|
871
|
-
{ paths = null, baselineFiles = [],
|
|
977
|
+
{ paths = null, baselineFiles = [], path, oldPath = '', maxBytes } = {}
|
|
872
978
|
) {
|
|
873
979
|
const tree = clean(baselineTree);
|
|
874
980
|
if (!tree) return null;
|
|
875
981
|
const root = await repositoryRoot(worktree).catch(() => null);
|
|
876
982
|
if (!root) return null;
|
|
877
983
|
const state = stateForRoot(root);
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
state,
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const currentTree = await captureTreeUnlocked(state, snapshot.toolPaths);
|
|
903
|
-
Object.assign(
|
|
904
|
-
snapshot,
|
|
905
|
-
withOmittedBaselines(
|
|
906
|
-
snapshot,
|
|
907
|
-
await diffTreesUnlocked(state, snapshot.baselineTree, currentTree, scopePaths, { omitPatch })
|
|
908
|
-
)
|
|
909
|
-
);
|
|
910
|
-
return snapshot;
|
|
911
|
-
});
|
|
984
|
+
const scope = Array.isArray(paths) && paths.length > 0 ? new Set(paths.map(pathKey)) : null;
|
|
985
|
+
const allowed = (rel) => !scope || scope.has(pathKey(rel));
|
|
986
|
+
const target = safeRelativePath(root, path);
|
|
987
|
+
if (!allowed(target)) return { patch: '' };
|
|
988
|
+
const source = clean(oldPath) ? safeRelativePath(root, oldPath) : '';
|
|
989
|
+
// Only authorized paths reach the diff.
|
|
990
|
+
const targets = source && allowed(source) && pathKey(source) !== pathKey(target) ? [target, source] : [target];
|
|
991
|
+
const keys = new Set(targets.map(pathKey));
|
|
992
|
+
const after = captureSequence(state);
|
|
993
|
+
return await withStateLock(
|
|
994
|
+
state,
|
|
995
|
+
async () => {
|
|
996
|
+
if (!(await recordedTreeUnlocked(state, tree))) return null;
|
|
997
|
+
// Only this file's recorded first states change its side of the tree.
|
|
998
|
+
const applied = await applyBaselineFilesUnlocked(
|
|
999
|
+
state,
|
|
1000
|
+
tree,
|
|
1001
|
+
(baselineFiles || []).filter((entry) => keys.has(pathKey(safeRelativePath(root, entry?.path))))
|
|
1002
|
+
);
|
|
1003
|
+
const currentTree = await captureTreeUnlocked(state, targets, { after });
|
|
1004
|
+
return await filePatchUnlocked(state, applied.tree, currentTree, targets[0], targets[1] || '', maxBytes);
|
|
1005
|
+
},
|
|
1006
|
+
'file-patch'
|
|
1007
|
+
);
|
|
912
1008
|
}
|
|
913
1009
|
|
|
914
1010
|
/** What a capture starting now has seen: the change stamp, the source index
|
|
@@ -918,11 +1014,11 @@ async function captureMark(state) {
|
|
|
918
1014
|
}
|
|
919
1015
|
|
|
920
1016
|
/** Could the worktree differ from the snapshot's last capture? */
|
|
921
|
-
async function captureNeeded(snapshot
|
|
1017
|
+
async function captureNeeded(snapshot) {
|
|
922
1018
|
const mark = snapshot.captured;
|
|
923
1019
|
if (!mark || snapshot.pendingBaselines.size > 0) return true;
|
|
924
1020
|
if (mark.stamp !== snapshot.state.changeStamp) return true;
|
|
925
|
-
if (Date.now() - mark.at >=
|
|
1021
|
+
if (Date.now() - mark.at >= MIN_RECAPTURE_GAP_MS) return true;
|
|
926
1022
|
return (await sourceIndexIdentity(snapshot.state)) !== mark.index;
|
|
927
1023
|
}
|
|
928
1024
|
|
|
@@ -930,17 +1026,22 @@ function startRefresh(snapshot) {
|
|
|
930
1026
|
const flight = { seq: (snapshot.refreshSeq || 0) + 1, promise: null };
|
|
931
1027
|
snapshot.refreshSeq = flight.seq;
|
|
932
1028
|
snapshot.refreshing = flight;
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1029
|
+
const after = captureSequence(snapshot.state);
|
|
1030
|
+
flight.promise = withStateLock(
|
|
1031
|
+
snapshot.state,
|
|
1032
|
+
async () => {
|
|
1033
|
+
const mark = await captureMark(snapshot.state);
|
|
1034
|
+
await materializeToolBaselines(snapshot);
|
|
1035
|
+
const currentTree = await captureTreeUnlocked(snapshot.state, snapshot.toolPaths, { after });
|
|
1036
|
+
if (currentTree !== snapshot.currentTree) {
|
|
1037
|
+
const review = await diffTreesUnlocked(snapshot.state, snapshot.baselineTree, currentTree, snapshot.scopePaths);
|
|
1038
|
+
Object.assign(snapshot, withOmittedBaselines(snapshot, review));
|
|
1039
|
+
}
|
|
1040
|
+
snapshot.captured = mark;
|
|
1041
|
+
return snapshot;
|
|
1042
|
+
},
|
|
1043
|
+
'refresh'
|
|
1044
|
+
).finally(() => {
|
|
944
1045
|
if (snapshot.refreshing === flight) snapshot.refreshing = null;
|
|
945
1046
|
});
|
|
946
1047
|
return flight.promise;
|
|
@@ -949,13 +1050,10 @@ function startRefresh(snapshot) {
|
|
|
949
1050
|
/** Bring the snapshot up to date with the worktree. Single-flight: a call
|
|
950
1051
|
* made while a capture runs waits for it, then captures again only if a
|
|
951
1052
|
* change was reported after that capture began. Without a reported change,
|
|
952
|
-
* an index change or the
|
|
953
|
-
*
|
|
954
|
-
|
|
955
|
-
* after this call. */
|
|
956
|
-
export async function refreshTurnWorktreeSnapshot(snapshot, { force = false, visible = false } = {}) {
|
|
1053
|
+
* an index change or MIN_RECAPTURE_GAP_MS elapsed, the last capture stands.
|
|
1054
|
+
* `force` always answers with a capture that began after this call. */
|
|
1055
|
+
export async function refreshTurnWorktreeSnapshot(snapshot, { force = false } = {}) {
|
|
957
1056
|
if (!snapshot?.state || !snapshot.baselineTree) return null;
|
|
958
|
-
const gapMs = visible ? VISIBLE_RECAPTURE_GAP_MS : MIN_RECAPTURE_GAP_MS;
|
|
959
1057
|
const calledAtSeq = snapshot.refreshSeq || 0;
|
|
960
1058
|
for (;;) {
|
|
961
1059
|
const flight = snapshot.refreshing;
|
|
@@ -964,7 +1062,7 @@ export async function refreshTurnWorktreeSnapshot(snapshot, { force = false, vis
|
|
|
964
1062
|
if (flight.seq > calledAtSeq) return snapshot;
|
|
965
1063
|
continue;
|
|
966
1064
|
}
|
|
967
|
-
if (!force && !(await captureNeeded(snapshot
|
|
1065
|
+
if (!force && !(await captureNeeded(snapshot))) return snapshot;
|
|
968
1066
|
if (snapshot.refreshing) continue;
|
|
969
1067
|
return await startRefresh(snapshot);
|
|
970
1068
|
}
|
|
@@ -981,87 +1079,6 @@ function safeRelativePath(root, value) {
|
|
|
981
1079
|
return rel.replace(/\\/g, '/');
|
|
982
1080
|
}
|
|
983
1081
|
|
|
984
|
-
async function restorePathFromTree(snapshot, rel) {
|
|
985
|
-
const listed = await runGit(
|
|
986
|
-
shadowArgs(snapshot.state, ['ls-tree', '-z', '--name-only', snapshot.baselineTree, '--', rel]),
|
|
987
|
-
{ cwd: snapshot.root }
|
|
988
|
-
);
|
|
989
|
-
if (listed.stdout.split('\0').some((value) => value === rel)) {
|
|
990
|
-
await runGit(shadowArgs(snapshot.state, ['checkout', snapshot.baselineTree, '--', rel]), { cwd: snapshot.root });
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
const target = resolve(snapshot.root, rel);
|
|
994
|
-
try {
|
|
995
|
-
const stat = await lstat(target);
|
|
996
|
-
if (stat.isDirectory()) throw new Error('turn review revert refuses to remove a directory');
|
|
997
|
-
await rm(target, { force: true });
|
|
998
|
-
} catch (error) {
|
|
999
|
-
if (error?.code !== 'ENOENT') throw error;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
async function revertPathsUnlocked(snapshot, targets) {
|
|
1004
|
-
// Checked for every target before the first restore: no partial revert.
|
|
1005
|
-
assertRevertable(snapshot, targets);
|
|
1006
|
-
for (const target of targets) {
|
|
1007
|
-
await restorePathFromTree(snapshot, target);
|
|
1008
|
-
}
|
|
1009
|
-
// Restores report no change signal; the next poll captures afresh.
|
|
1010
|
-
snapshot.captured = null;
|
|
1011
|
-
const currentTree = await captureTreeUnlocked(snapshot.state, snapshot.toolPaths);
|
|
1012
|
-
Object.assign(
|
|
1013
|
-
snapshot,
|
|
1014
|
-
withOmittedBaselines(
|
|
1015
|
-
snapshot,
|
|
1016
|
-
await diffTreesUnlocked(snapshot.state, snapshot.baselineTree, currentTree, snapshot.scopePaths)
|
|
1017
|
-
)
|
|
1018
|
-
);
|
|
1019
|
-
// The absolute paths now back at their baseline bytes.
|
|
1020
|
-
return targets.map((target) => resolve(snapshot.root, target));
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
export async function revertTurnWorktreeFile(snapshot, value) {
|
|
1024
|
-
if (!snapshot?.state || !snapshot.baselineTree) throw new Error('turn worktree snapshot is unavailable');
|
|
1025
|
-
await refreshTurnWorktreeSnapshot(snapshot, { force: true });
|
|
1026
|
-
return await withStateLock(snapshot.state, async () => {
|
|
1027
|
-
const rel = safeRelativePath(snapshot.root, value);
|
|
1028
|
-
const entry = snapshot.files.find(
|
|
1029
|
-
(file) => pathKey(file.path) === pathKey(rel) || pathKey(file.oldPath) === pathKey(rel)
|
|
1030
|
-
);
|
|
1031
|
-
const targets = [...new Set([entry?.path || rel, entry?.oldPath].filter(Boolean))].map((target) =>
|
|
1032
|
-
safeRelativePath(snapshot.root, target)
|
|
1033
|
-
);
|
|
1034
|
-
return await revertPathsUnlocked(snapshot, targets);
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
export async function revertTurnWorktreeSnapshot(snapshot) {
|
|
1039
|
-
if (!snapshot?.state || !snapshot.baselineTree) throw new Error('turn worktree snapshot is unavailable');
|
|
1040
|
-
await refreshTurnWorktreeSnapshot(snapshot, { force: true });
|
|
1041
|
-
return await withStateLock(snapshot.state, async () => {
|
|
1042
|
-
// Resolve and validate every path before the first mutation. Renames and
|
|
1043
|
-
// copies contribute both sides so the worktree returns to the exact
|
|
1044
|
-
// turn-start tree rather than leaving a destination behind.
|
|
1045
|
-
const targets = [...new Set(snapshot.files.flatMap((file) => [file.path, file.oldPath]).filter(Boolean))].map(
|
|
1046
|
-
(target) => safeRelativePath(snapshot.root, target)
|
|
1047
|
-
);
|
|
1048
|
-
return await revertPathsUnlocked(snapshot, targets);
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
/** Restore only the listed paths. This is the session-owned revert: a shared
|
|
1053
|
-
* worktree cannot attribute the whole-tree diff, but the paths this session's
|
|
1054
|
-
* own tools wrote are exactly attributable. */
|
|
1055
|
-
export async function revertTurnWorktreePaths(snapshot, values) {
|
|
1056
|
-
if (!snapshot?.state || !snapshot.baselineTree) throw new Error('turn worktree snapshot is unavailable');
|
|
1057
|
-
const requested = (Array.isArray(values) ? values : []).filter(Boolean);
|
|
1058
|
-
if (requested.length === 0) throw new Error('turn review revert has no restorable files');
|
|
1059
|
-
return await withStateLock(snapshot.state, async () => {
|
|
1060
|
-
const targets = [...new Set(requested.map((value) => safeRelativePath(snapshot.root, value)))];
|
|
1061
|
-
return await revertPathsUnlocked(snapshot, targets);
|
|
1062
|
-
});
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
1082
|
export function _resetTurnWorktreeSnapshotsForTest() {
|
|
1066
1083
|
states.clear();
|
|
1067
1084
|
}
|