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
|
@@ -124,6 +124,29 @@ export function _retireSessionIncarnation(id) {
|
|
|
124
124
|
if (id) _sessionIncarnations.delete(id);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// ── Delete fence ────────────────────────────────────────────────────────────
|
|
128
|
+
// An explicitly deleted session may still own a running turn whose later saves
|
|
129
|
+
// carry a brand-new write guard and incarnation, so neither the cancellation
|
|
130
|
+
// counter nor the retired incarnation stops them: an absent file reads as
|
|
131
|
+
// "creatable". The fence is the id-level refusal every write-admission check
|
|
132
|
+
// consults. Only explicit user deletes fence (ids are never reused); the cap
|
|
133
|
+
// only ever forgets the oldest, long-settled entries.
|
|
134
|
+
const SESSION_DELETE_FENCE_MAX = 4096;
|
|
135
|
+
const _deleteFence = new Set();
|
|
136
|
+
|
|
137
|
+
export function fenceSessionWrites(id) {
|
|
138
|
+
if (!id) return;
|
|
139
|
+
_deleteFence.delete(id);
|
|
140
|
+
_deleteFence.add(id);
|
|
141
|
+
if (_deleteFence.size > SESSION_DELETE_FENCE_MAX) {
|
|
142
|
+
_deleteFence.delete(_deleteFence.values().next().value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function isSessionWriteFenced(id) {
|
|
147
|
+
return _deleteFence.has(id);
|
|
148
|
+
}
|
|
149
|
+
|
|
127
150
|
/** Clear every save marker for an id (successful hard delete). */
|
|
128
151
|
export function _clearSessionSaveState(id) {
|
|
129
152
|
if (!id) return;
|
|
@@ -83,6 +83,13 @@ function _readSidecarOwnerPid(path) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/** True only when the sidecar recorded an owner pid that no longer exists
|
|
87
|
+
* (pid-less legacy sidecars prove nothing and read false). */
|
|
88
|
+
export function isSidecarOwnerDead(path) {
|
|
89
|
+
const pid = _readSidecarOwnerPid(path);
|
|
90
|
+
return pid > 0 && !isProcessAlive(pid);
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
// ── Heartbeat publish ─────────────────────────────────────
|
|
87
94
|
// Lightweight per-session timestamp file (`<id>.hb`) consumed by the
|
|
88
95
|
// status aggregator for fresh-session detection. Decoupled from the
|
|
@@ -193,25 +200,74 @@ export function isSessionHeartbeatOwnerDead(id) {
|
|
|
193
200
|
// writer on the same session file (dual-writer last-save-wins clobbering).
|
|
194
201
|
// Refreshed by the engine share tick (~3s call, throttled here) and cleared
|
|
195
202
|
// on session switch/dispose; staleness covers crashes.
|
|
203
|
+
//
|
|
204
|
+
// Presence is a sidecar OF a session record and never outlives it: it is
|
|
205
|
+
// only published while the record exists, and a publish that loses a race
|
|
206
|
+
// with a deletion (this process or another one unlinked the record and its
|
|
207
|
+
// sidecar while the write was in flight) removes what it just wrote.
|
|
196
208
|
const _PRESENCE_THROTTLE_MS = 20_000;
|
|
197
209
|
const _presenceLastAt = new Map();
|
|
210
|
+
// id -> in-flight publish; deleteSessionPresence cancels it so the write that
|
|
211
|
+
// lands after the unlink is removed again instead of resurrecting the file.
|
|
212
|
+
const _presenceInFlight = new Map();
|
|
198
213
|
|
|
199
214
|
function _presencePath(id) {
|
|
200
215
|
return _sessionFilePath(id, '.own');
|
|
201
216
|
}
|
|
202
217
|
|
|
218
|
+
async function _sessionRecordExists(id) {
|
|
219
|
+
try {
|
|
220
|
+
await fsp.stat(sessionPath(id));
|
|
221
|
+
return true;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
// Only a proven absence stops the beacon; an unreadable stat keeps it.
|
|
224
|
+
return error?.code !== 'ENOENT';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function _publishPresence(id, now, flight) {
|
|
229
|
+
try {
|
|
230
|
+
// A record that does not exist (never saved yet, or deleted) gets no
|
|
231
|
+
// beacon; the throttle stays open so the next tick retries.
|
|
232
|
+
if (!(await _sessionRecordExists(id)) || flight.cancelled) {
|
|
233
|
+
if (_presenceLastAt.get(id) === now) _presenceLastAt.delete(id);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const target = _presencePath(id);
|
|
237
|
+
await fsp.writeFile(target, `${now}\n${process.pid}\n`, 'utf8');
|
|
238
|
+
if (flight.cancelled || !(await _sessionRecordExists(id))) {
|
|
239
|
+
await fsp.unlink(target).catch(() => {});
|
|
240
|
+
if (_presenceLastAt.get(id) === now) _presenceLastAt.delete(id);
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
/* best-effort beacon */
|
|
244
|
+
} finally {
|
|
245
|
+
if (_presenceInFlight.get(id) === flight) _presenceInFlight.delete(id);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
203
249
|
export function publishSessionPresence(id, ts) {
|
|
204
250
|
if (!id) return;
|
|
205
251
|
const now = ts || Date.now();
|
|
206
252
|
const last = _presenceLastAt.get(id) || 0;
|
|
207
253
|
if (now - last < _PRESENCE_THROTTLE_MS) return;
|
|
208
|
-
|
|
254
|
+
if (_presenceInFlight.has(id)) return;
|
|
255
|
+
_assertSessionId(id);
|
|
209
256
|
_presenceLastAt.set(id, now);
|
|
210
|
-
|
|
257
|
+
const flight = { cancelled: false, done: null };
|
|
258
|
+
_presenceInFlight.set(id, flight);
|
|
259
|
+
flight.done = _publishPresence(id, now, flight);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Test/drain seam: resolves once the id's in-flight presence publish settled. */
|
|
263
|
+
export function settleSessionPresence(id) {
|
|
264
|
+
return _presenceInFlight.get(id)?.done || Promise.resolve();
|
|
211
265
|
}
|
|
212
266
|
|
|
213
267
|
export function deleteSessionPresence(id) {
|
|
214
268
|
if (!id) return;
|
|
269
|
+
const flight = _presenceInFlight.get(id);
|
|
270
|
+
if (flight) flight.cancelled = true;
|
|
215
271
|
try {
|
|
216
272
|
unlinkSync(_presencePath(id));
|
|
217
273
|
} catch {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MessageChannel, Worker } from 'node:worker_threads';
|
|
2
|
-
import { guardedSaveOptions as _guardedSaveOptions } from './write-guards.mjs';
|
|
2
|
+
import { guardedSaveOptions as _guardedSaveOptions, releaseSessionWriteControl } from './write-guards.mjs';
|
|
3
3
|
import { _ensureLifecycleFields } from './serialize.mjs';
|
|
4
4
|
import {
|
|
5
5
|
_attemptEvidence,
|
|
@@ -63,9 +63,12 @@ export { _hasSessionSaveBaseline } from './save-delta.mjs';
|
|
|
63
63
|
* the writes, so a write already posted for `id` still lands first; the next
|
|
64
64
|
* save for `id`, if any, simply goes out as a full snapshot.
|
|
65
65
|
*/
|
|
66
|
-
export function forgetSessionSaveBaseline(id) {
|
|
66
|
+
export function forgetSessionSaveBaseline(id, { releaseWriteControl = false } = {}) {
|
|
67
67
|
if (!id) return false;
|
|
68
68
|
const had = _dropDeltaBaseline(id);
|
|
69
|
+
// Only when the session itself is gone (close/teardown/delete): a mid-life
|
|
70
|
+
// forget (compaction) must keep the landed-epoch fence for in-flight writes.
|
|
71
|
+
if (releaseWriteControl) releaseSessionWriteControl(String(id));
|
|
69
72
|
if (_saveWorker) {
|
|
70
73
|
try {
|
|
71
74
|
_saveWorker.postMessage({ __forgetBase: String(id) });
|
|
@@ -854,7 +857,7 @@ export function purgeSessionSaveBookkeeping(id) {
|
|
|
854
857
|
}
|
|
855
858
|
}
|
|
856
859
|
// Also frees the worker-side base: a deleted id never saves again.
|
|
857
|
-
forgetSessionSaveBaseline(id);
|
|
860
|
+
forgetSessionSaveBaseline(id, { releaseWriteControl: true });
|
|
858
861
|
return purged;
|
|
859
862
|
}
|
|
860
863
|
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* edge plus the full local-state purge that must happen under the same lock
|
|
5
5
|
* (incarnation retirement, pending slots, purge hooks, markers, summary row).
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { join } from 'node:path';
|
|
7
|
+
import { unlinkSync } from 'node:fs';
|
|
9
8
|
import { isAgentOwner } from '../../agent-owner.mjs';
|
|
10
|
-
import {
|
|
9
|
+
import { listStoredAgentWorkerLinks } from '../store-agent-worker-rows.mjs';
|
|
10
|
+
import { listStoredSessionSummaries } from './listing.mjs';
|
|
11
|
+
import { STORED_SESSION_UNREADABLE, _storedSessionFromFile } from './serialize.mjs';
|
|
11
12
|
import { getStoreDir, sessionPath, deleteSessionPresence } from './paths-heartbeat.mjs';
|
|
12
13
|
import {
|
|
13
14
|
readCanonicalSessionRecord as _readCanonicalRecord,
|
|
@@ -22,49 +23,58 @@ import {
|
|
|
22
23
|
} from './write-guards.mjs';
|
|
23
24
|
import {
|
|
24
25
|
_clearLiveSession,
|
|
26
|
+
_liveSessions,
|
|
25
27
|
_retireSessionIncarnation,
|
|
26
28
|
_clearSessionSaveState,
|
|
27
29
|
clearSessionLifecycleCommitError,
|
|
30
|
+
fenceSessionWrites,
|
|
28
31
|
} from './live-state.mjs';
|
|
29
32
|
import { purgeSessionSaveBookkeeping as _purgeSessionSaveBookkeeping } from './save-worker.mjs';
|
|
30
|
-
import { _uncacheSessionSummary, _queueSessionSummaryRemoval } from './summary-cache.mjs';
|
|
33
|
+
import { _uncacheSessionSummary, _queueSessionSummaryRemoval, _cachedSummaryRows } from './summary-cache.mjs';
|
|
31
34
|
import { _savePending, _clearDebounce, _releasePendingSlot } from './pending-saves.mjs';
|
|
32
35
|
import { _runtimeLivenessVeto, _heartbeatLivenessVeto, _deleteHeartbeatUnlessNewer } from './liveness-veto.mjs';
|
|
33
36
|
import { _runSessionPurgeHooks } from './session-observers.mjs';
|
|
34
37
|
import { deleteCompactionArchive } from '../compaction-archive.mjs';
|
|
35
38
|
|
|
36
|
-
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
+
/** Child-agent session ids linked to one visible parent. Candidates come from
|
|
40
|
+
* metadata only (the summary rows and the agent-worker links — no folder
|
|
41
|
+
* scan); each candidate's own file is then read strictly and must still be an
|
|
42
|
+
* agent-owned record linked to this parent, so a stale/foreign metadata row
|
|
43
|
+
* can never name a deletion target. Used only by explicit parent deletion;
|
|
44
|
+
* ordinary close/context switches keep the relationship intact. */
|
|
39
45
|
export function listOwnedAgentSessionIds(ownerSessionId) {
|
|
40
46
|
const ownerId = String(ownerSessionId || '').trim();
|
|
41
47
|
if (!/^[A-Za-z0-9_-]+$/.test(ownerId)) return [];
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const linksTo = (row) => row?.ownerSessionId === ownerId || row?.parentSessionId === ownerId;
|
|
49
|
+
const candidates = new Set();
|
|
50
|
+
for (const row of [...listStoredSessionSummaries(), ...listStoredAgentWorkerLinks()]) {
|
|
51
|
+
const candidateId = String(row?.id || row?.sessionId || '').trim();
|
|
52
|
+
if (linksTo(row) && candidateId !== ownerId && /^[A-Za-z0-9_-]+$/.test(candidateId)) {
|
|
53
|
+
candidates.add(candidateId);
|
|
54
|
+
}
|
|
49
55
|
}
|
|
56
|
+
const dir = getStoreDir();
|
|
50
57
|
const ids = [];
|
|
51
|
-
for (const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const session = record.doc;
|
|
58
|
-
if (!isAgentOwner(session)) continue;
|
|
59
|
-
const linkedOwner = String(session.ownerSessionId || session.parentSessionId || '').trim();
|
|
60
|
-
if (linkedOwner === ownerId) ids.push(candidateId);
|
|
61
|
-
} catch {
|
|
62
|
-
/* unreadable/vanished records are never deletion targets */
|
|
63
|
-
}
|
|
58
|
+
for (const candidateId of candidates) {
|
|
59
|
+
const session = _storedSessionFromFile(dir, `${candidateId}.json`, false);
|
|
60
|
+
// Unreadable, vanished, corrupt or foreign records are never deletion targets.
|
|
61
|
+
if (!session || session === STORED_SESSION_UNREADABLE || !isAgentOwner(session)) continue;
|
|
62
|
+
const linkedOwner = String(session.ownerSessionId || session.parentSessionId || '').trim();
|
|
63
|
+
if (linkedOwner === ownerId) ids.push(candidateId);
|
|
64
64
|
}
|
|
65
65
|
return ids;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/** Working folders the session is known to have used (its cached summary row
|
|
69
|
+
* and live snapshot — no file read, no scan); the purge hooks derive
|
|
70
|
+
* per-folder artifacts such as the transcript path from them. */
|
|
71
|
+
function knownSessionCwds(id) {
|
|
72
|
+
const row = _cachedSummaryRows()?.find((candidate) => candidate.id === id);
|
|
73
|
+
const live = _liveSessions.get(id);
|
|
74
|
+
const cwds = [row?.cwd, row?.workspace?.cwd, row?.workspace?.projectPath, live?.cwd];
|
|
75
|
+
return [...new Set(cwds.filter((cwd) => typeof cwd === 'string' && cwd))];
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
export function deleteSession(id, options = {}) {
|
|
69
79
|
// Keep caller probes and all vetoes ahead of the non-reentrant lock and
|
|
70
80
|
// ahead of pending-save disruption, matching markSessionClosed().
|
|
@@ -122,12 +132,15 @@ export function deleteSession(id, options = {}) {
|
|
|
122
132
|
// its settlement moves no marker and touches no bookkeeping — and a
|
|
123
133
|
// re-created id gets a brand new incarnation of its own.
|
|
124
134
|
_retireSessionIncarnation(id);
|
|
135
|
+
// An explicit user delete also fences the id: a still-running turn's later
|
|
136
|
+
// saves get fresh guards/incarnations and would otherwise recreate the file.
|
|
137
|
+
if (options.fenceWrites === true) fenceSessionWrites(id);
|
|
125
138
|
_cancelSessionWrites(id);
|
|
126
139
|
_clearDebounce(id);
|
|
127
140
|
_releasePendingSlot(_savePending.get(id));
|
|
128
141
|
_savePending.delete(id);
|
|
129
142
|
_purgeSessionSaveBookkeeping(id);
|
|
130
|
-
_runSessionPurgeHooks(id);
|
|
143
|
+
_runSessionPurgeHooks(id, { cwds: knownSessionCwds(id) });
|
|
131
144
|
// Preserve a sidecar published strictly after the sweep's scan snapshot.
|
|
132
145
|
_deleteHeartbeatUnlessNewer(id, options);
|
|
133
146
|
deleteSessionPresence(id);
|
|
@@ -44,10 +44,12 @@ export function _publishLiveSession(session) {
|
|
|
44
44
|
|
|
45
45
|
_setLiveSessionPublisher(_publishLiveSession);
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
/** `context` (optional, e.g. `{ cwds }`: working folders the session is known
|
|
48
|
+
* to have used) is passed to every hook as its second argument. */
|
|
49
|
+
export function _runSessionPurgeHooks(id, context) {
|
|
48
50
|
for (const hook of _sessionPurgeHooks) {
|
|
49
51
|
try {
|
|
50
|
-
hook(id);
|
|
52
|
+
hook(id, context);
|
|
51
53
|
} catch {
|
|
52
54
|
/* a cleanup hook never breaks delete */
|
|
53
55
|
}
|
|
@@ -7,6 +7,11 @@ import { probePath, PROBE_PRESENT, PROBE_ABSENT } from '../fs-probe.mjs';
|
|
|
7
7
|
import { _queueSummaryIndexPrune } from '../summary-cache.mjs';
|
|
8
8
|
import { deleteSession } from '../session-delete.mjs';
|
|
9
9
|
import { COMPACTION_ARCHIVE_SUFFIX } from '../../compaction-archive.mjs';
|
|
10
|
+
import { isSidecarOwnerDead } from '../paths-heartbeat.mjs';
|
|
11
|
+
|
|
12
|
+
// A live presence owner rewrites `.own` every ~20s; one untouched for the
|
|
13
|
+
// attach guard's whole freshness window holds nothing open.
|
|
14
|
+
const PRESENCE_ORPHAN_AGE_MS = 2 * 60 * 1000;
|
|
10
15
|
|
|
11
16
|
/** Retention cap: prune resumable open (non-tombstone) sessions newest-first
|
|
12
17
|
* — keep the most recent openMaxCount, prune anything older than openMaxAgeMs
|
|
@@ -68,7 +73,13 @@ export function* reapOrphanSidecars({ plan, dir, now, tally }) {
|
|
|
68
73
|
if (probePath(join(dir, recordOf(h))).state !== PROBE_ABSENT) continue;
|
|
69
74
|
const sidecarProbe = probePath(join(dir, h));
|
|
70
75
|
if (sidecarProbe.state !== PROBE_PRESENT) continue;
|
|
71
|
-
|
|
76
|
+
// A presence sidecar is only ever published while its record exists,
|
|
77
|
+
// so one without a record whose recorded owner is dead (or which no
|
|
78
|
+
// live owner refreshed within the presence window) is a leftover of a
|
|
79
|
+
// deleted session, not a session mid-create.
|
|
80
|
+
const orphanPresence =
|
|
81
|
+
h.endsWith('.own') && (now - sidecarProbe.mtimeMs > PRESENCE_ORPHAN_AGE_MS || isSidecarOwnerDead(join(dir, h)));
|
|
82
|
+
if (orphanPresence || now - sidecarProbe.mtimeMs > plan.maxAge) {
|
|
72
83
|
try {
|
|
73
84
|
unlinkSync(join(dir, h));
|
|
74
85
|
tally.cleaned++;
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from './canonical-reader.mjs';
|
|
15
15
|
import { isCancelledWrite as _isCancelledWrite } from './write-guards.mjs';
|
|
16
16
|
import { _setSessionWriteAuthorityCheck } from './save-worker.mjs';
|
|
17
|
+
import { isSessionWriteFenced } from './live-state.mjs';
|
|
17
18
|
|
|
18
19
|
const _observeStamp = (target) => {
|
|
19
20
|
try {
|
|
@@ -71,7 +72,7 @@ function _writeAuthorityRecord(target, attempt) {
|
|
|
71
72
|
* under their own authority read).
|
|
72
73
|
*/
|
|
73
74
|
export function _shouldDrop(id, opts, attempt = null) {
|
|
74
|
-
if (_isCancelledWrite(opts)) return true;
|
|
75
|
+
if (_isCancelledWrite(opts) || isSessionWriteFenced(id)) return true;
|
|
75
76
|
const expected = typeof opts?.expectedGeneration === 'number' ? opts.expectedGeneration : null;
|
|
76
77
|
const target = sessionPath(id);
|
|
77
78
|
let record;
|
|
@@ -126,6 +127,7 @@ export function _shouldDrop(id, opts, attempt = null) {
|
|
|
126
127
|
// under the lock by _shouldDrop.
|
|
127
128
|
export function _sessionWriteAuthorityRefusal(id, attempt = null) {
|
|
128
129
|
if (!id) return null;
|
|
130
|
+
if (isSessionWriteFenced(id)) return 'deleted';
|
|
129
131
|
let authority;
|
|
130
132
|
try {
|
|
131
133
|
// A synchronous save passes its `attempt`: its pre-admission is then the
|
|
@@ -49,6 +49,22 @@ export function guardedSaveOptions(id, opts, epoch = null) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Drop the id's control buffer once the session is gone (close/forget/delete)
|
|
54
|
+
* so the map does not grow by one SharedArrayBuffer per session ever saved.
|
|
55
|
+
* Kept while a commit holds the lock: that write still serialises on this
|
|
56
|
+
* buffer. A later save for the same id simply allocates a fresh control.
|
|
57
|
+
*/
|
|
58
|
+
export function releaseSessionWriteControl(id) {
|
|
59
|
+
const control = _writeControls.get(id);
|
|
60
|
+
if (!control || Atomics.load(control, 1) !== 0) return false;
|
|
61
|
+
return _writeControls.delete(id);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function _hasSessionWriteControl(id) {
|
|
65
|
+
return _writeControls.has(id);
|
|
66
|
+
}
|
|
67
|
+
|
|
52
68
|
export function cancelSessionWrites(id) {
|
|
53
69
|
Atomics.add(writeControl(id), 0, 1);
|
|
54
70
|
}
|
|
@@ -106,13 +106,25 @@ function agentStatusValues(row) {
|
|
|
106
106
|
.filter((status, index, values) => Boolean(status) && values.indexOf(status) === index);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/** The worker's lifecycle. `status` decides; `stage` is only the runtime phase
|
|
110
|
+
* copied in by the progress sweep, so mid-work it may read idle/done/error
|
|
111
|
+
* beside a worker that is still running. Only a row with no status falls
|
|
112
|
+
* back to its stage. */
|
|
113
|
+
function workerLifecycle(row) {
|
|
114
|
+
return cleanValue(row?.status) || cleanValue(row?.stage);
|
|
115
|
+
}
|
|
116
|
+
|
|
109
117
|
function activeAgentWorker(row) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
118
|
+
// A cancel in either field ends the work (bare `cancelling` is still work).
|
|
119
|
+
if (
|
|
120
|
+
agentStatusValues(row).some(
|
|
121
|
+
(status) => CANCELLED_AGENT_STATUS.test(status) || CANCEL_UNCONFIRMED_AGENT_STATUS.test(status)
|
|
122
|
+
)
|
|
123
|
+
) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const lifecycle = workerLifecycle(row);
|
|
127
|
+
return Boolean(lifecycle) && !DEAD_AGENT_STATUS.test(lifecycle) && LIVING_AGENT_STATUS.test(lifecycle);
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
function stampMs(value) {
|
|
@@ -287,7 +299,7 @@ const withinPoolWindow = (at, now) => at > 0 && now - at <= AGENT_POOL_HEARTBEAT
|
|
|
287
299
|
* a working status whose own stamp is still inside the pool window. */
|
|
288
300
|
function poolRowWorking(row, heartbeatMtimes, now) {
|
|
289
301
|
if (withinPoolWindow(heartbeatMtimes.get(cleanValue(row?.sessionId)) || 0, now)) return true;
|
|
290
|
-
if (!WORKING_AGENT_STATUS.test(
|
|
302
|
+
if (!WORKING_AGENT_STATUS.test(workerLifecycle(row))) return false;
|
|
291
303
|
return withinPoolWindow(Date.parse(cleanValue(row?.updatedAt)) || 0, now);
|
|
292
304
|
}
|
|
293
305
|
|
|
@@ -307,7 +319,7 @@ function projectChildWorkerRow(row, { now, heartbeatMtimes }) {
|
|
|
307
319
|
const session = readWorkerSessionHeader(sessionId);
|
|
308
320
|
const declaredStatus = cleanValue(row.status) || 'running';
|
|
309
321
|
const declaredStage = cleanValue(row.stage || row.status) || 'running';
|
|
310
|
-
const declaredWorking = WORKING_AGENT_STATUS.test(
|
|
322
|
+
const declaredWorking = WORKING_AGENT_STATUS.test(workerLifecycle(row) || 'running');
|
|
311
323
|
const heartbeatFresh = withinPoolWindow(heartbeatMtimes.get(sessionId) || 0, now);
|
|
312
324
|
const recentlyUpdated = withinPoolWindow(Date.parse(cleanValue(row.updatedAt)) || 0, now);
|
|
313
325
|
const runtimePid = positiveNumber(row.runtimePid, 0);
|
|
@@ -414,7 +426,7 @@ function promoteHeartbeatSidecar(bySessionId, sessionId, heartbeatAt) {
|
|
|
414
426
|
bySessionId.set(sessionId, sidecarWorkerRow(current, session, { ...identity, status: cancel.status }));
|
|
415
427
|
return;
|
|
416
428
|
}
|
|
417
|
-
const currentStatus =
|
|
429
|
+
const currentStatus = workerLifecycle(current);
|
|
418
430
|
if (currentStatus && !WORKING_AGENT_STATUS.test(currentStatus)) return;
|
|
419
431
|
// The sidecar is the live lease. Durable child sessions are intentionally
|
|
420
432
|
// detached/closed while their external owner runs.
|
|
@@ -452,7 +464,7 @@ function projectLeadWorkerRow(row, { now, heartbeatMtimes }) {
|
|
|
452
464
|
const heartbeatAt = heartbeatMtimes.get(sessionId) || 0;
|
|
453
465
|
const heartbeatFresh = withinPoolWindow(heartbeatAt, now);
|
|
454
466
|
const recentlyUpdated = withinPoolWindow(Date.parse(cleanValue(row.updatedAt)) || 0, now);
|
|
455
|
-
const declaredStatus =
|
|
467
|
+
const declaredStatus = workerLifecycle(row) || 'idle';
|
|
456
468
|
const working = WORKING_AGENT_STATUS.test(declaredStatus) && (heartbeatFresh || recentlyUpdated);
|
|
457
469
|
const reapAt = Date.parse(cleanValue(row.reapAt)) || 0;
|
|
458
470
|
if (!heartbeatFresh && reapAt > 0 && now >= reapAt) return null;
|
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
* `label`, `agent`, `provider`, `model`, `effort`, `fast`, `finished`) and the
|
|
8
8
|
* "\n\nResult:\n" marker are the wire format the notification writer emits;
|
|
9
9
|
* they are matched verbatim.
|
|
10
|
+
*
|
|
11
|
+
* Only the parent(s) named by metadata are read — never the sessions
|
|
12
|
+
* directory: every vanished id reaches this path (deleted Lead records too),
|
|
13
|
+
* and a synchronous scan of every session file froze the daemon loop.
|
|
10
14
|
*/
|
|
11
|
-
import {
|
|
15
|
+
import { readFile } from 'node:fs/promises';
|
|
12
16
|
import { join } from 'node:path';
|
|
13
|
-
import { probePath, PROBE_PRESENT } from './store/fs-probe.mjs';
|
|
14
17
|
import { readTopLevelLifecycleRecord, isLifecycleUnreadable } from './lifecycle-scan.mjs';
|
|
15
18
|
import { dataDir } from './store-summary-locations.mjs';
|
|
16
|
-
import {
|
|
19
|
+
import { listStoredAgentWorkerLinks } from './store-agent-worker-rows.mjs';
|
|
20
|
+
import { cleanValue, desktopSession, isStoredSessionId, positiveNumber } from './store-summary-fields.mjs';
|
|
17
21
|
|
|
18
22
|
function archivedAgentNotification(content, sessionId) {
|
|
19
23
|
const text = typeof content === 'string' ? content : '';
|
|
@@ -52,23 +56,38 @@ function archivedAgentNotification(content, sessionId) {
|
|
|
52
56
|
};
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
/** Parent session ids metadata names for `sessionId`: its agent-workers.json
|
|
60
|
+
* link and its summary-catalog row (`catalogRow(id)`, optional). */
|
|
61
|
+
function parentSessionIds(sessionId, catalogRow) {
|
|
62
|
+
const parents = new Set();
|
|
63
|
+
for (const link of listStoredAgentWorkerLinks()) {
|
|
64
|
+
if (link.sessionId !== sessionId) continue;
|
|
65
|
+
parents.add(link.parentSessionId);
|
|
66
|
+
parents.add(link.ownerSessionId);
|
|
67
|
+
}
|
|
68
|
+
const row = typeof catalogRow === 'function' ? catalogRow(sessionId) : null;
|
|
69
|
+
if (row) {
|
|
70
|
+
parents.add(cleanValue(row.parentSessionId));
|
|
71
|
+
parents.add(cleanValue(row.ownerSessionId));
|
|
72
|
+
}
|
|
73
|
+
parents.delete(sessionId);
|
|
74
|
+
return [...parents].filter(isStoredSessionId);
|
|
75
|
+
}
|
|
76
|
+
|
|
55
77
|
/** Legacy recovery for child transcripts already unlinked by terminal reaping.
|
|
56
|
-
* The parent owns one canonical body-carrying completion notification
|
|
57
|
-
* only
|
|
58
|
-
|
|
78
|
+
* The parent owns one canonical body-carrying completion notification: read
|
|
79
|
+
* only the parent(s) metadata links the child to, asynchronously, and project
|
|
80
|
+
* the newest valid body. An id with no parent link (a deleted Lead record)
|
|
81
|
+
* resolves to null without touching any session file. */
|
|
82
|
+
export async function readArchivedAgentResult(sessionId, { catalogRow } = {}) {
|
|
83
|
+
const parents = parentSessionIds(sessionId, catalogRow);
|
|
84
|
+
if (parents.length === 0) return null;
|
|
59
85
|
const dir = join(dataDir(), 'sessions');
|
|
60
|
-
if (probePath(dir).state !== PROBE_PRESENT) return null;
|
|
61
|
-
let files;
|
|
62
|
-
try {
|
|
63
|
-
files = readdirSync(dir).filter((file) => file.endsWith('.json'));
|
|
64
|
-
} catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
86
|
let best = null;
|
|
68
|
-
for (const
|
|
87
|
+
for (const parentId of parents) {
|
|
69
88
|
let raw;
|
|
70
89
|
try {
|
|
71
|
-
raw =
|
|
90
|
+
raw = await readFile(join(dir, `${parentId}.json`), 'utf8');
|
|
72
91
|
} catch {
|
|
73
92
|
continue;
|
|
74
93
|
}
|
|
@@ -21,10 +21,13 @@ import { dataDir } from './store-summary-locations.mjs';
|
|
|
21
21
|
import {
|
|
22
22
|
desktopSession,
|
|
23
23
|
importKey,
|
|
24
|
+
inheritedFromSessionId,
|
|
24
25
|
isStoredSessionId,
|
|
25
26
|
positiveNumber,
|
|
27
|
+
realTurnCount,
|
|
26
28
|
workspaceSummary,
|
|
27
29
|
} from './store-summary-fields.mjs';
|
|
30
|
+
import { isRealTurnMessage } from './compact/user-message-kind.mjs';
|
|
28
31
|
import { byActivityThenId } from './catalog-db/schema.mjs';
|
|
29
32
|
import {
|
|
30
33
|
sessionStampOf,
|
|
@@ -181,6 +184,7 @@ export function normalizedRow(row, heartbeatAt = 0) {
|
|
|
181
184
|
permission: row.permission || null,
|
|
182
185
|
toolPermission: row.toolPermission || null,
|
|
183
186
|
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
187
|
+
realTurnCount: realTurnCount(row),
|
|
184
188
|
title: cleanText(row.title, 100),
|
|
185
189
|
preview: cleanText(row.preview),
|
|
186
190
|
generation: typeof row.generation === 'number' ? row.generation : 0,
|
|
@@ -188,6 +192,7 @@ export function normalizedRow(row, heartbeatAt = 0) {
|
|
|
188
192
|
storageSize: positiveNumber(row.storageSize, 0),
|
|
189
193
|
detachedReason: row.detachedReason || null,
|
|
190
194
|
importKey: importKey(row),
|
|
195
|
+
inheritedFromSessionId: inheritedFromSessionId(row),
|
|
191
196
|
};
|
|
192
197
|
}
|
|
193
198
|
|
|
@@ -238,6 +243,7 @@ function rowFromSession(session, heartbeatAt = 0) {
|
|
|
238
243
|
{
|
|
239
244
|
...session,
|
|
240
245
|
messageCount: messages.filter((message) => message?.role === 'user' || message?.role === 'assistant').length,
|
|
246
|
+
realTurnCount: messages.filter(isRealTurnMessage).length,
|
|
241
247
|
preview,
|
|
242
248
|
},
|
|
243
249
|
heartbeatAt
|
|
@@ -50,6 +50,19 @@ export function workspaceSummary(value) {
|
|
|
50
50
|
|
|
51
51
|
/** Dedupe key of an imported conversation: a session's `importOrigin.key`,
|
|
52
52
|
* or a summary row's projected `importKey`; null for ordinary sessions. */
|
|
53
|
+
export function inheritedFromSessionId(value) {
|
|
54
|
+
const id = typeof value?.inheritedFromSessionId === 'string' ? value.inheritedFromSessionId.trim() : '';
|
|
55
|
+
return id || null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Count of real human turns a session persisted (actual user instructions
|
|
59
|
+
* plus compact summaries, which stand for earlier human turns); null for a
|
|
60
|
+
* row written before the field existed. */
|
|
61
|
+
export function realTurnCount(value) {
|
|
62
|
+
const count = value?.realTurnCount;
|
|
63
|
+
return typeof count === 'number' && Number.isFinite(count) && count >= 0 ? Math.floor(count) : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
export function importKey(value) {
|
|
54
67
|
const key = typeof value?.importKey === 'string' ? value.importKey : value?.importOrigin?.key;
|
|
55
68
|
return typeof key === 'string' && key.trim() ? key : null;
|
|
@@ -20,10 +20,13 @@ import { cleanSessionPreview, isSessionPreviewNoise, sessionMessageText } from '
|
|
|
20
20
|
import { sessionVisibility } from './store-summary-visibility.mjs';
|
|
21
21
|
import {
|
|
22
22
|
importKey,
|
|
23
|
+
inheritedFromSessionId,
|
|
23
24
|
isStoredSessionId,
|
|
24
25
|
positiveNumber as _positiveNumber,
|
|
26
|
+
realTurnCount,
|
|
25
27
|
workspaceSummary,
|
|
26
28
|
} from './store-summary-fields.mjs';
|
|
29
|
+
import { isRealTurnMessage } from './compact/user-message-kind.mjs';
|
|
27
30
|
|
|
28
31
|
export const SESSION_SUMMARY_INDEX_VERSION = 2;
|
|
29
32
|
|
|
@@ -91,6 +94,7 @@ function _sessionMessageProjection(session) {
|
|
|
91
94
|
const cached = session && typeof session === 'object' ? sessionMessageProjectionMemo.get(session) : null;
|
|
92
95
|
let start = 0;
|
|
93
96
|
let count = 0;
|
|
97
|
+
let real = 0;
|
|
94
98
|
let preview = '';
|
|
95
99
|
let previewMessage = null;
|
|
96
100
|
const canAppend =
|
|
@@ -100,6 +104,7 @@ function _sessionMessageProjection(session) {
|
|
|
100
104
|
if (canAppend) {
|
|
101
105
|
start = cached.length;
|
|
102
106
|
count = cached.count;
|
|
107
|
+
real = cached.real;
|
|
103
108
|
preview = cached.preview;
|
|
104
109
|
previewMessage = cached.previewMessage;
|
|
105
110
|
// The first real user message is stable in normal append-only session
|
|
@@ -111,6 +116,7 @@ function _sessionMessageProjection(session) {
|
|
|
111
116
|
else {
|
|
112
117
|
start = 0;
|
|
113
118
|
count = 0;
|
|
119
|
+
real = 0;
|
|
114
120
|
preview = '';
|
|
115
121
|
previewMessage = null;
|
|
116
122
|
}
|
|
@@ -119,6 +125,7 @@ function _sessionMessageProjection(session) {
|
|
|
119
125
|
for (let index = start; index < messages.length; index += 1) {
|
|
120
126
|
const message = messages[index];
|
|
121
127
|
if (message && (message.role === 'user' || message.role === 'assistant')) count += 1;
|
|
128
|
+
if (isRealTurnMessage(message)) real += 1;
|
|
122
129
|
if (!preview) {
|
|
123
130
|
const candidate = _previewFromMessage(message);
|
|
124
131
|
if (candidate) {
|
|
@@ -132,11 +139,12 @@ function _sessionMessageProjection(session) {
|
|
|
132
139
|
length: messages.length,
|
|
133
140
|
lastMessage: messages[messages.length - 1] || null,
|
|
134
141
|
count,
|
|
142
|
+
real,
|
|
135
143
|
preview,
|
|
136
144
|
previewMessage,
|
|
137
145
|
});
|
|
138
146
|
}
|
|
139
|
-
return { count, preview };
|
|
147
|
+
return { count, real, preview };
|
|
140
148
|
}
|
|
141
149
|
|
|
142
150
|
function _desktopSessionSummary(value, cwd = null) {
|
|
@@ -198,6 +206,7 @@ export function _sessionSummary(session) {
|
|
|
198
206
|
: null,
|
|
199
207
|
taskType: session.taskType || null,
|
|
200
208
|
messageCount: messageProjection.count,
|
|
209
|
+
realTurnCount: messageProjection.real,
|
|
201
210
|
title: _cleanPreview(session.title || '', 100),
|
|
202
211
|
preview: messageProjection.preview,
|
|
203
212
|
generation: typeof session.generation === 'number' ? session.generation : 0,
|
|
@@ -209,6 +218,7 @@ export function _sessionSummary(session) {
|
|
|
209
218
|
// Imported-conversation identity, so "already imported" is answered
|
|
210
219
|
// from summaries without loading sessions.
|
|
211
220
|
importKey: importKey(session),
|
|
221
|
+
inheritedFromSessionId: inheritedFromSessionId(session),
|
|
212
222
|
};
|
|
213
223
|
}
|
|
214
224
|
|
|
@@ -251,6 +261,7 @@ function _normalizeSummaryRow(row) {
|
|
|
251
261
|
: null,
|
|
252
262
|
taskType: row.taskType || null,
|
|
253
263
|
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
264
|
+
realTurnCount: realTurnCount(row),
|
|
254
265
|
title: _cleanPreview(row.title || '', 100),
|
|
255
266
|
preview: _cleanPreview(row.preview || ''),
|
|
256
267
|
generation: typeof row.generation === 'number' ? row.generation : 0,
|
|
@@ -258,6 +269,7 @@ function _normalizeSummaryRow(row) {
|
|
|
258
269
|
storageSize: _positiveNumber(row.storageSize, 0),
|
|
259
270
|
detachedReason: row.detachedReason || null,
|
|
260
271
|
importKey: importKey(row),
|
|
272
|
+
inheritedFromSessionId: inheritedFromSessionId(row),
|
|
261
273
|
};
|
|
262
274
|
}
|
|
263
275
|
|