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
|
@@ -11,22 +11,29 @@
|
|
|
11
11
|
* appended as ONE segment to the session's sidecar, followed by the
|
|
12
12
|
* compaction divider.
|
|
13
13
|
*
|
|
14
|
-
* The commit
|
|
15
|
-
* number
|
|
16
|
-
*
|
|
17
|
-
* session
|
|
18
|
-
*
|
|
14
|
+
* The commit selects the dropped messages, restores their rows (the one
|
|
15
|
+
* visibility decision: a segment number is claimed only for a non-empty row
|
|
16
|
+
* set) and allocates the segment number
|
|
17
|
+
* (`session.compaction.archive.nextSeq`, saved with the commit); the
|
|
18
|
+
* namespacing and the write run on a later event-loop turn, serialized per
|
|
19
|
+
* session and fenced against a delete. A write the fence refuses is kept
|
|
20
|
+
* (retried in place in the session's queue) while the session may still come
|
|
21
|
+
* back to be written; a session that is really gone releases its number.
|
|
22
|
+
* Nothing here touches `session.messages`.
|
|
19
23
|
*/
|
|
20
24
|
import { randomBytes } from 'node:crypto';
|
|
21
|
-
import { existsSync, unlinkSync } from 'node:fs';
|
|
22
|
-
import { open, readFile } from 'node:fs/promises';
|
|
25
|
+
import { closeSync, existsSync, fsyncSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { open, readdir, readFile } from 'node:fs/promises';
|
|
23
27
|
import { basename, join } from 'node:path';
|
|
24
|
-
import { renameWithRetrySync } from '../../../shared/atomic-file.mjs';
|
|
28
|
+
import { quarantinePath, renameWithRetrySync } from '../../../shared/atomic-file.mjs';
|
|
25
29
|
import { writeStderrBestEffort } from '../../../shared/best-effort-stderr.mjs';
|
|
26
30
|
|
|
27
|
-
import { sanitizeContentForStoredHistory } from '../providers/media-normalization.mjs';
|
|
28
31
|
import { getStoreDir } from './store/session-paths.mjs';
|
|
29
32
|
import { forgetSessionSaveBaseline } from './store/save-worker.mjs';
|
|
33
|
+
import { _hasPendingPersistence } from './store/pending-saves.mjs';
|
|
34
|
+
import { _messagesForDisk } from './store/serialize.mjs';
|
|
35
|
+
import { subscribeSessionPersistence } from './store/session-persistence-events.mjs';
|
|
36
|
+
import { registerSessionPurgeHook } from './store/session-observers.mjs';
|
|
30
37
|
import { messageIdOf, stampMessageIds } from './message-ids.mjs';
|
|
31
38
|
import { restoreTranscriptItems } from './transcript-restore/restore.mjs';
|
|
32
39
|
import { isAgentModeSession } from './agent-mode.mjs';
|
|
@@ -134,7 +141,7 @@ export function compactionArchiveProjection(before, after) {
|
|
|
134
141
|
/** What a live view needs to find the rows of what left the context: the
|
|
135
142
|
* stable ids whose text rows went (projection `textIds`) and the tool calls
|
|
136
143
|
* that went (dropped results and calls no kept copy carries). */
|
|
137
|
-
|
|
144
|
+
function droppedIdentitySummary(projection) {
|
|
138
145
|
const callIds = new Set();
|
|
139
146
|
for (const message of projection.messages) {
|
|
140
147
|
for (const call of toolCallsOf(message)) if (callIdOf(call)) callIds.add(callIdOf(call));
|
|
@@ -155,17 +162,24 @@ export function stampCompactionMessageIds(messages, sessionId) {
|
|
|
155
162
|
}
|
|
156
163
|
// ── Segment projection and publication ───────────────────────────────────────
|
|
157
164
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
/** The rows what left the context (compactionArchiveProjection messages)
|
|
166
|
+
* restores to as the session store would keep them on disk — media stripped
|
|
167
|
+
* and the session's sensitive values redacted, by the store's own projection
|
|
168
|
+
* (`_messagesForDisk`, sensitive-projection.mjs) — the single visibility
|
|
169
|
+
* decision of the archive: a segment exists exactly when this is non-empty.
|
|
170
|
+
* Evaluated again at publication, so the redaction is the one in force then. */
|
|
171
|
+
function segmentRows(messages, sessionId) {
|
|
172
|
+
return restoreTranscriptItems(_messagesForDisk(messages, sessionId), { sessionId });
|
|
161
173
|
}
|
|
162
174
|
|
|
163
|
-
/** The display items of one segment: what left the context
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* visible left. */
|
|
175
|
+
/** The display items of one segment: what left the context as stored history
|
|
176
|
+
* restores it, namespaced and marked archived, then the divider. Empty when
|
|
177
|
+
* nothing visible left. */
|
|
167
178
|
export function compactionSegmentItems(messages, { sessionId, seq, at, divider = true }) {
|
|
168
|
-
|
|
179
|
+
return namespacedSegmentItems(segmentRows(messages, sessionId), { sessionId, seq, at, divider });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function namespacedSegmentItems(restored, { sessionId, seq, at, divider }) {
|
|
169
183
|
if (!restored.length) return [];
|
|
170
184
|
const prefix = `hist_${sessionId}_`;
|
|
171
185
|
const items = restored.map((item) => {
|
|
@@ -212,19 +226,46 @@ export function _setCompactionArchivePublishHook(hook) {
|
|
|
212
226
|
publishHook = typeof hook === 'function' ? hook : null;
|
|
213
227
|
}
|
|
214
228
|
|
|
229
|
+
let parkHook = null;
|
|
230
|
+
/** Test seam: runs when a refused publication parks on the session's
|
|
231
|
+
* persistence signal (its wait is already registered). */
|
|
232
|
+
export function _setCompactionArchiveParkHook(hook) {
|
|
233
|
+
parkHook = typeof hook === 'function' ? hook : null;
|
|
234
|
+
}
|
|
235
|
+
|
|
215
236
|
let syncFile = (handle) => handle.sync();
|
|
216
237
|
/** Test seam: replaces the temp file's fsync (null restores it). */
|
|
217
238
|
export function _setCompactionArchiveFsync(sync) {
|
|
218
239
|
syncFile = typeof sync === 'function' ? sync : (handle) => handle.sync();
|
|
219
240
|
}
|
|
220
241
|
|
|
242
|
+
const realFs = { readFileSync, readdir };
|
|
243
|
+
let fsOps = realFs;
|
|
244
|
+
/** Test seam: replaces the pending-sidecar read (`readFileSync`) and the store
|
|
245
|
+
* listing (`readdir`); null restores them. */
|
|
246
|
+
export function _setCompactionArchiveFs(ops) {
|
|
247
|
+
fsOps = ops ? { ...realFs, ...ops } : realFs;
|
|
248
|
+
}
|
|
249
|
+
|
|
221
250
|
const recordExists = (dir, sessionId) => existsSync(join(dir, `${sessionId}.json`));
|
|
222
251
|
|
|
252
|
+
/** Why a segment cannot be published now, or null. A delete that bumped the
|
|
253
|
+
* epoch since `epoch` was captured is final. A missing record is final too
|
|
254
|
+
* unless a save of that session is still pending — that save is what creates
|
|
255
|
+
* it, so the write is kept (`retry`). */
|
|
256
|
+
function publicationRefusal(dir, sessionId, epoch) {
|
|
257
|
+
if (compactionArchiveDeleteEpoch(sessionId) !== epoch) return { reason: 'session deleted', retry: false };
|
|
258
|
+
if (recordExists(dir, sessionId)) return null;
|
|
259
|
+
return _hasPendingPersistence(sessionId)
|
|
260
|
+
? { reason: 'session record missing while its save is pending', retry: true }
|
|
261
|
+
: { reason: 'session record missing', retry: false };
|
|
262
|
+
}
|
|
263
|
+
|
|
223
264
|
/** Write `text` to a private temp file and fsync it — a failed write or
|
|
224
265
|
* fsync fails the publication — then publish it with a synchronous fence
|
|
225
266
|
* check + rename: a delete that bumped the epoch (or removed the record)
|
|
226
267
|
* since `epoch` was captured wins. Any outcome but a rename removes the temp
|
|
227
|
-
* file. */
|
|
268
|
+
* file. Returns null once published, else the fence's refusal. */
|
|
228
269
|
async function publishArchive({ dir, path, sessionId, text, epoch }) {
|
|
229
270
|
const tmp = join(dir, `${basename(path)}.mixdog-tmp-${randomBytes(4).toString('hex')}`);
|
|
230
271
|
const removeTmp = () => {
|
|
@@ -248,9 +289,10 @@ async function publishArchive({ dir, path, sessionId, text, epoch }) {
|
|
|
248
289
|
throw error;
|
|
249
290
|
}
|
|
250
291
|
// No await between the fence and the rename.
|
|
251
|
-
|
|
292
|
+
const refusal = publicationRefusal(dir, sessionId, epoch);
|
|
293
|
+
if (refusal) {
|
|
252
294
|
removeTmp();
|
|
253
|
-
return
|
|
295
|
+
return refusal;
|
|
254
296
|
}
|
|
255
297
|
try {
|
|
256
298
|
renameWithRetrySync(tmp, path);
|
|
@@ -258,14 +300,17 @@ async function publishArchive({ dir, path, sessionId, text, epoch }) {
|
|
|
258
300
|
removeTmp();
|
|
259
301
|
throw error;
|
|
260
302
|
}
|
|
261
|
-
return
|
|
303
|
+
return null;
|
|
262
304
|
}
|
|
263
305
|
|
|
306
|
+
/** Appends one segment: `{ segments }` once published, else the fence's
|
|
307
|
+
* refusal `{ reason, retry }`. */
|
|
264
308
|
async function appendSegment({ sessionId, seq, at, items, clearedThrough, maxBytes, epoch }) {
|
|
265
309
|
const dir = getStoreDir();
|
|
266
310
|
const path = compactionArchivePath(dir, sessionId);
|
|
267
311
|
// A deleted session must not regrow its archive.
|
|
268
|
-
|
|
312
|
+
const early = publicationRefusal(dir, sessionId, epoch);
|
|
313
|
+
if (early) return early;
|
|
269
314
|
let text = '';
|
|
270
315
|
try {
|
|
271
316
|
text = await readFile(path, 'utf8');
|
|
@@ -286,20 +331,374 @@ async function appendSegment({ sessionId, seq, at, items, clearedThrough, maxByt
|
|
|
286
331
|
total -= Buffer.byteLength(existing.shift().line) + 1;
|
|
287
332
|
}
|
|
288
333
|
const lines = [...existing.map((entry) => entry.line), segment.line];
|
|
289
|
-
|
|
290
|
-
|
|
334
|
+
const refusal = await publishArchive({ dir, path, sessionId, text: `${lines.join('\n')}\n`, epoch });
|
|
335
|
+
if (refusal) return refusal;
|
|
336
|
+
return { segments: lines.map((line) => compactionSegmentHead(line)).filter(Boolean) };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function logArchiveSkip(sessionId, seq, what, reason) {
|
|
340
|
+
writeStderrBestEffort(`[session] compaction archive write ${what} (sess=${sessionId}, seq=${seq}): ${reason}\n`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** The session is really gone: give the claimed number back unless something
|
|
344
|
+
* newer was claimed after it or a clear already covers it. */
|
|
345
|
+
function releaseClaimedSeq(session, seq) {
|
|
346
|
+
const archive = session.compaction?.archive;
|
|
347
|
+
if (!archive || typeof archive !== 'object') return;
|
|
348
|
+
if (archive.nextSeq === seq + 1 && !(Number(archive.clearedThrough) >= seq)) archive.nextSeq = seq;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ── Unpublished segments ─────────────────────────────────────────────────────
|
|
352
|
+
// A segment's job lives in `unpublished[id]` (ascending seq) from the moment
|
|
353
|
+
// its number is claimed until it is published or its session is really gone.
|
|
354
|
+
// A failed write, or one that cannot be published yet, leaves the job there:
|
|
355
|
+
// the next archive write or the next successful save of the session retries it
|
|
356
|
+
// first. The moment a job is parked or has failed it is also written to a
|
|
357
|
+
// pending sidecar `<id>.compaction-archive.pending` (removed once published or
|
|
358
|
+
// when the session is deleted), which survives a crash or kill; the daemon's
|
|
359
|
+
// startup scan (recoverPendingCompactionArchives) and the session's next
|
|
360
|
+
// save/compaction pick it up. The sidecar holds the rows' source messages in their stored
|
|
361
|
+
// (disk-projected) form.
|
|
362
|
+
// A sidecar is only ever rewritten or removed by a process that has loaded it
|
|
363
|
+
// (`recovered`): a read that fails for any reason but absence leaves the
|
|
364
|
+
// session unrecovered, its compaction refused, and the sidecar untouched.
|
|
365
|
+
const COMPACTION_PENDING_SUFFIX = '.compaction-archive.pending';
|
|
366
|
+
const unpublished = new Map();
|
|
367
|
+
const recovered = new Set();
|
|
368
|
+
const sidecarOnDisk = new Set();
|
|
369
|
+
const waiters = new Map();
|
|
370
|
+
|
|
371
|
+
const jobsOf = (id) => {
|
|
372
|
+
let list = unpublished.get(id);
|
|
373
|
+
if (!list) {
|
|
374
|
+
list = [];
|
|
375
|
+
unpublished.set(id, list);
|
|
376
|
+
}
|
|
377
|
+
return list;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const pendingSidecarPath = (dir, id) => join(dir, `${id}${COMPACTION_PENDING_SUFFIX}`);
|
|
381
|
+
|
|
382
|
+
/** Rewrites the pending sidecar to the session's unpublished jobs (removes it
|
|
383
|
+
* when none are left). Synchronous: it also runs at process exit. */
|
|
384
|
+
function persistPending(id) {
|
|
385
|
+
// Jobs of an unrecovered sidecar are unknown here: rewriting or removing it
|
|
386
|
+
// would drop them.
|
|
387
|
+
if (!recovered.has(id)) {
|
|
388
|
+
writeStderrBestEffort(
|
|
389
|
+
`[session] compaction archive pending sidecar not rewritten before it was read (sess=${id})\n`
|
|
390
|
+
);
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
const path = pendingSidecarPath(getStoreDir(), id);
|
|
394
|
+
// A job of a deleted incarnation (older delete epoch) is never kept.
|
|
395
|
+
const epoch = compactionArchiveDeleteEpoch(id);
|
|
396
|
+
const jobs = (unpublished.get(id) ?? []).filter((job) => job.epoch === epoch);
|
|
397
|
+
if (!jobs.length) {
|
|
398
|
+
if (sidecarOnDisk.delete(id)) {
|
|
399
|
+
try {
|
|
400
|
+
unlinkSync(path);
|
|
401
|
+
} catch {
|
|
402
|
+
/* already gone */
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
const tmp = `${path}.mixdog-tmp-${randomBytes(4).toString('hex')}`;
|
|
408
|
+
try {
|
|
409
|
+
const text = JSON.stringify({
|
|
410
|
+
v: 1,
|
|
411
|
+
jobs: jobs.map(({ seq, at, divider, clearedThrough, maxBytes, messages }) => ({
|
|
412
|
+
seq,
|
|
413
|
+
at,
|
|
414
|
+
divider,
|
|
415
|
+
clearedThrough,
|
|
416
|
+
maxBytes,
|
|
417
|
+
messages: _messagesForDisk(messages, id),
|
|
418
|
+
})),
|
|
419
|
+
});
|
|
420
|
+
const fd = openSync(tmp, 'wx', 0o600);
|
|
421
|
+
try {
|
|
422
|
+
writeFileSync(fd, text, 'utf8');
|
|
423
|
+
fsyncSync(fd);
|
|
424
|
+
} finally {
|
|
425
|
+
closeSync(fd);
|
|
426
|
+
}
|
|
427
|
+
renameWithRetrySync(tmp, path);
|
|
428
|
+
sidecarOnDisk.add(id);
|
|
429
|
+
return true;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
try {
|
|
432
|
+
unlinkSync(tmp);
|
|
433
|
+
} catch {
|
|
434
|
+
/* never created */
|
|
435
|
+
}
|
|
436
|
+
writeStderrBestEffort(
|
|
437
|
+
`[session] compaction archive pending sidecar failed (sess=${id}): ${error?.message || error}\n`
|
|
438
|
+
);
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** The jobs a sidecar's text holds, or null when the text is not a job list
|
|
444
|
+
* (damaged: unparseable, or not `{ jobs: [{ seq, messages }, …] }`). */
|
|
445
|
+
function parsePendingJobs(text) {
|
|
446
|
+
let doc;
|
|
447
|
+
try {
|
|
448
|
+
doc = JSON.parse(text);
|
|
449
|
+
} catch {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
const jobs = doc?.jobs;
|
|
453
|
+
return Array.isArray(jobs) && jobs.every((job) => Number.isInteger(job?.seq) && Array.isArray(job.messages))
|
|
454
|
+
? jobs
|
|
455
|
+
: null;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Loads the jobs a previous process left in the session's pending sidecar,
|
|
459
|
+
* once per process. The session counts as recovered only after the sidecar
|
|
460
|
+
* was loaded or is confirmed absent (ENOENT): any other read failure throws
|
|
461
|
+
* and leaves it unrecovered (see persistPending). A damaged sidecar is moved
|
|
462
|
+
* aside whole as `<file>.corrupt-<ts>`, like every damaged state file
|
|
463
|
+
* (atomic-file.mjs); its bytes are kept, and a failure to move it throws. */
|
|
464
|
+
function recoverPending(id) {
|
|
465
|
+
if (recovered.has(id)) return;
|
|
466
|
+
const path = pendingSidecarPath(getStoreDir(), id);
|
|
467
|
+
let text;
|
|
468
|
+
try {
|
|
469
|
+
text = fsOps.readFileSync(path, 'utf8');
|
|
470
|
+
} catch (error) {
|
|
471
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
472
|
+
recovered.add(id);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const saved = parsePendingJobs(text);
|
|
476
|
+
if (!saved) {
|
|
477
|
+
const aside = quarantinePath(path);
|
|
478
|
+
renameWithRetrySync(path, aside);
|
|
479
|
+
writeStderrBestEffort(`[session] compaction archive pending sidecar damaged (sess=${id}); kept as ${aside}\n`);
|
|
480
|
+
recovered.add(id);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const list = jobsOf(id);
|
|
484
|
+
const epoch = compactionArchiveDeleteEpoch(id);
|
|
485
|
+
for (const job of saved) {
|
|
486
|
+
if (list.some((held) => held.seq === job.seq)) continue;
|
|
487
|
+
list.push({ ...job, id, session: null, epoch, segments: null });
|
|
488
|
+
}
|
|
489
|
+
list.sort((a, b) => a.seq - b.seq);
|
|
490
|
+
sidecarOnDisk.add(id);
|
|
491
|
+
recovered.add(id);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** recoverPending for callers that cannot refuse (a save event, the startup
|
|
495
|
+
* scan): a failure is reported and leaves the session unrecovered. Returns
|
|
496
|
+
* whether the session is recovered. */
|
|
497
|
+
function tryRecoverPending(id) {
|
|
498
|
+
try {
|
|
499
|
+
recoverPending(id);
|
|
500
|
+
return true;
|
|
501
|
+
} catch (error) {
|
|
502
|
+
writeStderrBestEffort(
|
|
503
|
+
`[session] compaction archive pending sidecar unreadable (sess=${id}): ${error?.message || error}; left untouched\n`
|
|
504
|
+
);
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/** Removes the session's pending sidecar from disk, whatever is in memory. */
|
|
510
|
+
function removePendingSidecar(id) {
|
|
511
|
+
sidecarOnDisk.delete(id);
|
|
512
|
+
try {
|
|
513
|
+
unlinkSync(pendingSidecarPath(getStoreDir(), id));
|
|
514
|
+
} catch {
|
|
515
|
+
/* none on disk */
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* One-shot scan after daemon startup: every pending sidecar a previous process
|
|
521
|
+
* left whose session record still exists is loaded and its segments are
|
|
522
|
+
* published through the normal write queue (oldest first). Returns how many
|
|
523
|
+
* sessions had segments to publish. A store directory that does not exist is
|
|
524
|
+
* empty; any other listing failure is thrown. A session whose sidecar cannot
|
|
525
|
+
* be read is reported and skipped, the others still recover.
|
|
526
|
+
*/
|
|
527
|
+
export async function recoverPendingCompactionArchives() {
|
|
528
|
+
const dir = getStoreDir();
|
|
529
|
+
let names;
|
|
530
|
+
try {
|
|
531
|
+
names = await fsOps.readdir(dir);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
if (error?.code === 'ENOENT') return 0;
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
let count = 0;
|
|
537
|
+
for (const name of names) {
|
|
538
|
+
if (!name.endsWith(COMPACTION_PENDING_SUFFIX)) continue;
|
|
539
|
+
const id = name.slice(0, -COMPACTION_PENDING_SUFFIX.length);
|
|
540
|
+
if (!compactionArchivePath('', id) || !recordExists(dir, id)) continue;
|
|
541
|
+
if (!tryRecoverPending(id)) continue;
|
|
542
|
+
if (!unpublished.get(id)?.length) continue;
|
|
543
|
+
count += 1;
|
|
544
|
+
void enqueueFlush(id);
|
|
545
|
+
}
|
|
546
|
+
return count;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/** Settles with 'landed' | 'closed' | 'deleted' at the session's next such event. */
|
|
550
|
+
function waitForSessionEvent(id) {
|
|
551
|
+
return new Promise((resolve) => {
|
|
552
|
+
let set = waiters.get(id);
|
|
553
|
+
if (!set) {
|
|
554
|
+
set = new Set();
|
|
555
|
+
waiters.set(id, set);
|
|
556
|
+
}
|
|
557
|
+
set.add(resolve);
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function wakeWaiters(id, event) {
|
|
562
|
+
const set = waiters.get(id);
|
|
563
|
+
if (!set) return;
|
|
564
|
+
waiters.delete(id);
|
|
565
|
+
for (const resolve of set) resolve(event);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** Gives up on one job (or all of the session's) with a logged reason. */
|
|
569
|
+
function dropJobs(id, reason, only = null) {
|
|
570
|
+
const list = unpublished.get(id);
|
|
571
|
+
if (!list) return;
|
|
572
|
+
const dropped = only ? list.filter((job) => job === only) : list.splice(0);
|
|
573
|
+
if (only) list.splice(list.indexOf(only), 1);
|
|
574
|
+
if (!list.length) unpublished.delete(id);
|
|
575
|
+
for (const job of [...dropped].reverse()) {
|
|
576
|
+
logArchiveSkip(id, job.seq, 'skipped', reason);
|
|
577
|
+
if (job.session) releaseClaimedSeq(job.session, job.seq);
|
|
578
|
+
}
|
|
579
|
+
if (sidecarOnDisk.has(id)) persistPending(id);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** Appends one job's segment: `{ segments }` once published, else a refusal
|
|
583
|
+
* `{ reason, retry, single }`. Rows are projected here, at publication. */
|
|
584
|
+
async function publishJob(job) {
|
|
585
|
+
const items = namespacedSegmentItems(segmentRows(job.messages, job.id), {
|
|
586
|
+
sessionId: job.id,
|
|
587
|
+
seq: job.seq,
|
|
588
|
+
at: job.at,
|
|
589
|
+
divider: job.divider,
|
|
590
|
+
});
|
|
591
|
+
if (!items.length) return { reason: 'no visible rows at publication', retry: false, single: true };
|
|
592
|
+
return appendSegment({
|
|
593
|
+
sessionId: job.id,
|
|
594
|
+
seq: job.seq,
|
|
595
|
+
at: job.at,
|
|
596
|
+
items,
|
|
597
|
+
clearedThrough: job.clearedThrough,
|
|
598
|
+
maxBytes: job.maxBytes,
|
|
599
|
+
epoch: job.epoch,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Publishes the session's unpublished jobs oldest first, inside its write
|
|
604
|
+
* queue. Stops at the first job that cannot be published yet; it stays. */
|
|
605
|
+
async function flushUnpublished(id) {
|
|
606
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
607
|
+
for (;;) {
|
|
608
|
+
const list = unpublished.get(id);
|
|
609
|
+
const job = list?.[0];
|
|
610
|
+
if (!job) {
|
|
611
|
+
unpublished.delete(id);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
let outcome;
|
|
615
|
+
try {
|
|
616
|
+
outcome = await publishJob(job);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
if (unpublished.get(id) !== list || list[0] !== job) return;
|
|
619
|
+
writeStderrBestEffort(
|
|
620
|
+
`[session] compaction archive write failed (sess=${id}, seq=${job.seq}): ${error?.message || error}; kept for retry\n`
|
|
621
|
+
);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
if (unpublished.get(id) !== list || list[0] !== job) return;
|
|
625
|
+
if (outcome.segments) {
|
|
626
|
+
list.shift();
|
|
627
|
+
job.segments = outcome.segments;
|
|
628
|
+
const archive = job.session?.compaction?.archive;
|
|
629
|
+
// Small metadata only; a later clear may have hidden this segment.
|
|
630
|
+
if (archive && typeof archive === 'object' && !(Number(archive.clearedThrough) >= job.seq)) {
|
|
631
|
+
archive.segments = outcome.segments;
|
|
632
|
+
}
|
|
633
|
+
if (sidecarOnDisk.has(id)) persistPending(id);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (outcome.retry) {
|
|
637
|
+
// The session's own save is what creates its record: wait for that save
|
|
638
|
+
// to land (no polling). A delete ends the wait; a close keeps the jobs
|
|
639
|
+
// for the next process.
|
|
640
|
+
if (!job.deferred) logArchiveSkip(id, job.seq, 'deferred', `${outcome.reason}; waiting for it`);
|
|
641
|
+
job.deferred = true;
|
|
642
|
+
const waiting = waitForSessionEvent(id);
|
|
643
|
+
parkHook?.({ sessionId: id, seq: job.seq });
|
|
644
|
+
const event = await waiting;
|
|
645
|
+
if (event !== 'landed') return;
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
dropJobs(id, outcome.reason, outcome.single ? job : null);
|
|
649
|
+
if (outcome.single) continue;
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function enqueueFlush(id) {
|
|
655
|
+
const task = (pendingWrites.get(id) || Promise.resolve())
|
|
656
|
+
.then(() => flushUnpublished(id))
|
|
657
|
+
.catch((error) => {
|
|
658
|
+
writeStderrBestEffort(`[session] compaction archive write failed (sess=${id}): ${error?.message || error}\n`);
|
|
659
|
+
});
|
|
660
|
+
pendingWrites.set(id, task);
|
|
661
|
+
void task.finally(() => {
|
|
662
|
+
if (pendingWrites.get(id) === task) pendingWrites.delete(id);
|
|
663
|
+
});
|
|
664
|
+
return task;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
subscribeSessionPersistence((id, event) => {
|
|
668
|
+
wakeWaiters(id, event);
|
|
669
|
+
if (event !== 'landed') return;
|
|
670
|
+
if (!tryRecoverPending(id)) return;
|
|
671
|
+
if (unpublished.get(id)?.length) void enqueueFlush(id);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
registerSessionPurgeHook((id) => {
|
|
675
|
+
wakeWaiters(id, 'deleted');
|
|
676
|
+
dropJobs(id, 'session deleted');
|
|
677
|
+
// A sidecar a previous process left is dropped too, in-memory jobs or not.
|
|
678
|
+
removePendingSidecar(id);
|
|
679
|
+
recovered.delete(id);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
/** Test seam: forget all in-memory state (a restart); sidecars stay on disk. */
|
|
683
|
+
export function _resetCompactionArchiveState() {
|
|
684
|
+
unpublished.clear();
|
|
685
|
+
recovered.clear();
|
|
686
|
+
sidecarOnDisk.clear();
|
|
687
|
+
waiters.clear();
|
|
291
688
|
}
|
|
292
689
|
|
|
293
690
|
/**
|
|
294
|
-
* Archive what a
|
|
295
|
-
* dropped messages, capture the delete fence
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
691
|
+
* Archive what a compaction drops, write-ahead. Synchronous part: select the
|
|
692
|
+
* dropped messages, capture the delete fence, allocate the segment number on
|
|
693
|
+
* `session.compaction.archive` and make the job durable in the pending
|
|
694
|
+
* sidecar — call it BEFORE the shortened transcript is adopted (and so can be
|
|
695
|
+
* saved). When the sidecar cannot be written the claim is given back and this
|
|
696
|
+
* throws: the caller must keep the old transcript. Returns `{ dropped,
|
|
697
|
+
* identities, start }` — `start()` queues the publication and returns the
|
|
698
|
+
* pending write (resolves to the retained segment list) — or null when
|
|
699
|
+
* nothing visible was dropped (a rewrite-only compaction).
|
|
301
700
|
*/
|
|
302
|
-
export function
|
|
701
|
+
export function prepareCompactionArchive({
|
|
303
702
|
session,
|
|
304
703
|
sessionId = null,
|
|
305
704
|
before,
|
|
@@ -312,40 +711,58 @@ export function scheduleCompactionArchive({
|
|
|
312
711
|
const projection = compactionArchiveProjection(before, after);
|
|
313
712
|
// Only what the transcript shows is archived; a pass that took out nothing
|
|
314
713
|
// visible (a rewrite, control rows, hidden context) leaves the view live.
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
714
|
+
// The rows are decided here, once, as the segment will hold them, so a
|
|
715
|
+
// number is claimed only for a segment that has items.
|
|
716
|
+
const rows = segmentRows(projection.messages, id);
|
|
717
|
+
if (!rows.length) return null;
|
|
319
718
|
const dropped = droppedByCompaction(before, after);
|
|
719
|
+
// Segments a previous run left unpublished (pending sidecar) hold their
|
|
720
|
+
// numbers. A sidecar that cannot be read throws before anything is claimed:
|
|
721
|
+
// the caller keeps the old transcript, and the sidecar is left as it is.
|
|
722
|
+
recoverPending(id);
|
|
320
723
|
if (!session.compaction || typeof session.compaction !== 'object') session.compaction = {};
|
|
321
724
|
const previous =
|
|
322
725
|
session.compaction.archive && typeof session.compaction.archive === 'object' ? session.compaction.archive : {};
|
|
323
|
-
const
|
|
726
|
+
const held = unpublished.get(id)?.at(-1)?.seq ?? 0;
|
|
727
|
+
const seq = Math.max(1, held + 1, Math.floor(Number(previous.nextSeq) || 1));
|
|
324
728
|
const clearedThrough = Math.max(0, Math.floor(Number(previous.clearedThrough) || 0));
|
|
325
729
|
const epoch = compactionArchiveDeleteEpoch(id);
|
|
326
730
|
session.compaction.archive = { ...previous, nextSeq: seq + 1 };
|
|
327
731
|
const divider = !isAgentModeSession(session);
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
732
|
+
const job = {
|
|
733
|
+
id,
|
|
734
|
+
session,
|
|
735
|
+
seq,
|
|
736
|
+
at,
|
|
737
|
+
divider,
|
|
738
|
+
messages: projection.messages,
|
|
739
|
+
clearedThrough,
|
|
740
|
+
maxBytes,
|
|
741
|
+
epoch,
|
|
742
|
+
segments: null,
|
|
339
743
|
};
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
744
|
+
const list = jobsOf(id);
|
|
745
|
+
list.push(job);
|
|
746
|
+
if (!persistPending(id)) {
|
|
747
|
+
list.pop();
|
|
748
|
+
if (!list.length) unpublished.delete(id);
|
|
749
|
+
releaseClaimedSeq(session, seq);
|
|
750
|
+
logArchiveSkip(id, seq, 'aborted', 'pending sidecar not written; compaction not applied');
|
|
751
|
+
throw new Error(`compaction archive: pending sidecar for ${id} could not be written`);
|
|
752
|
+
}
|
|
753
|
+
return {
|
|
754
|
+
dropped: dropped.length,
|
|
755
|
+
identities: droppedIdentitySummary(projection),
|
|
756
|
+
start: () => enqueueFlush(id).then(() => job.segments),
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** prepareCompactionArchive + start, for a caller that adopts the transcript
|
|
761
|
+
* itself first. Resolves `written` as before. */
|
|
762
|
+
export function scheduleCompactionArchive(args) {
|
|
763
|
+
const prepared = prepareCompactionArchive(args);
|
|
764
|
+
if (!prepared) return null;
|
|
765
|
+
return { dropped: prepared.dropped, identities: prepared.identities, written: prepared.start() };
|
|
349
766
|
}
|
|
350
767
|
|
|
351
768
|
/** Hide everything archived so far (a clear): later reads and the next write
|