mixdog 1.3.4 → 1.4.0
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/LICENSES/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +17 -2
- package/README.md +11 -0
- package/package.json +3 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/gate-local.mjs +4 -2
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test.mjs +12 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +25 -1
- package/src/defaults/skills/browser-use/SKILL.md +77 -17
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +33 -9
- package/src/defaults/skills/setup/SKILL.md +29 -2
- package/src/defaults/skills/setup/references/actions.md +117 -28
- package/src/defaults/skills/setup/references/surfaces.md +77 -35
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/rules/lead/LEAD.md +18 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/config.mjs +34 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
- package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +135 -154
- package/src/runtime/channels/lib/schedules-db.mjs +83 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +19 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-hops.mjs +62 -2
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +51 -35
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +33 -2
- package/src/runtime/shared/connector-catalog.mjs +166 -8
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -14
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +231 -59
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +52 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +34 -1
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -14
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/runtime/shared/worktrees/test-support.mjs +9 -4
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +27 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +486 -179
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +561 -199
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/begin.mjs +3 -2
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +8 -1
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +927 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +35 -13
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/routine-home-turn.mjs +33 -6
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +73 -31
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +8 -4
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +28 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +187 -29
- package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +161 -157
- package/src/standalone/daemon.mjs +33 -2
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +10 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +21 -10
- package/src/tui/app/model-options.mjs +4 -27
- package/src/tui/dist/index.mjs +81 -65
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +23 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +30 -2
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +36 -3
- package/src/tui/session/turn.mjs +23 -2
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -7,8 +7,13 @@
|
|
|
7
7
|
* Only turns the origin agent starts (the brief, a later `send` from it)
|
|
8
8
|
* report: when such a turn ends its result is queued durably for the agent's
|
|
9
9
|
* home (agent-home-delivery.mjs), which posts it and wakes the agent once to
|
|
10
|
-
* relay it.
|
|
11
|
-
*
|
|
10
|
+
* relay it. The session's other answers in that chain (given while work it
|
|
11
|
+
* handed to its agents still runs) are queued as progress updates that wake
|
|
12
|
+
* nobody: the agent reads them before its next turn, whatever starts it
|
|
13
|
+
* (agent-home-delivery.mjs postBeforeTurn), and the final report never
|
|
14
|
+
* repeats one that already carried its answer. A turn blocked on the user's
|
|
15
|
+
* answer or approval posts one faint line instead; the card stays in the task
|
|
16
|
+
* session for the user.
|
|
12
17
|
*
|
|
13
18
|
* An agent's send to any other session reports the same way, by an origin for
|
|
14
19
|
* that send only (reportOrigin). Every reporting turn is also watched durably
|
|
@@ -33,10 +38,12 @@ import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.m
|
|
|
33
38
|
import { collectTurnArtifacts, handoffContent, sessionRoots, turnSlice } from './handoff-attachments.mjs';
|
|
34
39
|
import { roomMemberOfSession } from '../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
35
40
|
import { wrapAgentMessage } from '../runtime/shared/agent-message-envelope.mjs';
|
|
36
|
-
import { sessionsWithPendingWork } from '../runtime/shared/pending-agent-work.mjs';
|
|
41
|
+
import { hasPendingAgentWork, sessionsWithPendingWork } from '../runtime/shared/pending-agent-work.mjs';
|
|
37
42
|
import { agentTaskIdOfExecution } from './services/agent-tool/restart-notify.mjs';
|
|
43
|
+
import { botChainOf } from '../runtime/shared/bot-hops.mjs';
|
|
38
44
|
import { worktreeError, worktreeNameError } from '../runtime/shared/worktrees/contract.mjs';
|
|
39
45
|
import { samePath } from '../runtime/shared/worktrees/paths.mjs';
|
|
46
|
+
import { readModelCatalog, routeByDisplayName, withEffort } from './task-session-route.mjs';
|
|
40
47
|
import { assertWorktreeMutationAllowed } from './services/worktree-access.mjs';
|
|
41
48
|
|
|
42
49
|
const TITLE_LIMIT = 60;
|
|
@@ -77,12 +84,15 @@ const turnStatus = (detail) =>
|
|
|
77
84
|
|
|
78
85
|
const INTERRUPTED = /^\[Request interrupted/;
|
|
79
86
|
|
|
80
|
-
const
|
|
81
|
-
typeof
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
const partText = (part) => {
|
|
88
|
+
if (typeof part === 'string') return part;
|
|
89
|
+
return part?.type === 'text' ? part.text || '' : '';
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const messageText = (content) => {
|
|
93
|
+
if (typeof content === 'string') return content;
|
|
94
|
+
return (Array.isArray(content) ? content : []).map(partText).join('');
|
|
95
|
+
};
|
|
86
96
|
|
|
87
97
|
/** The submissions a stored user message delivered: a turn's prompt
|
|
88
98
|
* (`checkpointId`, `submissionIds` of a merged batch) or a steered message. */
|
|
@@ -104,13 +114,21 @@ export function submissionIdsOf(message) {
|
|
|
104
114
|
export function submissionOutcome(messages, runId) {
|
|
105
115
|
const list = Array.isArray(messages) ? messages : [];
|
|
106
116
|
const start = list.findIndex((message) => submissionIdsOf(message).includes(runId));
|
|
107
|
-
|
|
117
|
+
return start < 0 ? null : outcomeFrom(list, start);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** submissionOutcome of the turn whose prompt is `list[start]`, read in
|
|
121
|
+
* place: the scan stops at that turn's end (its completion, an
|
|
122
|
+
* interruption, or the next turn's prompt). */
|
|
123
|
+
function outcomeFrom(list, start) {
|
|
108
124
|
for (let index = start; index < list.length; index += 1) {
|
|
109
125
|
const message = list[index];
|
|
110
126
|
const completion = message?.meta?.transcript?.completion;
|
|
111
127
|
if (completion) {
|
|
112
128
|
const status = turnStatus(completion);
|
|
113
|
-
|
|
129
|
+
let text = '';
|
|
130
|
+
if (status === 'failed') text = completion.detail || '';
|
|
131
|
+
else if (message.role === 'assistant') text = message.content;
|
|
114
132
|
return { status, text: messageText(text) };
|
|
115
133
|
}
|
|
116
134
|
if (index === start || message?.role !== 'user') continue;
|
|
@@ -140,7 +158,9 @@ const callArguments = (call) => {
|
|
|
140
158
|
* no task. Empty when the turn recorded none. */
|
|
141
159
|
function consumedTaskIds(message) {
|
|
142
160
|
const recorded = message?.meta?.transcript?.consumedExecutionIds;
|
|
143
|
-
|
|
161
|
+
let ids = [];
|
|
162
|
+
if (Array.isArray(recorded)) ids = recorded;
|
|
163
|
+
else if (message?.meta?.execution?.id) ids = [message.meta.execution.id];
|
|
144
164
|
return ids.map((id) => (id ? agentTaskIdOfExecution(id) : null));
|
|
145
165
|
}
|
|
146
166
|
|
|
@@ -158,10 +178,27 @@ function consumedTaskIds(message) {
|
|
|
158
178
|
* submissionOutcome and when its end was recorded (0 when unknown); null
|
|
159
179
|
* when no turn took runId. */
|
|
160
180
|
export function latestTurnOutcome(messages, runId) {
|
|
181
|
+
const chain = turnChain(messages, runId);
|
|
182
|
+
return chain && { id: chain.id, outcome: chain.outcome, at: chain.at, ambiguous: chain.ambiguous };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** latestTurnOutcome with what a report's wait needs besides: `answers`,
|
|
186
|
+
* every turn of the chain that recorded its end, oldest first (`{ id,
|
|
187
|
+
* status, text }`: its submission id and submissionOutcome); `taskIds`, the
|
|
188
|
+
* agent tasks the chain's turns started — the only tasks proven the chain's
|
|
189
|
+
* — and `busyIsOthers`: the session's last stored turn is one outside the
|
|
190
|
+
* chain that recorded no end yet, and a stored turn took the result of every
|
|
191
|
+
* one of `taskIds` (only those make a turn the chain's) — a busy session
|
|
192
|
+
* then runs that other turn, and no result of the chain can be on its way
|
|
193
|
+
* into a turn (woken by it, or steered into the running one) whose prompt or
|
|
194
|
+
* steer is not stored yet. */
|
|
195
|
+
function turnChain(messages, runId) {
|
|
161
196
|
const list = Array.isArray(messages) ? messages : [];
|
|
162
197
|
const start = list.findIndex((message) => submissionIdsOf(message).includes(runId));
|
|
163
198
|
if (start < 0) return null;
|
|
164
199
|
const started = new Set();
|
|
200
|
+
// The agent tasks whose results a stored turn took (as its prompt or steered in).
|
|
201
|
+
const taken = new Set();
|
|
165
202
|
const startingCalls = new Set();
|
|
166
203
|
let at = start;
|
|
167
204
|
let id = runId;
|
|
@@ -170,6 +207,8 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
170
207
|
// The turn the walk is in: where its prompt is stored and its submission id.
|
|
171
208
|
let turnAt = start;
|
|
172
209
|
let turnId = runId;
|
|
210
|
+
// Every turn of the chain, in order: where its prompt is stored, its id.
|
|
211
|
+
const turns = [{ at: start, id: runId }];
|
|
173
212
|
for (let index = start; index < list.length; index += 1) {
|
|
174
213
|
const message = list[index];
|
|
175
214
|
const checkpointId = index > start && message?.role === 'user' ? message.meta?.transcript?.checkpointId : '';
|
|
@@ -186,11 +225,13 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
186
225
|
index > start && message?.role === 'user' && message.meta?.source === 'task-notification'
|
|
187
226
|
? consumedTaskIds(message)
|
|
188
227
|
: null;
|
|
228
|
+
for (const taskId of delivered || []) if (taskId) taken.add(taskId);
|
|
189
229
|
if (delivered && !inChain) {
|
|
190
230
|
if (delivered.some((taskId) => taskId && started.has(taskId))) {
|
|
191
231
|
inChain = true;
|
|
192
232
|
at = turnAt;
|
|
193
233
|
id = turnId;
|
|
234
|
+
if (turns.at(-1).id !== turnId) turns.push({ at: turnAt, id: turnId });
|
|
194
235
|
} else if (!delivered.length || delivered.includes(null)) {
|
|
195
236
|
ambiguous = true;
|
|
196
237
|
}
|
|
@@ -209,9 +250,47 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
209
250
|
if (Array.isArray(message.agentTaskIds)) for (const taskId of message.agentTaskIds) started.add(String(taskId));
|
|
210
251
|
else if (message.toolKind !== 'error') ambiguous = true;
|
|
211
252
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
253
|
+
// Read in place, never on copied suffixes: each chain turn's outcome scan
|
|
254
|
+
// stops at that turn's end, so the turns' scans cover disjoint stretches
|
|
255
|
+
// and the whole walk stays linear in the transcript.
|
|
256
|
+
let ended = null;
|
|
257
|
+
for (let index = at; index < list.length && !ended; index += 1) {
|
|
258
|
+
if (list[index]?.meta?.transcript?.completion) ended = list[index];
|
|
259
|
+
}
|
|
260
|
+
const last = turnId === id ? null : outcomeFrom(list, turnAt);
|
|
261
|
+
const answers = [];
|
|
262
|
+
let outcome = null;
|
|
263
|
+
for (const chainTurn of turns) {
|
|
264
|
+
const turnOutcome = outcomeFrom(list, chainTurn.at);
|
|
265
|
+
if (chainTurn.at === at) outcome = turnOutcome;
|
|
266
|
+
if (turnOutcome !== 'open') answers.push({ id: chainTurn.id, ...turnOutcome });
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
id,
|
|
270
|
+
outcome,
|
|
271
|
+
at: Number(ended?.meta?.transcript?.at) || 0,
|
|
272
|
+
ambiguous,
|
|
273
|
+
answers,
|
|
274
|
+
taskIds: started,
|
|
275
|
+
busyIsOthers: last === 'open' && [...started].every((taskId) => taken.has(taskId)),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** The agent task an owed result reports; null when it names none. */
|
|
280
|
+
function owedTaskId(result) {
|
|
281
|
+
const executionId = result?.meta?.execution_id;
|
|
282
|
+
return executionId ? agentTaskIdOfExecution(executionId) : null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** The `owed` results that may be the chain's of agent tasks `taskIds`: its
|
|
286
|
+
* tasks' and those naming no task (their provenance is missing); every one
|
|
287
|
+
* for a null `taskIds` (the whole session). */
|
|
288
|
+
function owedOf(owed, taskIds) {
|
|
289
|
+
if (!taskIds) return owed;
|
|
290
|
+
return owed.filter((result) => {
|
|
291
|
+
const taskId = owedTaskId(result);
|
|
292
|
+
return !taskId || taskIds.has(taskId);
|
|
293
|
+
});
|
|
215
294
|
}
|
|
216
295
|
|
|
217
296
|
/** Whether task session `sessionId` still waits on agent work it handed off,
|
|
@@ -221,20 +300,76 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
221
300
|
* `agentTaskOwners` (the sessions owning a queued, spawning or running agent
|
|
222
301
|
* task — one still waiting for admission has no turn in flight, and its pool
|
|
223
302
|
* row reads `unknown`). An agent task owned by an Agent session under it, at
|
|
224
|
-
* any depth, counts too: every descendant's pool row names the Lead as owner.
|
|
303
|
+
* any depth, counts too: every descendant's pool row names the Lead as owner.
|
|
304
|
+
* `taskIds` narrows it to one chain's agent tasks (chainPendingWork). */
|
|
225
305
|
export function pendingWorkOf(
|
|
226
306
|
sessionId,
|
|
227
|
-
{
|
|
307
|
+
{
|
|
308
|
+
agentWorking = new Set(),
|
|
309
|
+
owed = [],
|
|
310
|
+
pool = [],
|
|
311
|
+
agentTaskOwners = new Set(),
|
|
312
|
+
taskIds = null,
|
|
313
|
+
working = new Set(),
|
|
314
|
+
agentTasks = [],
|
|
315
|
+
}
|
|
228
316
|
) {
|
|
317
|
+
if (taskIds) return chainPendingWork(sessionId, taskIds, { owed, pool, agentTaskOwners, working, agentTasks });
|
|
229
318
|
if (agentWorking.has(sessionId) || owed.length > 0 || agentTaskOwners.has(sessionId)) return true;
|
|
230
319
|
if (sessionsWithPendingWork(pool).has(sessionId)) return true;
|
|
231
320
|
return pool.some((row) => row?.ownerSessionId === sessionId && agentTaskOwners.has(String(row.sessionId || '')));
|
|
232
321
|
}
|
|
233
322
|
|
|
323
|
+
/** The agent work of the chain of Lead `sessionId` whose own agent tasks are
|
|
324
|
+
* `taskIds`. `taskIds`: those tasks alone, the only ones proven the chain's
|
|
325
|
+
* (no record says which task started a child agent). `sessions`: the Agent
|
|
326
|
+
* sessions those tasks run in (`agentTasks` `sessionId`, a pool row's
|
|
327
|
+
* `taskId`) and every Agent session under one (pool `parentSessionId`),
|
|
328
|
+
* never the Lead — work there may be the chain's, so it only holds the
|
|
329
|
+
* report (it may be another's: a session the chain's agent ran in can be
|
|
330
|
+
* sent another task, which can spawn children of its own). */
|
|
331
|
+
export function chainClosure(sessionId, taskIds, { pool = [], agentTasks = [] } = {}) {
|
|
332
|
+
const owned = new Set(taskIds);
|
|
333
|
+
const sessions = new Set();
|
|
334
|
+
const join = (id) => {
|
|
335
|
+
if (!id || id === sessionId || sessions.has(id)) return false;
|
|
336
|
+
sessions.add(id);
|
|
337
|
+
return true;
|
|
338
|
+
};
|
|
339
|
+
for (const task of agentTasks) if (owned.has(task.taskId)) join(task.sessionId);
|
|
340
|
+
for (const row of pool) if (owned.has(String(row?.taskId || ''))) join(String(row.sessionId || ''));
|
|
341
|
+
for (let grown = true; grown; ) {
|
|
342
|
+
grown = false;
|
|
343
|
+
for (const row of pool) {
|
|
344
|
+
if (sessions.has(String(row?.parentSessionId || '')) && join(String(row.sessionId || ''))) grown = true;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return { taskIds: owned, sessions };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** pendingWorkOf of the chain of agent tasks `taskIds` alone, with all its
|
|
351
|
+
* work (chainClosure): a task it owns or one of its Agent sessions runs
|
|
352
|
+
* (`agentTasks`: `{ taskId, sessionId, running }` of this process's agent
|
|
353
|
+
* tasks; a pool row's `taskId`) still queued or running, or its result owed
|
|
354
|
+
* (or an owed result naming no task), or one of those sessions with a turn
|
|
355
|
+
* in flight (`working`), owning a queued or running agent task, or pending
|
|
356
|
+
* on its pool row. Other work of the session never counts. */
|
|
357
|
+
function chainPendingWork(sessionId, taskIds, { owed, pool, agentTaskOwners, working, agentTasks }) {
|
|
358
|
+
const chain = chainClosure(sessionId, taskIds, { pool, agentTasks });
|
|
359
|
+
const sessionOf = new Map();
|
|
360
|
+
for (const row of pool) if (row?.taskId) sessionOf.set(String(row.taskId), String(row.sessionId || ''));
|
|
361
|
+
for (const task of agentTasks) if (task.sessionId) sessionOf.set(task.taskId, task.sessionId);
|
|
362
|
+
const ofChain = (taskId) => chain.taskIds.has(taskId) || chain.sessions.has(sessionOf.get(taskId));
|
|
363
|
+
if (owed.some((result) => !owedTaskId(result) || ofChain(owedTaskId(result)))) return true;
|
|
364
|
+
if (agentTasks.some((task) => task.running && ofChain(task.taskId))) return true;
|
|
365
|
+
for (const id of chain.sessions) if (working.has(id) || agentTaskOwners.has(id)) return true;
|
|
366
|
+
return pool.some((row) => chain.sessions.has(String(row?.sessionId || '')) && hasPendingAgentWork(row));
|
|
367
|
+
}
|
|
368
|
+
|
|
234
369
|
/** The agent results queued for `sessionId` that no turn has taken yet (its
|
|
235
370
|
* durable pending queue: an agent's completion, a restart failure): `{ text,
|
|
236
371
|
* meta }` as the notification bus delivers them. */
|
|
237
|
-
|
|
372
|
+
async function owedAgentResults(sessionId) {
|
|
238
373
|
const [{ _getPendingMessagesForSession }, { completionIsDelivered, isCompletionNotificationEntry }] =
|
|
239
374
|
await Promise.all([
|
|
240
375
|
import('../runtime/agent/orchestrator/session/manager/pending-messages.mjs'),
|
|
@@ -255,22 +390,37 @@ export async function owedAgentResults(sessionId) {
|
|
|
255
390
|
}
|
|
256
391
|
|
|
257
392
|
/** pendingWorkOf over this process's state: the in-flight runtimes, the
|
|
258
|
-
* durable pending queue, the agent pool and the background agent tasks
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
393
|
+
* durable pending queue, the agent pool and the background agent tasks;
|
|
394
|
+
* `taskIds` narrows it to one chain's agent tasks. */
|
|
395
|
+
export async function taskSessionPendingWork(sessionId, { owedResults = owedAgentResults, taskIds = null } = {}) {
|
|
396
|
+
const [
|
|
397
|
+
{ inFlightSessionActivity },
|
|
398
|
+
{ listStoredAgentWorkersAsync },
|
|
399
|
+
{ activeBackgroundTaskSessionIds, listBackgroundTasks },
|
|
400
|
+
] = await Promise.all([
|
|
401
|
+
import('../runtime/agent/orchestrator/session/manager/runtime-state.mjs'),
|
|
402
|
+
import('../runtime/agent/orchestrator/session/store-summary-reader.mjs'),
|
|
403
|
+
import('../runtime/shared/background-tasks.mjs'),
|
|
404
|
+
]);
|
|
266
405
|
const { working, agentWorking } = inFlightSessionActivity();
|
|
267
406
|
const agentTaskOwners = activeBackgroundTaskSessionIds({ surface: 'agent' });
|
|
268
|
-
|
|
269
|
-
|
|
407
|
+
const agentTasks = taskIds ? agentTaskRows(listBackgroundTasks) : [];
|
|
408
|
+
const live = { agentWorking, agentTaskOwners, taskIds, working, agentTasks };
|
|
409
|
+
if (pendingWorkOf(sessionId, live)) return true;
|
|
410
|
+
const owed = await owedResults(sessionId);
|
|
411
|
+
if (pendingWorkOf(sessionId, { ...live, owed })) return true;
|
|
270
412
|
const pool = await listStoredAgentWorkersAsync({ runningSessionIds: working });
|
|
271
|
-
return pendingWorkOf(sessionId, {
|
|
413
|
+
return pendingWorkOf(sessionId, { ...live, owed, pool });
|
|
272
414
|
}
|
|
273
415
|
|
|
416
|
+
/** This process's agent tasks as pendingWorkOf reads them. */
|
|
417
|
+
const agentTaskRows = (listBackgroundTasks) =>
|
|
418
|
+
listBackgroundTasks({ surface: 'agent' }).map((task) => ({
|
|
419
|
+
taskId: task.task_id,
|
|
420
|
+
sessionId: task.sessionId || null,
|
|
421
|
+
running: task.status === 'running',
|
|
422
|
+
}));
|
|
423
|
+
|
|
274
424
|
// A task session idle with nothing pending must stay so this long before its
|
|
275
425
|
// latest answer is final: a finished agent's result can start the Lead's next
|
|
276
426
|
// turn right after (the desktop notifier's quiet period).
|
|
@@ -285,9 +435,6 @@ const LOST_TEXT = 'The message never ran: the app restarted before its turn star
|
|
|
285
435
|
const GONE_TEXT = 'The session no longer exists.';
|
|
286
436
|
const RESUME_FAILED_TEXT =
|
|
287
437
|
'The agent task session could not continue after the app restarted: its agents’ results never reached it. Check that session.';
|
|
288
|
-
const PENDING_CAP_MS = 30 * 60_000;
|
|
289
|
-
const STILL_WORKING_TEXT =
|
|
290
|
-
'Still in progress: its agents are still working 30+ minutes after this answer. This is the latest answer; the final one is reported when they finish.';
|
|
291
438
|
// Deliveries of a restarted task session's queued agent results before its
|
|
292
439
|
// report gives up as RESUME_FAILED_TEXT.
|
|
293
440
|
const OWED_WAKE_TRIES = 3;
|
|
@@ -305,8 +452,9 @@ const LAST_USED_ROUTE_MAX_PAGES = 20;
|
|
|
305
452
|
const roomFrom = (originSessionId) => (roomMemberOfSession(originSessionId) ? { from: originSessionId } : {});
|
|
306
453
|
|
|
307
454
|
/** The route of `modelRef`: a provider/model[@effort…] ref (a superseded
|
|
308
|
-
* model runs on its successor)
|
|
309
|
-
|
|
455
|
+
* model runs on its successor), a preset name, else whatever `byName` makes
|
|
456
|
+
* of it (a catalog display name; it throws when it names none). */
|
|
457
|
+
async function configModelRoute(modelRef, byName) {
|
|
310
458
|
const [{ modelRouteFields, parseScheduleModelRef }, { migrateCodexRoute }, { loadConfig }] = await Promise.all([
|
|
311
459
|
import('../runtime/shared/schedule-model-ref.mjs'),
|
|
312
460
|
import('../runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs'),
|
|
@@ -315,13 +463,12 @@ async function configModelRoute(modelRef) {
|
|
|
315
463
|
const ref = parseScheduleModelRef(modelRef);
|
|
316
464
|
if (ref && typeof ref === 'object') return modelRouteFields(migrateCodexRoute(ref));
|
|
317
465
|
const preset = loadConfig({ secrets: false }).presets?.find((p) => p.id === ref || p.name === ref);
|
|
318
|
-
|
|
319
|
-
return modelRouteFields(preset);
|
|
466
|
+
return preset ? modelRouteFields(preset) : byName(ref);
|
|
320
467
|
}
|
|
321
468
|
|
|
322
469
|
/** The Lead (Main) route of the config — what a desktop New task starts on.
|
|
323
470
|
* Null while none is chosen. */
|
|
324
|
-
async function configLeadRoute() {
|
|
471
|
+
export async function configLeadRoute() {
|
|
325
472
|
const [{ modelRouteFields }, { resolveRoute }, { loadConfig }] = await Promise.all([
|
|
326
473
|
import('../runtime/shared/schedule-model-ref.mjs'),
|
|
327
474
|
import('./boot/shared.mjs'),
|
|
@@ -352,29 +499,60 @@ async function lastUsedSessionRoute(sessionService) {
|
|
|
352
499
|
/** Wait for the final answer of the task turn `runId` of `sessionId` ended
|
|
353
500
|
* completed: the end of the latest turn since it (a Lead that handed work
|
|
354
501
|
* to agents ends its turn and answers in the turn their results wake),
|
|
355
|
-
* once
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
502
|
+
* once that chain has no turn running and no pending agent work
|
|
503
|
+
* (pendingWork of its agent tasks) for `settleMs`. Work of the session
|
|
504
|
+
* outside the chain (a turn the user typed, its agents) never holds it;
|
|
505
|
+
* without the provenance to tell (no stored turn, `ambiguous`) the whole
|
|
506
|
+
* session must be idle with none pending. `immediate` is runId's own end,
|
|
507
|
+
* when this process saw it.
|
|
508
|
+
* Every other answer of the chain — each ended turn a later turn of the
|
|
509
|
+
* chain followed, and the latest while its work still runs or its
|
|
510
|
+
* provenance is missing — goes to `interim({ id, status, text })` once, in
|
|
511
|
+
* chain order (`id`: the submission of the turn that gave it): a progress
|
|
512
|
+
* update, which wakes nobody. Every check lists all the chain's ended turns,
|
|
513
|
+
* so answers that end close together are each reported. `progressedThrough`:
|
|
514
|
+
* the last turn an earlier process reported (the watch's durable mark):
|
|
515
|
+
* it and the turns before it are not reported again.
|
|
516
|
+
* `finish(status, text, answerId)` runs once (`answerId`: the turn whose
|
|
517
|
+
* answer it is, none for a report of no turn); `check` re-evaluates (call it
|
|
518
|
+
* on every state change of `runtime`), `stop` ends the wait. */
|
|
359
519
|
function followTaskToFinal(
|
|
360
|
-
{ loadSession, owedResults, deliverCompletion, pendingWork, settleMs, pollMs,
|
|
361
|
-
{ sessionId, runId, runtime, immediate = null, wakeOwed = false, finish,
|
|
520
|
+
{ loadSession, owedResults, deliverCompletion, pendingWork, settleMs, pollMs, log },
|
|
521
|
+
{ sessionId, runId, runtime, immediate = null, wakeOwed = false, finish, interim = null, progressedThrough = '' }
|
|
362
522
|
) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
|
|
523
|
+
// The chain's ended answers, oldest first, `{ id, status, text }`; runId's
|
|
524
|
+
// own is `immediate` when this process saw it. `latest`: turnChain's.
|
|
525
|
+
const answersOf = (latest) => {
|
|
526
|
+
const stored = latest?.answers || [];
|
|
527
|
+
if (!immediate) return stored;
|
|
528
|
+
return [{ id: runId, ...immediate }, ...stored.filter((answer) => answer.id !== runId)];
|
|
529
|
+
};
|
|
530
|
+
// The answers already handed to `interim`, by turn.
|
|
531
|
+
const interimIds = new Set();
|
|
532
|
+
/** Hand `answer` to `interim` once; false when that failed (logged): it is
|
|
533
|
+
* tried again at the next check, and no later answer goes out before it. */
|
|
534
|
+
async function reportInterim(answer) {
|
|
535
|
+
if (!answer?.id || !String(answer.text || '').trim() || typeof interim !== 'function') return true;
|
|
536
|
+
if (interimIds.has(answer.id)) return true;
|
|
537
|
+
try {
|
|
538
|
+
await interim(answer);
|
|
539
|
+
} catch (error) {
|
|
540
|
+
log(`progress update of ${sessionId} failed, retried at the next check: ${error?.message || error}`);
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
interimIds.add(answer.id);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
/** Report, in order, every one of `answers` past the durable mark but
|
|
547
|
+
* `held` (the final candidate, reported only when it proves not final);
|
|
548
|
+
* false, stopping there, at the first that could not be queued. */
|
|
549
|
+
async function reportEarlier(answers, held) {
|
|
550
|
+
const through = progressedThrough ? answers.findIndex((answer) => answer.id === progressedThrough) : -1;
|
|
551
|
+
for (const [index, answer] of answers.entries()) {
|
|
552
|
+
if (index <= through) interimIds.add(answer.id);
|
|
553
|
+
else if (answer !== held && !(await reportInterim(answer))) return false;
|
|
554
|
+
}
|
|
555
|
+
return true;
|
|
378
556
|
}
|
|
379
557
|
let done = false;
|
|
380
558
|
let checking = false;
|
|
@@ -383,6 +561,7 @@ function followTaskToFinal(
|
|
|
383
561
|
let candidateAt = 0;
|
|
384
562
|
let timer = null;
|
|
385
563
|
let wakes = 0;
|
|
564
|
+
let otherWakes = 0;
|
|
386
565
|
let wokeAt = 0;
|
|
387
566
|
const unwoken = (why) => {
|
|
388
567
|
log(`${sessionId} could not take its agents' results after the restart: ${why}`);
|
|
@@ -391,23 +570,32 @@ function followTaskToFinal(
|
|
|
391
570
|
// `wakeOwed` (a restarted watch): deliver the agent results queued for
|
|
392
571
|
// the idle session through the notification bus so a turn takes them, at
|
|
393
572
|
// most OWED_WAKE_TRIES times, a poll apart; null while that turn is owed.
|
|
394
|
-
|
|
573
|
+
// Every owed result is delivered, but only those of the chain's agent tasks
|
|
574
|
+
// (owedOf `taskIds`) hold the report or fail it; deliveries of the others
|
|
575
|
+
// alone have a bound of their own (`otherWakes`).
|
|
576
|
+
async function wakeForOwed(taskIds) {
|
|
395
577
|
const owed = await owedResults(sessionId);
|
|
396
578
|
if (!owed.length) return undefined;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
if (typeof deliverCompletion === 'function') {
|
|
404
|
-
const results = await Promise.all(owed.map(({ text, meta }) => deliverCompletion(sessionId, text, meta)));
|
|
405
|
-
delivered = results.some((result) => result === true);
|
|
406
|
-
}
|
|
407
|
-
} catch (error) {
|
|
408
|
-
return unwoken(error?.message || error);
|
|
579
|
+
const chain = new Set(owedOf(owed, taskIds));
|
|
580
|
+
const hold = chain.size ? null : undefined;
|
|
581
|
+
if (Date.now() - wokeAt < pollMs) return hold;
|
|
582
|
+
if (chain.size ? wakes >= OWED_WAKE_TRIES : otherWakes >= OWED_WAKE_TRIES) {
|
|
583
|
+
return chain.size ? unwoken(`still not taken after ${wakes} deliveries`) : undefined;
|
|
409
584
|
}
|
|
410
|
-
|
|
585
|
+
if (chain.size) wakes += 1;
|
|
586
|
+
else otherWakes += 1;
|
|
587
|
+
wokeAt = Date.now();
|
|
588
|
+
const outcomes = await Promise.allSettled(
|
|
589
|
+
owed.map(
|
|
590
|
+
async ({ text, meta }) =>
|
|
591
|
+
typeof deliverCompletion === 'function' && (await deliverCompletion(sessionId, text, meta)) === true
|
|
592
|
+
)
|
|
593
|
+
);
|
|
594
|
+
if (!chain.size) return undefined;
|
|
595
|
+
const ofChain = outcomes.filter((_, index) => chain.has(owed[index]));
|
|
596
|
+
const failure = ofChain.find((outcome) => outcome.status === 'rejected');
|
|
597
|
+
if (failure) return unwoken(failure.reason?.message || failure.reason);
|
|
598
|
+
return ofChain.some((outcome) => outcome.value === true) ? null : unwoken('no session runtime took the delivery');
|
|
411
599
|
}
|
|
412
600
|
const arm = (ms) => {
|
|
413
601
|
clearTimeout(timer);
|
|
@@ -421,26 +609,36 @@ function followTaskToFinal(
|
|
|
421
609
|
const failed = (error) => log(`following ${sessionId} to its final answer failed: ${error?.message || error}`);
|
|
422
610
|
// The end to report now, or null while the task still works.
|
|
423
611
|
async function ready() {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
612
|
+
const stored = await loadSession(sessionId);
|
|
613
|
+
const latest = turnChain(stored?.messages, runId);
|
|
614
|
+
// The chain's agent tasks; null (the whole session) without its provenance.
|
|
615
|
+
const taskIds = latest && !latest.ambiguous ? latest.taskIds : null;
|
|
616
|
+
// Every answer of the chain but its latest turn's is no final one: each
|
|
617
|
+
// goes out as progress, whatever the session does now.
|
|
618
|
+
const answers = answersOf(latest);
|
|
619
|
+
const latestId = latest ? latest.id : runId;
|
|
620
|
+
const answer = answers.at(-1)?.id === latestId ? answers.at(-1) : null;
|
|
621
|
+
// An answer that could not be queued holds the chain (later answers, the
|
|
622
|
+
// final report) until a later check queues it: the poll retries it.
|
|
623
|
+
if (!(await reportEarlier(answers, answer))) return null;
|
|
624
|
+
// A busy session may run the chain's turn unless that turn is provably another's.
|
|
625
|
+
const busy = Boolean(runtime?.getState?.()?.busy);
|
|
626
|
+
if (busy && !(taskIds && latest.busyIsOthers)) return null;
|
|
627
|
+
if (wakeOwed && !busy) {
|
|
628
|
+
const woke = await wakeForOwed(taskIds);
|
|
427
629
|
if (woke !== undefined) return woke;
|
|
428
630
|
}
|
|
429
|
-
if (await pendingWork(sessionId)) {
|
|
430
|
-
await
|
|
631
|
+
if (await pendingWork(sessionId, { taskIds })) {
|
|
632
|
+
await reportInterim(answer);
|
|
431
633
|
return null;
|
|
432
634
|
}
|
|
433
|
-
const stored = await loadSession(sessionId);
|
|
434
635
|
if (!stored && !immediate) return { key: 'gone', status: 'cancelled', text: GONE_TEXT };
|
|
435
|
-
const latest = latestTurnOutcome(stored?.messages, runId);
|
|
436
636
|
// Provenance the chain needs is missing: its answer may not be final.
|
|
437
637
|
if (latest?.ambiguous) {
|
|
438
|
-
await
|
|
638
|
+
await reportInterim(answer);
|
|
439
639
|
return null;
|
|
440
640
|
}
|
|
441
|
-
|
|
442
|
-
if (!latest?.outcome || latest.outcome === 'open') return null;
|
|
443
|
-
return { key: `${latest.id}:${latest.outcome.status}`, ...latest.outcome };
|
|
641
|
+
return answer && { key: `${answer.id}:${answer.status}`, ...answer };
|
|
444
642
|
}
|
|
445
643
|
async function check() {
|
|
446
644
|
if (done) return;
|
|
@@ -466,7 +664,7 @@ function followTaskToFinal(
|
|
|
466
664
|
arm(candidateAt + settleMs - Date.now());
|
|
467
665
|
} else {
|
|
468
666
|
stop();
|
|
469
|
-
await finish(end.status, end.text);
|
|
667
|
+
await finish(end.status, end.text, end.id);
|
|
470
668
|
}
|
|
471
669
|
} while (again && !done);
|
|
472
670
|
} finally {
|
|
@@ -624,7 +822,7 @@ async function rearmTaskWatch(
|
|
|
624
822
|
// The turn that answers: the watched submission's own, or the one it
|
|
625
823
|
// follows (a room member's turn under another submission id).
|
|
626
824
|
const turnId = watch.follows || watch.runId;
|
|
627
|
-
const finish = async (status, text = '') =>
|
|
825
|
+
const finish = async (status, text = '', answerId = '') =>
|
|
628
826
|
deliver(watch.agentId, {
|
|
629
827
|
runId: `${watch.runId}_end`,
|
|
630
828
|
taskSessionId: sessionId,
|
|
@@ -635,9 +833,8 @@ async function rearmTaskWatch(
|
|
|
635
833
|
...(watch.hop > 0 ? { hop: watch.hop } : {}),
|
|
636
834
|
...roomFrom(watch.homeSessionId),
|
|
637
835
|
watchId: watch.runId,
|
|
638
|
-
...(watch.
|
|
639
|
-
|
|
640
|
-
: await artifactFields(sessionId, turnId)),
|
|
836
|
+
...progressOf(watch.runId, answerId),
|
|
837
|
+
...(watch.textOnly || text === GONE_TEXT || text === LOST_TEXT ? {} : await artifactFields(sessionId, turnId)),
|
|
641
838
|
});
|
|
642
839
|
if (!(await loadSession(sessionId))) return finish('cancelled', GONE_TEXT);
|
|
643
840
|
let runtime;
|
|
@@ -682,21 +879,25 @@ async function rearmTaskWatch(
|
|
|
682
879
|
runId: turnId,
|
|
683
880
|
runtime,
|
|
684
881
|
wakeOwed: true,
|
|
685
|
-
|
|
882
|
+
// The turns an earlier process already reported as progress.
|
|
883
|
+
progressedThrough: watch.progressed || '',
|
|
884
|
+
interim: (answer) =>
|
|
686
885
|
deliver(watch.agentId, {
|
|
687
|
-
runId:
|
|
886
|
+
runId: progressRunId(watch.runId, answer.id),
|
|
688
887
|
taskSessionId: sessionId,
|
|
689
888
|
title: watch.title,
|
|
690
889
|
status: 'working',
|
|
691
|
-
text,
|
|
890
|
+
text: answer.text,
|
|
891
|
+
progressWatchId: watch.runId,
|
|
892
|
+
answerId: answer.id,
|
|
692
893
|
...(watch.target === 'session' ? { target: 'session' } : {}),
|
|
693
894
|
...(watch.hop > 0 ? { hop: watch.hop } : {}),
|
|
694
895
|
...roomFrom(watch.homeSessionId),
|
|
695
896
|
}),
|
|
696
|
-
finish: async (status, text) => {
|
|
897
|
+
finish: async (status, text, answerId) => {
|
|
697
898
|
done = true;
|
|
698
899
|
unsubscribe();
|
|
699
|
-
await finish(status, text);
|
|
900
|
+
await finish(status, text, answerId);
|
|
700
901
|
},
|
|
701
902
|
});
|
|
702
903
|
} else if (end) {
|
|
@@ -715,6 +916,161 @@ async function rearmTaskWatch(
|
|
|
715
916
|
await check();
|
|
716
917
|
}
|
|
717
918
|
|
|
919
|
+
/** The settlement of sends one turn answers: they settle together with the
|
|
920
|
+
* same `detail` object (a send steered into the turn while it ran, or queued
|
|
921
|
+
* and taken as one batch), and that turn reports once, closing every one of
|
|
922
|
+
* them. `nextSeq()` numbers the sends in submission order: the earliest of
|
|
923
|
+
* the sends one turn answers reports for them. */
|
|
924
|
+
function createSendSettlement(log) {
|
|
925
|
+
const settledTurns = new WeakMap();
|
|
926
|
+
let sendCount = 0;
|
|
927
|
+
|
|
928
|
+
/** Add send `member` (`{ seq, id, unwatch, settle }`) to the settlement
|
|
929
|
+
* `detail` of its turn, per reporting conversation. The sends of one turn
|
|
930
|
+
* settle in the same tick; after it the earliest (the turn's own, whose
|
|
931
|
+
* record holds the whole turn) settles with all their ids and the others
|
|
932
|
+
* stand down. */
|
|
933
|
+
function join(detail, origin, member) {
|
|
934
|
+
const key = `${origin.agentId}\0${origin.sessionId}`;
|
|
935
|
+
let groups = settledTurns.get(detail);
|
|
936
|
+
if (!groups) {
|
|
937
|
+
groups = new Map();
|
|
938
|
+
settledTurns.set(detail, groups);
|
|
939
|
+
}
|
|
940
|
+
let members = groups.get(key);
|
|
941
|
+
if (!members) {
|
|
942
|
+
members = [];
|
|
943
|
+
groups.set(key, members);
|
|
944
|
+
const settling = members;
|
|
945
|
+
void Promise.resolve()
|
|
946
|
+
.then(() => {
|
|
947
|
+
const [first, ...others] = settling.sort((a, b) => a.seq - b.seq);
|
|
948
|
+
for (const other of others) other.unwatch();
|
|
949
|
+
first.settle(settling.map((entry) => entry.id));
|
|
950
|
+
})
|
|
951
|
+
.catch((error) => log(`settling the sends of one turn failed: ${error?.message || error}`));
|
|
952
|
+
}
|
|
953
|
+
members.push(member);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const nextSeq = () => {
|
|
957
|
+
sendCount += 1;
|
|
958
|
+
return sendCount;
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
return { join, nextSeq };
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/** The queue row of send `id`'s progress update carrying the answer of turn
|
|
965
|
+
* `answerId` (followToFinal `interim`): one per answer, across restarts. */
|
|
966
|
+
const progressRunId = (id, answerId) => `${id}_progress_${answerId}`;
|
|
967
|
+
|
|
968
|
+
/** The queue row of send `id`'s report of `status`. 'working': a progress
|
|
969
|
+
* update (progressRunId), not the end. A wait for approval belongs to the
|
|
970
|
+
* target session, not to this send: every watcher of it names the same row,
|
|
971
|
+
* so it posts once. */
|
|
972
|
+
const turnReportRunId = (sessionId, id, status, { approvalId = '', answerId = '' } = {}) => {
|
|
973
|
+
if (status === 'blocked') return `${sessionId}_${approvalId}_blocked`;
|
|
974
|
+
return status === 'working' ? progressRunId(id, answerId) : `${id}_end`;
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
/** The report fields naming the progress update that may have carried the
|
|
978
|
+
* final answer of turn `answerId` already (the queue then does not repeat
|
|
979
|
+
* it); none for a report of no turn. */
|
|
980
|
+
const progressOf = (id, answerId) => (answerId ? { progressRunId: progressRunId(id, answerId) } : {});
|
|
981
|
+
|
|
982
|
+
/** The handler of send `id`'s task-session state: a wait for the user's
|
|
983
|
+
* approval reports blocked once; a later, different one reports again. */
|
|
984
|
+
function approvalWaitReporter(id, report) {
|
|
985
|
+
// The approval wait last reported.
|
|
986
|
+
let reportedApproval = null;
|
|
987
|
+
return (state) => {
|
|
988
|
+
if (!state.toolApproval) {
|
|
989
|
+
reportedApproval = null;
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
const approvalId = String(state.toolApproval.id ?? 'approval');
|
|
993
|
+
if (approvalId === reportedApproval) return;
|
|
994
|
+
// A send still queued behind the running turn is not the turn
|
|
995
|
+
// waiting: that turn's own watcher reports it.
|
|
996
|
+
if ((state.queued || []).some((entry) => entry?.id === id)) return;
|
|
997
|
+
reportedApproval = approvalId;
|
|
998
|
+
void report('blocked', '', { approvalId });
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/** The transcript meta of a send from `origin`. `fromAgent`: the sending bot,
|
|
1003
|
+
* recorded when it writes into another bot's home chat (otherwise the row
|
|
1004
|
+
* would read as the user's); `hop`: its place in a bot-to-bot chain
|
|
1005
|
+
* (bot-hops.mjs). */
|
|
1006
|
+
const sendTranscriptMeta = (origin) => ({
|
|
1007
|
+
sender: 'lead',
|
|
1008
|
+
...(origin.envelopeName ? { fromAgent: origin.agentId } : {}),
|
|
1009
|
+
...(origin.hop > 0 ? { hop: origin.hop } : {}),
|
|
1010
|
+
// The bots that handed this work on, the sender last (bot-hops.mjs).
|
|
1011
|
+
...(origin.botChain?.length ? { botChain: origin.botChain } : {}),
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
/** What a send tells its caller once submitted. */
|
|
1015
|
+
const sendResult = (accepted, notify, id, noReport) => ({
|
|
1016
|
+
accepted,
|
|
1017
|
+
// Names this send; its terminal report carries it back (routine.sendId).
|
|
1018
|
+
...(accepted && notify ? { sendId: id } : {}),
|
|
1019
|
+
reportsBack: accepted && notify,
|
|
1020
|
+
...(accepted && !notify ? { reportsBackReason: noReport } : {}),
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
/** create's brief and `worktree` location, checked before anything runs. */
|
|
1024
|
+
function createRequest(args, callerAgentId, callerScope) {
|
|
1025
|
+
if (!callerAgentId) throw new Error('session: create is only for an agent conversation');
|
|
1026
|
+
const brief = typeof args.brief === 'string' ? args.brief.trim() : '';
|
|
1027
|
+
if (!brief) throw new Error('session: brief is required for action=create');
|
|
1028
|
+
const location = taskLocation(args.worktree);
|
|
1029
|
+
if (location.mode === 'worktree') assertWorktreeMutationAllowed(callerScope, 'session: create with a worktree');
|
|
1030
|
+
return { brief, location };
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/** Where create starts (resolveTaskProject). An explicit worktree is never
|
|
1034
|
+
* silently dropped: it needs a registered Project. */
|
|
1035
|
+
function createWhere(args, projects, callerCwd, location) {
|
|
1036
|
+
const where = resolveTaskProject(args.project, { projects, cwd: callerCwd || process.cwd() });
|
|
1037
|
+
const registered = where.project && projects.some((entry) => samePath(entry.path, where.project.path));
|
|
1038
|
+
if (location.mode === 'worktree' && !registered) {
|
|
1039
|
+
throw taskWorktreeFailure(
|
|
1040
|
+
worktreeError('WORKTREE_PROJECT_UNKNOWN', 'A worktree needs a registered Project; run list-projects.', {
|
|
1041
|
+
projectPath: where.project?.path ?? null,
|
|
1042
|
+
})
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
return where;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/** The worktree failure `error` of task session `sessionId` whose empty
|
|
1049
|
+
* session could not be dropped (`dropFailure`): the model must know the
|
|
1050
|
+
* empty session is still there to delete. */
|
|
1051
|
+
const orphanSessionError = (error, sessionId, dropFailure) =>
|
|
1052
|
+
Object.assign(
|
|
1053
|
+
new Error(
|
|
1054
|
+
`${error?.message || error} The empty task session ${sessionId} could not be deleted either (${dropFailure?.message || dropFailure}); delete it with session action=delete session_id=${sessionId}.`
|
|
1055
|
+
),
|
|
1056
|
+
{
|
|
1057
|
+
...(typeof error?.code === 'string' ? { code: error.code } : {}),
|
|
1058
|
+
...(error?.details ? { details: error.details } : {}),
|
|
1059
|
+
orphanSessionId: sessionId,
|
|
1060
|
+
cause: error,
|
|
1061
|
+
}
|
|
1062
|
+
);
|
|
1063
|
+
|
|
1064
|
+
/** `ready` resolves once `started()` runs: "Started" lands in the home before
|
|
1065
|
+
* any report of the brief's turn. */
|
|
1066
|
+
function startedGate() {
|
|
1067
|
+
let started = () => {};
|
|
1068
|
+
const ready = new Promise((resolveReady) => {
|
|
1069
|
+
started = resolveReady;
|
|
1070
|
+
});
|
|
1071
|
+
return { ready, started };
|
|
1072
|
+
}
|
|
1073
|
+
|
|
718
1074
|
export function createAgentTaskSessions({
|
|
719
1075
|
getSessionService,
|
|
720
1076
|
deliver = async (agentId, report) => (await import('./agent-home-delivery.mjs')).deliverAgentTask(agentId, report),
|
|
@@ -737,6 +1093,8 @@ export function createAgentTaskSessions({
|
|
|
737
1093
|
newTaskDefault = async () =>
|
|
738
1094
|
(await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).worktree?.newTaskDefault,
|
|
739
1095
|
modelRoute = configModelRoute,
|
|
1096
|
+
// The model menu's catalog rows, read through the calling session's runtime.
|
|
1097
|
+
modelCatalog = (sessionId) => readModelCatalog(getSessionService(), sessionId),
|
|
740
1098
|
// The Lead (Main) route of the config — what a desktop New task starts on.
|
|
741
1099
|
leadRoute = configLeadRoute,
|
|
742
1100
|
// The model the user last used in a coding session.
|
|
@@ -767,9 +1125,6 @@ export function createAgentTaskSessions({
|
|
|
767
1125
|
owedResults = owedAgentResults,
|
|
768
1126
|
deliverCompletion = null,
|
|
769
1127
|
settleAgentResults = async () => {},
|
|
770
|
-
// Pending agent work this long after the latest answer reports that answer
|
|
771
|
-
// as still in progress (once); the final report still follows.
|
|
772
|
-
pendingCapMs = PENDING_CAP_MS,
|
|
773
1128
|
log = (line) => process.stderr.write(`mixdog task sessions: ${line}\n`),
|
|
774
1129
|
}) {
|
|
775
1130
|
/** The report fields of turn `runId`'s files; none when it produced none. */
|
|
@@ -791,61 +1146,106 @@ export function createAgentTaskSessions({
|
|
|
791
1146
|
};
|
|
792
1147
|
|
|
793
1148
|
const followToFinal = (follow) =>
|
|
794
|
-
followTaskToFinal(
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
);
|
|
1149
|
+
followTaskToFinal({ loadSession, owedResults, deliverCompletion, pendingWork, settleMs, pollMs, log }, follow);
|
|
1150
|
+
|
|
1151
|
+
const settlement = createSendSettlement(log);
|
|
798
1152
|
|
|
799
1153
|
/** Submit `prompt` as the origin agent's turn; its end (and a wait for the
|
|
800
1154
|
* user in it) reports to the agent's home. Resolves on intake. `submit`
|
|
801
1155
|
* takes the turn in another way than a prompt to `sessionId` (a message
|
|
802
1156
|
* into a group room, whose member answers in `sessionId`): given `{ id,
|
|
803
1157
|
* onSettled }`, it resolves `{ accepted }`. */
|
|
804
|
-
async function agentTurn(
|
|
1158
|
+
async function agentTurn(
|
|
1159
|
+
sessionId,
|
|
1160
|
+
prompt,
|
|
1161
|
+
origin,
|
|
1162
|
+
id = `atask_${randomUUID()}`,
|
|
1163
|
+
beforeReport = null,
|
|
1164
|
+
submit = null
|
|
1165
|
+
) {
|
|
805
1166
|
const sessions = service();
|
|
806
1167
|
const target = origin.target === 'session' ? { target: 'session' } : {};
|
|
807
1168
|
const { notify, noReport } = await watchTurn({ watches, owner: processToken, log }, sessionId, origin, id);
|
|
808
1169
|
const dropWatch = () =>
|
|
809
1170
|
watches.remove(id).catch((error) => log(`dropping the watch of ${sessionId} failed: ${error?.message || error}`));
|
|
810
1171
|
let runtime = null;
|
|
1172
|
+
// The sends this turn's end closes: this one, plus any that its turn
|
|
1173
|
+
// answered too (createSendSettlement).
|
|
1174
|
+
let sendIds = [id];
|
|
811
1175
|
// A report that cannot be queued is retried in this process; when every try
|
|
812
1176
|
// fails the durable watch stays and restart recovery delivers it (the queue
|
|
813
1177
|
// dedupes by runId, so a retry never posts twice).
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1178
|
+
// `answerId`: the turn whose answer a progress update or the end carries.
|
|
1179
|
+
// `deliverReport` rejects once every try failed (a progress update is
|
|
1180
|
+
// tried again at the follow's next check); `report` logs it instead.
|
|
1181
|
+
const deliverReport = (status, text = '', { approvalId = '', answerId = '' } = {}) =>
|
|
1182
|
+
Promise.resolve(beforeReport).then(async () =>
|
|
1183
|
+
deliverWithRetries({ deliver, retryDelaysMs, log }, origin.agentId, sessionId, {
|
|
1184
|
+
runId: turnReportRunId(sessionId, id, status, { approvalId, answerId }),
|
|
1185
|
+
taskSessionId: sessionId,
|
|
1186
|
+
title: origin.title,
|
|
1187
|
+
status,
|
|
1188
|
+
text,
|
|
1189
|
+
...target,
|
|
1190
|
+
...(origin.hop > 0 ? { hop: origin.hop } : {}),
|
|
1191
|
+
...roomFrom(origin.sessionId),
|
|
1192
|
+
// A progress update moves this send's durable mark to its turn
|
|
1193
|
+
// (agent-home-delivery.mjs enqueueTask), in the same write.
|
|
1194
|
+
...(status === 'working' ? { progressWatchId: id, answerId } : {}),
|
|
1195
|
+
// A turn's end brings back the files it produced, when the agent
|
|
1196
|
+
// may receive them (reportOrigin `textOnly`).
|
|
1197
|
+
...(status === 'blocked' || status === 'working'
|
|
1198
|
+
? {}
|
|
1199
|
+
: {
|
|
1200
|
+
watchId: id,
|
|
1201
|
+
...(sendIds.length > 1 ? { watchIds: sendIds } : {}),
|
|
1202
|
+
...progressOf(id, answerId),
|
|
1203
|
+
...(origin.textOnly ? {} : await artifactFields(sessionId, id, runtime)),
|
|
1204
|
+
}),
|
|
1205
|
+
})
|
|
1206
|
+
);
|
|
1207
|
+
const report = (status, text = '', options = {}) =>
|
|
1208
|
+
deliverReport(status, text, options).catch((error) =>
|
|
1209
|
+
log(`report of ${sessionId} failed: ${error?.message || error}`)
|
|
1210
|
+
);
|
|
835
1211
|
let unwatch = () => {};
|
|
836
1212
|
// Set once the turn ended completed: the report waits for the final answer.
|
|
837
1213
|
let follow = null;
|
|
838
1214
|
let result;
|
|
1215
|
+
/** This send ends the turn `detail` for the sends `answered` (createSendSettlement). */
|
|
1216
|
+
const settleTurn = (detail, answered) => {
|
|
1217
|
+
sendIds = answered;
|
|
1218
|
+
const status = turnStatus(detail);
|
|
1219
|
+
const text = detail?.result?.content || detail?.error || '';
|
|
1220
|
+
// A failed or cancelled turn reports at once; a completed one
|
|
1221
|
+
// reports the task's final answer (followToFinal).
|
|
1222
|
+
if (status !== 'completed') {
|
|
1223
|
+
unwatch();
|
|
1224
|
+
void report(status, text);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
follow = followToFinal({
|
|
1228
|
+
sessionId,
|
|
1229
|
+
runId: id,
|
|
1230
|
+
runtime,
|
|
1231
|
+
immediate: { status, text },
|
|
1232
|
+
finish: async (endStatus, endText, answerId) => {
|
|
1233
|
+
unwatch();
|
|
1234
|
+
await report(endStatus, endText, { answerId });
|
|
1235
|
+
},
|
|
1236
|
+
// Each other answer of its chain: a progress update that wakes nobody.
|
|
1237
|
+
interim: (answer) => deliverReport('working', answer.text, { answerId: answer.id }),
|
|
1238
|
+
});
|
|
1239
|
+
follow.check();
|
|
1240
|
+
};
|
|
839
1241
|
try {
|
|
840
1242
|
if (notify) {
|
|
841
1243
|
runtime = await sessions.materializeSession(sessionId);
|
|
842
|
-
|
|
1244
|
+
const onState = approvalWaitReporter(id, report);
|
|
843
1245
|
unwatch =
|
|
844
1246
|
runtime?.subscribe?.(() => {
|
|
845
1247
|
follow?.check();
|
|
846
|
-
|
|
847
|
-
blocked = true;
|
|
848
|
-
void report('blocked');
|
|
1248
|
+
onState(runtime.getState?.() || {});
|
|
849
1249
|
}) || unwatch;
|
|
850
1250
|
}
|
|
851
1251
|
// A bot's send to a session that is not its own task session reaches the
|
|
@@ -854,46 +1254,24 @@ export function createAgentTaskSessions({
|
|
|
854
1254
|
? wrapAgentMessage(prompt, { name: origin.envelopeName, agentId: origin.agentId })
|
|
855
1255
|
: prompt;
|
|
856
1256
|
const take = submit ?? ((options) => sessions.submitSession({ sessionId, prompt: sent, options }));
|
|
1257
|
+
const seq = settlement.nextSeq();
|
|
857
1258
|
result = await take({
|
|
858
1259
|
id,
|
|
859
1260
|
mode: 'prompt',
|
|
860
|
-
|
|
861
|
-
// bot's home chat (otherwise the row would read as the user's);
|
|
862
|
-
// `hop`: its place in a bot-to-bot chain (bot-hops.mjs).
|
|
863
|
-
transcriptMeta: {
|
|
864
|
-
sender: 'lead',
|
|
865
|
-
...(origin.envelopeName ? { fromAgent: origin.agentId } : {}),
|
|
866
|
-
...(origin.hop > 0 ? { hop: origin.hop } : {}),
|
|
867
|
-
},
|
|
1261
|
+
transcriptMeta: sendTranscriptMeta(origin),
|
|
868
1262
|
...(notify
|
|
869
1263
|
? {
|
|
870
1264
|
// A room answers this send in a turn under another submission
|
|
871
1265
|
// id (one turn for several sends, or a fresh turn for one cut
|
|
872
1266
|
// short): the watch follows that turn, recorded before it runs.
|
|
873
1267
|
follow: (submissionId) => watches.follow?.(id, submissionId),
|
|
874
|
-
onSettled: (detail) =>
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
void report(status, text);
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
follow = followToFinal({
|
|
885
|
-
sessionId,
|
|
886
|
-
runId: id,
|
|
887
|
-
runtime,
|
|
888
|
-
immediate: { status, text },
|
|
889
|
-
finish: async (endStatus, endText) => {
|
|
890
|
-
unwatch();
|
|
891
|
-
await report(endStatus, endText);
|
|
892
|
-
},
|
|
893
|
-
progress: (progressText) => report('working', progressText),
|
|
894
|
-
});
|
|
895
|
-
follow.check();
|
|
896
|
-
},
|
|
1268
|
+
onSettled: (detail) =>
|
|
1269
|
+
settlement.join(detail, origin, {
|
|
1270
|
+
seq,
|
|
1271
|
+
id,
|
|
1272
|
+
unwatch: () => unwatch(),
|
|
1273
|
+
settle: (answered) => settleTurn(detail, answered),
|
|
1274
|
+
}),
|
|
897
1275
|
}
|
|
898
1276
|
: {}),
|
|
899
1277
|
});
|
|
@@ -907,11 +1285,7 @@ export function createAgentTaskSessions({
|
|
|
907
1285
|
unwatch();
|
|
908
1286
|
if (notify) await dropWatch();
|
|
909
1287
|
}
|
|
910
|
-
return
|
|
911
|
-
accepted,
|
|
912
|
-
reportsBack: accepted && notify,
|
|
913
|
-
...(accepted && !notify ? { reportsBackReason: noReport } : {}),
|
|
914
|
-
};
|
|
1288
|
+
return sendResult(accepted, notify, id, noReport);
|
|
915
1289
|
}
|
|
916
1290
|
|
|
917
1291
|
const rearm = (watch) =>
|
|
@@ -954,24 +1328,18 @@ export function createAgentTaskSessions({
|
|
|
954
1328
|
* caller is refused and the New task default keeps it in the Project folder. */
|
|
955
1329
|
async function create(
|
|
956
1330
|
args = {},
|
|
957
|
-
{
|
|
1331
|
+
{
|
|
1332
|
+
callerSessionId,
|
|
1333
|
+
callerAgentId,
|
|
1334
|
+
callerCwd,
|
|
1335
|
+
callerHop = 0,
|
|
1336
|
+
callerBotChain = [],
|
|
1337
|
+
callerScope,
|
|
1338
|
+
attachmentParts = [],
|
|
1339
|
+
} = {}
|
|
958
1340
|
) {
|
|
959
|
-
|
|
960
|
-
const
|
|
961
|
-
if (!brief) throw new Error('session: brief is required for action=create');
|
|
962
|
-
const location = taskLocation(args.worktree);
|
|
963
|
-
if (location.mode === 'worktree') assertWorktreeMutationAllowed(callerScope, 'session: create with a worktree');
|
|
964
|
-
const projects = await listProjects();
|
|
965
|
-
const where = resolveTaskProject(args.project, { projects, cwd: callerCwd || process.cwd() });
|
|
966
|
-
// An explicit worktree is never silently dropped: it needs a registered Project.
|
|
967
|
-
const registered = where.project && projects.some((entry) => samePath(entry.path, where.project.path));
|
|
968
|
-
if (location.mode === 'worktree' && !registered) {
|
|
969
|
-
throw taskWorktreeFailure(
|
|
970
|
-
worktreeError('WORKTREE_PROJECT_UNKNOWN', 'A worktree needs a registered Project; run list-projects.', {
|
|
971
|
-
projectPath: where.project?.path ?? null,
|
|
972
|
-
})
|
|
973
|
-
);
|
|
974
|
-
}
|
|
1341
|
+
const { brief, location } = createRequest(args, callerAgentId, callerScope);
|
|
1342
|
+
const where = createWhere(args, await listProjects(), callerCwd, location);
|
|
975
1343
|
const requestId = `atask_${randomUUID()}`;
|
|
976
1344
|
const title = (clean(args.title) || clean(brief.split('\n')[0])).slice(0, TITLE_LIMIT);
|
|
977
1345
|
const taskOrigin = {
|
|
@@ -984,10 +1352,18 @@ export function createAgentTaskSessions({
|
|
|
984
1352
|
// A task session runs in coding mode under Lead: an explicit model wins,
|
|
985
1353
|
// else the Lead route, else the last-used model. Never a model-less
|
|
986
1354
|
// session — the composer would show no model and its first turn would fail.
|
|
987
|
-
|
|
988
|
-
|
|
1355
|
+
// The catalog is read only when a display name or an effort needs it.
|
|
1356
|
+
let catalog = null;
|
|
1357
|
+
const catalogRows = async () => (catalog ??= await modelCatalog(callerSessionId));
|
|
1358
|
+
let route = clean(args.model)
|
|
1359
|
+
? await modelRoute(clean(args.model), async (text) =>
|
|
1360
|
+
routeByDisplayName(text, await catalogRows(), args.effort !== undefined)
|
|
1361
|
+
)
|
|
989
1362
|
: (await leadRoute()) || (await lastUsedRoute(service()));
|
|
990
1363
|
if (!route) throw new Error('session: no model to start the task session on. Choose a Lead model first.');
|
|
1364
|
+
// An explicit effort is the setting the menu's effort pick makes (session.effort,
|
|
1365
|
+
// Auto effort off); it wins over one a model ref or name carried.
|
|
1366
|
+
if (args.effort !== undefined) route = withEffort(route, args.effort, await catalogRows());
|
|
991
1367
|
const sessions = service();
|
|
992
1368
|
// Where it starts: the `worktree` option, else the user's New task default
|
|
993
1369
|
// for a caller that may provision one. Decided before the session exists,
|
|
@@ -1031,31 +1407,17 @@ export function createAgentTaskSessions({
|
|
|
1031
1407
|
}
|
|
1032
1408
|
if (dropFailure) log(`dropping ${sessionId} failed: ${dropFailure?.message || dropFailure}`);
|
|
1033
1409
|
if (!dropFailure) throw error;
|
|
1034
|
-
|
|
1035
|
-
throw Object.assign(
|
|
1036
|
-
new Error(
|
|
1037
|
-
`${error?.message || error} The empty task session ${sessionId} could not be deleted either (${dropFailure?.message || dropFailure}); delete it with session action=delete session_id=${sessionId}.`
|
|
1038
|
-
),
|
|
1039
|
-
{
|
|
1040
|
-
...(typeof error?.code === 'string' ? { code: error.code } : {}),
|
|
1041
|
-
...(error?.details ? { details: error.details } : {}),
|
|
1042
|
-
orphanSessionId: sessionId,
|
|
1043
|
-
cause: error,
|
|
1044
|
-
}
|
|
1045
|
-
);
|
|
1410
|
+
throw orphanSessionError(error, sessionId, dropFailure);
|
|
1046
1411
|
}
|
|
1047
1412
|
}
|
|
1048
|
-
|
|
1049
|
-
let started = () => {};
|
|
1050
|
-
const ready = new Promise((resolveReady) => {
|
|
1051
|
-
started = resolveReady;
|
|
1052
|
-
});
|
|
1413
|
+
const { ready, started } = startedGate();
|
|
1053
1414
|
// The brief carries the files the agent forwarded (workspace-tools.mjs).
|
|
1054
1415
|
const prompt = handoffContent(brief, attachmentParts);
|
|
1416
|
+
const botChain = botChainOf(callerBotChain);
|
|
1055
1417
|
const { accepted, reportsBack, reportsBackReason } = await agentTurn(
|
|
1056
1418
|
sessionId,
|
|
1057
1419
|
prompt,
|
|
1058
|
-
callerHop > 0 ? {
|
|
1420
|
+
{ ...taskOrigin, ...(callerHop > 0 ? { hop: callerHop } : {}), ...(botChain.length ? { botChain } : {}) },
|
|
1059
1421
|
requestId,
|
|
1060
1422
|
ready
|
|
1061
1423
|
);
|