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
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* goal-mutations.mjs — the durable Goal state transitions: create, the
|
|
3
|
-
* model/user status update (complete / blocked)
|
|
3
|
+
* model/user status update (complete / blocked) and abandon.
|
|
4
4
|
* Each takes the shared runtime record (`ctx`) and commits through its store.
|
|
5
5
|
*/
|
|
6
6
|
import { randomUUID } from 'node:crypto';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
|
|
9
9
|
import { compactSessionTitle } from './session-title.mjs';
|
|
10
|
-
import { GOAL_TASK_SETTLED } from './goal-tool-defs.mjs';
|
|
11
|
-
import { applyGoalTaskChanges, normalizeGoalTasks } from './goal-tasks.mjs';
|
|
12
10
|
import {
|
|
13
11
|
GOAL_FILE_VERSION,
|
|
14
12
|
NO_DEADLINE_WARNING_MS,
|
|
15
|
-
activeElapsedMs,
|
|
16
13
|
assertSessionId,
|
|
17
14
|
checkpointActiveClock,
|
|
18
15
|
clearTurnFailures,
|
|
19
|
-
goalTimeMode,
|
|
20
16
|
parseGoalDuration,
|
|
21
17
|
publicGoal,
|
|
22
18
|
stopActiveClock,
|
|
@@ -33,6 +29,13 @@ export function goalFinished(goal) {
|
|
|
33
29
|
return FINISHED_STATUSES.includes(goal.status);
|
|
34
30
|
}
|
|
35
31
|
|
|
32
|
+
/** A Goal that is neither running nor finished (paused, blocked, usage
|
|
33
|
+
* limited, time budget reached): it waits for the user, who may clear or
|
|
34
|
+
* replace it. */
|
|
35
|
+
export function goalStalled(goal) {
|
|
36
|
+
return Boolean(goal) && goal.status !== 'active' && !goalFinished(goal);
|
|
37
|
+
}
|
|
38
|
+
|
|
36
39
|
/** Reject a write aimed at a Goal the caller no longer holds. */
|
|
37
40
|
export function assertExpectedGoal(goal, expectedGoalId, what = 'update') {
|
|
38
41
|
if (clean(expectedGoalId) && clean(expectedGoalId) !== clean(goal.id)) {
|
|
@@ -51,32 +54,18 @@ function initialTimeLimitMs(args, defaultTimeLimitMs) {
|
|
|
51
54
|
function newGoalRecord(id, args, at, defaultTimeLimitMs) {
|
|
52
55
|
const startTurn = args.startInCurrentTurn === true ? 1 : 0;
|
|
53
56
|
const timeLimitMs = initialTimeLimitMs(args, defaultTimeLimitMs);
|
|
54
|
-
const initialTasks = Array.isArray(args.tasks)
|
|
55
|
-
? normalizeGoalTasks(
|
|
56
|
-
args.tasks.filter((task) => clean(task?.text)),
|
|
57
|
-
[],
|
|
58
|
-
{ strict: true }
|
|
59
|
-
)
|
|
60
|
-
: [];
|
|
61
57
|
return {
|
|
62
58
|
id: randomUUID(),
|
|
63
|
-
revision: 1,
|
|
64
|
-
objectiveRevision: 1,
|
|
65
|
-
tasksObjectiveRevision: 1,
|
|
66
59
|
sessionId: id,
|
|
67
60
|
objective: validateObjective(args.objective),
|
|
68
61
|
title: compactSessionTitle(args.objective),
|
|
62
|
+
objectiveRevision: 0,
|
|
69
63
|
status: 'active',
|
|
70
|
-
tasks: initialTasks,
|
|
71
64
|
blocker: '',
|
|
72
65
|
failureReason: '',
|
|
73
66
|
failureCount: 0,
|
|
74
67
|
turnCount: startTurn,
|
|
75
|
-
lastDropTurn: initialTasks.some((task) => task.status === 'dropped') ? startTurn : -1,
|
|
76
|
-
tasksUpdatedAt: initialTasks.length > 0 ? at : null,
|
|
77
68
|
timeLimitMs,
|
|
78
|
-
// A stated budget commits that time; only an explicit cap permits early completion.
|
|
79
|
-
timeMode: goalTimeMode(args.timeMode, timeLimitMs > 0 ? 'duration' : 'max'),
|
|
80
69
|
timeUsedMs: 0,
|
|
81
70
|
deadlineWarnedMs: NO_DEADLINE_WARNING_MS,
|
|
82
71
|
warningRevision: 0,
|
|
@@ -88,37 +77,8 @@ function newGoalRecord(id, args, at, defaultTimeLimitMs) {
|
|
|
88
77
|
};
|
|
89
78
|
}
|
|
90
79
|
|
|
91
|
-
// Evidence gates the MODEL's completion claim, never the user's. A user
|
|
92
|
-
// completing their own Goal is an act of authority: without this the only
|
|
93
|
-
// user-side exit was deleting the Goal, which threw the record away.
|
|
94
|
-
// Unfinished rows stay unfinished so the record stays honest.
|
|
95
|
-
function assertCompletionEvidence(goal, at) {
|
|
96
|
-
if (goal.tasksObjectiveRevision !== goal.objectiveRevision) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
'cannot complete Goal: objective changed; read goal status and reconcile the full task list with set_tasks first'
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
const tasks = normalizeGoalTasks(goal.tasks || []);
|
|
102
|
-
const incomplete = tasks.filter((task) => !GOAL_TASK_SETTLED.includes(task.status));
|
|
103
|
-
if (incomplete.length > 0) {
|
|
104
|
-
throw new Error(`cannot complete Goal: ${incomplete.length} durable tasks remain incomplete`);
|
|
105
|
-
}
|
|
106
|
-
const turnCount = Math.max(0, Math.floor(Number(goal.turnCount) || 0));
|
|
107
|
-
if (goal.lastDropTurn >= 0 && goal.lastDropTurn === turnCount) {
|
|
108
|
-
throw new Error(
|
|
109
|
-
'cannot complete Goal: a task was dropped this turn; only a user scope change retires ' +
|
|
110
|
-
'requested work, so finish that work or let the user confirm the change first'
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
if (goal.timeMode === 'duration' && goal.timeLimitMs > 0 && activeElapsedMs(goal, at) < goal.timeLimitMs) {
|
|
114
|
-
throw new Error(
|
|
115
|
-
'cannot complete Goal before the requested duration ends; keep working, or the user may explicitly complete the Goal'
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
80
|
export function createGoalMutations(ctx) {
|
|
121
|
-
const { root, now, defaultTimeLimitMs, turnGoalIds, turnStartedAt } = ctx;
|
|
81
|
+
const { root, now, defaultTimeLimitMs, turnGoalIds, turnStartedAt, turnObjectiveRevisions } = ctx;
|
|
122
82
|
|
|
123
83
|
// The Goal owning the current turn keeps its open segment; any other stops.
|
|
124
84
|
const settleActiveClock = (id, goal, at) => {
|
|
@@ -140,13 +100,14 @@ export function createGoalMutations(ctx) {
|
|
|
140
100
|
const createGoal = async (sessionId, args = {}) => {
|
|
141
101
|
const id = assertSessionId(sessionId);
|
|
142
102
|
const record = ctx.readRecord(id);
|
|
143
|
-
|
|
103
|
+
const replaced = goalStalled(record.goal) && args.replace === true;
|
|
104
|
+
if (record.goal && !goalFinished(record.goal) && !replaced) {
|
|
144
105
|
// Recovery is part of the message on purpose: without it a stopped Goal
|
|
145
106
|
// (paused/blocked/limited) permanently blocked every later Goal in the
|
|
146
107
|
// session, because the model owns no way to retire one.
|
|
147
108
|
throw new Error(
|
|
148
109
|
`cannot create a new Goal while an unfinished Goal exists (status ${record.goal.status}); ` +
|
|
149
|
-
'
|
|
110
|
+
'finish it, or ask the user to stop it from the Goal controls'
|
|
150
111
|
);
|
|
151
112
|
}
|
|
152
113
|
const at = now();
|
|
@@ -154,17 +115,25 @@ export function createGoalMutations(ctx) {
|
|
|
154
115
|
if (args.startInCurrentTurn === true) {
|
|
155
116
|
goal.lastStartedAt = record.goal ? at : turnStartedAt.get(id) || at;
|
|
156
117
|
}
|
|
118
|
+
if (replaced) {
|
|
119
|
+
// The user confirmed: the stalled Goal retires into history as stopped.
|
|
120
|
+
record.goal.status = 'stopped';
|
|
121
|
+
record.goal.stoppedAt = at;
|
|
122
|
+
record.goal.archivedAt = at;
|
|
123
|
+
record.goal.updatedAt = at;
|
|
124
|
+
}
|
|
157
125
|
if (record.goal) await archiveFinishedGoal(id, record.goal);
|
|
158
126
|
const created = await ctx.commit(id, goal);
|
|
159
127
|
if (args.startInCurrentTurn === true) {
|
|
160
128
|
turnGoalIds.set(id, goal.id);
|
|
161
129
|
turnStartedAt.set(id, goal.lastStartedAt);
|
|
130
|
+
turnObjectiveRevisions.set(id, goal.objectiveRevision);
|
|
162
131
|
}
|
|
163
132
|
ctx.scheduleGoalTitle(id, goal);
|
|
164
133
|
return created;
|
|
165
134
|
};
|
|
166
135
|
|
|
167
|
-
const updateGoal = async (sessionId, args = {}, {
|
|
136
|
+
const updateGoal = async (sessionId, args = {}, { expectedGoalId = '' } = {}) => {
|
|
168
137
|
const id = assertSessionId(sessionId);
|
|
169
138
|
const goal = ctx.requireGoal(id);
|
|
170
139
|
assertExpectedGoal(goal, expectedGoalId, 'update');
|
|
@@ -180,7 +149,8 @@ export function createGoalMutations(ctx) {
|
|
|
180
149
|
throw new Error('a completed Goal cannot change status; edit it or create a new Goal');
|
|
181
150
|
}
|
|
182
151
|
if (status === 'complete') {
|
|
183
|
-
|
|
152
|
+
// Completion is the model's claim after its own evidence audit (or
|
|
153
|
+
// the user's act of authority); the runtime never completes by itself.
|
|
184
154
|
settleActiveClock(id, goal, at);
|
|
185
155
|
goal.status = 'complete';
|
|
186
156
|
goal.completedAt = at;
|
|
@@ -197,14 +167,6 @@ export function createGoalMutations(ctx) {
|
|
|
197
167
|
return ctx.commit(id, goal);
|
|
198
168
|
};
|
|
199
169
|
|
|
200
|
-
const setGoalTasks = async (sessionId, args = {}, { expectedGoalId = '', partial = false } = {}) => {
|
|
201
|
-
const id = assertSessionId(sessionId);
|
|
202
|
-
const goal = ctx.requireGoal(id);
|
|
203
|
-
assertExpectedGoal(goal, expectedGoalId, 'task update');
|
|
204
|
-
applyGoalTaskChanges(goal, args, { partial, at: now() });
|
|
205
|
-
return ctx.commit(id, goal);
|
|
206
|
-
};
|
|
207
|
-
|
|
208
170
|
// Explicit retirement of a superseded Goal. The create guard stays strict so
|
|
209
171
|
// parallel Goals stay impossible; this is the one way out of it, and it is
|
|
210
172
|
// deliberately an act the model has to take rather than a silent overwrite.
|
|
@@ -224,8 +186,9 @@ export function createGoalMutations(ctx) {
|
|
|
224
186
|
if (archive) goal.archivedAt = goal.stoppedAt;
|
|
225
187
|
await ctx.commit(id, goal);
|
|
226
188
|
turnGoalIds.delete(id);
|
|
189
|
+
turnObjectiveRevisions.delete(id);
|
|
227
190
|
return publicGoal(goal, now());
|
|
228
191
|
};
|
|
229
192
|
|
|
230
|
-
return { createGoal, updateGoal,
|
|
193
|
+
return { createGoal, updateGoal, abandonGoal };
|
|
231
194
|
}
|
|
@@ -13,6 +13,11 @@ import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
|
13
13
|
import { readStoredGoalSnapshot } from './goal-runtime.mjs';
|
|
14
14
|
import { goalStateReminder } from './goal-text.mjs';
|
|
15
15
|
import { clean } from './session-text.mjs';
|
|
16
|
+
import { todoContextBlock } from './todo-list.mjs';
|
|
17
|
+
|
|
18
|
+
// Stalled states the model is told about when they begin. Time-budget
|
|
19
|
+
// exhaustion has its own one-time closeout notice (deadline-reached).
|
|
20
|
+
const STALL_NOTICE_STATUSES = ['paused', 'blocked', 'usage_limited'];
|
|
16
21
|
|
|
17
22
|
function pendingRevision(session) {
|
|
18
23
|
return Math.max(0, Number(session?.pendingGoalReminder?.revision) || 0);
|
|
@@ -53,18 +58,38 @@ export function snapshotPendingGoalReminder(session, { dataDir = null, readGoal
|
|
|
53
58
|
} catch {
|
|
54
59
|
goal = null;
|
|
55
60
|
}
|
|
61
|
+
if (goal?.status === 'complete') goal = null;
|
|
62
|
+
const marked = clean(session.pendingGoalReminder?.reason);
|
|
63
|
+
// A Goal that left the stalled states is told afresh if it stalls again.
|
|
64
|
+
if (!goal || goal.status === 'active') delete session.goalStalledNotice;
|
|
65
|
+
// A stalled Goal is told once, when it stalls; compaction does not
|
|
66
|
+
// re-inject it. Only a Goal that keeps running needs its state restored.
|
|
67
|
+
if (marked === 'compaction' && goal && goal.status !== 'active') goal = null;
|
|
68
|
+
// Compaction drops the todo tool results too: the open list rides once.
|
|
69
|
+
const todoBlock =
|
|
70
|
+
revision && marked === 'compaction'
|
|
71
|
+
? todoContextBlock(session, 'Context was compacted; your todo list (exact wordings):')
|
|
72
|
+
: '';
|
|
56
73
|
// Nothing left to remind about (no Goal, or it already finished): drop the
|
|
57
74
|
// marker instead of re-reading the same file on every later turn.
|
|
58
|
-
if (!goal
|
|
75
|
+
if (!goal && !todoBlock) {
|
|
59
76
|
clearPendingGoalReminder(session);
|
|
60
77
|
return null;
|
|
61
78
|
}
|
|
62
79
|
if (!revision) {
|
|
63
|
-
if (!
|
|
80
|
+
if (!STALL_NOTICE_STATUSES.includes(goal.status)) return null;
|
|
81
|
+
// One notice per stall: a later pause, block or limit of the same Goal
|
|
82
|
+
// (a turn ran in between) is a new stall.
|
|
83
|
+
const stall = `${goal.id}:${goal.status}:${Math.max(0, Number(goal.turnCount) || 0)}`;
|
|
84
|
+
if (session.goalStalledNotice === stall) return null;
|
|
85
|
+
session.goalStalledNotice = stall;
|
|
64
86
|
revision = markPendingGoalReminder(session, goal.status).revision;
|
|
65
87
|
}
|
|
66
88
|
const reason = clean(session.pendingGoalReminder?.reason);
|
|
67
|
-
const
|
|
89
|
+
const goalContent = goal ? goalStateReminder(goal, { reason }) : '';
|
|
90
|
+
const content = [goalContent, todoBlock ? `<system-reminder>\n${todoBlock}\n</system-reminder>` : '']
|
|
91
|
+
.filter(Boolean)
|
|
92
|
+
.join('\n\n');
|
|
68
93
|
if (!content) {
|
|
69
94
|
clearPendingGoalReminder(session);
|
|
70
95
|
return null;
|
|
@@ -29,12 +29,16 @@ export {
|
|
|
29
29
|
MAX_GOAL_TIME_LIMIT_MS,
|
|
30
30
|
parseGoalDuration,
|
|
31
31
|
};
|
|
32
|
-
export { GOAL_STATUS_VALUES } from './goal-state.mjs';
|
|
33
32
|
export { listStoredActiveGoalSessionIds, readStoredGoalSnapshot } from './goal-storage.mjs';
|
|
34
33
|
|
|
35
34
|
export function createGoalRuntime({
|
|
36
35
|
dataDir,
|
|
37
|
-
now
|
|
36
|
+
now: injectedNow,
|
|
37
|
+
// Suspend evidence needs a monotonic clock paired with the wall clock. An
|
|
38
|
+
// injected `now` has no real pair, so a test supplies its own or none. A test
|
|
39
|
+
// that fakes `Date` must pass `monotonicNow: null` (undefined takes the default).
|
|
40
|
+
monotonicNow = injectedNow ? null : () => performance.now(),
|
|
41
|
+
suspendDetectable,
|
|
38
42
|
defaultTimeLimitMs = DEFAULT_GOAL_TIME_LIMIT_MS,
|
|
39
43
|
completedGoalTtlMs = DEFAULT_COMPLETED_GOAL_TTL_MS,
|
|
40
44
|
deadlineWarningMs = DEFAULT_GOAL_DEADLINE_WARNING_MS,
|
|
@@ -42,6 +46,7 @@ export function createGoalRuntime({
|
|
|
42
46
|
writeGoalRecord,
|
|
43
47
|
onStorageError = reportGoalStorageError,
|
|
44
48
|
} = {}) {
|
|
49
|
+
const now = injectedNow ?? (() => Date.now());
|
|
45
50
|
const ctx = {
|
|
46
51
|
dataDir,
|
|
47
52
|
root: goalsRoot(dataDir),
|
|
@@ -55,15 +60,15 @@ export function createGoalRuntime({
|
|
|
55
60
|
mutationChains: new Map(),
|
|
56
61
|
turnGoalIds: new Map(),
|
|
57
62
|
turnStartedAt: new Map(),
|
|
63
|
+
// Session -> the objective revision the current turn has seen (turn start,
|
|
64
|
+
// a status read, or a refusal that showed it). In memory like the turn.
|
|
65
|
+
turnObjectiveRevisions: new Map(),
|
|
58
66
|
titleJobs: new Map(),
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
// no progress: the one settled-duration review, or an automatic turn that
|
|
62
|
-
// called no tool. In memory on purpose: a restart is a new chance.
|
|
67
|
+
// Session -> the Goal identity and turn an automatic turn answered with no
|
|
68
|
+
// tool call. In memory on purpose: a restart is a new chance.
|
|
63
69
|
idleReviewTurns: new Map(),
|
|
64
|
-
// Session ->
|
|
65
|
-
//
|
|
66
|
-
// In memory on purpose: a restart is a fresh context that needs them again.
|
|
70
|
+
// Session -> id of the Goal whose full continuation rules this context
|
|
71
|
+
// already holds. In memory on purpose: a restart is a fresh context.
|
|
67
72
|
continuationTiers: new Map(),
|
|
68
73
|
// Session -> Goal id whose turn ended in a stop (failed, cancelled, usage
|
|
69
74
|
// limit) the record could not save. The record still reads active, so
|
|
@@ -74,11 +79,14 @@ export function createGoalRuntime({
|
|
|
74
79
|
Object.assign(ctx, createGoalStore(ctx));
|
|
75
80
|
ctx.deadlines = createGoalDeadlines({
|
|
76
81
|
now,
|
|
77
|
-
readRecord: ctx.
|
|
82
|
+
readRecord: ctx.readStoredRecord,
|
|
78
83
|
withMutation: ctx.withMutation,
|
|
79
84
|
commit: ctx.commit,
|
|
85
|
+
stampLive: ctx.stampLive,
|
|
80
86
|
onStorageError,
|
|
81
87
|
deadlineWarningMs,
|
|
88
|
+
monotonicNow,
|
|
89
|
+
...(suspendDetectable !== undefined ? { suspendDetectable } : {}),
|
|
82
90
|
});
|
|
83
91
|
ctx.scheduleGoalTitle = createGoalTitleScheduler(ctx);
|
|
84
92
|
Object.assign(ctx, createGoalMutations(ctx));
|
|
@@ -109,6 +117,23 @@ export function createGoalRuntime({
|
|
|
109
117
|
return withMutation(sessionId, () => ctx.control(sessionId, args));
|
|
110
118
|
},
|
|
111
119
|
executeTool: (name, args = {}, context = {}) => executeGoalTool(ctx, name, args, context),
|
|
120
|
+
// Tasks a legacy record still carries for the session's todo list to
|
|
121
|
+
// adopt; [] once adopted (clearLegacyTasks).
|
|
122
|
+
legacyTasks(sessionId) {
|
|
123
|
+
try {
|
|
124
|
+
return ctx.readRecord(sessionId).goal?.legacyTasks || [];
|
|
125
|
+
} catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
clearLegacyTasks(sessionId) {
|
|
130
|
+
return withMutation(sessionId, async (id) => {
|
|
131
|
+
const goal = ctx.readRecord(id).goal;
|
|
132
|
+
if (!goal?.legacyTasks?.length) return null;
|
|
133
|
+
goal.legacyTasks = [];
|
|
134
|
+
return ctx.commit(id, goal);
|
|
135
|
+
});
|
|
136
|
+
},
|
|
112
137
|
...createGoalTurnLifecycle(ctx),
|
|
113
138
|
close() {
|
|
114
139
|
ctx.closed = true;
|
|
@@ -117,7 +142,7 @@ export function createGoalRuntime({
|
|
|
117
142
|
deadlines.close();
|
|
118
143
|
ctx.turnGoalIds.clear();
|
|
119
144
|
ctx.turnStartedAt.clear();
|
|
120
|
-
ctx.
|
|
145
|
+
ctx.turnObjectiveRevisions.clear();
|
|
121
146
|
ctx.idleReviewTurns.clear();
|
|
122
147
|
ctx.continuationTiers.clear();
|
|
123
148
|
ctx.unsavedStops.clear();
|
|
@@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { clean } from '../../../shared/clean.mjs';
|
|
3
3
|
import { compactSessionTitle } from './session-title.mjs';
|
|
4
4
|
import { MAX_GOAL_TIME_LIMIT_MS } from './goal-tool-defs.mjs';
|
|
5
|
-
import { goalTaskProgress, normalizeGoalTasks } from './goal-tasks.mjs';
|
|
6
5
|
import { SESSION_ID_PATTERN } from './session-id.mjs';
|
|
7
6
|
|
|
8
7
|
export const DEFAULT_GOAL_TIME_LIMIT_MS = 0;
|
|
@@ -10,6 +9,9 @@ export const DEFAULT_COMPLETED_GOAL_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
10
9
|
export const DEFAULT_GOAL_DEADLINE_WARNING_MS = Object.freeze([10 * 60_000, 5 * 60_000]);
|
|
11
10
|
export const NO_DEADLINE_WARNING_MS = Number.MAX_SAFE_INTEGER;
|
|
12
11
|
export const GOAL_FILE_VERSION = 1;
|
|
12
|
+
// A live runtime re-stamps an active Goal's `liveAt` this often; a longer gap
|
|
13
|
+
// means nothing was running it (app closed, PC asleep).
|
|
14
|
+
export const GOAL_LIVE_HEARTBEAT_MS = 60_000;
|
|
13
15
|
export const SESSION_ID = SESSION_ID_PATTERN;
|
|
14
16
|
export const GOAL_STATUS_VALUES = Object.freeze([
|
|
15
17
|
'active',
|
|
@@ -23,14 +25,6 @@ export const GOAL_STATUS_VALUES = Object.freeze([
|
|
|
23
25
|
|
|
24
26
|
const MAX_OBJECTIVE_LENGTH = 4_000;
|
|
25
27
|
const MAX_GOAL_BLOCKER_LENGTH = 1_000;
|
|
26
|
-
const ACTIVE_AGENT_STATUSES = new Set([
|
|
27
|
-
'connecting',
|
|
28
|
-
'requesting',
|
|
29
|
-
'streaming',
|
|
30
|
-
'tool_running',
|
|
31
|
-
'running',
|
|
32
|
-
'cancelling',
|
|
33
|
-
]);
|
|
34
28
|
|
|
35
29
|
export function assertSessionId(value) {
|
|
36
30
|
const sessionId = clean(value);
|
|
@@ -56,12 +50,6 @@ export function validateGoalBlocker(value) {
|
|
|
56
50
|
return blocker;
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
export function goalTimeMode(value, fallback = 'max') {
|
|
60
|
-
if (value == null || value === '') return fallback;
|
|
61
|
-
if (!['max', 'duration'].includes(value)) throw new Error('goal timeMode must be max or duration');
|
|
62
|
-
return value;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
53
|
export function parseGoalDuration(value) {
|
|
66
54
|
if (typeof value === 'number') {
|
|
67
55
|
if (!Number.isFinite(value) || value <= 0) throw new Error('goal duration must be positive');
|
|
@@ -100,12 +88,10 @@ export function parseUserCommand(command) {
|
|
|
100
88
|
|
|
101
89
|
let objective = value;
|
|
102
90
|
let duration = null;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
objective = objective.replace(modeMatch[0], ' ').trim();
|
|
108
|
-
}
|
|
91
|
+
// The user's confirmation that a new Goal replaces a stalled one, naming it:
|
|
92
|
+
// `--replace=<goal id>`.
|
|
93
|
+
const replaceMatch = objective.match(/(?:^|\s)--replace(?:=(\S+))?(?=\s|$)/i);
|
|
94
|
+
if (replaceMatch) objective = objective.replace(replaceMatch[0], ' ').trim();
|
|
109
95
|
const equalsMatch = objective.match(/(?:^|\s)--time=([^\s]+)/i);
|
|
110
96
|
if (equalsMatch) {
|
|
111
97
|
duration = equalsMatch[1];
|
|
@@ -117,7 +103,12 @@ export function parseUserCommand(command) {
|
|
|
117
103
|
objective = objective.replace(spacedMatch[0], ' ').trim();
|
|
118
104
|
}
|
|
119
105
|
}
|
|
120
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
action: 'create',
|
|
108
|
+
objective,
|
|
109
|
+
duration,
|
|
110
|
+
...(replaceMatch ? { replace: true, ...(replaceMatch[1] ? { expectedGoalId: replaceMatch[1] } : {}) } : {}),
|
|
111
|
+
};
|
|
121
112
|
}
|
|
122
113
|
|
|
123
114
|
function storedTimeLimitMs(value) {
|
|
@@ -125,11 +116,19 @@ function storedTimeLimitMs(value) {
|
|
|
125
116
|
return Number.isFinite(stored) && stored > 0 ? Math.min(MAX_GOAL_TIME_LIMIT_MS, Math.max(60_000, stored)) : 0;
|
|
126
117
|
}
|
|
127
118
|
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return
|
|
119
|
+
// Tasks a record written before the todo list carried: kept on the record
|
|
120
|
+
// only until the session's todo list adopts them (todo-list.mjs).
|
|
121
|
+
function storedLegacyTasks(value) {
|
|
122
|
+
const tasks = value.legacyTasks ?? value.tasks ?? value.criteria;
|
|
123
|
+
return Array.isArray(tasks) && tasks.length
|
|
124
|
+
? tasks
|
|
125
|
+
.filter((task) => task && typeof task === 'object' && clean(task.text ?? task.content))
|
|
126
|
+
.map((task) => ({
|
|
127
|
+
text: clean(task.text ?? task.content),
|
|
128
|
+
// A criterion written before task statuses carried `satisfied`.
|
|
129
|
+
status: clean(task.status) || (task.satisfied === true ? 'completed' : 'pending'),
|
|
130
|
+
}))
|
|
131
|
+
: [];
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
export function normalizeStoredGoal(value, sessionId, resumedAt = Date.now()) {
|
|
@@ -139,31 +138,35 @@ export function normalizeStoredGoal(value, sessionId, resumedAt = Date.now()) {
|
|
|
139
138
|
if (!GOAL_STATUS_VALUES.includes(status)) throw new Error(`invalid stored Goal status: ${status}`);
|
|
140
139
|
return {
|
|
141
140
|
id: clean(value.id) || randomUUID(),
|
|
142
|
-
revision: Math.max(1, Math.floor(Number(value.revision) || 1)),
|
|
143
|
-
objectiveRevision: Math.max(1, Math.floor(Number(value.objectiveRevision) || 1)),
|
|
144
|
-
tasksObjectiveRevision: Math.max(1, Math.floor(Number(value.tasksObjectiveRevision) || 1)),
|
|
145
141
|
sessionId,
|
|
146
142
|
objective: validateObjective(value.objective),
|
|
147
143
|
title: compactSessionTitle(value.title || value.objective),
|
|
144
|
+
// Advances on every objective edit, so a settlement can tell whether the
|
|
145
|
+
// turn it answers saw the current wording.
|
|
146
|
+
objectiveRevision: Math.max(0, Math.floor(Number(value.objectiveRevision) || 0)),
|
|
148
147
|
status,
|
|
149
|
-
|
|
148
|
+
legacyTasks: storedLegacyTasks(value),
|
|
150
149
|
blocker: clean(value.blocker),
|
|
151
150
|
pauseReason: ['waiting', 'cancelled'].includes(value.pauseReason) ? value.pauseReason : 'user',
|
|
152
151
|
failureReason: clean(value.failureReason),
|
|
153
152
|
failureCount: Math.max(0, Math.floor(Number(value.failureCount) || 0)),
|
|
154
153
|
// Observations report progress; they never decide when work is complete.
|
|
155
154
|
turnCount: Math.max(0, Math.floor(Number(value.turnCount) || 0)),
|
|
156
|
-
lastDropTurn: storedLastDropTurn(value),
|
|
157
|
-
tasksUpdatedAt: Number(value.tasksUpdatedAt) > 0 ? Number(value.tasksUpdatedAt) : null,
|
|
158
155
|
timeLimitMs: storedTimeLimitMs(value),
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
// Time is an upper bound only: a legacy `duration` record reads as a cap.
|
|
157
|
+
// A record exhausted by an older build may carry wall-clock time past its
|
|
158
|
+
// cap; the cap is what was spent.
|
|
159
|
+
timeUsedMs: cappedTimeUsedMs(
|
|
160
|
+
status === 'duration_reached' ? storedTimeLimitMs(value) : 0,
|
|
161
|
+
Math.max(0, Number(value.timeUsedMs) || 0)
|
|
162
|
+
),
|
|
162
163
|
createdAt: Math.max(0, Number(value.createdAt) || Date.now()),
|
|
163
164
|
updatedAt: Math.max(0, Number(value.updatedAt) || Date.now()),
|
|
164
165
|
// Preserve the open segment across task writes, eviction, and restart.
|
|
165
166
|
lastStartedAt:
|
|
166
167
|
status === 'active' ? Math.max(0, Number(value.lastStartedAt) || Number(resumedAt) || Date.now()) : null,
|
|
168
|
+
// The last moment a live runtime was running this Goal's open segment.
|
|
169
|
+
liveAt: Math.max(0, Number(value.liveAt) || 0),
|
|
167
170
|
completedAt: Number(value.completedAt) > 0 ? Number(value.completedAt) : null,
|
|
168
171
|
stoppedAt: Number(value.stoppedAt) > 0 ? Number(value.stoppedAt) : null,
|
|
169
172
|
archivedAt: Number(value.archivedAt) > 0 ? Number(value.archivedAt) : null,
|
|
@@ -172,6 +175,51 @@ export function normalizeStoredGoal(value, sessionId, resumedAt = Date.now()) {
|
|
|
172
175
|
};
|
|
173
176
|
}
|
|
174
177
|
|
|
178
|
+
function cappedTimeUsedMs(limitMs, usedMs) {
|
|
179
|
+
return limitMs > 0 ? Math.min(limitMs, usedMs) : usedMs;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Drop the part of an active Goal's open segment that no live runtime ran
|
|
183
|
+
* (the app was closed, the PC slept): the segment start moves forward by the
|
|
184
|
+
* unobserved gap. Returns whether anything was dropped. */
|
|
185
|
+
export function discardUnobservedTime(goal, at, toleranceMs = 0) {
|
|
186
|
+
if (goal?.status !== 'active' || !(Number(goal.lastStartedAt) > 0)) return false;
|
|
187
|
+
const seenAt = Math.max(Number(goal.lastStartedAt), Number(goal.liveAt) || 0);
|
|
188
|
+
const gap = at - seenAt;
|
|
189
|
+
// The tolerance covers heartbeat jitter inside a live runtime only; a load
|
|
190
|
+
// after downtime passes none, so no closed-app interval is ever credited.
|
|
191
|
+
if (gap <= toleranceMs) return false;
|
|
192
|
+
goal.lastStartedAt = Number(goal.lastStartedAt) + gap;
|
|
193
|
+
goal.liveAt = at;
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Drop `suspendedMs` — time a live runtime provably did not run (the OS was
|
|
198
|
+
* suspended) — from an active Goal's open segment, never more than the gap
|
|
199
|
+
* since it was last seen. Returns whether anything was dropped. */
|
|
200
|
+
export function discardSuspendedTime(goal, at, suspendedMs) {
|
|
201
|
+
if (goal?.status !== 'active' || !(Number(goal.lastStartedAt) > 0)) return false;
|
|
202
|
+
const seenAt = Math.max(Number(goal.lastStartedAt), Number(goal.liveAt) || 0);
|
|
203
|
+
const dropped = Math.min(Math.floor(Number(suspendedMs) || 0), Math.max(0, at - seenAt));
|
|
204
|
+
if (!(dropped > 0)) return false;
|
|
205
|
+
goal.lastStartedAt = Number(goal.lastStartedAt) + dropped;
|
|
206
|
+
goal.liveAt = at;
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The Goal as a reader without a runtime sees it. Such a reader cannot know
|
|
211
|
+
* whether anything is running the Goal, so it uses no clock: the open
|
|
212
|
+
* segment counts only up to `liveAt`, the last moment a runtime persisted as
|
|
213
|
+
* having run it (at most one heartbeat behind). That makes the usage a pure
|
|
214
|
+
* function of the record — never above what the runtime has stored, never
|
|
215
|
+
* charging downtime, never moving backward between reads. */
|
|
216
|
+
export function freezeUnobservedSegment(goal) {
|
|
217
|
+
if (goal?.status !== 'active' || !(Number(goal.lastStartedAt) > 0)) return;
|
|
218
|
+
const observedMs = Math.max(0, (Number(goal.liveAt) || 0) - Number(goal.lastStartedAt));
|
|
219
|
+
goal.timeUsedMs = Math.max(0, Number(goal.timeUsedMs) || 0) + observedMs;
|
|
220
|
+
goal.lastStartedAt = null;
|
|
221
|
+
}
|
|
222
|
+
|
|
175
223
|
export function activeElapsedMs(goal, now = Date.now()) {
|
|
176
224
|
const committed = Math.max(0, Number(goal?.timeUsedMs) || 0);
|
|
177
225
|
if (goal?.status !== 'active' || !(Number(goal?.lastStartedAt) > 0)) return committed;
|
|
@@ -186,28 +234,23 @@ export function normalizeDeadlineWarnedMs(value) {
|
|
|
186
234
|
|
|
187
235
|
export function publicGoal(goal, now = Date.now()) {
|
|
188
236
|
if (!goal) return null;
|
|
189
|
-
const timeUsedMs = activeElapsedMs(goal, now);
|
|
190
237
|
const hasTimeLimit = Number(goal.timeLimitMs) > 0;
|
|
238
|
+
// Never above the cap: a read that lands after the deadline but before the
|
|
239
|
+
// expiry commit reports the cap, not the overshoot.
|
|
240
|
+
const timeUsedMs = cappedTimeUsedMs(Number(goal.timeLimitMs) || 0, activeElapsedMs(goal, now));
|
|
191
241
|
const remainingMs = hasTimeLimit ? Math.max(0, Number(goal.timeLimitMs) - timeUsedMs) : null;
|
|
192
|
-
const tasks = normalizeGoalTasks(goal.tasks || []);
|
|
193
242
|
return {
|
|
194
243
|
id: goal.id,
|
|
195
|
-
|
|
196
|
-
// Warning delivery does not invalidate the actionable task revision.
|
|
244
|
+
// Advances once per delivered deadline warning (deadline-delivery.mjs).
|
|
197
245
|
warningRevision: Math.max(0, Math.floor(Number(goal.warningRevision) || 0)),
|
|
198
|
-
needsTaskReview: goal.tasksObjectiveRevision !== goal.objectiveRevision,
|
|
199
246
|
sessionId: goal.sessionId,
|
|
200
247
|
objective: goal.objective,
|
|
201
248
|
title: goal.title || compactSessionTitle(goal.objective),
|
|
202
249
|
status: goal.status,
|
|
203
|
-
tasks,
|
|
204
250
|
turnCount: Math.max(0, Math.floor(Number(goal.turnCount) || 0)),
|
|
205
|
-
tasksUpdatedAt: Number(goal.tasksUpdatedAt) > 0 ? Number(goal.tasksUpdatedAt) : null,
|
|
206
|
-
...goalTaskProgress(tasks),
|
|
207
251
|
blocker: goal.blocker || '',
|
|
208
252
|
pauseReason: goal.pauseReason || 'user',
|
|
209
253
|
timeLimitMs: goal.timeLimitMs,
|
|
210
|
-
timeMode: goal.timeMode,
|
|
211
254
|
timeUsedMs,
|
|
212
255
|
remainingMs,
|
|
213
256
|
deadlineAt: hasTimeLimit && goal.status === 'active' && goal.lastStartedAt ? now + remainingMs : null,
|
|
@@ -310,14 +353,3 @@ export function resumeGoalState(goal, at, added = null) {
|
|
|
310
353
|
export function startActiveClock(goal, now = Date.now()) {
|
|
311
354
|
if (goal.status === 'active' && !goal.lastStartedAt) goal.lastStartedAt = now;
|
|
312
355
|
}
|
|
313
|
-
|
|
314
|
-
export function runningAgentWork(agentStatus) {
|
|
315
|
-
const jobs = Array.isArray(agentStatus?.agentJobs) ? agentStatus.agentJobs : [];
|
|
316
|
-
if (jobs.some((job) => clean(job?.status).toLowerCase() === 'running')) return true;
|
|
317
|
-
const workers = Array.isArray(agentStatus?.agentWorkers) ? agentStatus.agentWorkers : [];
|
|
318
|
-
return workers.some((worker) => {
|
|
319
|
-
const status = clean(worker?.status).toLowerCase();
|
|
320
|
-
const stage = clean(worker?.stage || worker?.worker_stage).toLowerCase();
|
|
321
|
-
return ACTIVE_AGENT_STATUSES.has(status) || ACTIVE_AGENT_STATUSES.has(stage);
|
|
322
|
-
});
|
|
323
|
-
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
SESSION_ID,
|
|
16
16
|
assertSessionId,
|
|
17
17
|
completedGoalExpired,
|
|
18
|
+
freezeUnobservedSegment,
|
|
18
19
|
normalizeStoredGoal,
|
|
19
20
|
publicGoal,
|
|
20
21
|
} from './goal-state.mjs';
|
|
@@ -40,7 +41,9 @@ export function deleteStoredGoalFile(dataDir, sessionId) {
|
|
|
40
41
|
function readStoredGoalFile(dataDir, sessionId, at = Date.now()) {
|
|
41
42
|
const id = assertSessionId(sessionId);
|
|
42
43
|
try {
|
|
43
|
-
|
|
44
|
+
const goal = readGoalRecordFile(goalFilePath(dataDir, id), id, normalizeStoredGoal, at).goal;
|
|
45
|
+
freezeUnobservedSegment(goal);
|
|
46
|
+
return goal;
|
|
44
47
|
} catch (error) {
|
|
45
48
|
reportGoalStorageError(error);
|
|
46
49
|
return null;
|