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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// manager/turn-durability.mjs
|
|
2
|
+
// One unsettled turn per session. A finished (or failed) turn stays
|
|
3
|
+
// unsettled — its checkpoint kept — until a landed save of its session
|
|
4
|
+
// carries the turn's coverage token (store/transcript-durability.mjs); only
|
|
5
|
+
// that acknowledgement deletes the checkpoint. The next turn of the session
|
|
6
|
+
// waits for it before it opens its own checkpoint or touches the session, and
|
|
7
|
+
// a save that cannot land blocks that turn with a TurnPersistenceError
|
|
8
|
+
// instead of releasing it. A settled promise, an idle queue or a coalesced,
|
|
9
|
+
// cancelled or dropped write is never an acknowledgement. Sessions are
|
|
10
|
+
// independent: nothing here waits on another session's saves.
|
|
11
|
+
import { readSessionLifecycleFromDisk, saveSessionAsync } from '../store.mjs';
|
|
12
|
+
import { landedTranscript, landedTranscriptMatches, mintTranscriptEpoch } from '../store/transcript-durability.mjs';
|
|
13
|
+
import { clearTurnCheckpoint } from './turn-checkpoint.mjs';
|
|
14
|
+
|
|
15
|
+
const _unsettled = new Map(); // sessionId → { session, turnToken, generation, ack, error }
|
|
16
|
+
|
|
17
|
+
export class TurnPersistenceError extends Error {
|
|
18
|
+
constructor(sessionId, message, cause = null) {
|
|
19
|
+
super(`Session ${sessionId}: ${message}${cause ? ` (${cause.message || cause})` : ''}`);
|
|
20
|
+
this.name = 'TurnPersistenceError';
|
|
21
|
+
this.code = 'ETURNNOTDURABLE';
|
|
22
|
+
this.sessionId = sessionId;
|
|
23
|
+
if (cause) this.cause = cause;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Split-brain re-adoption: another surface (e.g. desktop click-through
|
|
28
|
+
// of a session still actively owned by this process) may have
|
|
29
|
+
// resumed-and-detached this session, bumping the ON-DISK generation
|
|
30
|
+
// while the conversation kept going here. Every save from this process
|
|
31
|
+
// would then be silently dropped by _shouldDrop's ownership rule and
|
|
32
|
+
// the on-disk transcript would freeze at the last landed save. A new
|
|
33
|
+
// ask on a NON-closed session is an explicit ownership claim: adopt
|
|
34
|
+
// the disk generation so this turn's commits land and heal the file.
|
|
35
|
+
export function adoptDiskGeneration(session, sessionId) {
|
|
36
|
+
const diskLifecycle = readSessionLifecycleFromDisk(sessionId);
|
|
37
|
+
if (
|
|
38
|
+
diskLifecycle &&
|
|
39
|
+
diskLifecycle.closed !== true &&
|
|
40
|
+
diskLifecycle.generation > (typeof session.generation === 'number' ? session.generation : 0)
|
|
41
|
+
) {
|
|
42
|
+
session.generation = diskLifecycle.generation;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Whether a landed save of `session` carries `turnToken`'s coverage. */
|
|
47
|
+
export function turnCoveredOnDisk(session, turnToken) {
|
|
48
|
+
return landedTranscript(session)?.coverage === turnToken;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function release(sessionId, entry) {
|
|
52
|
+
if (_unsettled.get(sessionId) !== entry) return;
|
|
53
|
+
_unsettled.delete(sessionId);
|
|
54
|
+
clearTurnCheckpoint(sessionId, entry.turnToken);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Keep `turnToken` (already the session's `turnCoverage`) unsettled until a
|
|
58
|
+
* landed save covers it; `save` is the save expected to. Resolves whether
|
|
59
|
+
* it did. */
|
|
60
|
+
export function holdTurnUntilDurable({ sessionId, session, turnToken, generation, save }) {
|
|
61
|
+
const entry = { session, turnToken, generation, error: null, ack: null };
|
|
62
|
+
_unsettled.set(sessionId, entry);
|
|
63
|
+
entry.ack = Promise.resolve(save)
|
|
64
|
+
.then(
|
|
65
|
+
() => turnCoveredOnDisk(session, turnToken),
|
|
66
|
+
(error) => {
|
|
67
|
+
entry.error = error;
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
.then((covered) => {
|
|
72
|
+
if (covered) release(sessionId, entry);
|
|
73
|
+
return covered;
|
|
74
|
+
});
|
|
75
|
+
return entry.ack;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Whether this process holds a turn of `sessionId` not yet durable. */
|
|
79
|
+
export function hasUnsettledTurn(sessionId) {
|
|
80
|
+
return _unsettled.has(sessionId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A closed or deleted session: nothing will open on it again. */
|
|
84
|
+
export function forgetUnsettledTurn(sessionId) {
|
|
85
|
+
_unsettled.delete(sessionId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Resolves once the previous turn of `sessionId` is durably covered (its
|
|
89
|
+
* checkpoint then deleted). When its own save did not land it, one fresh
|
|
90
|
+
* save of the finalized transcript is attempted; if that does not land
|
|
91
|
+
* either, throws TurnPersistenceError and the checkpoint stays. A save the
|
|
92
|
+
* ownership guard refused because another surface moved the disk generation
|
|
93
|
+
* is retried under that generation: this ask is the ownership claim that
|
|
94
|
+
* adoptDiskGeneration describes, and the retry stays guarded by it, so a
|
|
95
|
+
* closed session or a further handoff still refuses it. */
|
|
96
|
+
export async function awaitPreviousTurnDurable(sessionId) {
|
|
97
|
+
const entry = _unsettled.get(sessionId);
|
|
98
|
+
if (!entry || (await entry.ack)) return;
|
|
99
|
+
adoptDiskGeneration(entry.session, sessionId);
|
|
100
|
+
entry.generation = Math.max(entry.generation, entry.session.generation);
|
|
101
|
+
try {
|
|
102
|
+
await saveSessionAsync(entry.session, { expectedGeneration: entry.generation });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
entry.error = error;
|
|
105
|
+
}
|
|
106
|
+
if (!turnCoveredOnDisk(entry.session, entry.turnToken)) {
|
|
107
|
+
throw new TurnPersistenceError(
|
|
108
|
+
sessionId,
|
|
109
|
+
'the previous turn is not saved to disk, so the next turn cannot start',
|
|
110
|
+
entry.error
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
release(sessionId, entry);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Make `session`'s current transcript durable, so a turn opening on it
|
|
117
|
+
* names a base that is on disk. A transcript that is not a pure extension
|
|
118
|
+
* of the last landed one (an unsaved rewrite) starts a new epoch first. */
|
|
119
|
+
export async function ensureDurableTranscript(session) {
|
|
120
|
+
if (landedTranscriptMatches(session)) return;
|
|
121
|
+
const landed = landedTranscript(session);
|
|
122
|
+
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
123
|
+
const extendsLanded =
|
|
124
|
+
landed &&
|
|
125
|
+
landed.epoch === session.transcriptEpoch &&
|
|
126
|
+
landed.refs.length <= messages.length &&
|
|
127
|
+
landed.refs.every((message, index) => message === messages[index]);
|
|
128
|
+
if (typeof session.transcriptEpoch !== 'string' || (landed && !extendsLanded)) {
|
|
129
|
+
session.transcriptEpoch = mintTranscriptEpoch();
|
|
130
|
+
}
|
|
131
|
+
let failure = null;
|
|
132
|
+
try {
|
|
133
|
+
await saveSessionAsync(session, { expectedGeneration: session.generation });
|
|
134
|
+
} catch (error) {
|
|
135
|
+
failure = error;
|
|
136
|
+
}
|
|
137
|
+
if (!landedTranscriptMatches(session)) {
|
|
138
|
+
throw new TurnPersistenceError(
|
|
139
|
+
session.id,
|
|
140
|
+
'the conversation is not saved to disk, so the next turn cannot start',
|
|
141
|
+
failure
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -225,7 +225,7 @@ function textBufferMethods(state) {
|
|
|
225
225
|
// The replayed attempt has started producing its OWN text, so the
|
|
226
226
|
// retracted tail it replaces is no longer what the user is looking
|
|
227
227
|
// at. Dropping it here keeps exactly one copy of the opening
|
|
228
|
-
// instead of one per retry (a 3-retry stall
|
|
228
|
+
// instead of one per retry (a 3-retry stall would persist four
|
|
229
229
|
// copies of the same first sentence). Reference agents never
|
|
230
230
|
// commit a replayed attempt's discarded stream at all; the
|
|
231
231
|
// tombstone exists only for the window between a retraction and
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { providerInputExcludesCache } from '../../providers/registry.mjs';
|
|
9
9
|
import { reasoningUsage, combineReasoningUsage } from '../../../../shared/llm/reasoning-usage.mjs';
|
|
10
10
|
import { foldUsageReadout } from '../../../../shared/usage-readout-totals.mjs';
|
|
11
|
+
import { emitSessionActivityChange } from '../../../../shared/session-activity-signal.mjs';
|
|
11
12
|
import {
|
|
12
13
|
loadSession,
|
|
13
14
|
saveSessionAsync,
|
|
@@ -560,6 +561,9 @@ export async function persistIterationMetrics(delta) {
|
|
|
560
561
|
// saved before this field began at their first call after it.
|
|
561
562
|
session.usageReadout = foldUsageReadout(session.usageReadout, delta, delta.requestMs);
|
|
562
563
|
recordLastContextTokens(session, iterationContext(delta));
|
|
564
|
+
// The Agents pool shows each agent's running totals from this live
|
|
565
|
+
// counter: it re-reads on this pulse instead of the next throttled save.
|
|
566
|
+
emitSessionActivityChange(sessionId);
|
|
563
567
|
}
|
|
564
568
|
session.lastIterationIndex = iterationIndex;
|
|
565
569
|
session.updatedAt = ts || Date.now();
|
|
@@ -65,7 +65,12 @@ export {
|
|
|
65
65
|
settlePendingMessageWrites,
|
|
66
66
|
markCompletionEntry,
|
|
67
67
|
COMPLETION_NOTIFICATION_KIND,
|
|
68
|
+
hasPendingMessages,
|
|
68
69
|
} from './manager/pending-messages.mjs';
|
|
70
|
+
// The per-session ask mutex every turn holds, tried without waiting, and the
|
|
71
|
+
// unsettled-turn check: the idle-only delete (session-delete.mjs) acts under them.
|
|
72
|
+
export { tryAcquireSessionLock, sessionLockContended } from './manager/session-lock.mjs';
|
|
73
|
+
export { hasUnsettledTurn } from './manager/turn-durability.mjs';
|
|
69
74
|
|
|
70
75
|
// ── Usage-metrics surface — re-exported unchanged so loop.mjs / smoke scripts
|
|
71
76
|
// keep resolving these through the facade. ────────────────────────────────
|
|
@@ -96,6 +101,7 @@ export { prewarmAgentLoop } from './manager/runtime-loaders.mjs';
|
|
|
96
101
|
export {
|
|
97
102
|
createSession,
|
|
98
103
|
updateSessionRoute,
|
|
104
|
+
holdSessionRoute,
|
|
99
105
|
prefetchSession,
|
|
100
106
|
prepareSessionProjection,
|
|
101
107
|
resumeSession,
|
|
@@ -15,7 +15,7 @@ import { mixyIdentityDir } from '../../../shared/agent-store.mjs';
|
|
|
15
15
|
export const DEFAULT_MIXY_NAME = 'Mixy';
|
|
16
16
|
|
|
17
17
|
/** The display name in a lead manifest (agent.json), else Mixy. */
|
|
18
|
-
|
|
18
|
+
function mixyNameOf(manifest) {
|
|
19
19
|
const name = typeof manifest?.name === 'string' ? manifest.name.replace(/\s+/g, ' ').trim() : '';
|
|
20
20
|
return name || DEFAULT_MIXY_NAME;
|
|
21
21
|
}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
snapshotPendingGoalReminder,
|
|
42
42
|
} from '../runtime-core/goal-reminder.mjs';
|
|
43
43
|
import { rearmLateDeferredTools } from '../runtime-core/tool-catalog.mjs';
|
|
44
|
+
import { rearmPromptSettingsNotice } from './manager/prompt-settings.mjs';
|
|
44
45
|
|
|
45
46
|
async function setStage(opts, stage) {
|
|
46
47
|
try {
|
|
@@ -437,7 +438,12 @@ function adoptCompactedTranscript(ctx, run) {
|
|
|
437
438
|
// The system prompt is rebuilt from current sources outside a running turn
|
|
438
439
|
// (session-runtime agent-profile-fold.mjs): the next cold point folds it in
|
|
439
440
|
// instead of adding a break to the turn that is already warm again.
|
|
440
|
-
|
|
441
|
+
// The fold runs at the next turn start (compactionFoldPending): it lists the
|
|
442
|
+
// late tools and the settings this compaction dropped the notices of.
|
|
443
|
+
if (sessionRef) {
|
|
444
|
+
sessionRef.promptFoldPending = true;
|
|
445
|
+
sessionRef.compactionFoldPending = true;
|
|
446
|
+
}
|
|
441
447
|
resetReadStateAfterCompaction(sessionId);
|
|
442
448
|
// Cross-turn receipts reference tool results that compaction just removed
|
|
443
449
|
// from context; a stub for them would point at nothing. Clear in place so
|
|
@@ -573,6 +579,7 @@ async function compactBeforeSend(ctx) {
|
|
|
573
579
|
// The adopted compaction strips late MCP tool announcements from older turns.
|
|
574
580
|
try {
|
|
575
581
|
rearmLateDeferredTools(sessionRef);
|
|
582
|
+
rearmPromptSettingsNotice(sessionRef);
|
|
576
583
|
} catch {
|
|
577
584
|
/* best-effort, like the Goal reminder: a tool reminder never fails the compaction */
|
|
578
585
|
}
|
|
@@ -60,9 +60,13 @@ parentPort.on('message', (message) => {
|
|
|
60
60
|
}
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
const { session, delta, id, opts, reqId } = message || {};
|
|
63
|
+
const { session, delta, id, opts, reqId, dataDir } = message || {};
|
|
64
64
|
let full = session;
|
|
65
65
|
try {
|
|
66
|
+
// This realm's env was copied when the worker spawned; the parent's data
|
|
67
|
+
// dir may have moved since (save-worker.mjs _postAsyncWrite), and every
|
|
68
|
+
// path below resolves it from the env at call time.
|
|
69
|
+
if (dataDir) process.env.MIXDOG_DATA_DIR = dataDir;
|
|
66
70
|
const sessionId = id ?? session?.id;
|
|
67
71
|
if (!full && delta) {
|
|
68
72
|
const base = _baseMessages.get(sessionId);
|
|
@@ -190,7 +190,7 @@ export function withdrawPendingOwnCommit(target, scratchStamp) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/** Read-only lifecycle verdict of a pending peer commit whose identity `stamp` carries. */
|
|
193
|
-
|
|
193
|
+
function pendingOwnCommittedLifecycle(target, stamp) {
|
|
194
194
|
_absorbPeerCommits();
|
|
195
195
|
const pending = _pendingOwnCommits.get(target);
|
|
196
196
|
return pending && _sameScratchIdentity(pending.stamp, stamp) ? pending.value : null;
|
|
@@ -17,7 +17,7 @@ import { probePath, readTextFile, PROBE_PRESENT } from './fs-probe.mjs';
|
|
|
17
17
|
export const LEGACY_MARKER_SUFFIXES = ['.hb', '.own'];
|
|
18
18
|
// An older live owner rewrote `.hb` every ≤5s mid-turn and `.own` every ~20s
|
|
19
19
|
// while holding the session open; one untouched this long holds nothing.
|
|
20
|
-
|
|
20
|
+
const LEGACY_MARKER_FRESH_MS = 2 * 60 * 1000;
|
|
21
21
|
|
|
22
22
|
function processAlive(pid) {
|
|
23
23
|
try {
|
|
@@ -47,7 +47,7 @@ export function readLegacyMarker(path, now = Date.now()) {
|
|
|
47
47
|
|
|
48
48
|
/** Whether a legacy marker of the session names a live owner pid with a
|
|
49
49
|
* fresh heartbeat: that session is driven by another live process. */
|
|
50
|
-
|
|
50
|
+
function _legacyOwnerLivenessVeto(id, now = Date.now()) {
|
|
51
51
|
if (typeof id !== 'string' || !/^[A-Za-z0-9_-]+$/.test(id)) return false;
|
|
52
52
|
const dir = getStoreDir();
|
|
53
53
|
return LEGACY_MARKER_SUFFIXES.some((suffix) => {
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
_acquireSessionIncarnation,
|
|
19
19
|
_releaseSessionIncarnation,
|
|
20
20
|
_isCurrentSessionIncarnation,
|
|
21
|
+
SAVE_OUTCOME_SAVED,
|
|
22
|
+
SAVE_OUTCOME_DROPPED,
|
|
21
23
|
SAVE_OUTCOME_STALE,
|
|
22
24
|
} from './live-state.mjs';
|
|
23
25
|
import {
|
|
@@ -33,6 +35,8 @@ import {
|
|
|
33
35
|
_sessionStoreTestMode,
|
|
34
36
|
} from './save-fault.mjs';
|
|
35
37
|
import { connectOwnCommitPeer } from './canonical-reader.mjs';
|
|
38
|
+
import { getPluginData } from '../../config.mjs';
|
|
39
|
+
import { captureTranscriptSnapshot, recordLandedTranscript } from './transcript-durability.mjs';
|
|
36
40
|
|
|
37
41
|
// ── Worker-thread async save ──────────────────────────────────────────────────
|
|
38
42
|
// Single long-lived Worker serializes all saveSessionAsync calls.
|
|
@@ -278,6 +282,7 @@ function _settleLandedWrite(
|
|
|
278
282
|
// built — never a row re-derived from the live session, which
|
|
279
283
|
// may already carry a newer (unwritten, possibly failing) turn.
|
|
280
284
|
_queueSessionSummaryUpsertRow(summaryRow, summaryVersion);
|
|
285
|
+
recordLandedTranscript(p.session, p.transcript, epoch);
|
|
281
286
|
// ONLY a landed write proves durability, so this is the only
|
|
282
287
|
// place the save-failure flag may be cleared — and only when
|
|
283
288
|
// THIS payload is at least as new as the newest failed/dropped
|
|
@@ -304,7 +309,14 @@ function _settleLandedWrite(
|
|
|
304
309
|
_rollbackCachedSessionSummary(id, summaryVersion);
|
|
305
310
|
_recordSaveDrop(id, epoch);
|
|
306
311
|
}
|
|
307
|
-
|
|
312
|
+
// Callers that act only on a durable write (e.g. a migration deleting its
|
|
313
|
+
// source) read this outcome; every other resolution leaves it undefined.
|
|
314
|
+
const settled = saved
|
|
315
|
+
? SAVE_OUTCOME_SAVED
|
|
316
|
+
: outcome === SAVE_OUTCOME_STALE
|
|
317
|
+
? SAVE_OUTCOME_STALE
|
|
318
|
+
: SAVE_OUTCOME_DROPPED;
|
|
319
|
+
for (const w of waiters) w.resolve(settled);
|
|
308
320
|
}
|
|
309
321
|
|
|
310
322
|
/**
|
|
@@ -531,6 +543,9 @@ function _postAsyncWrite(
|
|
|
531
543
|
{ forceFull = false, retriedFull = false } = {}
|
|
532
544
|
) {
|
|
533
545
|
const reqId = ++_saveWorkerReqId;
|
|
546
|
+
// Before the bytes are built: what this write will prove durable once it
|
|
547
|
+
// lands (store/transcript-durability.mjs).
|
|
548
|
+
const transcript = captureTranscriptSnapshot(session);
|
|
534
549
|
// The wire payload is built at POST time from the live session: N calls
|
|
535
550
|
// coalesced behind one in-flight write project ONCE (newest state), and a
|
|
536
551
|
// pure-append turn ships only the tail (see _buildWirePayload).
|
|
@@ -548,6 +563,7 @@ function _postAsyncWrite(
|
|
|
548
563
|
_saveWorkerPending.set(reqId, {
|
|
549
564
|
id,
|
|
550
565
|
session,
|
|
566
|
+
transcript,
|
|
551
567
|
attempt,
|
|
552
568
|
opts,
|
|
553
569
|
summaryVersion,
|
|
@@ -567,7 +583,12 @@ function _postAsyncWrite(
|
|
|
567
583
|
// Cheap key compare; posts only when the fault config/env moved.
|
|
568
584
|
_syncFaultToWorker(w);
|
|
569
585
|
const { attempt: _attempt, ...wireForPost } = wire;
|
|
570
|
-
|
|
586
|
+
// The worker's process.env is a copy taken at spawn, so a data dir that
|
|
587
|
+
// moved since (a pristine headless run per page) would otherwise keep
|
|
588
|
+
// receiving writes in the first run's removed directory. The dir current
|
|
589
|
+
// at POST time travels with the write: a write still queued in the worker
|
|
590
|
+
// when the env flips lands where its own session lives.
|
|
591
|
+
w.postMessage({ ...wireForPost, id, opts, reqId, dataDir: getPluginData() });
|
|
571
592
|
// Ref AFTER successful postMessage so a queue/throw failure path does
|
|
572
593
|
// not leave the worker held alive with no pending message. Paired with
|
|
573
594
|
// the unref in the message handler when count hits 0.
|
|
@@ -16,12 +16,12 @@ import { deleteSession } from '../session-delete.mjs';
|
|
|
16
16
|
import { readSweepRecord } from './sweep-record.mjs';
|
|
17
17
|
|
|
18
18
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const SUB_AGENT_ARCHIVE_AFTER_MS = 3 * DAY_MS;
|
|
20
|
+
const SUB_AGENT_DELETE_AFTER_MS = 30 * DAY_MS;
|
|
21
21
|
const PERMANENT_ID = /^sess_(?:home|room|rmem)_/;
|
|
22
22
|
// The desktop change an automatic archive applies: that one session, no
|
|
23
23
|
// cascade (an explicit archive of a parent takes its sub-agents along).
|
|
24
|
-
|
|
24
|
+
const SUB_AGENT_AUTO_ARCHIVE = Object.freeze({ archived: true, cascade: false });
|
|
25
25
|
|
|
26
26
|
const positive = (value) => (Number(value) > 0 ? Number(value) : 0);
|
|
27
27
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// store/transcript-durability.mjs
|
|
2
|
+
// What a session's canonical record durably holds, as evidence for the turn
|
|
3
|
+
// checkpoints (manager/turn-checkpoint.mjs) — never inferred from in-memory
|
|
4
|
+
// freshness.
|
|
5
|
+
//
|
|
6
|
+
// transcriptEpoch persisted. Names one append-only lineage of the
|
|
7
|
+
// transcript: a record of epoch E with `length` messages
|
|
8
|
+
// agrees with every other record of E on their common
|
|
9
|
+
// prefix. A history rewrite (compaction, a prefix edit,
|
|
10
|
+
// a recovery splice) mints a new epoch.
|
|
11
|
+
// turnCoverage persisted. The token of the last turn whose finalized
|
|
12
|
+
// transcript this record carries, so a checkpoint the
|
|
13
|
+
// record already covers is deleted, never replayed.
|
|
14
|
+
//
|
|
15
|
+
// A landed save records, per session object, the exact epoch, coverage and
|
|
16
|
+
// message references it serialized (captured when the bytes were built, so a
|
|
17
|
+
// coalesced or later-built write reports what it actually wrote). Only that
|
|
18
|
+
// record proves what is on disk; a settled promise or an idle queue does not.
|
|
19
|
+
import { randomUUID } from 'node:crypto';
|
|
20
|
+
import { basename, dirname } from 'node:path';
|
|
21
|
+
import { sessionPath } from './session-paths.mjs';
|
|
22
|
+
import { _storedSessionFromFile, STORED_SESSION_UNREADABLE } from './serialize.mjs';
|
|
23
|
+
|
|
24
|
+
const _landed = new WeakMap(); // session → { epoch, coverage, refs, saveEpoch }
|
|
25
|
+
const _snapshotHooks = new WeakMap(); // session → () => void
|
|
26
|
+
|
|
27
|
+
export function mintTranscriptEpoch() {
|
|
28
|
+
return randomUUID();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Run `hook` before every serialization of `session` (the turn checkpoint
|
|
32
|
+
* journals a history rewrite, under a new epoch, before any record carries
|
|
33
|
+
* it). Returns the unregister function. */
|
|
34
|
+
export function setTranscriptSnapshotHook(session, hook) {
|
|
35
|
+
_snapshotHooks.set(session, hook);
|
|
36
|
+
return () => {
|
|
37
|
+
if (_snapshotHooks.get(session) === hook) _snapshotHooks.delete(session);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The transcript identity a save of `session` is about to serialize. */
|
|
42
|
+
export function captureTranscriptSnapshot(session) {
|
|
43
|
+
_snapshotHooks.get(session)?.();
|
|
44
|
+
return {
|
|
45
|
+
epoch: typeof session.transcriptEpoch === 'string' ? session.transcriptEpoch : null,
|
|
46
|
+
coverage: typeof session.turnCoverage === 'string' ? session.turnCoverage : null,
|
|
47
|
+
refs: Array.isArray(session.messages) ? session.messages.slice() : [],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A save of `snapshot` (issued under `saveEpoch`) landed on disk. */
|
|
52
|
+
export function recordLandedTranscript(session, snapshot, saveEpoch) {
|
|
53
|
+
if (!session || !snapshot) return;
|
|
54
|
+
const current = _landed.get(session);
|
|
55
|
+
const issued = Number.isFinite(saveEpoch) ? saveEpoch : 0;
|
|
56
|
+
if (current && current.saveEpoch > issued) return;
|
|
57
|
+
_landed.set(session, { ...snapshot, saveEpoch: issued });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function landedTranscript(session) {
|
|
61
|
+
return (session && _landed.get(session)) || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Whether the last landed save of `session` holds exactly its transcript. */
|
|
65
|
+
export function landedTranscriptMatches(session) {
|
|
66
|
+
const landed = landedTranscript(session);
|
|
67
|
+
const messages = Array.isArray(session?.messages) ? session.messages : null;
|
|
68
|
+
if (!landed || !messages || landed.epoch !== session.transcriptEpoch) return false;
|
|
69
|
+
if (landed.refs.length !== messages.length) return false;
|
|
70
|
+
return landed.refs.every((message, index) => message === messages[index]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The durable transcript a turn opening now would be taken against
|
|
74
|
+
* (`{ epoch, refs }`), or null when no landed save holds `session`'s
|
|
75
|
+
* current transcript exactly. */
|
|
76
|
+
export function durableTranscriptBase(session) {
|
|
77
|
+
if (!landedTranscriptMatches(session)) return null;
|
|
78
|
+
const landed = landedTranscript(session);
|
|
79
|
+
return { epoch: landed.epoch, refs: landed.refs };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The canonical record of `id` as it is on disk (never a cached or live
|
|
83
|
+
* copy), or null when it is absent, unreadable or not this session's. */
|
|
84
|
+
export function readDurableSessionRecord(id) {
|
|
85
|
+
const path = sessionPath(id);
|
|
86
|
+
const record = _storedSessionFromFile(dirname(path), basename(path));
|
|
87
|
+
return record && record !== STORED_SESSION_UNREADABLE ? record : null;
|
|
88
|
+
}
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
import { dataDir, storedAgentWorkerIndexPath, storedLeadWorkerIndexPath } from './store-summary-locations.mjs';
|
|
43
43
|
import { cleanValue, isStoredSessionId, positiveNumber } from './store-summary-fields.mjs';
|
|
44
44
|
import { sessionUsageTotals } from '../../../shared/session-usage-totals.mjs';
|
|
45
|
+
import { todoProgress } from '../runtime-core/todo-list.mjs';
|
|
45
46
|
|
|
46
47
|
const WORKING_AGENT_STATUS =
|
|
47
48
|
/^(?:connecting|requesting|streaming|tool[-_\s]?running|running|queued|pending|starting|cancelling)$/i;
|
|
@@ -327,6 +328,7 @@ const WORKER_HEADER_FIELDS = [
|
|
|
327
328
|
'totalInputTokens',
|
|
328
329
|
'totalOutputTokens',
|
|
329
330
|
'totalToolCalls',
|
|
331
|
+
'todo',
|
|
330
332
|
...SESSION_CANCEL_FIELDS,
|
|
331
333
|
];
|
|
332
334
|
|
|
@@ -584,6 +586,7 @@ function projectChildWorkerRow(row, context) {
|
|
|
584
586
|
// beside a header that records a cancel-unconfirmed (possibly running)
|
|
585
587
|
// process. A new row may precede its first session save (null header).
|
|
586
588
|
const session = readWorkerSessionHeader(sessionId);
|
|
589
|
+
const todo = todoProgress(session);
|
|
587
590
|
const declaredStatus = cleanValue(row.status) || 'running';
|
|
588
591
|
const declaredStage = cleanValue(row.stage || row.status) || 'running';
|
|
589
592
|
const declaredWorking = WORKING_AGENT_STATUS.test(workerLifecycle(row) || 'running');
|
|
@@ -649,6 +652,8 @@ function projectChildWorkerRow(row, context) {
|
|
|
649
652
|
clientHostPid: positiveNumber(row.clientHostPid || session?.clientHostPid, 0) || null,
|
|
650
653
|
taskId: cleanValue(row.task_id || row.taskId) || null,
|
|
651
654
|
...sessionUsageTotals(session),
|
|
655
|
+
// The agent's own step list as counts only; a closed agent keeps its last.
|
|
656
|
+
...(todo ? { todo } : {}),
|
|
652
657
|
};
|
|
653
658
|
}
|
|
654
659
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { readFileSync, statSync } from 'node:fs';
|
|
20
20
|
import { dirname, join } from 'node:path';
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
const SUMMARY_LOG_FILENAME = 'session-summaries.log';
|
|
23
23
|
|
|
24
24
|
export function summaryLogPath(indexPath) {
|
|
25
25
|
return join(dirname(indexPath), SUMMARY_LOG_FILENAME);
|
|
@@ -15,6 +15,7 @@ import { join } from 'node:path';
|
|
|
15
15
|
import { desktopSession, positiveNumber } from './store-summary-fields.mjs';
|
|
16
16
|
import { compactionArchiveRange, readCompactionArchivePage } from './compaction-archive.mjs';
|
|
17
17
|
import { dataDir } from './store-summary-locations.mjs';
|
|
18
|
+
import { todoSnapshot } from '../runtime-core/todo-list.mjs';
|
|
18
19
|
|
|
19
20
|
/** The parse/recovery/projection body of a stored transcript read. Runs once
|
|
20
21
|
* per distinct content; its result is shared read-only through the cache. */
|
|
@@ -40,7 +41,9 @@ export async function prepareStoredTranscriptBase(sessionId, doc, options) {
|
|
|
40
41
|
// A running turn is never recovered: project its checkpoint read-only.
|
|
41
42
|
if (isSessionTurnLive(sessionId)) projectCheckpoint();
|
|
42
43
|
else {
|
|
43
|
-
|
|
44
|
+
// `doc` is the record exactly as this read parsed it from disk: the
|
|
45
|
+
// durable evidence recovery decides on.
|
|
46
|
+
session = recoverSessionAfterProcessRestart(sessionId, doc) || session;
|
|
44
47
|
// A closed session keeps its marker unrecovered.
|
|
45
48
|
if (session?.activeTurnCheckpoint) projectCheckpoint();
|
|
46
49
|
}
|
|
@@ -78,9 +81,10 @@ export async function prepareStoredTranscriptBase(sessionId, doc, options) {
|
|
|
78
81
|
}
|
|
79
82
|
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
80
83
|
const hasConversationActivity = messages.some((message) => message?.role === 'user');
|
|
81
|
-
const measuredStats =
|
|
82
|
-
measurement: sessionContextMeasurement(session, hasConversationActivity),
|
|
83
|
-
|
|
84
|
+
const measuredStats = {
|
|
85
|
+
...contextMeasurementStats({ measurement: sessionContextMeasurement(session, hasConversationActivity) }),
|
|
86
|
+
usageReadout: session.usageReadout || null,
|
|
87
|
+
};
|
|
84
88
|
const contextWindow = positiveNumber(session.contextWindow);
|
|
85
89
|
const rawContextWindow = positiveNumber(session.rawContextWindow, contextWindow);
|
|
86
90
|
const displayContextWindow = positiveNumber(session.compactBoundaryTokens, contextWindow);
|
|
@@ -218,7 +222,7 @@ function budgetedEdge(from, step, length, rows, byteBudget, sizeOf) {
|
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
/** The most rows one historical-position read carries, whatever it asks. */
|
|
221
|
-
|
|
225
|
+
const CURSOR_PAGE_MAX_ROWS = 512;
|
|
222
226
|
|
|
223
227
|
/** A cursor's page size: 1 to CURSOR_PAGE_MAX_ROWS rows. */
|
|
224
228
|
export function cursorPageRows(maxRows) {
|
|
@@ -305,6 +309,11 @@ export async function windowStoredTranscript(sessionId, base, options) {
|
|
|
305
309
|
cwd: session.cwd || '',
|
|
306
310
|
desktopSession: desktopSession(session.desktopSession, session.cwd),
|
|
307
311
|
workflow: session.workflow || null,
|
|
312
|
+
todo: todoSnapshot(session),
|
|
313
|
+
// Turns whose checkpoints recovery could not apply, kept on disk.
|
|
314
|
+
...(Array.isArray(session.unrecoveredTurns) && session.unrecoveredTurns.length
|
|
315
|
+
? { unrecoveredTurns: session.unrecoveredTurns }
|
|
316
|
+
: {}),
|
|
308
317
|
...contextFields,
|
|
309
318
|
};
|
|
310
319
|
}
|
|
@@ -337,8 +337,8 @@ const baseBuilds = createBaseBuilds({ build: buildBase });
|
|
|
337
337
|
// history) and a page of a retained one costs ~0.2 s, a rebuilt one ~1 s. 96
|
|
338
338
|
// MB keeps the largest sessions seen (~78 MB records) smooth; 128 MB holds
|
|
339
339
|
// one of those beside a few ordinary ones.
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
const WORKER_HISTORY_SESSION_MAX_BYTES = 96 * 1024 * 1024;
|
|
341
|
+
const WORKER_HISTORY_MAX_BYTES = 128 * 1024 * 1024;
|
|
342
342
|
|
|
343
343
|
/**
|
|
344
344
|
* The display histories one worker retains (`{ value, rowBytes, bytes }`,
|
|
@@ -31,6 +31,7 @@ export {
|
|
|
31
31
|
_removeSessionSummary,
|
|
32
32
|
} from './store-summary-index.mjs';
|
|
33
33
|
import { _sessionForDisk, _ensureLifecycleFields } from './store/serialize.mjs';
|
|
34
|
+
import { captureTranscriptSnapshot, recordLandedTranscript } from './store/transcript-durability.mjs';
|
|
34
35
|
import {
|
|
35
36
|
_cacheSessionSummary,
|
|
36
37
|
_rollbackCachedSessionSummary,
|
|
@@ -337,6 +338,7 @@ function _doSaveSync(payload) {
|
|
|
337
338
|
// rebuilt from them, never re-read from the later mutable live session.
|
|
338
339
|
let attempted = null;
|
|
339
340
|
try {
|
|
341
|
+
const transcript = captureTranscriptSnapshot(session);
|
|
340
342
|
const disk = _sessionForDisk(session);
|
|
341
343
|
attempted = JSON.stringify(disk);
|
|
342
344
|
// The lifecycle these exact bytes carry, recorded with the commit stamp.
|
|
@@ -382,6 +384,7 @@ function _doSaveSync(payload) {
|
|
|
382
384
|
_recordOwnSessionCommit(target, scratch, lifecycle);
|
|
383
385
|
_publishLandedWriteEpoch(opts, epoch);
|
|
384
386
|
_untrackSaveTmp(tmp);
|
|
387
|
+
recordLandedTranscript(session, transcript, epoch);
|
|
385
388
|
if (mayMutate()) {
|
|
386
389
|
if (publishSummary) _queueSessionSummaryUpsert(session, summaryVersion);
|
|
387
390
|
_clearSaveStateIfCurrent(id, epoch);
|
|
@@ -627,6 +630,7 @@ async function _doSave(payload) {
|
|
|
627
630
|
// payload, never the live session as it looks at settlement time.
|
|
628
631
|
let attempted = null;
|
|
629
632
|
try {
|
|
633
|
+
const transcript = captureTranscriptSnapshot(session);
|
|
630
634
|
const disk = _sessionForDisk(session);
|
|
631
635
|
attempted = JSON.stringify(disk);
|
|
632
636
|
// Captured before the await: the lifecycle of exactly these bytes.
|
|
@@ -671,6 +675,7 @@ async function _doSave(payload) {
|
|
|
671
675
|
_recordOwnSessionCommit(target, scratch, lifecycle);
|
|
672
676
|
_publishLandedWriteEpoch(opts, epoch);
|
|
673
677
|
_untrackSaveTmp(tmp);
|
|
678
|
+
recordLandedTranscript(session, transcript, epoch);
|
|
674
679
|
if (mayMutate()) {
|
|
675
680
|
_queueSessionSummaryUpsert(session, summaryVersion);
|
|
676
681
|
_clearSaveStateIfCurrent(id, epoch);
|
|
@@ -45,6 +45,11 @@ const RUNTIME_CONTROL_SOURCES = new Set([
|
|
|
45
45
|
'task-notification',
|
|
46
46
|
]);
|
|
47
47
|
const ENVELOPE_OPEN_RE = new RegExp(`^\\s*<${SYNTHETIC_USER_ENVELOPE_TAG}\\b`, 'i');
|
|
48
|
+
// The stored crash marker reads like a user cancel ("Request interrupted ..."),
|
|
49
|
+
// and models answered it with "stopped as you asked" and dropped the work.
|
|
50
|
+
// On the wire it names the restart and asks to resume.
|
|
51
|
+
const PROCESS_RESTART_MARKER = '[Request interrupted by process restart]';
|
|
52
|
+
const PROCESS_RESTART_WIRE_TEXT = '[mixdog-runtime] The app restarted mid-turn. Continue from where you left off.';
|
|
48
53
|
|
|
49
54
|
function messageText(message) {
|
|
50
55
|
if (typeof message?.content === 'string') return message.content;
|
|
@@ -128,7 +133,8 @@ export function projectSyntheticUserEnvelopes(messages) {
|
|
|
128
133
|
const projected = messages.map((message) => {
|
|
129
134
|
const kind = classifySyntheticUserMessage(message);
|
|
130
135
|
if (!kind) return message;
|
|
131
|
-
const
|
|
136
|
+
const source = messageText(message).trim() === PROCESS_RESTART_MARKER ? PROCESS_RESTART_WIRE_TEXT : message.content;
|
|
137
|
+
const content = envelopeContent(source, kind);
|
|
132
138
|
if (content === message.content) return message;
|
|
133
139
|
changed = true;
|
|
134
140
|
stats.wrapped += 1;
|
|
@@ -119,7 +119,15 @@ export function matchTailReminder(context, rules = TAIL_REMINDER_RULES) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/** `messages` plus the tail reminder for this request (a copy; the input is untouched). */
|
|
122
|
-
|
|
122
|
+
// The session's own response language (as its prompt and settings notices
|
|
123
|
+
// carry it, session/manager/prompt-settings.mjs); a session without one (an
|
|
124
|
+
// agent, an older record) reads the config.
|
|
125
|
+
export function withTailReminder(
|
|
126
|
+
messages,
|
|
127
|
+
{ session, provider, model },
|
|
128
|
+
// null is a recorded "no language", not a missing record.
|
|
129
|
+
language = session?.responseLanguage !== undefined ? session.responseLanguage : _buildLeadLanguage()
|
|
130
|
+
) {
|
|
123
131
|
const text = matchTailReminder({
|
|
124
132
|
provider: session?.provider || provider?.name || '',
|
|
125
133
|
model,
|