mixdog 1.3.4 → 1.4.0
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/LICENSES/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +17 -2
- package/README.md +11 -0
- package/package.json +3 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/gate-local.mjs +4 -2
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test.mjs +12 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +25 -1
- package/src/defaults/skills/browser-use/SKILL.md +77 -17
- 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 +33 -9
- package/src/defaults/skills/setup/SKILL.md +29 -2
- package/src/defaults/skills/setup/references/actions.md +117 -28
- package/src/defaults/skills/setup/references/surfaces.md +77 -35
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/rules/lead/LEAD.md +18 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- 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/config.mjs +34 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
- package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- 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.mjs +160 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -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 +19 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
- 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/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- 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 +43 -3
- 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/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- 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 +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
- 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/eager-dispatch.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
- 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 +14 -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 +10 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- 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-commit.mjs +2 -0
- 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 +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -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 +23 -13
- 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 +129 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- 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-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- 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/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
- 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/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/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.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- 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/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +135 -154
- package/src/runtime/channels/lib/schedules-db.mjs +83 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- 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 +19 -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 +27 -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/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/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- 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-hops.mjs +62 -2
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +51 -35
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +33 -2
- package/src/runtime/shared/connector-catalog.mjs +166 -8
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -14
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/err-text.mjs +24 -15
- 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/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- 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 +104 -0
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/mixy-presence.mjs +22 -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 +231 -59
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- 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 +52 -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 +34 -1
- 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-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -14
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/runtime/shared/worktrees/test-support.mjs +9 -4
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +27 -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 +486 -179
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +561 -199
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/begin.mjs +3 -2
- 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 +8 -1
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
- 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 +61 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +927 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- 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 +35 -13
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -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/routine-home-turn.mjs +33 -6
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- 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 +73 -31
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +8 -4
- 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 +28 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +187 -29
- package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- 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 +161 -157
- package/src/standalone/daemon.mjs +33 -2
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +10 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- 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 +23 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +21 -10
- package/src/tui/app/model-options.mjs +4 -27
- package/src/tui/dist/index.mjs +81 -65
- 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 +23 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- 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 +30 -2
- 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 +5 -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 +36 -3
- package/src/tui/session/turn.mjs +23 -2
- 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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted copy of the agent pool's session-header cache. The in-memory
|
|
3
|
+
* cache of store-agent-worker-rows.mjs died with the daemon, so the first
|
|
4
|
+
* pool listing after every restart re-read every worker transcript (~1,700
|
|
5
|
+
* records, ~1 GB) just to rebuild headers that had not changed: seconds of
|
|
6
|
+
* I/O and CPU right when the restored conversation was loading.
|
|
7
|
+
*
|
|
8
|
+
* The file only seeds the in-memory cache. Every seeded entry is still served
|
|
9
|
+
* under that cache's full-stamp policy — the record must still have the exact
|
|
10
|
+
* { dev, ino, size, mtimeNs, ctimeNs } its header was read from, a stamp that
|
|
11
|
+
* was settled when cached — so a record changed while no daemon ran (or by
|
|
12
|
+
* another process) is read again. Being a cache, an absent, unreadable,
|
|
13
|
+
* damaged, other-version or other-field-set file seeds nothing (a bad entry
|
|
14
|
+
* alone is skipped) and is replaced by the next write; writes are atomic.
|
|
15
|
+
*/
|
|
16
|
+
import { randomBytes } from 'node:crypto';
|
|
17
|
+
import { readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { sessionStampOf } from './store/canonical-reader.mjs';
|
|
20
|
+
import { isStoredSessionId } from './store-summary-fields.mjs';
|
|
21
|
+
import { dataDir } from './store-summary-locations.mjs';
|
|
22
|
+
|
|
23
|
+
const VERSION = 1;
|
|
24
|
+
const STAMP_KEYS = ['dev', 'ino', 'size', 'mtimeNs', 'ctimeNs'];
|
|
25
|
+
const UNSIGNED = /^\d+$/;
|
|
26
|
+
|
|
27
|
+
export function storedAgentWorkerHeaderCachePath(dir = dataDir()) {
|
|
28
|
+
return join(dir, 'agent-worker-header-cache.json');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function decodeStamp(value) {
|
|
32
|
+
if (!Array.isArray(value) || value.length !== STAMP_KEYS.length) return null;
|
|
33
|
+
if (!value.every((part) => typeof part === 'string' && UNSIGNED.test(part))) return null;
|
|
34
|
+
return sessionStampOf(Object.fromEntries(STAMP_KEYS.map((key, index) => [key, BigInt(value[index])])));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function decodeHeader(value, fields) {
|
|
38
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
39
|
+
return Object.keys(value).every((key) => fields.has(key)) ? value : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** sessionId -> { stamp, header } from the file at `path`; empty when the
|
|
43
|
+
* file is absent, unreadable, damaged, or written for another version or
|
|
44
|
+
* header field list. */
|
|
45
|
+
export async function readPersistedWorkerHeaders(path, fields) {
|
|
46
|
+
const entries = new Map();
|
|
47
|
+
let doc;
|
|
48
|
+
try {
|
|
49
|
+
doc = JSON.parse(await readFile(path, 'utf8'));
|
|
50
|
+
} catch {
|
|
51
|
+
return entries;
|
|
52
|
+
}
|
|
53
|
+
if (doc?.version !== VERSION || doc.fields !== fields.join(',')) return entries;
|
|
54
|
+
if (!doc.sessions || typeof doc.sessions !== 'object' || Array.isArray(doc.sessions)) return entries;
|
|
55
|
+
const wanted = new Set(fields);
|
|
56
|
+
for (const [sessionId, entry] of Object.entries(doc.sessions)) {
|
|
57
|
+
if (!isStoredSessionId(sessionId) || !entry || typeof entry !== 'object') continue;
|
|
58
|
+
const stamp = decodeStamp(entry.stamp);
|
|
59
|
+
const header = decodeHeader(entry.header, wanted);
|
|
60
|
+
if (stamp && header) entries.set(sessionId, { stamp, header });
|
|
61
|
+
}
|
|
62
|
+
return entries;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Replaces the file at `path` with `entries` (sessionId -> { stamp, header })
|
|
66
|
+
* by rename, so a reader sees the old file or the new one, never a torn one.
|
|
67
|
+
* Resolves false instead of throwing: a lost write only costs the next
|
|
68
|
+
* restart some re-reads. The data directory is never created: a store that
|
|
69
|
+
* is gone gets no cache. */
|
|
70
|
+
export async function writePersistedWorkerHeaders(path, fields, entries) {
|
|
71
|
+
const sessions = {};
|
|
72
|
+
for (const [sessionId, { stamp, header }] of entries) {
|
|
73
|
+
sessions[sessionId] = { stamp: STAMP_KEYS.map((key) => String(stamp[key])), header };
|
|
74
|
+
}
|
|
75
|
+
// The temp shape is atomic-file.mjs's `<name>.mixdog-tmp-<hex>`.
|
|
76
|
+
const tmp = `${path}.mixdog-tmp-${randomBytes(8).toString('hex')}`;
|
|
77
|
+
try {
|
|
78
|
+
await writeFile(tmp, JSON.stringify({ version: VERSION, fields: fields.join(','), sessions }), { flag: 'wx' });
|
|
79
|
+
await rename(tmp, path);
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
await unlink(tmp).catch(() => {});
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { readFileSync } from 'node:fs';
|
|
19
19
|
import { readFile, stat } from 'node:fs/promises';
|
|
20
|
-
import { join } from 'node:path';
|
|
20
|
+
import { basename, join } from 'node:path';
|
|
21
21
|
import {
|
|
22
22
|
isSettledSessionStamp,
|
|
23
23
|
sameSessionStamp,
|
|
@@ -27,6 +27,11 @@ import {
|
|
|
27
27
|
import { readJsonProjection } from '../../../shared/pending-json-projection.mjs';
|
|
28
28
|
import { listStoredAgentWorkers as assembleStoredAgentWorkers } from './store-agent-worker-pool.mjs';
|
|
29
29
|
import { readTopLevelFields } from './store-agent-worker-header.mjs';
|
|
30
|
+
import {
|
|
31
|
+
readPersistedWorkerHeaders,
|
|
32
|
+
storedAgentWorkerHeaderCachePath,
|
|
33
|
+
writePersistedWorkerHeaders,
|
|
34
|
+
} from './store-agent-worker-header-cache.mjs';
|
|
30
35
|
import {
|
|
31
36
|
TAG_TOMBSTONE_TTL_MS,
|
|
32
37
|
agentPaneVisible,
|
|
@@ -156,12 +161,12 @@ function cancelState(source) {
|
|
|
156
161
|
function effectiveCancel(row, session) {
|
|
157
162
|
const rowCancel = cancelState(row);
|
|
158
163
|
const sessionCancel = cancelState(session);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
+
}
|
|
165
170
|
if (!cancel) return null;
|
|
166
171
|
const newerWork = cancel.at > 0 && Math.max(workStampMs(row), workStampMs(session)) > cancel.at;
|
|
167
172
|
return newerWork ? null : cancel;
|
|
@@ -330,8 +335,104 @@ const WORKER_HEADER_FIELDS = [
|
|
|
330
335
|
// assembly the first answer stands, absent or unreadable included.
|
|
331
336
|
let passHeaders = null; // path -> header | null, live only during an assembly
|
|
332
337
|
|
|
333
|
-
function workerSessionHeaderPath(sessionId) {
|
|
334
|
-
return join(
|
|
338
|
+
function workerSessionHeaderPath(sessionId, dir = dataDir()) {
|
|
339
|
+
return join(dir, 'sessions', `${sessionId}.json`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Every change to the header cache is counted, so the persisted copy is
|
|
343
|
+
// rewritten only when the cache it mirrors moved.
|
|
344
|
+
let headerCacheEdits = 0;
|
|
345
|
+
|
|
346
|
+
function cacheHeader(path, entry) {
|
|
347
|
+
workerSessionHeaderCache.set(path, entry);
|
|
348
|
+
headerCacheEdits += 1;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function dropHeader(path) {
|
|
352
|
+
if (workerSessionHeaderCache.delete(path)) headerCacheEdits += 1;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// The header cache outlives the daemon through store-agent-worker-header-
|
|
356
|
+
// cache.mjs: the first awaited listing of a data directory seeds the cache
|
|
357
|
+
// from its file (entries already in memory win), and an awaited listing that
|
|
358
|
+
// changed the cache schedules a rewrite. The write waits out
|
|
359
|
+
// HEADER_PERSIST_DELAY_MS — off the boot path, and at most one write per
|
|
360
|
+
// window however often agents churn. Seeded entries pass the same full-stamp
|
|
361
|
+
// check as any other, so a seed never serves a header its file no longer has.
|
|
362
|
+
const HEADER_PERSIST_DELAY_MS = 10_000;
|
|
363
|
+
let seededDir = null;
|
|
364
|
+
let seeding = null;
|
|
365
|
+
// The edit count the last write left on disk, and the directory it went to:
|
|
366
|
+
// a count persisted for one data directory says nothing about another.
|
|
367
|
+
let persistedEdits = 0;
|
|
368
|
+
let persistedDir = null;
|
|
369
|
+
let persistTimer = null;
|
|
370
|
+
let persistTimerDir = null;
|
|
371
|
+
let persistWrite = null;
|
|
372
|
+
|
|
373
|
+
function seedPersistedHeaders() {
|
|
374
|
+
const dir = dataDir();
|
|
375
|
+
if (seededDir !== dir) {
|
|
376
|
+
seededDir = dir;
|
|
377
|
+
seeding = readPersistedWorkerHeaders(storedAgentWorkerHeaderCachePath(dir), WORKER_HEADER_FIELDS).then(
|
|
378
|
+
(entries) => {
|
|
379
|
+
for (const [sessionId, { stamp, header }] of entries) {
|
|
380
|
+
const path = workerSessionHeaderPath(sessionId, dir);
|
|
381
|
+
if (!workerSessionHeaderCache.has(path)) workerSessionHeaderCache.set(path, { stamp, header, pass: 0 });
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return seeding;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function schedulePersistHeaders() {
|
|
390
|
+
const dir = dataDir();
|
|
391
|
+
// A write pending for the directory this listing left is dropped: the
|
|
392
|
+
// listing just forgot that directory's unread headers, so writing it now
|
|
393
|
+
// would shrink its file. Its last written cache still stands.
|
|
394
|
+
if (persistTimer && persistTimerDir !== dir) {
|
|
395
|
+
clearTimeout(persistTimer);
|
|
396
|
+
persistTimer = null;
|
|
397
|
+
}
|
|
398
|
+
if (persistTimer || (persistedDir === dir && headerCacheEdits === persistedEdits)) return;
|
|
399
|
+
persistTimerDir = dir;
|
|
400
|
+
persistTimer = setTimeout(() => {
|
|
401
|
+
persistTimer = null;
|
|
402
|
+
persistHeaders(persistTimerDir);
|
|
403
|
+
}, HEADER_PERSIST_DELAY_MS);
|
|
404
|
+
persistTimer.unref?.();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function persistHeaders(dir) {
|
|
408
|
+
if (persistWrite) return persistWrite;
|
|
409
|
+
const edits = headerCacheEdits;
|
|
410
|
+
const entries = [];
|
|
411
|
+
for (const [path, entry] of workerSessionHeaderCache) {
|
|
412
|
+
const sessionId = basename(path, '.json');
|
|
413
|
+
if (isStoredSessionId(sessionId) && workerSessionHeaderPath(sessionId, dir) === path) {
|
|
414
|
+
entries.push([sessionId, entry]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
persistWrite = writePersistedWorkerHeaders(storedAgentWorkerHeaderCachePath(dir), WORKER_HEADER_FIELDS, entries).then(
|
|
418
|
+
(written) => {
|
|
419
|
+
if (written) {
|
|
420
|
+
persistedEdits = edits;
|
|
421
|
+
persistedDir = dir;
|
|
422
|
+
}
|
|
423
|
+
persistWrite = null;
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
return persistWrite;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** Test/diagnostic seam: writes a scheduled header-cache persist now and
|
|
430
|
+
* resolves once no write is in flight. */
|
|
431
|
+
export function flushStoredAgentWorkerHeaderCache() {
|
|
432
|
+
if (!persistTimer) return persistWrite || Promise.resolve();
|
|
433
|
+
clearTimeout(persistTimer);
|
|
434
|
+
persistTimer = null;
|
|
435
|
+
return persistHeaders(persistTimerDir);
|
|
335
436
|
}
|
|
336
437
|
|
|
337
438
|
function readWorkerSessionHeader(sessionId) {
|
|
@@ -358,15 +459,18 @@ function observeStamp(path) {
|
|
|
358
459
|
// window when read. Size + mtime alone let an equal-length rewrite that kept
|
|
359
460
|
// its mtime serve stale ownership, status and cancel fields.
|
|
360
461
|
function probeWorkerSessionHeader(path) {
|
|
361
|
-
// Absent and unreadable alike: no header.
|
|
462
|
+
// Absent and unreadable alike: no header, and no cached one kept for it.
|
|
362
463
|
const before = observeStamp(path);
|
|
363
|
-
if (!before)
|
|
464
|
+
if (!before) {
|
|
465
|
+
dropHeader(path);
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
364
468
|
const cached = workerSessionHeaderCache.get(path);
|
|
365
469
|
if (cached && sameSessionStamp(cached.stamp, before)) {
|
|
366
470
|
cached.pass = headerPass;
|
|
367
471
|
return cached.header;
|
|
368
472
|
}
|
|
369
|
-
|
|
473
|
+
dropHeader(path);
|
|
370
474
|
// A rewrite re-reads only this record, and only its top-level fields are
|
|
371
475
|
// decoded: the transcript bytes are validated, never materialized.
|
|
372
476
|
let header = null;
|
|
@@ -378,7 +482,7 @@ function probeWorkerSessionHeader(path) {
|
|
|
378
482
|
if (!header) return null;
|
|
379
483
|
workerHeaderReads += 1;
|
|
380
484
|
if (isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path))) {
|
|
381
|
-
|
|
485
|
+
cacheHeader(path, { stamp: before, header, pass: headerPass });
|
|
382
486
|
}
|
|
383
487
|
return header;
|
|
384
488
|
}
|
|
@@ -395,10 +499,13 @@ async function observeStampAsync(path) {
|
|
|
395
499
|
* same full-stamp cache policy. */
|
|
396
500
|
async function probeWorkerSessionHeaderAsync(path) {
|
|
397
501
|
const before = await observeStampAsync(path);
|
|
398
|
-
if (!before)
|
|
502
|
+
if (!before) {
|
|
503
|
+
dropHeader(path);
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
399
506
|
const cached = workerSessionHeaderCache.get(path);
|
|
400
507
|
if (cached && sameSessionStamp(cached.stamp, before)) return cached.header;
|
|
401
|
-
|
|
508
|
+
dropHeader(path);
|
|
402
509
|
let header = null;
|
|
403
510
|
try {
|
|
404
511
|
header = readTopLevelFields(await readFile(path), WORKER_HEADER_FIELDS);
|
|
@@ -408,7 +515,7 @@ async function probeWorkerSessionHeaderAsync(path) {
|
|
|
408
515
|
if (!header) return null;
|
|
409
516
|
workerHeaderReads += 1;
|
|
410
517
|
if (isSettledSessionStamp(before) && sameSessionStamp(before, await observeStampAsync(path))) {
|
|
411
|
-
|
|
518
|
+
cacheHeader(path, { stamp: before, header, pass: headerPass });
|
|
412
519
|
}
|
|
413
520
|
return header;
|
|
414
521
|
}
|
|
@@ -418,7 +525,9 @@ async function probeWorkerSessionHeaderAsync(path) {
|
|
|
418
525
|
const HEADER_READ_CONCURRENCY = 4;
|
|
419
526
|
|
|
420
527
|
async function prefetchWorkerHeaders(sessionIds, headers) {
|
|
421
|
-
const paths = [...new Set([...sessionIds].map(
|
|
528
|
+
const paths = [...new Set([...sessionIds].map((sessionId) => workerSessionHeaderPath(sessionId)))].filter(
|
|
529
|
+
(path) => !headers.has(path)
|
|
530
|
+
);
|
|
422
531
|
let next = 0;
|
|
423
532
|
const drain = async () => {
|
|
424
533
|
while (next < paths.length) {
|
|
@@ -524,6 +633,8 @@ function projectChildWorkerRow(row, context) {
|
|
|
524
633
|
// A live turn blocked on the user's approval (persisted by the agent
|
|
525
634
|
// tool's worker index); never carried by a row that is no longer working.
|
|
526
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 } : {}),
|
|
527
638
|
// Why a settled failed row failed; a row working again has no failure.
|
|
528
639
|
...(!working && failedAgentWorker(row) && cleanValue(row.failureReason)
|
|
529
640
|
? { failureReason: cleanValue(row.failureReason) }
|
|
@@ -634,7 +745,7 @@ export function listStoredAgentWorkers({ runningSessionIds = null } = {}) {
|
|
|
634
745
|
// Keep what the latest assembly read, whatever the index size; forget the rest.
|
|
635
746
|
function forgetUnreadHeaders() {
|
|
636
747
|
for (const [path, entry] of workerSessionHeaderCache) {
|
|
637
|
-
if (entry.pass !== headerPass)
|
|
748
|
+
if (entry.pass !== headerPass) dropHeader(path);
|
|
638
749
|
}
|
|
639
750
|
}
|
|
640
751
|
|
|
@@ -643,6 +754,8 @@ function forgetUnreadHeaders() {
|
|
|
643
754
|
* each Lead row's, and each child's owner) are read first with awaited
|
|
644
755
|
* stats and reads; the assembly then runs over what was read, with no file
|
|
645
756
|
* access left in it (any header it still asks for falls back to a read).
|
|
757
|
+
* The first listing after a restart seeds the header cache from its
|
|
758
|
+
* persisted copy, so only records changed since are read again.
|
|
646
759
|
* `runningSessionIds` may be a function, asked once the reads are done. */
|
|
647
760
|
export async function listStoredAgentWorkersAsync({ runningSessionIds = null } = {}) {
|
|
648
761
|
const agentIndexPath = storedAgentWorkerIndexPath();
|
|
@@ -650,6 +763,7 @@ export async function listStoredAgentWorkersAsync({ runningSessionIds = null } =
|
|
|
650
763
|
const [agentIndex, leadIndex] = await Promise.all([
|
|
651
764
|
readWorkerIndexFileAsync(agentIndexPath),
|
|
652
765
|
readWorkerIndexFileAsync(leadIndexPath),
|
|
766
|
+
seedPersistedHeaders(),
|
|
653
767
|
]);
|
|
654
768
|
const headers = new Map();
|
|
655
769
|
const childRows = workerRows(agentIndex);
|
|
@@ -683,6 +797,7 @@ export async function listStoredAgentWorkersAsync({ runningSessionIds = null } =
|
|
|
683
797
|
passHeaders = null;
|
|
684
798
|
passIndexes = null;
|
|
685
799
|
forgetUnreadHeaders();
|
|
800
|
+
schedulePersistHeaders();
|
|
686
801
|
}
|
|
687
802
|
}
|
|
688
803
|
|
|
@@ -131,8 +131,9 @@ export function isLeadVisibleRow(row) {
|
|
|
131
131
|
if (!agentSession && !isRootLeadSession(row) && owner && !LEAD_OWNERS.has(owner)) return false;
|
|
132
132
|
// Mirror listLeadSessions: a previewless zero-message row is an unusable
|
|
133
133
|
// scratch (desktop boot leftovers, crashed first turns) — resuming it
|
|
134
|
-
// shows an empty conversation, so the catalog hides it.
|
|
135
|
-
|
|
134
|
+
// shows an empty conversation, so the catalog hides it. A titled one is a
|
|
135
|
+
// real conversation whose prompts were rewound away: it stays listed.
|
|
136
|
+
if (!row.preview && row.messageCount === 0 && !row.title) return false;
|
|
136
137
|
const sourceType = String(row.sourceType || '')
|
|
137
138
|
.trim()
|
|
138
139
|
.toLowerCase();
|
|
@@ -282,6 +283,15 @@ function rowFromSession(session) {
|
|
|
282
283
|
});
|
|
283
284
|
}
|
|
284
285
|
|
|
286
|
+
/** The catalog row of one session read off `<directory>/<sessionId>.json` by
|
|
287
|
+
* id — the same strict, stamp-cached derivation a listing uses — or null
|
|
288
|
+
* when it is absent, unreadable, foreign or not one the catalog lists. */
|
|
289
|
+
export function storedSessionCatalogRow(directory, sessionId) {
|
|
290
|
+
if (!isStoredSessionId(sessionId)) return null;
|
|
291
|
+
const entry = reconcileSessionFile(directory, sessionId, null, false);
|
|
292
|
+
return entry?.kind === 'row' && isLeadVisibleRow(entry.row) ? entry.row : null;
|
|
293
|
+
}
|
|
294
|
+
|
|
285
295
|
// ── Session file reconcile ──────────────────────────────────────────────────
|
|
286
296
|
// What one `sessions/<id>.json` name contributes to the catalog, with the full
|
|
287
297
|
// stamp { dev, ino, size, mtimeNs, ctimeNs } it was judged at (and the
|
|
@@ -391,31 +391,58 @@ const _pendingRemovals = new Set(); // ids to drop (upsert/removal are mutually
|
|
|
391
391
|
let _summaryRetryTimer = null;
|
|
392
392
|
let _summaryFlushScheduled = false;
|
|
393
393
|
let _summaryFlushInflight = 0;
|
|
394
|
-
|
|
394
|
+
// Ids whose ops the async flush in flight took (they are requeued if it fails).
|
|
395
|
+
const _inflightSummaryIds = new Set();
|
|
396
|
+
const _summarySettleWaiters = new Set(); // { ids: string[]|null, resolve }
|
|
395
397
|
|
|
396
398
|
export function _hasUnsettledSummaryOps() {
|
|
397
399
|
return _pendingUpserts.size > 0 || _pendingRemovals.size > 0 || _summaryFlushScheduled || _summaryFlushInflight > 0;
|
|
398
400
|
}
|
|
399
401
|
|
|
402
|
+
/** The ids among `ids` whose summary ops are queued or being written. */
|
|
403
|
+
export function pendingSummaryIndexIds(ids) {
|
|
404
|
+
return ids.filter((id) => _pendingUpserts.has(id) || _pendingRemovals.has(id) || _inflightSummaryIds.has(id));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function _waiterSettled({ ids }) {
|
|
408
|
+
return ids ? pendingSummaryIndexIds(ids).length === 0 : !_hasUnsettledSummaryOps();
|
|
409
|
+
}
|
|
410
|
+
|
|
400
411
|
function _notifySummarySettled() {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
412
|
+
for (const waiter of _summarySettleWaiters) {
|
|
413
|
+
if (_waiterSettled(waiter)) waiter.settle(true);
|
|
414
|
+
}
|
|
404
415
|
}
|
|
405
416
|
|
|
406
417
|
/**
|
|
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.
|
|
418
|
+
* Resolves true once the queued summary-index mutations have landed — of
|
|
419
|
+
* `ids` only when given, else every one with no flush scheduled or in flight;
|
|
420
|
+
* from then on this process no longer touches the index for them. Callers
|
|
421
|
+
* are told a save/sweep is done before its deferred index flush runs, so an
|
|
422
|
+
* owner that removes or switches the data dir awaits this first. A flush
|
|
423
|
+
* refused by a held lock (an overlapping flush of this process, or another
|
|
424
|
+
* process) stays queued and settles when its retry lands. When `signal`
|
|
425
|
+
* aborts first it resolves false and stops waiting; the ops stay queued.
|
|
414
426
|
*/
|
|
415
|
-
export function settleSummaryIndexWrites() {
|
|
416
|
-
|
|
427
|
+
export function settleSummaryIndexWrites({ ids = null, signal = null } = {}) {
|
|
428
|
+
const waiter = { ids, settle: null };
|
|
429
|
+
if (_waiterSettled(waiter)) return Promise.resolve(true);
|
|
430
|
+
if (signal?.aborted) return Promise.resolve(false);
|
|
417
431
|
_summaryRetryTimer?.ref?.();
|
|
418
|
-
return new Promise((resolve) =>
|
|
432
|
+
return new Promise((resolve) => {
|
|
433
|
+
const onAbort = () => waiter.settle(false);
|
|
434
|
+
// The one exit, landed or abandoned: the waiter and its abort listener
|
|
435
|
+
// both go, so a long-lived signal collects nothing per settle.
|
|
436
|
+
waiter.settle = (landed) => {
|
|
437
|
+
if (!_summarySettleWaiters.delete(waiter)) return;
|
|
438
|
+
signal?.removeEventListener('abort', onAbort);
|
|
439
|
+
// Only an awaited settlement keeps the process alive for the retry.
|
|
440
|
+
if (_summarySettleWaiters.size === 0) _summaryRetryTimer?.unref?.();
|
|
441
|
+
resolve(landed);
|
|
442
|
+
};
|
|
443
|
+
_summarySettleWaiters.add(waiter);
|
|
444
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
445
|
+
});
|
|
419
446
|
}
|
|
420
447
|
|
|
421
448
|
function _scheduleSummaryRetry() {
|
|
@@ -498,11 +525,14 @@ export function _flushPendingSummaryOps({ sync = false } = {}) {
|
|
|
498
525
|
return;
|
|
499
526
|
}
|
|
500
527
|
_summaryFlushInflight++;
|
|
528
|
+
const taken = [...upserts.keys(), ...removals];
|
|
529
|
+
for (const id of taken) _inflightSummaryIds.add(id);
|
|
501
530
|
withFileLock(`${path}.lock`, () => _appendSummaryOpsLocked(path, text), { timeoutMs: SUMMARY_LOCK_TIMEOUT_MS })
|
|
502
531
|
.then(() => _sweepStaleSummaryTempOnce(path))
|
|
503
532
|
.catch(requeue)
|
|
504
533
|
.finally(() => {
|
|
505
534
|
_summaryFlushInflight--;
|
|
535
|
+
for (const id of taken) _inflightSummaryIds.delete(id);
|
|
506
536
|
if (_pendingUpserts.size > 0 || _pendingRemovals.size > 0) _scheduleSummaryFlush();
|
|
507
537
|
_notifySummarySettled();
|
|
508
538
|
});
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
scanSessionFiles,
|
|
23
23
|
scanSessionFilesAsync,
|
|
24
24
|
stampedDerivation,
|
|
25
|
+
storedSessionCatalogRow,
|
|
25
26
|
} from './store-summary-catalog.mjs';
|
|
26
27
|
import { createStoredTranscriptCache, nextProjectionStamp } from './store-transcript-cache.mjs';
|
|
27
28
|
import {
|
|
@@ -34,10 +35,17 @@ import { projectStoredTranscript } from './store-transcript-projection.mjs';
|
|
|
34
35
|
import { dataDir } from './store-summary-locations.mjs';
|
|
35
36
|
import { isStoredSessionId, workspaceSummary } from './store-summary-fields.mjs';
|
|
36
37
|
import { readArchivedAgentResult } from './store-summary-archived-agent.mjs';
|
|
37
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
compactionArchivePath,
|
|
40
|
+
compactionArchiveRange,
|
|
41
|
+
readCompactionArchivePage,
|
|
42
|
+
visitCompactionArchive,
|
|
43
|
+
} from './compaction-archive.mjs';
|
|
44
|
+
import { attachmentHistoryRows } from './stored-attachment-history.mjs';
|
|
38
45
|
import { currentSessionCatalogDb, onSessionCatalogDbChange, sessionCatalogDb } from './catalog-db/index.mjs';
|
|
39
46
|
import { decodeCatalogCursor, encodeCatalogCursor, isAfterCursor } from './catalog-db/schema.mjs';
|
|
40
|
-
import { onSessionActivityChange } from '../../../shared/session-activity-signal.mjs';
|
|
47
|
+
import { onSessionActivityChange, onSessionApprovalCardChange } from '../../../shared/session-activity-signal.mjs';
|
|
48
|
+
import { inFlightSessionActivity } from './manager/runtime-state.mjs';
|
|
41
49
|
import {
|
|
42
50
|
readCanonicalSessionRecord as readCanonicalLifecycle,
|
|
43
51
|
offerStrictSessionRecord,
|
|
@@ -59,6 +67,11 @@ export { storedAgentWorkerIndexPath } from './store-summary-locations.mjs';
|
|
|
59
67
|
// the desktop service worker keeps its own for the deep-history prefetch.
|
|
60
68
|
const storedTranscriptCache = createStoredTranscriptCache();
|
|
61
69
|
|
|
70
|
+
// The attachment rows of whole stored conversations: the same cache mechanism
|
|
71
|
+
// and byte budget as the windows above, as an instance of its own, which a
|
|
72
|
+
// lookup can neither fill nor evict.
|
|
73
|
+
const attachmentHistoryCache = createStoredTranscriptCache();
|
|
74
|
+
|
|
62
75
|
/** Test seam: forget every cached projection. */
|
|
63
76
|
export function clearStoredTranscriptCache() {
|
|
64
77
|
storedTranscriptCache.clear();
|
|
@@ -106,7 +119,23 @@ onSessionCatalogDbChange(() => notifyCatalogListeners());
|
|
|
106
119
|
// Runtime activity transitions (turn start/settle, task wait, background
|
|
107
120
|
// hand-off/finish) change only the Agents pool projection: `agent-activity`
|
|
108
121
|
// lets the reader refresh just that, at once.
|
|
109
|
-
|
|
122
|
+
// A transition that starts or settles a turn also changes the catalog rows'
|
|
123
|
+
// `working` / `agentWorking` overlay. The listing that a turn's final save
|
|
124
|
+
// triggers can run before the turn leaves the runtime map, and nothing else
|
|
125
|
+
// re-lists: a bot home (no title write after its turn) kept `working` — a
|
|
126
|
+
// spinner and no completion dot — until its next file change.
|
|
127
|
+
let catalogActivity = '';
|
|
128
|
+
onSessionActivityChange(() => {
|
|
129
|
+
notifyCatalogListeners('agent-activity');
|
|
130
|
+
const { working, agentWorking } = inFlightSessionActivity();
|
|
131
|
+
const next = `${[...working].sort().join(',')}|${[...agentWorking].sort().join(',')}`;
|
|
132
|
+
if (next === catalogActivity) return;
|
|
133
|
+
catalogActivity = next;
|
|
134
|
+
notifyCatalogListeners('turn-activity');
|
|
135
|
+
});
|
|
136
|
+
// A session started or stopped waiting on the user's approval: its catalog row
|
|
137
|
+
// (`awaitingApproval`) changed, though no store file did.
|
|
138
|
+
onSessionApprovalCardChange(() => notifyCatalogListeners('approval'));
|
|
110
139
|
|
|
111
140
|
/** `listener()` runs when a background pass finds a session file that changed
|
|
112
141
|
* without a directory or index change, i.e. when an ordinary listing would
|
|
@@ -232,6 +261,12 @@ export async function listStoredSessionSummaryPage(options = {}) {
|
|
|
232
261
|
};
|
|
233
262
|
}
|
|
234
263
|
|
|
264
|
+
/** One session's catalog row by id, read off its record (not the paged
|
|
265
|
+
* listing): null when the catalog would not list it. */
|
|
266
|
+
export function readStoredSessionCatalogRow(id) {
|
|
267
|
+
return storedSessionCatalogRow(join(dataDir(), 'sessions'), String(id || '').trim());
|
|
268
|
+
}
|
|
269
|
+
|
|
235
270
|
/** Exact, fail-closed existence check for a durable session address.
|
|
236
271
|
* Summary indexes and desktop metadata are presentation caches; neither may
|
|
237
272
|
* make a missing sessions/<id>.json record addressable again. */
|
|
@@ -269,6 +304,7 @@ function storedSessionMetadata(sessionId, session) {
|
|
|
269
304
|
ownerSessionId: session.ownerSessionId || session.parentSessionId || null,
|
|
270
305
|
visibility: sessionVisibility(session),
|
|
271
306
|
agentTag: session.agentTag || null,
|
|
307
|
+
title: session.title || null,
|
|
272
308
|
cwd: session.cwd || '',
|
|
273
309
|
...(workspaceSummary(session.workspace) ? { workspace: workspaceSummary(session.workspace) } : {}),
|
|
274
310
|
provider: session.provider || null,
|
|
@@ -405,6 +441,49 @@ async function produceStoredTranscript(text, stamp, context) {
|
|
|
405
441
|
}
|
|
406
442
|
}
|
|
407
443
|
|
|
444
|
+
/** The rows of one session's whole stored conversation that name attachments
|
|
445
|
+
* (see attachmentHistoryRows): its record's messages and every visible
|
|
446
|
+
* segment of its compaction archive (a cleared or capped archive stays
|
|
447
|
+
* excluded, as for a window). Read from the files alone, under the same
|
|
448
|
+
* strict record authority as a transcript read, and cached by content
|
|
449
|
+
* identity in a cache of its own: the transcript windows are untouched.
|
|
450
|
+
* Null when the record is absent, unreadable or not this session's. */
|
|
451
|
+
async function readStoredAttachmentHistory(sessionId, recordPath, archivePath) {
|
|
452
|
+
const recordStat = probePath(recordPath);
|
|
453
|
+
if (recordStat.state !== PROBE_PRESENT) return null;
|
|
454
|
+
const archive = probePath(archivePath);
|
|
455
|
+
const key = `${sessionId}|attachments`;
|
|
456
|
+
let complete = true;
|
|
457
|
+
const { value } = await attachmentHistoryCache.read({
|
|
458
|
+
key,
|
|
459
|
+
fingerprint: [archive.state, archive.mtimeMs, archive.ctimeMs, archive.size, archive.ino, archive.dev].join(':'),
|
|
460
|
+
fileStat: recordStat,
|
|
461
|
+
loadText: () => {
|
|
462
|
+
const body = readTextFile(recordPath);
|
|
463
|
+
return body.state === PROBE_PRESENT ? body.text : null;
|
|
464
|
+
},
|
|
465
|
+
produce: async (text) => {
|
|
466
|
+
const record = readTopLevelLifecycleRecord(text);
|
|
467
|
+
if (isLifecycleUnreadable(record) || record.id !== sessionId) return null;
|
|
468
|
+
const { restoreTranscriptItems } = await import('./transcript-restore/restore.mjs');
|
|
469
|
+
const items = [];
|
|
470
|
+
const range = compactionArchiveRange(record.doc);
|
|
471
|
+
// An archive that exists but could not be walked is a transient miss,
|
|
472
|
+
// not a state worth keeping.
|
|
473
|
+
const walked = range
|
|
474
|
+
? await visitCompactionArchive(join(dataDir(), 'sessions'), sessionId, range, (segment) =>
|
|
475
|
+
items.push(...attachmentHistoryRows(segment))
|
|
476
|
+
)
|
|
477
|
+
: true;
|
|
478
|
+
complete = walked || archive.state !== PROBE_PRESENT;
|
|
479
|
+
items.push(...attachmentHistoryRows(restoreTranscriptItems(record.doc.messages, { sessionId })));
|
|
480
|
+
return { sessionId, items };
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
if (!complete) attachmentHistoryCache.forget(key);
|
|
484
|
+
return value ?? null;
|
|
485
|
+
}
|
|
486
|
+
|
|
408
487
|
/** Read exactly one persisted session for a visible desktop pane. This never
|
|
409
488
|
* enumerates siblings. Normal reads stay independent of runtime ownership;
|
|
410
489
|
* interrupted turns conditionally use the same durable reconnect recovery as
|
|
@@ -416,6 +495,8 @@ export async function readStoredSessionTranscript(id, options = {}) {
|
|
|
416
495
|
// absent, unreadable, ambiguous or foreign record yields no transcript.
|
|
417
496
|
const { recordPath, checkpointPath, archivePath } = storedTranscriptInputPaths(sessionId);
|
|
418
497
|
if (options.metadataOnly === true) return readStoredSessionMetadata(recordPath, sessionId);
|
|
498
|
+
// The attachments the whole conversation names, apart from any window.
|
|
499
|
+
if (options.attachmentHistory === true) return readStoredAttachmentHistory(sessionId, recordPath, archivePath);
|
|
419
500
|
// One page of the display-only compaction archive (the live head loader
|
|
420
501
|
// pages it once a runtime's in-memory rows are exhausted).
|
|
421
502
|
if (options.compactionArchive && typeof options.compactionArchive === 'object') {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The attachments a session's whole stored conversation names (its record and
|
|
2
|
+
// its compaction archive), reduced to the metadata that says which rows name
|
|
3
|
+
// which attachment. Whether an attachment id belongs to a session is decided
|
|
4
|
+
// against this, not against a transcript window (a tail plus the pages paged
|
|
5
|
+
// in), which no longer holds rows a compaction moved into the archive.
|
|
6
|
+
|
|
7
|
+
const pick = (entry, keys) =>
|
|
8
|
+
Object.fromEntries(keys.filter((key) => entry?.[key] != null).map((key) => [key, entry[key]]));
|
|
9
|
+
const picked = (list, keys) => (Array.isArray(list) ? list.map((entry) => pick(entry, keys)) : []);
|
|
10
|
+
|
|
11
|
+
/** The rows of `items` that name attachments, reduced to the fields the
|
|
12
|
+
* desktop reads to attribute an id to its row: the row kind (and a notice's
|
|
13
|
+
* routine / room kind) and each attachment's id, name, type and size. No
|
|
14
|
+
* text, tool output or other row content is kept. */
|
|
15
|
+
export function attachmentHistoryRows(items) {
|
|
16
|
+
const rows = [];
|
|
17
|
+
for (const item of Array.isArray(items) ? items : []) {
|
|
18
|
+
const attachments = picked(item?.attachments, ['id', 'name', 'mimeType', 'kind', 'size']);
|
|
19
|
+
const images = picked(item?.images, ['attachmentRef', 'name', 'mimeType', 'bytes']);
|
|
20
|
+
const imageRefs = picked(item?.imageRefs, ['ref']);
|
|
21
|
+
if (!attachments.length && !images.length && !imageRefs.length) continue;
|
|
22
|
+
rows.push({
|
|
23
|
+
kind: item.kind,
|
|
24
|
+
...(item.routine?.kind ? { routine: { kind: item.routine.kind } } : {}),
|
|
25
|
+
...(item.room?.kind ? { room: { kind: item.room.kind } } : {}),
|
|
26
|
+
...(attachments.length ? { attachments } : {}),
|
|
27
|
+
...(images.length ? { images } : {}),
|
|
28
|
+
...(imageRefs.length ? { imageRefs } : {}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return rows;
|
|
32
|
+
}
|