mixdog 1.3.5 → 1.4.1
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/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- 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 +35 -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 +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- 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/cache-break-trace.mjs +2 -0
- 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 +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- 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/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- 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-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- 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/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- 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/mimo-oauth-login.mjs +1 -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-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- 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/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- 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 +38 -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 +49 -16
- 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 +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- 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/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- 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/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- 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 +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -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/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- 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 +5 -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 +49 -21
- 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-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -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 +24 -14
- 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 +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -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-delete.mjs +7 -0
- 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 +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -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 +79 -3
- 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/stall-policy.mjs +23 -0
- 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/invocation-plan.mjs +2 -2
- 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/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- 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/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- 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 +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -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/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- 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/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- 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-owned-work.mjs +56 -0
- 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 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- 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/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- 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 +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -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 +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- 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-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -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 +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- 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 +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.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/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- 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/model-route/set-route.mjs +11 -2
- 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/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -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/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- 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/provider-admin.mjs +2 -1
- 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 +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- 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 +142 -190
- 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/agent-tree/agent-turns.mjs +16 -12
- 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 +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- 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 +16 -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/ui/statusline-agents.mjs +1 -3
- 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,9 +1,10 @@
|
|
|
1
1
|
// One-off model selection for a spawn the user explicitly pinned to a model.
|
|
2
2
|
// The agent's preset route stays the default; this only runs when `model` is
|
|
3
|
-
// passed. The query is matched against the
|
|
4
|
-
// then exact display name, then every query
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// passed. The query is matched against the quick/cached provider catalog —
|
|
4
|
+
// literal id, then normalized id, then exact display name, then every query
|
|
5
|
+
// word — and a catalog miss retries once on a catalog refreshed for the
|
|
6
|
+
// requested provider (concurrent misses share that refresh). One match runs;
|
|
7
|
+
// several or none fail with candidates instead of substituting another model.
|
|
7
8
|
import { clean } from './helpers.mjs';
|
|
8
9
|
|
|
9
10
|
const CANDIDATE_LIMIT = 8;
|
|
@@ -20,7 +21,7 @@ const terms = (value) =>
|
|
|
20
21
|
.filter(Boolean);
|
|
21
22
|
const label = (row) => `${row.provider}/${row.id}`;
|
|
22
23
|
|
|
23
|
-
function providerRows(rows, provider) {
|
|
24
|
+
export function providerRows(rows, provider) {
|
|
24
25
|
if (!provider) return rows;
|
|
25
26
|
const wanted = norm(provider);
|
|
26
27
|
const exact = rows.filter((row) => norm(row.provider) === wanted);
|
|
@@ -33,6 +34,8 @@ function matchModels(rows, query) {
|
|
|
33
34
|
// Nothing to match on ("???"): `[].every` would accept every row.
|
|
34
35
|
if (!words.length) return [];
|
|
35
36
|
const tiers = [
|
|
37
|
+
// A literal id wins over its normalized look-alikes (`foo-bar` vs `foo_bar`).
|
|
38
|
+
(row) => row.id === query,
|
|
36
39
|
(row) => norm(row.id) === wanted,
|
|
37
40
|
(row) => norm(row.display) === wanted,
|
|
38
41
|
(row) => words.every((word) => `${norm(row.id)} ${norm(row.display)}`.includes(word)),
|
|
@@ -49,6 +52,34 @@ function candidates(rows) {
|
|
|
49
52
|
return rows.length > CANDIDATE_LIMIT ? `${shown}, … (${rows.length} total)` : shown;
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
// A quick read answered from the loaded catalog marks every row complete.
|
|
56
|
+
// Otherwise a hit stands only when it is a literal id of the exact provider the
|
|
57
|
+
// caller named, which the full catalog would pick the same way.
|
|
58
|
+
function quickAnswerHolds(rows, hits, model, provider) {
|
|
59
|
+
if (rows.length > 0 && rows.every((row) => row.catalogComplete === true)) return true;
|
|
60
|
+
const wanted = norm(provider);
|
|
61
|
+
return hits.length > 0 && wanted !== '' && hits.every((row) => row.id === model && norm(row.provider) === wanted);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The forced refresh in flight per catalog reader and provider scope: concurrent
|
|
65
|
+
// misses share it instead of each refreshing.
|
|
66
|
+
const refreshing = new WeakMap();
|
|
67
|
+
|
|
68
|
+
// One forced refresh, scoped to the requested provider when one is given.
|
|
69
|
+
function refreshedRows(listProviderModels, provider) {
|
|
70
|
+
if (!refreshing.has(listProviderModels)) refreshing.set(listProviderModels, new Map());
|
|
71
|
+
const inflight = refreshing.get(listProviderModels);
|
|
72
|
+
const scope = norm(provider);
|
|
73
|
+
if (!inflight.has(scope)) {
|
|
74
|
+
const request = listProviderModels({ force: true, ...(provider ? { provider } : {}) });
|
|
75
|
+
inflight.set(
|
|
76
|
+
scope,
|
|
77
|
+
request.finally(() => inflight.delete(scope))
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return inflight.get(scope);
|
|
81
|
+
}
|
|
82
|
+
|
|
52
83
|
/** Resolve args.provider/args.model to one exact catalog route. Without a
|
|
53
84
|
* catalog (headless/daemon surfaces) exact provider + model pass through. */
|
|
54
85
|
export async function resolveRequestedModel(args, listProviderModels) {
|
|
@@ -64,10 +95,20 @@ export async function resolveRequestedModel(args, listProviderModels) {
|
|
|
64
95
|
if (!terms(model).length) throw new Error(`agent spawn: model "${model}" for provider "${provider}" not found`);
|
|
65
96
|
return args;
|
|
66
97
|
}
|
|
67
|
-
|
|
98
|
+
// The quick/cached catalog answers first. A loaded catalog is final, so its
|
|
99
|
+
// hit never waits on a provider; an incomplete one (cold or partial) may
|
|
100
|
+
// lack the model that wins, or one that makes the name ambiguous, so only a
|
|
101
|
+
// literal id inside an exact, explicitly named provider is taken from it.
|
|
102
|
+
// Anything else waits for the full catalog.
|
|
103
|
+
const quickRows = await listProviderModels({ quick: true });
|
|
104
|
+
let pool = providerRows(quickRows, provider);
|
|
68
105
|
let hits = matchModels(pool, model);
|
|
106
|
+
if (!quickAnswerHolds(quickRows, hits, model, provider)) {
|
|
107
|
+
pool = providerRows(await listProviderModels(), provider);
|
|
108
|
+
hits = matchModels(pool, model);
|
|
109
|
+
}
|
|
69
110
|
if (!hits.length) {
|
|
70
|
-
pool = providerRows(await listProviderModels
|
|
111
|
+
pool = providerRows(await refreshedRows(listProviderModels, provider), provider);
|
|
71
112
|
hits = matchModels(pool, model);
|
|
72
113
|
}
|
|
73
114
|
if (hits.length === 1) return { ...args, provider: hits[0].provider, model: hits[0].id };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Pure result-rendering and finish-classification helpers.
|
|
2
2
|
import { oneLine } from '../../../runtime/shared/clean.mjs';
|
|
3
|
+
import { isActiveWorkerRow } from './job-views/agent-list.mjs';
|
|
4
|
+
import { isTerminalJobStatus } from './job-views/session-progress.mjs';
|
|
3
5
|
import { compactIso, elapsedFromStamps, stripFinalAnswerWrapper } from './helpers.mjs';
|
|
4
6
|
|
|
5
7
|
// A worker that gets truncated mid-synthesis or produces an empty terminal
|
|
@@ -32,25 +34,77 @@ export function abnormalEmptyFinishError(result, agent) {
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const ROSTER_TAIL = 5;
|
|
38
|
+
const ROSTER_MATCH_CAP = 25;
|
|
39
|
+
|
|
40
|
+
function workerLine(worker) {
|
|
41
|
+
const status = worker.status || 'idle';
|
|
42
|
+
const stage = worker.worker_stage || worker.stage;
|
|
43
|
+
const progress = oneLine(worker.last_progress);
|
|
44
|
+
const summary = progress.length > 60 ? `${progress.slice(0, 59)}…` : progress;
|
|
45
|
+
return `- ${worker.tag} ${status}${stage && stage !== status ? `/${stage}` : ''}${summary ? ` ${summary}` : ''}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function jobLine(job) {
|
|
49
|
+
const error = job.status === 'failed' ? oneLine(job.error) : '';
|
|
50
|
+
return `- ${job.task_id} ${job.type} ${job.status} ${job.tag || '-'}${error ? ` error=${error}` : ''}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const stamp = (row) => String(row.updatedAt || row.lastUsedAt || row.finishedAt || row.startedAt || '');
|
|
54
|
+
|
|
55
|
+
// Inactive rows: newest first (stable for rows without stamps).
|
|
56
|
+
const newestFirst = (rows) => rows.slice().sort((a, b) => stamp(b).localeCompare(stamp(a)));
|
|
57
|
+
|
|
58
|
+
// Inactive rows collapse to a count plus a short tail. A `tag` filter lists the
|
|
59
|
+
// matching inactive rows (bounded) so a specific one can still be found.
|
|
60
|
+
function inactiveSection(label, rows, render, tagOf, filter) {
|
|
61
|
+
if (rows.length === 0) return [];
|
|
62
|
+
const needle = filter.toLowerCase();
|
|
63
|
+
const matched = needle
|
|
64
|
+
? rows.filter((row) =>
|
|
65
|
+
String(tagOf(row) ?? '')
|
|
66
|
+
.toLowerCase()
|
|
67
|
+
.includes(needle)
|
|
68
|
+
)
|
|
69
|
+
: null;
|
|
70
|
+
const shown = newestFirst(matched ?? rows).slice(0, matched ? ROSTER_MATCH_CAP : ROSTER_TAIL);
|
|
71
|
+
const shownFilter = filter.replace(/\s+/g, ' ');
|
|
72
|
+
const head = matched
|
|
73
|
+
? `${label}: ${rows.length} (${matched.length} match tag "${shownFilter}", showing ${shown.length})`
|
|
74
|
+
: `${label}: ${rows.length} (latest ${shown.length}; pass tag=<text> to find one)`;
|
|
75
|
+
// Bulleted: a non-bullet line would make the list read as an authored response.
|
|
76
|
+
return [`- ${head}`, ...shown.map(render)];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The /agents roster: active workers and running tasks in full; idle/closed
|
|
80
|
+
// workers and finished tasks as counts with a bounded tail.
|
|
81
|
+
function renderRoster(value, filter = '') {
|
|
37
82
|
const workers = Array.isArray(value.workers) ? value.workers : [];
|
|
38
83
|
const jobs = Array.isArray(value.jobs) ? value.jobs : [];
|
|
39
84
|
const lines = [`agents: ${workers.length} · tasks: ${jobs.length}`];
|
|
85
|
+
const isActive = (worker) => Boolean(worker.awaitingUser) || isActiveWorkerRow(worker);
|
|
86
|
+
const isRunning = (job) => !isTerminalJobStatus(job.status);
|
|
40
87
|
// Keep list bullets: agent cards distinguish these rows from authored responses.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
88
|
+
lines.push(...workers.filter(isActive).map(workerLine));
|
|
89
|
+
lines.push(...jobs.filter(isRunning).map(jobLine));
|
|
90
|
+
lines.push(
|
|
91
|
+
...inactiveSection(
|
|
92
|
+
'idle/closed agents',
|
|
93
|
+
workers.filter((w) => !isActive(w)),
|
|
94
|
+
workerLine,
|
|
95
|
+
(w) => w.tag,
|
|
96
|
+
filter
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
lines.push(
|
|
100
|
+
...inactiveSection(
|
|
101
|
+
'finished tasks',
|
|
102
|
+
jobs.filter((j) => !isRunning(j)),
|
|
103
|
+
jobLine,
|
|
104
|
+
(j) => j.tag || j.task_id,
|
|
105
|
+
filter
|
|
106
|
+
)
|
|
107
|
+
);
|
|
54
108
|
if (workers.length === 0 && jobs.length === 0) lines.push('(no agents or tasks)');
|
|
55
109
|
return lines.join('\n');
|
|
56
110
|
}
|
|
@@ -151,11 +205,11 @@ function renderContent(value) {
|
|
|
151
205
|
return `${header}\n${stripFinalAnswerWrapper(value.content)}`;
|
|
152
206
|
}
|
|
153
207
|
|
|
154
|
-
export function renderResult(value, { includeDiagnostics = false } = {}) {
|
|
208
|
+
export function renderResult(value, { includeDiagnostics = false, listFilter = '' } = {}) {
|
|
155
209
|
if (value === undefined || value === null) return '';
|
|
156
210
|
if (typeof value === 'string') return value;
|
|
157
211
|
if (value && typeof value === 'object') {
|
|
158
|
-
if (Array.isArray(value.workers) || Array.isArray(value.jobs)) return renderRoster(value);
|
|
212
|
+
if (Array.isArray(value.workers) || Array.isArray(value.jobs)) return renderRoster(value, listFilter);
|
|
159
213
|
// close-flow results carry `closed` plus task_id: render the close outcome.
|
|
160
214
|
// close-flow results carry `closed` plus task_id: render the close outcome.
|
|
161
215
|
if (value.closed !== undefined) return renderClosed(value);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { configuredAgentRouteCandidates } from '../../../runtime/shared/agent-route-config.mjs';
|
|
2
|
+
import { executionFallbacks } from '../../../runtime/shared/model-route-chain.mjs';
|
|
2
3
|
import {
|
|
3
4
|
agentPresetName,
|
|
4
5
|
clean,
|
|
@@ -29,8 +30,14 @@ export function resolveAgentSpawnPreset(config, args = {}) {
|
|
|
29
30
|
const agentName = normalizeAgentName(args.agent);
|
|
30
31
|
// The agent's stored route: config.agents, or a store bot's own folder.
|
|
31
32
|
const candidates = !clean(args.preset) ? configuredAgentRouteCandidates(config, agentName) : [];
|
|
32
|
-
const
|
|
33
|
-
const
|
|
33
|
+
const storedCandidate = candidates.find((candidate) => normalizeAgentRoute(candidate));
|
|
34
|
+
const agentRoute = storedCandidate ? normalizeAgentRoute(storedCandidate) : null;
|
|
35
|
+
const storedFallbacks = agentRoute
|
|
36
|
+
? executionFallbacks({ ...agentRoute, fallbacks: storedCandidate.fallbacks })
|
|
37
|
+
: undefined;
|
|
38
|
+
// Where the spawned session reads its fallback chain at each hop
|
|
39
|
+
// (route-fallback-chain.mjs): this agent's route, else Main's.
|
|
40
|
+
const fallbackSource = { agent: agentName };
|
|
34
41
|
if (agentRoute) {
|
|
35
42
|
return {
|
|
36
43
|
presetName: agentPresetName(agentName),
|
|
@@ -41,6 +48,7 @@ export function resolveAgentSpawnPreset(config, args = {}) {
|
|
|
41
48
|
provider: agentRoute.provider,
|
|
42
49
|
model: agentRoute.model,
|
|
43
50
|
...(storedFallbacks?.length ? { fallbacks: storedFallbacks.map(normalizeAgentRoute) } : {}),
|
|
51
|
+
fallbackSource,
|
|
44
52
|
effort: agentRoute.effort,
|
|
45
53
|
fast: agentRoute.fast === true,
|
|
46
54
|
...(agentRoute.autoEffort === false ? { autoEffort: false } : {}),
|
|
@@ -54,12 +62,13 @@ export function resolveAgentSpawnPreset(config, args = {}) {
|
|
|
54
62
|
|
|
55
63
|
const mainPreset = !clean(args.preset) ? findPreset(config, config?.default) : null;
|
|
56
64
|
if (normalizeAgentRoute(mainPreset)) {
|
|
57
|
-
return { presetName: mainPreset.id || mainPreset.name, preset: mainPreset };
|
|
65
|
+
return { presetName: mainPreset.id || mainPreset.name, preset: { ...mainPreset, fallbackSource } };
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
const presetName = clean(args.preset);
|
|
61
69
|
if (!presetName) throw new Error(`agent: agent "${agentName}" has no Main model assignment`);
|
|
62
|
-
const
|
|
70
|
+
const stored = findPreset(config, presetName);
|
|
71
|
+
const preset = stored ? { ...stored, fallbackSource: { preset: presetName } } : synthesizePreset(config, presetName);
|
|
63
72
|
if (!preset) throw new Error(`agent: preset "${presetName}" not found`);
|
|
64
73
|
if (!normalizeAgentRoute(preset)) throw new Error(`agent: preset "${presetName}" has no complete route`);
|
|
65
74
|
return { presetName, preset };
|
|
@@ -28,7 +28,7 @@ export const AGENT_TOOL = {
|
|
|
28
28
|
agentHidden: false,
|
|
29
29
|
},
|
|
30
30
|
description:
|
|
31
|
-
'Run scoped agent work as background tasks. spawn/send return task_id immediately. Reuse one tag for one scope for its whole lifetime; give distinct tags only to independent scopes. Never mint a new tag because a session expired or was lost — same-tag spawn respawns it with the full brief.
|
|
31
|
+
'Run scoped agent work as background tasks. spawn/send return task_id immediately. Reuse one tag for one scope for its whole lifetime; give distinct tags only to independent scopes. Never mint a new tag because a session expired or was lost — same-tag spawn respawns it with the full brief. Completion notifications arrive automatically; use status/read only for manual recovery. Close finished agents yourself.',
|
|
32
32
|
inputSchema: {
|
|
33
33
|
type: 'object',
|
|
34
34
|
properties: {
|
|
@@ -36,21 +36,21 @@ export const AGENT_TOOL = {
|
|
|
36
36
|
type: 'string',
|
|
37
37
|
enum: ['spawn', 'send', 'list', 'close', 'cancel', 'status', 'read', 'cleanup'],
|
|
38
38
|
description:
|
|
39
|
-
'Required action. New spawn requires agent and one of prompt/message/file; send requires tag and one of prompt/message/file; read/status/close/cancel require task_id or tag; list/cleanup require no target.',
|
|
39
|
+
'Required action. New spawn requires agent and one of prompt/message/file; send requires tag and one of prompt/message/file; read/status/close/cancel require task_id or tag; list/cleanup require no target. list shows active agents and running tasks in full; idle/closed agents and finished tasks are counts plus a short latest tail (list with tag=<text> shows matching ones: case-insensitive substring, at most 25 per section).',
|
|
40
40
|
},
|
|
41
|
-
task_id: { type: 'string', description: 'Task ID returned by spawn/send
|
|
41
|
+
task_id: { type: 'string', description: 'Task ID returned by spawn/send.' },
|
|
42
42
|
agent: {
|
|
43
43
|
type: 'string',
|
|
44
|
-
description: 'Workflow agent id
|
|
44
|
+
description: 'Workflow agent id; same-tag reuse can inherit it.',
|
|
45
45
|
},
|
|
46
46
|
tag: {
|
|
47
47
|
type: 'string',
|
|
48
48
|
description:
|
|
49
|
-
'Stable scope handle
|
|
49
|
+
'Stable scope handle; never derive a variant by appending a number or suffix. spawn generates one when omitted.',
|
|
50
50
|
},
|
|
51
|
-
prompt: { type: 'string', description: 'Spawn task brief
|
|
52
|
-
message: { type: 'string', description: 'Send follow-up
|
|
53
|
-
file: { type: 'string', description: 'Path to task text;
|
|
51
|
+
prompt: { type: 'string', description: 'Spawn task brief.' },
|
|
52
|
+
message: { type: 'string', description: 'Send follow-up.' },
|
|
53
|
+
file: { type: 'string', description: 'Path to task text; used instead of prompt/message.' },
|
|
54
54
|
cwd: { type: 'string', description: 'Project directory for a new or respawned agent.' },
|
|
55
55
|
context: { type: 'string', description: 'Extra context for the spawn/send turn.' },
|
|
56
56
|
attachments: {
|
|
@@ -63,7 +63,7 @@ export const AGENT_TOOL = {
|
|
|
63
63
|
model: {
|
|
64
64
|
type: 'string',
|
|
65
65
|
description:
|
|
66
|
-
"Spawn only, and only when the user explicitly names a model: runs this one spawn on it instead of the agent's preset route
|
|
66
|
+
"Spawn only, and only when the user explicitly names a model: runs this one spawn on it instead of the agent's preset route. Id, display name, or words of it; ambiguous or unknown names fail with candidates. The agent still supplies role and permissions.",
|
|
67
67
|
},
|
|
68
68
|
provider: { type: 'string', description: 'Narrows model to one provider; requires model.' },
|
|
69
69
|
effort: { type: 'string', description: 'Reasoning effort for an explicit model.' },
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// worker-index/awaiting-user-sync.mjs
|
|
2
|
+
// A worker row's approval wait (`awaitingUser`), fenced to its session's turn
|
|
3
|
+
// and kept in step with the runtime this process runs for that session. Owns
|
|
4
|
+
// the sessions whose deferred rows still wait in the mutation batch.
|
|
5
|
+
import { clean } from '../helpers.mjs';
|
|
6
|
+
import { applyAwaitingUser, awaitingUserWouldChange } from './approval-wait.mjs';
|
|
7
|
+
|
|
8
|
+
export function createAwaitingUserSync(store, mgr) {
|
|
9
|
+
// Sessions whose deferred rows still wait in the batch; reconciled once the
|
|
10
|
+
// batch lands in the store.
|
|
11
|
+
const unreconciled = new Set();
|
|
12
|
+
|
|
13
|
+
/** Persist (or clear) a worker's approval wait as a field update fenced to
|
|
14
|
+
* the session and its turn (`turnAt`, ms): a row whose turn started later is
|
|
15
|
+
* left alone. Writes only when a row of that session exists and would
|
|
16
|
+
* change, so a Lead's own approvals cost nothing; a row that is not in the
|
|
17
|
+
* store yet takes the wait when it lands (reconcileAwaitingUser). */
|
|
18
|
+
function setAwaitingUser(sessionId, awaitingUser, { turnAt = 0 } = {}) {
|
|
19
|
+
const fence = {
|
|
20
|
+
sessionId: clean(sessionId),
|
|
21
|
+
awaitingUser: awaitingUser === 'approval' ? 'approval' : null,
|
|
22
|
+
turnAt: Number(turnAt) || 0,
|
|
23
|
+
};
|
|
24
|
+
if (!fence.sessionId || !awaitingUserWouldChange(store.readAll(), fence)) return false;
|
|
25
|
+
store.update((byKey) => applyAwaitingUser(byKey, fence));
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The runtime of a session this process runs is the authority on its
|
|
30
|
+
* approval wait (the runtime-liveness counter behind `awaitingUser`): every
|
|
31
|
+
* row it creates or refreshes takes that state, so a missed or stale
|
|
32
|
+
* transition heals on the next write. No runtime here: not ours to judge. */
|
|
33
|
+
function reconcileAwaitingUser(sessionId) {
|
|
34
|
+
const runtime = mgr.getSessionRuntime?.(sessionId);
|
|
35
|
+
if (runtime) setAwaitingUser(sessionId, runtime.awaitingUser, { turnAt: runtime.askStartedAt });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A deferred row of `sessionId` is queued; reconciled when its batch lands. */
|
|
39
|
+
function deferReconcile(sessionId) {
|
|
40
|
+
unreconciled.add(sessionId);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The batch landed: reconcile every session whose deferred row it carried. */
|
|
44
|
+
function reconcileDeferred() {
|
|
45
|
+
const ids = [...unreconciled];
|
|
46
|
+
unreconciled.clear();
|
|
47
|
+
for (const id of ids) reconcileAwaitingUser(id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { setAwaitingUser, reconcileAwaitingUser, deferReconcile, reconcileDeferred };
|
|
51
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// worker-index/disposal.mjs
|
|
2
|
+
// Disposing a worker index: queued rows persist first, then `teardown`
|
|
3
|
+
// releases its registrations; a failed drain leaves them for a retry. Owns
|
|
4
|
+
// the disposed / in-flight dispose state.
|
|
5
|
+
export function createIndexDisposal({ batch, store, teardown }) {
|
|
6
|
+
let disposed = false;
|
|
7
|
+
let disposePromise = null;
|
|
8
|
+
|
|
9
|
+
async function drainThenTeardown() {
|
|
10
|
+
batch.flush();
|
|
11
|
+
await store.flush();
|
|
12
|
+
teardown();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Persist queued rows, then release the exit registration. Active rows are
|
|
16
|
+
* NOT settled: they are keyed by process pid, which every inline runtime in
|
|
17
|
+
* the daemon shares, so settling here would idle other runtimes' workers.
|
|
18
|
+
* When the sync flush cannot run (this process's async writer holds the
|
|
19
|
+
* lock) the exit hook and store ref stay until the async drain settles. */
|
|
20
|
+
function dispose() {
|
|
21
|
+
if (disposed) return;
|
|
22
|
+
disposed = true;
|
|
23
|
+
let flushed = false;
|
|
24
|
+
try {
|
|
25
|
+
batch.flush();
|
|
26
|
+
flushed = store.flushSync();
|
|
27
|
+
} catch {}
|
|
28
|
+
if (flushed) {
|
|
29
|
+
teardown();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
disposePromise = drainThenTeardown().catch(reopenAfterFailedDrain);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A failed drain tears nothing down; allow a later dispose to retry it. */
|
|
36
|
+
function reopenAfterFailedDrain() {
|
|
37
|
+
disposed = false;
|
|
38
|
+
disposePromise = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Awaitable dispose: drain everything, then tear down. A failed drain
|
|
42
|
+
* rejects and leaves the registrations in place. */
|
|
43
|
+
function disposeAsync() {
|
|
44
|
+
if (!disposePromise) {
|
|
45
|
+
disposed = true;
|
|
46
|
+
disposePromise = drainThenTeardown().catch((error) => {
|
|
47
|
+
reopenAfterFailedDrain();
|
|
48
|
+
throw error;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return disposePromise;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { dispose, disposeAsync };
|
|
55
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// worker-index/released-store.mjs
|
|
2
|
+
// Which agent sessions their owner closed (the agent tool's `close`): session
|
|
3
|
+
// id → when. A closed session stays open and resumable, so a session scan
|
|
4
|
+
// (also after a daemon restart) would list it again; the marker is what keeps
|
|
5
|
+
// its rows `released` until a new turn starts on it. It lives in a file of its
|
|
6
|
+
// own beside the worker index, so a writer that rebuilds the index document
|
|
7
|
+
// (an older daemon) can never drop it. Entries expire after the tombstone TTL,
|
|
8
|
+
// judged on every read, not only when the file changes.
|
|
9
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
10
|
+
|
|
11
|
+
import { parseJsonText } from '../../../../runtime/shared/atomic-file.mjs';
|
|
12
|
+
import { createIndexWriteQueue } from '../index-write-queue.mjs';
|
|
13
|
+
import { TAG_TOMBSTONE_TTL_MS } from '../worker-rows.mjs';
|
|
14
|
+
|
|
15
|
+
export const RELEASED_FILE = 'agent-workers-released.json';
|
|
16
|
+
|
|
17
|
+
/** id → epoch ms of every entry of a raw document (expired ones included). */
|
|
18
|
+
function parseReleased(doc) {
|
|
19
|
+
const raw = doc?.released && typeof doc.released === 'object' ? doc.released : {};
|
|
20
|
+
return new Map(
|
|
21
|
+
Object.entries(raw)
|
|
22
|
+
.map(([sessionId, at]) => [sessionId, Date.parse(String(at)) || 0])
|
|
23
|
+
.filter(([sessionId]) => sessionId)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The next document: unexpired entries plus the mutator's changes. */
|
|
28
|
+
function rewriteReleased(cur, mutator) {
|
|
29
|
+
const cutoff = Date.now() - TAG_TOMBSTONE_TTL_MS;
|
|
30
|
+
const entries = new Map();
|
|
31
|
+
for (const [sessionId, at] of parseReleased(cur)) {
|
|
32
|
+
if (at >= cutoff) entries.set(sessionId, new Date(at).toISOString());
|
|
33
|
+
}
|
|
34
|
+
mutator(entries);
|
|
35
|
+
return { version: 1, released: Object.fromEntries(entries) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** `file` may be null (no data dir): reads are empty and writes are no-ops. */
|
|
39
|
+
export function createReleasedStore(file) {
|
|
40
|
+
let cache = null; // { mtimeMs, size, doc, at }
|
|
41
|
+
let dirty = true;
|
|
42
|
+
// Unpersisted writes are read from the queue's projection.
|
|
43
|
+
let projected = null; // { version, at }
|
|
44
|
+
|
|
45
|
+
const queue = createIndexWriteQueue({
|
|
46
|
+
file,
|
|
47
|
+
rewrite: rewriteReleased,
|
|
48
|
+
readDoc: () => {
|
|
49
|
+
dirty = true;
|
|
50
|
+
entries();
|
|
51
|
+
return cache?.doc ?? null;
|
|
52
|
+
},
|
|
53
|
+
onPersisted: () => {
|
|
54
|
+
dirty = true;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/** id → epoch ms, as written (not yet filtered by expiry). */
|
|
59
|
+
function entries() {
|
|
60
|
+
const doc = queue.projection();
|
|
61
|
+
if (doc) {
|
|
62
|
+
if (!projected || projected.version !== queue.version()) {
|
|
63
|
+
projected = { version: queue.version(), at: parseReleased(doc) };
|
|
64
|
+
}
|
|
65
|
+
return projected.at;
|
|
66
|
+
}
|
|
67
|
+
if (!file) return new Map();
|
|
68
|
+
// Only a missing file is an empty store; any other read error propagates
|
|
69
|
+
// (queue.write reports it and queues nothing; a reader sees it thrown).
|
|
70
|
+
let st;
|
|
71
|
+
try {
|
|
72
|
+
st = statSync(file);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
75
|
+
cache = null;
|
|
76
|
+
return new Map();
|
|
77
|
+
}
|
|
78
|
+
if (!dirty && cache && cache.mtimeMs === st.mtimeMs && cache.size === st.size) return cache.at;
|
|
79
|
+
let parsed = null;
|
|
80
|
+
let text = null;
|
|
81
|
+
try {
|
|
82
|
+
text = readFileSync(file, 'utf8');
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
85
|
+
}
|
|
86
|
+
if (text !== null) {
|
|
87
|
+
try {
|
|
88
|
+
parsed = parseJsonText(text);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Left on disk and reported; the next save moves it aside to
|
|
91
|
+
// `<file>.corrupt-<ts>` (atomic-file's damaged-state recovery) instead
|
|
92
|
+
// of overwriting it.
|
|
93
|
+
process.stderr.write(`[released-store] ${file} is malformed (${error.message}); markers unreadable\n`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
cache = { mtimeMs: st.mtimeMs, size: st.size, doc: parsed, at: parseReleased(parsed) };
|
|
97
|
+
dirty = false;
|
|
98
|
+
return cache.at;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
/** Whether the owner closed this session less than the TTL ago. */
|
|
103
|
+
isReleased: (sessionId) => entries().get(sessionId) >= Date.now() - TAG_TOMBSTONE_TTL_MS,
|
|
104
|
+
mark(sessionIds) {
|
|
105
|
+
const at = new Date().toISOString();
|
|
106
|
+
queue.write((entriesById) => {
|
|
107
|
+
for (const sessionId of sessionIds) entriesById.set(sessionId, at);
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
clear(sessionId) {
|
|
111
|
+
if (!entries().has(sessionId)) return;
|
|
112
|
+
queue.write((entriesById) => {
|
|
113
|
+
entriesById.delete(sessionId);
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
flush: queue.flush,
|
|
117
|
+
flushSync: queue.flushSync,
|
|
118
|
+
hasUnpersisted: queue.hasUnpersisted,
|
|
119
|
+
invalidate: () => {
|
|
120
|
+
dirty = true;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// worker-index/restart-settlement.mjs
|
|
2
|
+
// Rows whose turn a process exit or restart ended: this process's active rows
|
|
3
|
+
// settled as failed on exit, stale active rows and approval waits settled at
|
|
4
|
+
// startup and on later sweeps, and the owner's restart-failure notification
|
|
5
|
+
// stamped once queued. Owns the keys of the rows this process marked active.
|
|
6
|
+
import { positiveInt } from '../helpers.mjs';
|
|
7
|
+
import { workerRowKey } from '../worker-rows.mjs';
|
|
8
|
+
import { clearAwaitingUser } from './approval-wait.mjs';
|
|
9
|
+
import {
|
|
10
|
+
createRowLiveness,
|
|
11
|
+
isActiveWorkerRow,
|
|
12
|
+
isStaleApprovalWait,
|
|
13
|
+
isUnnotifiedRestartFailure,
|
|
14
|
+
PROCESS_RESTART_FAILURE,
|
|
15
|
+
runtimeDied,
|
|
16
|
+
} from './row-liveness.mjs';
|
|
17
|
+
|
|
18
|
+
export function createRestartSettlement({ store, cfgMod, mgr }) {
|
|
19
|
+
const liveness = createRowLiveness({
|
|
20
|
+
cfgMod,
|
|
21
|
+
isSessionRunning: (sessionId) => mgr.isSessionTurnInFlight?.(sessionId) === true,
|
|
22
|
+
});
|
|
23
|
+
// Rows this process marked active; settled as failed on exit (their turn
|
|
24
|
+
// dies with the process) so a crash of the host never leaves them
|
|
25
|
+
// "running" forever.
|
|
26
|
+
const activeWorkerKeys = new Set();
|
|
27
|
+
|
|
28
|
+
/** Track whether the row written under `key` is one this process runs. */
|
|
29
|
+
function trackActiveRow(key, row) {
|
|
30
|
+
if (isActiveWorkerRow(row)) activeWorkerKeys.add(key);
|
|
31
|
+
else activeWorkerKeys.delete(key);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function flushActiveWorkerRows() {
|
|
35
|
+
if (!activeWorkerKeys.size) return;
|
|
36
|
+
const keys = [...activeWorkerKeys];
|
|
37
|
+
activeWorkerKeys.clear();
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
store.write((byKey) => {
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
const current = byKey.get(key);
|
|
42
|
+
if (!current || positiveInt(current.runtimePid) !== process.pid) continue;
|
|
43
|
+
if (isActiveWorkerRow(current)) {
|
|
44
|
+
byKey.set(key, liveness.failedRow(current, now, PROCESS_RESTART_FAILURE, true));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Startup recovery: stale active rows settle — as failed when another
|
|
51
|
+
// process ran their turn (it is gone, or is the predecessor still shutting
|
|
52
|
+
// down: either way the turn never finished), otherwise to idle — and an
|
|
53
|
+
// approval wait left behind by a runtime that is gone (daemon restart) is
|
|
54
|
+
// cleared. `deadOnly` (later sweeps) settles only rows whose runtime is
|
|
55
|
+
// provably dead, never a live process's quiet turn.
|
|
56
|
+
function recoverStaleWorkerRows({ deadOnly = false } = {}) {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const rows = store.readAll();
|
|
59
|
+
const isStale = deadOnly
|
|
60
|
+
? (row) => isActiveWorkerRow(row) && runtimeDied(row)
|
|
61
|
+
: (row) => liveness.isStaleActive(row, now);
|
|
62
|
+
const staleActive = rows.filter(isStale);
|
|
63
|
+
const staleWaits = rows.some(isStaleApprovalWait);
|
|
64
|
+
if (!staleActive.length && !staleWaits) return false;
|
|
65
|
+
if (staleActive.length) {
|
|
66
|
+
store.write((byKey) => {
|
|
67
|
+
for (const row of staleActive) {
|
|
68
|
+
const key = workerRowKey(row);
|
|
69
|
+
const current = byKey.get(key);
|
|
70
|
+
if (!current || !isStale(current)) continue;
|
|
71
|
+
const ranElsewhere = positiveInt(current.runtimePid) && positiveInt(current.runtimePid) !== process.pid;
|
|
72
|
+
byKey.set(
|
|
73
|
+
key,
|
|
74
|
+
ranElsewhere ? liveness.failedRow(current, now, PROCESS_RESTART_FAILURE) : liveness.idleRow(current, now)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (staleWaits) store.update((byKey) => clearAwaitingUser(byKey, isStaleApprovalWait));
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Rows whose turn a process restart ended and whose owner has not been told
|
|
84
|
+
* yet — whoever settled the row (this process at startup, or the
|
|
85
|
+
* predecessor's exit flush). Read-only: the caller enqueues the owner's
|
|
86
|
+
* notification durably, THEN calls `markRestartNotified`, so a crash in
|
|
87
|
+
* between retries (the notification's stable id makes the retry exactly-once). */
|
|
88
|
+
function pendingRestartFailures() {
|
|
89
|
+
return store.readAll().filter(isUnnotifiedRestartFailure);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Stamp these rows as told, once their notification is durably queued. */
|
|
93
|
+
function markRestartNotified(rows) {
|
|
94
|
+
const stamp = new Date().toISOString();
|
|
95
|
+
store.write((byKey) => {
|
|
96
|
+
for (const row of rows) {
|
|
97
|
+
const key = workerRowKey(row);
|
|
98
|
+
const current = byKey.get(key);
|
|
99
|
+
if (!current || !isUnnotifiedRestartFailure(current)) continue;
|
|
100
|
+
byKey.set(key, { ...current, restartNotified: stamp });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
trackActiveRow,
|
|
107
|
+
flushActiveWorkerRows,
|
|
108
|
+
recoverStaleWorkerRows,
|
|
109
|
+
pendingRestartFailures,
|
|
110
|
+
markRestartNotified,
|
|
111
|
+
};
|
|
112
|
+
}
|