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
|
@@ -18,7 +18,14 @@ import {
|
|
|
18
18
|
withDefaultPresets,
|
|
19
19
|
} from '../runtime/agent/orchestrator/config-presets.mjs';
|
|
20
20
|
import { syncSessionCompaction } from './session-compaction-sync.mjs';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
configChangeDomains,
|
|
23
|
+
configChangeIsEmpty,
|
|
24
|
+
configChangeSnapshot,
|
|
25
|
+
diffConfigSnapshots,
|
|
26
|
+
withConfigDomainPending,
|
|
27
|
+
} from './config-change-effects.mjs';
|
|
28
|
+
import { configWithOutputStyle, createConfigWriters } from './config-lifecycle/config-writers.mjs';
|
|
22
29
|
import { createLeadRouteSync } from './config-lifecycle/lead-route-sync.mjs';
|
|
23
30
|
import { createOutputStyleStatusCache } from './config-lifecycle/output-style-cache.mjs';
|
|
24
31
|
|
|
@@ -45,9 +52,19 @@ function seedChanges(config, changes) {
|
|
|
45
52
|
.map((key) => ({ path: [key], value: structuredClone(config[key]), ifAbsent: true }));
|
|
46
53
|
}
|
|
47
54
|
|
|
55
|
+
// The agent config load carries no root `outputStyle`; the stored root value
|
|
56
|
+
// is laid on it (absent: the default style applies).
|
|
57
|
+
function withStoredOutputStyle(config, storedOutputStyle) {
|
|
58
|
+
return typeof storedOutputStyle === 'string' && storedOutputStyle.trim()
|
|
59
|
+
? { ...config, outputStyle: storedOutputStyle.trim() }
|
|
60
|
+
: config;
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
// The config just read from disk with this runtime's still-pending edits laid
|
|
49
64
|
// back on it (the config and skills writers hold the only copy of them).
|
|
50
|
-
|
|
65
|
+
// The agent config load carries no root `outputStyle`: the stored root value
|
|
66
|
+
// (`storedOutputStyle`) is laid on it, and this runtime's pending pick wins.
|
|
67
|
+
function withPendingEdits(loaded, writers, storedOutputStyle) {
|
|
51
68
|
let next = loaded;
|
|
52
69
|
if (writers.hasPendingConfigChanges()) {
|
|
53
70
|
// Preserve only our pending edits. Peer changes and fresh secret overlays
|
|
@@ -67,6 +84,9 @@ function withPendingEdits(loaded, writers) {
|
|
|
67
84
|
if (pendingSkills !== null) {
|
|
68
85
|
next = { ...next, skills: { ...(next.skills || {}), disabled: pendingSkills } };
|
|
69
86
|
}
|
|
87
|
+
const pendingOutputStyle = writers.pendingOutputStyle();
|
|
88
|
+
if (pendingOutputStyle !== null) next = configWithOutputStyle(next, pendingOutputStyle);
|
|
89
|
+
else next = withStoredOutputStyle(next, storedOutputStyle);
|
|
70
90
|
return next;
|
|
71
91
|
}
|
|
72
92
|
|
|
@@ -85,6 +105,7 @@ function withPendingEdits(loaded, writers) {
|
|
|
85
105
|
export function resolveInitialConfigState({
|
|
86
106
|
initialConfig,
|
|
87
107
|
loadConfig,
|
|
108
|
+
readStoredOutputStyle = null,
|
|
88
109
|
resolveRoute,
|
|
89
110
|
provider,
|
|
90
111
|
model,
|
|
@@ -97,8 +118,12 @@ export function resolveInitialConfigState({
|
|
|
97
118
|
selectedContextWindow,
|
|
98
119
|
agentOwned = false,
|
|
99
120
|
}) {
|
|
121
|
+
// An injected config is complete as given; a loaded one takes the stored
|
|
122
|
+
// root output style (`readStoredOutputStyle`) as every reload does.
|
|
100
123
|
const config = withGrandfatheredBuiltins(
|
|
101
|
-
initialConfig && typeof initialConfig === 'object'
|
|
124
|
+
initialConfig && typeof initialConfig === 'object'
|
|
125
|
+
? initialConfig
|
|
126
|
+
: withStoredOutputStyle(loadConfig(), readStoredOutputStyle?.())
|
|
102
127
|
);
|
|
103
128
|
// A requested percent replaces a saved token window; requested tokens
|
|
104
129
|
// replace both (resolveRouteContextState lets tokens win).
|
|
@@ -135,10 +160,12 @@ export function createConfigLifecycle({
|
|
|
135
160
|
getConfigHasSecrets,
|
|
136
161
|
setConfigHasSecrets,
|
|
137
162
|
getRoute,
|
|
138
|
-
// The live session follows the reloaded global compaction settings
|
|
139
|
-
// the runtime runs on an injected (headless) config.
|
|
163
|
+
// The live session follows the reloaded global compaction settings.
|
|
140
164
|
getSession,
|
|
141
|
-
|
|
165
|
+
// A headless caller injects a complete, deliberately isolated config: it
|
|
166
|
+
// never reloads from disk (no change subscription either), and its sessions
|
|
167
|
+
// keep the compaction they were created with.
|
|
168
|
+
injectedConfig = false,
|
|
142
169
|
// Main route sync on reload (both optional: a lifecycle without them never syncs)
|
|
143
170
|
setRouteState,
|
|
144
171
|
resolveRoute,
|
|
@@ -153,9 +180,14 @@ export function createConfigLifecycle({
|
|
|
153
180
|
clean,
|
|
154
181
|
resolve,
|
|
155
182
|
performanceNow = () => performance.now(),
|
|
156
|
-
|
|
183
|
+
// The runtime's change-driven effects (config-change-effects.mjs), given the
|
|
184
|
+
// diff against the snapshot this runtime last applied.
|
|
185
|
+
onConfigChanged,
|
|
186
|
+
// The backoff of a failed effect's retries; the last delay repeats.
|
|
187
|
+
effectRetryDelaysMs = [1000, 4000, 15000, 60000],
|
|
157
188
|
STANDALONE_DATA_DIR,
|
|
158
189
|
}) {
|
|
190
|
+
let disposed = false;
|
|
159
191
|
const outputStyleCache = createOutputStyleStatusCache({
|
|
160
192
|
cfgMod,
|
|
161
193
|
STANDALONE_DATA_DIR,
|
|
@@ -217,25 +249,125 @@ export function createConfigLifecycle({
|
|
|
217
249
|
}
|
|
218
250
|
|
|
219
251
|
// --- reload / ensure --------------------------------------------------------
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
252
|
+
const changeSnapshot = (config) =>
|
|
253
|
+
configChangeSnapshot({
|
|
254
|
+
config,
|
|
255
|
+
root: sharedCfgMod?.readConfig?.(),
|
|
256
|
+
secretTokens: sharedCfgMod?.secretChangeTokens?.(),
|
|
257
|
+
});
|
|
258
|
+
// What this runtime's effects last ran against, starting from the boot
|
|
259
|
+
// config (the snapshot does not depend on whether a load read secrets).
|
|
260
|
+
let appliedSnapshot = changeSnapshot(getConfig());
|
|
261
|
+
// The stored settings the current config object was taken from (never
|
|
262
|
+
// marked pending): the object is replaced only when they changed, so a
|
|
263
|
+
// retry of a failed effect alone keeps it.
|
|
264
|
+
let loadedSnapshot = appliedSnapshot;
|
|
265
|
+
|
|
266
|
+
// A pending debounced write holds the only copy of the latest change.
|
|
267
|
+
// Flush it before re-reading from disk so loadConfig() observes (and the
|
|
268
|
+
// subsequent adopt preserves) that change instead of reverting to a stale
|
|
269
|
+
// on-disk snapshot.
|
|
270
|
+
function loadFullConfig() {
|
|
225
271
|
writers.flushConfigSave();
|
|
226
|
-
|
|
272
|
+
if (injectedConfig) return null;
|
|
273
|
+
return withPendingEdits(cfgMod.loadConfig(), writers, sharedCfgMod?.readConfig?.()?.outputStyle);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function reloadFullConfig() {
|
|
277
|
+
return adoptFullConfig(loadFullConfig());
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function adoptFullConfig(next) {
|
|
281
|
+
if (!next) return getConfig();
|
|
227
282
|
const adopted = adoptConfig(next, { hasSecrets: true });
|
|
228
283
|
configSaveBaseline = structuredClone(adopted);
|
|
229
284
|
leadRoute.syncEmptyMainRoute(adopted);
|
|
230
|
-
|
|
285
|
+
syncSessionCompaction(getSession?.(), adopted);
|
|
231
286
|
return adopted;
|
|
232
287
|
}
|
|
233
288
|
|
|
234
289
|
function ensureFullConfig() {
|
|
235
|
-
if (getConfigHasSecrets()) return getConfig();
|
|
290
|
+
if (injectedConfig || getConfigHasSecrets()) return getConfig();
|
|
236
291
|
return reloadFullConfig();
|
|
237
292
|
}
|
|
238
293
|
|
|
294
|
+
/** The change step: reload, then run the effects keyed by what differs from
|
|
295
|
+
* the last applied snapshot. A writer that runs it right after its own write
|
|
296
|
+
* has applied the change, so the notification that follows finds nothing.
|
|
297
|
+
* The config object is replaced only when the stored settings changed (a
|
|
298
|
+
* retry of a failed effect alone keeps it): consumers key turn-start
|
|
299
|
+
* reconciles on its identity. */
|
|
300
|
+
function applyConfigChange() {
|
|
301
|
+
// A load that throws changes nothing: pending domains stay pending.
|
|
302
|
+
const loaded = loadFullConfig();
|
|
303
|
+
// Every pending domain is in this step's diff, so it runs again here (and
|
|
304
|
+
// a scheduled retry is this step).
|
|
305
|
+
pendingDomains = new Set();
|
|
306
|
+
clearTimeout(retryTimer);
|
|
307
|
+
retryTimer = null;
|
|
308
|
+
outputStyleCache.invalidateOutputStyleStatusCache();
|
|
309
|
+
const next = changeSnapshot(loaded ?? getConfig());
|
|
310
|
+
const change = diffConfigSnapshots(appliedSnapshot, next);
|
|
311
|
+
appliedSnapshot = next;
|
|
312
|
+
const settingsChanged = !configChangeIsEmpty(diffConfigSnapshots(loadedSnapshot, next));
|
|
313
|
+
loadedSnapshot = next;
|
|
314
|
+
const config = settingsChanged ? adoptFullConfig(loaded) : getConfig();
|
|
315
|
+
let outcome;
|
|
316
|
+
try {
|
|
317
|
+
outcome = onConfigChanged?.(change, config);
|
|
318
|
+
} catch {
|
|
319
|
+
outcome = configChangeDomains(change);
|
|
320
|
+
}
|
|
321
|
+
void Promise.resolve(outcome).then(
|
|
322
|
+
(failedDomains) => settleConfigEffects(failedDomains),
|
|
323
|
+
() => settleConfigEffects(configChangeDomains(change))
|
|
324
|
+
);
|
|
325
|
+
return change;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// A domain whose effect failed is marked not applied, so the next change
|
|
329
|
+
// step (any notification, a writer's own step, or the retry below) runs it
|
|
330
|
+
// again; the applied snapshot otherwise advances as the step runs, which
|
|
331
|
+
// keeps a step overlapping an in-flight effect from repeating it. The retry
|
|
332
|
+
// backs off to the last delay and keeps going until the domain succeeds or
|
|
333
|
+
// the sync is disposed.
|
|
334
|
+
let pendingDomains = new Set();
|
|
335
|
+
let retryTimer = null;
|
|
336
|
+
let retryAttempt = 0;
|
|
337
|
+
function settleConfigEffects(failedDomains) {
|
|
338
|
+
for (const domain of Array.isArray(failedDomains) ? failedDomains : []) {
|
|
339
|
+
appliedSnapshot = withConfigDomainPending(appliedSnapshot, domain);
|
|
340
|
+
pendingDomains.add(domain);
|
|
341
|
+
}
|
|
342
|
+
if (!pendingDomains.size) {
|
|
343
|
+
retryAttempt = 0;
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (retryTimer || disposed) return;
|
|
347
|
+
const delay = effectRetryDelaysMs[Math.min(retryAttempt, effectRetryDelaysMs.length - 1)];
|
|
348
|
+
retryTimer = setTimeout(() => {
|
|
349
|
+
retryTimer = null;
|
|
350
|
+
retryAttempt += 1;
|
|
351
|
+
try {
|
|
352
|
+
applyConfigChange();
|
|
353
|
+
} catch {
|
|
354
|
+
// The reload failed (e.g. a config mid-write): still pending, retry later.
|
|
355
|
+
settleConfigEffects([]);
|
|
356
|
+
}
|
|
357
|
+
}, delay);
|
|
358
|
+
retryTimer.unref?.();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// The output style this runtime selected but has not yet persisted wins over
|
|
362
|
+
// a status read from disk, so a concurrent write never shows the old style.
|
|
363
|
+
function getOutputStyleStatusCached(options) {
|
|
364
|
+
const status = outputStyleCache.getOutputStyleStatusCached(options);
|
|
365
|
+
const pending = writers.pendingOutputStyle();
|
|
366
|
+
if (pending === null || status?.configured === pending) return status;
|
|
367
|
+
const current = (status?.styles || []).find((style) => style.id === pending) || status?.current;
|
|
368
|
+
return { ...status, configured: pending, current };
|
|
369
|
+
}
|
|
370
|
+
|
|
239
371
|
function ensureConfigForRouteProvider() {
|
|
240
372
|
const config = getConfig();
|
|
241
373
|
const providerId = clean(getRoute().provider);
|
|
@@ -246,19 +378,22 @@ export function createConfigLifecycle({
|
|
|
246
378
|
return ensureFullConfig();
|
|
247
379
|
}
|
|
248
380
|
|
|
249
|
-
// Settings persisted by any other runtime (or process)
|
|
250
|
-
//
|
|
251
|
-
// shared layer coalesces bursts into one pass per
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
outputStyleCache.invalidateOutputStyleStatusCache();
|
|
255
|
-
onConfigReloaded?.();
|
|
256
|
-
});
|
|
381
|
+
// Settings and secrets persisted by this or any other runtime (or process)
|
|
382
|
+
// reach this runtime's config and effects. reloadFullConfig keeps our own
|
|
383
|
+
// pending edits, and the shared layer coalesces bursts into one pass per
|
|
384
|
+
// subscriber. An injected config never follows the disk.
|
|
385
|
+
const unsubscribeConfigChange = injectedConfig ? null : sharedCfgMod?.subscribeConfigChange?.(applyConfigChange);
|
|
257
386
|
|
|
258
387
|
return {
|
|
259
|
-
disposeConfigSync: () =>
|
|
388
|
+
disposeConfigSync: () => {
|
|
389
|
+
disposed = true;
|
|
390
|
+
clearTimeout(retryTimer);
|
|
391
|
+
retryTimer = null;
|
|
392
|
+
unsubscribeConfigChange?.();
|
|
393
|
+
},
|
|
260
394
|
// output-style cache
|
|
261
395
|
...outputStyleCache,
|
|
396
|
+
getOutputStyleStatusCached,
|
|
262
397
|
// adopt / save
|
|
263
398
|
adoptConfig,
|
|
264
399
|
saveConfigAndAdopt,
|
|
@@ -270,6 +405,7 @@ export function createConfigLifecycle({
|
|
|
270
405
|
flushAllConfigSavesAsync: writers.flushAllConfigSavesAsync,
|
|
271
406
|
// reload / ensure
|
|
272
407
|
reloadFullConfig,
|
|
408
|
+
applyConfigChange,
|
|
273
409
|
ensureFullConfig,
|
|
274
410
|
displayConfig: () => getConfig(),
|
|
275
411
|
ensureConfigForRouteProvider,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* codex $CODEX_HOME/sessions/**\/rollout-*.jsonl[.zst] (default ~/.codex)
|
|
5
5
|
* claude $CLAUDE_CONFIG_DIR/projects/*\/*.jsonl (default ~/.claude)
|
|
6
|
+
* cowork %APPDATA%\Claude\local-agent-mode-sessions (Claude Desktop; Windows only)
|
|
6
7
|
*
|
|
7
8
|
* Enumeration is stat-only; per-file metadata (foreign id, cwd, title, and
|
|
8
9
|
* the message count when the whole file fit the read) comes from a bounded
|
|
@@ -11,7 +12,7 @@
|
|
|
11
12
|
* shares one cache, one candidate registry and one set of listing views.
|
|
12
13
|
*/
|
|
13
14
|
import { createHash, randomUUID } from 'node:crypto';
|
|
14
|
-
import { open, readdir, realpath, stat } from 'node:fs/promises';
|
|
15
|
+
import { open, readdir, readFile, realpath, stat } from 'node:fs/promises';
|
|
15
16
|
import { homedir } from 'node:os';
|
|
16
17
|
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
17
18
|
import { pipeline } from 'node:stream';
|
|
@@ -77,6 +78,11 @@ const envDir = (value) => (typeof value === 'string' && value.trim() ? resolve(v
|
|
|
77
78
|
export function sourceRoot(source, env = process.env) {
|
|
78
79
|
if (source === 'codex') return join(envDir(env.CODEX_HOME) || join(homedir(), '.codex'), 'sessions');
|
|
79
80
|
if (source === 'claude') return join(envDir(env.CLAUDE_CONFIG_DIR) || join(homedir(), '.claude'), 'projects');
|
|
81
|
+
// Only Windows sets APPDATA; elsewhere Claude Desktop has no Cowork folder to search.
|
|
82
|
+
if (source === 'cowork') {
|
|
83
|
+
const appData = envDir(env.APPDATA);
|
|
84
|
+
return appData ? join(appData, 'Claude', 'local-agent-mode-sessions') : null;
|
|
85
|
+
}
|
|
80
86
|
throw importError('unsupported');
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -91,8 +97,8 @@ export function loadStoredTitles(source, env = process.env) {
|
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
/** The app's stored title of a conversation, else the one derived from its messages. */
|
|
94
|
-
export function displayTitle(meta, titles) {
|
|
95
|
-
return (meta?.foreignSessionId && titles?.get(meta.foreignSessionId)) || meta?.title || null;
|
|
100
|
+
export function displayTitle(meta, titles, manifestTitle = null) {
|
|
101
|
+
return manifestTitle || (meta?.foreignSessionId && titles?.get(meta.foreignSessionId)) || meta?.title || null;
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
const caseFold = (value) => (process.platform === 'win32' ? value.toLowerCase() : value);
|
|
@@ -215,6 +221,68 @@ async function walkClaude(rootReal, files) {
|
|
|
215
221
|
}
|
|
216
222
|
}
|
|
217
223
|
|
|
224
|
+
// Cowork keeps one `local_<id>.json` manifest per conversation (the account/org folders hold
|
|
225
|
+
// `local_*.json` directly, the agent variant under `agent/`), next to a `local_<id>/` folder whose
|
|
226
|
+
// `.claude/projects/<project>/<cliSessionId>.jsonl` is the Claude Code transcript. The manifest also
|
|
227
|
+
// holds the system prompt and account details, so only title / isStarred / cliSessionId are read.
|
|
228
|
+
// The agent variant (`local_ditto_*`, sessionType "agent") is the user's own Claude desktop chat
|
|
229
|
+
// (its initialMessage is user-typed), so it is listed like the others.
|
|
230
|
+
const COWORK_MANIFEST_MAX_BYTES = 8 * 1024 * 1024;
|
|
231
|
+
const COWORK_TITLE_MAX_CHARS = 200;
|
|
232
|
+
const COWORK_SESSION_ID_RE = /^[\w-]+$/;
|
|
233
|
+
|
|
234
|
+
async function coworkManifestPaths(rootReal) {
|
|
235
|
+
const manifests = [];
|
|
236
|
+
for (const account of await entriesOf(rootReal)) {
|
|
237
|
+
if (!account.isDirectory()) continue;
|
|
238
|
+
for (const org of await entriesOf(join(rootReal, account.name))) {
|
|
239
|
+
if (!org.isDirectory()) continue;
|
|
240
|
+
for (const dir of [join(rootReal, account.name, org.name), join(rootReal, account.name, org.name, 'agent')]) {
|
|
241
|
+
for (const file of await entriesOf(dir)) {
|
|
242
|
+
if (file.isFile() && /^local_.+\.json$/.test(file.name)) manifests.push(join(dir, file.name));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return manifests;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// The transcript a manifest names, with the manifest's title and star; null when it has none.
|
|
251
|
+
async function coworkTranscript(rootReal, manifestPath) {
|
|
252
|
+
const real = await containedRealpath(rootReal, manifestPath);
|
|
253
|
+
const info = real ? await stat(real).catch(() => null) : null;
|
|
254
|
+
if (!info?.isFile() || info.size > COWORK_MANIFEST_MAX_BYTES) return null;
|
|
255
|
+
let manifest;
|
|
256
|
+
try {
|
|
257
|
+
manifest = JSON.parse(await readFile(real, 'utf8'));
|
|
258
|
+
} catch {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const cliSessionId = manifest?.cliSessionId;
|
|
262
|
+
if (typeof cliSessionId !== 'string' || !COWORK_SESSION_ID_RE.test(cliSessionId)) return null;
|
|
263
|
+
const projects = join(real.slice(0, -'.json'.length), '.claude', 'projects');
|
|
264
|
+
for (const project of await entriesOf(projects)) {
|
|
265
|
+
if (!project.isDirectory()) continue;
|
|
266
|
+
const path = join(projects, project.name, `${cliSessionId}.jsonl`);
|
|
267
|
+
if (await containedRealpath(rootReal, path)) {
|
|
268
|
+
const title = typeof manifest.title === 'string' ? manifest.title.trim().slice(0, COWORK_TITLE_MAX_CHARS) : '';
|
|
269
|
+
return { path, title: title || null, starred: manifest.isStarred === true };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function walkCowork(rootReal, files, extras) {
|
|
276
|
+
const found = await mapBounded(await coworkManifestPaths(rootReal), STAT_CONCURRENCY, (path) =>
|
|
277
|
+
coworkTranscript(rootReal, path)
|
|
278
|
+
);
|
|
279
|
+
for (const entry of found) {
|
|
280
|
+
if (!entry) continue;
|
|
281
|
+
files.push(entry.path);
|
|
282
|
+
extras.set(entry.path, { title: entry.title, starred: entry.starred });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
218
286
|
async function mapBounded(items, limit, fn) {
|
|
219
287
|
const out = new Array(items.length);
|
|
220
288
|
let next = 0;
|
|
@@ -231,6 +299,7 @@ async function mapBounded(items, limit, fn) {
|
|
|
231
299
|
/** Regular files under the source root, canonical and contained, as candidates. */
|
|
232
300
|
export async function enumerateCandidates(source, env = process.env) {
|
|
233
301
|
const root = sourceRoot(source, env);
|
|
302
|
+
if (!root) return { root: '', candidates: [] };
|
|
234
303
|
let rootReal;
|
|
235
304
|
try {
|
|
236
305
|
rootReal = await realpath(root);
|
|
@@ -238,13 +307,17 @@ export async function enumerateCandidates(source, env = process.env) {
|
|
|
238
307
|
return { root, candidates: [] };
|
|
239
308
|
}
|
|
240
309
|
const paths = [];
|
|
310
|
+
const extras = new Map(); // transcript path → manifest title / star (Cowork)
|
|
241
311
|
if (source === 'codex') await walkCodex(rootReal, rootReal, paths, new Set([caseFold(rootReal)]));
|
|
312
|
+
else if (source === 'cowork') await walkCowork(rootReal, paths, extras);
|
|
242
313
|
else await walkClaude(rootReal, paths);
|
|
243
314
|
const stamped = await mapBounded(paths, STAT_CONCURRENCY, async (path) => {
|
|
244
315
|
const real = await containedRealpath(rootReal, path);
|
|
245
316
|
if (!real) return null;
|
|
246
317
|
const info = await stat(real).catch(() => null);
|
|
247
|
-
return info?.isFile()
|
|
318
|
+
return info?.isFile()
|
|
319
|
+
? { path: real, size: info.size, mtimeMs: info.mtimeMs, fileId: fileIdentity(info), ...extras.get(path) }
|
|
320
|
+
: null;
|
|
248
321
|
});
|
|
249
322
|
const unique = new Map();
|
|
250
323
|
for (const file of stamped) {
|
|
@@ -261,7 +334,9 @@ function compareCodeUnits(left, right) {
|
|
|
261
334
|
return left < right ? -1 : 1;
|
|
262
335
|
}
|
|
263
336
|
|
|
337
|
+
// Starred conversations (Cowork) come first, then newest first.
|
|
264
338
|
function byModifiedThenId(left, right) {
|
|
339
|
+
if (!left.starred !== !right.starred) return left.starred ? -1 : 1;
|
|
265
340
|
if (right.mtimeMs !== left.mtimeMs) return right.mtimeMs - left.mtimeMs;
|
|
266
341
|
return compareCodeUnits(left.id, right.id);
|
|
267
342
|
}
|
|
@@ -418,7 +493,7 @@ async function readPrefixMeta(candidate) {
|
|
|
418
493
|
// nothing but sidechain messages has been seen the scan streams on, line by
|
|
419
494
|
// line, to EOF or to the import file-size limit. A file over that limit stays
|
|
420
495
|
// unclassified (listed); importing it fails as tooLarge.
|
|
421
|
-
const claude = candidate.source === 'claude';
|
|
496
|
+
const claude = candidate.source === 'claude' || candidate.source === 'cowork';
|
|
422
497
|
let read;
|
|
423
498
|
try {
|
|
424
499
|
read = await streamJsonlLines(handle, (line) => converter.pushLine(line), {
|
|
@@ -793,7 +868,8 @@ function rowFor(candidate, meta, cwdInfo, imported, titles) {
|
|
|
793
868
|
return {
|
|
794
869
|
id: candidate.id,
|
|
795
870
|
source: candidate.source,
|
|
796
|
-
title: displayTitle(meta, titles),
|
|
871
|
+
title: displayTitle(meta, titles, candidate.title),
|
|
872
|
+
starred: candidate.starred === true,
|
|
797
873
|
cwd: cwdInfo?.path ?? null,
|
|
798
874
|
modifiedAt: candidate.mtimeMs,
|
|
799
875
|
messageCount: meta?.messageCount ?? null,
|
|
@@ -946,7 +1022,10 @@ async function filterCandidates(candidates, filters, imported, titles) {
|
|
|
946
1022
|
const cwdInfo = meta.cwd ? await resolveSourceCwd(meta.cwd, cwdCache) : null;
|
|
947
1023
|
if (
|
|
948
1024
|
needle &&
|
|
949
|
-
!(
|
|
1025
|
+
!(
|
|
1026
|
+
foldText(displayTitle(meta, titles, candidate.title)).includes(needle) ||
|
|
1027
|
+
foldText(cwdInfo?.path).includes(needle)
|
|
1028
|
+
)
|
|
950
1029
|
) {
|
|
951
1030
|
continue;
|
|
952
1031
|
}
|
|
@@ -143,7 +143,7 @@ const titlesFor = (candidate, context) => (candidate.source === 'codex' ? contex
|
|
|
143
143
|
function describeCandidate(candidateId, context) {
|
|
144
144
|
const candidate = getCandidate(candidateId);
|
|
145
145
|
return {
|
|
146
|
-
title: candidate ? displayTitle(cachedMeta(candidate), titlesFor(candidate, context)) : null,
|
|
146
|
+
title: candidate ? displayTitle(cachedMeta(candidate), titlesFor(candidate, context), candidate.title) : null,
|
|
147
147
|
sourceName: candidate ? basename(candidate.path) : null,
|
|
148
148
|
};
|
|
149
149
|
}
|
|
@@ -196,7 +196,8 @@ async function importOne(candidateId, context) {
|
|
|
196
196
|
}
|
|
197
197
|
const title = displayTitle(
|
|
198
198
|
{ foreignSessionId: conversion.foreignSessionId, title: listedTitle || conversion.title },
|
|
199
|
-
titlesFor(candidate, context)
|
|
199
|
+
titlesFor(candidate, context),
|
|
200
|
+
candidate.title
|
|
200
201
|
);
|
|
201
202
|
if (title) conversion = { ...conversion, title };
|
|
202
203
|
recordConversion(candidate, conversion);
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { markPendingGoalReminder } from '../runtime/agent/orchestrator/runtime-core/goal-reminder.mjs';
|
|
2
|
+
import { adoptLegacyGoalTasks, todoContextBlock } from '../runtime/agent/orchestrator/runtime-core/todo-list.mjs';
|
|
3
|
+
import { SAVE_OUTCOME_SAVED } from '../runtime/agent/orchestrator/session/store/live-state.mjs';
|
|
2
4
|
|
|
3
|
-
export function createGoalFacadeApi({
|
|
5
|
+
export function createGoalFacadeApi({
|
|
6
|
+
agentStatusState,
|
|
7
|
+
createCurrentSession,
|
|
8
|
+
getSession,
|
|
9
|
+
getSessionId,
|
|
10
|
+
goalRuntime,
|
|
11
|
+
persistSession,
|
|
12
|
+
}) {
|
|
4
13
|
const markGoalReminder = (reason = '') => {
|
|
5
14
|
try {
|
|
6
15
|
const pending = markPendingGoalReminder(getSession(), reason);
|
|
@@ -14,10 +23,39 @@ export function createGoalFacadeApi({ agentStatusState, createCurrentSession, ge
|
|
|
14
23
|
return null;
|
|
15
24
|
}
|
|
16
25
|
};
|
|
26
|
+
// A Goal record written before the todo list still carries its tasks: the
|
|
27
|
+
// session's list adopts them, and only once that session is saved does the
|
|
28
|
+
// record let them go. A failed save keeps them on the record, so the next
|
|
29
|
+
// load adopts again (items already listed are skipped, never duplicated).
|
|
30
|
+
const adopting = new Map();
|
|
31
|
+
const adoptLegacyTasks = (sessionId) => {
|
|
32
|
+
if (adopting.has(sessionId)) return adopting.get(sessionId);
|
|
33
|
+
const tasks = goalRuntime.legacyTasks?.(sessionId) || [];
|
|
34
|
+
if (!tasks.length) return null;
|
|
35
|
+
const session = getSession();
|
|
36
|
+
if (session?.id !== sessionId) return null;
|
|
37
|
+
adoptLegacyGoalTasks(session, tasks);
|
|
38
|
+
const adoption = (async () => {
|
|
39
|
+
try {
|
|
40
|
+
// Only an explicit committed write lets the record go: a dropped or
|
|
41
|
+
// stale save resolves too, without the list on disk.
|
|
42
|
+
if ((await persistSession(session)) !== SAVE_OUTCOME_SAVED) return;
|
|
43
|
+
await goalRuntime.clearLegacyTasks(sessionId);
|
|
44
|
+
} catch {
|
|
45
|
+
// Kept on the record; retried on the next load.
|
|
46
|
+
} finally {
|
|
47
|
+
adopting.delete(sessionId);
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
50
|
+
adopting.set(sessionId, adoption);
|
|
51
|
+
return adoption;
|
|
52
|
+
};
|
|
17
53
|
return {
|
|
18
54
|
goalStatus() {
|
|
19
55
|
const sessionId = getSessionId();
|
|
20
|
-
|
|
56
|
+
if (!sessionId) return null;
|
|
57
|
+
adoptLegacyTasks(sessionId);
|
|
58
|
+
return goalRuntime.snapshot(sessionId);
|
|
21
59
|
},
|
|
22
60
|
async goalControl(args = {}) {
|
|
23
61
|
let sessionId = getSessionId();
|
|
@@ -31,10 +69,18 @@ export function createGoalFacadeApi({ agentStatusState, createCurrentSession, ge
|
|
|
31
69
|
return result;
|
|
32
70
|
},
|
|
33
71
|
markGoalReminder,
|
|
72
|
+
/** Settles once a legacy Goal record's tasks are adopted and saved (or kept after a failed save). */
|
|
73
|
+
adoptLegacyGoalTasks(sessionId = getSessionId()) {
|
|
74
|
+
return Promise.resolve(sessionId ? adoptLegacyTasks(sessionId) : null);
|
|
75
|
+
},
|
|
34
76
|
goalContinuation() {
|
|
35
77
|
const sessionId = getSessionId();
|
|
36
78
|
if (!sessionId) return { run: false, reason: 'missing-session', goal: null };
|
|
37
|
-
|
|
79
|
+
adoptLegacyTasks(sessionId);
|
|
80
|
+
return goalRuntime.continuation(sessionId, {
|
|
81
|
+
agentStatus: agentStatusState(),
|
|
82
|
+
todo: todoContextBlock(getSession(), 'Todo list for this Goal (exact wordings):', { whole: true }),
|
|
83
|
+
});
|
|
38
84
|
},
|
|
39
85
|
goalTurnStarted() {
|
|
40
86
|
const sessionId = getSessionId();
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ASK_USER_MAX_TEXT,
|
|
9
9
|
} from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
|
|
10
10
|
import { createCallerContextResolvers } from './caller-context.mjs';
|
|
11
|
+
import { noteTodoUserWait } from '../../runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs';
|
|
11
12
|
import { runAbortable } from '../../runtime/shared/abort-race.mjs';
|
|
12
13
|
import {
|
|
13
14
|
HANDOFF_OUTCOMES as AGENT_OUTCOMES,
|
|
@@ -309,6 +310,8 @@ export function createAskUserToolHandlers({ rt }) {
|
|
|
309
310
|
normalizeAskUserAnswers(result.answers, questions),
|
|
310
311
|
normalizeAskUserReply(result.reply)
|
|
311
312
|
);
|
|
313
|
+
// A question left unanswered: the turn may end waiting on the user.
|
|
314
|
+
noteTodoUserWait(sessionIdFor(callerCtx));
|
|
312
315
|
return DISMISSED;
|
|
313
316
|
},
|
|
314
317
|
};
|
|
@@ -68,8 +68,9 @@ export function createBridgeToolHandlers({ rt }) {
|
|
|
68
68
|
if (environmentDisabled(callerCtx, 'MIXDOG_FEATURE_VISUALIZE')) {
|
|
69
69
|
throw new Error('the visualize tool is disabled in this environment');
|
|
70
70
|
}
|
|
71
|
-
//
|
|
72
|
-
//
|
|
71
|
+
// The Settings toggle at execution time too: it reaches a turn already
|
|
72
|
+
// running and callers outside the session's deny set (an env override
|
|
73
|
+
// wins, as in builtinFeatureActive).
|
|
73
74
|
if (!(featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ?? moduleEnabled(rt.config, 'visualize', true))) {
|
|
74
75
|
return {
|
|
75
76
|
content: [
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
// Optional feature tools
|
|
2
|
-
//
|
|
1
|
+
// Optional feature tools: office, media and tidy (each loaded on first use;
|
|
2
|
+
// their settings gates apply in the executor), and the setup tool.
|
|
3
3
|
import { standaloneDataDir } from '../runtime-paths.mjs';
|
|
4
4
|
import { createCallerContextResolvers } from './caller-context.mjs';
|
|
5
5
|
|
|
6
|
-
export function createFeatureToolHandlers({ rt, setupTool
|
|
6
|
+
export function createFeatureToolHandlers({ rt, setupTool }) {
|
|
7
7
|
const { sessionIdFor, signalFor } = createCallerContextResolvers(rt);
|
|
8
|
-
const requireEnabled = (callerCtx, enabled, label) => {
|
|
9
|
-
if (callerCtx?.invocationSource === 'model-tool' && !enabled()) {
|
|
10
|
-
throw new Error(`${label} is disabled in settings; start a new session to refresh the tool list`);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
8
|
|
|
14
9
|
return {
|
|
15
10
|
office: async (args, { callerCtx, callerCwd }) => {
|
|
16
|
-
requireEnabled(callerCtx, officeToolsEnabled, 'office');
|
|
17
11
|
const { executeOfficeTool } = await import('../../runtime/office/index.mjs');
|
|
18
12
|
return await executeOfficeTool(args, {
|
|
19
13
|
cwd: callerCwd,
|
|
@@ -25,7 +19,6 @@ export function createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, m
|
|
|
25
19
|
});
|
|
26
20
|
},
|
|
27
21
|
media: async (args, { callerCtx, callerCwd }) => {
|
|
28
|
-
requireEnabled(callerCtx, mediaToolEnabled, 'media');
|
|
29
22
|
const { executeMediaTool } = await import('../../runtime/media/tool.mjs');
|
|
30
23
|
const { getSession } = await import('../../runtime/agent/orchestrator/session/manager/session-crud.mjs');
|
|
31
24
|
// Generation spend is attributed to the calling session in the usage ledger.
|
|
@@ -38,7 +31,6 @@ export function createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, m
|
|
|
38
31
|
});
|
|
39
32
|
},
|
|
40
33
|
tidy: async (args, { callerCtx, callerCwd }) => {
|
|
41
|
-
requireEnabled(callerCtx, tidyToolEnabled, 'tidy');
|
|
42
34
|
const { executeTidyTool } = await import('../../runtime/tidy/tool.mjs');
|
|
43
35
|
return await executeTidyTool(args, {
|
|
44
36
|
cwd: callerCwd,
|
|
@@ -17,6 +17,7 @@ import { scheduleCallerScope } from '../services/schedule-access.mjs';
|
|
|
17
17
|
import { worktreeCallerScope } from '../services/worktree-access.mjs';
|
|
18
18
|
import { resolveHandoffAttachments, sessionRoots } from '../handoff-attachments.mjs';
|
|
19
19
|
import { delegationChainOf } from '../bot-chain.mjs';
|
|
20
|
+
import { openTodoItems } from '../../runtime/agent/orchestrator/runtime-core/todo-list.mjs';
|
|
20
21
|
import { BOT_TARGET_ACTIONS, resolveBotTarget } from '../bot-target.mjs';
|
|
21
22
|
|
|
22
23
|
const USER_TITLE_ERROR = 'session: the user already titled this session; the title was not changed';
|
|
@@ -322,9 +323,21 @@ export function createWorkspaceToolHandlers({
|
|
|
322
323
|
goal: async (args, { name, callerCtx }) => {
|
|
323
324
|
// A Goal keeps working in its session across turns: a heartbeat's would
|
|
324
325
|
// run on unseen in the hidden session.
|
|
325
|
-
|
|
326
|
+
const action = clean(args?.action).toLowerCase();
|
|
327
|
+
if (action === 'create' && callerIsHeartbeat(callerCtx)) {
|
|
326
328
|
throw new Error(`goal: ${HEARTBEAT_NO_HANDOFF}`);
|
|
327
329
|
}
|
|
330
|
+
// A completion claim with steps still open in the session's own list
|
|
331
|
+
// has not finished the work it recorded.
|
|
332
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
333
|
+
if (action === 'complete' && rt.session?.id === callerSessionId) {
|
|
334
|
+
const open = openTodoItems(rt.session);
|
|
335
|
+
if (open.length) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`goal: cannot complete with ${open.length} open todo item(s) (${open.map((task) => task.content).join('; ')}); finish them, or drop or block each with its reason`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
328
341
|
return await goalRuntime.executeTool(name, args || {}, {
|
|
329
342
|
callerSessionId: callerCtx?.callerSessionId || rt.session?.id || rt.reservedSessionId || null,
|
|
330
343
|
});
|
|
@@ -335,9 +348,10 @@ export function createWorkspaceToolHandlers({
|
|
|
335
348
|
if (['spawn', 'send'].includes((clean(args?.type) || 'spawn').toLowerCase()) && callerIsHeartbeat(callerCtx)) {
|
|
336
349
|
throw new Error(`agent: ${HEARTBEAT_NO_HANDOFF}`);
|
|
337
350
|
}
|
|
338
|
-
// `attachmentParts`
|
|
339
|
-
// `attachments` reach the agent's turn
|
|
340
|
-
|
|
351
|
+
// `attachmentParts` and `skillSelection` are never the model's to set:
|
|
352
|
+
// only the resolved `attachments` reach the agent's turn, and its skills
|
|
353
|
+
// are selected from its whole prompt.
|
|
354
|
+
const { attachments, attachmentParts: _ignored, skillSelection: _selection, ...rest } = args || {};
|
|
341
355
|
const attachmentParts = callerAttachmentParts(rt, { attachments }, callerCwd, 'agent');
|
|
342
356
|
return await agentTool.execute(attachmentParts.length ? { ...rest, attachmentParts } : rest, {
|
|
343
357
|
callerCwd,
|