mixdog 1.3.5 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +35 -9
- package/src/defaults/skills/setup/SKILL.md +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +10 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +7 -1
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +10 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +142 -190
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +17 -1
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +19 -2
- package/src/tui/session/turn.mjs +1 -0
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -39,6 +39,8 @@ import { join } from 'node:path';
|
|
|
39
39
|
import { getPluginData } from '../../config.mjs';
|
|
40
40
|
import { writeFileAtomicSync } from '../../../../shared/atomic-file.mjs';
|
|
41
41
|
import { sanitizeContentForStoredHistory } from '../../providers/media-normalization.mjs';
|
|
42
|
+
import { redactSettledText, sensitiveValuesVersion } from '../sensitive-values.mjs';
|
|
43
|
+
import { redactSensitiveContent, redactSensitiveMessage, redactSensitiveToolCall } from '../sensitive-projection.mjs';
|
|
42
44
|
import { promptContentText } from './prompt-utils.mjs';
|
|
43
45
|
|
|
44
46
|
const TURN_CHECKPOINT_VERSION = 1;
|
|
@@ -96,7 +98,7 @@ export function checkpointMessage(message) {
|
|
|
96
98
|
return content === message.content ? message : { ...message, content };
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
function matchingUserMessage(message, currentUserContent) {
|
|
100
102
|
return (
|
|
101
103
|
message?.role === 'user' &&
|
|
102
104
|
(message.content === currentUserContent ||
|
|
@@ -131,16 +133,54 @@ export function emptyInterruptionSnapshot() {
|
|
|
131
133
|
};
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
//
|
|
136
|
+
// Observed tool calls ([id, { call, eagerStarted }]) and early tool results
|
|
137
|
+
// ([id, tool message]) as a checkpoint stores them (sensitive-projection.mjs).
|
|
138
|
+
const storedCallEntries = (sessionId, entries) =>
|
|
139
|
+
entries.map(([id, entry]) => {
|
|
140
|
+
const call = redactSensitiveToolCall(sessionId, entry?.call);
|
|
141
|
+
return [id, call === entry?.call ? entry : { ...entry, call }];
|
|
142
|
+
});
|
|
143
|
+
const storedResultEntries = (sessionId, entries) =>
|
|
144
|
+
entries.map(([id, message]) => [id, redactSensitiveMessage(sessionId, message)]);
|
|
145
|
+
|
|
146
|
+
/** Interruption snapshot `snapshot` as `sessionId`'s checkpoint stores it:
|
|
147
|
+
* sensitive values (sensitive-values.mjs) replaced, and of the partial text
|
|
148
|
+
* only what no value can still complete. */
|
|
149
|
+
function redactInterruptionSnapshot(sessionId, snapshot) {
|
|
150
|
+
const source = snapshot ?? emptyInterruptionSnapshot();
|
|
151
|
+
if (!sensitiveValuesVersion(sessionId)) return source;
|
|
152
|
+
return {
|
|
153
|
+
...source,
|
|
154
|
+
partialAssistantContent: redactSettledText(sessionId, String(source.partialAssistantContent || '')),
|
|
155
|
+
partialReasoningContent: redactSettledText(sessionId, String(source.partialReasoningContent || '')),
|
|
156
|
+
observedToolCalls: storedCallEntries(
|
|
157
|
+
sessionId,
|
|
158
|
+
Array.isArray(source.observedToolCalls) ? source.observedToolCalls : []
|
|
159
|
+
),
|
|
160
|
+
observedToolResults: storedResultEntries(
|
|
161
|
+
sessionId,
|
|
162
|
+
Array.isArray(source.observedToolResults) ? source.observedToolResults : []
|
|
163
|
+
),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── Header (written once per turn, again when a sensitive value is marked) ──
|
|
135
168
|
|
|
136
169
|
export function writeCheckpointHeader(checkpoint) {
|
|
137
|
-
const
|
|
170
|
+
const { sessionId } = checkpoint;
|
|
171
|
+
const target = turnCheckpointPath(sessionId);
|
|
138
172
|
const payload = {
|
|
139
173
|
...checkpoint,
|
|
140
174
|
version: TURN_CHECKPOINT_VERSION,
|
|
141
|
-
currentUserContent:
|
|
142
|
-
|
|
143
|
-
|
|
175
|
+
currentUserContent: redactSensitiveContent(
|
|
176
|
+
sessionId,
|
|
177
|
+
sanitizeContentForStoredHistory(checkpoint.currentUserContent)
|
|
178
|
+
),
|
|
179
|
+
turnMessages: (Array.isArray(checkpoint.turnMessages) ? checkpoint.turnMessages : []).map((message) =>
|
|
180
|
+
redactSensitiveMessage(sessionId, checkpointMessage(message))
|
|
181
|
+
),
|
|
182
|
+
interruption: redactInterruptionSnapshot(sessionId, checkpoint.interruption),
|
|
183
|
+
// Counts, token readings and signatures only: no text.
|
|
144
184
|
contextState: checkpoint.contextState ?? null,
|
|
145
185
|
updatedAt: Date.now(),
|
|
146
186
|
};
|
|
@@ -488,7 +528,15 @@ export function replayTurnJournal(header) {
|
|
|
488
528
|
if (seq > expected) break;
|
|
489
529
|
expected = seq + 1;
|
|
490
530
|
if (!head) {
|
|
491
|
-
|
|
531
|
+
// A header rewritten for a newly marked sensitive value starts a new
|
|
532
|
+
// journal epoch; the journal it replaces never replays onto it.
|
|
533
|
+
if (
|
|
534
|
+
record.t !== 'h' ||
|
|
535
|
+
record.turnToken !== header.turnToken ||
|
|
536
|
+
(Number(record.e) || 0) !== (Number(header.journalEpoch) || 0)
|
|
537
|
+
) {
|
|
538
|
+
return header;
|
|
539
|
+
}
|
|
492
540
|
head = true;
|
|
493
541
|
continue;
|
|
494
542
|
}
|
|
@@ -529,7 +577,7 @@ export function replayTurnJournal(header) {
|
|
|
529
577
|
* turn-relative index) plus an opaque interruption cursor, so each encode()
|
|
530
578
|
* touches only what changed since the previous flush.
|
|
531
579
|
*/
|
|
532
|
-
export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
580
|
+
export function createTurnJournalEncoder({ turnToken, sessionId = null } = {}) {
|
|
533
581
|
let journaled = [];
|
|
534
582
|
let fullTranscript = false;
|
|
535
583
|
let cursor = null;
|
|
@@ -537,27 +585,65 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
|
537
585
|
// Per-turn record sequence. It is assigned where records are produced, so
|
|
538
586
|
// the number line is exactly the enqueue order the write lane preserves.
|
|
539
587
|
let seq = 0;
|
|
588
|
+
// Set while the session has sensitive values (sensitive-values.mjs): the
|
|
589
|
+
// tracker's state as its deltas build it (`raw`) and the state the journal
|
|
590
|
+
// replays to (`written`). Interruption deltas are then journaled as the
|
|
591
|
+
// difference of the redacted text, so a value that streams in across
|
|
592
|
+
// flushes never lands in pieces. A newly marked value reseeds the encoder
|
|
593
|
+
// (turn-checkpoint.mjs), so the session's values are known whenever this is null.
|
|
594
|
+
let mirror = null;
|
|
540
595
|
const line = (record) => {
|
|
541
596
|
const stamped = { ...record, s: seq };
|
|
542
597
|
seq += 1;
|
|
543
598
|
return `${JSON.stringify(stamped)}\n`;
|
|
544
599
|
};
|
|
600
|
+
const stored = (message) => redactSensitiveMessage(sessionId, checkpointMessage(message));
|
|
601
|
+
const interruptionRecord = (delta) => {
|
|
602
|
+
if (!mirror) return delta;
|
|
603
|
+
applyInterruptionDelta(mirror.raw, delta);
|
|
604
|
+
const out = {};
|
|
605
|
+
for (const key of ['rs', 'ph', 'cc', 'od']) if (Object.hasOwn(delta, key)) out[key] = delta[key];
|
|
606
|
+
for (const [field, setKey, appendKey] of [
|
|
607
|
+
['text', 'ts', 'ta'],
|
|
608
|
+
['reasoning', 'qs', 'qa'],
|
|
609
|
+
]) {
|
|
610
|
+
const settled = redactSettledText(sessionId, mirror.raw[field]);
|
|
611
|
+
const written = mirror.written[field];
|
|
612
|
+
if (settled === written) continue;
|
|
613
|
+
if (settled.startsWith(written)) out[appendKey] = settled.slice(written.length);
|
|
614
|
+
else out[setKey] = settled;
|
|
615
|
+
}
|
|
616
|
+
if (Object.hasOwn(delta, 'tb')) out.tb = redactSensitiveContent(sessionId, mirror.raw.tomb);
|
|
617
|
+
if (delta.cs) out.cs = storedCallEntries(sessionId, delta.cs);
|
|
618
|
+
if (delta.os) out.os = storedResultEntries(sessionId, delta.os);
|
|
619
|
+
applyInterruptionDelta(mirror.written, out);
|
|
620
|
+
return out;
|
|
621
|
+
};
|
|
545
622
|
return {
|
|
546
623
|
/** Seed from the turn's opening state; returns the header's messages
|
|
547
624
|
* and the journal's opening lines (head record, plus — defensively —
|
|
548
625
|
* the delta that makes the journal authoritative if the tracker already
|
|
549
|
-
* carried state at open time).
|
|
550
|
-
|
|
626
|
+
* carried state at open time). `snapshot` is the tracker snapshot the
|
|
627
|
+
* header stores; `epoch` names the journal a rewritten header starts. */
|
|
628
|
+
seed(turnOutgoing, currentUserContent, interruption, contextState = null, { snapshot = null, epoch = 0 } = {}) {
|
|
551
629
|
const source = Array.isArray(turnOutgoing) ? turnOutgoing : [];
|
|
552
630
|
const start = findTurnStart(source, currentUserContent);
|
|
553
631
|
fullTranscript = start < 0;
|
|
554
632
|
journaled = fullTranscript ? source.slice() : source.slice(start);
|
|
633
|
+
mirror = sensitiveValuesVersion(sessionId)
|
|
634
|
+
? {
|
|
635
|
+
raw: interruptionStateFromSnapshot(snapshot),
|
|
636
|
+
written: interruptionStateFromSnapshot(redactInterruptionSnapshot(sessionId, snapshot)),
|
|
637
|
+
}
|
|
638
|
+
: null;
|
|
555
639
|
const seeded = typeof interruption?.journalDelta === 'function' ? interruption.journalDelta(null) : null;
|
|
556
640
|
cursor = seeded ? seeded.cursor : null;
|
|
557
641
|
contextStateKey = contextState == null ? null : JSON.stringify(contextState);
|
|
558
642
|
seq = 0;
|
|
559
|
-
const openLines = [line({ t: 'h', turnToken, at: Date.now() })];
|
|
560
|
-
|
|
643
|
+
const openLines = [line({ t: 'h', turnToken, ...(epoch ? { e: epoch } : {}), at: Date.now() })];
|
|
644
|
+
const seededDelta = seeded?.changed ? interruptionRecord(seeded.delta) : null;
|
|
645
|
+
if (seededDelta && Object.keys(seededDelta).length)
|
|
646
|
+
openLines.push(line({ t: 'x', d: seededDelta, at: Date.now() }));
|
|
561
647
|
return {
|
|
562
648
|
turnMessages: journaled.map(checkpointMessage),
|
|
563
649
|
fullTranscript,
|
|
@@ -579,7 +665,7 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
|
579
665
|
journaled = source.slice();
|
|
580
666
|
records.push({
|
|
581
667
|
t: 'full',
|
|
582
|
-
ms: journaled.map(
|
|
668
|
+
ms: journaled.map(stored),
|
|
583
669
|
});
|
|
584
670
|
} else {
|
|
585
671
|
const offset = fullTranscript ? 0 : start;
|
|
@@ -588,7 +674,7 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
|
588
674
|
const raw = source[offset + index];
|
|
589
675
|
if (journaled[index] === raw) continue;
|
|
590
676
|
journaled[index] = raw;
|
|
591
|
-
records.push({ t: 'm', i: index, m:
|
|
677
|
+
records.push({ t: 'm', i: index, m: stored(raw) });
|
|
592
678
|
}
|
|
593
679
|
if (journaled.length > length) {
|
|
594
680
|
journaled.length = length;
|
|
@@ -598,7 +684,8 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
|
|
|
598
684
|
if (typeof interruption?.journalDelta === 'function') {
|
|
599
685
|
const next = interruption.journalDelta(cursor);
|
|
600
686
|
cursor = next.cursor;
|
|
601
|
-
|
|
687
|
+
const delta = next.changed ? interruptionRecord(next.delta) : null;
|
|
688
|
+
if (delta && Object.keys(delta).length) records.push({ t: 'x', d: delta });
|
|
602
689
|
}
|
|
603
690
|
const nextContextStateKey = contextState == null ? null : JSON.stringify(contextState);
|
|
604
691
|
if (nextContextStateKey !== contextStateKey) {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from './turn-checkpoint-journal.mjs';
|
|
20
20
|
import { captureTurnCheckpointContextState, restoreTurnCheckpointContextState } from './turn-checkpoint-context.mjs';
|
|
21
21
|
import { stripProviderReplayMessages } from './provider-replay.mjs';
|
|
22
|
+
import { sensitiveValuesVersion } from '../sensitive-values.mjs';
|
|
22
23
|
|
|
23
24
|
export { turnCheckpointPath, turnMessagesForCheckpoint };
|
|
24
25
|
export { captureTurnCheckpointContextState, restoreTurnCheckpointContextState };
|
|
@@ -67,9 +68,14 @@ export function projectTurnCheckpointMessages(session, checkpoint) {
|
|
|
67
68
|
// long tool-heavy turn no longer amplifies into the shared engine thread that
|
|
68
69
|
// every fanned-out agent session shares.
|
|
69
70
|
export function createTurnCheckpointRecorder({ sessionId, generation, turnToken, startedAt }) {
|
|
70
|
-
const encoder = createTurnJournalEncoder({ turnToken });
|
|
71
|
+
const encoder = createTurnJournalEncoder({ turnToken, sessionId });
|
|
71
72
|
let opened = false;
|
|
72
73
|
let stopped = false;
|
|
74
|
+
// The sensitive-values version (sensitive-values.mjs) the header and
|
|
75
|
+
// journal were written under. A newly marked value rewrites both from the
|
|
76
|
+
// turn's current state with the value replaced, under a new journal epoch,
|
|
77
|
+
// so no recovery record keeps it.
|
|
78
|
+
let openedVersion = 0;
|
|
73
79
|
return {
|
|
74
80
|
get opened() {
|
|
75
81
|
return opened;
|
|
@@ -77,8 +83,14 @@ export function createTurnCheckpointRecorder({ sessionId, generation, turnToken,
|
|
|
77
83
|
/** True when this call wrote or queued durable state. */
|
|
78
84
|
record({ currentUserContent, turnOutgoing, interruption, contextState = null }) {
|
|
79
85
|
if (stopped || !sessionId || !turnToken) return false;
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
const version = sensitiveValuesVersion(sessionId);
|
|
87
|
+
if (!opened || version !== openedVersion) {
|
|
88
|
+
const snapshot =
|
|
89
|
+
typeof interruption?.snapshot === 'function' ? interruption.snapshot() : emptyInterruptionSnapshot();
|
|
90
|
+
const seed = encoder.seed(turnOutgoing, currentUserContent, interruption, contextState, {
|
|
91
|
+
snapshot,
|
|
92
|
+
epoch: version,
|
|
93
|
+
});
|
|
82
94
|
writeCheckpointHeader({
|
|
83
95
|
sessionId,
|
|
84
96
|
generation,
|
|
@@ -87,10 +99,11 @@ export function createTurnCheckpointRecorder({ sessionId, generation, turnToken,
|
|
|
87
99
|
currentUserContent,
|
|
88
100
|
turnMessages: seed.turnMessages,
|
|
89
101
|
...(seed.fullTranscript ? { fullTranscript: true } : {}),
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
...(version ? { journalEpoch: version } : {}),
|
|
103
|
+
interruption: snapshot,
|
|
92
104
|
contextState: seed.contextState,
|
|
93
105
|
});
|
|
106
|
+
openedVersion = version;
|
|
94
107
|
// Opening the journal is part of the anchor: a leftover journal
|
|
95
108
|
// from an earlier turn of this session must never replay onto
|
|
96
109
|
// the new header. It lands synchronously when the lane is idle
|
|
@@ -211,6 +224,9 @@ export function recoverTurnCheckpoint(session) {
|
|
|
211
224
|
// mark it unanchored so the first resumed send cannot compact from a gross
|
|
212
225
|
// whole-transcript estimate.
|
|
213
226
|
const contextStateRestored = restoreTurnCheckpointContextState(session, checkpoint);
|
|
227
|
+
// Diagnostic only (anthropic-prefix-trace.mjs): the turn's last settled
|
|
228
|
+
// request, whatever the context reading's alignment.
|
|
229
|
+
if (checkpoint.contextState?.prefixBaseline) session.providerPrefixBaseline = checkpoint.contextState.prefixBaseline;
|
|
214
230
|
delete session.activeTurnCheckpoint;
|
|
215
231
|
delete session.providerState;
|
|
216
232
|
delete session._providerPrefixGuardState;
|
|
@@ -110,6 +110,15 @@ function finalizeInterruptedTurn({
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// Killed with a request on the wire (phase 'streaming' after a tool run):
|
|
114
|
+
// the provider received the transcript up to its last message, and may
|
|
115
|
+
// have cached it. Stamping that message keeps what this interruption
|
|
116
|
+
// appends (the marker, the next prompt) out of the tool_result it ends —
|
|
117
|
+
// see toAnthropicMessages. The stamp is meta only; no wire bytes change.
|
|
118
|
+
const sentTail = messages.length - 1;
|
|
119
|
+
if (phase === 'streaming' && (messages[sentTail]?.role === 'tool' || messages[sentTail]?.role === 'user')) {
|
|
120
|
+
messages[sentTail] = { ...messages[sentTail], meta: { ...messages[sentTail].meta, sentRequestTail: true } };
|
|
121
|
+
}
|
|
113
122
|
appendObservedTurnOutput(messages, {
|
|
114
123
|
partialAssistantContent,
|
|
115
124
|
partialReasoningContent,
|
|
@@ -30,7 +30,7 @@ import { traceAgentCompact, messagePrefixHash } from '../agent-trace.mjs';
|
|
|
30
30
|
import { invalidateProviderRequestToolsScope } from '../runtime-core/provider-request-tools.mjs';
|
|
31
31
|
import { bumpUsageMetricsEpoch } from './manager/usage-metrics.mjs';
|
|
32
32
|
import { resetReadStateAfterCompaction } from './read-dedup.mjs';
|
|
33
|
-
import {
|
|
33
|
+
import { prepareCompactionArchive, stampCompactionMessageIds } from './compaction-archive-capture.mjs';
|
|
34
34
|
import { writeStderrBestEffort } from '../../../shared/best-effort-stderr.mjs';
|
|
35
35
|
import { isAgentModeSession } from './agent-mode.mjs';
|
|
36
36
|
import { applyPendingWorkspaceReroot } from './manager/prompt-utils.mjs';
|
|
@@ -92,7 +92,11 @@ function preSendCompactDecision(state, compactPolicy) {
|
|
|
92
92
|
messages,
|
|
93
93
|
sessionRef,
|
|
94
94
|
});
|
|
95
|
-
const cacheExpired = shouldCompactForExpiredAgentCache(sessionRef, state.opts
|
|
95
|
+
const cacheExpired = shouldCompactForExpiredAgentCache(sessionRef, state.opts, {
|
|
96
|
+
provider: sessionRef?.provider || state.provider?.name,
|
|
97
|
+
model: state.model,
|
|
98
|
+
messages,
|
|
99
|
+
});
|
|
96
100
|
const mediaPressure = shouldCompactForRequestMedia(messages, sessionRef?.provider);
|
|
97
101
|
const requireReduction = !reactivePending && !mediaPressure;
|
|
98
102
|
let shouldCompact =
|
|
@@ -409,19 +413,31 @@ function adoptCompactedTranscript(ctx, run) {
|
|
|
409
413
|
const { messages, sessionRef, sessionId, opts } = state;
|
|
410
414
|
const replaced = messages.slice();
|
|
411
415
|
if (sessionRef) backfillFallbackSessionTitle(sessionRef, replaced);
|
|
416
|
+
// Display-only archive of the dropped conversation, made durable (pending
|
|
417
|
+
// sidecar) BEFORE the shortened transcript is adopted and can be saved; a
|
|
418
|
+
// sidecar that cannot be written keeps the old transcript and fails this
|
|
419
|
+
// compaction. The publication runs off this synchronous adoption.
|
|
420
|
+
let prepared = null;
|
|
421
|
+
if (sessionRef) {
|
|
422
|
+
try {
|
|
423
|
+
prepared = prepareCompactionArchive({ session: sessionRef, sessionId, before: replaced, after: run.compacted });
|
|
424
|
+
} catch (archiveError) {
|
|
425
|
+
throwCompactFailure(ctx, run, archiveError);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
412
428
|
messages.length = 0;
|
|
413
429
|
messages.push(...run.compacted);
|
|
414
|
-
|
|
415
|
-
// write run off this synchronous adoption.
|
|
416
|
-
const archived = sessionRef
|
|
417
|
-
? scheduleCompactionArchive({ session: sessionRef, sessionId, before: replaced, after: messages })
|
|
418
|
-
: null;
|
|
430
|
+
const archived = prepared ? { ...prepared, written: prepared.start() } : null;
|
|
419
431
|
ctx.droppedMessages = archived?.dropped ?? 0;
|
|
420
432
|
ctx.droppedIdentities = archived?.identities ?? null;
|
|
421
433
|
acknowledgeGoalReminder(sessionRef, run);
|
|
422
434
|
// The compacted transcript breaks the cache anyway: a staged working-folder
|
|
423
435
|
// change re-roots the system prompt now and needs no notice any more.
|
|
424
436
|
if (sessionRef) applyPendingWorkspaceReroot(sessionRef, messages);
|
|
437
|
+
// The system prompt is rebuilt from current sources outside a running turn
|
|
438
|
+
// (session-runtime agent-profile-fold.mjs): the next cold point folds it in
|
|
439
|
+
// instead of adding a break to the turn that is already warm again.
|
|
440
|
+
if (sessionRef) sessionRef.promptFoldPending = true;
|
|
425
441
|
resetReadStateAfterCompaction(sessionId);
|
|
426
442
|
// Cross-turn receipts reference tool results that compaction just removed
|
|
427
443
|
// from context; a stub for them would point at nothing. Clear in place so
|
|
@@ -444,8 +460,7 @@ function adoptCompactedTranscript(ctx, run) {
|
|
|
444
460
|
ctx.providerStateCleared = true;
|
|
445
461
|
// Compaction starts a new loop epoch: the iteration counter restarts at 0
|
|
446
462
|
// so iteration indices (traces, usage metrics, cross-turn dedup windows)
|
|
447
|
-
// count from the compacted transcript.
|
|
448
|
-
// loop has no iteration cap.
|
|
463
|
+
// count from the compacted transcript.
|
|
449
464
|
ctx.iterations = 0;
|
|
450
465
|
// New loop epoch so persistIterationMetrics idempotency keys do not
|
|
451
466
|
// collide when iteration indices restart at 1 (incl. iter 1 → iter 1).
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// prompt-cache-idle-gaps.mjs — per session, the idle gaps the cache warmer
|
|
2
|
+
// learns from (cache-warm.mjs): from the send of the session's last real
|
|
3
|
+
// request (when it used the cache) to its next real request. Kept beside the
|
|
4
|
+
// prompt-cache touch store, never in the session record. A gap is kept as
|
|
5
|
+
// `{ at, ms }`, or `{ at, ms, censored: true }` when the session was still
|
|
6
|
+
// idle at the window it was cut at. The session's open gap (`open`: its
|
|
7
|
+
// start) survives a restart. Gaps older than the lookback are pruned on
|
|
8
|
+
// write; a deleted session's entry is dropped.
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { readJsonStateSync, writeJsonAtomicAsync } from '../../../shared/atomic-file.mjs';
|
|
11
|
+
import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
12
|
+
|
|
13
|
+
const PROMPT_CACHE_IDLE_GAP_FILE = 'prompt-cache-idle-gaps.json';
|
|
14
|
+
|
|
15
|
+
const validGap = (gap) => gap && Number(gap.at) > 0 && Number(gap.ms) > 0;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An idle-gap store backed by `path` (default: the data dir's file).
|
|
19
|
+
* `lookbackMs`: how long gaps are kept; `windowMs`: a gap reaching it is
|
|
20
|
+
* recorded as censored there; `minMs`: shorter gaps are not kept (no
|
|
21
|
+
* refresh point lies inside them).
|
|
22
|
+
*/
|
|
23
|
+
export function createIdleGapStore({
|
|
24
|
+
path = () => join(resolvePluginData(), PROMPT_CACHE_IDLE_GAP_FILE),
|
|
25
|
+
lookbackMs,
|
|
26
|
+
windowMs,
|
|
27
|
+
minMs,
|
|
28
|
+
now = () => Date.now(),
|
|
29
|
+
log = (line) => process.stderr.write(`[cache-warm] ${line}\n`),
|
|
30
|
+
} = {}) {
|
|
31
|
+
let sessions = null;
|
|
32
|
+
let writing = Promise.resolve();
|
|
33
|
+
const filePath = () => (typeof path === 'function' ? path() : path);
|
|
34
|
+
const load = () => {
|
|
35
|
+
if (sessions) return sessions;
|
|
36
|
+
sessions = new Map();
|
|
37
|
+
const stored = readJsonStateSync(filePath(), { fallback: null });
|
|
38
|
+
for (const [sessionId, entry] of Object.entries(stored?.sessions || {})) {
|
|
39
|
+
const gaps = Array.isArray(entry?.gaps)
|
|
40
|
+
? entry.gaps.filter(validGap).map((gap) => {
|
|
41
|
+
const item = { at: Number(gap.at), ms: Number(gap.ms) };
|
|
42
|
+
if (gap.censored) item.censored = true;
|
|
43
|
+
return item;
|
|
44
|
+
})
|
|
45
|
+
: [];
|
|
46
|
+
const open = Number(entry?.open) > 0 ? Number(entry.open) : 0;
|
|
47
|
+
if (gaps.length || open) sessions.set(sessionId, { open, gaps });
|
|
48
|
+
}
|
|
49
|
+
return sessions;
|
|
50
|
+
};
|
|
51
|
+
const entryOf = (sessionId) => {
|
|
52
|
+
const id = String(sessionId || '');
|
|
53
|
+
if (!load().has(id)) sessions.set(id, { open: 0, gaps: [] });
|
|
54
|
+
return sessions.get(id);
|
|
55
|
+
};
|
|
56
|
+
const flush = () => {
|
|
57
|
+
const since = now() - lookbackMs;
|
|
58
|
+
const out = {};
|
|
59
|
+
for (const [sessionId, entry] of load()) {
|
|
60
|
+
entry.gaps = entry.gaps.filter((gap) => gap.at >= since);
|
|
61
|
+
if (entry.open && entry.open < since) entry.open = 0;
|
|
62
|
+
if (!entry.gaps.length && !entry.open) sessions.delete(sessionId);
|
|
63
|
+
else out[sessionId] = { ...(entry.open ? { open: entry.open } : {}), gaps: entry.gaps };
|
|
64
|
+
}
|
|
65
|
+
writing = writing
|
|
66
|
+
.catch(() => {})
|
|
67
|
+
.then(() => writeJsonAtomicAsync(filePath(), { version: 1, sessions: out }, { compact: true }));
|
|
68
|
+
return writing;
|
|
69
|
+
};
|
|
70
|
+
// A failed write is reported; the next flush recovers the queue and writes.
|
|
71
|
+
const scheduleFlush = () =>
|
|
72
|
+
void flush().catch((err) => log(`idle-gap store write failed (${filePath()}): ${err?.message || err}`));
|
|
73
|
+
return {
|
|
74
|
+
/** The session went idle: its last real request used the cache at `at`. */
|
|
75
|
+
open(sessionId, at) {
|
|
76
|
+
if (!sessionId || !(Number(at) > 0)) return;
|
|
77
|
+
entryOf(sessionId).open = Number(at);
|
|
78
|
+
scheduleFlush();
|
|
79
|
+
},
|
|
80
|
+
/** A real request arrived at `at`: close the open gap as a return, or as
|
|
81
|
+
* censored at the window when it outlasted it. Resolves the kept gap or
|
|
82
|
+
* null (none open, or too short to keep). */
|
|
83
|
+
close(sessionId, at) {
|
|
84
|
+
const entry = sessionId ? load().get(String(sessionId)) : null;
|
|
85
|
+
if (!entry?.open) return null;
|
|
86
|
+
const start = entry.open;
|
|
87
|
+
entry.open = 0;
|
|
88
|
+
const ms = Number(at) - start;
|
|
89
|
+
let gap = null;
|
|
90
|
+
if (ms >= windowMs) gap = { at: start, ms: windowMs, censored: true };
|
|
91
|
+
else if (ms > minMs) gap = { at: start, ms };
|
|
92
|
+
if (gap) entry.gaps.push(gap);
|
|
93
|
+
scheduleFlush();
|
|
94
|
+
return gap;
|
|
95
|
+
},
|
|
96
|
+
/** The session's kept gaps of the lookback (oldest first). */
|
|
97
|
+
gaps(sessionId) {
|
|
98
|
+
const since = now() - lookbackMs;
|
|
99
|
+
const entry = sessionId ? load().get(String(sessionId)) : null;
|
|
100
|
+
return (entry?.gaps || []).filter((gap) => gap.at >= since).map((gap) => ({ ...gap }));
|
|
101
|
+
},
|
|
102
|
+
/** When the session's open gap started, or 0. */
|
|
103
|
+
openAt(sessionId) {
|
|
104
|
+
return (sessionId && load().get(String(sessionId))?.open) || 0;
|
|
105
|
+
},
|
|
106
|
+
/** The session is gone (deleted): drop everything it learned. */
|
|
107
|
+
forget(sessionId) {
|
|
108
|
+
if (sessionId && load().delete(String(sessionId))) scheduleFlush();
|
|
109
|
+
},
|
|
110
|
+
/** Write pending changes now (tests, shutdown). */
|
|
111
|
+
flush,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// prompt-cache-touch-store.mjs — the durable record cache warms re-arm from
|
|
2
|
+
// after a restart (cache-warm.mjs): per session, the last confirmed touch of
|
|
3
|
+
// its cached prefix — when a call whose usage showed a read or write on it was
|
|
4
|
+
// sent, the tier the provider reported writing, the account that holds it and
|
|
5
|
+
// the prompt size. Kept beside the session records, never in them: a warm's
|
|
6
|
+
// touch must not move a session's own timestamps (activity, autoClear).
|
|
7
|
+
// Expired entries are dropped on write.
|
|
8
|
+
//
|
|
9
|
+
// Beside the touches: per session, when a warm last used the cache
|
|
10
|
+
// successfully (a read with no write). The idle auto-clear measures idleness
|
|
11
|
+
// from it (idle-auto-clear-gate.mjs) until a real request settles, so it
|
|
12
|
+
// outlives the touch: a later miss or an expiry drops the touch, not the use.
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { readJsonStateSync, writeJsonAtomicAsync } from '../../../shared/atomic-file.mjs';
|
|
15
|
+
import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
16
|
+
|
|
17
|
+
const PROMPT_CACHE_TOUCH_FILE = 'prompt-cache-touches.json';
|
|
18
|
+
|
|
19
|
+
function validTouch(value) {
|
|
20
|
+
return (
|
|
21
|
+
value &&
|
|
22
|
+
typeof value === 'object' &&
|
|
23
|
+
Number(value.touchAt) > 0 &&
|
|
24
|
+
Number(value.ttlMs) > 0 &&
|
|
25
|
+
Number(value.promptTokens) > 0
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// A warm use as stored: `{ at, provider, model, accountId }` — the route and
|
|
30
|
+
// account whose cached prefix the warm read. A bare time (written before the
|
|
31
|
+
// identity was recorded) keeps its time and matches no route.
|
|
32
|
+
function warmUseRecord(value) {
|
|
33
|
+
const record = value && typeof value === 'object' ? value : { at: value };
|
|
34
|
+
if (!(Number(record.at) > 0)) return null;
|
|
35
|
+
return {
|
|
36
|
+
at: Number(record.at),
|
|
37
|
+
provider: String(record.provider || ''),
|
|
38
|
+
model: String(record.model || ''),
|
|
39
|
+
accountId: String(record.accountId || ''),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A touch store backed by `path` (default: the data dir's file). */
|
|
44
|
+
export function createPromptCacheTouchStore(
|
|
45
|
+
path = () => join(resolvePluginData(), PROMPT_CACHE_TOUCH_FILE),
|
|
46
|
+
{ log = (line) => process.stderr.write(`[cache-warm] ${line}\n`) } = {}
|
|
47
|
+
) {
|
|
48
|
+
let touches = null;
|
|
49
|
+
let warmUses = null;
|
|
50
|
+
let writing = Promise.resolve();
|
|
51
|
+
const filePath = () => (typeof path === 'function' ? path() : path);
|
|
52
|
+
const load = () => {
|
|
53
|
+
if (touches) return touches;
|
|
54
|
+
touches = new Map();
|
|
55
|
+
warmUses = new Map();
|
|
56
|
+
const stored = readJsonStateSync(filePath(), { fallback: null });
|
|
57
|
+
for (const [sessionId, touch] of Object.entries(stored?.touches || {})) {
|
|
58
|
+
if (validTouch(touch)) touches.set(sessionId, { ...touch });
|
|
59
|
+
}
|
|
60
|
+
for (const [sessionId, use] of Object.entries(stored?.warmUses || {})) {
|
|
61
|
+
const record = warmUseRecord(use);
|
|
62
|
+
if (record) warmUses.set(sessionId, record);
|
|
63
|
+
}
|
|
64
|
+
return touches;
|
|
65
|
+
};
|
|
66
|
+
const uses = () => {
|
|
67
|
+
load();
|
|
68
|
+
return warmUses;
|
|
69
|
+
};
|
|
70
|
+
// Touches change once per real request or warm: each change is written at
|
|
71
|
+
// once, the writes serialized.
|
|
72
|
+
const flush = () => {
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const [sessionId, touch] of load()) {
|
|
76
|
+
if (touch.touchAt + touch.ttlMs > now) out[sessionId] = touch;
|
|
77
|
+
else touches.delete(sessionId);
|
|
78
|
+
}
|
|
79
|
+
writing = writing
|
|
80
|
+
.catch(() => {})
|
|
81
|
+
.then(() =>
|
|
82
|
+
writeJsonAtomicAsync(
|
|
83
|
+
filePath(),
|
|
84
|
+
{ version: 1, touches: out, warmUses: Object.fromEntries(uses()) },
|
|
85
|
+
{ compact: true }
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
return writing;
|
|
89
|
+
};
|
|
90
|
+
// A failed write is reported; the next flush recovers the queue and writes.
|
|
91
|
+
const scheduleFlush = () =>
|
|
92
|
+
void flush().catch((err) => log(`touch store write failed (${filePath()}): ${err?.message || err}`));
|
|
93
|
+
return {
|
|
94
|
+
/** The session's last confirmed touch, or null. */
|
|
95
|
+
get(sessionId) {
|
|
96
|
+
const touch = load().get(String(sessionId || ''));
|
|
97
|
+
return touch ? { ...touch } : null;
|
|
98
|
+
},
|
|
99
|
+
/** The sessions holding a touch. */
|
|
100
|
+
ids() {
|
|
101
|
+
return [...load().keys()];
|
|
102
|
+
},
|
|
103
|
+
/** Record a confirmed touch `{ touchAt, ttlMs, accountId, promptTokens }`. */
|
|
104
|
+
record(sessionId, touch) {
|
|
105
|
+
if (!sessionId || !validTouch(touch)) return;
|
|
106
|
+
load().set(String(sessionId), {
|
|
107
|
+
touchAt: Number(touch.touchAt),
|
|
108
|
+
ttlMs: Number(touch.ttlMs),
|
|
109
|
+
promptTokens: Number(touch.promptTokens),
|
|
110
|
+
...(touch.accountId ? { accountId: String(touch.accountId) } : {}),
|
|
111
|
+
});
|
|
112
|
+
scheduleFlush();
|
|
113
|
+
},
|
|
114
|
+
/** The session's cached prefix is gone (compaction, clear, close, miss). */
|
|
115
|
+
forget(sessionId) {
|
|
116
|
+
if (load().delete(String(sessionId || ''))) scheduleFlush();
|
|
117
|
+
},
|
|
118
|
+
/** When a warm last used the session's cache successfully, or 0. */
|
|
119
|
+
lastWarmUse(sessionId) {
|
|
120
|
+
return uses().get(String(sessionId || ''))?.at || 0;
|
|
121
|
+
},
|
|
122
|
+
/** The session's last successful warm use `{ at, provider, model,
|
|
123
|
+
* accountId }`, or null. */
|
|
124
|
+
warmUse(sessionId) {
|
|
125
|
+
const use = uses().get(String(sessionId || ''));
|
|
126
|
+
return use ? { ...use } : null;
|
|
127
|
+
},
|
|
128
|
+
/** A warm read the session's cache without writing at `at`, on the
|
|
129
|
+
* route and account `{ provider, model, accountId }`. */
|
|
130
|
+
recordWarmUse(sessionId, at, identity = {}) {
|
|
131
|
+
const record = warmUseRecord({ ...identity, at });
|
|
132
|
+
if (!sessionId || !record) return;
|
|
133
|
+
uses().set(String(sessionId), record);
|
|
134
|
+
scheduleFlush();
|
|
135
|
+
},
|
|
136
|
+
/** The session's own activity supersedes its warm use (a real request
|
|
137
|
+
* settled) or its cached conversation is gone (compaction, clear). */
|
|
138
|
+
forgetWarmUse(sessionId) {
|
|
139
|
+
if (uses().delete(String(sessionId || ''))) scheduleFlush();
|
|
140
|
+
},
|
|
141
|
+
/** Write pending changes now (tests, shutdown). */
|
|
142
|
+
flush,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let defaultStore = null;
|
|
147
|
+
export function promptCacheTouchStore() {
|
|
148
|
+
defaultStore ??= createPromptCacheTouchStore();
|
|
149
|
+
return defaultStore;
|
|
150
|
+
}
|