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
|
@@ -55,9 +55,7 @@
|
|
|
55
55
|
}
|
|
56
56
|
],
|
|
57
57
|
"store": false,
|
|
58
|
-
"include": [
|
|
59
|
-
"reasoning.encrypted_content"
|
|
60
|
-
],
|
|
58
|
+
"include": ["reasoning.encrypted_content"],
|
|
61
59
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
62
60
|
"tools": [
|
|
63
61
|
{
|
|
@@ -148,9 +146,7 @@
|
|
|
148
146
|
}
|
|
149
147
|
],
|
|
150
148
|
"store": false,
|
|
151
|
-
"include": [
|
|
152
|
-
"reasoning.encrypted_content"
|
|
153
|
-
],
|
|
149
|
+
"include": ["reasoning.encrypted_content"],
|
|
154
150
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
155
151
|
"tools": [
|
|
156
152
|
{
|
|
@@ -278,9 +274,7 @@
|
|
|
278
274
|
}
|
|
279
275
|
],
|
|
280
276
|
"store": false,
|
|
281
|
-
"include": [
|
|
282
|
-
"reasoning.encrypted_content"
|
|
283
|
-
],
|
|
277
|
+
"include": ["reasoning.encrypted_content"],
|
|
284
278
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
285
279
|
"tools": [
|
|
286
280
|
{
|
|
@@ -400,9 +394,7 @@
|
|
|
400
394
|
}
|
|
401
395
|
],
|
|
402
396
|
"store": false,
|
|
403
|
-
"include": [
|
|
404
|
-
"reasoning.encrypted_content"
|
|
405
|
-
],
|
|
397
|
+
"include": ["reasoning.encrypted_content"],
|
|
406
398
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
407
399
|
"instructions": "sys",
|
|
408
400
|
"tools": [
|
|
@@ -513,9 +505,7 @@
|
|
|
513
505
|
}
|
|
514
506
|
],
|
|
515
507
|
"store": false,
|
|
516
|
-
"include": [
|
|
517
|
-
"reasoning.encrypted_content"
|
|
518
|
-
],
|
|
508
|
+
"include": ["reasoning.encrypted_content"],
|
|
519
509
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
520
510
|
"tools": [
|
|
521
511
|
{
|
|
@@ -626,9 +616,7 @@
|
|
|
626
616
|
}
|
|
627
617
|
],
|
|
628
618
|
"store": false,
|
|
629
|
-
"include": [
|
|
630
|
-
"reasoning.encrypted_content"
|
|
631
|
-
],
|
|
619
|
+
"include": ["reasoning.encrypted_content"],
|
|
632
620
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
633
621
|
"tools": [
|
|
634
622
|
{
|
|
@@ -738,9 +726,7 @@
|
|
|
738
726
|
}
|
|
739
727
|
],
|
|
740
728
|
"store": false,
|
|
741
|
-
"include": [
|
|
742
|
-
"reasoning.encrypted_content"
|
|
743
|
-
],
|
|
729
|
+
"include": ["reasoning.encrypted_content"],
|
|
744
730
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
745
731
|
"tools": [
|
|
746
732
|
{
|
|
@@ -865,9 +851,7 @@
|
|
|
865
851
|
}
|
|
866
852
|
],
|
|
867
853
|
"store": false,
|
|
868
|
-
"include": [
|
|
869
|
-
"reasoning.encrypted_content"
|
|
870
|
-
],
|
|
854
|
+
"include": ["reasoning.encrypted_content"],
|
|
871
855
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
872
856
|
"tools": [
|
|
873
857
|
{
|
|
@@ -971,9 +955,7 @@
|
|
|
971
955
|
}
|
|
972
956
|
],
|
|
973
957
|
"store": false,
|
|
974
|
-
"include": [
|
|
975
|
-
"reasoning.encrypted_content"
|
|
976
|
-
],
|
|
958
|
+
"include": ["reasoning.encrypted_content"],
|
|
977
959
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
978
960
|
"tools": [
|
|
979
961
|
{
|
|
@@ -1078,9 +1060,7 @@
|
|
|
1078
1060
|
}
|
|
1079
1061
|
],
|
|
1080
1062
|
"store": false,
|
|
1081
|
-
"include": [
|
|
1082
|
-
"reasoning.encrypted_content"
|
|
1083
|
-
],
|
|
1063
|
+
"include": ["reasoning.encrypted_content"],
|
|
1084
1064
|
"prompt_cache_key": "f0858c98-2db1-4764-9d18-e6e1ec2ea4c7",
|
|
1085
1065
|
"tools": [
|
|
1086
1066
|
{
|
|
@@ -129,6 +129,36 @@ export function builtinFeatureActive(configLike, id) {
|
|
|
129
129
|
return false;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/** Every tool a settings gate of featureDisallowedToolsFor can deny (the
|
|
133
|
+
* headless `load_tool` rule derives from these and is not a setting). */
|
|
134
|
+
export const FEATURE_GATED_TOOL_NAMES = Object.freeze([
|
|
135
|
+
'web_search',
|
|
136
|
+
'web_fetch',
|
|
137
|
+
'memory',
|
|
138
|
+
'recall',
|
|
139
|
+
'git',
|
|
140
|
+
'github',
|
|
141
|
+
'browser',
|
|
142
|
+
'browser_devtools',
|
|
143
|
+
'computer',
|
|
144
|
+
'terminal',
|
|
145
|
+
'office',
|
|
146
|
+
'media',
|
|
147
|
+
'tidy',
|
|
148
|
+
'visualize',
|
|
149
|
+
'explore',
|
|
150
|
+
'react',
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
/** Gated by desktop bridge presence, not a setting. */
|
|
154
|
+
const BRIDGE_GATED_TOOL_NAMES = Object.freeze(['browser', 'browser_devtools', 'computer', 'terminal']);
|
|
155
|
+
|
|
156
|
+
/** The tools a user setting turns off (react answers for itself: it gates
|
|
157
|
+
* non-model callers too). */
|
|
158
|
+
export const SETTING_GATED_TOOL_NAMES = Object.freeze(
|
|
159
|
+
FEATURE_GATED_TOOL_NAMES.filter((name) => !BRIDGE_GATED_TOOL_NAMES.includes(name) && name !== 'react')
|
|
160
|
+
);
|
|
161
|
+
|
|
132
162
|
/** The session-surface exclusion list. Session build, the empty-session policy
|
|
133
163
|
* refresh, and the deferred tool catalog all consume THIS list, so an
|
|
134
164
|
* uninstalled or disabled feature never reaches a session's tool surface.
|
|
@@ -348,17 +348,6 @@ export function resolveAutoClearIdleMs(config, provider) {
|
|
|
348
348
|
return autoClearIdleMsForProvider(provider, raw.providerIdleMs);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
/** The idle auto-clear gate's settings for a thread on `provider`
|
|
352
|
-
* (idle-auto-clear-gate.mjs): `{ enabled, idleMs, minContextPercent }`. */
|
|
353
|
-
function autoClearGateSettings(config, provider) {
|
|
354
|
-
const normalized = normalizeAutoClearConfig(config?.autoClear);
|
|
355
|
-
return {
|
|
356
|
-
enabled: normalized.enabled,
|
|
357
|
-
idleMs: resolveAutoClearIdleMs(config, provider),
|
|
358
|
-
minContextPercent: normalized.minContextPercent,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
351
|
export function normalizeSystemShellConfig(value = {}) {
|
|
363
352
|
const raw = value && typeof value === 'object' ? value : {};
|
|
364
353
|
const command = clean(raw.command);
|
|
@@ -14,6 +14,14 @@ export function sessionDisallows(session, name) {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/** A settings gate denies `name` to the session (as last reconciled, tool-availability.mjs). */
|
|
18
|
+
export function sessionFeatureDenies(session, name) {
|
|
19
|
+
return (
|
|
20
|
+
Array.isArray(session?.featureDeniedTools) &&
|
|
21
|
+
session.featureDeniedTools.some((denied) => lowerName(denied) === lowerName(name))
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
/**
|
|
18
26
|
* The session's own tool policy permits `name`: discovery (load_tool by name
|
|
19
27
|
* or query) shows, selects and describes only what execution would run.
|
|
@@ -2,12 +2,15 @@ import { clean } from './session-text.mjs';
|
|
|
2
2
|
import { parseMcpToolName } from '../../../shared/tool-primitives.mjs';
|
|
3
3
|
import { mcpServerLines } from '../context/deferred-tools.mjs';
|
|
4
4
|
|
|
5
|
+
// A delta names tools; its description is a hint, the schema arrives on load.
|
|
6
|
+
export const DELTA_DESCRIPTION_MAX = 80;
|
|
7
|
+
|
|
5
8
|
function compactDescription(value) {
|
|
6
|
-
|
|
9
|
+
const text = String(value || '')
|
|
7
10
|
.replace(/\s+/g, ' ')
|
|
8
11
|
.replace(/[<>]/g, '')
|
|
9
|
-
.trim()
|
|
10
|
-
|
|
12
|
+
.trim();
|
|
13
|
+
return text.length > DELTA_DESCRIPTION_MAX ? `${text.slice(0, DELTA_DESCRIPTION_MAX - 1).trimEnd()}…` : text;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
function addedEntry(entry) {
|
|
@@ -168,6 +171,17 @@ export function snapshotPendingDeferredToolDelta(session) {
|
|
|
168
171
|
};
|
|
169
172
|
}
|
|
170
173
|
|
|
174
|
+
/** Drop pending additions the prompt now lists itself (`listed`). */
|
|
175
|
+
export function dropPendingDeferredToolAdds(session, listed) {
|
|
176
|
+
const pending = session?.pendingDeferredToolDelta;
|
|
177
|
+
if (!pending || !Array.isArray(pending.added)) return;
|
|
178
|
+
const added = pending.added.filter((entry) => !listed.has(clean(entry?.name)));
|
|
179
|
+
if (added.length === pending.added.length) return;
|
|
180
|
+
if (!added.length && !(pending.removed || []).length) delete session.pendingDeferredToolDelta;
|
|
181
|
+
else session.pendingDeferredToolDelta = { ...pending, added };
|
|
182
|
+
session.updatedAt = Date.now();
|
|
183
|
+
}
|
|
184
|
+
|
|
171
185
|
export function acknowledgePendingDeferredToolDelta(session, revision) {
|
|
172
186
|
if (!session || typeof session !== 'object') return false;
|
|
173
187
|
const current = normalizedDelta(session.pendingDeferredToolDelta);
|
|
@@ -5,21 +5,20 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { durationLabel } from './goal-text.mjs';
|
|
7
7
|
import { compactSessionTitle } from './session-title.mjs';
|
|
8
|
-
import { applyGoalTaskChanges, optionalGoalTaskChanges } from './goal-tasks.mjs';
|
|
9
8
|
import {
|
|
10
9
|
NO_DEADLINE_WARNING_MS,
|
|
11
10
|
activateGoal,
|
|
12
11
|
activeElapsedMs,
|
|
13
12
|
assertSessionId,
|
|
14
13
|
clearTurnFailures,
|
|
15
|
-
goalTimeMode,
|
|
16
14
|
parseGoalDuration,
|
|
17
15
|
parseUserCommand,
|
|
16
|
+
publicGoal,
|
|
18
17
|
resumeGoalState,
|
|
19
18
|
stopActiveClock,
|
|
20
19
|
validateObjective,
|
|
21
20
|
} from './goal-state.mjs';
|
|
22
|
-
import { assertExpectedGoal, goalFinished } from './goal-mutations.mjs';
|
|
21
|
+
import { assertExpectedGoal, goalFinished, goalStalled } from './goal-mutations.mjs';
|
|
23
22
|
import { clean } from '../../../shared/clean.mjs';
|
|
24
23
|
|
|
25
24
|
/** "12m remaining" for a budgeted Goal, "3m elapsed" for an open-ended one. */
|
|
@@ -31,6 +30,9 @@ function goalTimingLabel(goal) {
|
|
|
31
30
|
|
|
32
31
|
const reply = (action, goal, message) => ({ ok: true, action, goal, message });
|
|
33
32
|
|
|
33
|
+
/** `code` of the create answer that asks the user to confirm replacing a stalled Goal. */
|
|
34
|
+
export const GOAL_REPLACE_REQUIRED = 'goal-replace-required';
|
|
35
|
+
|
|
34
36
|
const GOAL_CONTROL_ACTIONS = {
|
|
35
37
|
async stop(ctx, { id, expectedGoalId, action }) {
|
|
36
38
|
const goal = await ctx.abandonGoal(id, { expectedGoalId, archive: true });
|
|
@@ -52,34 +54,27 @@ const GOAL_CONTROL_ACTIONS = {
|
|
|
52
54
|
throw new Error('a completed Goal cannot be resumed; edit it or create a new Goal');
|
|
53
55
|
}
|
|
54
56
|
const added = args.duration ? parseGoalDuration(args.duration) : null;
|
|
55
|
-
const timeMode = goalTimeMode(args.timeMode, goal.timeMode);
|
|
56
|
-
// Empty optional fields from frozen provider schemas still mean a plain
|
|
57
|
-
// resume. Supplied changes validate before activation and share its write.
|
|
58
|
-
const taskChanges = optionalGoalTaskChanges(args);
|
|
59
|
-
const hasTaskChanges = [taskChanges.updates, taskChanges.tasks].some(
|
|
60
|
-
(value) => value != null && (!Array.isArray(value) || value.length > 0)
|
|
61
|
-
);
|
|
62
|
-
if (hasTaskChanges) applyGoalTaskChanges(goal, taskChanges, { partial: true, at });
|
|
63
57
|
resumeGoalState(goal, at, added);
|
|
64
|
-
goal.timeMode = timeMode;
|
|
65
58
|
const resumed = await ctx.commit(id, goal);
|
|
66
59
|
return reply(action, resumed, `Goal resumed · ${resumed.objective} · ${goalTimingLabel(resumed)}`);
|
|
67
60
|
},
|
|
68
61
|
async edit(ctx, { id, goal, args, at, action }) {
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
// away every completed row; re-aligning a stale list is set_tasks' job.
|
|
62
|
+
// The session's todo list is untouched by an objective edit; the
|
|
63
|
+
// objective-updated reminder asks the model to re-align it.
|
|
72
64
|
const objective = validateObjective(args.objective);
|
|
73
65
|
let timeLimitMs = goal.timeLimitMs;
|
|
74
|
-
if (args.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
if (args.time_limit_minutes != null && args.time_limit_minutes !== '') {
|
|
67
|
+
const minutes = Number(args.time_limit_minutes);
|
|
68
|
+
if (!Number.isFinite(minutes) || minutes < 0) throw new Error('goal time_limit_minutes must be 0 or more');
|
|
69
|
+
timeLimitMs = minutes === 0 ? 0 : parseGoalDuration(minutes * 60_000);
|
|
70
|
+
} else if (args.timeLimitMs != null) {
|
|
71
|
+
timeLimitMs = args.timeLimitMs === 0 ? 0 : parseGoalDuration(args.timeLimitMs);
|
|
72
|
+
} else if (args.duration != null) timeLimitMs = parseGoalDuration(args.duration);
|
|
73
|
+
if (objective !== goal.objective) goal.objectiveRevision = (Number(goal.objectiveRevision) || 0) + 1;
|
|
78
74
|
goal.objective = objective;
|
|
79
75
|
goal.title = compactSessionTitle(goal.objective);
|
|
80
76
|
if (timeLimitMs !== goal.timeLimitMs) goal.deadlineWarnedMs = NO_DEADLINE_WARNING_MS;
|
|
81
77
|
goal.timeLimitMs = timeLimitMs;
|
|
82
|
-
goal.timeMode = timeMode;
|
|
83
78
|
if (goal.status === 'complete') activateGoal(goal, at);
|
|
84
79
|
else clearTurnFailures(goal);
|
|
85
80
|
goal.updatedAt = at;
|
|
@@ -106,13 +101,19 @@ const GOAL_CONTROL_ACTIONS = {
|
|
|
106
101
|
return reply(action, timed, `Goal duration · ${durationLabel(limit)}`);
|
|
107
102
|
},
|
|
108
103
|
async complete(ctx, { id, action }) {
|
|
109
|
-
const goal = await ctx.updateGoal(id, { status: 'complete' }
|
|
104
|
+
const goal = await ctx.updateGoal(id, { status: 'complete' });
|
|
110
105
|
return reply(action, goal, `Goal complete · ${goal.objective} · ${durationLabel(goal.timeUsedMs)} elapsed`);
|
|
111
106
|
},
|
|
112
|
-
async dismiss(ctx, { id, goal, at, action }) {
|
|
107
|
+
async dismiss(ctx, { id, goal, at, action, expectedGoalId }) {
|
|
108
|
+
// A stalled Goal waits for the user and nothing else retires it: dismissing
|
|
109
|
+
// it is the stop-and-archive the Stop control confirms.
|
|
110
|
+
if (goalStalled(goal)) {
|
|
111
|
+
await ctx.abandonGoal(id, { expectedGoalId, archive: true });
|
|
112
|
+
return reply(action, null, `Goal dismissed · ${goal.objective}`);
|
|
113
|
+
}
|
|
113
114
|
// The user's acknowledgement of finished work: the same archive a new
|
|
114
115
|
// prompt applies, so the chrome lets go while the record stays.
|
|
115
|
-
if (!goalFinished(goal)) throw new Error('only a finished Goal can be dismissed; stop
|
|
116
|
+
if (!goalFinished(goal)) throw new Error('only a finished or stalled Goal can be dismissed; stop it first');
|
|
116
117
|
if (!goal.archivedAt) {
|
|
117
118
|
goal.archivedAt = at;
|
|
118
119
|
goal.updatedAt = at;
|
|
@@ -129,11 +130,33 @@ export async function runGoalControl(ctx, sessionId, rawArgs = {}) {
|
|
|
129
130
|
else if (rawArgs?.command != null) args = { ...rawArgs, ...parseUserCommand(rawArgs.command) };
|
|
130
131
|
const action = clean(args?.action || 'get').toLowerCase();
|
|
131
132
|
if (action === 'create') {
|
|
133
|
+
const existing = ctx.readRecord(id).goal;
|
|
134
|
+
// Replacing a stalled Goal is the user's call: ask once, and let the same
|
|
135
|
+
// request come back with `replace` once they agree.
|
|
136
|
+
if (goalStalled(existing) && args.replace !== true) {
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
action,
|
|
140
|
+
code: GOAL_REPLACE_REQUIRED,
|
|
141
|
+
goal: publicGoal(existing, ctx.now()),
|
|
142
|
+
message:
|
|
143
|
+
`A ${existing.status} Goal already exists · ${existing.objective}. ` +
|
|
144
|
+
`Run /goal --replace=${existing.id} followed by the new objective to replace it.`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// The confirmation names the Goal the user saw; it runs inside the
|
|
148
|
+
// mutation queue, so a Goal that changed since refuses instead of retiring.
|
|
149
|
+
if (args.replace === true && existing) {
|
|
150
|
+
if (goalStalled(existing) && !clean(args.expectedGoalId)) {
|
|
151
|
+
throw new Error(`a replace must name the stalled Goal to retire (--replace=${existing.id})`);
|
|
152
|
+
}
|
|
153
|
+
if (clean(args.expectedGoalId)) assertExpectedGoal(existing, args.expectedGoalId, 'replace');
|
|
154
|
+
}
|
|
132
155
|
const goal = await ctx.createGoal(id, {
|
|
133
156
|
objective: args.objective,
|
|
134
157
|
duration: args.duration,
|
|
135
158
|
timeLimitMs: args.timeLimitMs,
|
|
136
|
-
|
|
159
|
+
replace: args.replace === true,
|
|
137
160
|
});
|
|
138
161
|
return reply(action, goal, `Goal active · ${goal.objective} · ${goalTimingLabel(goal)}`);
|
|
139
162
|
}
|
|
@@ -153,9 +176,6 @@ export async function runGoalControl(ctx, sessionId, rawArgs = {}) {
|
|
|
153
176
|
const goal = ctx.requireGoal(id);
|
|
154
177
|
const expectedGoalId = clean(args.expectedGoalId);
|
|
155
178
|
assertExpectedGoal(goal, expectedGoalId, 'update');
|
|
156
|
-
if (args.revision != null && args.revision !== goal.revision) {
|
|
157
|
-
throw new Error('Goal changed while editing; reopen the editor before saving');
|
|
158
|
-
}
|
|
159
179
|
const at = ctx.now();
|
|
160
180
|
if (goal.status === 'stopped' && action !== 'dismiss') {
|
|
161
181
|
throw new Error('a stopped Goal cannot be changed; create a new Goal');
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
GOAL_LIVE_HEARTBEAT_MS,
|
|
3
|
+
activeElapsedMs,
|
|
4
|
+
assertSessionId,
|
|
5
|
+
discardSuspendedTime,
|
|
6
|
+
normalizeDeadlineWarnedMs,
|
|
7
|
+
stopActiveClock,
|
|
8
|
+
} from './goal-state.mjs';
|
|
9
|
+
|
|
10
|
+
// Wall time may outrun the monotonic clock by a little without any suspend.
|
|
11
|
+
const MIN_SUSPEND_EVIDENCE_MS = 5_000;
|
|
2
12
|
|
|
3
13
|
function normalizeDeadlineWarningMs(value) {
|
|
4
14
|
const thresholds = (Array.isArray(value) ? value : [value])
|
|
@@ -9,10 +19,52 @@ function normalizeDeadlineWarningMs(value) {
|
|
|
9
19
|
|
|
10
20
|
// Timers only observe crossings. Every durable transition joins the same
|
|
11
21
|
// mutation queue as user edits and re-checks the current committed record.
|
|
12
|
-
export function createGoalDeadlines({
|
|
22
|
+
export function createGoalDeadlines({
|
|
23
|
+
now,
|
|
24
|
+
readRecord,
|
|
25
|
+
withMutation,
|
|
26
|
+
commit,
|
|
27
|
+
stampLive,
|
|
28
|
+
onStorageError,
|
|
29
|
+
deadlineWarningMs,
|
|
30
|
+
monotonicNow = null,
|
|
31
|
+
// libuv's clock (Node's performance.now) stops during suspend on Linux and
|
|
32
|
+
// macOS (CLOCK_MONOTONIC, mach_absolute_time) but typically keeps counting
|
|
33
|
+
// on Windows (QueryPerformanceCounter), so there it proves nothing.
|
|
34
|
+
suspendDetectable = process.platform !== 'win32',
|
|
35
|
+
}) {
|
|
13
36
|
const warningThresholdsMs = normalizeDeadlineWarningMs(deadlineWarningMs);
|
|
37
|
+
// Session -> wall and monotonic readings at the last moment the Goal was
|
|
38
|
+
// known to be running. Wall time that outran the monotonic clock since then
|
|
39
|
+
// is suspend: positive evidence. A stall of an awake process moves both
|
|
40
|
+
// clocks, so it is never dropped; without evidence time is counted, because
|
|
41
|
+
// overcounting is safer than a Goal running past its cap.
|
|
42
|
+
const anchors = new Map();
|
|
43
|
+
// Only a Goal that is still running keeps an anchor: an inactive one has its
|
|
44
|
+
// entry retired by armDeadline, and recording it again would leak.
|
|
45
|
+
const observe = (id, at, goal) => {
|
|
46
|
+
if (goal?.status !== 'active') return;
|
|
47
|
+
const mono = suspendDetectable ? monotonicNow?.() : null;
|
|
48
|
+
if (mono == null) anchors.delete(id);
|
|
49
|
+
else anchors.set(id, { wall: at, mono });
|
|
50
|
+
};
|
|
51
|
+
const suspendedMs = (id, at) => {
|
|
52
|
+
const anchor = anchors.get(id);
|
|
53
|
+
if (!anchor) return 0;
|
|
54
|
+
const mono = monotonicNow?.();
|
|
55
|
+
// No reading now: this interval has no evidence and is counted. The anchor
|
|
56
|
+
// goes too, or a later valid reading would discard time already counted.
|
|
57
|
+
if (mono == null) {
|
|
58
|
+
anchors.delete(id);
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
const suspended = at - anchor.wall - (mono - anchor.mono);
|
|
62
|
+
return suspended > MIN_SUSPEND_EVIDENCE_MS ? suspended : 0;
|
|
63
|
+
};
|
|
64
|
+
const dropSuspended = (id, goal, at) => discardSuspendedTime(goal, at, suspendedMs(id, at));
|
|
14
65
|
const deadlineTimers = new Map();
|
|
15
66
|
const warningTimers = new Map();
|
|
67
|
+
const heartbeatTimers = new Map();
|
|
16
68
|
const expiryPending = new Set();
|
|
17
69
|
const warningPending = new Set();
|
|
18
70
|
let closed = false;
|
|
@@ -30,25 +82,70 @@ export function createGoalDeadlines({ now, readRecord, withMutation, commit, onS
|
|
|
30
82
|
}
|
|
31
83
|
};
|
|
32
84
|
|
|
85
|
+
// The heartbeat has its own lifetime: re-arming the deadline (every snapshot
|
|
86
|
+
// read does) must not restart its interval, or reads under a minute apart
|
|
87
|
+
// would keep it from ever firing.
|
|
88
|
+
const stopHeartbeat = (sessionId) => {
|
|
89
|
+
const heartbeat = heartbeatTimers.get(sessionId);
|
|
90
|
+
if (heartbeat) {
|
|
91
|
+
clearInterval(heartbeat);
|
|
92
|
+
heartbeatTimers.delete(sessionId);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// An active Goal's clock runs only while a live runtime holds it. The beat
|
|
97
|
+
// re-stamps the record so a later load can tell how long nothing ran it; a
|
|
98
|
+
// beat that finds a long silence (the PC slept) drops that stretch from the
|
|
99
|
+
// open segment and republishes. The stamp alone is a silent write.
|
|
100
|
+
const beat = (sessionId) => {
|
|
101
|
+
const id = assertSessionId(sessionId);
|
|
102
|
+
void withMutation(id, async () => {
|
|
103
|
+
if (closed) return;
|
|
104
|
+
const goal = readRecord(id).goal;
|
|
105
|
+
if (goal?.status !== 'active' || !goal.lastStartedAt) return;
|
|
106
|
+
const at = now();
|
|
107
|
+
if (dropSuspended(id, goal, at)) await commit(id, goal);
|
|
108
|
+
else {
|
|
109
|
+
goal.liveAt = at;
|
|
110
|
+
await stampLive(id, goal);
|
|
111
|
+
}
|
|
112
|
+
observe(id, at, goal);
|
|
113
|
+
}).catch(onStorageError);
|
|
114
|
+
};
|
|
115
|
+
|
|
33
116
|
const expiredProjection = (goal, at) => {
|
|
34
117
|
if (goal?.status !== 'active' || !(goal.timeLimitMs > 0) || activeElapsedMs(goal, at) < goal.timeLimitMs)
|
|
35
118
|
return false;
|
|
36
119
|
stopActiveClock(goal, at);
|
|
37
120
|
goal.status = 'duration_reached';
|
|
38
|
-
|
|
121
|
+
// The cap is what was spent; a late observation must not report more.
|
|
122
|
+
goal.timeUsedMs = goal.timeLimitMs;
|
|
39
123
|
goal.updatedAt = at;
|
|
40
124
|
return true;
|
|
41
125
|
};
|
|
42
126
|
|
|
127
|
+
// Every path that reads an open segment against the clock (expiry, warnings,
|
|
128
|
+
// the next timer) first drops the suspended stretch, as the heartbeat does;
|
|
129
|
+
// otherwise a read after the PC slept would charge the sleep to the budget.
|
|
130
|
+
// `goal` is a working copy. Returns whether it changed.
|
|
131
|
+
const settleProjection = (id, goal, at) => {
|
|
132
|
+
const dropped = dropSuspended(id, goal, at);
|
|
133
|
+
return expiredProjection(goal, at) || dropped;
|
|
134
|
+
};
|
|
135
|
+
|
|
43
136
|
const limitIfExpired = (sessionId) => {
|
|
44
137
|
const id = assertSessionId(sessionId);
|
|
45
138
|
const goal = readRecord(id).goal;
|
|
46
|
-
if (
|
|
139
|
+
if (settleProjection(id, goal, now()) && !closed && !expiryPending.has(id)) {
|
|
47
140
|
expiryPending.add(id);
|
|
48
141
|
void withMutation(id, async () => {
|
|
49
142
|
if (closed) return;
|
|
50
143
|
const current = readRecord(id).goal;
|
|
51
|
-
|
|
144
|
+
const at = now();
|
|
145
|
+
if (settleProjection(id, current, at)) {
|
|
146
|
+
await commit(id, current);
|
|
147
|
+
observe(id, at, current);
|
|
148
|
+
}
|
|
52
149
|
})
|
|
53
150
|
.catch(onStorageError)
|
|
54
151
|
.finally(() => expiryPending.delete(id));
|
|
@@ -78,8 +175,9 @@ export function createGoalDeadlines({ now, readRecord, withMutation, commit, onS
|
|
|
78
175
|
return delay;
|
|
79
176
|
};
|
|
80
177
|
|
|
81
|
-
const warningState = (goal, at) => {
|
|
178
|
+
const warningState = (id, goal, at) => {
|
|
82
179
|
if (goal?.status !== 'active' || !goal.lastStartedAt || !(Number(goal.timeLimitMs) > 0)) return null;
|
|
180
|
+
dropSuspended(id, goal, at);
|
|
83
181
|
const remainingMs = Math.max(0, Number(goal.timeLimitMs) - activeElapsedMs(goal, at));
|
|
84
182
|
if (remainingMs <= 0) return null;
|
|
85
183
|
return {
|
|
@@ -92,14 +190,14 @@ export function createGoalDeadlines({ now, readRecord, withMutation, commit, onS
|
|
|
92
190
|
const deliverDeadlineWarning = (sessionId, thresholdMs = 0) => {
|
|
93
191
|
const id = assertSessionId(sessionId);
|
|
94
192
|
if (closed || warningPending.has(id)) return;
|
|
95
|
-
const pending = warningState(readRecord(id).goal, now());
|
|
193
|
+
const pending = warningState(id, readRecord(id).goal, now());
|
|
96
194
|
const target = thresholdMs || pending?.crossedMs || 0;
|
|
97
195
|
if (!pending || !target || pending.crossedMs !== target) return;
|
|
98
196
|
warningPending.add(id);
|
|
99
197
|
void withMutation(id, async () => {
|
|
100
198
|
if (closed) return;
|
|
101
199
|
const current = readRecord(id).goal;
|
|
102
|
-
const state = warningState(current, now());
|
|
200
|
+
const state = warningState(id, current, now());
|
|
103
201
|
if (!state?.crossedMs || state.crossedMs !== target) return;
|
|
104
202
|
current.deadlineWarnedMs = Math.min(normalizeDeadlineWarnedMs(current.deadlineWarnedMs), target);
|
|
105
203
|
current.warningRevision = Math.max(0, Math.floor(Number(current.warningRevision) || 0)) + 1;
|
|
@@ -123,14 +221,26 @@ export function createGoalDeadlines({ now, readRecord, withMutation, commit, onS
|
|
|
123
221
|
if (closed) return;
|
|
124
222
|
clearDeadline(sessionId);
|
|
125
223
|
const goal = readRecord(sessionId).goal;
|
|
224
|
+
if (goal?.status === 'active' && goal.lastStartedAt) {
|
|
225
|
+
if (!anchors.has(sessionId)) observe(sessionId, now(), goal);
|
|
226
|
+
if (!heartbeatTimers.has(sessionId)) {
|
|
227
|
+
const timer = setInterval(() => beat(sessionId), GOAL_LIVE_HEARTBEAT_MS);
|
|
228
|
+
timer.unref?.();
|
|
229
|
+
heartbeatTimers.set(sessionId, timer);
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
stopHeartbeat(sessionId);
|
|
233
|
+
anchors.delete(sessionId);
|
|
234
|
+
}
|
|
126
235
|
if (goal?.status !== 'active' || !goal.lastStartedAt || !(Number(goal.timeLimitMs) > 0)) return;
|
|
127
236
|
const at = now();
|
|
237
|
+
dropSuspended(sessionId, goal, at);
|
|
128
238
|
const remainingMs = Math.max(0, goal.timeLimitMs - activeElapsedMs(goal, at));
|
|
129
239
|
if (remainingMs <= 0) {
|
|
130
240
|
queueMicrotask(() => reportingErrors(() => limitIfExpired(sessionId)));
|
|
131
241
|
return;
|
|
132
242
|
}
|
|
133
|
-
const warnings = warningState(goal, at);
|
|
243
|
+
const warnings = warningState(sessionId, goal, at);
|
|
134
244
|
if (warnings?.crossedMs) {
|
|
135
245
|
const thresholdMs = warnings.crossedMs;
|
|
136
246
|
queueMicrotask(() => reportingErrors(() => deliverDeadlineWarning(sessionId, thresholdMs)));
|
|
@@ -146,7 +256,19 @@ export function createGoalDeadlines({ now, readRecord, withMutation, commit, onS
|
|
|
146
256
|
}
|
|
147
257
|
const timer = setTimeout(() => {
|
|
148
258
|
deadlineTimers.delete(sessionId);
|
|
149
|
-
|
|
259
|
+
// A timer that fires after the PC slept must not expire the Goal on
|
|
260
|
+
// time nobody ran: settle the suspension first, then decide.
|
|
261
|
+
reportingErrors(() => {
|
|
262
|
+
void withMutation(sessionId, async () => {
|
|
263
|
+
if (closed) return;
|
|
264
|
+
const current = readRecord(sessionId).goal;
|
|
265
|
+
const at = now();
|
|
266
|
+
if (current && settleProjection(sessionId, current, at)) {
|
|
267
|
+
await commit(sessionId, current);
|
|
268
|
+
observe(sessionId, at, current);
|
|
269
|
+
}
|
|
270
|
+
}).catch(onStorageError);
|
|
271
|
+
});
|
|
150
272
|
}, remainingMs);
|
|
151
273
|
timer.unref?.();
|
|
152
274
|
deadlineTimers.set(sessionId, timer);
|
|
@@ -156,14 +278,23 @@ export function createGoalDeadlines({ now, readRecord, withMutation, commit, onS
|
|
|
156
278
|
armDeadline,
|
|
157
279
|
clearDeadline,
|
|
158
280
|
limitIfExpired,
|
|
281
|
+
// A write is about to stamp `liveAt`: charge the pending suspend to the
|
|
282
|
+
// working copy first, so the stamp never hides it, and `refreshAnchor`
|
|
283
|
+
// starts the next evidence window once the write has landed.
|
|
284
|
+
settleSuspended: dropSuspended,
|
|
285
|
+
refreshAnchor: observe,
|
|
286
|
+
anchorCount: () => anchors.size,
|
|
159
287
|
close() {
|
|
160
288
|
closed = true;
|
|
161
289
|
for (const timer of deadlineTimers.values()) clearTimeout(timer);
|
|
162
290
|
deadlineTimers.clear();
|
|
163
291
|
for (const timer of warningTimers.values()) clearTimeout(timer);
|
|
164
292
|
warningTimers.clear();
|
|
293
|
+
for (const timer of heartbeatTimers.values()) clearInterval(timer);
|
|
294
|
+
heartbeatTimers.clear();
|
|
165
295
|
warningPending.clear();
|
|
166
296
|
expiryPending.clear();
|
|
297
|
+
anchors.clear();
|
|
167
298
|
},
|
|
168
299
|
};
|
|
169
300
|
}
|