@gajae-code/coding-agent 0.11.0 → 0.11.2
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/CHANGELOG.md +63 -1
- package/dist/types/async/job-manager.d.ts +46 -7
- package/dist/types/cli/config-cli.d.ts +11 -1
- package/dist/types/cli/notify-cli.d.ts +1 -0
- package/dist/types/cli/session-picker.d.ts +2 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/mcp-serve.d.ts +33 -0
- package/dist/types/commands/mcp.d.ts +1 -1
- package/dist/types/commands/notify.d.ts +0 -2
- package/dist/types/commands/sdk.d.ts +1 -1
- package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
- package/dist/types/config/file-lock.d.ts +14 -4
- package/dist/types/config/keybindings.d.ts +42 -1
- package/dist/types/config/model-auth.d.ts +3 -0
- package/dist/types/config/model-bindings-applier.d.ts +13 -0
- package/dist/types/config/model-discovery-manager.d.ts +49 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-registry.d.ts +16 -15
- package/dist/types/config/model-resolver.d.ts +28 -18
- package/dist/types/config/models-config-schema.d.ts +11 -0
- package/dist/types/config/settings-schema.d.ts +56 -1
- package/dist/types/config/settings.d.ts +14 -11
- package/dist/types/coordinator-mcp/policy.d.ts +5 -0
- package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +0 -6
- package/dist/types/edit/index.d.ts +3 -0
- package/dist/types/eval/py/env.d.ts +9 -0
- package/dist/types/extensibility/extensions/loader.d.ts +2 -1
- package/dist/types/extensibility/extensions/runner.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +34 -3
- package/dist/types/extensibility/hooks/runner.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
- package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
- package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
- package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
- package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
- package/dist/types/gjc-runtime/team-store.d.ts +160 -0
- package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
- package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
- package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
- package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/goals/state.d.ts +8 -0
- package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
- package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
- package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/state.d.ts +11 -4
- package/dist/types/lsp/lspmux.d.ts +5 -3
- package/dist/types/lsp/path-trust.d.ts +1 -0
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memories/storage.d.ts +1 -0
- package/dist/types/modes/action-registry.d.ts +29 -0
- package/dist/types/modes/components/chrome.d.ts +14 -0
- package/dist/types/modes/components/command-palette.d.ts +27 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +2 -1
- package/dist/types/modes/components/pet-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
- package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
- package/dist/types/modes/components/queue-pane.d.ts +12 -0
- package/dist/types/modes/components/read-tool-group.d.ts +6 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
- package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
- package/dist/types/modes/components/settings-selector.d.ts +1 -1
- package/dist/types/modes/components/show-images-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +1 -94
- package/dist/types/modes/components/tasks-pane.d.ts +15 -0
- package/dist/types/modes/components/theme-selector.d.ts +1 -1
- package/dist/types/modes/components/tool-execution.d.ts +15 -0
- package/dist/types/modes/components/tool-status-header.d.ts +111 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
- package/dist/types/modes/controllers/input-controller.d.ts +8 -1
- package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
- package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
- package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
- package/dist/types/modes/interactive-mode.d.ts +21 -19
- package/dist/types/modes/jobs-observer.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +21 -2
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
- package/dist/types/modes/tasks-aggregator.d.ts +39 -0
- package/dist/types/modes/transcript-item-registry.d.ts +60 -0
- package/dist/types/modes/types.d.ts +22 -15
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +78 -12
- package/dist/types/sdk/broker/ensure.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
- package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
- package/dist/types/sdk/broker/session-index.d.ts +0 -1
- package/dist/types/sdk/broker/state-version.d.ts +2 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
- package/dist/types/sdk/bus/config-commands.d.ts +1 -1
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
- package/dist/types/sdk/bus/html-format.d.ts +3 -2
- package/dist/types/sdk/bus/index.d.ts +27 -0
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/notification-service.d.ts +20 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
- package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
- package/dist/types/sdk/bus/rich-document.d.ts +7 -0
- package/dist/types/sdk/bus/rich-render.d.ts +18 -41
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +69 -3
- package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/session-directory.d.ts +56 -0
- package/dist/types/session/agent-session.d.ts +82 -47
- package/dist/types/session/agent-storage.d.ts +7 -0
- package/dist/types/session/fallback-chain-controller.d.ts +10 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
- package/dist/types/session/messages.d.ts +16 -0
- package/dist/types/session/session-manager-internal.d.ts +14 -0
- package/dist/types/session/session-manager.d.ts +82 -12
- package/dist/types/session/session-storage.d.ts +43 -3
- package/dist/types/session/volatile-context-pruning.d.ts +15 -0
- package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
- package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
- package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
- package/dist/types/system-prompt.d.ts +2 -11
- package/dist/types/task/executor.d.ts +3 -1
- package/dist/types/thinking.d.ts +7 -0
- package/dist/types/tool-discovery/tool-index.d.ts +1 -50
- package/dist/types/tools/ask.d.ts +64 -2
- package/dist/types/tools/bash.d.ts +3 -0
- package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
- package/dist/types/tools/cron.d.ts +2 -0
- package/dist/types/tools/fetch.d.ts +1 -0
- package/dist/types/tools/index.d.ts +30 -15
- package/dist/types/tools/output-meta.d.ts +1 -0
- package/dist/types/tools/read.d.ts +3 -0
- package/dist/types/tools/search-tool-bm25.d.ts +3 -4
- package/dist/types/tools/subagent-render.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +6 -1
- package/dist/types/utils/event-bus.d.ts +1 -1
- package/dist/types/web/search/index.d.ts +3 -1
- package/package.json +21 -9
- package/scripts/build-sdk-package-smoke.ts +39 -1
- package/scripts/generate-hotkeys-docs.ts +74 -0
- package/scripts/generate-sdk-operation-inventory.ts +64 -20
- package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
- package/src/async/job-manager.ts +455 -69
- package/src/cli/config-cli.ts +75 -2
- package/src/cli/notify-cli.ts +92 -26
- package/src/cli/session-picker.ts +20 -3
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/harness.ts +34 -106
- package/src/commands/launch.ts +6 -4
- package/src/commands/mcp-serve.ts +85 -8
- package/src/commands/mcp.ts +2 -2
- package/src/commands/notify.ts +8 -4
- package/src/commands/sdk.ts +61 -40
- package/src/commands/ultragoal.ts +4 -1
- package/src/config/atomic-yaml-patch.ts +80 -13
- package/src/config/file-lock.ts +105 -43
- package/src/config/keybindings.ts +64 -20
- package/src/config/model-auth.ts +9 -0
- package/src/config/model-bindings-applier.ts +139 -0
- package/src/config/model-discovery-manager.ts +224 -0
- package/src/config/model-equivalence.ts +66 -0
- package/src/config/model-registry.ts +169 -302
- package/src/config/model-resolver.ts +361 -369
- package/src/config/models-config-schema.ts +5 -4
- package/src/config/settings-schema.ts +159 -2
- package/src/config/settings.ts +458 -482
- package/src/coordinator-mcp/policy.ts +76 -11
- package/src/coordinator-mcp/question-gate-codec.ts +274 -0
- package/src/coordinator-mcp/question-state.ts +778 -0
- package/src/coordinator-mcp/server.ts +1702 -238
- package/src/debug/index.ts +1 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
- package/src/defaults/gjc/skills/team/SKILL.md +16 -0
- package/src/discovery/helpers.ts +1 -50
- package/src/edit/index.ts +64 -0
- package/src/eval/py/env.ts +26 -0
- package/src/eval/py/kernel.ts +5 -3
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +12 -3
- package/src/extensibility/extensions/types.ts +39 -1
- package/src/extensibility/gjc-plugins/installer.ts +16 -16
- package/src/extensibility/hooks/runner.ts +2 -2
- package/src/extensibility/runtime-skill-discovery.ts +33 -13
- package/src/extensibility/shared-events.ts +2 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
- package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
- package/src/gjc-runtime/deep-interview-state.ts +317 -5
- package/src/gjc-runtime/goal-mode-request.ts +55 -8
- package/src/gjc-runtime/launch-tmux.ts +2 -5
- package/src/gjc-runtime/launch-worktree.ts +5 -5
- package/src/gjc-runtime/linux-proc.ts +71 -0
- package/src/gjc-runtime/ralplan-runtime.ts +74 -90
- package/src/gjc-runtime/session-resolution.ts +18 -3
- package/src/gjc-runtime/session-state-sidecar.ts +6 -107
- package/src/gjc-runtime/state-migrations.ts +1 -1
- package/src/gjc-runtime/state-runtime.ts +320 -249
- package/src/gjc-runtime/state-writer.ts +36 -40
- package/src/gjc-runtime/team-launch.ts +319 -0
- package/src/gjc-runtime/team-notify.ts +326 -0
- package/src/gjc-runtime/team-runtime.ts +1099 -1482
- package/src/gjc-runtime/team-store.ts +828 -0
- package/src/gjc-runtime/team-workers.ts +962 -0
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
- package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
- package/src/gjc-runtime/tmux-sessions.ts +4 -21
- package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
- package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
- package/src/gjc-runtime/ultragoal-guard.ts +83 -12
- package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +156 -1319
- package/src/gjc-runtime/workflow-cli-common.ts +30 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
- package/src/gjc-runtime/workflow-manifest.ts +18 -0
- package/src/goals/continuation-timeout-guard.ts +79 -0
- package/src/goals/runtime.ts +7 -5
- package/src/goals/state.ts +18 -0
- package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
- package/src/harness-control-plane/phase-rollup.ts +4 -7
- package/src/harness-control-plane/receipt-ingest.ts +5 -0
- package/src/harness-control-plane/receipt-spool.ts +51 -7
- package/src/harness-control-plane/session-lease.ts +12 -130
- package/src/hindsight/backend.ts +26 -27
- package/src/hindsight/state.ts +101 -32
- package/src/hooks/skill-state.ts +17 -11
- package/src/internal-urls/docs-index.generated.ts +21 -18
- package/src/lsp/client.ts +1 -1
- package/src/lsp/config.ts +145 -26
- package/src/lsp/index.ts +1 -1
- package/src/lsp/lspmux.ts +39 -10
- package/src/lsp/path-trust.ts +81 -0
- package/src/main.ts +28 -8
- package/src/memories/index.ts +1 -0
- package/src/memories/storage.ts +6 -2
- package/src/modes/action-registry.ts +165 -0
- package/src/modes/components/chrome.ts +40 -0
- package/src/modes/components/command-palette.ts +168 -0
- package/src/modes/components/custom-editor.ts +1 -1
- package/src/modes/components/custom-provider-wizard.ts +51 -14
- package/src/modes/components/index.ts +2 -1
- package/src/modes/components/pet-selector.ts +11 -17
- package/src/modes/components/plan-preview-overlay.ts +295 -0
- package/src/modes/components/queue-mode-selector.ts +10 -27
- package/src/modes/components/queue-pane.ts +79 -0
- package/src/modes/components/read-tool-group.ts +13 -0
- package/src/modes/components/session-observer-overlay.ts +207 -772
- package/src/modes/components/sessions-dashboard.ts +181 -0
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +10 -24
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +1 -969
- package/src/modes/components/tasks-pane.ts +73 -0
- package/src/modes/components/theme-selector.ts +11 -31
- package/src/modes/components/tool-execution.ts +22 -0
- package/src/modes/components/tool-status-header.ts +1056 -0
- package/src/modes/components/transcript-viewer-overlay.ts +384 -0
- package/src/modes/controllers/btw-controller.ts +1 -0
- package/src/modes/controllers/command-controller.ts +9 -15
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +72 -25
- package/src/modes/controllers/goal-mode-controller.ts +474 -0
- package/src/modes/controllers/input-controller.ts +764 -229
- package/src/modes/controllers/mode-gate.ts +24 -0
- package/src/modes/controllers/plan-mode-controller.ts +517 -0
- package/src/modes/controllers/selector-controller.ts +225 -10
- package/src/modes/controllers/stt-controller.ts +125 -0
- package/src/modes/interactive-mode.ts +302 -1151
- package/src/modes/jobs-observer.ts +3 -0
- package/src/modes/runtime-init.ts +8 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +78 -19
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
- package/src/modes/tasks-aggregator.ts +199 -0
- package/src/modes/transcript-item-registry.ts +229 -0
- package/src/modes/types.ts +47 -20
- package/src/modes/utils/ui-helpers.ts +17 -9
- package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
- package/src/prompts/agent-fragments/restricted-bash.md +1 -0
- package/src/prompts/agents/architect.md +2 -8
- package/src/prompts/agents/critic.md +2 -8
- package/src/prompts/agents/executor.md +3 -0
- package/src/prompts/agents/planner.md +4 -12
- package/src/prompts/system/custom-system-prompt.md +1 -23
- package/src/prompts/system/plan-mode-approved.md +8 -0
- package/src/prompts/system/system-prompt.md +17 -96
- package/src/prompts/tools/bash.md +4 -4
- package/src/prompts/tools/browser.md +15 -52
- package/src/prompts/tools/find.md +2 -2
- package/src/prompts/tools/hashline.md +13 -55
- package/src/prompts/tools/read.md +3 -6
- package/src/prompts/tools/search-tool-bm25.md +0 -2
- package/src/prompts/tools/search.md +2 -4
- package/src/prompts/tools/skill-discovery.md +1 -2
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-mcp/manager.ts +1 -1
- package/src/sdk/broker/broker.ts +206 -78
- package/src/sdk/broker/discovery.ts +21 -2
- package/src/sdk/broker/ensure.ts +85 -25
- package/src/sdk/broker/lifecycle-codec.ts +11 -0
- package/src/sdk/broker/lifecycle-ledger.ts +371 -74
- package/src/sdk/broker/lifecycle.ts +1399 -142
- package/src/sdk/broker/process-incarnation.ts +5 -13
- package/src/sdk/broker/session-index.ts +145 -56
- package/src/sdk/broker/state-version.ts +21 -0
- package/src/sdk/broker/transport.ts +1 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -28
- package/src/sdk/bus/chat-effect-journal.ts +14 -4
- package/src/sdk/bus/config-commands.ts +5 -4
- package/src/sdk/bus/config.ts +98 -0
- package/src/sdk/bus/conversation-store.ts +5 -29
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +25 -8
- package/src/sdk/bus/html-format.ts +20 -3
- package/src/sdk/bus/index.ts +834 -113
- package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
- package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
- package/src/sdk/bus/natives-augment.d.ts +26 -1
- package/src/sdk/bus/notification-service.ts +168 -20
- package/src/sdk/bus/rate-limit-pool.ts +58 -14
- package/src/sdk/bus/recent-activity.ts +188 -74
- package/src/sdk/bus/rich-document.ts +258 -0
- package/src/sdk/bus/rich-render.ts +62 -70
- package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
- package/src/sdk/bus/telegram-daemon.ts +1491 -247
- package/src/sdk/bus/threaded-inbound.ts +8 -3
- package/src/sdk/bus/threaded-render.ts +57 -1
- package/src/sdk/bus/topic-registry.ts +120 -13
- package/src/sdk/host/control/dispatch.ts +14 -2
- package/src/sdk/host/control/operations.ts +6 -1
- package/src/sdk/host/host.ts +31 -1
- package/src/sdk/host/query/handlers.ts +20 -22
- package/src/sdk/host/query/revision-store.ts +80 -57
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
- package/src/sdk/session-directory.ts +141 -0
- package/src/sdk/session.ts +458 -479
- package/src/session/agent-session.ts +1901 -637
- package/src/session/agent-storage.ts +18 -0
- package/src/session/cache-economics.ts +25 -8
- package/src/session/fallback-chain-controller.ts +53 -3
- package/src/session/internal/managed-session-scope.ts +2226 -0
- package/src/session/internal/managed-session-storage.ts +453 -0
- package/src/session/messages.ts +91 -2
- package/src/session/session-manager-internal.ts +33 -0
- package/src/session/session-manager.ts +1112 -333
- package/src/session/session-storage.ts +444 -39
- package/src/session/volatile-context-pruning.ts +78 -0
- package/src/setup/credential-auto-import.ts +2 -1
- package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
- package/src/setup/provider-onboarding.ts +36 -61
- package/src/setup/provider-presets.json +5 -0
- package/src/skill-state/active-state.ts +3 -13
- package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
- package/src/skill-state/workflow-state-contract.ts +6 -41
- package/src/slash-commands/builtin-registry.ts +46 -11
- package/src/system-prompt.ts +37 -44
- package/src/task/agents.ts +19 -1
- package/src/task/executor.ts +90 -6
- package/src/task/index.ts +62 -37
- package/src/thinking.ts +21 -6
- package/src/tool-discovery/tool-index.ts +2 -134
- package/src/tools/ask.ts +157 -21
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/bash.ts +38 -0
- package/src/tools/browser/dead-tab-recovery.ts +78 -0
- package/src/tools/browser/tab-supervisor.ts +282 -36
- package/src/tools/computer.ts +10 -3
- package/src/tools/cron.ts +30 -16
- package/src/tools/eval.ts +1 -2
- package/src/tools/fetch.ts +11 -4
- package/src/tools/index.ts +76 -24
- package/src/tools/output-meta.ts +13 -5
- package/src/tools/read.ts +37 -1
- package/src/tools/resource-gc.ts +2 -2
- package/src/tools/search-tool-bm25.ts +11 -74
- package/src/tools/subagent-render.ts +19 -8
- package/src/tools/subagent.ts +84 -32
- package/src/utils/event-bus.ts +9 -4
- package/src/web/search/index.ts +3 -3
- package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
- package/src/discovery/plugin-dir-roots.ts +0 -28
- package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
package/src/async/job-manager.ts
CHANGED
|
@@ -12,6 +12,7 @@ const DELIVERY_MAX_TEXT_BYTES = 64 * 1024;
|
|
|
12
12
|
const DELIVERY_PREVIEW_HEAD_BYTES = 32 * 1024;
|
|
13
13
|
const DELIVERY_PREVIEW_TAIL_BYTES = 32 * 1024;
|
|
14
14
|
const DELIVERY_MAX_ATTEMPTS = 3;
|
|
15
|
+
const MAX_DEAD_LETTERED_DELIVERIES = 50;
|
|
15
16
|
|
|
16
17
|
export interface AsyncJob {
|
|
17
18
|
id: string;
|
|
@@ -75,6 +76,47 @@ export type SubagentRunOutcome = { kind: "completed"; text: string } | { kind: "
|
|
|
75
76
|
/** Canonical lifecycle of a subagent across pause/resume cycles. */
|
|
76
77
|
export type SubagentLifecycle = "running" | "paused" | "queued" | "completed" | "failed" | "cancelled";
|
|
77
78
|
|
|
79
|
+
/** Maximum time allowed to prove owned subagents have stopped before replacement. */
|
|
80
|
+
export const OWNER_SUBAGENT_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
81
|
+
|
|
82
|
+
export class OwnerSubagentShutdownError extends Error {
|
|
83
|
+
readonly code = "owner_shutdown_in_progress";
|
|
84
|
+
|
|
85
|
+
constructor() {
|
|
86
|
+
super("Cannot start subagent while owner shutdown is in progress.");
|
|
87
|
+
this.name = "OwnerSubagentShutdownError";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface OwnerSubagentShutdownTarget {
|
|
92
|
+
subagentId: string;
|
|
93
|
+
jobId: string | null;
|
|
94
|
+
source: "record" | "metadata_job";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface OwnerSubagentShutdownLease {
|
|
98
|
+
ownerId: string;
|
|
99
|
+
id: string;
|
|
100
|
+
targets: readonly OwnerSubagentShutdownTarget[];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface OwnerSubagentShutdownProof {
|
|
104
|
+
ownerId: string;
|
|
105
|
+
leaseId: string;
|
|
106
|
+
confirmed: boolean;
|
|
107
|
+
reason: "confirmed" | "deadline_exceeded" | "missing_terminal_evidence" | "lease_lost";
|
|
108
|
+
targets: readonly OwnerSubagentShutdownTarget[];
|
|
109
|
+
terminalIds: readonly string[];
|
|
110
|
+
unresolvedIds: readonly string[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface OwnerSubagentShutdownLeaseState {
|
|
114
|
+
lease: OwnerSubagentShutdownLease;
|
|
115
|
+
backingJobIds: ReadonlyMap<string, readonly string[]>;
|
|
116
|
+
phase: "active" | "proving" | "proved";
|
|
117
|
+
proof?: OwnerSubagentShutdownProof;
|
|
118
|
+
}
|
|
119
|
+
|
|
78
120
|
/**
|
|
79
121
|
* Live, executor-owned control handle for a RUNNING subagent. Registered when a
|
|
80
122
|
* subagent run starts and removed on pause/terminal so a paused subagent retains
|
|
@@ -122,6 +164,12 @@ export interface ResumeDescriptor {
|
|
|
122
164
|
data: unknown;
|
|
123
165
|
}
|
|
124
166
|
|
|
167
|
+
/**
|
|
168
|
+
* In-memory resume runner bound to the session that originally launched a
|
|
169
|
+
* subagent. Never serialized: process restart drops it so resume fails closed.
|
|
170
|
+
*/
|
|
171
|
+
export type ResumeRunner = (subagentId: string, message?: string, descriptor?: ResumeDescriptor) => string | undefined;
|
|
172
|
+
|
|
125
173
|
function sessionFileFromResumeDescriptorData(data: unknown): string | null {
|
|
126
174
|
if (typeof data !== "object" || data === null) return null;
|
|
127
175
|
const sessionFile = (data as { sessionFile?: unknown }).sessionFile;
|
|
@@ -160,6 +208,12 @@ interface AsyncJobDelivery {
|
|
|
160
208
|
promise?: Promise<void>;
|
|
161
209
|
}
|
|
162
210
|
|
|
211
|
+
interface DeadLetteredDelivery {
|
|
212
|
+
jobId: string;
|
|
213
|
+
attempt: number;
|
|
214
|
+
lastError?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
163
217
|
export interface AsyncJobDeliveryState {
|
|
164
218
|
queued: number;
|
|
165
219
|
delivering: boolean;
|
|
@@ -324,9 +378,20 @@ export class AsyncJobManager {
|
|
|
324
378
|
readonly #subagentProgress = new Map<string, AgentProgress>();
|
|
325
379
|
readonly #resumeQueue: ResumeQueueEntry[] = [];
|
|
326
380
|
#resumeSeq = 0;
|
|
327
|
-
#resumeRunner?:
|
|
381
|
+
#resumeRunner?: ResumeRunner;
|
|
328
382
|
readonly #resumeDescriptors = new Map<string, ResumeDescriptor>();
|
|
329
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Per-descriptor in-memory resume runners, keyed by subagentId, captured at
|
|
385
|
+
* registerResumeDescriptor time so each resume executes under the authority of
|
|
386
|
+
* the session that originally launched that subagent. Fixes #2303's global
|
|
387
|
+
* last-writer-wins slot. In-memory only: a process restart drops these and
|
|
388
|
+
* resume fails closed with reason "no_runner".
|
|
389
|
+
*/
|
|
390
|
+
readonly #descriptorResumeRunners = new Map<string, ResumeRunner>();
|
|
391
|
+
readonly #deadLetteredDeliveries = new Map<string, DeadLetteredDelivery>();
|
|
392
|
+
readonly #deadLetteredDeliveryOwners = new Map<string, string | undefined>();
|
|
393
|
+
readonly #ownerSubagentShutdownLeases = new Map<string, OwnerSubagentShutdownLeaseState>();
|
|
394
|
+
#ownerSubagentShutdownSeq = 0;
|
|
330
395
|
#lastDisposeDiagnostics: AsyncJobDisposeDiagnostics = { stuckJobIds: [], deliveriesDrained: true };
|
|
331
396
|
/**
|
|
332
397
|
* Change listeners notified on any mutation that can alter the live job set
|
|
@@ -387,6 +452,9 @@ export class AsyncJobManager {
|
|
|
387
452
|
if (this.#disposed) {
|
|
388
453
|
throw new Error("Async job manager is disposed");
|
|
389
454
|
}
|
|
455
|
+
if (options?.ownerId && this.#isOwnerSubagentShutdownFenced(options.ownerId)) {
|
|
456
|
+
throw new OwnerSubagentShutdownError();
|
|
457
|
+
}
|
|
390
458
|
const runningCount = this.getRunningJobs().length;
|
|
391
459
|
if (runningCount >= this.#maxRunningJobs) {
|
|
392
460
|
throw new Error(
|
|
@@ -687,14 +755,23 @@ export class AsyncJobManager {
|
|
|
687
755
|
}
|
|
688
756
|
|
|
689
757
|
/** Install the TaskTool-owned resume runner. Returns the new job id, or undefined on failure. */
|
|
690
|
-
setResumeRunner(
|
|
691
|
-
runner: (subagentId: string, message?: string, descriptor?: ResumeDescriptor) => string | undefined,
|
|
692
|
-
): void {
|
|
758
|
+
setResumeRunner(runner: ResumeRunner): void {
|
|
693
759
|
this.#resumeRunner = runner;
|
|
694
760
|
}
|
|
695
761
|
|
|
696
|
-
registerResumeDescriptor(descriptor: ResumeDescriptor): void {
|
|
762
|
+
registerResumeDescriptor(descriptor: ResumeDescriptor, runner?: ResumeRunner): void {
|
|
697
763
|
this.#resumeDescriptors.set(descriptor.subagentId, descriptor);
|
|
764
|
+
if (runner) this.#descriptorResumeRunners.set(descriptor.subagentId, runner);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Resolve the resume runner for a subagent: prefer the per-descriptor runner
|
|
769
|
+
* captured at registration time (the originating parent's execution authority),
|
|
770
|
+
* falling back to the process-global runner only for descriptors registered
|
|
771
|
+
* without one.
|
|
772
|
+
*/
|
|
773
|
+
#resolveResumeRunner(subagentId: string): ResumeRunner | undefined {
|
|
774
|
+
return this.#descriptorResumeRunners.get(subagentId) ?? this.#resumeRunner;
|
|
698
775
|
}
|
|
699
776
|
|
|
700
777
|
getResumeDescriptor(subagentId: string, filter?: AsyncJobFilter): ResumeDescriptor | undefined {
|
|
@@ -704,6 +781,224 @@ export class AsyncJobManager {
|
|
|
704
781
|
return descriptor;
|
|
705
782
|
}
|
|
706
783
|
|
|
784
|
+
#isOwnerSubagentShutdownFenced(ownerId: string | undefined): boolean {
|
|
785
|
+
return ownerId !== undefined && this.#ownerSubagentShutdownLeases.has(ownerId);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
#isTerminalSubagentStatus(status: SubagentLifecycle): boolean {
|
|
789
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
beginOwnerSubagentShutdown(ownerId: string): OwnerSubagentShutdownLease | undefined {
|
|
793
|
+
if (!ownerId || this.#ownerSubagentShutdownLeases.has(ownerId)) return undefined;
|
|
794
|
+
const targets = new Map<string, OwnerSubagentShutdownTarget>();
|
|
795
|
+
const backingJobIds = new Map<string, Set<string>>();
|
|
796
|
+
const addBackingJob = (subagentId: string, jobId: string | null): void => {
|
|
797
|
+
if (!jobId) return;
|
|
798
|
+
const ids = backingJobIds.get(subagentId) ?? new Set<string>();
|
|
799
|
+
ids.add(jobId);
|
|
800
|
+
backingJobIds.set(subagentId, ids);
|
|
801
|
+
};
|
|
802
|
+
for (const record of this.#subagentRecords.values()) {
|
|
803
|
+
if (record.ownerId !== ownerId || this.#isTerminalSubagentStatus(record.status)) continue;
|
|
804
|
+
targets.set(record.subagentId, {
|
|
805
|
+
subagentId: record.subagentId,
|
|
806
|
+
jobId: record.status === "queued" ? null : record.currentJobId,
|
|
807
|
+
source: "record",
|
|
808
|
+
});
|
|
809
|
+
if (record.status !== "queued") addBackingJob(record.subagentId, record.currentJobId);
|
|
810
|
+
}
|
|
811
|
+
for (const job of this.#jobs.values()) {
|
|
812
|
+
const subagentId = job.metadata?.subagent?.id;
|
|
813
|
+
if (
|
|
814
|
+
job.ownerId !== ownerId ||
|
|
815
|
+
!subagentId ||
|
|
816
|
+
(job.status !== "running" && job.status !== "paused" && job.status !== "cancelled")
|
|
817
|
+
) {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
if (!targets.has(subagentId)) {
|
|
821
|
+
targets.set(subagentId, { subagentId, jobId: job.id, source: "metadata_job" });
|
|
822
|
+
}
|
|
823
|
+
addBackingJob(subagentId, job.id);
|
|
824
|
+
}
|
|
825
|
+
const lease: OwnerSubagentShutdownLease = {
|
|
826
|
+
ownerId,
|
|
827
|
+
id: `owner_shutdown_${++this.#ownerSubagentShutdownSeq}`,
|
|
828
|
+
targets: Array.from(targets.values()),
|
|
829
|
+
};
|
|
830
|
+
this.#ownerSubagentShutdownLeases.set(ownerId, {
|
|
831
|
+
lease,
|
|
832
|
+
backingJobIds: new Map<string, readonly string[]>(
|
|
833
|
+
Array.from(backingJobIds, ([subagentId, jobIds]): [string, readonly string[]] => [
|
|
834
|
+
subagentId,
|
|
835
|
+
Array.from(jobIds),
|
|
836
|
+
]),
|
|
837
|
+
),
|
|
838
|
+
phase: "active",
|
|
839
|
+
});
|
|
840
|
+
return lease;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
runOwnerProducerCleanups(filter?: AsyncJobFilter): void {
|
|
844
|
+
this.#runOwnerProducerCleanups(filter, false);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
runOwnerProducerCleanupsStrict(filter?: AsyncJobFilter): void {
|
|
848
|
+
this.#runOwnerProducerCleanups(filter, true);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
#runOwnerProducerCleanups(filter: AsyncJobFilter | undefined, strict: boolean): void {
|
|
852
|
+
const ownerId = filter?.ownerId;
|
|
853
|
+
const targets: Array<[string, Set<() => void>]> = [];
|
|
854
|
+
if (ownerId) {
|
|
855
|
+
const bag = this.#ownerCleanups.get(ownerId);
|
|
856
|
+
if (bag) targets.push([ownerId, bag]);
|
|
857
|
+
} else {
|
|
858
|
+
for (const entry of this.#ownerCleanups.entries()) targets.push(entry);
|
|
859
|
+
}
|
|
860
|
+
const errors: unknown[] = [];
|
|
861
|
+
for (const [id, bag] of targets) {
|
|
862
|
+
const callbacks = Array.from(bag);
|
|
863
|
+
bag.clear();
|
|
864
|
+
this.#ownerCleanups.delete(id);
|
|
865
|
+
for (const cleanup of callbacks) {
|
|
866
|
+
try {
|
|
867
|
+
cleanup();
|
|
868
|
+
} catch (error) {
|
|
869
|
+
errors.push(error);
|
|
870
|
+
if (strict) {
|
|
871
|
+
let retryBag = this.#ownerCleanups.get(id);
|
|
872
|
+
if (!retryBag) {
|
|
873
|
+
retryBag = new Set();
|
|
874
|
+
this.#ownerCleanups.set(id, retryBag);
|
|
875
|
+
}
|
|
876
|
+
retryBag.add(cleanup);
|
|
877
|
+
}
|
|
878
|
+
logger.warn("Async job owner cleanup failed", {
|
|
879
|
+
ownerId: id,
|
|
880
|
+
error: error instanceof Error ? error.message : String(error),
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
if (strict && errors.length > 0) throw new AggregateError(errors, "Async job owner cleanup failed");
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
async cancelAndProveOwnerSubagents(
|
|
889
|
+
lease: OwnerSubagentShutdownLease,
|
|
890
|
+
options?: { timeoutMs?: number },
|
|
891
|
+
): Promise<OwnerSubagentShutdownProof> {
|
|
892
|
+
const state = this.#ownerSubagentShutdownLeases.get(lease.ownerId);
|
|
893
|
+
if (!state || state.lease.id !== lease.id) {
|
|
894
|
+
return this.#ownerSubagentShutdownProof(
|
|
895
|
+
lease,
|
|
896
|
+
"lease_lost",
|
|
897
|
+
lease.targets.map(target => target.subagentId),
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
if (state.phase === "proved" && state.proof) return state.proof;
|
|
901
|
+
state.phase = "proving";
|
|
902
|
+
const settled = new Set<string>();
|
|
903
|
+
const promises: Promise<void>[] = [];
|
|
904
|
+
for (const target of lease.targets) {
|
|
905
|
+
const backingJobs = (state.backingJobIds.get(target.subagentId) ?? []).map(jobId => ({
|
|
906
|
+
jobId,
|
|
907
|
+
job: this.#jobs.get(jobId),
|
|
908
|
+
}));
|
|
909
|
+
if (target.source === "record") this.cancelSubagent(target.subagentId, { ownerId: lease.ownerId });
|
|
910
|
+
for (const { jobId, job } of backingJobs) {
|
|
911
|
+
this.cancel(jobId, { ownerId: lease.ownerId });
|
|
912
|
+
if (!job || job.ownerId !== lease.ownerId) continue;
|
|
913
|
+
promises.push(
|
|
914
|
+
job.promise.then(
|
|
915
|
+
() => {
|
|
916
|
+
settled.add(jobId);
|
|
917
|
+
},
|
|
918
|
+
() => {
|
|
919
|
+
settled.add(jobId);
|
|
920
|
+
},
|
|
921
|
+
),
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
const timeoutMs = Math.max(0, options?.timeoutMs ?? OWNER_SUBAGENT_SHUTDOWN_TIMEOUT_MS);
|
|
926
|
+
let deadlineExceeded = false;
|
|
927
|
+
await Promise.race([
|
|
928
|
+
Promise.allSettled(promises),
|
|
929
|
+
Bun.sleep(timeoutMs).then(() => {
|
|
930
|
+
deadlineExceeded = true;
|
|
931
|
+
}),
|
|
932
|
+
]);
|
|
933
|
+
const current = this.#ownerSubagentShutdownLeases.get(lease.ownerId);
|
|
934
|
+
if (!current || current.lease.id !== lease.id || current.phase !== "proving") {
|
|
935
|
+
return this.#ownerSubagentShutdownProof(
|
|
936
|
+
lease,
|
|
937
|
+
"lease_lost",
|
|
938
|
+
lease.targets.map(target => target.subagentId),
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
const unresolvedIds = lease.targets
|
|
942
|
+
.filter(target => !this.#hasTerminalShutdownEvidence(target, lease.ownerId, current.backingJobIds, settled))
|
|
943
|
+
.map(target => target.subagentId);
|
|
944
|
+
const reason =
|
|
945
|
+
unresolvedIds.length === 0
|
|
946
|
+
? "confirmed"
|
|
947
|
+
: deadlineExceeded
|
|
948
|
+
? "deadline_exceeded"
|
|
949
|
+
: "missing_terminal_evidence";
|
|
950
|
+
const proof = this.#ownerSubagentShutdownProof(lease, reason, unresolvedIds);
|
|
951
|
+
current.phase = "proved";
|
|
952
|
+
current.proof = proof;
|
|
953
|
+
return proof;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
#hasTerminalShutdownEvidence(
|
|
957
|
+
target: OwnerSubagentShutdownTarget,
|
|
958
|
+
ownerId: string,
|
|
959
|
+
backingJobIds: ReadonlyMap<string, readonly string[]>,
|
|
960
|
+
settled: ReadonlySet<string>,
|
|
961
|
+
): boolean {
|
|
962
|
+
const record = this.#subagentRecords.get(target.subagentId);
|
|
963
|
+
if (!record || record.ownerId !== ownerId || !this.#isTerminalSubagentStatus(record.status)) return false;
|
|
964
|
+
return (backingJobIds.get(target.subagentId) ?? []).every(jobId => {
|
|
965
|
+
if (!settled.has(jobId)) return false;
|
|
966
|
+
const job = this.#jobs.get(jobId);
|
|
967
|
+
return job === undefined || job.ownerId === ownerId;
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
#ownerSubagentShutdownProof(
|
|
972
|
+
lease: OwnerSubagentShutdownLease,
|
|
973
|
+
reason: OwnerSubagentShutdownProof["reason"],
|
|
974
|
+
unresolvedIds: readonly string[],
|
|
975
|
+
): OwnerSubagentShutdownProof {
|
|
976
|
+
const unresolved = new Set(unresolvedIds);
|
|
977
|
+
return {
|
|
978
|
+
ownerId: lease.ownerId,
|
|
979
|
+
leaseId: lease.id,
|
|
980
|
+
confirmed: reason === "confirmed",
|
|
981
|
+
reason,
|
|
982
|
+
targets: lease.targets,
|
|
983
|
+
terminalIds: lease.targets
|
|
984
|
+
.filter(target => !unresolved.has(target.subagentId))
|
|
985
|
+
.map(target => target.subagentId),
|
|
986
|
+
unresolvedIds: [...unresolvedIds],
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
finishOwnerSubagentShutdown(lease: OwnerSubagentShutdownLease, outcome: "commit" | "release"): void {
|
|
991
|
+
const state = this.#ownerSubagentShutdownLeases.get(lease.ownerId);
|
|
992
|
+
if (!state || state.lease.id !== lease.id) return;
|
|
993
|
+
if (outcome === "commit" && state.proof?.confirmed) {
|
|
994
|
+
const pendingJobIds = this.getDeliveryState({ ownerId: lease.ownerId }).pendingJobIds;
|
|
995
|
+
this.acknowledgeDeliveries(pendingJobIds);
|
|
996
|
+
this.#purgeOwnerSubagentState(lease.ownerId);
|
|
997
|
+
}
|
|
998
|
+
this.#ownerSubagentShutdownLeases.delete(lease.ownerId);
|
|
999
|
+
if (outcome === "release") this.#ensureDeliveryLoop();
|
|
1000
|
+
}
|
|
1001
|
+
|
|
707
1002
|
#recordByJobId(jobId: string): SubagentRecord | undefined {
|
|
708
1003
|
for (const rec of this.#subagentRecords.values()) {
|
|
709
1004
|
if (rec.currentJobId === jobId) return rec;
|
|
@@ -758,6 +1053,9 @@ export class AsyncJobManager {
|
|
|
758
1053
|
): { ok: boolean; status?: SubagentLifecycle; jobId?: string; queued?: boolean; reason?: string } {
|
|
759
1054
|
const rec = this.getSubagentRecord(subagentId, filter);
|
|
760
1055
|
if (!rec) return { ok: false, reason: "not_found" };
|
|
1056
|
+
if (this.#isOwnerSubagentShutdownFenced(rec.ownerId)) {
|
|
1057
|
+
return { ok: false, status: rec.status, reason: "owner_shutdown_in_progress" };
|
|
1058
|
+
}
|
|
761
1059
|
if (rec.status === "running") return { ok: false, status: "running", reason: "already_running" };
|
|
762
1060
|
if (rec.status === "queued") {
|
|
763
1061
|
if (message !== undefined && rec.queued) {
|
|
@@ -769,7 +1067,7 @@ export class AsyncJobManager {
|
|
|
769
1067
|
return { ok: false, status: "queued", reason: "already_queued" };
|
|
770
1068
|
}
|
|
771
1069
|
if (!rec.resumable || !rec.sessionFile) return { ok: false, reason: "context_unavailable" };
|
|
772
|
-
if (!this.#
|
|
1070
|
+
if (!this.#resolveResumeRunner(rec.subagentId)) return { ok: false, reason: "no_runner" };
|
|
773
1071
|
if (this.getRunningJobs().length >= this.#maxRunningJobs) {
|
|
774
1072
|
const seq = ++this.#resumeSeq;
|
|
775
1073
|
rec.status = "queued";
|
|
@@ -790,11 +1088,15 @@ export class AsyncJobManager {
|
|
|
790
1088
|
rec: SubagentRecord,
|
|
791
1089
|
message?: string,
|
|
792
1090
|
): { ok: boolean; status?: SubagentLifecycle; jobId?: string; reason?: string } {
|
|
1091
|
+
if (this.#isOwnerSubagentShutdownFenced(rec.ownerId)) {
|
|
1092
|
+
return { ok: false, status: rec.status, reason: "owner_shutdown_in_progress" };
|
|
1093
|
+
}
|
|
793
1094
|
const prevJobId = rec.currentJobId;
|
|
794
1095
|
// Clear any retained progress from the previous run so a resumed subagent
|
|
795
1096
|
// never renders the prior run's tool/output as live before it emits again.
|
|
796
1097
|
this.#subagentProgress.delete(rec.subagentId);
|
|
797
|
-
const
|
|
1098
|
+
const runner = this.#resolveResumeRunner(rec.subagentId);
|
|
1099
|
+
const newJobId = runner?.(rec.subagentId, message, this.#resumeDescriptors.get(rec.subagentId));
|
|
798
1100
|
if (!newJobId) return { ok: false, reason: "resume_failed" };
|
|
799
1101
|
if (prevJobId && prevJobId !== newJobId) rec.historicalJobIds.push(prevJobId);
|
|
800
1102
|
rec.currentJobId = newJobId;
|
|
@@ -803,16 +1105,36 @@ export class AsyncJobManager {
|
|
|
803
1105
|
return { ok: true, status: rec.status, jobId: newJobId };
|
|
804
1106
|
}
|
|
805
1107
|
|
|
806
|
-
/** Drain queued resumes
|
|
1108
|
+
/** Drain queued resumes while preserving fenced owners and allowing foreign progress. */
|
|
807
1109
|
#drainResumeQueue(): void {
|
|
808
1110
|
if (this.#resumeQueue.length === 0) return;
|
|
809
1111
|
this.#resumeQueue.sort((a, b) => a.seq - b.seq);
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1112
|
+
let index = 0;
|
|
1113
|
+
while (index < this.#resumeQueue.length && this.getRunningJobs().length < this.#maxRunningJobs) {
|
|
1114
|
+
const entry = this.#resumeQueue[index];
|
|
813
1115
|
const rec = this.#subagentRecords.get(entry.subagentId);
|
|
814
|
-
if (rec?.status !== "queued")
|
|
815
|
-
|
|
1116
|
+
if (rec?.status !== "queued") {
|
|
1117
|
+
this.#resumeQueue.splice(index, 1);
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
if (this.#isOwnerSubagentShutdownFenced(entry.ownerId)) {
|
|
1121
|
+
index += 1;
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
try {
|
|
1125
|
+
const result = this.#startResume(rec, entry.message);
|
|
1126
|
+
if (result.reason === "owner_shutdown_in_progress") {
|
|
1127
|
+
index += 1;
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
this.#resumeQueue.splice(index, 1);
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
if (error instanceof OwnerSubagentShutdownError) {
|
|
1133
|
+
index += 1;
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
throw error;
|
|
1137
|
+
}
|
|
816
1138
|
}
|
|
817
1139
|
}
|
|
818
1140
|
|
|
@@ -854,6 +1176,7 @@ export class AsyncJobManager {
|
|
|
854
1176
|
if (!ownerId || rec.ownerId === ownerId) {
|
|
855
1177
|
this.#liveHandles.delete(sid);
|
|
856
1178
|
this.#resumeDescriptors.delete(sid);
|
|
1179
|
+
this.#descriptorResumeRunners.delete(sid);
|
|
857
1180
|
this.#subagentRecords.delete(sid);
|
|
858
1181
|
this.#subagentProgress.delete(sid);
|
|
859
1182
|
}
|
|
@@ -1011,45 +1334,23 @@ export class AsyncJobManager {
|
|
|
1011
1334
|
};
|
|
1012
1335
|
}
|
|
1013
1336
|
|
|
1014
|
-
/**
|
|
1015
|
-
* Run and clear every registered cleanup for the given filter. Idempotent
|
|
1016
|
-
* and error-isolated: a throwing cleanup does not prevent siblings from
|
|
1017
|
-
* running and never escalates to the caller.
|
|
1018
|
-
*/
|
|
1337
|
+
/** Run producer cleanups, then perform the legacy destructive subagent purge. */
|
|
1019
1338
|
runOwnerCleanups(filter?: AsyncJobFilter): void {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (ownerId) {
|
|
1023
|
-
const bag = this.#ownerCleanups.get(ownerId);
|
|
1024
|
-
if (bag) targets.push([ownerId, bag]);
|
|
1025
|
-
} else {
|
|
1026
|
-
for (const entry of this.#ownerCleanups.entries()) targets.push(entry);
|
|
1027
|
-
}
|
|
1028
|
-
for (const [id, bag] of targets) {
|
|
1029
|
-
const callbacks = Array.from(bag);
|
|
1030
|
-
bag.clear();
|
|
1031
|
-
this.#ownerCleanups.delete(id);
|
|
1032
|
-
for (const cleanup of callbacks) {
|
|
1033
|
-
try {
|
|
1034
|
-
cleanup();
|
|
1035
|
-
} catch (error) {
|
|
1036
|
-
logger.warn("Async job owner cleanup failed", {
|
|
1037
|
-
ownerId: id,
|
|
1038
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
this.#purgeOwnerSubagentState(ownerId);
|
|
1339
|
+
this.runOwnerProducerCleanups(filter);
|
|
1340
|
+
this.#purgeOwnerSubagentState(filter?.ownerId);
|
|
1044
1341
|
}
|
|
1045
1342
|
|
|
1046
1343
|
getDeliveryState(filter?: AsyncJobFilter): AsyncJobDeliveryState {
|
|
1344
|
+
this.#expireMonitorTombstones();
|
|
1345
|
+
this.#pruneEvictedDeadLetters();
|
|
1047
1346
|
const deliveries = this.#filterDeliveries(filter);
|
|
1048
1347
|
const inFlightDeliveries = this.#filterInFlightDeliveries(filter);
|
|
1049
1348
|
const ownerId = filter?.ownerId;
|
|
1050
|
-
const deadLettered =
|
|
1051
|
-
|
|
1052
|
-
|
|
1349
|
+
const deadLettered = ownerId
|
|
1350
|
+
? Array.from(this.#deadLetteredDeliveries.keys()).filter(
|
|
1351
|
+
jobId => this.#deadLetteredDeliveryOwners.get(jobId) === ownerId,
|
|
1352
|
+
).length
|
|
1353
|
+
: this.#deadLetteredDeliveries.size;
|
|
1053
1354
|
const nextRetryAt = deliveries.reduce<number | undefined>((next, delivery) => {
|
|
1054
1355
|
if (next === undefined) return delivery.nextAttemptAt;
|
|
1055
1356
|
return Math.min(next, delivery.nextAttemptAt);
|
|
@@ -1084,6 +1385,7 @@ export class AsyncJobManager {
|
|
|
1084
1385
|
removed += 1;
|
|
1085
1386
|
}
|
|
1086
1387
|
}
|
|
1388
|
+
if (removed > 0) this.#ensureDeliveryLoop();
|
|
1087
1389
|
return removed;
|
|
1088
1390
|
}
|
|
1089
1391
|
|
|
@@ -1099,7 +1401,7 @@ export class AsyncJobManager {
|
|
|
1099
1401
|
this.#deliveries.splice(
|
|
1100
1402
|
0,
|
|
1101
1403
|
this.#deliveries.length,
|
|
1102
|
-
...this.#deliveries.filter(delivery => !this
|
|
1404
|
+
...this.#deliveries.filter(delivery => !this.#isDeliveryAcknowledged(delivery.jobId)),
|
|
1103
1405
|
);
|
|
1104
1406
|
return before - this.#deliveries.length;
|
|
1105
1407
|
}
|
|
@@ -1119,6 +1421,48 @@ export class AsyncJobManager {
|
|
|
1119
1421
|
}
|
|
1120
1422
|
}
|
|
1121
1423
|
|
|
1424
|
+
async waitForOwnerInFlightDeliveries(ownerId: string, options?: { timeoutMs?: number }): Promise<boolean> {
|
|
1425
|
+
const inFlight = this.#inFlightDeliveries
|
|
1426
|
+
.filter(delivery => delivery.ownerId === ownerId)
|
|
1427
|
+
.map(delivery => delivery.promise)
|
|
1428
|
+
.filter((promise): promise is Promise<void> => promise !== undefined);
|
|
1429
|
+
if (inFlight.length === 0) return true;
|
|
1430
|
+
const timeoutMs = Math.max(0, options?.timeoutMs ?? OWNER_SUBAGENT_SHUTDOWN_TIMEOUT_MS);
|
|
1431
|
+
let timedOut = false;
|
|
1432
|
+
await Promise.race([
|
|
1433
|
+
Promise.allSettled(inFlight),
|
|
1434
|
+
Bun.sleep(timeoutMs).then(() => {
|
|
1435
|
+
timedOut = true;
|
|
1436
|
+
}),
|
|
1437
|
+
]);
|
|
1438
|
+
return !timedOut;
|
|
1439
|
+
}
|
|
1440
|
+
async cancelAndSettleOwnerJobs(ownerId: string, options?: { timeoutMs?: number }): Promise<boolean> {
|
|
1441
|
+
const jobs = this.getAllJobs({ ownerId });
|
|
1442
|
+
for (const job of jobs) this.cancel(job.id, { ownerId });
|
|
1443
|
+
const timeoutMs = Math.max(0, options?.timeoutMs ?? OWNER_SUBAGENT_SHUTDOWN_TIMEOUT_MS);
|
|
1444
|
+
let timedOut = false;
|
|
1445
|
+
await Promise.race([
|
|
1446
|
+
Promise.allSettled(jobs.map(job => job.promise)),
|
|
1447
|
+
Bun.sleep(timeoutMs).then(() => {
|
|
1448
|
+
timedOut = true;
|
|
1449
|
+
}),
|
|
1450
|
+
]);
|
|
1451
|
+
const inFlight = this.#inFlightDeliveries
|
|
1452
|
+
.filter(delivery => delivery.ownerId === ownerId)
|
|
1453
|
+
.map(delivery => delivery.promise)
|
|
1454
|
+
.filter((promise): promise is Promise<void> => promise !== undefined);
|
|
1455
|
+
if (inFlight.length === 0) return !timedOut;
|
|
1456
|
+
let deliveryTimedOut = false;
|
|
1457
|
+
await Promise.race([
|
|
1458
|
+
Promise.allSettled(inFlight),
|
|
1459
|
+
Bun.sleep(timeoutMs).then(() => {
|
|
1460
|
+
deliveryTimedOut = true;
|
|
1461
|
+
}),
|
|
1462
|
+
]);
|
|
1463
|
+
return !timedOut && !deliveryTimedOut;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1122
1466
|
getLastDisposeDiagnostics(): AsyncJobDisposeDiagnostics {
|
|
1123
1467
|
return { ...this.#lastDisposeDiagnostics, stuckJobIds: [...this.#lastDisposeDiagnostics.stuckJobIds] };
|
|
1124
1468
|
}
|
|
@@ -1221,6 +1565,7 @@ export class AsyncJobManager {
|
|
|
1221
1565
|
this.#deliveries.length = 0;
|
|
1222
1566
|
this.#inFlightDeliveries.length = 0;
|
|
1223
1567
|
this.#deadLetteredDeliveries.clear();
|
|
1568
|
+
this.#deadLetteredDeliveryOwners.clear();
|
|
1224
1569
|
this.#suppressedDeliveries.clear();
|
|
1225
1570
|
this.#watchedJobs.clear();
|
|
1226
1571
|
this.#outputState.clear();
|
|
@@ -1229,7 +1574,9 @@ export class AsyncJobManager {
|
|
|
1229
1574
|
this.#liveHandles.clear();
|
|
1230
1575
|
this.#subagentProgress.clear();
|
|
1231
1576
|
this.#resumeDescriptors.clear();
|
|
1577
|
+
this.#descriptorResumeRunners.clear();
|
|
1232
1578
|
this.#resumeQueue.length = 0;
|
|
1579
|
+
this.#ownerSubagentShutdownLeases.clear();
|
|
1233
1580
|
this.#notifyChange();
|
|
1234
1581
|
this.#changeListeners.clear();
|
|
1235
1582
|
return drained && waitResult.completed;
|
|
@@ -1281,12 +1628,15 @@ export class AsyncJobManager {
|
|
|
1281
1628
|
}
|
|
1282
1629
|
|
|
1283
1630
|
#evictJob(jobId: string): void {
|
|
1631
|
+
this.#expireMonitorTombstones();
|
|
1284
1632
|
this.#recordMonitorTombstone(jobId);
|
|
1285
1633
|
this.#runLifecycle(jobId, "evict");
|
|
1286
1634
|
this.#purgeTerminalSubagentStateForJob(jobId);
|
|
1287
1635
|
this.#jobs.delete(jobId);
|
|
1288
1636
|
this.#lifecycles.delete(jobId);
|
|
1289
1637
|
this.#lifecyclePhases.delete(jobId);
|
|
1638
|
+
this.#deadLetteredDeliveries.delete(jobId);
|
|
1639
|
+
this.#deadLetteredDeliveryOwners.delete(jobId);
|
|
1290
1640
|
this.#suppressedDeliveries.delete(jobId);
|
|
1291
1641
|
this.#watchedJobs.delete(jobId);
|
|
1292
1642
|
this.#outputState.delete(jobId);
|
|
@@ -1315,12 +1665,22 @@ export class AsyncJobManager {
|
|
|
1315
1665
|
);
|
|
1316
1666
|
}
|
|
1317
1667
|
|
|
1668
|
+
#isDeliveryFenced(delivery: AsyncJobDelivery): boolean {
|
|
1669
|
+
return Boolean(delivery.ownerId && this.#isOwnerSubagentShutdownFenced(delivery.ownerId));
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
#hasDeliverable(): boolean {
|
|
1673
|
+
return this.#deliveries.some(
|
|
1674
|
+
delivery => !this.isDeliverySuppressed(delivery.jobId) && !this.#isDeliveryFenced(delivery),
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1318
1678
|
async #deliverNextFiltered(filter: AsyncJobFilter, deadline: number): Promise<boolean> {
|
|
1319
1679
|
while (true) {
|
|
1320
1680
|
let selected: AsyncJobDelivery | undefined;
|
|
1321
1681
|
for (const delivery of this.#deliveries) {
|
|
1322
1682
|
if (delivery.ownerId !== filter.ownerId) continue;
|
|
1323
|
-
if (this.isDeliverySuppressed(delivery.jobId)) continue;
|
|
1683
|
+
if (this.isDeliverySuppressed(delivery.jobId) || this.#isDeliveryFenced(delivery)) continue;
|
|
1324
1684
|
if (!selected || delivery.nextAttemptAt < selected.nextAttemptAt) {
|
|
1325
1685
|
selected = delivery;
|
|
1326
1686
|
}
|
|
@@ -1328,7 +1688,7 @@ export class AsyncJobManager {
|
|
|
1328
1688
|
|
|
1329
1689
|
if (!selected) {
|
|
1330
1690
|
const inFlight = this.#filterInFlightDeliveries(filter);
|
|
1331
|
-
if (inFlight.length === 0) return
|
|
1691
|
+
if (inFlight.length === 0) return this.#filterDeliveries(filter).length === 0;
|
|
1332
1692
|
return this.#waitForDeliveryPromise(inFlight[0]?.promise, deadline);
|
|
1333
1693
|
}
|
|
1334
1694
|
|
|
@@ -1348,13 +1708,44 @@ export class AsyncJobManager {
|
|
|
1348
1708
|
}
|
|
1349
1709
|
}
|
|
1350
1710
|
|
|
1711
|
+
#isDeliveryAcknowledged(jobId: string): boolean {
|
|
1712
|
+
return this.#suppressedDeliveries.has(jobId);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1351
1715
|
isDeliverySuppressed(jobId: string): boolean {
|
|
1352
|
-
return this.#
|
|
1716
|
+
return this.#isDeliveryAcknowledged(jobId) || this.#watchedJobs.has(jobId);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
#pruneEvictedDeadLetters(): void {
|
|
1720
|
+
for (const jobId of this.#deadLetteredDeliveries.keys()) {
|
|
1721
|
+
if (this.#jobs.has(jobId)) continue;
|
|
1722
|
+
this.#deadLetteredDeliveries.delete(jobId);
|
|
1723
|
+
this.#deadLetteredDeliveryOwners.delete(jobId);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
#recordDeadLetter(delivery: AsyncJobDelivery): void {
|
|
1728
|
+
this.#pruneEvictedDeadLetters();
|
|
1729
|
+
if (!this.#jobs.has(delivery.jobId)) return;
|
|
1730
|
+
this.#deadLetteredDeliveries.delete(delivery.jobId);
|
|
1731
|
+
this.#deadLetteredDeliveryOwners.delete(delivery.jobId);
|
|
1732
|
+
this.#deadLetteredDeliveries.set(delivery.jobId, {
|
|
1733
|
+
jobId: delivery.jobId,
|
|
1734
|
+
attempt: delivery.attempt,
|
|
1735
|
+
lastError: delivery.lastError,
|
|
1736
|
+
});
|
|
1737
|
+
this.#deadLetteredDeliveryOwners.set(delivery.jobId, delivery.ownerId);
|
|
1738
|
+
while (this.#deadLetteredDeliveries.size > MAX_DEAD_LETTERED_DELIVERIES) {
|
|
1739
|
+
const oldestJobId = this.#deadLetteredDeliveries.keys().next().value;
|
|
1740
|
+
if (oldestJobId === undefined) return;
|
|
1741
|
+
this.#deadLetteredDeliveries.delete(oldestJobId);
|
|
1742
|
+
this.#deadLetteredDeliveryOwners.delete(oldestJobId);
|
|
1743
|
+
}
|
|
1353
1744
|
}
|
|
1354
1745
|
|
|
1355
1746
|
#enqueueDelivery(jobId: string, text: string): void {
|
|
1356
1747
|
// Skip delivery if already acknowledged
|
|
1357
|
-
if (this
|
|
1748
|
+
if (this.#isDeliveryAcknowledged(jobId)) {
|
|
1358
1749
|
return;
|
|
1359
1750
|
}
|
|
1360
1751
|
const deliveryText = this.#boundedDeliveryText(text);
|
|
@@ -1369,7 +1760,7 @@ export class AsyncJobManager {
|
|
|
1369
1760
|
});
|
|
1370
1761
|
while (this.#deliveries.length > DEFAULT_MAX_DELIVERY_QUEUE) {
|
|
1371
1762
|
const dropped = this.#deliveries.shift();
|
|
1372
|
-
if (dropped) this.#
|
|
1763
|
+
if (dropped) this.#recordDeadLetter(dropped);
|
|
1373
1764
|
}
|
|
1374
1765
|
this.#ensureDeliveryLoop();
|
|
1375
1766
|
}
|
|
@@ -1399,7 +1790,7 @@ export class AsyncJobManager {
|
|
|
1399
1790
|
})
|
|
1400
1791
|
.finally(() => {
|
|
1401
1792
|
this.#deliveryLoop = undefined;
|
|
1402
|
-
if (!this.#disposed && this.#
|
|
1793
|
+
if (!this.#disposed && this.#hasDeliverable()) {
|
|
1403
1794
|
this.#ensureDeliveryLoop();
|
|
1404
1795
|
}
|
|
1405
1796
|
});
|
|
@@ -1407,24 +1798,19 @@ export class AsyncJobManager {
|
|
|
1407
1798
|
|
|
1408
1799
|
async #runDeliveryLoop(): Promise<void> {
|
|
1409
1800
|
while (this.#deliveries.length > 0) {
|
|
1410
|
-
const delivery = this.#deliveries
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
}
|
|
1801
|
+
const delivery = this.#deliveries.find(
|
|
1802
|
+
candidate => !this.isDeliverySuppressed(candidate.jobId) && !this.#isDeliveryFenced(candidate),
|
|
1803
|
+
);
|
|
1804
|
+
if (!delivery) return;
|
|
1415
1805
|
const waitMs = delivery.nextAttemptAt - Date.now();
|
|
1416
1806
|
if (waitMs > 0) {
|
|
1417
1807
|
await Bun.sleep(waitMs);
|
|
1418
1808
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
if (this.isDeliverySuppressed(delivery.jobId)) {
|
|
1423
|
-
this.#deliveries.shift();
|
|
1424
|
-
continue;
|
|
1425
|
-
}
|
|
1809
|
+
const index = this.#deliveries.indexOf(delivery);
|
|
1810
|
+
if (index === -1) continue;
|
|
1811
|
+
if (this.isDeliverySuppressed(delivery.jobId) || this.#isDeliveryFenced(delivery)) continue;
|
|
1426
1812
|
|
|
1427
|
-
this.#deliveries.
|
|
1813
|
+
this.#deliveries.splice(index, 1);
|
|
1428
1814
|
await this.#deliverDelivery(delivery);
|
|
1429
1815
|
}
|
|
1430
1816
|
}
|
|
@@ -1438,7 +1824,7 @@ export class AsyncJobManager {
|
|
|
1438
1824
|
delivery.attempt += 1;
|
|
1439
1825
|
delivery.lastError = error instanceof Error ? error.message : String(error);
|
|
1440
1826
|
if (delivery.attempt >= DELIVERY_MAX_ATTEMPTS) {
|
|
1441
|
-
this.#
|
|
1827
|
+
this.#recordDeadLetter(delivery);
|
|
1442
1828
|
logger.warn("Async job completion delivery reached retry cap", {
|
|
1443
1829
|
jobId: delivery.jobId,
|
|
1444
1830
|
attempt: delivery.attempt,
|
|
@@ -1446,7 +1832,7 @@ export class AsyncJobManager {
|
|
|
1446
1832
|
});
|
|
1447
1833
|
} else {
|
|
1448
1834
|
delivery.nextAttemptAt = Date.now() + this.#getRetryDelay(delivery.attempt);
|
|
1449
|
-
if (!this
|
|
1835
|
+
if (!this.#isDeliveryAcknowledged(delivery.jobId)) {
|
|
1450
1836
|
this.#deliveries.push(delivery);
|
|
1451
1837
|
}
|
|
1452
1838
|
logger.warn("Async job completion delivery failed", {
|
|
@@ -1459,7 +1845,7 @@ export class AsyncJobManager {
|
|
|
1459
1845
|
} finally {
|
|
1460
1846
|
const index = this.#inFlightDeliveries.indexOf(delivery);
|
|
1461
1847
|
if (index !== -1) this.#inFlightDeliveries.splice(index, 1);
|
|
1462
|
-
if (!this.#disposed && this.#
|
|
1848
|
+
if (!this.#disposed && this.#hasDeliverable()) this.#ensureDeliveryLoop();
|
|
1463
1849
|
}
|
|
1464
1850
|
})();
|
|
1465
1851
|
delivery.promise = promise;
|