mixdog 1.3.5 → 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/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +4 -2
- 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 +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +17 -1
- package/src/defaults/skills/browser-use/SKILL.md +59 -11
- 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 +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- 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/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- 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 +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +181 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- 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 +145 -66
- 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-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- 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 +2 -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/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 +26 -0
- 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/runtime-core/ask-user-tool-defs.mjs +12 -7
- 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/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.mjs +125 -30
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
- 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 +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- 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 +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -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 +3 -2
- 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 +12 -3
- 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-execute.mjs +133 -90
- 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/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/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.mjs +36 -9
- 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-rows.mjs +8 -6
- 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/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/transcript-restore/restore.mjs +18 -1
- 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/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -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 +10 -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/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/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- 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/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/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/usage-rollup.mjs +1 -1
- 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 +7 -1
- 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 +10 -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-result-summary.mjs +1 -14
- 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 +448 -185
- package/src/session-runtime/agent-task-sessions.mjs +329 -180
- package/src/session-runtime/boot/apis.mjs +9 -0
- 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 +6 -1
- 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/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 +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- 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 +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- 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-owner.mjs +40 -9
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- 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 +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- 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 +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +16 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- 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 +124 -188
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- 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-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/dist/index.mjs +58 -64
- 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 +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- 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 +17 -1
- 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 +19 -2
- package/src/tui/session/turn.mjs +1 -0
- 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
|
|
@@ -79,12 +84,15 @@ const turnStatus = (detail) =>
|
|
|
79
84
|
|
|
80
85
|
const INTERRUPTED = /^\[Request interrupted/;
|
|
81
86
|
|
|
82
|
-
const
|
|
83
|
-
typeof
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
};
|
|
88
96
|
|
|
89
97
|
/** The submissions a stored user message delivered: a turn's prompt
|
|
90
98
|
* (`checkpointId`, `submissionIds` of a merged batch) or a steered message. */
|
|
@@ -106,13 +114,21 @@ export function submissionIdsOf(message) {
|
|
|
106
114
|
export function submissionOutcome(messages, runId) {
|
|
107
115
|
const list = Array.isArray(messages) ? messages : [];
|
|
108
116
|
const start = list.findIndex((message) => submissionIdsOf(message).includes(runId));
|
|
109
|
-
|
|
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) {
|
|
110
124
|
for (let index = start; index < list.length; index += 1) {
|
|
111
125
|
const message = list[index];
|
|
112
126
|
const completion = message?.meta?.transcript?.completion;
|
|
113
127
|
if (completion) {
|
|
114
128
|
const status = turnStatus(completion);
|
|
115
|
-
|
|
129
|
+
let text = '';
|
|
130
|
+
if (status === 'failed') text = completion.detail || '';
|
|
131
|
+
else if (message.role === 'assistant') text = message.content;
|
|
116
132
|
return { status, text: messageText(text) };
|
|
117
133
|
}
|
|
118
134
|
if (index === start || message?.role !== 'user') continue;
|
|
@@ -142,7 +158,9 @@ const callArguments = (call) => {
|
|
|
142
158
|
* no task. Empty when the turn recorded none. */
|
|
143
159
|
function consumedTaskIds(message) {
|
|
144
160
|
const recorded = message?.meta?.transcript?.consumedExecutionIds;
|
|
145
|
-
|
|
161
|
+
let ids = [];
|
|
162
|
+
if (Array.isArray(recorded)) ids = recorded;
|
|
163
|
+
else if (message?.meta?.execution?.id) ids = [message.meta.execution.id];
|
|
146
164
|
return ids.map((id) => (id ? agentTaskIdOfExecution(id) : null));
|
|
147
165
|
}
|
|
148
166
|
|
|
@@ -164,7 +182,9 @@ export function latestTurnOutcome(messages, runId) {
|
|
|
164
182
|
return chain && { id: chain.id, outcome: chain.outcome, at: chain.at, ambiguous: chain.ambiguous };
|
|
165
183
|
}
|
|
166
184
|
|
|
167
|
-
/** latestTurnOutcome with what a report's wait needs besides: `
|
|
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
|
|
168
188
|
* agent tasks the chain's turns started — the only tasks proven the chain's
|
|
169
189
|
* — and `busyIsOthers`: the session's last stored turn is one outside the
|
|
170
190
|
* chain that recorded no end yet, and a stored turn took the result of every
|
|
@@ -187,6 +207,8 @@ function turnChain(messages, runId) {
|
|
|
187
207
|
// The turn the walk is in: where its prompt is stored and its submission id.
|
|
188
208
|
let turnAt = start;
|
|
189
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 }];
|
|
190
212
|
for (let index = start; index < list.length; index += 1) {
|
|
191
213
|
const message = list[index];
|
|
192
214
|
const checkpointId = index > start && message?.role === 'user' ? message.meta?.transcript?.checkpointId : '';
|
|
@@ -209,6 +231,7 @@ function turnChain(messages, runId) {
|
|
|
209
231
|
inChain = true;
|
|
210
232
|
at = turnAt;
|
|
211
233
|
id = turnId;
|
|
234
|
+
if (turns.at(-1).id !== turnId) turns.push({ at: turnAt, id: turnId });
|
|
212
235
|
} else if (!delivered.length || delivered.includes(null)) {
|
|
213
236
|
ambiguous = true;
|
|
214
237
|
}
|
|
@@ -227,14 +250,27 @@ function turnChain(messages, runId) {
|
|
|
227
250
|
if (Array.isArray(message.agentTaskIds)) for (const taskId of message.agentTaskIds) started.add(String(taskId));
|
|
228
251
|
else if (message.toolKind !== 'error') ambiguous = true;
|
|
229
252
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
+
}
|
|
233
268
|
return {
|
|
234
269
|
id,
|
|
235
|
-
outcome
|
|
270
|
+
outcome,
|
|
236
271
|
at: Number(ended?.meta?.transcript?.at) || 0,
|
|
237
272
|
ambiguous,
|
|
273
|
+
answers,
|
|
238
274
|
taskIds: started,
|
|
239
275
|
busyIsOthers: last === 'open' && [...started].every((taskId) => taken.has(taskId)),
|
|
240
276
|
};
|
|
@@ -333,7 +369,7 @@ function chainPendingWork(sessionId, taskIds, { owed, pool, agentTaskOwners, wor
|
|
|
333
369
|
/** The agent results queued for `sessionId` that no turn has taken yet (its
|
|
334
370
|
* durable pending queue: an agent's completion, a restart failure): `{ text,
|
|
335
371
|
* meta }` as the notification bus delivers them. */
|
|
336
|
-
|
|
372
|
+
async function owedAgentResults(sessionId) {
|
|
337
373
|
const [{ _getPendingMessagesForSession }, { completionIsDelivered, isCompletionNotificationEntry }] =
|
|
338
374
|
await Promise.all([
|
|
339
375
|
import('../runtime/agent/orchestrator/session/manager/pending-messages.mjs'),
|
|
@@ -399,9 +435,6 @@ const LOST_TEXT = 'The message never ran: the app restarted before its turn star
|
|
|
399
435
|
const GONE_TEXT = 'The session no longer exists.';
|
|
400
436
|
const RESUME_FAILED_TEXT =
|
|
401
437
|
'The agent task session could not continue after the app restarted: its agents’ results never reached it. Check that session.';
|
|
402
|
-
const PENDING_CAP_MS = 30 * 60_000;
|
|
403
|
-
const STILL_WORKING_TEXT =
|
|
404
|
-
'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.';
|
|
405
438
|
// Deliveries of a restarted task session's queued agent results before its
|
|
406
439
|
// report gives up as RESUME_FAILED_TEXT.
|
|
407
440
|
const OWED_WAKE_TRIES = 3;
|
|
@@ -472,27 +505,54 @@ async function lastUsedSessionRoute(sessionService) {
|
|
|
472
505
|
* without the provenance to tell (no stored turn, `ambiguous`) the whole
|
|
473
506
|
* session must be idle with none pending. `immediate` is runId's own end,
|
|
474
507
|
* when this process saw it.
|
|
475
|
-
*
|
|
476
|
-
*
|
|
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. */
|
|
477
519
|
function followTaskToFinal(
|
|
478
|
-
{ loadSession, owedResults, deliverCompletion, pendingWork, settleMs, pollMs,
|
|
479
|
-
{ 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 = '' }
|
|
480
522
|
) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
|
|
495
|
-
|
|
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;
|
|
496
556
|
}
|
|
497
557
|
let done = false;
|
|
498
558
|
let checking = false;
|
|
@@ -553,6 +613,14 @@ function followTaskToFinal(
|
|
|
553
613
|
const latest = turnChain(stored?.messages, runId);
|
|
554
614
|
// The chain's agent tasks; null (the whole session) without its provenance.
|
|
555
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;
|
|
556
624
|
// A busy session may run the chain's turn unless that turn is provably another's.
|
|
557
625
|
const busy = Boolean(runtime?.getState?.()?.busy);
|
|
558
626
|
if (busy && !(taskIds && latest.busyIsOthers)) return null;
|
|
@@ -561,18 +629,16 @@ function followTaskToFinal(
|
|
|
561
629
|
if (woke !== undefined) return woke;
|
|
562
630
|
}
|
|
563
631
|
if (await pendingWork(sessionId, { taskIds })) {
|
|
564
|
-
await
|
|
632
|
+
await reportInterim(answer);
|
|
565
633
|
return null;
|
|
566
634
|
}
|
|
567
635
|
if (!stored && !immediate) return { key: 'gone', status: 'cancelled', text: GONE_TEXT };
|
|
568
636
|
// Provenance the chain needs is missing: its answer may not be final.
|
|
569
637
|
if (latest?.ambiguous) {
|
|
570
|
-
await
|
|
638
|
+
await reportInterim(answer);
|
|
571
639
|
return null;
|
|
572
640
|
}
|
|
573
|
-
|
|
574
|
-
if (!latest?.outcome || latest.outcome === 'open') return null;
|
|
575
|
-
return { key: `${latest.id}:${latest.outcome.status}`, ...latest.outcome };
|
|
641
|
+
return answer && { key: `${answer.id}:${answer.status}`, ...answer };
|
|
576
642
|
}
|
|
577
643
|
async function check() {
|
|
578
644
|
if (done) return;
|
|
@@ -598,7 +664,7 @@ function followTaskToFinal(
|
|
|
598
664
|
arm(candidateAt + settleMs - Date.now());
|
|
599
665
|
} else {
|
|
600
666
|
stop();
|
|
601
|
-
await finish(end.status, end.text);
|
|
667
|
+
await finish(end.status, end.text, end.id);
|
|
602
668
|
}
|
|
603
669
|
} while (again && !done);
|
|
604
670
|
} finally {
|
|
@@ -756,7 +822,7 @@ async function rearmTaskWatch(
|
|
|
756
822
|
// The turn that answers: the watched submission's own, or the one it
|
|
757
823
|
// follows (a room member's turn under another submission id).
|
|
758
824
|
const turnId = watch.follows || watch.runId;
|
|
759
|
-
const finish = async (status, text = '') =>
|
|
825
|
+
const finish = async (status, text = '', answerId = '') =>
|
|
760
826
|
deliver(watch.agentId, {
|
|
761
827
|
runId: `${watch.runId}_end`,
|
|
762
828
|
taskSessionId: sessionId,
|
|
@@ -767,6 +833,7 @@ async function rearmTaskWatch(
|
|
|
767
833
|
...(watch.hop > 0 ? { hop: watch.hop } : {}),
|
|
768
834
|
...roomFrom(watch.homeSessionId),
|
|
769
835
|
watchId: watch.runId,
|
|
836
|
+
...progressOf(watch.runId, answerId),
|
|
770
837
|
...(watch.textOnly || text === GONE_TEXT || text === LOST_TEXT ? {} : await artifactFields(sessionId, turnId)),
|
|
771
838
|
});
|
|
772
839
|
if (!(await loadSession(sessionId))) return finish('cancelled', GONE_TEXT);
|
|
@@ -812,21 +879,25 @@ async function rearmTaskWatch(
|
|
|
812
879
|
runId: turnId,
|
|
813
880
|
runtime,
|
|
814
881
|
wakeOwed: true,
|
|
815
|
-
|
|
882
|
+
// The turns an earlier process already reported as progress.
|
|
883
|
+
progressedThrough: watch.progressed || '',
|
|
884
|
+
interim: (answer) =>
|
|
816
885
|
deliver(watch.agentId, {
|
|
817
|
-
runId:
|
|
886
|
+
runId: progressRunId(watch.runId, answer.id),
|
|
818
887
|
taskSessionId: sessionId,
|
|
819
888
|
title: watch.title,
|
|
820
889
|
status: 'working',
|
|
821
|
-
text,
|
|
890
|
+
text: answer.text,
|
|
891
|
+
progressWatchId: watch.runId,
|
|
892
|
+
answerId: answer.id,
|
|
822
893
|
...(watch.target === 'session' ? { target: 'session' } : {}),
|
|
823
894
|
...(watch.hop > 0 ? { hop: watch.hop } : {}),
|
|
824
895
|
...roomFrom(watch.homeSessionId),
|
|
825
896
|
}),
|
|
826
|
-
finish: async (status, text) => {
|
|
897
|
+
finish: async (status, text, answerId) => {
|
|
827
898
|
done = true;
|
|
828
899
|
unsubscribe();
|
|
829
|
-
await finish(status, text);
|
|
900
|
+
await finish(status, text, answerId);
|
|
830
901
|
},
|
|
831
902
|
});
|
|
832
903
|
} else if (end) {
|
|
@@ -845,6 +916,161 @@ async function rearmTaskWatch(
|
|
|
845
916
|
await check();
|
|
846
917
|
}
|
|
847
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
|
+
|
|
848
1074
|
export function createAgentTaskSessions({
|
|
849
1075
|
getSessionService,
|
|
850
1076
|
deliver = async (agentId, report) => (await import('./agent-home-delivery.mjs')).deliverAgentTask(agentId, report),
|
|
@@ -868,7 +1094,7 @@ export function createAgentTaskSessions({
|
|
|
868
1094
|
(await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).worktree?.newTaskDefault,
|
|
869
1095
|
modelRoute = configModelRoute,
|
|
870
1096
|
// The model menu's catalog rows, read through the calling session's runtime.
|
|
871
|
-
modelCatalog = (sessionId) => readModelCatalog(
|
|
1097
|
+
modelCatalog = (sessionId) => readModelCatalog(getSessionService(), sessionId),
|
|
872
1098
|
// The Lead (Main) route of the config — what a desktop New task starts on.
|
|
873
1099
|
leadRoute = configLeadRoute,
|
|
874
1100
|
// The model the user last used in a coding session.
|
|
@@ -899,9 +1125,6 @@ export function createAgentTaskSessions({
|
|
|
899
1125
|
owedResults = owedAgentResults,
|
|
900
1126
|
deliverCompletion = null,
|
|
901
1127
|
settleAgentResults = async () => {},
|
|
902
|
-
// Pending agent work this long after the latest answer reports that answer
|
|
903
|
-
// as still in progress (once); the final report still follows.
|
|
904
|
-
pendingCapMs = PENDING_CAP_MS,
|
|
905
1128
|
log = (line) => process.stderr.write(`mixdog task sessions: ${line}\n`),
|
|
906
1129
|
}) {
|
|
907
1130
|
/** The report fields of turn `runId`'s files; none when it produced none. */
|
|
@@ -923,44 +1146,9 @@ export function createAgentTaskSessions({
|
|
|
923
1146
|
};
|
|
924
1147
|
|
|
925
1148
|
const followToFinal = (follow) =>
|
|
926
|
-
followTaskToFinal(
|
|
927
|
-
{ loadSession, owedResults, deliverCompletion, pendingWork, settleMs, pollMs, pendingCapMs, log },
|
|
928
|
-
follow
|
|
929
|
-
);
|
|
930
|
-
|
|
931
|
-
// Sends one turn answers settle together with the same `detail` object: a
|
|
932
|
-
// send steered into the turn while it ran, or queued and taken as one batch.
|
|
933
|
-
// That turn reports once, closing every one of them.
|
|
934
|
-
const settledTurns = new WeakMap();
|
|
935
|
-
let sendCount = 0;
|
|
1149
|
+
followTaskToFinal({ loadSession, owedResults, deliverCompletion, pendingWork, settleMs, pollMs, log }, follow);
|
|
936
1150
|
|
|
937
|
-
|
|
938
|
-
* `detail` of its turn, per reporting conversation. The sends of one turn
|
|
939
|
-
* settle in the same tick; after it the earliest (the turn's own, whose
|
|
940
|
-
* record holds the whole turn) settles with all their ids and the others
|
|
941
|
-
* stand down. */
|
|
942
|
-
function joinSettlement(detail, origin, member) {
|
|
943
|
-
const key = `${origin.agentId}\0${origin.sessionId}`;
|
|
944
|
-
let groups = settledTurns.get(detail);
|
|
945
|
-
if (!groups) {
|
|
946
|
-
groups = new Map();
|
|
947
|
-
settledTurns.set(detail, groups);
|
|
948
|
-
}
|
|
949
|
-
let members = groups.get(key);
|
|
950
|
-
if (!members) {
|
|
951
|
-
members = [];
|
|
952
|
-
groups.set(key, members);
|
|
953
|
-
const settling = members;
|
|
954
|
-
void Promise.resolve()
|
|
955
|
-
.then(() => {
|
|
956
|
-
const [first, ...others] = settling.sort((a, b) => a.seq - b.seq);
|
|
957
|
-
for (const other of others) other.unwatch();
|
|
958
|
-
first.settle(settling.map((entry) => entry.id));
|
|
959
|
-
})
|
|
960
|
-
.catch((error) => log(`settling the sends of one turn failed: ${error?.message || error}`));
|
|
961
|
-
}
|
|
962
|
-
members.push(member);
|
|
963
|
-
}
|
|
1151
|
+
const settlement = createSendSettlement(log);
|
|
964
1152
|
|
|
965
1153
|
/** Submit `prompt` as the origin agent's turn; its end (and a wait for the
|
|
966
1154
|
* user in it) reports to the agent's home. Resolves on intake. `submit`
|
|
@@ -982,41 +1170,49 @@ export function createAgentTaskSessions({
|
|
|
982
1170
|
watches.remove(id).catch((error) => log(`dropping the watch of ${sessionId} failed: ${error?.message || error}`));
|
|
983
1171
|
let runtime = null;
|
|
984
1172
|
// The sends this turn's end closes: this one, plus any that its turn
|
|
985
|
-
// answered too (
|
|
1173
|
+
// answered too (createSendSettlement).
|
|
986
1174
|
let sendIds = [id];
|
|
987
1175
|
// A report that cannot be queued is retried in this process; when every try
|
|
988
1176
|
// fails the durable watch stays and restart recovery delivers it (the queue
|
|
989
1177
|
// dedupes by runId, so a retry never posts twice).
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
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
|
+
);
|
|
1015
1211
|
let unwatch = () => {};
|
|
1016
1212
|
// Set once the turn ended completed: the report waits for the final answer.
|
|
1017
1213
|
let follow = null;
|
|
1018
1214
|
let result;
|
|
1019
|
-
/** This send ends the turn `detail` for the sends `answered` (
|
|
1215
|
+
/** This send ends the turn `detail` for the sends `answered` (createSendSettlement). */
|
|
1020
1216
|
const settleTurn = (detail, answered) => {
|
|
1021
1217
|
sendIds = answered;
|
|
1022
1218
|
const status = turnStatus(detail);
|
|
@@ -1033,24 +1229,23 @@ export function createAgentTaskSessions({
|
|
|
1033
1229
|
runId: id,
|
|
1034
1230
|
runtime,
|
|
1035
1231
|
immediate: { status, text },
|
|
1036
|
-
finish: async (endStatus, endText) => {
|
|
1232
|
+
finish: async (endStatus, endText, answerId) => {
|
|
1037
1233
|
unwatch();
|
|
1038
|
-
await report(endStatus, endText);
|
|
1234
|
+
await report(endStatus, endText, { answerId });
|
|
1039
1235
|
},
|
|
1040
|
-
|
|
1236
|
+
// Each other answer of its chain: a progress update that wakes nobody.
|
|
1237
|
+
interim: (answer) => deliverReport('working', answer.text, { answerId: answer.id }),
|
|
1041
1238
|
});
|
|
1042
1239
|
follow.check();
|
|
1043
1240
|
};
|
|
1044
1241
|
try {
|
|
1045
1242
|
if (notify) {
|
|
1046
1243
|
runtime = await sessions.materializeSession(sessionId);
|
|
1047
|
-
|
|
1244
|
+
const onState = approvalWaitReporter(id, report);
|
|
1048
1245
|
unwatch =
|
|
1049
1246
|
runtime?.subscribe?.(() => {
|
|
1050
1247
|
follow?.check();
|
|
1051
|
-
|
|
1052
|
-
blocked = true;
|
|
1053
|
-
void report('blocked');
|
|
1248
|
+
onState(runtime.getState?.() || {});
|
|
1054
1249
|
}) || unwatch;
|
|
1055
1250
|
}
|
|
1056
1251
|
// A bot's send to a session that is not its own task session reaches the
|
|
@@ -1059,22 +1254,11 @@ export function createAgentTaskSessions({
|
|
|
1059
1254
|
? wrapAgentMessage(prompt, { name: origin.envelopeName, agentId: origin.agentId })
|
|
1060
1255
|
: prompt;
|
|
1061
1256
|
const take = submit ?? ((options) => sessions.submitSession({ sessionId, prompt: sent, options }));
|
|
1062
|
-
|
|
1063
|
-
sendCount += 1;
|
|
1064
|
-
const seq = sendCount;
|
|
1257
|
+
const seq = settlement.nextSeq();
|
|
1065
1258
|
result = await take({
|
|
1066
1259
|
id,
|
|
1067
1260
|
mode: 'prompt',
|
|
1068
|
-
|
|
1069
|
-
// bot's home chat (otherwise the row would read as the user's);
|
|
1070
|
-
// `hop`: its place in a bot-to-bot chain (bot-hops.mjs).
|
|
1071
|
-
transcriptMeta: {
|
|
1072
|
-
sender: 'lead',
|
|
1073
|
-
...(origin.envelopeName ? { fromAgent: origin.agentId } : {}),
|
|
1074
|
-
...(origin.hop > 0 ? { hop: origin.hop } : {}),
|
|
1075
|
-
// The bots that handed this work on, the sender last (bot-hops.mjs).
|
|
1076
|
-
...(origin.botChain?.length ? { botChain: origin.botChain } : {}),
|
|
1077
|
-
},
|
|
1261
|
+
transcriptMeta: sendTranscriptMeta(origin),
|
|
1078
1262
|
...(notify
|
|
1079
1263
|
? {
|
|
1080
1264
|
// A room answers this send in a turn under another submission
|
|
@@ -1082,7 +1266,7 @@ export function createAgentTaskSessions({
|
|
|
1082
1266
|
// short): the watch follows that turn, recorded before it runs.
|
|
1083
1267
|
follow: (submissionId) => watches.follow?.(id, submissionId),
|
|
1084
1268
|
onSettled: (detail) =>
|
|
1085
|
-
|
|
1269
|
+
settlement.join(detail, origin, {
|
|
1086
1270
|
seq,
|
|
1087
1271
|
id,
|
|
1088
1272
|
unwatch: () => unwatch(),
|
|
@@ -1101,13 +1285,7 @@ export function createAgentTaskSessions({
|
|
|
1101
1285
|
unwatch();
|
|
1102
1286
|
if (notify) await dropWatch();
|
|
1103
1287
|
}
|
|
1104
|
-
return
|
|
1105
|
-
accepted,
|
|
1106
|
-
// Names this send; its terminal report carries it back (routine.sendId).
|
|
1107
|
-
...(accepted && notify ? { sendId: id } : {}),
|
|
1108
|
-
reportsBack: accepted && notify,
|
|
1109
|
-
...(accepted && !notify ? { reportsBackReason: noReport } : {}),
|
|
1110
|
-
};
|
|
1288
|
+
return sendResult(accepted, notify, id, noReport);
|
|
1111
1289
|
}
|
|
1112
1290
|
|
|
1113
1291
|
const rearm = (watch) =>
|
|
@@ -1160,22 +1338,8 @@ export function createAgentTaskSessions({
|
|
|
1160
1338
|
attachmentParts = [],
|
|
1161
1339
|
} = {}
|
|
1162
1340
|
) {
|
|
1163
|
-
|
|
1164
|
-
const
|
|
1165
|
-
if (!brief) throw new Error('session: brief is required for action=create');
|
|
1166
|
-
const location = taskLocation(args.worktree);
|
|
1167
|
-
if (location.mode === 'worktree') assertWorktreeMutationAllowed(callerScope, 'session: create with a worktree');
|
|
1168
|
-
const projects = await listProjects();
|
|
1169
|
-
const where = resolveTaskProject(args.project, { projects, cwd: callerCwd || process.cwd() });
|
|
1170
|
-
// An explicit worktree is never silently dropped: it needs a registered Project.
|
|
1171
|
-
const registered = where.project && projects.some((entry) => samePath(entry.path, where.project.path));
|
|
1172
|
-
if (location.mode === 'worktree' && !registered) {
|
|
1173
|
-
throw taskWorktreeFailure(
|
|
1174
|
-
worktreeError('WORKTREE_PROJECT_UNKNOWN', 'A worktree needs a registered Project; run list-projects.', {
|
|
1175
|
-
projectPath: where.project?.path ?? null,
|
|
1176
|
-
})
|
|
1177
|
-
);
|
|
1178
|
-
}
|
|
1341
|
+
const { brief, location } = createRequest(args, callerAgentId, callerScope);
|
|
1342
|
+
const where = createWhere(args, await listProjects(), callerCwd, location);
|
|
1179
1343
|
const requestId = `atask_${randomUUID()}`;
|
|
1180
1344
|
const title = (clean(args.title) || clean(brief.split('\n')[0])).slice(0, TITLE_LIMIT);
|
|
1181
1345
|
const taskOrigin = {
|
|
@@ -1243,25 +1407,10 @@ export function createAgentTaskSessions({
|
|
|
1243
1407
|
}
|
|
1244
1408
|
if (dropFailure) log(`dropping ${sessionId} failed: ${dropFailure?.message || dropFailure}`);
|
|
1245
1409
|
if (!dropFailure) throw error;
|
|
1246
|
-
|
|
1247
|
-
throw Object.assign(
|
|
1248
|
-
new Error(
|
|
1249
|
-
`${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}.`
|
|
1250
|
-
),
|
|
1251
|
-
{
|
|
1252
|
-
...(typeof error?.code === 'string' ? { code: error.code } : {}),
|
|
1253
|
-
...(error?.details ? { details: error.details } : {}),
|
|
1254
|
-
orphanSessionId: sessionId,
|
|
1255
|
-
cause: error,
|
|
1256
|
-
}
|
|
1257
|
-
);
|
|
1410
|
+
throw orphanSessionError(error, sessionId, dropFailure);
|
|
1258
1411
|
}
|
|
1259
1412
|
}
|
|
1260
|
-
|
|
1261
|
-
let started = () => {};
|
|
1262
|
-
const ready = new Promise((resolveReady) => {
|
|
1263
|
-
started = resolveReady;
|
|
1264
|
-
});
|
|
1413
|
+
const { ready, started } = startedGate();
|
|
1265
1414
|
// The brief carries the files the agent forwarded (workspace-tools.mjs).
|
|
1266
1415
|
const prompt = handoffContent(brief, attachmentParts);
|
|
1267
1416
|
const botChain = botChainOf(callerBotChain);
|