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
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rows posted into an agent's home thread (`sess_home_<owner>`) from outside
|
|
3
3
|
* its turns: held requests of unattended runs, reports of task sessions the
|
|
4
|
-
* agent created,
|
|
5
|
-
*
|
|
4
|
+
* agent created, the agent's own reply of a turn it ran in a hidden session
|
|
5
|
+
* (Mixy's heartbeat), and routine results left by runs from before routines
|
|
6
|
+
* ran as turns in the thread itself (routine-home-turn.mjs).
|
|
6
7
|
*
|
|
7
8
|
* Delivery is durable. The finished run is first ENQUEUED in a persisted
|
|
8
9
|
* per-home queue (`routine-results.json`), keyed by the run id, so a retried
|
|
9
10
|
* enqueue adds nothing and a crash between posting and dequeuing never posts
|
|
10
11
|
* twice (the posted message id is derived from the run id). One drainer per
|
|
11
12
|
* home appends every queued result FIFO once the thread is idle; posting never
|
|
12
|
-
* starts a turn.
|
|
13
|
+
* starts a turn. Rows that wake nobody (a task session's progress updates,
|
|
14
|
+
* among others) also post right before any turn of the thread starts
|
|
15
|
+
* (postBeforeTurn), so that turn reads them ahead of its prompt. A home thread that does not exist yet keeps its results
|
|
13
16
|
* queued until it does; a deleted owner's results move to Mixy's home. Each
|
|
14
17
|
* home keeps at most ROUTINE_LIMITS.homeQueueLimit results (the oldest drop
|
|
15
18
|
* behind one "n omitted" notice), and posted run ids stay in a ledger so a
|
|
@@ -57,6 +60,7 @@ import {
|
|
|
57
60
|
routineSummary,
|
|
58
61
|
} from '../runtime/agent/orchestrator/session/routine-result.mjs';
|
|
59
62
|
import { readJsonState, updateJsonAtomic } from '../runtime/shared/atomic-file.mjs';
|
|
63
|
+
import { endHandoff, noteHandoff } from '../runtime/shared/delegated-approval-host.mjs';
|
|
60
64
|
import { ROUTINE_LIMITS } from '../runtime/shared/routine-limits.mjs';
|
|
61
65
|
import { submissionIdsOf, submissionOutcome } from './agent-task-sessions.mjs';
|
|
62
66
|
import {
|
|
@@ -64,22 +68,50 @@ import {
|
|
|
64
68
|
heldRequestNotice,
|
|
65
69
|
} from '../runtime/agent/orchestrator/session/human-gate/held-message.mjs';
|
|
66
70
|
|
|
71
|
+
/** `message` marked as posted outside any turn (`meta.transcript.posted`):
|
|
72
|
+
* its row is a turn of its own, never part of the rows around it
|
|
73
|
+
* (transcript-turn-keys.mjs). */
|
|
74
|
+
function postedMessage(message) {
|
|
75
|
+
return { ...message, meta: { ...message.meta, transcript: { ...message.meta?.transcript, posted: true } } };
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
/** A queued item's stored message and live notice: a routine result, or a
|
|
68
79
|
* held request of an unattended run (session-runtime/held-requests.mjs). */
|
|
69
80
|
function deliveredRow(item, messageId) {
|
|
81
|
+
const row = queuedRow(item, messageId);
|
|
82
|
+
return { ...row, message: postedMessage(row.message) };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** A task report past the round-trip cap with another agent's own thread:
|
|
86
|
+
* each wake could answer the other agent again without end. */
|
|
87
|
+
const taskWakeCapped = (item) =>
|
|
88
|
+
(isAgentHomeSessionId(item.taskSessionId) || isRoomThreadSessionId(item.taskSessionId)) &&
|
|
89
|
+
(item.round || 0) > TASK_LOOP_CAP;
|
|
90
|
+
|
|
91
|
+
/** Whether queued `item` wakes the agent once posted: a task turn's end (not
|
|
92
|
+
* its start, a wait for the user or a progress update), unless capped. */
|
|
93
|
+
const wakesAgent = (item) => item.kind === 'agent-task' && item.roundTrip === true && !taskWakeCapped(item);
|
|
94
|
+
|
|
95
|
+
const MIXY_HOME = agentHomeSessionId(MIXY_AGENT_ID);
|
|
96
|
+
/** A heartbeat report: Mixy's own reply from a check turn (mixy-heartbeat.mjs
|
|
97
|
+
* wakeMixy, its only sender of agent messages). */
|
|
98
|
+
const isHeartbeatReport = (item) => item?.kind === 'agent-message';
|
|
99
|
+
|
|
100
|
+
function queuedRow(item, messageId) {
|
|
101
|
+
// The agent's own words from a turn it ran elsewhere (mixy-heartbeat.mjs):
|
|
102
|
+
// one ordinary reply of the agent in its thread.
|
|
103
|
+
if (item.kind === 'agent-message') {
|
|
104
|
+
const message = { role: 'assistant', content: item.text, meta: { transcript: { at: item.at, messageId } } };
|
|
105
|
+
return { message, notice: { text: item.text, tone: 'info', assistant: true } };
|
|
106
|
+
}
|
|
70
107
|
if (item.kind === 'held-request') {
|
|
71
108
|
const message = heldRequestMessage({ ...item.held, at: item.at, messageId });
|
|
72
109
|
return { message, notice: heldRequestNotice(message) };
|
|
73
110
|
}
|
|
74
111
|
if (item.kind === 'agent-task') {
|
|
75
|
-
|
|
76
|
-
// except past the round-trip cap with another agent's own thread, where
|
|
77
|
-
// each wake could answer the other agent again without end.
|
|
78
|
-
const capped =
|
|
79
|
-
(isAgentHomeSessionId(item.taskSessionId) || isRoomThreadSessionId(item.taskSessionId)) &&
|
|
80
|
-
(item.round || 0) > TASK_LOOP_CAP;
|
|
112
|
+
const capped = taskWakeCapped(item);
|
|
81
113
|
const message = agentTaskMessage({ ...item, messageId, ...(capped ? { woken: false } : {}) });
|
|
82
|
-
return { message, notice: routineResultNotice(message), wake: item
|
|
114
|
+
return { message, notice: routineResultNotice(message), wake: wakesAgent(item) };
|
|
83
115
|
}
|
|
84
116
|
const message = routineResultMessage({ ...item, messageId });
|
|
85
117
|
return { message, notice: routineResultNotice(message) };
|
|
@@ -124,9 +156,11 @@ export function scheduleOwnerAgentId(schedule) {
|
|
|
124
156
|
}
|
|
125
157
|
|
|
126
158
|
let liveSurface = null;
|
|
127
|
-
/** `{ busy(sessionId), notice(sessionId, notice),
|
|
128
|
-
*
|
|
129
|
-
*
|
|
159
|
+
/** `{ busy(sessionId), notice(sessionId, notice), react(sessionId, messageId,
|
|
160
|
+
* reactions), wake(sessionId, prompt, { id }), submit(sessionId, prompt,
|
|
161
|
+
* options), abort(sessionId) }` from the session service; `react` shows a
|
|
162
|
+
* message's new reactions in the runtime hosting it (message-reactions.mjs),
|
|
163
|
+
* `wake` starts one hidden turn with submission `id` (resolves
|
|
130
164
|
* false when it was refused, else `{ settled }`: a promise of the turn's end
|
|
131
165
|
* status, 'done' when it finished), `submit` queues a routine turn
|
|
132
166
|
* (resolves whether it was accepted), `abort` stops the running turn. */
|
|
@@ -143,14 +177,14 @@ export function agentHomeLiveSurface() {
|
|
|
143
177
|
// even into a thread that was reset ("대화 새로 시작") since.
|
|
144
178
|
const DELIVERED_LEDGER = 500;
|
|
145
179
|
// A target's round-trip count left idle this long starts over.
|
|
146
|
-
|
|
180
|
+
const ROUND_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
147
181
|
// Per home: round-trip targets counted (least recently counted drop first),
|
|
148
182
|
// reporting turns watched at once, and posted results owed a wake.
|
|
149
183
|
export const QUEUE_CAPS = Object.freeze({ roundTargets: 100, watches: 200, wakeRunIds: 100 });
|
|
150
184
|
// Waits before the 2nd, 3rd and 4th try of a relay (wake) turn that did not finish.
|
|
151
|
-
|
|
185
|
+
const WAKE_RETRY_DELAYS_MS = Object.freeze([30_000, 120_000, 600_000]);
|
|
152
186
|
// Refused wake submissions of one batch, a minute apart, before it is given up.
|
|
153
|
-
|
|
187
|
+
const WAKE_REFUSAL_LIMIT = 10;
|
|
154
188
|
|
|
155
189
|
/** `homeId`'s wake obligations in `wakes`: posted task results not in a batch
|
|
156
190
|
* yet (`owed`), those past the cap collapsed into one marker (`overflow`:
|
|
@@ -255,6 +289,25 @@ function withDequeued(doc, homeId, runIds, omitted) {
|
|
|
255
289
|
return { ...doc, homes, omitted: omittedNow, delivered };
|
|
256
290
|
}
|
|
257
291
|
|
|
292
|
+
/** `{ doc, item }`: task report `item` for `homeId` and the `doc` it joins,
|
|
293
|
+
* so that a final report never repeats progress update `progressRunId` that
|
|
294
|
+
* carried the same answer. Posted (`posted`: the thread holds it, or the
|
|
295
|
+
* ledger says so), the final report points to it instead of repeating its
|
|
296
|
+
* text (the row's one-line summary stays); only queued, that update is
|
|
297
|
+
* dropped and the final report carries the answer in its place. */
|
|
298
|
+
function withoutRepeatedProgress(doc, homeId, item, posted) {
|
|
299
|
+
const { progressRunId, ...report } = item;
|
|
300
|
+
if (!progressRunId) return { doc, item };
|
|
301
|
+
if (posted || (doc.delivered[homeId] || []).includes(progressRunId)) {
|
|
302
|
+
const { text: _text, textChars: _textChars, ...rest } = report;
|
|
303
|
+
return { doc, item: { ...rest, text: '', answerAbove: true } };
|
|
304
|
+
}
|
|
305
|
+
const queued = doc.homes[homeId] || [];
|
|
306
|
+
if (!queued.some((row) => row.runId === progressRunId)) return { doc, item: report };
|
|
307
|
+
const homes = { ...doc.homes, [homeId]: queued.filter((row) => row.runId !== progressRunId) };
|
|
308
|
+
return { doc: { ...doc, homes }, item: report };
|
|
309
|
+
}
|
|
310
|
+
|
|
258
311
|
/** The persisted queue at `filePath`: `{ homes: { [homeSessionId]: item[] },
|
|
259
312
|
* omitted: { [homeSessionId]: { id, count } }, delivered: { [homeSessionId]:
|
|
260
313
|
* runId[] } }`. A home keeps at most `limit` items: past it the oldest drop,
|
|
@@ -290,16 +343,23 @@ export function createRoutineResultQueue(
|
|
|
290
343
|
}
|
|
291
344
|
/** Read-modify-write under the file lock (`mutator(doc)` returns the next
|
|
292
345
|
* doc, or nothing to keep the file); every write also retires stale and
|
|
293
|
-
* surplus round counts.
|
|
294
|
-
|
|
295
|
-
|
|
346
|
+
* surplus round counts. The watches a write added or removed, by any path
|
|
347
|
+
* (a report, a turn never taken, a cap eviction, a pruned home), start or
|
|
348
|
+
* end their hand-offs (delegated-approval-host.mjs). */
|
|
349
|
+
async function update(mutator) {
|
|
350
|
+
let watches = null;
|
|
351
|
+
const result = await updateJsonAtomic(
|
|
296
352
|
filePath,
|
|
297
353
|
(raw) => {
|
|
298
|
-
const
|
|
354
|
+
const doc = parts(raw);
|
|
355
|
+
const next = mutator(doc);
|
|
356
|
+
watches = { before: doc.watches, after: next ? section(next, 'watches') : doc.watches };
|
|
299
357
|
return next ? { ...next, rounds: tidyRounds(next.rounds) } : undefined;
|
|
300
358
|
},
|
|
301
359
|
{ backup: true }
|
|
302
360
|
);
|
|
361
|
+
if (watches) syncHandoffs(watches.before, watches.after);
|
|
362
|
+
return result;
|
|
303
363
|
}
|
|
304
364
|
const appended = (doc, homeId, items, omittedCount = 0) => appendedItems(doc, homeId, items, limit, omittedCount);
|
|
305
365
|
return {
|
|
@@ -413,10 +473,15 @@ export function createRoutineResultQueue(
|
|
|
413
473
|
async omitted(homeId) {
|
|
414
474
|
return (await read()).omitted[homeId] || null;
|
|
415
475
|
},
|
|
416
|
-
/** Queue `item` for `homeId`; `unwatch` drops
|
|
417
|
-
* also when the item was queued or posted before.
|
|
418
|
-
|
|
419
|
-
|
|
476
|
+
/** Queue `item` for `homeId`; `unwatch` (run ids) drops those watches in the
|
|
477
|
+
* same write, also when the item was queued or posted before.
|
|
478
|
+
* `progressPosted`: the thread holds the progress update a final report
|
|
479
|
+
* names (withoutRepeatedProgress). `progressed` (`{ runId, turnId }`):
|
|
480
|
+
* watch `runId` reported turn `turnId` as progress — its durable mark,
|
|
481
|
+
* stored in the same write (agent-task-sessions.mjs progressedThrough). */
|
|
482
|
+
async add(homeId, queued, { unwatch = [], progressPosted = false, progressed = null } = {}) {
|
|
483
|
+
await update((stored) => {
|
|
484
|
+
const { doc, item } = withoutRepeatedProgress(stored, homeId, queued, progressPosted);
|
|
420
485
|
let next = null;
|
|
421
486
|
if (item.roundTrip !== true) next = appended(doc, homeId, [item]);
|
|
422
487
|
else {
|
|
@@ -434,10 +499,19 @@ export function createRoutineResultQueue(
|
|
|
434
499
|
next = { ...next, rounds: { ...doc.rounds, [homeId]: counted } };
|
|
435
500
|
}
|
|
436
501
|
}
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
502
|
+
let watches = doc.watches;
|
|
503
|
+
const marked = progressed?.turnId && watches[progressed.runId];
|
|
504
|
+
if (marked && marked.progressed !== progressed.turnId) {
|
|
505
|
+
watches = { ...watches, [progressed.runId]: { ...marked, progressed: progressed.turnId } };
|
|
506
|
+
}
|
|
507
|
+
const dropped = unwatch.filter((runId) => watches[runId]);
|
|
508
|
+
if (dropped.length) {
|
|
509
|
+
watches = { ...watches };
|
|
510
|
+
for (const runId of dropped) delete watches[runId];
|
|
511
|
+
}
|
|
512
|
+
const base = next || doc;
|
|
513
|
+
if (watches === doc.watches) return base === stored ? undefined : base;
|
|
514
|
+
return { ...base, watches };
|
|
441
515
|
});
|
|
442
516
|
},
|
|
443
517
|
/** Keep `watch` ({ runId, agentId, homeSessionId, taskSessionId, title,
|
|
@@ -459,9 +533,12 @@ export function createRoutineResultQueue(
|
|
|
459
533
|
});
|
|
460
534
|
},
|
|
461
535
|
watchCap: caps.watches,
|
|
462
|
-
/** Every stored watch, oldest first
|
|
536
|
+
/** Every stored watch, oldest first; each is a live hand-off (after a
|
|
537
|
+
* restart, this read brings them back). */
|
|
463
538
|
async watches() {
|
|
464
|
-
|
|
539
|
+
const watches = Object.values((await read()).watches);
|
|
540
|
+
for (const watch of watches) noteHandoff(watch ?? {});
|
|
541
|
+
return watches;
|
|
465
542
|
},
|
|
466
543
|
/** The watch of `runId` follows the turn submitted as `submissionId`: a
|
|
467
544
|
* room member's one turn answering several sends, or a fresh turn
|
|
@@ -527,7 +604,7 @@ const messageIdOf = (runId) => `routine_${runId}`;
|
|
|
527
604
|
* or false (logged) when the surface threw. */
|
|
528
605
|
function noticeLive(live, log, homeId, notice) {
|
|
529
606
|
try {
|
|
530
|
-
return live()?.notice?.(homeId, notice);
|
|
607
|
+
return live()?.notice?.(homeId, { ...notice, posted: true });
|
|
531
608
|
} catch (error) {
|
|
532
609
|
log(`live notice in ${homeId} failed: ${error?.message || error}`);
|
|
533
610
|
return false;
|
|
@@ -561,6 +638,13 @@ function routineResultItem(row, result) {
|
|
|
561
638
|
};
|
|
562
639
|
}
|
|
563
640
|
|
|
641
|
+
/** End the hand-off of each watch in `before` and not `after`, and start
|
|
642
|
+
* each one `after` added (both keyed by run id). */
|
|
643
|
+
function syncHandoffs(before, after) {
|
|
644
|
+
for (const runId of Object.keys(before)) if (!after[runId]) endHandoff(runId);
|
|
645
|
+
for (const [runId, watch] of Object.entries(after)) if (!before[runId]) noteHandoff(watch ?? {});
|
|
646
|
+
}
|
|
647
|
+
|
|
564
648
|
/** The run ids of `watches` a home at its watch `cap` gives up before
|
|
565
649
|
* storing `watch`: its oldest whose target sessions are proven deleted. */
|
|
566
650
|
function watchesToDrop(watches, watch, cap, sessionDeleted) {
|
|
@@ -584,8 +668,24 @@ function homeProvenDeleted(homeId, agentPresence, roomPresence) {
|
|
|
584
668
|
return Boolean(roomId) && roomPresence(roomId) === 'deleted';
|
|
585
669
|
}
|
|
586
670
|
|
|
671
|
+
// Task report statuses that post a row and wake nobody.
|
|
672
|
+
const QUIET_TASK_STATUSES = new Set(['started', 'blocked', 'working']);
|
|
673
|
+
|
|
587
674
|
/** The queued item of one task report (enqueueTask). */
|
|
588
|
-
function taskReportItem({
|
|
675
|
+
function taskReportItem({
|
|
676
|
+
runId,
|
|
677
|
+
taskSessionId,
|
|
678
|
+
title,
|
|
679
|
+
status,
|
|
680
|
+
text = '',
|
|
681
|
+
target,
|
|
682
|
+
files = [],
|
|
683
|
+
omitted = 0,
|
|
684
|
+
hop = 0,
|
|
685
|
+
watchId = '',
|
|
686
|
+
watchIds = [],
|
|
687
|
+
progressRunId = '',
|
|
688
|
+
}) {
|
|
589
689
|
// The model-facing reply keeps its lines up to REPLY_LIMIT; the row's
|
|
590
690
|
// one-line summary is derived from it (agentTaskMessage).
|
|
591
691
|
const reply = boundedReply(text);
|
|
@@ -601,9 +701,19 @@ function taskReportItem({ runId, taskSessionId, title, status, text = '', target
|
|
|
601
701
|
...(reply.chars > reply.text.length ? { textChars: reply.chars } : {}),
|
|
602
702
|
// Any session other than a task session the agent created.
|
|
603
703
|
...(target === 'session' ? { target } : {}),
|
|
704
|
+
// The send this report ends (the `sendId` the send's result named).
|
|
705
|
+
...(watchId ? { sendId: String(watchId) } : {}),
|
|
706
|
+
// Every send this one turn answered (sends folded into a running turn
|
|
707
|
+
// report once); `sendId` is the first of them.
|
|
708
|
+
...(watchIds.length > 1 ? { sendIds: watchIds.map(String) } : {}),
|
|
604
709
|
// The files the turn produced (handoff-attachments.mjs collectTurnArtifacts).
|
|
605
710
|
...(Array.isArray(files) && files.length ? { files, ...(omitted > 0 ? { omitted } : {}) } : {}),
|
|
606
|
-
|
|
711
|
+
// The progress update that carried this final report's answer, if one
|
|
712
|
+
// did: the queue keeps the answer from being repeated (withoutRepeatedProgress).
|
|
713
|
+
...(progressRunId ? { progressRunId: String(progressRunId) } : {}),
|
|
714
|
+
// Only a turn's end wakes the agent and counts a round trip: not its
|
|
715
|
+
// start, a wait for the user, or a progress update (status 'working').
|
|
716
|
+
roundTrip: !QUIET_TASK_STATUSES.has(status),
|
|
607
717
|
...(hop > 0 ? { hop } : {}),
|
|
608
718
|
at: Date.now(),
|
|
609
719
|
};
|
|
@@ -617,7 +727,7 @@ function postQueuedRows(session, items, omitted) {
|
|
|
617
727
|
const fresh = [];
|
|
618
728
|
const omittedId = omitted ? messageIdOf(omitted.id) : '';
|
|
619
729
|
if (omitted && !posted.has(omittedId)) {
|
|
620
|
-
const message = routineOmittedMessage({ count: omitted.count, messageId: omittedId });
|
|
730
|
+
const message = postedMessage(routineOmittedMessage({ count: omitted.count, messageId: omittedId }));
|
|
621
731
|
session.messages.push(message);
|
|
622
732
|
fresh.push({ message, notice: routineResultNotice(message) });
|
|
623
733
|
}
|
|
@@ -689,15 +799,17 @@ function createHomeRelay({
|
|
|
689
799
|
log(`the relay turn in ${homeId} ${why}; its results stay posted unrelayed`);
|
|
690
800
|
const messageId = messageIdOf(`relay_${batch.id}`);
|
|
691
801
|
if (!session.messages.some((message) => message?.meta?.transcript?.messageId === messageId)) {
|
|
692
|
-
const message =
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
802
|
+
const message = postedMessage(
|
|
803
|
+
agentTaskRelayFailedMessage({
|
|
804
|
+
count: batch.runIds.length + (batch.overflow?.count || 0),
|
|
805
|
+
tries,
|
|
806
|
+
messageId,
|
|
807
|
+
})
|
|
808
|
+
);
|
|
697
809
|
session.messages.push(message);
|
|
698
810
|
session.updatedAt = Date.now();
|
|
699
811
|
await saveSession(session);
|
|
700
|
-
noticeLive(live, log, homeId, { ...routineResultNotice(message), messageId });
|
|
812
|
+
noticeLive(live, log, homeId, { ...routineResultNotice(message), messageId, at: message.meta.transcript.at });
|
|
701
813
|
}
|
|
702
814
|
await queue.woke(homeId, batch.id);
|
|
703
815
|
return true;
|
|
@@ -780,6 +892,200 @@ function createHomeRelay({
|
|
|
780
892
|
return { wakeOwed };
|
|
781
893
|
}
|
|
782
894
|
|
|
895
|
+
/** Whether home thread `sessionId` is busy: a turn in flight in this process,
|
|
896
|
+
* or the `live()` surface says so (false when it threw). */
|
|
897
|
+
const homeBusy = (turnInFlight, live) => (sessionId) => {
|
|
898
|
+
if (turnInFlight(sessionId)) return true;
|
|
899
|
+
try {
|
|
900
|
+
return live()?.busy?.(sessionId) === true;
|
|
901
|
+
} catch {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
/** `later(homeId, ms)`: drain `homeId` again after `ms`, one retry timer per
|
|
907
|
+
* home armed at a time. */
|
|
908
|
+
function createRetryTimer(retryLater, drain) {
|
|
909
|
+
const waiting = new Set(); // homes with a retry timer armed
|
|
910
|
+
return function later(homeId, ms) {
|
|
911
|
+
if (waiting.has(homeId)) return;
|
|
912
|
+
waiting.add(homeId);
|
|
913
|
+
retryLater(() => {
|
|
914
|
+
waiting.delete(homeId);
|
|
915
|
+
void drain(homeId);
|
|
916
|
+
}, ms);
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/** The held requests shown live in a busy thread. A held request waits on the
|
|
921
|
+
* user, possibly raised by the very turn that keeps its thread busy (a
|
|
922
|
+
* routine turn): `show` shows it live at once; the stored row (same
|
|
923
|
+
* messageId) replaces it once the thread is idle, and `forget` lets a later
|
|
924
|
+
* one show again. */
|
|
925
|
+
function createHeldLiveDisplay(live, log) {
|
|
926
|
+
const shownLive = new Set(); // held-request message ids shown live in a busy thread
|
|
927
|
+
return {
|
|
928
|
+
show(homeId, items) {
|
|
929
|
+
for (const item of items) {
|
|
930
|
+
if (item.kind !== 'held-request') continue;
|
|
931
|
+
const messageId = messageIdOf(item.runId);
|
|
932
|
+
if (shownLive.has(messageId)) continue;
|
|
933
|
+
shownLive.add(messageId);
|
|
934
|
+
noticeLive(live, log, homeId, { ...deliveredRow(item, messageId).notice, messageId, at: item.at });
|
|
935
|
+
}
|
|
936
|
+
},
|
|
937
|
+
forget(items) {
|
|
938
|
+
for (const item of items) shownLive.delete(messageIdOf(item.runId));
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/** The queues a drain drops unposted instead of retrying them. */
|
|
944
|
+
function createQueueDrops({ queue, log, homeAgent, homeDeleted, sessionDeleted }) {
|
|
945
|
+
/** A room thread proven gone: its queued rows are dropped, never retried. */
|
|
946
|
+
async function dropGoneRoomThread(homeId, items, omitted) {
|
|
947
|
+
if (!isRoomThreadSessionId(homeId) || !(homeDeleted(homeId) || sessionDeleted(homeId))) return false;
|
|
948
|
+
log(`${items.length} task report(s) for the deleted ${homeId} are dropped`);
|
|
949
|
+
await dropQueued(queue, homeId, items, omitted);
|
|
950
|
+
return true;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/** A room log's queued rows: task reports go to member sessions now, so what
|
|
954
|
+
* an earlier version left under the log's id is dropped, never posted. */
|
|
955
|
+
async function dropRoomLogQueue(logId, items, omitted) {
|
|
956
|
+
log(`${items.length} legacy task report(s) queued for the room log ${logId} are dropped`);
|
|
957
|
+
await dropQueued(queue, logId, items, omitted);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/** A missing home of an agent that can never have one (no bot): its queued
|
|
961
|
+
* rows are dropped with a log line, never retried. */
|
|
962
|
+
async function dropHomeless(homeId, items, omitted) {
|
|
963
|
+
const agentId = agentIdOfHomeSession(homeId);
|
|
964
|
+
if (!agentId || agentId === MIXY_AGENT_ID || homeAgent(agentId) !== 'none') return false;
|
|
965
|
+
log(`${items.length} result(s) for ${homeId} are dropped: ${agentId} is not a bot, so it has no chat`);
|
|
966
|
+
await dropQueued(queue, homeId, items, omitted);
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
return { dropGoneRoomThread, dropRoomLogQueue, dropHomeless };
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/** The durable intake of a home: each enqueue persists its row for the home
|
|
974
|
+
* thread, then starts draining it, and resolves once it is queued. */
|
|
975
|
+
function createHomeEnqueue(queue, drain, { postingFor, postedIn }) {
|
|
976
|
+
/** Persist `result` ({ runId, kind, ok, text, runSessionId }) of a schedule
|
|
977
|
+
* or webhook `row` for its owner's home, then start draining it. Resolves
|
|
978
|
+
* once it is queued. */
|
|
979
|
+
async function enqueue(row, result) {
|
|
980
|
+
const homeId = agentHomeSessionId(scheduleOwnerAgentId(row));
|
|
981
|
+
if (!result?.runId) throw new Error('routine result: runId required');
|
|
982
|
+
await queue.add(homeId, routineResultItem(row, result));
|
|
983
|
+
void drain(homeId);
|
|
984
|
+
return homeId;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/** Persist one held-request row (`held`: normalizeHeldRequest fields) for
|
|
988
|
+
* `agentId`'s home thread, then start draining it. Each status posts once. */
|
|
989
|
+
async function enqueueHeld(agentId, held) {
|
|
990
|
+
const homeId = agentHomeSessionId(agentId) || agentHomeSessionId(MIXY_AGENT_ID);
|
|
991
|
+
if (!held?.id) throw new Error('held request: id required');
|
|
992
|
+
await queue.add(homeId, {
|
|
993
|
+
runId: `${held.id}_${held.status || 'pending'}`,
|
|
994
|
+
kind: 'held-request',
|
|
995
|
+
held,
|
|
996
|
+
at: Date.now(),
|
|
997
|
+
});
|
|
998
|
+
void drain(homeId);
|
|
999
|
+
return homeId;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/** Persist one report of a coding task session `agentId` created (session
|
|
1003
|
+
* create) for its home thread, then start draining it. `status` 'started'
|
|
1004
|
+
* and 'blocked' only post a line; a turn's end also wakes the agent. */
|
|
1005
|
+
async function enqueueTask(agentId, report) {
|
|
1006
|
+
const { runId, taskSessionId, watchId = '', watchIds = [], from = '' } = report;
|
|
1007
|
+
const homeId = agentHomeSessionId(agentId) || agentHomeSessionId(MIXY_AGENT_ID);
|
|
1008
|
+
if (!runId || !taskSessionId) throw new Error('task report: runId and taskSessionId required');
|
|
1009
|
+
const item = taskReportItem(report);
|
|
1010
|
+
const unwatch = [...new Set([watchId, ...watchIds].filter(Boolean).map(String))];
|
|
1011
|
+
// A room member's own work reports to its member session, never its home.
|
|
1012
|
+
const target = roomMemberOfSession(from)?.agentId === agentId ? from : homeId;
|
|
1013
|
+
if (item.progressRunId) {
|
|
1014
|
+
// Whether its answer already went out as that progress update is
|
|
1015
|
+
// decided in the thread's posting turn, against the thread itself: a
|
|
1016
|
+
// row being posted (or posted before a crash, not dequeued yet) counts
|
|
1017
|
+
// as posted, never as still queued.
|
|
1018
|
+
await postingFor(target, async () =>
|
|
1019
|
+
queue.add(target, item, { unwatch, progressPosted: postedIn(target, item.progressRunId) })
|
|
1020
|
+
);
|
|
1021
|
+
} else {
|
|
1022
|
+
// A progress update moves its send's watch mark (`progressed`) to the
|
|
1023
|
+
// turn it carries, in the same write: a restart never reports it again.
|
|
1024
|
+
const progressed = report.progressWatchId
|
|
1025
|
+
? { runId: String(report.progressWatchId), turnId: String(report.answerId || '') }
|
|
1026
|
+
: null;
|
|
1027
|
+
await queue.add(target, item, { unwatch, progressed });
|
|
1028
|
+
}
|
|
1029
|
+
void drain(target);
|
|
1030
|
+
return target;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/** Persist `text`, the agent's own reply from turn `runId` it ran outside
|
|
1034
|
+
* its thread, for `agentId`'s home thread, then start draining it. It posts
|
|
1035
|
+
* once, as the agent's message; no turn follows. */
|
|
1036
|
+
async function enqueueMessage(agentId, { runId, text }) {
|
|
1037
|
+
const homeId = agentHomeSessionId(agentId) || agentHomeSessionId(MIXY_AGENT_ID);
|
|
1038
|
+
if (!runId || !String(text || '').trim()) throw new Error('agent message: runId and text required');
|
|
1039
|
+
await queue.add(homeId, { runId: String(runId), kind: 'agent-message', text: String(text).trim(), at: Date.now() });
|
|
1040
|
+
void drain(homeId);
|
|
1041
|
+
return homeId;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return { enqueue, enqueueHeld, enqueueTask, enqueueMessage };
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/** The queue's bookkeeping across processes: report watches and the restart
|
|
1048
|
+
* recovery that retires deleted homes and drains what was left queued. */
|
|
1049
|
+
function createHomeBookkeeping({ queue, log, homeDeleted, sessionDeleted, drain }) {
|
|
1050
|
+
/** Retire the bookkeeping of deleted homes (and stale round counts). */
|
|
1051
|
+
async function prune() {
|
|
1052
|
+
await queue.prune((await queue.bookkeptHomes()).filter(homeDeleted));
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/** Store `watch`; a home at its watch cap first gives up its oldest watches
|
|
1056
|
+
* whose target sessions are proven deleted, and refuses the watch (so the
|
|
1057
|
+
* send claims no report) when that frees too few. */
|
|
1058
|
+
async function watchTask(watch) {
|
|
1059
|
+
const drop = watchesToDrop(await queue.watches(), watch, queue.watchCap, sessionDeleted);
|
|
1060
|
+
if (drop.length) {
|
|
1061
|
+
log(`${watch.homeSessionId} is at its watch cap: dropped ${drop.length} watch(es) of deleted sessions`);
|
|
1062
|
+
}
|
|
1063
|
+
await queue.watch(watch, { drop });
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/** Drain every queue left by an earlier process, after retiring stale bookkeeping. */
|
|
1067
|
+
async function recover() {
|
|
1068
|
+
try {
|
|
1069
|
+
await prune();
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
log(`pruning the routine results failed: ${error?.message || error}`);
|
|
1072
|
+
}
|
|
1073
|
+
// The stored watches name the hand-offs an earlier process left open:
|
|
1074
|
+
// reading them notes each one now, so a target that asks again after the
|
|
1075
|
+
// restart reaches the chat that handed it the work.
|
|
1076
|
+
try {
|
|
1077
|
+
await queue.watches();
|
|
1078
|
+
} catch (error) {
|
|
1079
|
+
log(`reading the report watches failed: ${error?.message || error}`);
|
|
1080
|
+
}
|
|
1081
|
+
const homes = await queue.pendingHomes();
|
|
1082
|
+
for (const homeId of homes) void drain(homeId);
|
|
1083
|
+
return homes;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
return { watchTask, recover };
|
|
1087
|
+
}
|
|
1088
|
+
|
|
783
1089
|
export function createAgentHomeDelivery({
|
|
784
1090
|
loadSession,
|
|
785
1091
|
saveSession,
|
|
@@ -801,6 +1107,9 @@ export function createAgentHomeDelivery({
|
|
|
801
1107
|
// at all. 'none' (proven: a workflow agent such as Reviewer) drops the rows
|
|
802
1108
|
// queued for its missing home instead of waiting on it forever.
|
|
803
1109
|
homeAgent = () => 'unknown',
|
|
1110
|
+
// Whether the representative bot exists (mixy-presence.mjs): while it does
|
|
1111
|
+
// not, heartbeat reports queued for its home are dropped.
|
|
1112
|
+
mixyExists = async () => true,
|
|
804
1113
|
// Waits before each new try of a relay turn that did not finish; past the
|
|
805
1114
|
// last one the results stay posted with a note and nobody is woken.
|
|
806
1115
|
wakeRetryDelaysMs = WAKE_RETRY_DELAYS_MS,
|
|
@@ -813,19 +1122,20 @@ export function createAgentHomeDelivery({
|
|
|
813
1122
|
roomRelay = (sessionId, prompt, options) => agentRoomHost().relay(sessionId, prompt, options),
|
|
814
1123
|
log = (line) => process.stderr.write(`mixdog routines: ${line}\n`),
|
|
815
1124
|
}) {
|
|
816
|
-
const busy = (
|
|
817
|
-
if (turnInFlight(sessionId)) return true;
|
|
818
|
-
try {
|
|
819
|
-
return live()?.busy?.(sessionId) === true;
|
|
820
|
-
} catch {
|
|
821
|
-
return false;
|
|
822
|
-
}
|
|
823
|
-
};
|
|
1125
|
+
const busy = homeBusy(turnInFlight, live);
|
|
824
1126
|
const draining = new Map(); // home → the running pass
|
|
825
1127
|
const again = new Set(); // homes enqueued to while their pass ran
|
|
826
|
-
const waiting = new Set(); // homes with a retry timer armed
|
|
827
|
-
const shownLive = new Set(); // held-request message ids shown live in a busy thread
|
|
828
1128
|
const waitLogged = new Set(); // missing homes whose wait was logged
|
|
1129
|
+
const later = createRetryTimer(retryLater, drain);
|
|
1130
|
+
const heldLive = createHeldLiveDisplay(live, log);
|
|
1131
|
+
const homeDeleted = (homeId) => homeProvenDeleted(homeId, agentPresence, roomPresence);
|
|
1132
|
+
const { dropGoneRoomThread, dropRoomLogQueue, dropHomeless } = createQueueDrops({
|
|
1133
|
+
queue,
|
|
1134
|
+
log,
|
|
1135
|
+
homeAgent,
|
|
1136
|
+
homeDeleted,
|
|
1137
|
+
sessionDeleted,
|
|
1138
|
+
});
|
|
829
1139
|
const relay = createHomeRelay({
|
|
830
1140
|
queue,
|
|
831
1141
|
saveSession,
|
|
@@ -840,61 +1150,123 @@ export function createAgentHomeDelivery({
|
|
|
840
1150
|
busyRetryMs,
|
|
841
1151
|
missingRetryMs,
|
|
842
1152
|
});
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1153
|
+
// Per thread, one posting turn at a time: push queued rows, save the
|
|
1154
|
+
// thread, dequeue them (the drain, the turn prelude), and a final report's
|
|
1155
|
+
// check whether its answer already went out as a progress update. No pass
|
|
1156
|
+
// ever dequeues rows another pushed but has not saved yet, and a row being
|
|
1157
|
+
// posted is never mistaken for one still queued.
|
|
1158
|
+
const postingChains = new Map();
|
|
1159
|
+
function postingFor(homeId, fn) {
|
|
1160
|
+
const run = (postingChains.get(homeId) || Promise.resolve()).then(fn);
|
|
1161
|
+
const tail = run.then(
|
|
1162
|
+
() => {},
|
|
1163
|
+
() => {}
|
|
1164
|
+
);
|
|
1165
|
+
postingChains.set(homeId, tail);
|
|
1166
|
+
void tail.then(() => {
|
|
1167
|
+
if (postingChains.get(homeId) === tail) postingChains.delete(homeId);
|
|
1168
|
+
});
|
|
1169
|
+
return run;
|
|
855
1170
|
}
|
|
1171
|
+
/** Whether queued row `runId` is in thread `homeId` (posted, saved or not). */
|
|
1172
|
+
const postedIn = (homeId, runId) => {
|
|
1173
|
+
const messages = loadSession(homeId)?.messages;
|
|
1174
|
+
const messageId = messageIdOf(runId);
|
|
1175
|
+
return Array.isArray(messages) && messages.some((message) => message?.meta?.transcript?.messageId === messageId);
|
|
1176
|
+
};
|
|
1177
|
+
const intake = createHomeEnqueue(queue, drain, { postingFor, postedIn });
|
|
1178
|
+
const { watchTask, recover } = createHomeBookkeeping({ queue, log, homeDeleted, sessionDeleted, drain });
|
|
856
1179
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
},
|
|
1180
|
+
/** Append `items` (and the omission notice `omitted`) not posted before to
|
|
1181
|
+
* `homeId`'s thread `session`, store it and show the new rows live.
|
|
1182
|
+
* Returns the run ids owed a wake (postQueuedRows). Nothing awaits before
|
|
1183
|
+
* the push, so no turn can start in between. */
|
|
1184
|
+
async function postRows(homeId, session, items, omitted) {
|
|
1185
|
+
if (!Array.isArray(session.messages)) session.messages = [];
|
|
1186
|
+
const { fresh, toWake } = postQueuedRows(session, items, omitted);
|
|
1187
|
+
heldLive.forget(items);
|
|
1188
|
+
// The caller dequeues `items` (and `omitted`) next: the thread holding
|
|
1189
|
+
// them is saved first, also when an earlier pass pushed them but its save
|
|
1190
|
+
// failed (they are in the thread in memory, not on disk).
|
|
1191
|
+
if (items.length || omitted) {
|
|
1192
|
+
if (fresh.length) session.updatedAt = Date.now();
|
|
1193
|
+
await saveSession(session);
|
|
1194
|
+
}
|
|
1195
|
+
for (const { message, notice } of fresh) {
|
|
1196
|
+
const { messageId, at } = message.meta.transcript;
|
|
1197
|
+
noticeLive(live, log, homeId, { ...notice, messageId, at });
|
|
1198
|
+
}
|
|
1199
|
+
return toWake;
|
|
864
1200
|
}
|
|
865
1201
|
|
|
866
|
-
/**
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1202
|
+
/** Right before a turn of thread `homeId` takes its prompt (whatever started
|
|
1203
|
+
* it: the user, a routine, a wake): post its queued rows that wake nobody —
|
|
1204
|
+
* progress updates of the sessions its agent handed work to, start and wait
|
|
1205
|
+
* lines, held requests, routine results — in queue order, so the turn reads
|
|
1206
|
+
* them before its prompt. Rows that wake the agent stay for the drain after
|
|
1207
|
+
* the turn, which posts them and wakes it as before. The caller starts the
|
|
1208
|
+
* turn, so the thread's busy state is its own. Rows the queue dropped past
|
|
1209
|
+
* its limit are announced first by the "n omitted" notice, retired in the
|
|
1210
|
+
* same write that dequeues the posted rows (as the drain does). It runs
|
|
1211
|
+
* in the thread's posting turn (postingFor), never beside the drain's. A
|
|
1212
|
+
* failure is logged; the turn goes on and the drain posts the rows later. */
|
|
1213
|
+
async function postBeforeTurn(homeId) {
|
|
1214
|
+
try {
|
|
1215
|
+
await postingFor(homeId, async () => {
|
|
1216
|
+
const items = (await queue.items(homeId)).filter((item) => !wakesAgent(item));
|
|
1217
|
+
const omitted = await queue.omitted(homeId);
|
|
1218
|
+
if (!items.length && !omitted) return;
|
|
1219
|
+
const session = loadSession(homeId);
|
|
1220
|
+
if (!session || session.closed === true) return;
|
|
1221
|
+
await postRows(homeId, session, items, omitted);
|
|
1222
|
+
await queue.remove(
|
|
1223
|
+
homeId,
|
|
1224
|
+
items.map((item) => item.runId),
|
|
1225
|
+
omitted
|
|
1226
|
+
);
|
|
1227
|
+
});
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
log(`posting the rows owed before a turn of ${homeId} failed: ${error?.message || error}`);
|
|
1230
|
+
}
|
|
872
1231
|
}
|
|
873
1232
|
|
|
874
|
-
/**
|
|
875
|
-
*
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1233
|
+
/** Mixy's heartbeat reports queued for her home while she does not exist
|
|
1234
|
+
* are dropped, never posted once she is created (issue #243); so is the
|
|
1235
|
+
* omission notice when nothing else waits (the rows it counts were these
|
|
1236
|
+
* reports too). Returns what is left to post. */
|
|
1237
|
+
async function dropHeartbeatReports(homeId, items, omitted) {
|
|
1238
|
+
const kept = items.filter((item) => !isHeartbeatReport(item));
|
|
1239
|
+
const retired = kept.length ? null : omitted;
|
|
1240
|
+
log(`${items.length - kept.length} heartbeat report(s) for ${homeId} are dropped: Mixy was never created`);
|
|
1241
|
+
await queue.remove(
|
|
1242
|
+
homeId,
|
|
1243
|
+
items.filter(isHeartbeatReport).map((item) => item.runId),
|
|
1244
|
+
retired
|
|
1245
|
+
);
|
|
1246
|
+
return { items: kept, omitted: retired ? null : omitted };
|
|
879
1247
|
}
|
|
880
1248
|
|
|
881
|
-
/**
|
|
882
|
-
*
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
await
|
|
888
|
-
return true;
|
|
1249
|
+
/** Post every queued result of `homeId` if its thread is idle, in its
|
|
1250
|
+
* posting turn, then wake the agent for what it owes; else retry later.
|
|
1251
|
+
* The wake runs outside the posting turn: the turn it starts posts first
|
|
1252
|
+
* (postBeforeTurn). */
|
|
1253
|
+
async function drainOnce(homeId) {
|
|
1254
|
+
const session = await postingFor(homeId, () => postIdle(homeId));
|
|
1255
|
+
if (session) await relay.wakeOwed(homeId, session);
|
|
889
1256
|
}
|
|
890
1257
|
|
|
891
|
-
/**
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1258
|
+
/** drainOnce's posting: the thread it posted to; nothing when nothing is
|
|
1259
|
+
* owed or the pass ends here (moved, busy, missing, dropped). */
|
|
1260
|
+
async function postIdle(homeId) {
|
|
1261
|
+
let items = await queue.items(homeId);
|
|
1262
|
+
let omitted = await queue.omitted(homeId);
|
|
895
1263
|
const owed = owesWake(await queue.wakes(homeId));
|
|
896
1264
|
if (!items.length && !omitted && !owed) return;
|
|
897
1265
|
if (roomIdOfSession(homeId)) return dropRoomLogQueue(homeId, items, omitted);
|
|
1266
|
+
if (homeId === MIXY_HOME && items.some(isHeartbeatReport) && !(await mixyExists())) {
|
|
1267
|
+
({ items, omitted } = await dropHeartbeatReports(homeId, items, omitted));
|
|
1268
|
+
if (!items.length && !omitted && !owed) return;
|
|
1269
|
+
}
|
|
898
1270
|
const agentId = agentIdOfHomeSession(homeId);
|
|
899
1271
|
if (agentId && agentId !== MIXY_AGENT_ID && agentPresence(agentId) === 'deleted') {
|
|
900
1272
|
// The owner is gone (its home archived): Mixy, the default owner, gets them.
|
|
@@ -906,7 +1278,7 @@ export function createAgentHomeDelivery({
|
|
|
906
1278
|
return;
|
|
907
1279
|
}
|
|
908
1280
|
if (busy(homeId)) {
|
|
909
|
-
|
|
1281
|
+
heldLive.show(homeId, items);
|
|
910
1282
|
return later(homeId, busyRetryMs);
|
|
911
1283
|
}
|
|
912
1284
|
// From here to the push nothing awaits, so no turn can start in between.
|
|
@@ -921,16 +1293,7 @@ export function createAgentHomeDelivery({
|
|
|
921
1293
|
}
|
|
922
1294
|
return later(homeId, missingRetryMs);
|
|
923
1295
|
}
|
|
924
|
-
|
|
925
|
-
const { fresh, toWake } = postQueuedRows(session, items, omitted);
|
|
926
|
-
for (const item of items) shownLive.delete(messageIdOf(item.runId));
|
|
927
|
-
if (fresh.length) {
|
|
928
|
-
session.updatedAt = Date.now();
|
|
929
|
-
await saveSession(session);
|
|
930
|
-
for (const { message, notice } of fresh) {
|
|
931
|
-
noticeLive(live, log, homeId, { ...notice, messageId: message.meta.transcript.messageId });
|
|
932
|
-
}
|
|
933
|
-
}
|
|
1296
|
+
const toWake = await postRows(homeId, session, items, omitted);
|
|
934
1297
|
// Dequeuing and owing the wake are one write: no restart between posting
|
|
935
1298
|
// and waking loses the wake.
|
|
936
1299
|
const { overflowed } = await queue.remove(
|
|
@@ -940,7 +1303,7 @@ export function createAgentHomeDelivery({
|
|
|
940
1303
|
{ wake: toWake }
|
|
941
1304
|
);
|
|
942
1305
|
if (overflowed) log(`${homeId} is at its wake cap: ${overflowed} result(s) ride the next wake as a count`);
|
|
943
|
-
|
|
1306
|
+
return session;
|
|
944
1307
|
}
|
|
945
1308
|
|
|
946
1309
|
/** The single drainer of `homeId`; a call during a pass runs one more pass. */
|
|
@@ -967,48 +1330,6 @@ export function createAgentHomeDelivery({
|
|
|
967
1330
|
return pass;
|
|
968
1331
|
}
|
|
969
1332
|
|
|
970
|
-
/** Persist `result` ({ runId, kind, ok, text, runSessionId }) of a schedule
|
|
971
|
-
* or webhook `row` for its owner's home, then start draining it. Resolves
|
|
972
|
-
* once it is queued. */
|
|
973
|
-
async function enqueue(row, result) {
|
|
974
|
-
const homeId = agentHomeSessionId(scheduleOwnerAgentId(row));
|
|
975
|
-
if (!result?.runId) throw new Error('routine result: runId required');
|
|
976
|
-
await queue.add(homeId, routineResultItem(row, result));
|
|
977
|
-
void drain(homeId);
|
|
978
|
-
return homeId;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
/** Persist one held-request row (`held`: normalizeHeldRequest fields) for
|
|
982
|
-
* `agentId`'s home thread, then start draining it. Each status posts once. */
|
|
983
|
-
async function enqueueHeld(agentId, held) {
|
|
984
|
-
const homeId = agentHomeSessionId(agentId) || agentHomeSessionId(MIXY_AGENT_ID);
|
|
985
|
-
if (!held?.id) throw new Error('held request: id required');
|
|
986
|
-
await queue.add(homeId, {
|
|
987
|
-
runId: `${held.id}_${held.status || 'pending'}`,
|
|
988
|
-
kind: 'held-request',
|
|
989
|
-
held,
|
|
990
|
-
at: Date.now(),
|
|
991
|
-
});
|
|
992
|
-
void drain(homeId);
|
|
993
|
-
return homeId;
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
/** Persist one report of a coding task session `agentId` created (session
|
|
997
|
-
* create) for its home thread, then start draining it. `status` 'started'
|
|
998
|
-
* and 'blocked' only post a line; a turn's end also wakes the agent. */
|
|
999
|
-
async function enqueueTask(agentId, report) {
|
|
1000
|
-
const { runId, taskSessionId, watchId = '', from = '' } = report;
|
|
1001
|
-
const homeId = agentHomeSessionId(agentId) || agentHomeSessionId(MIXY_AGENT_ID);
|
|
1002
|
-
if (!runId || !taskSessionId) throw new Error('task report: runId and taskSessionId required');
|
|
1003
|
-
const item = taskReportItem(report);
|
|
1004
|
-
const unwatch = watchId ? String(watchId) : '';
|
|
1005
|
-
// A room member's own work reports to its member session, never its home.
|
|
1006
|
-
const target = roomMemberOfSession(from)?.agentId === agentId ? from : homeId;
|
|
1007
|
-
await queue.add(target, item, { unwatch });
|
|
1008
|
-
void drain(target);
|
|
1009
|
-
return target;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
1333
|
/** Show `run`'s live-only row (routineRunNotice: `running`, or a result whose
|
|
1013
1334
|
* delivery failed) in `row`'s owner home when it is open and idle. Nothing
|
|
1014
1335
|
* is stored and no turn or unread mark follows; the posted result (same
|
|
@@ -1020,42 +1341,11 @@ export function createAgentHomeDelivery({
|
|
|
1020
1341
|
return noticeLive(live, log, homeId, notice) === true;
|
|
1021
1342
|
}
|
|
1022
1343
|
|
|
1023
|
-
const homeDeleted = (homeId) => homeProvenDeleted(homeId, agentPresence, roomPresence);
|
|
1024
|
-
|
|
1025
|
-
/** Retire the bookkeeping of deleted homes (and stale round counts). */
|
|
1026
|
-
async function prune() {
|
|
1027
|
-
await queue.prune((await queue.bookkeptHomes()).filter(homeDeleted));
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
/** Store `watch`; a home at its watch cap first gives up its oldest watches
|
|
1031
|
-
* whose target sessions are proven deleted, and refuses the watch (so the
|
|
1032
|
-
* send claims no report) when that frees too few. */
|
|
1033
|
-
async function watchTask(watch) {
|
|
1034
|
-
const drop = watchesToDrop(await queue.watches(), watch, queue.watchCap, sessionDeleted);
|
|
1035
|
-
if (drop.length) {
|
|
1036
|
-
log(`${watch.homeSessionId} is at its watch cap: dropped ${drop.length} watch(es) of deleted sessions`);
|
|
1037
|
-
}
|
|
1038
|
-
await queue.watch(watch, { drop });
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
/** Drain every queue left by an earlier process, after retiring stale bookkeeping. */
|
|
1042
|
-
async function recover() {
|
|
1043
|
-
try {
|
|
1044
|
-
await prune();
|
|
1045
|
-
} catch (error) {
|
|
1046
|
-
log(`pruning the routine results failed: ${error?.message || error}`);
|
|
1047
|
-
}
|
|
1048
|
-
const homes = await queue.pendingHomes();
|
|
1049
|
-
for (const homeId of homes) void drain(homeId);
|
|
1050
|
-
return homes;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
1344
|
return {
|
|
1054
|
-
|
|
1055
|
-
enqueueHeld,
|
|
1056
|
-
enqueueTask,
|
|
1345
|
+
...intake,
|
|
1057
1346
|
announce,
|
|
1058
1347
|
drain,
|
|
1348
|
+
postBeforeTurn,
|
|
1059
1349
|
recover,
|
|
1060
1350
|
watchTask,
|
|
1061
1351
|
unwatchTask: (runId) => queue.unwatch(runId),
|
|
@@ -1097,6 +1387,9 @@ async function processDelivery() {
|
|
|
1097
1387
|
// A home chat exists only for bots; only a definition read successfully
|
|
1098
1388
|
// can prove an agent is none (readHomeAgent).
|
|
1099
1389
|
homeAgent: (agentId) => readHomeAgent({ dataDir: getPluginData(), shippedRoot: mixdogRoot(), id: agentId }),
|
|
1390
|
+
// The one rule (mixy-presence.mjs; routine-owner.mjs imports this
|
|
1391
|
+
// module, hence late).
|
|
1392
|
+
mixyExists: async () => (await import('./routine-owner.mjs')).readMixyExists(),
|
|
1100
1393
|
// Deleting a session unlinks its record (session-delete.mjs) and leaves
|
|
1101
1394
|
// no tombstone: only an absent file with no in-memory copy proves it.
|
|
1102
1395
|
// An unreadable file probes 'unreadable' and stays.
|
|
@@ -1128,6 +1421,11 @@ export async function deliverHeldRequest(agentId, held) {
|
|
|
1128
1421
|
return (await processDelivery()).enqueueHeld(agentId, held);
|
|
1129
1422
|
}
|
|
1130
1423
|
|
|
1424
|
+
/** Queue the agent's own reply of a turn it ran outside its home thread. */
|
|
1425
|
+
export async function deliverAgentMessage(agentId, message) {
|
|
1426
|
+
return (await processDelivery()).enqueueMessage(agentId, message);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1131
1429
|
/** Queue one report of a task session for `agentId`'s home thread. */
|
|
1132
1430
|
export async function deliverAgentTask(agentId, report) {
|
|
1133
1431
|
return (await processDelivery()).enqueueTask(agentId, report);
|
|
@@ -1158,6 +1456,15 @@ export async function agentTaskWatches() {
|
|
|
1158
1456
|
return (await processDelivery()).taskWatches();
|
|
1159
1457
|
}
|
|
1160
1458
|
|
|
1459
|
+
/** The session turn prelude (session-turn-prelude.mjs) of the daemon: before a
|
|
1460
|
+
* turn of an agent's home thread or a room member session starts, its queued
|
|
1461
|
+
* rows that wake nobody post first (postBeforeTurn). Null for any other
|
|
1462
|
+
* session: nothing to wait for. */
|
|
1463
|
+
export function postBeforeAgentTurn(sessionId) {
|
|
1464
|
+
if (!isAgentHomeSessionId(sessionId) && !roomMemberOfSession(sessionId)) return null;
|
|
1465
|
+
return processDelivery().then((delivery) => delivery.postBeforeTurn(sessionId));
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1161
1468
|
/** Daemon start: post the results an earlier process left queued. */
|
|
1162
1469
|
export async function recoverScheduleResults() {
|
|
1163
1470
|
return (await processDelivery()).recover();
|