mixdog 1.3.5 → 1.4.1
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/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +35 -9
- package/src/defaults/skills/setup/SKILL.md +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +10 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +7 -1
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +10 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +142 -190
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +17 -1
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +19 -2
- package/src/tui/session/turn.mjs +1 -0
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -98,6 +98,24 @@ function nextMainRequest(sessionId) {
|
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* The session's last provider request as its next turn would rebuild it from
|
|
103
|
+
* the record (a runtime restart lost the in-memory one): the next request
|
|
104
|
+
* without the reply that last request produced, stamped with the session's
|
|
105
|
+
* last send time. Null when the transcript does not end in that reply or the
|
|
106
|
+
* session has no rebuildable request (nextMainRequest).
|
|
107
|
+
*/
|
|
108
|
+
export function lastSentMainRequest(sessionId) {
|
|
109
|
+
const request = nextMainRequest(sessionId);
|
|
110
|
+
if (!request) return null;
|
|
111
|
+
let end = request.messages.length;
|
|
112
|
+
while (end > 0 && request.messages[end - 1]?.role === 'assistant') end -= 1;
|
|
113
|
+
if (end === 0 || end === request.messages.length) return null;
|
|
114
|
+
const sentAt = Number(request.opts.session?.lastProviderSendAt);
|
|
115
|
+
if (!(sentAt > 0)) return null;
|
|
116
|
+
return { ...request, messages: request.messages.slice(0, end), sentAt };
|
|
117
|
+
}
|
|
118
|
+
|
|
101
119
|
function answerText(response) {
|
|
102
120
|
if (response?.toolCalls?.length) return SIDE_QUESTION_TOOL_ATTEMPT;
|
|
103
121
|
const text = typeof response?.content === 'string' ? response.content.trim() : '';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// A thread can live for days while its loaded skills change underneath it (an
|
|
2
|
+
// app update ships a new SKILL.md, the user edits or switches one off). Compact
|
|
3
|
+
// restores every skill from its current source; between Compacts, each turn
|
|
4
|
+
// tells the model once that a skill it holds instructions for changed, as a
|
|
5
|
+
// new runtime message after the history, never by rewriting what was sent.
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { loadConfig } from '../config.mjs';
|
|
8
|
+
import { collectSkillsCached, currentSkillDelivery } from '../context/collect.mjs';
|
|
9
|
+
import { latestSkillBodies, skillMessageText } from '../context/skill-state.mjs';
|
|
10
|
+
|
|
11
|
+
export const SKILL_UPDATE_SOURCE = 'skill-update-notice';
|
|
12
|
+
|
|
13
|
+
const UNAVAILABLE = 'unavailable';
|
|
14
|
+
// The loaded body is what the Skill tool delivers again (after an
|
|
15
|
+
// unavailable notice): its instructions are back in force.
|
|
16
|
+
const CURRENT = 'current';
|
|
17
|
+
|
|
18
|
+
function skillUpdateNotice(name, version) {
|
|
19
|
+
let text = `[mixdog-runtime] Skill "${name}" was updated since it was loaded. Reload it with the Skill tool before relying on it.`;
|
|
20
|
+
if (version === UNAVAILABLE) {
|
|
21
|
+
text = `[mixdog-runtime] Skill "${name}" is no longer available in this session (switched off, removed, or outside this project's scope). Do not rely on its instructions.`;
|
|
22
|
+
} else if (version === CURRENT) {
|
|
23
|
+
text = `[mixdog-runtime] Skill "${name}" is available again, unchanged; its loaded instructions apply again.`;
|
|
24
|
+
}
|
|
25
|
+
return { role: 'user', content: text, meta: { source: SKILL_UPDATE_SOURCE, synthetic: true, skill: name, version } };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* For every skill whose latest body is in `messages`, compare it with what the
|
|
30
|
+
* Skill tool would deliver at `cwd` now. Its state — the loaded body still
|
|
31
|
+
* current, a changed version, or unavailable — is announced whenever it
|
|
32
|
+
* differs from the last state told after that body (none told: current).
|
|
33
|
+
* So each transition notifies once: changed, switched off, and back again
|
|
34
|
+
* unchanged. A reload replaces the body and starts over. Returns the
|
|
35
|
+
* appended notices.
|
|
36
|
+
*/
|
|
37
|
+
export function appendSkillUpdateNotices(messages, cwd) {
|
|
38
|
+
const bodies = latestSkillBodies(messages);
|
|
39
|
+
if (!bodies.length) return [];
|
|
40
|
+
const config = loadConfig({ secrets: false });
|
|
41
|
+
const skills = collectSkillsCached(cwd);
|
|
42
|
+
const notices = [];
|
|
43
|
+
for (const { name, message } of bodies) {
|
|
44
|
+
const delivery = currentSkillDelivery(name, cwd, config, skills);
|
|
45
|
+
let version = UNAVAILABLE;
|
|
46
|
+
if (delivery) {
|
|
47
|
+
version =
|
|
48
|
+
skillMessageText(message.content).trimStart() === delivery.body
|
|
49
|
+
? CURRENT
|
|
50
|
+
: createHash('sha256').update(delivery.body).digest('hex').slice(0, 16);
|
|
51
|
+
}
|
|
52
|
+
const told =
|
|
53
|
+
messages
|
|
54
|
+
.slice(messages.lastIndexOf(message) + 1)
|
|
55
|
+
.findLast((m) => m?.meta?.source === SKILL_UPDATE_SOURCE && m.meta.skill === name)?.meta.version || CURRENT;
|
|
56
|
+
if (told !== version) notices.push(skillUpdateNotice(name, version));
|
|
57
|
+
}
|
|
58
|
+
messages.push(...notices);
|
|
59
|
+
return notices;
|
|
60
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { contentCarriesLiveMedia } from '../../providers/media-normalization.mjs';
|
|
2
|
+
import { publishSessionPersistence } from './session-persistence-events.mjs';
|
|
2
3
|
|
|
3
4
|
const _lastSaveError = new Map(); // id -> { message, at }
|
|
4
5
|
|
|
@@ -287,9 +288,11 @@ export function _recordSaveDrop(id, epoch = null) {
|
|
|
287
288
|
export function _clearSaveStateIfCurrent(id, epoch = null) {
|
|
288
289
|
if (!id) return false;
|
|
289
290
|
const marked = _failureEpochs.get(id);
|
|
290
|
-
|
|
291
|
-
_clearSessionSaveState(id);
|
|
292
|
-
|
|
291
|
+
const cleared = !(marked !== undefined && Number.isFinite(epoch) && epoch < marked);
|
|
292
|
+
if (cleared) _clearSessionSaveState(id);
|
|
293
|
+
// A landed write put the record on disk whether or not it clears the markers.
|
|
294
|
+
publishSessionPersistence(id, 'landed');
|
|
295
|
+
return cleared;
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
/** True while this process's last save attempt for `id` failed. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hash } from 'node:crypto';
|
|
2
2
|
import { _messagesForDisk, _sessionForDisk } from './serialize.mjs';
|
|
3
|
+
import { sensitiveValuesVersion } from '../sensitive-values.mjs';
|
|
3
4
|
|
|
4
5
|
// ── Delta handoff to the save worker ─────────────────────────────────────────
|
|
5
6
|
// Worker.postMessage structured-clones on the CALLER's thread, so a full-
|
|
@@ -93,7 +94,7 @@ function _attemptRecord(header, refs, prints) {
|
|
|
93
94
|
*/
|
|
94
95
|
export function _attemptEvidence(attempt) {
|
|
95
96
|
if (!attempt) return null;
|
|
96
|
-
const messages = _messagesForDisk(attempt.refs);
|
|
97
|
+
const messages = _messagesForDisk(attempt.refs, attempt.header.id);
|
|
97
98
|
for (let index = 0; index < messages.length; index += 1) {
|
|
98
99
|
const print = attempt.prints[index];
|
|
99
100
|
if (print === null || _messagePrint(messages[index]) !== print) return null;
|
|
@@ -112,14 +113,17 @@ function _isPureAppend(refs, live) {
|
|
|
112
113
|
export function _buildWirePayload(id, session, forceFull = false) {
|
|
113
114
|
const live = Array.isArray(session.messages) ? session.messages : [];
|
|
114
115
|
const base = forceFull ? null : _deltaBaseline.get(id);
|
|
115
|
-
|
|
116
|
+
// A value marked sensitive since the baseline was sent must leave the
|
|
117
|
+
// messages the worker already holds too: write the whole transcript again.
|
|
118
|
+
const sensitive = sensitiveValuesVersion(id);
|
|
119
|
+
const resyncDue = !base || base.deltasSinceFull >= DELTA_FULL_RESYNC_EVERY || base.sensitive !== sensitive;
|
|
116
120
|
if (!resyncDue && _isPureAppend(base.refs, live)) {
|
|
117
121
|
// Header: full disk projection with an empty message list (strips
|
|
118
122
|
// transient aliases); tail: per-message projection of the appended
|
|
119
123
|
// suffix only. Both use the same idempotent per-message projection
|
|
120
124
|
// as a full send, so worker-side reconstruction is byte-identical.
|
|
121
125
|
const header = _sessionForDisk({ ...session, messages: [] });
|
|
122
|
-
const tailMessages = _messagesForDisk(live.slice(base.refs.length));
|
|
126
|
+
const tailMessages = _messagesForDisk(live.slice(base.refs.length), id);
|
|
123
127
|
// The EXACT payload the worker will reconstruct and write is the sent
|
|
124
128
|
// baseline + this delta. Its evidence is fingerprint-checked against
|
|
125
129
|
// what was sent, never a fresh projection of the mutable live session
|
|
@@ -127,7 +131,7 @@ export function _buildWirePayload(id, session, forceFull = false) {
|
|
|
127
131
|
// reference-identity delta). Only the tail is fingerprinted.
|
|
128
132
|
const prints = base.prints ? base.prints.concat(tailMessages.map(_messagePrint)) : null;
|
|
129
133
|
const refs = live.slice();
|
|
130
|
-
_deltaBaseline.set(id, { refs, deltasSinceFull: base.deltasSinceFull + 1, prints });
|
|
134
|
+
_deltaBaseline.set(id, { refs, deltasSinceFull: base.deltasSinceFull + 1, prints, sensitive });
|
|
131
135
|
return {
|
|
132
136
|
delta: { baseCount: base.refs.length, header, tailMessages },
|
|
133
137
|
attempt: _attemptRecord(header, refs, prints),
|
|
@@ -142,7 +146,7 @@ export function _buildWirePayload(id, session, forceFull = false) {
|
|
|
142
146
|
// DELTA_FULL_RESYNC_EVERY saves.
|
|
143
147
|
const refs = live.slice();
|
|
144
148
|
const prints = _printMessages(live, diskMessages, resyncDue ? null : base);
|
|
145
|
-
_deltaBaseline.set(id, { refs, deltasSinceFull: 0, prints });
|
|
149
|
+
_deltaBaseline.set(id, { refs, deltasSinceFull: 0, prints, sensitive });
|
|
146
150
|
// (saveSessionAsync normalizes `messages` to an array before any post.)
|
|
147
151
|
return { session: full, attempt: _attemptRecord(full, refs, prints) };
|
|
148
152
|
}
|
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { renameWithRetrySync } from '../../../../shared/atomic-file.mjs';
|
|
4
4
|
import { sanitizeContentForStoredHistory } from '../../providers/media-normalization.mjs';
|
|
5
5
|
import { readTopLevelLifecycleRecord, isLifecycleUnreadable } from '../lifecycle-scan.mjs';
|
|
6
|
+
import { redactSensitiveMessage } from '../sensitive-projection.mjs';
|
|
6
7
|
|
|
7
8
|
// Inline base64 images (tool screenshots included) are written as
|
|
8
9
|
// content-addressed refs, like prompt media, so session JSON stays small while
|
|
@@ -13,17 +14,17 @@ import { readTopLevelLifecycleRecord, isLifecycleUnreadable } from '../lifecycle
|
|
|
13
14
|
// Per-message disk projection shared by _sessionForDisk and the save-worker
|
|
14
15
|
// delta path (tail-only projection). Returns the SAME array reference when no
|
|
15
16
|
// message changed. Idempotent: already-sanitized content passes through
|
|
16
|
-
// unchanged, so projecting a projected message is a no-op.
|
|
17
|
-
|
|
17
|
+
// unchanged, so projecting a projected message is a no-op. A value session
|
|
18
|
+
// `sessionId` marked sensitive (sensitive-values.mjs) is stored as a marker in
|
|
19
|
+
// the message's text (sensitive-projection.mjs).
|
|
20
|
+
export function _messagesForDisk(messages, sessionId) {
|
|
18
21
|
let changed = false;
|
|
19
22
|
const out = messages.map((m) => {
|
|
20
23
|
if (!m || typeof m !== 'object') return m;
|
|
21
24
|
const content = sanitizeContentForStoredHistory(m.content);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
return m;
|
|
25
|
+
const projected = redactSensitiveMessage(sessionId, content === m.content ? m : { ...m, content });
|
|
26
|
+
if (projected !== m) changed = true;
|
|
27
|
+
return projected;
|
|
27
28
|
});
|
|
28
29
|
return changed ? out : messages;
|
|
29
30
|
}
|
|
@@ -46,7 +47,7 @@ export function _sessionForDisk(session) {
|
|
|
46
47
|
Object.hasOwn(session, 'toolApprovalHook') ||
|
|
47
48
|
Object.hasOwn(session, '_providerPrefixGuardState'));
|
|
48
49
|
const messages = Array.isArray(session?.messages) ? session.messages : null;
|
|
49
|
-
const out = messages?.length ? _messagesForDisk(messages) : messages;
|
|
50
|
+
const out = messages?.length ? _messagesForDisk(messages, session.id) : messages;
|
|
50
51
|
if (out === messages) {
|
|
51
52
|
if (!hasTransient) return session;
|
|
52
53
|
return _withMidTurnContextAnchor(_withoutTransientFields(session), session);
|
|
@@ -36,6 +36,7 @@ import { _savePending, _clearDebounce, _releasePendingSlot } from './pending-sav
|
|
|
36
36
|
import { _runtimeLivenessVeto } from './liveness-veto.mjs';
|
|
37
37
|
import { _runSessionPurgeHooks } from './session-observers.mjs';
|
|
38
38
|
import { deleteCompactionArchive } from '../compaction-archive.mjs';
|
|
39
|
+
import { removeSessionScratchDir } from '../../../../shared/session-scratch.mjs';
|
|
39
40
|
|
|
40
41
|
/** Child-agent session ids linked to one visible parent. Candidates come from
|
|
41
42
|
* metadata only (the summary rows and the agent-worker links — no folder
|
|
@@ -154,6 +155,12 @@ export function deleteSession(id, options = {}) {
|
|
|
154
155
|
_runSessionPurgeHooks(id, { cwds: knownSessionCwds(id) });
|
|
155
156
|
// The display-only compaction archive has no meaning without its session.
|
|
156
157
|
deleteCompactionArchive(getStoreDir(), id);
|
|
158
|
+
// The session's scratch directory (shell TEMP/TMP/TMPDIR) goes with it.
|
|
159
|
+
try {
|
|
160
|
+
removeSessionScratchDir(id);
|
|
161
|
+
} catch {
|
|
162
|
+
/* a locked scratch file never fails the delete */
|
|
163
|
+
}
|
|
157
164
|
_clearLiveSession(id);
|
|
158
165
|
// The file is gone: every marker of the retired incarnation goes with it
|
|
159
166
|
// (save error + failed-snapshot evidence, split-brain drop flag, lifecycle
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only notifications about a session's persistence lifecycle, for owners
|
|
3
|
+
* of derived sidecars (the compaction archive) that must wait for the session
|
|
4
|
+
* record instead of polling for it. A pure registry: it persists nothing and
|
|
5
|
+
* imports no write path.
|
|
6
|
+
*
|
|
7
|
+
* 'landed' — a save of the session reached disk (its record exists);
|
|
8
|
+
* 'closed' — the session was closed (no further save is to be expected).
|
|
9
|
+
*
|
|
10
|
+
* A hard delete is announced through the existing purge hooks
|
|
11
|
+
* (session-observers.mjs registerSessionPurgeHook).
|
|
12
|
+
*/
|
|
13
|
+
const listeners = new Set();
|
|
14
|
+
|
|
15
|
+
export function subscribeSessionPersistence(listener) {
|
|
16
|
+
if (typeof listener !== 'function') return () => {};
|
|
17
|
+
listeners.add(listener);
|
|
18
|
+
return () => listeners.delete(listener);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function publishSessionPersistence(id, event) {
|
|
22
|
+
for (const listener of [...listeners]) {
|
|
23
|
+
try {
|
|
24
|
+
listener(id, event);
|
|
25
|
+
} catch {
|
|
26
|
+
/* observers never affect persistence */
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -161,12 +161,12 @@ function cancelState(source) {
|
|
|
161
161
|
function effectiveCancel(row, session) {
|
|
162
162
|
const rowCancel = cancelState(row);
|
|
163
163
|
const sessionCancel = cancelState(session);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
let cancel;
|
|
165
|
+
if (rowCancel && sessionCancel && CANCEL_UNCONFIRMED_AGENT_STATUS.test(sessionCancel.status)) {
|
|
166
|
+
cancel = CANCEL_UNCONFIRMED_AGENT_STATUS.test(rowCancel.status) ? rowCancel : sessionCancel;
|
|
167
|
+
} else {
|
|
168
|
+
cancel = rowCancel || sessionCancel;
|
|
169
|
+
}
|
|
170
170
|
if (!cancel) return null;
|
|
171
171
|
const newerWork = cancel.at > 0 && Math.max(workStampMs(row), workStampMs(session)) > cancel.at;
|
|
172
172
|
return newerWork ? null : cancel;
|
|
@@ -633,6 +633,8 @@ function projectChildWorkerRow(row, context) {
|
|
|
633
633
|
// A live turn blocked on the user's approval (persisted by the agent
|
|
634
634
|
// tool's worker index); never carried by a row that is no longer working.
|
|
635
635
|
...(working && row.awaitingUser === 'approval' ? { awaitingUser: 'approval' } : {}),
|
|
636
|
+
// The owner closed this agent (status unchanged); reused work clears it.
|
|
637
|
+
...(!working && row.released === true ? { released: true } : {}),
|
|
636
638
|
// Why a settled failed row failed; a row working again has no failure.
|
|
637
639
|
...(!working && failedAgentWorker(row) && cleanValue(row.failureReason)
|
|
638
640
|
? { failureReason: cleanValue(row.failureReason) }
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
inheritedFromSessionId,
|
|
31
31
|
isStoredSessionId,
|
|
32
32
|
positiveNumber,
|
|
33
|
+
compactCountField,
|
|
33
34
|
realTurnCount,
|
|
34
35
|
workspaceSummary,
|
|
35
36
|
} from './store-summary-fields.mjs';
|
|
@@ -131,8 +132,9 @@ export function isLeadVisibleRow(row) {
|
|
|
131
132
|
if (!agentSession && !isRootLeadSession(row) && owner && !LEAD_OWNERS.has(owner)) return false;
|
|
132
133
|
// Mirror listLeadSessions: a previewless zero-message row is an unusable
|
|
133
134
|
// scratch (desktop boot leftovers, crashed first turns) — resuming it
|
|
134
|
-
// shows an empty conversation, so the catalog hides it.
|
|
135
|
-
|
|
135
|
+
// shows an empty conversation, so the catalog hides it. A titled one is a
|
|
136
|
+
// real conversation whose prompts were rewound away: it stays listed.
|
|
137
|
+
if (!row.preview && row.messageCount === 0 && !row.title) return false;
|
|
136
138
|
const sourceType = String(row.sourceType || '')
|
|
137
139
|
.trim()
|
|
138
140
|
.toLowerCase();
|
|
@@ -215,6 +217,9 @@ export function normalizedRow(row) {
|
|
|
215
217
|
// Absent from a row read off a session file: its assistant text is not
|
|
216
218
|
// decoded there, so the unread count falls back until the next save.
|
|
217
219
|
...(Array.isArray(row.incomingTail) ? { incomingTail: normalizedIncomingTail(row.incomingTail) } : {}),
|
|
220
|
+
// Absent from a row read off a session file (its compaction record is not
|
|
221
|
+
// decoded there) until the next save.
|
|
222
|
+
...compactCountField(row),
|
|
218
223
|
realTurnCount: realTurnCount(row),
|
|
219
224
|
title: cleanText(row.title, 100),
|
|
220
225
|
preview: cleanText(row.preview),
|
|
@@ -282,6 +287,15 @@ function rowFromSession(session) {
|
|
|
282
287
|
});
|
|
283
288
|
}
|
|
284
289
|
|
|
290
|
+
/** The catalog row of one session read off `<directory>/<sessionId>.json` by
|
|
291
|
+
* id — the same strict, stamp-cached derivation a listing uses — or null
|
|
292
|
+
* when it is absent, unreadable, foreign or not one the catalog lists. */
|
|
293
|
+
export function storedSessionCatalogRow(directory, sessionId) {
|
|
294
|
+
if (!isStoredSessionId(sessionId)) return null;
|
|
295
|
+
const entry = reconcileSessionFile(directory, sessionId, null, false);
|
|
296
|
+
return entry?.kind === 'row' && isLeadVisibleRow(entry.row) ? entry.row : null;
|
|
297
|
+
}
|
|
298
|
+
|
|
285
299
|
// ── Session file reconcile ──────────────────────────────────────────────────
|
|
286
300
|
// What one `sessions/<id>.json` name contributes to the catalog, with the full
|
|
287
301
|
// stamp { dev, ino, size, mtimeNs, ctimeNs } it was judged at (and the
|
|
@@ -63,6 +63,21 @@ export function realTurnCount(value) {
|
|
|
63
63
|
return typeof count === 'number' && Number.isFinite(count) && count >= 0 ? Math.floor(count) : null;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/** How many compactions a session has had (`compaction.compactCount`): each
|
|
67
|
+
* one trims the live messages and so restarts their numbering (the unread
|
|
68
|
+
* cursors count them). Null for a value without it, e.g. a row written
|
|
69
|
+
* before the field existed. */
|
|
70
|
+
export function compactCount(value) {
|
|
71
|
+
const count = value?.compactCount;
|
|
72
|
+
return typeof count === 'number' && Number.isSafeInteger(count) && count >= 0 ? count : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `{ compactCount }` for a row that carries one, else nothing. */
|
|
76
|
+
export function compactCountField(value) {
|
|
77
|
+
const count = compactCount(value);
|
|
78
|
+
return count === null ? {} : { compactCount: count };
|
|
79
|
+
}
|
|
80
|
+
|
|
66
81
|
export function importKey(value) {
|
|
67
82
|
const key = typeof value?.importKey === 'string' ? value.importKey : value?.importOrigin?.key;
|
|
68
83
|
return typeof key === 'string' && key.trim() ? key : null;
|
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
inheritedFromSessionId,
|
|
25
25
|
isStoredSessionId,
|
|
26
26
|
positiveNumber as _positiveNumber,
|
|
27
|
+
compactCount,
|
|
28
|
+
compactCountField,
|
|
27
29
|
realTurnCount,
|
|
28
30
|
workspaceSummary,
|
|
29
31
|
} from './store-summary-fields.mjs';
|
|
@@ -54,26 +56,50 @@ const _computeCleanPreview = cleanSummaryPreview;
|
|
|
54
56
|
// cleaning runs a chain of regex replaces that each allocate a copy. Those
|
|
55
57
|
// short strings repeat from flush to flush, so the pure result is memoized per
|
|
56
58
|
// (max, text). Long raw message text is not kept here (see _previewFromMessage).
|
|
59
|
+
// Every new message's raw text (up to the text limit) also passes through, so
|
|
60
|
+
// the memo is bounded by the characters it holds, least recently used first
|
|
61
|
+
// out: at 8192 entries alone it kept ~16–32 MB of message text per `max`.
|
|
57
62
|
const CLEAN_PREVIEW_MEMO_TEXT_LIMIT = 2048;
|
|
58
63
|
const CLEAN_PREVIEW_MEMO_ENTRIES = 8192;
|
|
59
|
-
const
|
|
64
|
+
const CLEAN_PREVIEW_MEMO_MAX_CHARS = 512 * 1024;
|
|
65
|
+
const cleanPreviewMemo = new Map(); // max → { byText: Map<text, cleaned>, chars }
|
|
60
66
|
|
|
61
67
|
function _cleanPreview(text, max = 240) {
|
|
62
68
|
if (typeof text !== 'string' || text.length > CLEAN_PREVIEW_MEMO_TEXT_LIMIT) return _computeCleanPreview(text, max);
|
|
63
|
-
let
|
|
64
|
-
if (!
|
|
65
|
-
|
|
66
|
-
cleanPreviewMemo.set(max,
|
|
69
|
+
let memo = cleanPreviewMemo.get(max);
|
|
70
|
+
if (!memo) {
|
|
71
|
+
memo = { byText: new Map(), chars: 0 };
|
|
72
|
+
cleanPreviewMemo.set(max, memo);
|
|
67
73
|
}
|
|
74
|
+
const { byText } = memo;
|
|
68
75
|
let cleaned = byText.get(text);
|
|
69
|
-
if (cleaned
|
|
70
|
-
|
|
71
|
-
if (byText.size >= CLEAN_PREVIEW_MEMO_ENTRIES) byText.clear();
|
|
76
|
+
if (cleaned !== undefined) {
|
|
77
|
+
byText.delete(text);
|
|
72
78
|
byText.set(text, cleaned);
|
|
79
|
+
return cleaned;
|
|
80
|
+
}
|
|
81
|
+
cleaned = _computeCleanPreview(text, max);
|
|
82
|
+
byText.set(text, cleaned);
|
|
83
|
+
memo.chars += text.length + cleaned.length;
|
|
84
|
+
for (const [oldText, oldCleaned] of byText) {
|
|
85
|
+
if (byText.size <= CLEAN_PREVIEW_MEMO_ENTRIES && memo.chars <= CLEAN_PREVIEW_MEMO_MAX_CHARS) break;
|
|
86
|
+
byText.delete(oldText);
|
|
87
|
+
memo.chars -= oldText.length + oldCleaned.length;
|
|
73
88
|
}
|
|
74
89
|
return cleaned;
|
|
75
90
|
}
|
|
76
91
|
|
|
92
|
+
/** Entries and characters the preview memo holds, summed over every `max` (tests). */
|
|
93
|
+
export function _cleanPreviewMemoStatsForTest() {
|
|
94
|
+
let entries = 0;
|
|
95
|
+
let chars = 0;
|
|
96
|
+
for (const memo of cleanPreviewMemo.values()) {
|
|
97
|
+
entries += memo.byText.size;
|
|
98
|
+
chars += memo.chars;
|
|
99
|
+
}
|
|
100
|
+
return { entries, chars };
|
|
101
|
+
}
|
|
102
|
+
|
|
77
103
|
const sessionMessageProjectionMemo = new WeakMap();
|
|
78
104
|
// message → { raw, preview }. A session's first real user message is
|
|
79
105
|
// re-checked on every save; its text is re-derived (cheap) and the preview is
|
|
@@ -265,6 +291,8 @@ export function _sessionSummary(session) {
|
|
|
265
291
|
taskType: session.taskType || null,
|
|
266
292
|
messageCount: messageProjection.count,
|
|
267
293
|
...(messageProjection.incoming ? { incomingTail: messageProjection.incoming.slice() } : {}),
|
|
294
|
+
// The numbering epoch of messageCount: a compaction restarts it.
|
|
295
|
+
compactCount: compactCount(session.compaction) ?? 0,
|
|
268
296
|
realTurnCount: messageProjection.real,
|
|
269
297
|
title: _cleanPreview(session.title || '', 100),
|
|
270
298
|
preview: messageProjection.preview,
|
|
@@ -324,6 +352,7 @@ function _normalizeSummaryRow(row) {
|
|
|
324
352
|
taskType: row.taskType || null,
|
|
325
353
|
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
326
354
|
...(Array.isArray(row.incomingTail) ? { incomingTail: normalizedIncomingTail(row.incomingTail) } : {}),
|
|
355
|
+
...compactCountField(row),
|
|
327
356
|
realTurnCount: realTurnCount(row),
|
|
328
357
|
title: _cleanPreview(row.title || '', 100),
|
|
329
358
|
preview: _cleanPreview(row.preview || ''),
|
|
@@ -391,31 +420,58 @@ const _pendingRemovals = new Set(); // ids to drop (upsert/removal are mutually
|
|
|
391
420
|
let _summaryRetryTimer = null;
|
|
392
421
|
let _summaryFlushScheduled = false;
|
|
393
422
|
let _summaryFlushInflight = 0;
|
|
394
|
-
|
|
423
|
+
// Ids whose ops the async flush in flight took (they are requeued if it fails).
|
|
424
|
+
const _inflightSummaryIds = new Set();
|
|
425
|
+
const _summarySettleWaiters = new Set(); // { ids: string[]|null, resolve }
|
|
395
426
|
|
|
396
427
|
export function _hasUnsettledSummaryOps() {
|
|
397
428
|
return _pendingUpserts.size > 0 || _pendingRemovals.size > 0 || _summaryFlushScheduled || _summaryFlushInflight > 0;
|
|
398
429
|
}
|
|
399
430
|
|
|
431
|
+
/** The ids among `ids` whose summary ops are queued or being written. */
|
|
432
|
+
export function pendingSummaryIndexIds(ids) {
|
|
433
|
+
return ids.filter((id) => _pendingUpserts.has(id) || _pendingRemovals.has(id) || _inflightSummaryIds.has(id));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function _waiterSettled({ ids }) {
|
|
437
|
+
return ids ? pendingSummaryIndexIds(ids).length === 0 : !_hasUnsettledSummaryOps();
|
|
438
|
+
}
|
|
439
|
+
|
|
400
440
|
function _notifySummarySettled() {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
441
|
+
for (const waiter of _summarySettleWaiters) {
|
|
442
|
+
if (_waiterSettled(waiter)) waiter.settle(true);
|
|
443
|
+
}
|
|
404
444
|
}
|
|
405
445
|
|
|
406
446
|
/**
|
|
407
|
-
* Resolves once
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
* done before its deferred index flush runs, so an
|
|
411
|
-
* switches the data dir awaits this first. A flush
|
|
412
|
-
* (an overlapping flush of this process, or another
|
|
413
|
-
* and settles when its retry lands.
|
|
447
|
+
* Resolves true once the queued summary-index mutations have landed — of
|
|
448
|
+
* `ids` only when given, else every one with no flush scheduled or in flight;
|
|
449
|
+
* from then on this process no longer touches the index for them. Callers
|
|
450
|
+
* are told a save/sweep is done before its deferred index flush runs, so an
|
|
451
|
+
* owner that removes or switches the data dir awaits this first. A flush
|
|
452
|
+
* refused by a held lock (an overlapping flush of this process, or another
|
|
453
|
+
* process) stays queued and settles when its retry lands. When `signal`
|
|
454
|
+
* aborts first it resolves false and stops waiting; the ops stay queued.
|
|
414
455
|
*/
|
|
415
|
-
export function settleSummaryIndexWrites() {
|
|
416
|
-
|
|
456
|
+
export function settleSummaryIndexWrites({ ids = null, signal = null } = {}) {
|
|
457
|
+
const waiter = { ids, settle: null };
|
|
458
|
+
if (_waiterSettled(waiter)) return Promise.resolve(true);
|
|
459
|
+
if (signal?.aborted) return Promise.resolve(false);
|
|
417
460
|
_summaryRetryTimer?.ref?.();
|
|
418
|
-
return new Promise((resolve) =>
|
|
461
|
+
return new Promise((resolve) => {
|
|
462
|
+
const onAbort = () => waiter.settle(false);
|
|
463
|
+
// The one exit, landed or abandoned: the waiter and its abort listener
|
|
464
|
+
// both go, so a long-lived signal collects nothing per settle.
|
|
465
|
+
waiter.settle = (landed) => {
|
|
466
|
+
if (!_summarySettleWaiters.delete(waiter)) return;
|
|
467
|
+
signal?.removeEventListener('abort', onAbort);
|
|
468
|
+
// Only an awaited settlement keeps the process alive for the retry.
|
|
469
|
+
if (_summarySettleWaiters.size === 0) _summaryRetryTimer?.unref?.();
|
|
470
|
+
resolve(landed);
|
|
471
|
+
};
|
|
472
|
+
_summarySettleWaiters.add(waiter);
|
|
473
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
474
|
+
});
|
|
419
475
|
}
|
|
420
476
|
|
|
421
477
|
function _scheduleSummaryRetry() {
|
|
@@ -498,11 +554,14 @@ export function _flushPendingSummaryOps({ sync = false } = {}) {
|
|
|
498
554
|
return;
|
|
499
555
|
}
|
|
500
556
|
_summaryFlushInflight++;
|
|
557
|
+
const taken = [...upserts.keys(), ...removals];
|
|
558
|
+
for (const id of taken) _inflightSummaryIds.add(id);
|
|
501
559
|
withFileLock(`${path}.lock`, () => _appendSummaryOpsLocked(path, text), { timeoutMs: SUMMARY_LOCK_TIMEOUT_MS })
|
|
502
560
|
.then(() => _sweepStaleSummaryTempOnce(path))
|
|
503
561
|
.catch(requeue)
|
|
504
562
|
.finally(() => {
|
|
505
563
|
_summaryFlushInflight--;
|
|
564
|
+
for (const id of taken) _inflightSummaryIds.delete(id);
|
|
506
565
|
if (_pendingUpserts.size > 0 || _pendingRemovals.size > 0) _scheduleSummaryFlush();
|
|
507
566
|
_notifySummarySettled();
|
|
508
567
|
});
|