mixdog 1.4.2 → 1.4.3
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/package.json +2 -2
- package/scripts/build-shipped-history.mjs +15 -5
- package/scripts/gate-local.mjs +65 -19
- package/scripts/graph-parity.mjs +8 -3
- package/scripts/lib/gate-scheduler.mjs +2 -1
- package/scripts/lib/run-node-tests.mjs +17 -5
- package/scripts/lib/test-failure-summary.mjs +1 -8
- package/scripts/lib/test-spawn-ledger.mjs +33 -2
- package/scripts/post-release-check.mjs +26 -16
- package/scripts/provider-pingpong-live.mjs +1 -1
- package/scripts/prune-embedding-runtime.mjs +3 -1
- package/scripts/release-paths.mjs +7 -1
- package/src/defaults/shipped-history.json +20 -0
- package/src/defaults/skills/setup/SKILL.md +2 -2
- package/src/defaults/skills/setup/references/actions.md +16 -3
- package/src/defaults/skills/setup/references/surfaces.md +3 -0
- package/src/defaults/skills/setup-guide/SKILL.md +6 -1
- package/src/lib/instruction-text.cjs +36 -0
- package/src/lib/rules-builder.cjs +34 -20
- package/src/orchestration/balanced.md +23 -0
- package/src/orchestration/focused.md +22 -0
- package/src/orchestration/none.md +5 -0
- package/src/orchestration/swarm.md +24 -0
- package/src/rules/agent-mode/AGENT_MODE.md +3 -1
- package/src/rules/agent-mode/MIXY.md +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +0 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +0 -9
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -4
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -1
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +199 -46
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +3 -5
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +9 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -5
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +30 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
- package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
- package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
- package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -48
- package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
- package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +9 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +1 -2
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +57 -26
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +19 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +19 -10
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +31 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +42 -6
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
- package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +14 -5
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +7 -3
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +27 -9
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -4
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
- package/src/runtime/attachments/attachment-reference.mjs +1 -1
- package/src/runtime/attachments/pdf-extract.mjs +1 -1
- package/src/runtime/attachments/store.d.mts +1 -0
- package/src/runtime/attachments/store.mjs +2 -2
- package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
- package/src/runtime/channels/lib/scheduler.mjs +33 -0
- package/src/runtime/channels/lib/schedules-db.mjs +11 -4
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
- package/src/runtime/channels/lib/whisper-server.mjs +12 -12
- package/src/runtime/channels/lib/worker-main.mjs +9 -31
- package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
- package/src/runtime/connectors/connector-accounts-mcp.mjs +169 -33
- package/src/runtime/connectors/gmail-app-password.mjs +2 -2
- package/src/runtime/connectors/google-mcp.mjs +1 -1
- package/src/runtime/effort-judge/judge-client.mjs +2 -2
- package/src/runtime/effort-judge/judge-input.mjs +4 -4
- package/src/runtime/explore/explorer.mjs +0 -5
- package/src/runtime/explore/tool.mjs +3 -3
- package/src/runtime/local-provider/hardware.mjs +1 -1
- package/src/runtime/media/adapters/codex-image.mjs +3 -3
- package/src/runtime/media/adapters/gemini-video.mjs +1 -1
- package/src/runtime/media/media-usage.mjs +1 -1
- package/src/runtime/media/store.mjs +1 -1
- package/src/runtime/memory/index.mjs +11 -1
- package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
- package/src/runtime/memory/lib/memory-embed.mjs +83 -14
- package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
- package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
- package/src/runtime/memory/lib/memory.mjs +17 -0
- package/src/runtime/memory/lib/pg/process.mjs +1 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
- package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
- package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
- package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
- package/src/runtime/office/capabilities.mjs +1 -1
- package/src/runtime/office/core/office-core.mjs +1 -1
- package/src/runtime/office/design/content-model.mjs +3 -3
- package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
- package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
- package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
- package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
- package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
- package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +8 -6
- package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
- package/src/runtime/shared/atomic-file.d.mts +8 -0
- package/src/runtime/shared/background-tasks.mjs +22 -4
- package/src/runtime/shared/background-wait.mjs +66 -0
- package/src/runtime/shared/child-guardian.mjs +17 -7
- package/src/runtime/shared/config.mjs +313 -16
- package/src/runtime/shared/connector-catalog.d.mts +4 -5
- package/src/runtime/shared/connector-catalog.mjs +11 -1
- package/src/runtime/shared/file-lock.mjs +8 -3
- package/src/runtime/shared/git-runner.mjs +7 -2
- package/src/runtime/shared/idle-gc.mjs +30 -7
- package/src/runtime/shared/llm/http-agent.mjs +8 -5
- package/src/runtime/shared/llm/system-proxy.mjs +2 -1
- package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
- package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
- package/src/runtime/shared/maintenance-schedule.mjs +109 -0
- package/src/runtime/shared/process-runner.mjs +8 -4
- package/src/runtime/shared/routine-trigger.d.mts +13 -0
- package/src/runtime/shared/routine-trigger.mjs +34 -0
- package/src/runtime/shared/run-child.d.mts +2 -1
- package/src/runtime/shared/run-child.mjs +70 -18
- package/src/runtime/shared/session-scratch.mjs +14 -2
- package/src/runtime/shared/shipped-definitions.mjs +7 -1
- package/src/runtime/shared/tool-card-model.golden.json +3 -12
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-work-units.mjs +31 -6
- package/src/runtime/shared/turn-snapshot.mjs +26 -4
- package/src/runtime/shared/turn-worktree-snapshot.mjs +6 -11
- package/src/runtime/shared/worker-requests.mjs +17 -3
- package/src/runtime/shared/worktrees/branch-management.mjs +1 -5
- package/src/runtime/shared/worktrees/contract.mjs +16 -6
- package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
- package/src/runtime/shared/worktrees/disk.mjs +79 -2
- package/src/runtime/shared/worktrees/git.mjs +44 -11
- package/src/runtime/shared/worktrees/include.mjs +6 -4
- package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
- package/src/runtime/shared/worktrees/manager.mjs +772 -75
- package/src/runtime/shared/worktrees/settings.mjs +33 -7
- package/src/runtime/shared/worktrees/test-support.mjs +5 -1
- package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
- package/src/runtime/tidy/process.mjs +2 -1
- package/src/runtime/web-search/index.mjs +3 -22
- package/src/runtime/web-search/lib/web-tools.mjs +2 -2
- package/src/runtime/web-search/tool-defs.mjs +17 -26
- package/src/session-runtime/agent-profile-changes.mjs +55 -4
- package/src/session-runtime/boot/apis.mjs +27 -4
- package/src/session-runtime/boot/begin.mjs +2 -0
- package/src/session-runtime/boot/config.mjs +10 -1
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/providers.mjs +70 -3
- package/src/session-runtime/boot/session.mjs +2 -0
- package/src/session-runtime/boot/tools.mjs +69 -25
- package/src/session-runtime/channel-config-api.mjs +1 -0
- package/src/session-runtime/config-change-effects.mjs +179 -0
- package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
- package/src/session-runtime/config-lifecycle.mjs +160 -24
- package/src/session-runtime/conversation-import/discovery.mjs +86 -7
- package/src/session-runtime/conversation-import/service.mjs +3 -2
- package/src/session-runtime/goal-facade-api.mjs +49 -3
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +3 -11
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +18 -4
- package/src/session-runtime/internal-tool-executor.mjs +12 -19
- package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
- package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
- package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +4 -4
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +14 -3
- package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
- package/src/session-runtime/model-route/set-route.mjs +40 -7
- package/src/session-runtime/native-web-search/routes.mjs +3 -3
- package/src/session-runtime/provider-auth/refresh.mjs +6 -3
- package/src/session-runtime/provider-auth/setup.mjs +1 -1
- package/src/session-runtime/provider-readiness.mjs +53 -4
- package/src/session-runtime/resource-api.mjs +1 -1
- package/src/session-runtime/resource-extension-sync.mjs +4 -8
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +5 -1
- package/src/session-runtime/resource-mcp-secrets.mjs +3 -3
- package/src/session-runtime/resource-plugins-api.mjs +4 -0
- package/src/session-runtime/routine-executor.mjs +152 -0
- package/src/session-runtime/routine-home-turn.mjs +46 -5
- package/src/session-runtime/runtime-facade.mjs +5 -0
- package/src/session-runtime/runtime-feature-gates.mjs +2 -11
- package/src/session-runtime/schedule-session-run.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +110 -6
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +24 -6
- package/src/session-runtime/services/agent-tool.mjs +51 -1
- package/src/session-runtime/services/channel-admin.mjs +18 -0
- package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
- package/src/session-runtime/services/plugin-admin.mjs +4 -0
- package/src/session-runtime/services/seeds.mjs +7 -0
- package/src/session-runtime/services/usage-stats-model.mjs +32 -11
- package/src/session-runtime/session-create/materialize.mjs +4 -2
- package/src/session-runtime/session-create/session-options.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +1 -9
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +1 -1
- package/src/session-runtime/setup-tool/executor.mjs +8 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +26 -1
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
- package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
- package/src/session-runtime/tool-policy-refresh.mjs +182 -5
- package/src/session-runtime/tool-surface.mjs +38 -1
- package/src/session-runtime/turn/agent-ops.mjs +10 -2
- package/src/session-runtime/turn/session-ops.mjs +2 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
- package/src/session-runtime/turn/turn-run.mjs +1 -1
- package/src/session-runtime/webhook-session-run.mjs +2 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +1 -4
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +3 -10
- package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
- package/src/session-runtime/workflow-agents-api.mjs +2 -0
- package/src/session-runtime/workspace/project-branches.mjs +1 -1
- package/src/session-runtime/workspace/session-view.mjs +1 -1
- package/src/session-runtime/workspace/transitions.mjs +20 -4
- package/src/session-runtime/workspace/worktree-maintenance.mjs +126 -6
- package/src/session-runtime/workspace/worktree-setup.mjs +13 -1
- package/src/session-runtime/workspace/worktree-tool.mjs +16 -0
- package/src/standalone/daemon-agent-control.mjs +25 -8
- package/src/standalone/daemon-desktop-runtime.mjs +6 -2
- package/src/standalone/daemon-lag-profiler.mjs +7 -7
- package/src/standalone/daemon.mjs +54 -2
- package/src/standalone/local-session-runtime.mjs +16 -0
- package/src/standalone/session-client/daemon-unavailable.mjs +1 -1
- package/src/standalone/session-protocol.mjs +11 -0
- package/src/standalone/session-service/agent-route-follow.mjs +23 -20
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
- package/src/standalone/session-service/quota-resume.mjs +9 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
- package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
- package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
- package/src/standalone/session-service/session-calls.mjs +2 -1
- package/src/tui/App.jsx +8 -0
- package/src/tui/app/clipboard.mjs +1 -1
- package/src/tui/app/create-app-pickers.mjs +2 -0
- package/src/tui/app/doctor.mjs +2 -2
- package/src/tui/app/panel-signature.mjs +1 -1
- package/src/tui/app/settings-picker.mjs +53 -8
- package/src/tui/app/shell-layout/row-budgets.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +1 -1
- package/src/tui/app/use-prompt-queue-history.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +1 -1
- package/src/tui/app/use-transcript-window/geometry.mjs +1 -1
- package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
- package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
- package/src/tui/components/question-panel-model.mjs +4 -4
- package/src/tui/components/tool-execution/standard-card.jsx +3 -4
- package/src/tui/components/user-message-skills.mjs +1 -1
- package/src/tui/dist/index.mjs +309 -30
- package/src/tui/markdown/render-ansi.mjs +1 -1
- package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
- package/src/tui/session/agent-job-feed.mjs +2 -0
- package/src/tui/session/context-state.mjs +3 -0
- package/src/tui/session/quota-resume.mjs +1 -1
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +5 -0
- package/src/tui/session/session-api/worktree.mjs +13 -1
- package/src/tui/session/session-flow/drain.mjs +11 -14
- package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
- package/src/tui/session/turn-quiet.mjs +1 -1
- package/src/tui/session/turn.mjs +3 -0
- package/src/tui/session/worktree-setup-notice.mjs +1 -1
- package/src/tui/spinner-meta.mjs +3 -3
- package/src/tui/spinner-verbs.mjs +2 -2
- package/src/defaults/skills/goal-management/SKILL.md +0 -169
- package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
- package/src/session-runtime/goal-tasks.mjs +0 -1
|
@@ -5,132 +5,398 @@ import {
|
|
|
5
5
|
cancelJournalWrites,
|
|
6
6
|
createTurnJournalEncoder,
|
|
7
7
|
emptyInterruptionSnapshot,
|
|
8
|
-
|
|
8
|
+
finishPendingSetAside,
|
|
9
9
|
openJournalForTurn,
|
|
10
10
|
readTurnCheckpointHeader,
|
|
11
11
|
readTurnCheckpointHeaderState,
|
|
12
|
+
removeOrphanTurnJournal,
|
|
12
13
|
removeTurnJournal,
|
|
13
14
|
replayTurnJournal,
|
|
14
15
|
settleTurnJournalWrites,
|
|
15
16
|
turnCheckpointPath,
|
|
16
17
|
turnCheckpointReadPath,
|
|
17
|
-
|
|
18
|
+
withCheckpointOpenLock,
|
|
18
19
|
writeCheckpointHeader,
|
|
19
20
|
} from './turn-checkpoint-journal.mjs';
|
|
20
21
|
import { captureTurnCheckpointContextState, restoreTurnCheckpointContextState } from './turn-checkpoint-context.mjs';
|
|
21
22
|
import { stripProviderReplayMessages } from './provider-replay.mjs';
|
|
22
23
|
import { sensitiveValuesVersion } from '../sensitive-values.mjs';
|
|
24
|
+
import { contextMessagesSignature } from '../context-utils.mjs';
|
|
25
|
+
import { mintTranscriptEpoch, readDurableSessionRecord } from '../store/transcript-durability.mjs';
|
|
23
26
|
|
|
24
|
-
export { turnCheckpointPath
|
|
27
|
+
export { turnCheckpointPath };
|
|
25
28
|
export { captureTurnCheckpointContextState, restoreTurnCheckpointContextState };
|
|
26
29
|
|
|
27
30
|
/** Resolve once every queued journal append for a session has been written. */
|
|
28
31
|
export const settleTurnCheckpointWrites = settleTurnJournalWrites;
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
// ── What a checkpoint means against the durable record ──────────────────────
|
|
34
|
+
// A checkpoint names the base it was taken against: `base` (the transcript
|
|
35
|
+
// epoch and length of the landed record the turn opened on) and `boundary`
|
|
36
|
+
// (how many of that record's messages precede the turn). A self-contained
|
|
37
|
+
// checkpoint (`fullTranscript`) carries the whole context instead. Every
|
|
38
|
+
// history rewrite in this process mints a new transcript epoch before a
|
|
39
|
+
// record carries it, and only the base or a later save of the turn can be
|
|
40
|
+
// on disk after the turn opened (store/transcript-durability.mjs), so:
|
|
41
|
+
// covered the record carries the finished turn's coverage token: nothing
|
|
42
|
+
// to replay, the checkpoint only awaits deletion;
|
|
43
|
+
// replay the record is of the base epoch and holds the base prefix (the
|
|
44
|
+
// base itself, a pre-open save of it, or a save of this same
|
|
45
|
+
// active turn), or — for a self-contained checkpoint — is this
|
|
46
|
+
// same active turn's save on any epoch;
|
|
47
|
+
// conflict anything else. The checkpoint is kept and reported, never
|
|
48
|
+
// spliced onto a record it was not taken against.
|
|
49
|
+
// A legacy checkpoint (written before checkpoints named their base: no
|
|
50
|
+
// `boundary`) replays only when the record is exactly the prefix the turn
|
|
51
|
+
// recorded in its context state (count and exact-content signature), or a
|
|
52
|
+
// save of that same turn holding that prefix; otherwise it is a conflict.
|
|
53
|
+
|
|
54
|
+
/** `{ action: 'covered' | 'replay' | 'conflict', reason?, boundary? }` for
|
|
55
|
+
* `checkpoint` against the canonical record `durable` (as it is on disk);
|
|
56
|
+
* `boundary` is how many of the record's messages a replay keeps. */
|
|
57
|
+
export function checkpointDisposition(durable, checkpoint) {
|
|
58
|
+
if (!durable || !Array.isArray(durable.messages)) {
|
|
59
|
+
return { action: 'conflict', reason: 'the session has no durable record' };
|
|
60
|
+
}
|
|
61
|
+
if (typeof checkpoint?.turnToken === 'string' && durable.turnCoverage === checkpoint.turnToken) {
|
|
62
|
+
return { action: 'covered' };
|
|
63
|
+
}
|
|
64
|
+
if (checkpoint && !Object.hasOwn(checkpoint, 'boundary')) return legacyDisposition(durable, checkpoint);
|
|
65
|
+
const base = checkpoint?.base;
|
|
66
|
+
const baseEpoch = typeof base?.epoch === 'string' && base.epoch ? base.epoch : null;
|
|
67
|
+
const sameTurn = durable.activeTurnCheckpoint?.turnToken === checkpoint?.turnToken;
|
|
68
|
+
const onBaseEpoch = baseEpoch !== null && durable.transcriptEpoch === baseEpoch;
|
|
69
|
+
if (checkpoint?.fullTranscript === true) {
|
|
70
|
+
if (onBaseEpoch || sameTurn) return { action: 'replay' };
|
|
71
|
+
return { action: 'conflict', reason: 'the record is neither the base nor a save of this turn' };
|
|
72
|
+
}
|
|
73
|
+
const boundary = checkpoint?.boundary;
|
|
74
|
+
if (!baseEpoch || !Number.isInteger(boundary) || boundary < 0 || boundary > Number(base.length)) {
|
|
75
|
+
return { action: 'conflict', reason: 'the checkpoint names no base revision' };
|
|
76
|
+
}
|
|
77
|
+
if (onBaseEpoch && durable.messages.length >= boundary) return { action: 'replay', boundary };
|
|
78
|
+
return { action: 'conflict', reason: 'the record is not of the base the checkpoint was taken against' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function legacyDisposition(durable, checkpoint) {
|
|
82
|
+
const conflict = (reason) => ({ action: 'conflict', reason: `legacy checkpoint: ${reason}` });
|
|
83
|
+
if ((Number(checkpoint.generation) || 0) !== (Number(durable.generation) || 0)) {
|
|
84
|
+
return conflict('the record is of another lifecycle generation');
|
|
85
|
+
}
|
|
86
|
+
const marker = durable.activeTurnCheckpoint?.turnToken;
|
|
87
|
+
if (typeof marker === 'string' && marker !== checkpoint.turnToken) {
|
|
88
|
+
return conflict('the record names another active turn');
|
|
89
|
+
}
|
|
90
|
+
if (checkpoint.fullTranscript === true) return conflict('a self-contained checkpoint names no base');
|
|
91
|
+
const state = checkpoint.contextState;
|
|
92
|
+
const count = state?.committedPrefixCount;
|
|
93
|
+
if (!Number.isInteger(count) || count < 0 || typeof state.committedPrefixSignature !== 'string') {
|
|
94
|
+
return conflict('the checkpoint records no prefix');
|
|
95
|
+
}
|
|
96
|
+
if (
|
|
97
|
+
durable.messages.length < count ||
|
|
98
|
+
contextMessagesSignature(durable.messages, count) !== state.committedPrefixSignature
|
|
99
|
+
) {
|
|
100
|
+
return conflict('the record does not hold the prefix the turn opened on');
|
|
101
|
+
}
|
|
102
|
+
if (durable.messages.length !== count && marker !== checkpoint.turnToken) {
|
|
103
|
+
return conflict('the record holds more than the prefix the turn opened on');
|
|
104
|
+
}
|
|
105
|
+
return { action: 'replay', boundary: count };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The messages `checkpoint` contributes (finalized as interrupted unless it
|
|
109
|
+
* was sealed) and the transcript they make on `durable`, keeping its first
|
|
110
|
+
* `boundary` messages. */
|
|
111
|
+
function replayCheckpoint(durable, checkpoint, boundary, routeChanged) {
|
|
112
|
+
const finalized =
|
|
113
|
+
checkpoint.sealed === true
|
|
114
|
+
? { messages: checkpoint.turnMessages.slice(), responsePreserved: true }
|
|
115
|
+
: finalizeTurnInterruptionSnapshot({
|
|
116
|
+
turnOutgoing: checkpoint.turnMessages,
|
|
117
|
+
currentUserContent: checkpoint.currentUserContent,
|
|
118
|
+
snapshot: checkpoint.interruption,
|
|
119
|
+
abortReason: 'process-crash',
|
|
120
|
+
});
|
|
121
|
+
// The route switched models mid-turn and the journal may predate the
|
|
122
|
+
// switch: its turn still carries the previous provider's native replay
|
|
123
|
+
// state, which the saved route's provider must not receive.
|
|
124
|
+
const turnMessages = routeChanged ? stripProviderReplayMessages(finalized.messages) : finalized.messages;
|
|
125
|
+
const messages =
|
|
126
|
+
checkpoint.fullTranscript === true ? turnMessages : [...durable.messages.slice(0, boundary), ...turnMessages];
|
|
127
|
+
return { messages, responsePreserved: finalized.responsePreserved };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const _reportedConflicts = new Set();
|
|
131
|
+
|
|
132
|
+
export function reportCheckpointConflict(sessionId, checkpoint, reason) {
|
|
133
|
+
const key = `${sessionId}:${checkpoint?.turnToken}`;
|
|
134
|
+
if (_reportedConflicts.has(key)) return;
|
|
135
|
+
_reportedConflicts.add(key);
|
|
136
|
+
try {
|
|
137
|
+
process.stderr.write(
|
|
138
|
+
`[turn-checkpoint] kept unrecovered checkpoint session=${sessionId} turn=${checkpoint?.turnToken}: ${reason}\n`
|
|
139
|
+
);
|
|
140
|
+
} catch {
|
|
141
|
+
/* stderr best-effort */
|
|
142
|
+
}
|
|
45
143
|
}
|
|
46
144
|
|
|
47
|
-
/** Read-only projection for a turn still owned by another process
|
|
48
|
-
*
|
|
49
|
-
* or marks a live turn as interrupted.
|
|
145
|
+
/** Read-only projection for a turn still owned by another process (or not
|
|
146
|
+
* yet durable in this one). Unlike recoverTurnCheckpoint this never mutates
|
|
147
|
+
* the session, clears the checkpoint, or marks a live turn as interrupted.
|
|
148
|
+
* `session` is read as the durable record. */
|
|
50
149
|
export function projectTurnCheckpointMessages(session, checkpoint) {
|
|
51
150
|
const source = Array.isArray(session?.messages) ? session.messages : [];
|
|
52
151
|
if (!session?.id || checkpoint?.sessionId !== session.id || !Array.isArray(checkpoint?.turnMessages)) return source;
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const start = findTurnStart(source, checkpoint.currentUserContent);
|
|
58
|
-
return [...(start >= 0 ? source.slice(0, start) : source), ...checkpoint.turnMessages];
|
|
152
|
+
const disposition = checkpointDisposition(session, checkpoint);
|
|
153
|
+
if (disposition.action !== 'replay') return source;
|
|
154
|
+
if (checkpoint.fullTranscript === true) return checkpoint.turnMessages.slice();
|
|
155
|
+
return [...source.slice(0, disposition.boundary), ...checkpoint.turnMessages];
|
|
59
156
|
}
|
|
60
157
|
|
|
61
158
|
// ── Journal-backed checkpoint writes ─────────────────────────────────────────
|
|
62
|
-
// A turn is checkpointed as a
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
|
|
159
|
+
// A turn is checkpointed as a header plus an append-only delta journal
|
|
160
|
+
// (turn-checkpoint-journal.mjs). The recorder owns exactly one turn: the FIRST
|
|
161
|
+
// record() writes the header synchronously — it is the crash durability
|
|
162
|
+
// anchor for the prompt and must land before the provider runs — and every
|
|
163
|
+
// later record() appends ONLY what changed since the previous flush. Per-flush
|
|
164
|
+
// cost is therefore O(new bytes), not O(whole growing turn).
|
|
165
|
+
//
|
|
166
|
+
// The header names its base explicitly: the turn opens at `boundary` (the
|
|
167
|
+
// index of its user message) on the durable landed transcript `base`
|
|
168
|
+
// (`{ epoch, refs }`, store/transcript-durability.mjs). When the messages
|
|
169
|
+
// before the boundary are not exactly the base's, or a later flush finds any
|
|
170
|
+
// of them replaced (compaction, a prefix rewrite), the header is rewritten as
|
|
171
|
+
// ONE self-contained replacement of the whole context under a new transcript
|
|
172
|
+
// epoch — atomically, before any session record can carry that epoch — and
|
|
173
|
+
// the journal restarts under it. A bare index move is never journaled.
|
|
174
|
+
export function createTurnCheckpointRecorder({
|
|
175
|
+
session = null,
|
|
176
|
+
sessionId,
|
|
177
|
+
generation,
|
|
178
|
+
turnToken,
|
|
179
|
+
startedAt,
|
|
180
|
+
base = null,
|
|
181
|
+
}) {
|
|
71
182
|
const encoder = createTurnJournalEncoder({ turnToken, sessionId });
|
|
72
183
|
let opened = false;
|
|
73
184
|
let stopped = false;
|
|
74
185
|
// The sensitive-values version (sensitive-values.mjs) the header and
|
|
75
186
|
// journal were written under. A newly marked value rewrites both from the
|
|
76
|
-
// turn's current state with the value replaced,
|
|
77
|
-
//
|
|
187
|
+
// turn's current state with the value replaced, so no recovery record
|
|
188
|
+
// keeps it.
|
|
78
189
|
let openedVersion = 0;
|
|
190
|
+
let headerPending = false;
|
|
191
|
+
let headerWrites = 0;
|
|
192
|
+
// Frame: the header holds turnOutgoing[offset..] (offset 0 when
|
|
193
|
+
// self-contained); `pinned` are the messages before the turn's own region,
|
|
194
|
+
// any replacement of which is a history rewrite.
|
|
195
|
+
let fullTranscript = false;
|
|
196
|
+
let offset = 0;
|
|
197
|
+
let pinned = [];
|
|
198
|
+
let openingIndex = -1;
|
|
199
|
+
let openingRef = null;
|
|
200
|
+
let transcriptEpoch = null;
|
|
201
|
+
let contextState = null;
|
|
202
|
+
|
|
203
|
+
const adoptEpoch = (epoch) => {
|
|
204
|
+
transcriptEpoch = epoch;
|
|
205
|
+
if (session) session.transcriptEpoch = epoch;
|
|
206
|
+
};
|
|
207
|
+
const opensOnBase = (source, boundary) => {
|
|
208
|
+
if (!base || typeof base.epoch !== 'string' || !Array.isArray(base.refs)) return false;
|
|
209
|
+
if (session && session.transcriptEpoch !== base.epoch) return false;
|
|
210
|
+
if (boundary > base.refs.length) return false;
|
|
211
|
+
for (let index = 0; index < boundary; index += 1) if (source[index] !== base.refs[index]) return false;
|
|
212
|
+
return true;
|
|
213
|
+
};
|
|
214
|
+
const pinnedIntact = (source) => {
|
|
215
|
+
if (source.length < pinned.length) return false;
|
|
216
|
+
for (let index = 0; index < pinned.length; index += 1) if (source[index] !== pinned[index]) return false;
|
|
217
|
+
return true;
|
|
218
|
+
};
|
|
219
|
+
const trackOpening = (source) => {
|
|
220
|
+
openingRef = openingIndex >= 0 ? (source[openingIndex] ?? null) : null;
|
|
221
|
+
};
|
|
222
|
+
const writeHeader = ({ source, currentUserContent, interruption, captureContext, sealed }) => {
|
|
223
|
+
const snapshot =
|
|
224
|
+
typeof interruption?.snapshot === 'function' ? interruption.snapshot() : emptyInterruptionSnapshot();
|
|
225
|
+
if (typeof captureContext === 'function') contextState = captureContext(offset);
|
|
226
|
+
headerWrites += 1;
|
|
227
|
+
const seed = encoder.seed(source, interruption, contextState, { offset, snapshot, epoch: headerWrites });
|
|
228
|
+
// Header publish and journal open are one step under the session's
|
|
229
|
+
// checkpoint lock, so the orphan-journal sweep of another process never
|
|
230
|
+
// deletes the journal between them (turn-checkpoint-journal.mjs).
|
|
231
|
+
withCheckpointOpenLock(sessionId, () => {
|
|
232
|
+
writeCheckpointHeader({
|
|
233
|
+
sessionId,
|
|
234
|
+
generation,
|
|
235
|
+
turnToken,
|
|
236
|
+
startedAt,
|
|
237
|
+
currentUserContent,
|
|
238
|
+
base: base ? { epoch: base.epoch, length: base.refs.length } : null,
|
|
239
|
+
boundary: offset,
|
|
240
|
+
transcriptEpoch,
|
|
241
|
+
...(fullTranscript ? { fullTranscript: true } : {}),
|
|
242
|
+
...(sealed ? { sealed: true } : {}),
|
|
243
|
+
journalEpoch: headerWrites,
|
|
244
|
+
turnMessages: seed.turnMessages,
|
|
245
|
+
interruption: snapshot,
|
|
246
|
+
contextState: seed.contextState,
|
|
247
|
+
});
|
|
248
|
+
// Opening the journal is part of the anchor: a leftover journal (an
|
|
249
|
+
// earlier turn's, or this turn's under its previous header) must never
|
|
250
|
+
// replay onto the new header. It lands synchronously when the lane is
|
|
251
|
+
// idle and is otherwise FENCED behind the older in-flight write; the
|
|
252
|
+
// head record's token and journal epoch guard the interim window.
|
|
253
|
+
openJournalForTurn(sessionId, seed.openLines);
|
|
254
|
+
});
|
|
255
|
+
trackOpening(source);
|
|
256
|
+
return true;
|
|
257
|
+
};
|
|
258
|
+
const report = (text) => {
|
|
259
|
+
try {
|
|
260
|
+
process.stderr.write(`[turn-checkpoint] session=${sessionId}: ${text}\n`);
|
|
261
|
+
} catch {
|
|
262
|
+
// stderr may be closed; the removal is already done
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
// The journal removal is owed until it is confirmed (`journalOwed`): the
|
|
266
|
+
// recorder writes nothing meanwhile, and every flush (and stop) tries again.
|
|
267
|
+
let journalOwed = false;
|
|
268
|
+
let journalRemoving = false;
|
|
269
|
+
let redactionFailure = '';
|
|
270
|
+
const removeOwedJournal = () => {
|
|
271
|
+
if (journalRemoving) return;
|
|
272
|
+
journalRemoving = true;
|
|
273
|
+
removeTurnJournal(sessionId).then((gone) => {
|
|
274
|
+
journalRemoving = false;
|
|
275
|
+
if (gone) journalOwed = false;
|
|
276
|
+
report(
|
|
277
|
+
gone
|
|
278
|
+
? `removed the checkpoint: a value marked sensitive could not be redacted in it (${redactionFailure})`
|
|
279
|
+
: 'the checkpoint journal still holds a value marked sensitive and could not be removed; trying again on the next flush'
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
// The rewrite that would redact a newly marked value failed: remove the
|
|
284
|
+
// checkpoint rather than leave the plaintext (losing recovery data is the
|
|
285
|
+
// lesser harm). Once header and journal are both gone the next flush opens a
|
|
286
|
+
// fresh, redacted one. When the header cannot be removed the recorder stops,
|
|
287
|
+
// so nothing more is appended to the files.
|
|
288
|
+
const failClosed = (cause) => {
|
|
289
|
+
redactionFailure = cause?.message || String(cause);
|
|
290
|
+
let removed = false;
|
|
291
|
+
try {
|
|
292
|
+
removed = clearTurnCheckpoint(sessionId, turnToken);
|
|
293
|
+
} catch {
|
|
294
|
+
// reported below with the stop
|
|
295
|
+
}
|
|
296
|
+
if (removed) {
|
|
297
|
+
opened = false;
|
|
298
|
+
headerPending = false;
|
|
299
|
+
journalOwed = true;
|
|
300
|
+
removeOwedJournal();
|
|
301
|
+
} else {
|
|
302
|
+
stopped = true;
|
|
303
|
+
cancelJournalWrites(sessionId);
|
|
304
|
+
report(
|
|
305
|
+
`a value marked sensitive could not be redacted in the checkpoint (${redactionFailure}); could not remove it, no longer recording`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
};
|
|
310
|
+
// Persistence state advances only once the header is written: a write that
|
|
311
|
+
// fails (a transient EPERM) leaves the header pending, so the next flush
|
|
312
|
+
// writes it again instead of appending journal deltas under a header that
|
|
313
|
+
// never landed (recovery reads no journal without one).
|
|
314
|
+
const commitHeader = (header, version) => {
|
|
315
|
+
headerPending = true;
|
|
316
|
+
let written;
|
|
317
|
+
try {
|
|
318
|
+
written = writeHeader(header);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
// The files on disk hold the turn from before a value was marked
|
|
321
|
+
// sensitive and could not be replaced: they must not outlive this call.
|
|
322
|
+
if (opened && version !== openedVersion) return failClosed(error);
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
headerPending = false;
|
|
326
|
+
openedVersion = version;
|
|
327
|
+
opened = true;
|
|
328
|
+
return written;
|
|
329
|
+
};
|
|
79
330
|
return {
|
|
80
331
|
get opened() {
|
|
81
332
|
return opened;
|
|
82
333
|
},
|
|
83
|
-
/** True when this call wrote or queued durable state.
|
|
84
|
-
|
|
334
|
+
/** True when this call wrote or queued durable state. `boundary` is the
|
|
335
|
+
* index of the turn's user message in `turnOutgoing` (read when the
|
|
336
|
+
* checkpoint opens); `captureContext(boundary)` reads the context state
|
|
337
|
+
* aligned with the header's split, re-read when `contextDirty`;
|
|
338
|
+
* `sealed` records the turn as finished. */
|
|
339
|
+
record({
|
|
340
|
+
boundary,
|
|
341
|
+
currentUserContent,
|
|
342
|
+
turnOutgoing,
|
|
343
|
+
interruption,
|
|
344
|
+
captureContext = null,
|
|
345
|
+
contextDirty = false,
|
|
346
|
+
sealed = false,
|
|
347
|
+
}) {
|
|
85
348
|
if (stopped || !sessionId || !turnToken) return false;
|
|
349
|
+
if (journalOwed) {
|
|
350
|
+
removeOwedJournal();
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
const source = Array.isArray(turnOutgoing) ? turnOutgoing : [];
|
|
86
354
|
const version = sensitiveValuesVersion(sessionId);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
...(version ? { journalEpoch: version } : {}),
|
|
103
|
-
interruption: snapshot,
|
|
104
|
-
contextState: seed.contextState,
|
|
105
|
-
});
|
|
106
|
-
openedVersion = version;
|
|
107
|
-
// Opening the journal is part of the anchor: a leftover journal
|
|
108
|
-
// from an earlier turn of this session must never replay onto
|
|
109
|
-
// the new header. It lands synchronously when the lane is idle
|
|
110
|
-
// and is otherwise FENCED behind the older in-flight write, so
|
|
111
|
-
// an old turn can never appear beneath this turn's token (the
|
|
112
|
-
// head record's token guards the interim window).
|
|
113
|
-
openJournalForTurn(sessionId, seed.openLines);
|
|
114
|
-
opened = true;
|
|
115
|
-
return true;
|
|
355
|
+
const header = { source, currentUserContent, interruption, captureContext, sealed };
|
|
356
|
+
if (!opened) {
|
|
357
|
+
const at = Number.isInteger(boundary) && boundary >= 0 && boundary <= source.length ? boundary : source.length;
|
|
358
|
+
if (opensOnBase(source, at)) {
|
|
359
|
+
fullTranscript = false;
|
|
360
|
+
offset = at;
|
|
361
|
+
transcriptEpoch = base.epoch;
|
|
362
|
+
} else {
|
|
363
|
+
fullTranscript = true;
|
|
364
|
+
offset = 0;
|
|
365
|
+
adoptEpoch(mintTranscriptEpoch());
|
|
366
|
+
}
|
|
367
|
+
pinned = source.slice(0, at);
|
|
368
|
+
openingIndex = at < source.length ? at : -1;
|
|
369
|
+
return commitHeader(header, version);
|
|
116
370
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
371
|
+
if (!pinnedIntact(source)) {
|
|
372
|
+
// A history rewrite. The turn's own opening message, found by
|
|
373
|
+
// identity, bounds the new pinned prefix; without it every message
|
|
374
|
+
// present now is pinned.
|
|
375
|
+
const at = openingRef ? source.indexOf(openingRef) : -1;
|
|
376
|
+
fullTranscript = true;
|
|
377
|
+
offset = 0;
|
|
378
|
+
adoptEpoch(mintTranscriptEpoch());
|
|
379
|
+
pinned = source.slice(0, at >= 0 ? at : source.length);
|
|
380
|
+
openingIndex = at;
|
|
381
|
+
return commitHeader(header, version);
|
|
382
|
+
}
|
|
383
|
+
if (headerPending || version !== openedVersion) return commitHeader(header, version);
|
|
384
|
+
if (contextDirty && typeof captureContext === 'function') contextState = captureContext(offset);
|
|
385
|
+
const lines = encoder.encode({ turnOutgoing: source, interruption, contextState, sealed });
|
|
386
|
+
trackOpening(source);
|
|
123
387
|
if (lines.length === 0) return false;
|
|
124
|
-
appendJournalLines(sessionId, lines);
|
|
388
|
+
appendJournalLines(sessionId, lines, { immediate: sealed });
|
|
125
389
|
return true;
|
|
126
390
|
},
|
|
127
|
-
|
|
391
|
+
/** `keepQueued`: the turn was sealed, so its queued appends still land. */
|
|
392
|
+
stop({ keepQueued = false } = {}) {
|
|
128
393
|
if (stopped) return;
|
|
129
394
|
stopped = true;
|
|
395
|
+
if (journalOwed) removeOwedJournal();
|
|
130
396
|
// Retire queued (not yet written) appends. The durable prefix stays on
|
|
131
|
-
// disk on purpose:
|
|
132
|
-
//
|
|
133
|
-
cancelJournalWrites(sessionId);
|
|
397
|
+
// disk on purpose: until a save covering the turn lands a crash must
|
|
398
|
+
// still recover the turn's work, not just its opening header.
|
|
399
|
+
if (!keepQueued) cancelJournalWrites(sessionId);
|
|
134
400
|
},
|
|
135
401
|
};
|
|
136
402
|
}
|
|
@@ -147,6 +413,9 @@ export function readTurnCheckpoint(sessionId) {
|
|
|
147
413
|
// the directory probes left the event loop.
|
|
148
414
|
export function clearTurnCheckpoint(sessionId, turnToken = null) {
|
|
149
415
|
const target = turnCheckpointReadPath(sessionId);
|
|
416
|
+
// The files of a set-aside still being moved are not this checkpoint's to
|
|
417
|
+
// clear (turn-checkpoint-journal.mjs): it is finished first, or kept.
|
|
418
|
+
if (finishPendingSetAside(sessionId).pending) return false;
|
|
150
419
|
// Queued appends are stale the moment the caller decides to clear this
|
|
151
420
|
// turn's checkpoint; retire them BEFORE the token guard reads disk so a
|
|
152
421
|
// lagging write can never resurrect state after the unlink.
|
|
@@ -180,44 +449,48 @@ export function clearTurnCheckpoint(sessionId, turnToken = null) {
|
|
|
180
449
|
}
|
|
181
450
|
|
|
182
451
|
/**
|
|
183
|
-
* Merge a force-killed turn checkpoint into a loaded session
|
|
184
|
-
*
|
|
452
|
+
* Merge a force-killed turn checkpoint into a loaded session, deciding from
|
|
453
|
+
* the canonical record `durable` as it is on disk (checkpointDisposition) —
|
|
454
|
+
* never from the in-memory session's freshness. Returns `covered` when that
|
|
455
|
+
* record already carries the turn (the caller deletes the checkpoint) and
|
|
456
|
+
* `conflict` when the checkpoint was not taken against it (kept, reported).
|
|
457
|
+
* A recovered session carries the turn's coverage token; the caller must
|
|
458
|
+
* durably save it before clearing the returned token.
|
|
185
459
|
*/
|
|
186
|
-
export function recoverTurnCheckpoint(session) {
|
|
460
|
+
export function recoverTurnCheckpoint(session, durableRecord = undefined) {
|
|
187
461
|
if (!session?.id || session.closed === true) {
|
|
188
462
|
return { changed: false, recovered: false, turnToken: null };
|
|
189
463
|
}
|
|
190
464
|
const checkpoint = readTurnCheckpoint(session.id);
|
|
191
465
|
const marker = session.activeTurnCheckpoint;
|
|
192
466
|
if (!checkpoint) {
|
|
467
|
+
// A journal left without its header (a removal that failed, or a restart
|
|
468
|
+
// that cut it short) can never be recovered: delete it.
|
|
469
|
+
removeOrphanTurnJournal(session.id);
|
|
193
470
|
if (!marker) return { changed: false, recovered: false, turnToken: null };
|
|
194
471
|
delete session.activeTurnCheckpoint;
|
|
195
472
|
return { changed: true, recovered: false, turnToken: null };
|
|
196
473
|
}
|
|
197
474
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
475
|
+
const durable = durableRecord === undefined ? readDurableSessionRecord(session.id) : durableRecord;
|
|
476
|
+
const disposition = checkpointDisposition(durable, checkpoint);
|
|
477
|
+
if (disposition.action === 'covered') {
|
|
478
|
+
return { changed: false, recovered: false, covered: true, turnToken: checkpoint.turnToken };
|
|
479
|
+
}
|
|
480
|
+
if (disposition.action === 'conflict') {
|
|
481
|
+
reportCheckpointConflict(session.id, checkpoint, disposition.reason);
|
|
482
|
+
return { changed: false, recovered: false, conflict: disposition.reason, turnToken: null };
|
|
204
483
|
}
|
|
205
484
|
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// The route switched models mid-turn (the marker says so) and the journal
|
|
216
|
-
// may predate the switch: its turn still carries the previous provider's
|
|
217
|
-
// native replay state, which the saved route's provider must not receive.
|
|
218
|
-
const turnMessages =
|
|
219
|
-
marker?.routeChanged === true ? stripProviderReplayMessages(finalized.messages) : finalized.messages;
|
|
220
|
-
session.messages = checkpoint.fullTranscript === true ? turnMessages : [...kept, ...turnMessages];
|
|
485
|
+
const routeChanged = [durable.activeTurnCheckpoint, marker].some(
|
|
486
|
+
(entry) => entry?.turnToken === checkpoint.turnToken && entry.routeChanged === true
|
|
487
|
+
);
|
|
488
|
+
const finalized = replayCheckpoint(durable, checkpoint, disposition.boundary, routeChanged);
|
|
489
|
+
session.messages = finalized.messages;
|
|
490
|
+
// The splice is a history rewrite of its own, and this record now covers
|
|
491
|
+
// the turn: a crash after it is saved never replays the checkpoint again.
|
|
492
|
+
session.transcriptEpoch = mintTranscriptEpoch();
|
|
493
|
+
session.turnCoverage = checkpoint.turnToken;
|
|
221
494
|
// Re-anchor provider usage / post-compact replacement state to the exact
|
|
222
495
|
// canonical checkpoint representation. If a legacy or corrupted checkpoint
|
|
223
496
|
// cannot prove alignment, retain the last provider reading for display but
|