mixdog 0.9.62 → 0.9.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +9 -2
- package/scripts/code-graph-description-contract.mjs +3 -3
- package/scripts/verify-release-assets.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +19 -124
- package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
- package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +1 -1
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
- package/src/runtime/channels/tool-defs.mjs +2 -3
- package/src/runtime/memory/index.mjs +1 -5
- package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
- package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
- package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
- package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
- package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
- package/src/runtime/memory/lib/ko-morph.mjs +2 -2
- package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
- package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
- package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
- package/src/runtime/memory/lib/memory-embed.mjs +2 -6
- package/src/runtime/memory/lib/memory-log.mjs +8 -0
- package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
- package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
- package/src/runtime/memory/lib/memory-score.mjs +1 -1
- package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
- package/src/runtime/memory/lib/memory.mjs +2 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
- package/src/runtime/memory/lib/pg/process.mjs +1 -5
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
- package/src/runtime/memory/lib/recall-format.mjs +2 -2
- package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
- package/src/runtime/memory/lib/trace-store.mjs +2 -6
- package/src/runtime/search/lib/http-fetch.mjs +6 -6
- package/src/runtime/search/lib/web-tools.mjs +2 -2
- package/src/runtime/shared/buffered-appender.mjs +1 -1
- package/src/runtime/shared/child-guardian.mjs +1 -1
- package/src/runtime/shared/child-spawn-gate.mjs +1 -1
- package/src/runtime/shared/config.mjs +4 -4
- package/src/runtime/shared/launcher-control.mjs +9 -10
- package/src/runtime/shared/llm/index.mjs +5 -20
- package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
- package/src/runtime/shared/memory-snapshot.mjs +4 -4
- package/src/runtime/shared/pristine-execution.mjs +1 -1
- package/src/runtime/shared/process-lifecycle.mjs +0 -1
- package/src/runtime/shared/process-shutdown.mjs +1 -1
- package/src/runtime/shared/resource-admission.mjs +2 -2
- package/src/runtime/shared/schedule-session-run.mjs +1 -1
- package/src/runtime/shared/schedules-db.mjs +1 -1
- package/src/runtime/shared/service-discovery.mjs +2 -2
- package/src/runtime/shared/staged-update.mjs +6 -6
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-primitives.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +2 -2
- package/src/runtime/shared/update-checker.mjs +1 -1
- package/src/runtime/shared/user-cwd.mjs +3 -3
- package/src/runtime/shared/user-data-guard.mjs +4 -4
- package/src/runtime/shared/workspace-router.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +2 -3
- package/src/session-runtime/config-lifecycle.mjs +1 -1
- package/src/session-runtime/hitch-profile.mjs +1 -1
- package/src/session-runtime/lifecycle-api.mjs +6 -4
- package/src/session-runtime/model-recency.mjs +5 -5
- package/src/session-runtime/plugin-mcp.mjs +1 -1
- package/src/session-runtime/provider-request-snapshot.mjs +338 -0
- package/src/session-runtime/session-text.mjs +4 -4
- package/src/session-runtime/tool-catalog-schema.mjs +197 -0
- package/src/session-runtime/tool-catalog.mjs +6 -483
- package/src/session-runtime/tool-defs.mjs +1 -1
- package/src/standalone/agent-task-status.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/channel-admin.mjs +4 -4
- package/src/standalone/explore-tool.mjs +3 -3
- package/src/standalone/hook-bus/config.mjs +1 -1
- package/src/standalone/hook-bus/constants.mjs +1 -1
- package/src/standalone/hook-bus/rules.mjs +1 -1
- package/src/standalone/projects.mjs +1 -1
- package/src/standalone/provider-admin.mjs +2 -2
- package/src/tui/app/app-format.mjs +2 -2
- package/src/tui/app/clipboard.mjs +3 -3
- package/src/tui/app/input-parsers.mjs +1 -1
- package/src/tui/app/text-layout.mjs +1 -1
- package/src/tui/app/transcript-row-estimate.mjs +360 -0
- package/src/tui/app/transcript-window.mjs +5 -347
- package/src/tui/app/use-mouse-input.mjs +1 -1
- package/src/tui/app/use-prompt-handlers.mjs +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
- package/src/tui/components/tool-execution/text-format.mjs +2 -2
- package/src/tui/components/tool-output-format.mjs +7 -7
- package/src/tui/display-width.mjs +4 -4
- package/src/tui/dist/index.mjs +63 -97
- package/src/tui/engine/agent-envelope.mjs +5 -5
- package/src/tui/engine/notification-plan.mjs +1 -1
- package/src/tui/engine/queue-helpers.mjs +3 -3
- package/src/tui/engine/render-timing.mjs +4 -5
- package/src/tui/engine/session-api-ext.mjs +3 -3
- package/src/tui/engine/tool-result-status.mjs +5 -5
- package/src/tui/engine/tool-result-text.mjs +1 -1
- package/src/tui/engine/transcript-spill.mjs +585 -0
- package/src/tui/engine/tui-steering-persist.mjs +1 -1
- package/src/tui/engine.mjs +2 -474
- package/src/tui/lib/voice-setup.mjs +1 -1
- package/src/tui/markdown/format-token.mjs +1 -1
- package/src/tui/markdown/render-ansi.mjs +1 -1
- package/src/tui/markdown/table-layout.mjs +4 -4
- package/src/tui/paste-attachments.mjs +1 -1
- package/src/tui/prompt-history-store.mjs +3 -3
- package/src/tui/statusline-ansi-bridge.mjs +2 -2
- package/src/tui/theme.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +2 -2
- package/src/ui/ansi.mjs +0 -9
- package/src/ui/statusline-format.mjs +2 -4
- package/scripts/_bench-cwc.json +0 -20
- package/scripts/_jitter-fuzz.jsx +0 -42
- package/scripts/_jitter-fuzz2.jsx +0 -30
- package/scripts/_jitter-probe.jsx +0 -35
- package/scripts/_jp2.jsx +0 -16
- package/scripts/_smoke_wd.mjs +0 -7
- package/scripts/abort-recovery-test.mjs +0 -175
- package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
- package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
- package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
- package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
- package/scripts/agent-loop-policy-test.mjs +0 -37
- package/scripts/agent-model-liveness-test.mjs +0 -763
- package/scripts/agent-parallel-smoke.mjs +0 -435
- package/scripts/agent-route-batch-test.mjs +0 -40
- package/scripts/agent-tag-reuse-smoke.mjs +0 -441
- package/scripts/agent-terminal-reap-test.mjs +0 -252
- package/scripts/agent-trace-io-test.mjs +0 -133
- package/scripts/ansi-color-capability-test.mjs +0 -90
- package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
- package/scripts/anthropic-maxtokens-test.mjs +0 -119
- package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
- package/scripts/anthropic-transport-policy-test.mjs +0 -726
- package/scripts/apply-patch-edit-smoke.mjs +0 -71
- package/scripts/arg-guard-test.mjs +0 -93
- package/scripts/async-notify-settlement-test.mjs +0 -99
- package/scripts/atomic-lock-tryonce-test.mjs +0 -125
- package/scripts/background-task-meta-smoke.mjs +0 -38
- package/scripts/bench-run.mjs +0 -508
- package/scripts/boot-smoke.mjs +0 -137
- package/scripts/build-runtime-windows.ps1 +0 -242
- package/scripts/channel-daemon-smoke.mjs +0 -1103
- package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
- package/scripts/code-graph-disk-hit-test.mjs +0 -290
- package/scripts/code-graph-dispatch-test.mjs +0 -96
- package/scripts/code-graph-root-federation-test.mjs +0 -273
- package/scripts/compact-active-turn-test.mjs +0 -68
- package/scripts/compact-file-reattach-test.mjs +0 -88
- package/scripts/compact-pressure-test.mjs +0 -788
- package/scripts/compact-prior-context-flatten-test.mjs +0 -252
- package/scripts/compact-recall-digest-test.mjs +0 -57
- package/scripts/compact-smoke.mjs +0 -1017
- package/scripts/compact-trigger-migration-smoke.mjs +0 -261
- package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
- package/scripts/context-mcp-metering-test.mjs +0 -1406
- package/scripts/debounced-skills-async-save-test.mjs +0 -57
- package/scripts/deferred-tool-loading-test.mjs +0 -250
- package/scripts/desktop-session-bridge-test.mjs +0 -1013
- package/scripts/dispatch-persist-recovery-test.mjs +0 -141
- package/scripts/embedding-worker-exit-test.mjs +0 -76
- package/scripts/execution-completion-dedup-test.mjs +0 -205
- package/scripts/execution-pending-resume-kick-test.mjs +0 -151
- package/scripts/execution-resume-esc-integration-test.mjs +0 -176
- package/scripts/explore-bench-tmp.mjs +0 -36
- package/scripts/explore-bench.mjs +0 -248
- package/scripts/explore-prompt-policy-test.mjs +0 -256
- package/scripts/explore-timeout-cancel-test.mjs +0 -345
- package/scripts/find-fuzzy-hidden-test.mjs +0 -267
- package/scripts/forwarder-rebind-tail-test.mjs +0 -67
- package/scripts/freevar-smoke.mjs +0 -98
- package/scripts/gemini-provider-test.mjs +0 -1448
- package/scripts/generate-oc-icons.mjs +0 -11
- package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
- package/scripts/headless-pristine-execution-test.mjs +0 -614
- package/scripts/hook-bus-test.mjs +0 -552
- package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
- package/scripts/internal-comms-bench-test.mjs +0 -226
- package/scripts/internal-comms-bench.mjs +0 -853
- package/scripts/internal-comms-smoke.mjs +0 -277
- package/scripts/internal-tools-normalization-test.mjs +0 -62
- package/scripts/interrupted-turn-history-test.mjs +0 -399
- package/scripts/lead-workflow-smoke.mjs +0 -592
- package/scripts/lifecycle-api-test.mjs +0 -137
- package/scripts/live-share-test.mjs +0 -148
- package/scripts/live-worker-smoke.mjs +0 -333
- package/scripts/log-writer-guard-smoke.mjs +0 -131
- package/scripts/maintenance-default-routes-test.mjs +0 -164
- package/scripts/max-output-recovery-persist-test.mjs +0 -81
- package/scripts/max-output-recovery-test.mjs +0 -368
- package/scripts/mcp-client-normalization-test.mjs +0 -45
- package/scripts/mcp-grace-deferred-test.mjs +0 -225
- package/scripts/memory-core-input-test.mjs +0 -167
- package/scripts/memory-cycle-routing-test.mjs +0 -111
- package/scripts/memory-meta-concurrency-test.mjs +0 -20
- package/scripts/memory-pg-recovery-test.mjs +0 -59
- package/scripts/memory-rule-contract-test.mjs +0 -93
- package/scripts/model-list-sanitize-test.mjs +0 -37
- package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
- package/scripts/native-edit-wire-test.mjs +0 -152
- package/scripts/notify-completion-mirror-test.mjs +0 -73
- package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
- package/scripts/openai-ws-early-settle-test.mjs +0 -216
- package/scripts/output-style-bench.mjs +0 -292
- package/scripts/output-style-smoke.mjs +0 -145
- package/scripts/parent-abort-link-test.mjs +0 -90
- package/scripts/patch-binary-cache-test.mjs +0 -181
- package/scripts/path-suffix-test.mjs +0 -57
- package/scripts/pending-completion-drop-test.mjs +0 -239
- package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
- package/scripts/pretool-ask-runtime-test.mjs +0 -54
- package/scripts/process-lifecycle-test.mjs +0 -461
- package/scripts/prompt-immediate-render-test.mjs +0 -89
- package/scripts/prompt-input-parity-test.mjs +0 -145
- package/scripts/provider-admission-scheduler-test.mjs +0 -681
- package/scripts/provider-contract-test.mjs +0 -632
- package/scripts/provider-stream-stall-test.mjs +0 -276
- package/scripts/provider-toolcall-test.mjs +0 -3063
- package/scripts/reactive-compact-persist-smoke.mjs +0 -203
- package/scripts/recall-bench.mjs +0 -359
- package/scripts/resource-admission-test.mjs +0 -792
- package/scripts/result-classification-test.mjs +0 -75
- package/scripts/rg-runner-test.mjs +0 -240
- package/scripts/routing-corpus-test.mjs +0 -349
- package/scripts/sanitize-tool-pairs-test.mjs +0 -260
- package/scripts/session-bench-cache-break-test.mjs +0 -102
- package/scripts/session-bench.mjs +0 -1708
- package/scripts/session-context-bench.mjs +0 -344
- package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
- package/scripts/session-ingest-compaction-smoke.mjs +0 -279
- package/scripts/session-ingest-smoke.mjs +0 -177
- package/scripts/session-orphan-sweep-test.mjs +0 -109
- package/scripts/set-effort-config-test.mjs +0 -41
- package/scripts/shell-failure-diagnostics-test.mjs +0 -280
- package/scripts/shell-hardening-test.mjs +0 -209
- package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
- package/scripts/ship-mode-test.mjs +0 -98
- package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
- package/scripts/smoke-loop-failure-summary.mjs +0 -16
- package/scripts/smoke-loop-report.mjs +0 -221
- package/scripts/smoke-loop.mjs +0 -202
- package/scripts/smoke-runtime-negative.ps1 +0 -106
- package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
- package/scripts/steering-drain-buckets-test.mjs +0 -467
- package/scripts/steering-fold-provenance-test.mjs +0 -71
- package/scripts/streaming-tail-window-test.mjs +0 -175
- package/scripts/submit-commandbusy-race-test.mjs +0 -257
- package/scripts/task-bench.mjs +0 -205
- package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
- package/scripts/termio-input-smoke.mjs +0 -199
- package/scripts/tool-result-hook-test.mjs +0 -48
- package/scripts/tool-smoke.mjs +0 -2453
- package/scripts/tool-tui-presentation-test.mjs +0 -286
- package/scripts/toolcall-args-test.mjs +0 -239
- package/scripts/tui-ambiguous-width-test.mjs +0 -113
- package/scripts/tui-background-failure-smoke.mjs +0 -73
- package/scripts/tui-perf-run.ps1 +0 -26
- package/scripts/tui-render-smoke.mjs +0 -90
- package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
- package/scripts/tui-runtime-load-bench.mjs +0 -50
- package/scripts/tui-store-frame-batch-test.mjs +0 -99
- package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
- package/scripts/tui-transcript-perf-test.mjs +0 -934
- package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
- package/scripts/verify-release-assets-test.mjs +0 -281
- package/scripts/web-fetch-routing-test.mjs +0 -158
- package/scripts/webhook-smoke.mjs +0 -201
- package/scripts/windows-hide-spawn-options-test.mjs +0 -19
- package/scripts/worker-notify-rejection-test.mjs +0 -51
- package/scripts/write-backpressure-test.mjs +0 -147
|
@@ -54,231 +54,21 @@ export {
|
|
|
54
54
|
readSessionPresenceMtime,
|
|
55
55
|
isSessionPresenceOwnerDead,
|
|
56
56
|
} from './store/paths-heartbeat.mjs';
|
|
57
|
+
import { _readStoredSessionCached } from './store/load-cache.mjs';
|
|
58
|
+
import { _sessionForDisk, _renameWithRetrySync, _ensureLifecycleFields, _storedSessionFromFile } from './store/serialize.mjs';
|
|
59
|
+
import { _ensureSummaryCacheDataDir, _cachedSummaryRows, _setSummaryRowsCache, _cacheSessionSummary, _uncacheSessionSummary, _rollbackCachedSessionSummary, _queueSessionSummaryUpsert, _queueSessionSummaryRemoval, _queueSummaryIndexPrune, _scanStoredSessionSummaryRows, _summaryCacheVersions, _summaryCacheRemovals, _summaryRowsCache } from './store/summary-cache.mjs';
|
|
60
|
+
import { _lastSaveError, _liveSessions, _droppedSaveIds, setLiveSession, _clearLiveSession, LIVE_MEDIA_RETENTION_MS, _messagesCarryLiveMedia, getSessionSaveError, clearSessionSaveError } from './store/live-state.mjs';
|
|
61
|
+
import { _saveWorkerPending, _saveAsyncQueued, _saveAsyncInflight, _deferredSessionSaves, saveSessionAsync, saveSessionAsyncDeferred, _resetSaveWorkerBookkeeping } from './store/save-worker.mjs';
|
|
62
|
+
export { setLiveSession, getSessionSaveError, clearSessionSaveError } from './store/live-state.mjs';
|
|
63
|
+
export { saveSessionAsync, saveSessionAsyncDeferred } from './store/save-worker.mjs';
|
|
57
64
|
|
|
58
|
-
const _lastSaveError = new Map(); // id -> { message, at }
|
|
59
65
|
|
|
60
|
-
// Recent full-session reads are much hotter than writes while a user hops
|
|
61
|
-
// between conversations. Verify the file identity on every access, but reuse
|
|
62
|
-
// the parsed object while the atomic file has not changed.
|
|
63
|
-
const SESSION_LOAD_CACHE_LIMIT = 8;
|
|
64
|
-
const _sessionLoadCache = new Map(); // path → { signature, session }
|
|
65
|
-
let _sessionLoadCacheDataDir = null;
|
|
66
66
|
|
|
67
|
-
function _readStoredSessionCached(id, path) {
|
|
68
|
-
const dataDir = getPluginData();
|
|
69
|
-
if (_sessionLoadCacheDataDir !== dataDir) {
|
|
70
|
-
_sessionLoadCacheDataDir = dataDir;
|
|
71
|
-
_sessionLoadCache.clear();
|
|
72
|
-
}
|
|
73
|
-
let signature;
|
|
74
|
-
try {
|
|
75
|
-
const info = statSync(path, { bigint: true });
|
|
76
|
-
signature = `${info.dev}:${info.ino}:${info.size}:${info.mtimeNs}`;
|
|
77
|
-
} catch {
|
|
78
|
-
_sessionLoadCache.delete(path);
|
|
79
|
-
return { exists: existsSync(path), session: null };
|
|
80
|
-
}
|
|
81
|
-
const cached = _sessionLoadCache.get(path);
|
|
82
|
-
if (cached?.signature === signature) {
|
|
83
|
-
_sessionLoadCache.delete(path);
|
|
84
|
-
_sessionLoadCache.set(path, cached);
|
|
85
|
-
return { exists: true, session: cached.session };
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
const stored = JSON.parse(readFileSync(path, 'utf-8'));
|
|
89
|
-
if (stored?.id !== id) {
|
|
90
|
-
_sessionLoadCache.delete(path);
|
|
91
|
-
return { exists: true, session: null };
|
|
92
|
-
}
|
|
93
|
-
_sessionLoadCache.delete(path);
|
|
94
|
-
_sessionLoadCache.set(path, { signature, session: stored });
|
|
95
|
-
while (_sessionLoadCache.size > SESSION_LOAD_CACHE_LIMIT) {
|
|
96
|
-
const oldest = _sessionLoadCache.keys().next().value;
|
|
97
|
-
if (oldest === undefined) break;
|
|
98
|
-
_sessionLoadCache.delete(oldest);
|
|
99
|
-
}
|
|
100
|
-
return { exists: true, session: stored };
|
|
101
|
-
} catch {
|
|
102
|
-
_sessionLoadCache.delete(path);
|
|
103
|
-
return { exists: true, session: null };
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Listing is much hotter than writing, especially while the desktop session
|
|
108
|
-
// browser is open. Keep the compact sidecar in memory after the first read;
|
|
109
|
-
// local durability mutations update this cache synchronously, while an
|
|
110
|
-
// explicit refresh remains the authoritative cross-process/disk reconciliation
|
|
111
|
-
// path. Pending overlays cover a write that lands before the first listing.
|
|
112
|
-
let _summaryRowsCache = null;
|
|
113
|
-
const _summaryCacheUpserts = new Map();
|
|
114
|
-
const _summaryCacheRemovals = new Set();
|
|
115
|
-
const _summaryCacheVersions = new Map();
|
|
116
|
-
let _summaryCacheDataDir = null;
|
|
117
|
-
|
|
118
|
-
function _ensureSummaryCacheDataDir() {
|
|
119
|
-
const dataDir = getPluginData();
|
|
120
|
-
if (_summaryCacheDataDir === dataDir) return;
|
|
121
|
-
_summaryCacheDataDir = dataDir;
|
|
122
|
-
_summaryRowsCache = null;
|
|
123
|
-
_summaryScanCache.clear();
|
|
124
|
-
_summaryCacheUpserts.clear();
|
|
125
|
-
_summaryCacheRemovals.clear();
|
|
126
|
-
_summaryCacheVersions.clear();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function _summaryRowsWithLocalMutations(rows, { discardLocalMutations = false } = {}) {
|
|
130
|
-
if (discardLocalMutations) {
|
|
131
|
-
_summaryCacheUpserts.clear();
|
|
132
|
-
_summaryCacheRemovals.clear();
|
|
133
|
-
}
|
|
134
|
-
const byId = new Map(_normalizeSummaryIndex({ rows }).rows.map((row) => [row.id, row]));
|
|
135
|
-
for (const id of _summaryCacheRemovals) byId.delete(id);
|
|
136
|
-
for (const [id, row] of _summaryCacheUpserts) byId.set(id, row);
|
|
137
|
-
return [...byId.values()].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function _setSummaryRowsCache(rows, options) {
|
|
141
|
-
if (options?.discardLocalMutations === true) {
|
|
142
|
-
_summaryCacheUpserts.clear();
|
|
143
|
-
_summaryCacheRemovals.clear();
|
|
144
|
-
}
|
|
145
|
-
_summaryRowsCache = _normalizeSummaryIndex({ rows }).rows;
|
|
146
|
-
return _summaryRowsWithLocalMutations(_summaryRowsCache);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function _cachedSummaryRows() {
|
|
150
|
-
return _summaryRowsCache === null ? null : _summaryRowsWithLocalMutations(_summaryRowsCache);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function _setCachedBaseSummary(row) {
|
|
154
|
-
if (!row || _summaryRowsCache === null) return;
|
|
155
|
-
const byId = new Map(_summaryRowsCache.map((existing) => [existing.id, existing]));
|
|
156
|
-
byId.set(row.id, row);
|
|
157
|
-
_summaryRowsCache = [...byId.values()].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function _removeCachedBaseSummary(id) {
|
|
161
|
-
if (_summaryRowsCache === null) return;
|
|
162
|
-
_summaryRowsCache = _summaryRowsCache.filter((row) => row.id !== id);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function _cacheSessionSummary(session) {
|
|
166
|
-
_ensureSummaryCacheDataDir();
|
|
167
|
-
const row = _sessionSummary(session);
|
|
168
|
-
if (!row) return;
|
|
169
|
-
_summaryCacheVersions.set(row.id, (_summaryCacheVersions.get(row.id) || 0) + 1);
|
|
170
|
-
_summaryCacheRemovals.delete(row.id);
|
|
171
|
-
_summaryCacheUpserts.set(row.id, row);
|
|
172
|
-
return _summaryCacheVersions.get(row.id);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function _uncacheSessionSummary(id) {
|
|
176
|
-
_ensureSummaryCacheDataDir();
|
|
177
|
-
if (!id) return;
|
|
178
|
-
_summaryCacheVersions.set(id, (_summaryCacheVersions.get(id) || 0) + 1);
|
|
179
|
-
_summaryCacheUpserts.delete(id);
|
|
180
|
-
_summaryCacheRemovals.add(id);
|
|
181
|
-
_removeCachedBaseSummary(id);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function _rollbackCachedSessionSummary(id, version) {
|
|
185
|
-
if ((_summaryCacheVersions.get(id) || 0) !== version) return;
|
|
186
|
-
_summaryCacheUpserts.delete(id);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function _queueSessionSummaryUpsert(session, version = null) {
|
|
190
|
-
const row = _sessionSummary(session);
|
|
191
|
-
if (!row) return;
|
|
192
|
-
_setCachedBaseSummary(row);
|
|
193
|
-
if (version === null || (_summaryCacheVersions.get(row.id) || 0) === version) {
|
|
194
|
-
_summaryCacheUpserts.delete(row.id);
|
|
195
|
-
_summaryCacheRemovals.delete(row.id);
|
|
196
|
-
}
|
|
197
|
-
_upsertSessionSummary(session);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function _queueSessionSummaryRemoval(id) {
|
|
201
|
-
_uncacheSessionSummary(id);
|
|
202
|
-
_removeSessionSummary(id);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function _queueSummaryIndexPrune(ids) {
|
|
206
|
-
for (const id of ids) _uncacheSessionSummary(id);
|
|
207
|
-
_pruneSummaryIndexIds(ids);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// The live in-memory session (and every model request)
|
|
211
|
-
// retains attached image bytes across turns so multi-turn recognition works.
|
|
212
|
-
// The persisted session JSON, however, replaces image content with a short
|
|
213
|
-
// text placeholder at serialization time — keeping session files small without
|
|
214
|
-
// starving the model of the image mid-conversation. Returns the same object
|
|
215
|
-
// reference when nothing changed (no-image sessions pay only a shallow scan).
|
|
216
|
-
function _sessionForDisk(session) {
|
|
217
|
-
// Strip transient in-flight aliases askSession sets for the turn duration:
|
|
218
|
-
// - liveTurnMessages: live working transcript (so contextStatus() can
|
|
219
|
-
// estimate live context growth) — a duplicate of the working transcript
|
|
220
|
-
// that must never be serialized (mid-turn saves would bloat the file and
|
|
221
|
-
// persist a non-canonical message array).
|
|
222
|
-
// - toolApprovalHook: the askOpts.onToolApproval callback wired for the
|
|
223
|
-
// turn — a function that must never be serialized.
|
|
224
|
-
const hasTransient = session && typeof session === 'object'
|
|
225
|
-
&& (Object.prototype.hasOwnProperty.call(session, 'liveTurnMessages')
|
|
226
|
-
|| Object.prototype.hasOwnProperty.call(session, 'toolApprovalHook'));
|
|
227
|
-
const messages = Array.isArray(session?.messages) ? session.messages : null;
|
|
228
|
-
if (!messages || messages.length === 0) {
|
|
229
|
-
if (!hasTransient) return session;
|
|
230
|
-
const { liveTurnMessages: _dropLTM, toolApprovalHook: _dropTAH, ...rest } = session;
|
|
231
|
-
return rest;
|
|
232
|
-
}
|
|
233
|
-
let changed = false;
|
|
234
|
-
const out = messages.map((m) => {
|
|
235
|
-
if (!m || typeof m !== 'object') return m;
|
|
236
|
-
const content = sanitizeContentForStoredHistory(m.content);
|
|
237
|
-
if (content !== m.content) { changed = true; return { ...m, content }; }
|
|
238
|
-
return m;
|
|
239
|
-
});
|
|
240
|
-
if (!changed) {
|
|
241
|
-
if (!hasTransient) return session;
|
|
242
|
-
const { liveTurnMessages: _dropLTM, toolApprovalHook: _dropTAH, ...rest } = session;
|
|
243
|
-
return rest;
|
|
244
|
-
}
|
|
245
|
-
const { liveTurnMessages: _dropLTM, toolApprovalHook: _dropTAH, ...rest } = session;
|
|
246
|
-
return { ...rest, messages: out };
|
|
247
|
-
}
|
|
248
67
|
|
|
249
|
-
function _renameWithRetrySync(tmp, target) {
|
|
250
|
-
return renameWithRetrySync(tmp, target);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Ensure generation/closed defaults on every session object.
|
|
255
|
-
* Older persisted sessions predate these fields; we normalise at load and save.
|
|
256
|
-
*/
|
|
257
|
-
function _ensureLifecycleFields(session) {
|
|
258
|
-
if (typeof session.generation !== 'number') session.generation = 0;
|
|
259
|
-
if (typeof session.closed !== 'boolean') session.closed = false;
|
|
260
|
-
if (!Array.isArray(session.messages)) session.messages = [];
|
|
261
|
-
if (!Array.isArray(session.tools)) session.tools = [];
|
|
262
|
-
return session;
|
|
263
|
-
}
|
|
264
68
|
|
|
265
69
|
/** Module-level map tracking in-flight saves per session ID to prevent concurrent write corruption. */
|
|
266
70
|
const _savePending = new Map();
|
|
267
71
|
|
|
268
|
-
/** Same-process authoritative session snapshots (createSession → loadSession / askSession). */
|
|
269
|
-
const _liveSessions = new Map();
|
|
270
|
-
|
|
271
|
-
// Session ids whose most recent save attempt was DROPPED by the ownership
|
|
272
|
-
// guard (_shouldDrop: disk generation moved past the caller's expected
|
|
273
|
-
// generation) or failed outright. For these ids the local live snapshot is
|
|
274
|
-
// AHEAD of disk — the on-disk transcript froze at the last landed save — so
|
|
275
|
-
// it must not be evicted (data loss) nor shadowed by the stale disk copy.
|
|
276
|
-
const _droppedSaveIds = new Set();
|
|
277
|
-
|
|
278
|
-
export function setLiveSession(session) {
|
|
279
|
-
if (!session?.id) return;
|
|
280
|
-
_liveSessions.set(session.id, session);
|
|
281
|
-
}
|
|
282
72
|
|
|
283
73
|
/**
|
|
284
74
|
* Cheap authoritative lifecycle read straight from disk (no live/pending
|
|
@@ -312,9 +102,6 @@ export function readSessionHeartbeatMtime(id) {
|
|
|
312
102
|
return _heartbeatMtime(id);
|
|
313
103
|
}
|
|
314
104
|
|
|
315
|
-
function _clearLiveSession(id) {
|
|
316
|
-
if (id) _liveSessions.delete(id);
|
|
317
|
-
}
|
|
318
105
|
|
|
319
106
|
/** True while any pending/in-flight persistence still references this id. */
|
|
320
107
|
function _hasPendingPersistence(id) {
|
|
@@ -334,20 +121,6 @@ export function evictLiveSession(id) {
|
|
|
334
121
|
return _liveSessions.delete(id);
|
|
335
122
|
}
|
|
336
123
|
|
|
337
|
-
// Live snapshots that still carry raw media bytes (images are placeholder'd
|
|
338
|
-
// in the persisted JSON) stay resident for this long after their last use so
|
|
339
|
-
// multi-turn image recognition keeps working across an idle gap. Beyond the
|
|
340
|
-
// TTL the memory cost wins and the snapshot is reclaimed like any other.
|
|
341
|
-
const LIVE_MEDIA_RETENTION_MS = 60 * 60 * 1000; // 1h
|
|
342
|
-
|
|
343
|
-
function _messagesCarryLiveMedia(messages) {
|
|
344
|
-
if (!Array.isArray(messages)) return false;
|
|
345
|
-
for (const m of messages) {
|
|
346
|
-
if (!m || typeof m !== 'object') continue;
|
|
347
|
-
if (sanitizeContentForStoredHistory(m.content) !== m.content) return true;
|
|
348
|
-
}
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
124
|
|
|
352
125
|
/**
|
|
353
126
|
* Idle sweep for the same-process snapshot cache. _liveSessions previously
|
|
@@ -496,253 +269,6 @@ function _flushScheduled(id) {
|
|
|
496
269
|
});
|
|
497
270
|
}
|
|
498
271
|
|
|
499
|
-
// ── Worker-thread async save ──────────────────────────────────────────────────
|
|
500
|
-
// Single long-lived Worker serializes all saveSessionAsync calls.
|
|
501
|
-
// The worker's message queue preserves generation-race ordering.
|
|
502
|
-
let _saveWorker = null;
|
|
503
|
-
// In-flight writes, keyed by reqId. Value: { id, session, opts, waiters:[{resolve,reject}] }.
|
|
504
|
-
// At most ONE entry per session id at a time (single-in-flight-per-id).
|
|
505
|
-
let _saveWorkerPending = new Map();
|
|
506
|
-
// Latest-wins queued payload per session, keyed by id. Value: { session, opts, waiters:[] }.
|
|
507
|
-
// At most ONE queued write per id: a newer saveSessionAsync while a write is in
|
|
508
|
-
// flight overwrites session/opts here and appends its resolver to waiters, so
|
|
509
|
-
// every superseded caller resolves when this single queued write finally lands.
|
|
510
|
-
let _saveAsyncQueued = new Map();
|
|
511
|
-
// id → reqId of the in-flight write for that id (enforces one-in-flight-per-id).
|
|
512
|
-
let _saveAsyncInflight = new Map();
|
|
513
|
-
let _saveWorkerReqId = 0;
|
|
514
|
-
let _saveWorkerRefCount = 0;
|
|
515
|
-
let _deferredSaveReqId = 0;
|
|
516
|
-
const _deferredSessionSaves = new Map();
|
|
517
|
-
|
|
518
|
-
function _getOrSpawnWorker() {
|
|
519
|
-
if (_saveWorker) return _saveWorker;
|
|
520
|
-
_saveWorker = new Worker(new URL('./save-session-worker.mjs', import.meta.url), {
|
|
521
|
-
execArgv: [],
|
|
522
|
-
});
|
|
523
|
-
// Worker logs arrive as `{ __log }` messages, NOT via piped stdio
|
|
524
|
-
// (stdout:true/stderr:true): once the parent starts reading a worker's
|
|
525
|
-
// piped stdio, the underlying MessagePort stays ref'd for the worker's
|
|
526
|
-
// lifetime regardless of worker.unref(), so every process that ever
|
|
527
|
-
// saved a session could no longer exit (test runners hung after
|
|
528
|
-
// completion). The worker overrides its own process.stdout/stderr.write
|
|
529
|
-
// to forward through this channel, which keeps stray prints off the TUI
|
|
530
|
-
// frame (routed through the parent's guardable stderr) without holding
|
|
531
|
-
// the event loop.
|
|
532
|
-
_saveWorker.on('message', ({ __log, ok, saved, error, reqId }) => {
|
|
533
|
-
if (__log !== undefined) {
|
|
534
|
-
try { process.stderr.write(String(__log)); } catch { /* best-effort */ }
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
const p = _saveWorkerPending.get(reqId);
|
|
538
|
-
if (!p) return;
|
|
539
|
-
_saveWorkerPending.delete(reqId);
|
|
540
|
-
// Drop the ref AFTER pending was registered ref-up'd so the worker
|
|
541
|
-
// becomes unref'd again once all in-flight writes settle. _saveWorker
|
|
542
|
-
// null-check covers the error/exit race where the worker died first.
|
|
543
|
-
if (--_saveWorkerRefCount === 0 && _saveWorker) _saveWorker.unref();
|
|
544
|
-
const { id, session, summaryVersion, waiters } = p;
|
|
545
|
-
_saveAsyncInflight.delete(id);
|
|
546
|
-
// Resolve/reject every caller whose payload this write represents
|
|
547
|
-
// (the originating call plus any that coalesced onto it before it was
|
|
548
|
-
// posted). A supersede never lands here as a rejection — only a real
|
|
549
|
-
// worker failure does.
|
|
550
|
-
if (ok) {
|
|
551
|
-
// A close/delete may have completed while the worker was writing.
|
|
552
|
-
// Do not let this older completion put an open row back in the
|
|
553
|
-
// process-local cache after its tombstone/removal.
|
|
554
|
-
if (saved) {
|
|
555
|
-
_queueSessionSummaryUpsert(session, summaryVersion);
|
|
556
|
-
_droppedSaveIds.delete(id);
|
|
557
|
-
} else {
|
|
558
|
-
// The worker's _shouldDrop declined the write: disk ownership
|
|
559
|
-
// moved past this snapshot — flag the split-brain so eviction
|
|
560
|
-
// and disk-over-live arbitration keep the richer local copy.
|
|
561
|
-
_rollbackCachedSessionSummary(id, summaryVersion);
|
|
562
|
-
_droppedSaveIds.add(id);
|
|
563
|
-
}
|
|
564
|
-
clearSessionSaveError(id);
|
|
565
|
-
for (const w of waiters) w.resolve();
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
const e = new Error(`[session-store] worker save failed: ${error}`);
|
|
569
|
-
for (const w of waiters) w.reject(e);
|
|
570
|
-
}
|
|
571
|
-
// Promote the latest-wins queued payload (if any) into the now-free
|
|
572
|
-
// in-flight slot for this id. Runs regardless of ok: the queued write
|
|
573
|
-
// is a newer, independent payload and must still be attempted so its
|
|
574
|
-
// (possibly superseded) waiters resolve when it lands.
|
|
575
|
-
const q = _saveAsyncQueued.get(id);
|
|
576
|
-
if (q) {
|
|
577
|
-
_saveAsyncQueued.delete(id);
|
|
578
|
-
try {
|
|
579
|
-
_postAsyncWrite(id, q.session, q.opts, q.waiters, q.summaryVersion);
|
|
580
|
-
} catch (err) {
|
|
581
|
-
_rollbackCachedSessionSummary(id, q.summaryVersion);
|
|
582
|
-
for (const w of q.waiters) w.reject(err);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
_saveWorker.on('error', (err) => {
|
|
587
|
-
for (const [, p] of _saveWorkerPending) {
|
|
588
|
-
_rollbackCachedSessionSummary(p.id, p.summaryVersion);
|
|
589
|
-
for (const w of p.waiters) w.reject(err);
|
|
590
|
-
}
|
|
591
|
-
_saveWorkerPending.clear();
|
|
592
|
-
for (const [id, q] of _saveAsyncQueued) {
|
|
593
|
-
_rollbackCachedSessionSummary(id, q.summaryVersion);
|
|
594
|
-
for (const w of q.waiters) w.reject(err);
|
|
595
|
-
}
|
|
596
|
-
_saveAsyncQueued.clear();
|
|
597
|
-
_saveAsyncInflight.clear();
|
|
598
|
-
_saveWorkerRefCount = 0;
|
|
599
|
-
_saveWorker = null;
|
|
600
|
-
});
|
|
601
|
-
_saveWorker.on('exit', (code) => {
|
|
602
|
-
// Reject pending resolvers on ANY exit (code 0 included) so an idle
|
|
603
|
-
// worker that races a pending postMessage cannot leak resolvers. The
|
|
604
|
-
// map is empty on the normal idle-exit path so the loop is a no-op,
|
|
605
|
-
// but it remains safe for the race window where exit fires after
|
|
606
|
-
// saveSessionAsync registered a resolver but before the worker
|
|
607
|
-
// received the message.
|
|
608
|
-
const err = new Error(`[session-store] save worker exited with code ${code}`);
|
|
609
|
-
for (const [, p] of _saveWorkerPending) {
|
|
610
|
-
_rollbackCachedSessionSummary(p.id, p.summaryVersion);
|
|
611
|
-
for (const w of p.waiters) w.reject(err);
|
|
612
|
-
}
|
|
613
|
-
_saveWorkerPending.clear();
|
|
614
|
-
for (const [id, q] of _saveAsyncQueued) {
|
|
615
|
-
_rollbackCachedSessionSummary(id, q.summaryVersion);
|
|
616
|
-
for (const w of q.waiters) w.reject(err);
|
|
617
|
-
}
|
|
618
|
-
_saveAsyncQueued.clear();
|
|
619
|
-
_saveAsyncInflight.clear();
|
|
620
|
-
_saveWorkerRefCount = 0;
|
|
621
|
-
_saveWorker = null;
|
|
622
|
-
});
|
|
623
|
-
_saveWorker.unref(); // don't keep process alive
|
|
624
|
-
return _saveWorker;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* Post one in-flight write for `id` to the worker and register it as the
|
|
629
|
-
* single in-flight entry for that id. Callers guarantee no write is already
|
|
630
|
-
* in flight for `id`. Throws (after cleaning its own map entries) if the
|
|
631
|
-
* worker postMessage fails so the caller can reject the affected waiters.
|
|
632
|
-
*/
|
|
633
|
-
function _postAsyncWrite(id, session, opts, waiters, summaryVersion) {
|
|
634
|
-
const reqId = ++_saveWorkerReqId;
|
|
635
|
-
_saveWorkerPending.set(reqId, {
|
|
636
|
-
id,
|
|
637
|
-
session,
|
|
638
|
-
opts,
|
|
639
|
-
summaryVersion,
|
|
640
|
-
waiters,
|
|
641
|
-
});
|
|
642
|
-
_saveAsyncInflight.set(id, reqId);
|
|
643
|
-
try {
|
|
644
|
-
const w = _getOrSpawnWorker();
|
|
645
|
-
w.postMessage({ session, opts, reqId });
|
|
646
|
-
// Ref AFTER successful postMessage so a queue/throw failure path does
|
|
647
|
-
// not leave the worker held alive with no pending message. Paired with
|
|
648
|
-
// the unref in the message handler when count hits 0.
|
|
649
|
-
if (++_saveWorkerRefCount === 1) w.ref();
|
|
650
|
-
} catch (err) {
|
|
651
|
-
_saveWorkerPending.delete(reqId);
|
|
652
|
-
_saveAsyncInflight.delete(id);
|
|
653
|
-
throw err;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* Async save via a dedicated Worker thread.
|
|
659
|
-
* Errors surface as thrown Errors — callers must not silently swallow them.
|
|
660
|
-
*
|
|
661
|
-
* Per-session latest-wins coalescing: for a given id there is at most one
|
|
662
|
-
* write in flight plus one queued follow-up. N rapid saves for the same id in
|
|
663
|
-
* a turn collapse to (in-flight + one queued-latest), keeping the single
|
|
664
|
-
* worker's backlog bounded. Per-id write ORDERING is preserved (a queued write
|
|
665
|
-
* is only posted once the prior in-flight write for that id settles); different
|
|
666
|
-
* ids interleave freely as before.
|
|
667
|
-
*/
|
|
668
|
-
export function saveSessionAsync(session, opts) {
|
|
669
|
-
_ensureLifecycleFields(session);
|
|
670
|
-
setLiveSession(session);
|
|
671
|
-
const id = session.id;
|
|
672
|
-
const summaryVersion = _cacheSessionSummary(session);
|
|
673
|
-
const safeOpts = opts?._sessionWriteGuard ? opts : _guardedSaveOptions(id, opts);
|
|
674
|
-
// The Worker `postMessage` below structured-clones the whole session on the
|
|
675
|
-
// main thread. `session.liveTurnMessages` (live working transcript) and
|
|
676
|
-
// `session.toolApprovalHook` (askOpts.onToolApproval callback) are transient
|
|
677
|
-
// in-flight aliases askSession sets for the turn duration; both carry
|
|
678
|
-
// non-cloneable values (a function, and raw messages that can hold functions),
|
|
679
|
-
// which makes structuredClone throw "could not be cloned" for every mid-turn
|
|
680
|
-
// iteration save. The worker strips both via _sessionForDisk anyway, so drop
|
|
681
|
-
// them from the cloned payload here WITHOUT mutating the live session object.
|
|
682
|
-
const clonePayload = (session && typeof session === 'object'
|
|
683
|
-
&& (Object.prototype.hasOwnProperty.call(session, 'liveTurnMessages')
|
|
684
|
-
|| Object.prototype.hasOwnProperty.call(session, 'toolApprovalHook')))
|
|
685
|
-
? (() => { const { liveTurnMessages: _dropLTM, toolApprovalHook: _dropTAH, ...rest } = session; return rest; })()
|
|
686
|
-
: session;
|
|
687
|
-
return new Promise((resolve, reject) => {
|
|
688
|
-
const waiter = { resolve, reject };
|
|
689
|
-
if (_saveAsyncInflight.has(id)) {
|
|
690
|
-
// A write is already on disk for this id — coalesce into the single
|
|
691
|
-
// latest-wins queued slot. Existing queued waiters carry over so a
|
|
692
|
-
// superseded caller resolves when THIS newer write lands (never
|
|
693
|
-
// hang, never reject on supersede).
|
|
694
|
-
const q = _saveAsyncQueued.get(id);
|
|
695
|
-
if (q) {
|
|
696
|
-
q.session = clonePayload;
|
|
697
|
-
q.opts = safeOpts;
|
|
698
|
-
q.summaryVersion = summaryVersion;
|
|
699
|
-
q.waiters.push(waiter);
|
|
700
|
-
} else {
|
|
701
|
-
_saveAsyncQueued.set(id, {
|
|
702
|
-
session: clonePayload,
|
|
703
|
-
opts: safeOpts,
|
|
704
|
-
summaryVersion,
|
|
705
|
-
waiters: [waiter],
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
// Idle for this id — post immediately as the in-flight write. The
|
|
711
|
-
// in-flight entry persists {session, opts} so drainSessionStore can
|
|
712
|
-
// sync-flush outstanding writes if process exit interrupts the queue.
|
|
713
|
-
try {
|
|
714
|
-
_postAsyncWrite(id, clonePayload, safeOpts, [waiter], summaryVersion);
|
|
715
|
-
} catch (err) {
|
|
716
|
-
_rollbackCachedSessionSummary(id, summaryVersion);
|
|
717
|
-
reject(err);
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Register a save for the exit drain now, but yield one check phase before
|
|
724
|
-
* Worker.postMessage performs its main-thread structured clone.
|
|
725
|
-
*/
|
|
726
|
-
export function saveSessionAsyncDeferred(session, opts) {
|
|
727
|
-
_ensureLifecycleFields(session);
|
|
728
|
-
setLiveSession(session);
|
|
729
|
-
_cacheSessionSummary(session);
|
|
730
|
-
const reqId = ++_deferredSaveReqId;
|
|
731
|
-
return new Promise((resolve, reject) => {
|
|
732
|
-
_deferredSessionSaves.set(reqId, {
|
|
733
|
-
session,
|
|
734
|
-
opts: _guardedSaveOptions(session.id, opts),
|
|
735
|
-
resolve,
|
|
736
|
-
reject,
|
|
737
|
-
});
|
|
738
|
-
setImmediate(() => {
|
|
739
|
-
const pending = _deferredSessionSaves.get(reqId);
|
|
740
|
-
if (!pending) return;
|
|
741
|
-
_deferredSessionSaves.delete(reqId);
|
|
742
|
-
saveSessionAsync(pending.session, pending.opts).then(resolve, reject);
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
272
|
|
|
747
273
|
/**
|
|
748
274
|
* Exported for save-session-worker — not part of the public API.
|
|
@@ -897,9 +423,7 @@ export function drainSessionStore() {
|
|
|
897
423
|
}
|
|
898
424
|
_deferredSessionSaves.clear();
|
|
899
425
|
_saveWorkerPending.clear();
|
|
900
|
-
|
|
901
|
-
_saveAsyncInflight.clear();
|
|
902
|
-
_saveWorkerRefCount = 0;
|
|
426
|
+
_resetSaveWorkerBookkeeping();
|
|
903
427
|
}
|
|
904
428
|
|
|
905
429
|
function _drainQueue(id) {
|
|
@@ -1179,17 +703,6 @@ export function loadSession(id) {
|
|
|
1179
703
|
return stored ? _ensureLifecycleFields(stored) : null;
|
|
1180
704
|
}
|
|
1181
705
|
|
|
1182
|
-
/**
|
|
1183
|
-
* Returns the last save error for a session id, or null if no error has occurred.
|
|
1184
|
-
* Shape: { message: string, at: number } | null
|
|
1185
|
-
*/
|
|
1186
|
-
export function getSessionSaveError(id) {
|
|
1187
|
-
return _lastSaveError.get(id) ?? null;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
export function clearSessionSaveError(id) {
|
|
1191
|
-
_lastSaveError.delete(id);
|
|
1192
|
-
}
|
|
1193
706
|
|
|
1194
707
|
export function deleteSession(id, options = {}) {
|
|
1195
708
|
// Keep caller probes and all vetoes ahead of the non-reentrant lock and
|
|
@@ -1252,18 +765,6 @@ const RESUMABLE_OPEN_MAX_COUNT = 300;
|
|
|
1252
765
|
// idle this long — see the blank-scratch branch in sweepStaleSessions.
|
|
1253
766
|
const BLANK_SCRATCH_MAX_AGE_MS = 60 * 60 * 1000; // 1h
|
|
1254
767
|
|
|
1255
|
-
function _storedSessionFromFile(dir, filename, ensureLifecycle = true) {
|
|
1256
|
-
if (!filename.endsWith('.json')) return null;
|
|
1257
|
-
const storageId = filename.slice(0, -5);
|
|
1258
|
-
if (!storageId || !/^[A-Za-z0-9_-]+$/.test(storageId)) return null;
|
|
1259
|
-
try {
|
|
1260
|
-
const session = JSON.parse(readFileSync(join(dir, filename), 'utf-8'));
|
|
1261
|
-
if (session?.id !== storageId) return null;
|
|
1262
|
-
return ensureLifecycle ? _ensureLifecycleFields(session) : session;
|
|
1263
|
-
} catch {
|
|
1264
|
-
return null;
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
768
|
|
|
1268
769
|
export function listStoredSessions(options = {}) {
|
|
1269
770
|
const dir = getStoreDir();
|
|
@@ -1312,59 +813,6 @@ function _withUnpersistedSessions(stored, invalidStorageIds = new Set()) {
|
|
|
1312
813
|
return [...sessionsById.values()].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
1313
814
|
}
|
|
1314
815
|
|
|
1315
|
-
// ── Incremental storage scan ────────────────────────────────────────────────
|
|
1316
|
-
// refreshFromStorage used to re-parse EVERY session JSON (full transcripts,
|
|
1317
|
-
// multi-MB files) on each desktop sidebar refresh. A summary only changes when
|
|
1318
|
-
// its file changes, so key a per-file row cache on (mtimeMs, size): unchanged
|
|
1319
|
-
// files reuse the cached row, changed/new files re-parse, vanished files drop
|
|
1320
|
-
// out. Storage stays the truth source — the sidecar index is never trusted.
|
|
1321
|
-
const _summaryScanCache = new Map(); // filename → { mtimeMs, size, row|null }
|
|
1322
|
-
|
|
1323
|
-
function _scanStoredSessionSummaryRows() {
|
|
1324
|
-
const dir = getStoreDir();
|
|
1325
|
-
if (!existsSync(dir)) {
|
|
1326
|
-
const changed = _summaryScanCache.size > 0;
|
|
1327
|
-
_summaryScanCache.clear();
|
|
1328
|
-
return { rows: [], invalidStorageIds: new Set(), changed };
|
|
1329
|
-
}
|
|
1330
|
-
const files = readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
1331
|
-
const present = new Set(files);
|
|
1332
|
-
let changed = false;
|
|
1333
|
-
for (const key of [..._summaryScanCache.keys()]) {
|
|
1334
|
-
if (!present.has(key)) {
|
|
1335
|
-
_summaryScanCache.delete(key);
|
|
1336
|
-
changed = true;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
const rows = [];
|
|
1340
|
-
const invalidStorageIds = new Set();
|
|
1341
|
-
const markInvalid = (filename) => {
|
|
1342
|
-
const storageId = filename.slice(0, -5);
|
|
1343
|
-
if (/^[A-Za-z0-9_-]+$/.test(storageId)) invalidStorageIds.add(storageId);
|
|
1344
|
-
};
|
|
1345
|
-
for (const f of files) {
|
|
1346
|
-
let fileStat = null;
|
|
1347
|
-
try { fileStat = statSync(join(dir, f)); } catch { /* deleted mid-scan */ }
|
|
1348
|
-
if (!fileStat) {
|
|
1349
|
-
if (_summaryScanCache.delete(f)) changed = true;
|
|
1350
|
-
continue;
|
|
1351
|
-
}
|
|
1352
|
-
const cached = _summaryScanCache.get(f);
|
|
1353
|
-
if (cached && cached.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) {
|
|
1354
|
-
if (cached.row) rows.push(cached.row);
|
|
1355
|
-
else markInvalid(f);
|
|
1356
|
-
continue;
|
|
1357
|
-
}
|
|
1358
|
-
const session = _storedSessionFromFile(dir, f);
|
|
1359
|
-
const row = session ? _sessionSummary(session) : null;
|
|
1360
|
-
_summaryScanCache.set(f, { mtimeMs: fileStat.mtimeMs, size: fileStat.size, row });
|
|
1361
|
-
changed = true;
|
|
1362
|
-
if (row) rows.push(row);
|
|
1363
|
-
else markInvalid(f);
|
|
1364
|
-
}
|
|
1365
|
-
rows.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
1366
|
-
return { rows, invalidStorageIds, changed };
|
|
1367
|
-
}
|
|
1368
816
|
|
|
1369
817
|
// Summary-level twin of _withUnpersistedSessions: overlay queued/in-flight
|
|
1370
818
|
// saves that have no disk record yet (read-your-writes for brand-new sessions).
|
|
@@ -1385,7 +833,7 @@ function _overlayUnpersistedSummaryRows(rows, invalidStorageIds = new Set()) {
|
|
|
1385
833
|
return [...byId.values()].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
1386
834
|
}
|
|
1387
835
|
|
|
1388
|
-
|
|
836
|
+
function rebuildSessionSummaryIndex() {
|
|
1389
837
|
_ensureSummaryCacheDataDir();
|
|
1390
838
|
const { rows } = _scanStoredSessionSummaryRows();
|
|
1391
839
|
return _setSummaryRowsCache(_writeSummaryIndex(rows));
|
|
@@ -10,7 +10,7 @@ const TOOL_RESULT_PREVIEW_CHARS = 2_000;
|
|
|
10
10
|
const TOOL_RESULT_SHELL_THRESHOLD_CHARS = 30_000;
|
|
11
11
|
const TOOL_RESULT_SEARCH_THRESHOLD_CHARS = 50_000;
|
|
12
12
|
const TOOL_RESULT_GREP_THRESHOLD_CHARS = 20_000;
|
|
13
|
-
|
|
13
|
+
const TOOL_RESULT_OFFLOAD_PREFIX = '[tool output offloaded:';
|
|
14
14
|
const OFFLOAD_PRUNE_MIN_AGE_MS = 10 * 60 * 1000;
|
|
15
15
|
|
|
16
16
|
// Per-tool persistence limits mirror reference per-tool maxResultSizeChars
|
|
@@ -193,7 +193,7 @@ export function isOffloadedToolResultText(text) {
|
|
|
193
193
|
return typeof text === 'string' && text.startsWith(TOOL_RESULT_OFFLOAD_PREFIX);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
function compactOffloadedToolResultText(text) {
|
|
197
197
|
if (!isOffloadedToolResultText(text)) return text;
|
|
198
198
|
const value = String(text);
|
|
199
199
|
const lineEnd = value.indexOf('\n');
|