@gajae-code/coding-agent 0.18.11 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +760 -0
- package/dist/types/async/job-manager.d.ts +7 -3
- package/dist/types/async/result-formatting.d.ts +2 -0
- package/dist/types/autoresearch/git.d.ts +1 -1
- package/dist/types/capability/fs.d.ts +2 -2
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/accounts-cli.d.ts +2 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
- package/dist/types/cli/install-activation.d.ts +10 -4
- package/dist/types/cli/public-command-errors.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +21 -3
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/cli-ordinary.d.ts +2 -2
- package/dist/types/commands/accounts.d.ts +2 -1
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +13 -0
- package/dist/types/commit/changelog/index.d.ts +1 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +53 -1
- package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
- package/dist/types/config/config-hot-reload.d.ts +44 -0
- package/dist/types/config/file-lock.d.ts +52 -3
- package/dist/types/config/model-discovery-manager.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +79 -9
- package/dist/types/config/model-registry.d.ts +72 -8
- package/dist/types/config/model-resolver.d.ts +7 -4
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +57 -4
- package/dist/types/config/settings.d.ts +33 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/config.d.ts +8 -4
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/debug/system-info.d.ts +6 -1
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/discovery/mcp-compat.d.ts +3 -1
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +14 -0
- package/dist/types/edit/modes/replace.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +12 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +2 -1
- package/dist/types/migrate/adapters/index.d.ts +42 -1
- package/dist/types/migrate/executor.d.ts +3 -1
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +82 -3
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -0
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
- package/dist/types/runtime-mcp/manager.d.ts +7 -0
- package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
- package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
- package/dist/types/sdk/broker/broker.d.ts +29 -2
- package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
- package/dist/types/sdk/broker/ensure.d.ts +141 -9
- package/dist/types/sdk/broker/hop.d.ts +45 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
- package/dist/types/sdk/broker/session-index.d.ts +6 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +44 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/cli/session-cli.d.ts +0 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +40 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
- package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +107 -9
- package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
- package/dist/types/session/messages.d.ts +5 -1
- package/dist/types/session/session-manager.d.ts +29 -3
- package/dist/types/session/startup-auth-config.d.ts +24 -2
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/session/terminal-abort.d.ts +3 -1
- package/dist/types/session-import/service.d.ts +1 -1
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/executor.d.ts +19 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
- package/dist/types/tools/atomic-file-write.d.ts +9 -7
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +15 -2
- package/dist/types/tools/fetch.d.ts +7 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/python.d.ts +1 -1
- package/dist/types/tools/report-issue.d.ts +41 -0
- package/dist/types/tools/subagent.d.ts +23 -0
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/scrapers/types.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/dist/types/web/search/providers/insane.d.ts +7 -1
- package/dist/types/web/search/providers/searxng.d.ts +5 -0
- package/package.json +10 -9
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/build-binary.ts +2 -1
- package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
- package/scripts/generate-docs-index.ts +8 -1
- package/scripts/generate-sdk-operation-inventory.ts +38 -4
- package/src/async/job-manager.ts +356 -56
- package/src/async/result-formatting.ts +40 -0
- package/src/autoresearch/git.ts +9 -2
- package/src/autoresearch/prompts/prompt-setup.md +1 -1
- package/src/capability/fs.ts +58 -22
- package/src/capability/index.ts +2 -2
- package/src/capability/rule.ts +11 -2
- package/src/cli/accounts-cli.ts +176 -8
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/auth-broker-cli.ts +21 -11
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/doctor/install-repairs.ts +35 -5
- package/src/cli/fast-help.ts +1 -3
- package/src/cli/install-activation.ts +47 -4
- package/src/cli/migrate-cli.ts +19 -6
- package/src/cli/public-command-errors.ts +6 -0
- package/src/cli/root-flags.ts +2 -4
- package/src/cli/stats-cli.ts +9 -5
- package/src/cli/update-cli.ts +471 -13
- package/src/cli-ordinary.ts +7 -2
- package/src/commands/accounts.ts +10 -5
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +277 -50
- package/src/commands/ultragoal.ts +16 -8
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/changelog/index.ts +39 -1
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/message.ts +10 -3
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +50 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload-trust.ts +148 -0
- package/src/config/config-hot-reload.ts +742 -0
- package/src/config/file-lock.ts +1615 -328
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-bindings-applier.ts +85 -47
- package/src/config/model-discovery-manager.ts +10 -0
- package/src/config/model-profile-activation.ts +889 -148
- package/src/config/model-registry.ts +1706 -205
- package/src/config/model-resolver.ts +52 -22
- package/src/config/models-config-schema.ts +6 -0
- package/src/config/settings-schema.ts +73 -4
- package/src/config/settings.ts +460 -24
- package/src/coordinator-mcp/policy.ts +26 -1
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +7 -1
- package/src/customization/import.ts +279 -8
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/config.ts +31 -11
- package/src/dap/defaults.json +1 -1
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +89 -6
- package/src/debug/report-bundle.ts +90 -2
- package/src/debug/system-info.ts +8 -3
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +25 -16
- package/src/discovery/mcp-compat.ts +120 -17
- package/src/edit/apply-patch/index.ts +12 -1
- package/src/edit/index.ts +204 -22
- package/src/edit/modes/patch.ts +214 -8
- package/src/edit/modes/replace.ts +136 -0
- package/src/eval/js/context-manager.ts +29 -3
- package/src/eval/js/worker-entry.ts +51 -18
- package/src/eval/py/executor.ts +14 -4
- package/src/eval/py/runner.py +144 -5
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/bash-executor.ts +81 -4
- package/src/exec/bash-shell-guardian.ts +4 -1
- package/src/exec/bash-shell-supervisor.ts +8 -4
- package/src/exec/isolated-shell.ts +5 -1
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +86 -6
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/custom-tools/wrapper.ts +3 -1
- package/src/extensibility/extensions/loader.ts +10 -1
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +37 -7
- package/src/gjc-runtime/launch-tmux.ts +30 -15
- package/src/gjc-runtime/launch-worktree.test.ts +148 -0
- package/src/gjc-runtime/launch-worktree.ts +101 -14
- package/src/gjc-runtime/managed-owner-admission.ts +46 -2
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/session-resolution.ts +38 -6
- package/src/gjc-runtime/session-state-lock.ts +59 -14
- package/src/gjc-runtime/state-runtime.ts +23 -19
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +17 -12
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +23 -22
- package/src/internal-urls/local-protocol.ts +167 -8
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/lsp/index.ts +29 -0
- package/src/lsp/workspace-path.ts +23 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +574 -2
- package/src/migrate/adapters/claude-code.ts +162 -5
- package/src/migrate/adapters/codex.ts +77 -7
- package/src/migrate/adapters/index.ts +258 -5
- package/src/migrate/adapters/opencode.ts +10 -1
- package/src/migrate/executor.ts +222 -7
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +63 -2
- package/src/migrate/types.ts +84 -4
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/acp/permission-mode.ts +18 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/assistant-message.ts +55 -1
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/settings-selector.ts +103 -37
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +300 -9
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +66 -11
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +76 -7
- package/src/modes/interactive-mode.ts +123 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/theme/theme.ts +15 -0
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/report-issue.md +13 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-credential-selector.ts +2 -2
- package/src/runtime-mcp/content-limits.ts +7 -0
- package/src/runtime-mcp/manager-pool.test.ts +5 -4
- package/src/runtime-mcp/manager.ts +40 -3
- package/src/runtime-mcp/oauth-flow.ts +32 -6
- package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/render.ts +16 -1
- package/src/runtime-mcp/tool-bridge.ts +194 -21
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +10 -5
- package/src/sdk/broker/broker.ts +484 -85
- package/src/sdk/broker/daemon-entry.ts +338 -116
- package/src/sdk/broker/doctor-restart.ts +2 -1
- package/src/sdk/broker/ensure.ts +1214 -268
- package/src/sdk/broker/hop.ts +286 -0
- package/src/sdk/broker/lifecycle-ledger.ts +38 -11
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/lock-host-identity.ts +29 -0
- package/src/sdk/broker/session-index.ts +91 -16
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-command-policy.ts +16 -3
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +460 -35
- package/src/sdk/bus/reconciliation-store.ts +201 -8
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +23 -6
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1737 -129
- package/src/sdk/host/session-runtime.ts +684 -83
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +15 -1
- package/src/sdk/prompt-deadline-flush.ts +78 -5
- package/src/sdk/prompt-deadline-manager.ts +68 -14
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +81 -12
- package/src/sdk/protocol/operation-inventory.generated.json +261 -2
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/reconciliation-extensions.ts +1 -0
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +237 -97
- package/src/session/agent-session.ts +2726 -479
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +173 -57
- package/src/session/internal/managed-session-storage.ts +85 -12
- package/src/session/internal/native-publish-outcome.ts +5 -1
- package/src/session/messages.ts +47 -6
- package/src/session/session-manager.ts +326 -19
- package/src/session/startup-auth-config.ts +116 -8
- package/src/session/streaming-output.ts +262 -4
- package/src/session/terminal-abort.ts +14 -2
- package/src/session-import/redact.ts +53 -2
- package/src/session-import/service.ts +1 -0
- package/src/skill-state/workflow-mutation-guard.ts +2145 -206
- package/src/slash-commands/builtin-registry.ts +21 -6
- package/src/slash-commands/helpers/credential-switch.ts +10 -5
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +115 -20
- package/src/task/index.ts +389 -175
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-edit-write-path.ts +64 -0
- package/src/tools/ast-edit.ts +18 -1
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/atomic-file-write.ts +46 -15
- package/src/tools/auto-generated-guard.ts +15 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +214 -6
- package/src/tools/bash-interactive.ts +14 -2
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +113 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +16 -8
- package/src/tools/descriptors.test.ts +44 -0
- package/src/tools/descriptors.ts +50 -6
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +93 -4
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/index.ts +2 -0
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +74 -5
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +26 -37
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/report-issue.ts +184 -0
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent-render.ts +114 -12
- package/src/tools/subagent.ts +118 -5
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/todo-write.ts +41 -3
- package/src/tools/tool-catalog.generated.ts +32 -4
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +42 -3
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +12 -4
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +75 -7
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +72 -22
- package/src/web/scrapers/index.ts +3 -1
- package/src/web/scrapers/types.ts +8 -0
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
- package/src/web/search/providers/insane.ts +91 -28
- package/src/web/search/providers/searxng.ts +62 -10
package/src/async/job-manager.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { logger } from "@gajae-code/utils";
|
|
2
2
|
import {
|
|
3
3
|
lookupOwnedRegistration,
|
|
4
|
+
migrateOwnedAttemptSaturationsForEndpoint,
|
|
4
5
|
registerOwnedRegistration,
|
|
5
6
|
resolveToolLineage,
|
|
6
7
|
retireOwnedRegistrationForDeadLetter,
|
|
@@ -192,7 +193,15 @@ export interface SubagentRecord {
|
|
|
192
193
|
* file, followed by a separately available runner (`no_runner` otherwise).
|
|
193
194
|
*/
|
|
194
195
|
resumable: boolean;
|
|
195
|
-
queued?: {
|
|
196
|
+
queued?: {
|
|
197
|
+
ownerId?: string;
|
|
198
|
+
seq: number;
|
|
199
|
+
message?: string;
|
|
200
|
+
resumeToolCallId?: string;
|
|
201
|
+
admissionEndpointId?: string;
|
|
202
|
+
ownedRegistrationEndpointId?: string;
|
|
203
|
+
createdAt: number;
|
|
204
|
+
};
|
|
196
205
|
/** Last queued-resume seq for a CANCELLED queued resume (rec.queued is
|
|
197
206
|
* cleared on cancel): retained on the record so owned settlement's second
|
|
198
207
|
* proof can still see the generation as provably cancelled, without a
|
|
@@ -227,6 +236,7 @@ interface OwnerSubagentShutdownRecordCapture {
|
|
|
227
236
|
readonly record: SubagentRecord;
|
|
228
237
|
readonly currentJobId: string | null;
|
|
229
238
|
readonly currentJobGeneration?: string;
|
|
239
|
+
readonly queuedSeq?: number;
|
|
230
240
|
}
|
|
231
241
|
|
|
232
242
|
/** Lightweight, manager-owned resume payload. The async layer treats `data` as opaque. */
|
|
@@ -245,6 +255,8 @@ export type ResumeRunner = (
|
|
|
245
255
|
message?: string,
|
|
246
256
|
descriptor?: ResumeDescriptor,
|
|
247
257
|
resumeToolCallId?: string,
|
|
258
|
+
admissionEndpointId?: string,
|
|
259
|
+
ownedRegistrationEndpointId?: string,
|
|
248
260
|
) => string | undefined;
|
|
249
261
|
|
|
250
262
|
function sessionFileFromResumeDescriptorData(data: unknown): string | null {
|
|
@@ -275,6 +287,9 @@ interface ResumeQueueEntry {
|
|
|
275
287
|
ownerId?: string;
|
|
276
288
|
seq: number;
|
|
277
289
|
message?: string;
|
|
290
|
+
resumeToolCallId?: string;
|
|
291
|
+
admissionEndpointId?: string;
|
|
292
|
+
ownedRegistrationEndpointId?: string;
|
|
278
293
|
createdAt: number;
|
|
279
294
|
}
|
|
280
295
|
|
|
@@ -633,7 +648,7 @@ export class AsyncJobManager {
|
|
|
633
648
|
* resume can neither resolve its lineage nor register its owned tuple
|
|
634
649
|
* (review thread P1). Returns TRUE when the mapping was moved (or no move
|
|
635
650
|
* was needed); returns FALSE when the successor endpoint is owned by a
|
|
636
|
-
|
|
651
|
+
/**
|
|
637
652
|
* BEFORE retiring predecessor state, because leaving this manager under
|
|
638
653
|
* the predecessor while tools resolve the successor to the foreign
|
|
639
654
|
* manager sends jobs to the wrong session and owned aborts lose their
|
|
@@ -653,6 +668,11 @@ export class AsyncJobManager {
|
|
|
653
668
|
}
|
|
654
669
|
AsyncJobManager.#byEndpoint.delete(predecessorEndpointId);
|
|
655
670
|
AsyncJobManager.#byEndpoint.set(successorEndpointId, manager);
|
|
671
|
+
migrateOwnedAttemptSaturationsForEndpoint(predecessorEndpointId, successorEndpointId);
|
|
672
|
+
// Migrate queued ownership registrations from the predecessor endpoint to
|
|
673
|
+
// the successor. The admission endpoint remains unchanged for exact lineage
|
|
674
|
+
// lookup; each queue entry tracks the separate endpoint used for ownership.
|
|
675
|
+
manager.#migrateQueuedResumeRegistrations(predecessorEndpointId, successorEndpointId);
|
|
656
676
|
return true;
|
|
657
677
|
}
|
|
658
678
|
|
|
@@ -1225,6 +1245,7 @@ export class AsyncJobManager {
|
|
|
1225
1245
|
this.#externallySettled.delete(job.generation);
|
|
1226
1246
|
if (this.#jobs.get(id) === job) this.#settledJobs.add(job);
|
|
1227
1247
|
this.#settleExecution(execution);
|
|
1248
|
+
if (!this.#disposed && !this.#registrationClosed) this.#drainResumeQueue();
|
|
1228
1249
|
})
|
|
1229
1250
|
.then(completion.resolve, completion.reject);
|
|
1230
1251
|
|
|
@@ -1261,13 +1282,19 @@ export class AsyncJobManager {
|
|
|
1261
1282
|
cancel(id: string, filter?: AsyncJobFilter): boolean {
|
|
1262
1283
|
if (id.startsWith("queued:")) {
|
|
1263
1284
|
// A queued resume (no real job yet): owned settlement cancels it by
|
|
1264
|
-
// removing the queued subagent record and publishing the terminal
|
|
1265
|
-
//
|
|
1285
|
+
// removing the queued subagent record and publishing the terminal.
|
|
1286
|
+
// Preserve generation identity by checking that the requested sequence
|
|
1287
|
+
// matches the current record's queued sequence (review thread P2).
|
|
1266
1288
|
const colon = id.lastIndexOf(":");
|
|
1267
1289
|
const subagentId = colon > "queued:".length ? id.slice("queued:".length, colon) : undefined;
|
|
1268
1290
|
if (!subagentId) return false;
|
|
1291
|
+
const seqStr = id.slice(colon + 1);
|
|
1292
|
+
// Validate that seqStr is exactly a positive integer without leading zeros (e.g., 1, 123, not 01, 1junk, 1.5)
|
|
1293
|
+
if (!/^[1-9][0-9]*$/.test(seqStr)) return false;
|
|
1294
|
+
const seq = parseInt(seqStr, 10);
|
|
1295
|
+
if (Number.isNaN(seq)) return false;
|
|
1269
1296
|
const rec = this.getSubagentRecord(subagentId);
|
|
1270
|
-
if (rec?.status !== "queued") return false;
|
|
1297
|
+
if (rec?.status !== "queued" || rec.queued?.seq !== seq) return false;
|
|
1271
1298
|
return this.cancelSubagent(subagentId, filter);
|
|
1272
1299
|
}
|
|
1273
1300
|
const job = this.#jobs.get(id);
|
|
@@ -1383,6 +1410,10 @@ export class AsyncJobManager {
|
|
|
1383
1410
|
if (currentJob && record.currentJobGeneration === undefined) record.currentJobGeneration = currentJob.generation;
|
|
1384
1411
|
this.#subagentRecords.set(record.subagentId, record);
|
|
1385
1412
|
this.#notifyChange();
|
|
1413
|
+
// Drain stale entries in case this replacement invalidated queued resumes
|
|
1414
|
+
// for the previous owner/generation. Stale-entry retirement is independent
|
|
1415
|
+
// of capacity, so it happens immediately, not waiting for a free slot.
|
|
1416
|
+
this.#drainResumeQueue();
|
|
1386
1417
|
}
|
|
1387
1418
|
|
|
1388
1419
|
/**
|
|
@@ -1564,6 +1595,7 @@ export class AsyncJobManager {
|
|
|
1564
1595
|
record,
|
|
1565
1596
|
currentJobId: record.currentJobId,
|
|
1566
1597
|
currentJobGeneration: record.currentJobGeneration,
|
|
1598
|
+
queuedSeq: record.status === "queued" ? record.queued?.seq : undefined,
|
|
1567
1599
|
});
|
|
1568
1600
|
if (this.#isTerminalSubagentStatus(record.status) && backingExecution?.physicallySettled !== false) continue;
|
|
1569
1601
|
targets.set(record.subagentId, {
|
|
@@ -1675,20 +1707,23 @@ export class AsyncJobManager {
|
|
|
1675
1707
|
for (const target of lease.targets) {
|
|
1676
1708
|
const executions = state.backingExecutions.get(target.subagentId) ?? [];
|
|
1677
1709
|
const capture = state.records.get(target.subagentId);
|
|
1678
|
-
|
|
1710
|
+
const record = capture?.record;
|
|
1711
|
+
const preserveQueuedRecord =
|
|
1679
1712
|
target.source === "record" &&
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1713
|
+
target.jobId === null &&
|
|
1714
|
+
capture !== undefined &&
|
|
1715
|
+
record !== undefined &&
|
|
1716
|
+
this.#subagentRecords.get(target.subagentId) === record &&
|
|
1717
|
+
record.ownerId === lease.ownerId &&
|
|
1718
|
+
record.currentJobId === capture.currentJobId &&
|
|
1719
|
+
record.currentJobGeneration === capture.currentJobGeneration &&
|
|
1720
|
+
record.status === "queued" &&
|
|
1721
|
+
record.queued?.seq === capture.queuedSeq;
|
|
1686
1722
|
for (const execution of executions) {
|
|
1687
|
-
this.#cancelExactExecution(execution);
|
|
1723
|
+
if (!preserveQueuedRecord) this.#cancelExactExecution(execution);
|
|
1688
1724
|
promises.add(execution.physicalCompletion);
|
|
1689
1725
|
}
|
|
1690
1726
|
if (target.source !== "record" || executions.length > 0) continue;
|
|
1691
|
-
const record = capture?.record;
|
|
1692
1727
|
if (!capture || !record || this.#subagentRecords.get(target.subagentId) !== record) continue;
|
|
1693
1728
|
if (record.status === "paused") {
|
|
1694
1729
|
const row = record.currentJobId ? this.#jobs.get(record.currentJobId) : undefined;
|
|
@@ -1737,19 +1772,50 @@ export class AsyncJobManager {
|
|
|
1737
1772
|
): boolean {
|
|
1738
1773
|
const capture = state.records.get(target.subagentId);
|
|
1739
1774
|
const record = capture?.record;
|
|
1775
|
+
const physicallySettled = (state.backingExecutions.get(target.subagentId) ?? []).every(
|
|
1776
|
+
execution => execution.job.ownerId === ownerId && execution.physicallySettled,
|
|
1777
|
+
);
|
|
1778
|
+
if (!capture || !record) return false;
|
|
1779
|
+
|
|
1780
|
+
// A queued record is terminal only for the captured owner record and generation:
|
|
1781
|
+
// the lease fence keeps it queued until release or commit, and it must not be
|
|
1782
|
+
// cancelled merely to collect shutdown evidence.
|
|
1783
|
+
if (
|
|
1784
|
+
target.source === "record" &&
|
|
1785
|
+
target.jobId === null &&
|
|
1786
|
+
this.#subagentRecords.get(target.subagentId) === record &&
|
|
1787
|
+
record.ownerId === ownerId &&
|
|
1788
|
+
record.currentJobId === capture.currentJobId &&
|
|
1789
|
+
record.currentJobGeneration === capture.currentJobGeneration &&
|
|
1790
|
+
record.status === "queued" &&
|
|
1791
|
+
record.queued?.seq === capture.queuedSeq
|
|
1792
|
+
) {
|
|
1793
|
+
return physicallySettled;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1740
1796
|
if (
|
|
1741
|
-
!record ||
|
|
1742
1797
|
this.#subagentRecords.get(target.subagentId) !== record ||
|
|
1743
1798
|
record.ownerId !== ownerId ||
|
|
1744
|
-
record.currentJobId !== capture.currentJobId ||
|
|
1745
|
-
record.currentJobGeneration !== capture.currentJobGeneration ||
|
|
1746
1799
|
!this.#isTerminalSubagentStatus(record.status)
|
|
1747
1800
|
) {
|
|
1748
1801
|
return false;
|
|
1749
1802
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1803
|
+
if (capture.queuedSeq !== undefined) {
|
|
1804
|
+
const queuedGeneration = `queued:${target.subagentId}:${capture.queuedSeq}`;
|
|
1805
|
+
if (
|
|
1806
|
+
record.status !== "cancelled" ||
|
|
1807
|
+
record.terminalQueuedSeq !== capture.queuedSeq ||
|
|
1808
|
+
record.terminalGeneration !== queuedGeneration
|
|
1809
|
+
) {
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
} else if (
|
|
1813
|
+
record.currentJobId !== capture.currentJobId ||
|
|
1814
|
+
record.currentJobGeneration !== capture.currentJobGeneration
|
|
1815
|
+
) {
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
return physicallySettled;
|
|
1753
1819
|
}
|
|
1754
1820
|
|
|
1755
1821
|
#ownerSubagentShutdownProof(
|
|
@@ -1771,7 +1837,11 @@ export class AsyncJobManager {
|
|
|
1771
1837
|
};
|
|
1772
1838
|
}
|
|
1773
1839
|
|
|
1774
|
-
finishOwnerSubagentShutdown(
|
|
1840
|
+
finishOwnerSubagentShutdown(
|
|
1841
|
+
lease: OwnerSubagentShutdownLease,
|
|
1842
|
+
outcome: "commit" | "release",
|
|
1843
|
+
options?: { drainResumeQueue?: boolean },
|
|
1844
|
+
): void {
|
|
1775
1845
|
const state = this.#ownerSubagentShutdownLeases.get(lease.ownerId);
|
|
1776
1846
|
if (!state || state.lease.id !== lease.id) return;
|
|
1777
1847
|
if (outcome === "commit" && state.proof?.confirmed) {
|
|
@@ -1780,7 +1850,10 @@ export class AsyncJobManager {
|
|
|
1780
1850
|
this.#purgeOwnerSubagentState(lease.ownerId);
|
|
1781
1851
|
}
|
|
1782
1852
|
this.#ownerSubagentShutdownLeases.delete(lease.ownerId);
|
|
1783
|
-
if (outcome === "release")
|
|
1853
|
+
if (outcome === "release") {
|
|
1854
|
+
if (options?.drainResumeQueue && !this.#disposed && !this.#registrationClosed) this.#drainResumeQueue();
|
|
1855
|
+
this.#ensureDeliveryLoop();
|
|
1856
|
+
}
|
|
1784
1857
|
}
|
|
1785
1858
|
|
|
1786
1859
|
#recordByJobId(jobId: string, expectedGeneration?: string): SubagentRecord | undefined {
|
|
@@ -1851,7 +1924,10 @@ export class AsyncJobManager {
|
|
|
1851
1924
|
if (rec.status === "queued") {
|
|
1852
1925
|
if (message !== undefined && rec.queued) {
|
|
1853
1926
|
rec.queued.message = message;
|
|
1854
|
-
const queued = this.#resumeQueue.find(
|
|
1927
|
+
const queued = this.#resumeQueue.find(
|
|
1928
|
+
entry =>
|
|
1929
|
+
entry.subagentId === rec.subagentId && entry.ownerId === rec.ownerId && entry.seq === rec.queued?.seq,
|
|
1930
|
+
);
|
|
1855
1931
|
if (queued) queued.message = message;
|
|
1856
1932
|
return { ok: true, queued: true, status: "queued" };
|
|
1857
1933
|
}
|
|
@@ -1864,12 +1940,28 @@ export class AsyncJobManager {
|
|
|
1864
1940
|
if (!this.#resolveResumeRunner(rec, descriptor)) return { ok: false, reason: "no_runner" };
|
|
1865
1941
|
if (this.getRunningJobs().length >= this.#maxRunningJobs) {
|
|
1866
1942
|
const seq = ++this.#resumeSeq;
|
|
1943
|
+
rec.terminalQueuedSeq = undefined;
|
|
1867
1944
|
rec.status = "queued";
|
|
1945
|
+
// Resolve the admission endpoint before queueing: when the manager is
|
|
1946
|
+
// rekeyed before the queue is drained or the subagent is cancelled, we
|
|
1947
|
+
// need the original endpoint to unregister the queued registration.
|
|
1948
|
+
let admissionEndpointId: string | undefined;
|
|
1949
|
+
let ownedRegistrationEndpointId: string | undefined;
|
|
1950
|
+
if (resumeToolCallId) {
|
|
1951
|
+
const managerEndpointId = AsyncJobManager.endpointIdOf(this);
|
|
1952
|
+
const lineage = resolveToolLineage(resumeToolCallId, managerEndpointId);
|
|
1953
|
+
if (lineage) {
|
|
1954
|
+
admissionEndpointId = lineage.endpointId ?? managerEndpointId;
|
|
1955
|
+
ownedRegistrationEndpointId = managerEndpointId ?? lineage.endpointId;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1868
1958
|
rec.queued = {
|
|
1869
1959
|
ownerId: rec.ownerId,
|
|
1870
1960
|
seq,
|
|
1871
1961
|
message,
|
|
1872
1962
|
...(resumeToolCallId ? { resumeToolCallId } : {}),
|
|
1963
|
+
...(admissionEndpointId ? { admissionEndpointId } : {}),
|
|
1964
|
+
...(ownedRegistrationEndpointId ? { ownedRegistrationEndpointId } : {}),
|
|
1873
1965
|
createdAt: Date.now(),
|
|
1874
1966
|
};
|
|
1875
1967
|
this.#resumeQueue.push({
|
|
@@ -1878,6 +1970,8 @@ export class AsyncJobManager {
|
|
|
1878
1970
|
seq,
|
|
1879
1971
|
message,
|
|
1880
1972
|
...(resumeToolCallId ? { resumeToolCallId } : {}),
|
|
1973
|
+
...(admissionEndpointId ? { admissionEndpointId } : {}),
|
|
1974
|
+
...(ownedRegistrationEndpointId ? { ownedRegistrationEndpointId } : {}),
|
|
1881
1975
|
createdAt: rec.queued.createdAt,
|
|
1882
1976
|
});
|
|
1883
1977
|
// Register the QUEUED generation as owned work of the resume request's
|
|
@@ -1890,7 +1984,7 @@ export class AsyncJobManager {
|
|
|
1890
1984
|
if (lineage) {
|
|
1891
1985
|
const queuedGeneration = `queued:${rec.subagentId}:${seq}`;
|
|
1892
1986
|
registerOwnedRegistration({
|
|
1893
|
-
...(
|
|
1987
|
+
...(ownedRegistrationEndpointId ? { endpointId: ownedRegistrationEndpointId } : {}),
|
|
1894
1988
|
lineageIdHash: lineage.lineageIdHash,
|
|
1895
1989
|
promptAttemptEpoch: lineage.promptAttemptEpoch,
|
|
1896
1990
|
endpointGeneration: lineage.endpointGeneration,
|
|
@@ -1916,21 +2010,82 @@ export class AsyncJobManager {
|
|
|
1916
2010
|
if (registration) unregisterOwnedRegistration(registration);
|
|
1917
2011
|
}
|
|
1918
2012
|
|
|
2013
|
+
#migrateQueuedResumeRegistrations(predecessorEndpointId: string, successorEndpointId: string): void {
|
|
2014
|
+
// Keep the admission endpoint for exact lineage lookup, but migrate each
|
|
2015
|
+
// queued ownership tuple with the manager so cleanup and resumed-job
|
|
2016
|
+
// ownership use the successor endpoint.
|
|
2017
|
+
|
|
2018
|
+
// Iterate through both the resume queue and subagent records to migrate all
|
|
2019
|
+
// entries whose owned registration is still at the predecessor endpoint.
|
|
2020
|
+
const processedSeqs = new Set<number>();
|
|
2021
|
+
|
|
2022
|
+
// First pass: migrate queue entries
|
|
2023
|
+
for (const queueEntry of this.#resumeQueue) {
|
|
2024
|
+
const queuedEndpointId =
|
|
2025
|
+
queueEntry.ownedRegistrationEndpointId ?? queueEntry.admissionEndpointId ?? predecessorEndpointId;
|
|
2026
|
+
if (queuedEndpointId === predecessorEndpointId) {
|
|
2027
|
+
processedSeqs.add(queueEntry.seq);
|
|
2028
|
+
|
|
2029
|
+
// Migrate the owned registration from E1 to E2
|
|
2030
|
+
const queuedGeneration = `queued:${queueEntry.subagentId}:${queueEntry.seq}`;
|
|
2031
|
+
const oldReg = lookupOwnedRegistration(queuedGeneration, queuedGeneration, queuedEndpointId);
|
|
2032
|
+
if (oldReg) {
|
|
2033
|
+
unregisterOwnedRegistration(oldReg);
|
|
2034
|
+
registerOwnedRegistration({
|
|
2035
|
+
endpointId: successorEndpointId,
|
|
2036
|
+
lineageIdHash: oldReg.lineageIdHash,
|
|
2037
|
+
promptAttemptEpoch: oldReg.promptAttemptEpoch,
|
|
2038
|
+
endpointGeneration: oldReg.endpointGeneration,
|
|
2039
|
+
jobId: queuedGeneration,
|
|
2040
|
+
jobGeneration: queuedGeneration,
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
queueEntry.ownedRegistrationEndpointId = successorEndpointId;
|
|
2044
|
+
const rec = this.#subagentRecords.get(queueEntry.subagentId);
|
|
2045
|
+
if (rec?.queued?.seq === queueEntry.seq) rec.queued.ownedRegistrationEndpointId = successorEndpointId;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// Second pass: check subagent records for queued entries not in the queue
|
|
2050
|
+
// (e.g., entries that were queued before this rekey)
|
|
2051
|
+
for (const [, rec] of this.#subagentRecords) {
|
|
2052
|
+
if (rec.queued && !processedSeqs.has(rec.queued.seq)) {
|
|
2053
|
+
const queuedEndpointId =
|
|
2054
|
+
rec.queued.ownedRegistrationEndpointId ?? rec.queued.admissionEndpointId ?? predecessorEndpointId;
|
|
2055
|
+
if (queuedEndpointId === predecessorEndpointId) {
|
|
2056
|
+
// Migrate the owned registration
|
|
2057
|
+
const queuedGeneration = `queued:${rec.subagentId}:${rec.queued.seq}`;
|
|
2058
|
+
const oldReg = lookupOwnedRegistration(queuedGeneration, queuedGeneration, queuedEndpointId);
|
|
2059
|
+
if (oldReg) {
|
|
2060
|
+
unregisterOwnedRegistration(oldReg);
|
|
2061
|
+
registerOwnedRegistration({
|
|
2062
|
+
endpointId: successorEndpointId,
|
|
2063
|
+
lineageIdHash: oldReg.lineageIdHash,
|
|
2064
|
+
promptAttemptEpoch: oldReg.promptAttemptEpoch,
|
|
2065
|
+
endpointGeneration: oldReg.endpointGeneration,
|
|
2066
|
+
jobId: queuedGeneration,
|
|
2067
|
+
jobGeneration: queuedGeneration,
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
rec.queued.ownedRegistrationEndpointId = successorEndpointId;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
|
|
1919
2076
|
#retireQueuedOwned(rec: SubagentRecord): void {
|
|
1920
2077
|
const seq = rec.queued?.seq ?? rec.terminalQueuedSeq;
|
|
1921
2078
|
if (seq === undefined) return;
|
|
1922
2079
|
const queuedGeneration = `queued:${rec.subagentId}:${seq}`;
|
|
1923
|
-
//
|
|
1924
|
-
//
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
AsyncJobManager.endpointIdOf(this))
|
|
1933
|
-
: AsyncJobManager.endpointIdOf(this);
|
|
2080
|
+
// The ownership endpoint follows the manager during rekey, while the
|
|
2081
|
+
// admission endpoint remains tied to the original tool lineage.
|
|
2082
|
+
const endpointId =
|
|
2083
|
+
rec.queued?.ownedRegistrationEndpointId ??
|
|
2084
|
+
rec.queued?.admissionEndpointId ??
|
|
2085
|
+
(rec.queued?.resumeToolCallId
|
|
2086
|
+
? resolveToolLineage(rec.queued.resumeToolCallId, AsyncJobManager.endpointIdOf(this))?.endpointId
|
|
2087
|
+
: undefined) ??
|
|
2088
|
+
AsyncJobManager.endpointIdOf(this);
|
|
1934
2089
|
const registration = lookupOwnedRegistration(queuedGeneration, queuedGeneration, endpointId);
|
|
1935
2090
|
if (registration) unregisterOwnedRegistration(registration);
|
|
1936
2091
|
}
|
|
@@ -1940,17 +2095,31 @@ export class AsyncJobManager {
|
|
|
1940
2095
|
message: string | undefined,
|
|
1941
2096
|
descriptor: ResumeDescriptor | undefined,
|
|
1942
2097
|
resumeToolCallId?: string,
|
|
2098
|
+
admissionEndpointId?: string,
|
|
2099
|
+
ownedRegistrationEndpointId?: string,
|
|
1943
2100
|
): { ok: boolean; status?: SubagentLifecycle; jobId?: string; reason?: string } {
|
|
1944
2101
|
if (this.#isOwnerSubagentShutdownFenced(rec.ownerId)) {
|
|
1945
2102
|
return { ok: false, status: rec.status, reason: "owner_shutdown_in_progress" };
|
|
1946
2103
|
}
|
|
1947
2104
|
const prevJobId = rec.currentJobId;
|
|
1948
2105
|
const queuedGeneration = rec.queued?.seq !== undefined ? `queued:${rec.subagentId}:${rec.queued.seq}` : undefined;
|
|
2106
|
+
const queuedEndpointId =
|
|
2107
|
+
ownedRegistrationEndpointId ??
|
|
2108
|
+
rec.queued?.ownedRegistrationEndpointId ??
|
|
2109
|
+
rec.queued?.admissionEndpointId ??
|
|
2110
|
+
AsyncJobManager.endpointIdOf(this);
|
|
1949
2111
|
// Clear any retained progress from the previous run so a resumed subagent
|
|
1950
2112
|
// never renders the prior run's tool/output as live before it emits again.
|
|
1951
2113
|
this.#subagentProgress.delete(rec.subagentId);
|
|
1952
2114
|
const runner = this.#resolveResumeRunner(rec, descriptor);
|
|
1953
|
-
const newJobId = runner?.(
|
|
2115
|
+
const newJobId = runner?.(
|
|
2116
|
+
rec.subagentId,
|
|
2117
|
+
message,
|
|
2118
|
+
descriptor,
|
|
2119
|
+
resumeToolCallId,
|
|
2120
|
+
admissionEndpointId ?? rec.queued?.admissionEndpointId,
|
|
2121
|
+
ownedRegistrationEndpointId ?? queuedEndpointId,
|
|
2122
|
+
);
|
|
1954
2123
|
if (!newJobId) {
|
|
1955
2124
|
// The queued resume FAILED to start: retire its owned registration
|
|
1956
2125
|
// so the tuple does not accumulate indefinitely (review thread P2).
|
|
@@ -1970,17 +2139,10 @@ export class AsyncJobManager {
|
|
|
1970
2139
|
// causal set has exactly one tuple per job. The EXACT stored tuple
|
|
1971
2140
|
// is looked up first — unregisterOwnedRegistration now verifies the
|
|
1972
2141
|
// full five-tuple before deleting (review thread P1). The lookup is
|
|
1973
|
-
// ENDPOINT-qualified
|
|
1974
|
-
//
|
|
1975
|
-
//
|
|
1976
|
-
|
|
1977
|
-
const resumeEndpoint = resumeToolCallId
|
|
1978
|
-
? (resolveToolLineage(resumeToolCallId, AsyncJobManager.endpointIdOf(this))?.endpointId ??
|
|
1979
|
-
// The binding may have been evicted (8192-cap FIFO): fall back
|
|
1980
|
-
// to the manager's own endpoint so the lookup never degrades
|
|
1981
|
-
// into the cross-endpoint scan (review thread P2).
|
|
1982
|
-
AsyncJobManager.endpointIdOf(this))
|
|
1983
|
-
: undefined;
|
|
2142
|
+
// ENDPOINT-qualified by the saved ownership endpoint. The tool lineage
|
|
2143
|
+
// is looked up at admissionEndpointId, but the queued registration may
|
|
2144
|
+
// have moved with the manager during rekey.
|
|
2145
|
+
const resumeEndpoint = queuedEndpointId;
|
|
1984
2146
|
const queuedReg = lookupOwnedRegistration(queuedGeneration, queuedGeneration, resumeEndpoint);
|
|
1985
2147
|
if (queuedReg) unregisterOwnedRegistration(queuedReg);
|
|
1986
2148
|
}
|
|
@@ -1992,24 +2154,101 @@ export class AsyncJobManager {
|
|
|
1992
2154
|
#drainResumeQueue(): void {
|
|
1993
2155
|
if (this.#resumeQueue.length === 0) return;
|
|
1994
2156
|
this.#resumeQueue.sort((a, b) => a.seq - b.seq);
|
|
2157
|
+
|
|
2158
|
+
// First pass: retire stale entries independently of capacity. This ensures
|
|
2159
|
+
// stale registrations are cleaned up even when the pool is saturated.
|
|
1995
2160
|
let index = 0;
|
|
1996
|
-
while (index < this.#resumeQueue.length
|
|
2161
|
+
while (index < this.#resumeQueue.length) {
|
|
1997
2162
|
const entry = this.#resumeQueue[index];
|
|
1998
2163
|
const rec = this.#subagentRecords.get(entry.subagentId);
|
|
1999
|
-
if (
|
|
2164
|
+
if (
|
|
2165
|
+
rec?.status !== "queued" ||
|
|
2166
|
+
rec.ownerId !== entry.ownerId ||
|
|
2167
|
+
rec.queued?.ownerId !== entry.ownerId ||
|
|
2168
|
+
rec.queued?.seq !== entry.seq
|
|
2169
|
+
) {
|
|
2170
|
+
// Retire the stale queued registration: the entry no longer matches
|
|
2171
|
+
// the current record state (owner changed, seq mismatch, or record
|
|
2172
|
+
// transitioned away from queued). Unregister its owned tuple so a
|
|
2173
|
+
// later owned abort does not see orphaned work.
|
|
2174
|
+
const staleQueuedGeneration = `queued:${entry.subagentId}:${entry.seq}`;
|
|
2175
|
+
// The tuple follows the manager's ownership endpoint during rekey;
|
|
2176
|
+
// the original admission endpoint remains solely for lineage lookup.
|
|
2177
|
+
const endpointId =
|
|
2178
|
+
entry.ownedRegistrationEndpointId ?? entry.admissionEndpointId ?? AsyncJobManager.endpointIdOf(this);
|
|
2179
|
+
const registration = lookupOwnedRegistration(staleQueuedGeneration, staleQueuedGeneration, endpointId);
|
|
2180
|
+
if (registration) unregisterOwnedRegistration(registration);
|
|
2181
|
+
// Mark the stale generation as terminal so waiters for that exact
|
|
2182
|
+
// generation do not block forever; do NOT update the current record
|
|
2183
|
+
// because it may belong to a different owner/generation.
|
|
2184
|
+
if (!this.#publishedTerminalGenerations.has(staleQueuedGeneration)) {
|
|
2185
|
+
this.#publishedTerminalGenerations.add(staleQueuedGeneration);
|
|
2186
|
+
this.#terminalEvents.set(staleQueuedGeneration, {
|
|
2187
|
+
generation: staleQueuedGeneration,
|
|
2188
|
+
jobId: null,
|
|
2189
|
+
subagentId: entry.subagentId,
|
|
2190
|
+
ownerId: entry.ownerId,
|
|
2191
|
+
status: "cancelled",
|
|
2192
|
+
createdAt: Date.now(),
|
|
2193
|
+
});
|
|
2194
|
+
// Notify any waiters for this exact generation.
|
|
2195
|
+
for (const state of this.#terminalWaits.values()) {
|
|
2196
|
+
if (
|
|
2197
|
+
state.targets.some(
|
|
2198
|
+
target =>
|
|
2199
|
+
target.generation === staleQueuedGeneration ||
|
|
2200
|
+
this.#waitGenerationAliases.get(target.generation) === staleQueuedGeneration,
|
|
2201
|
+
)
|
|
2202
|
+
)
|
|
2203
|
+
this.#maybeResolveWait(state);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2000
2206
|
this.#resumeQueue.splice(index, 1);
|
|
2001
2207
|
continue;
|
|
2002
2208
|
}
|
|
2209
|
+
index += 1;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
// Second pass: resume valid entries while capacity allows. By this point,
|
|
2213
|
+
// all stale entries have been retired, so we only process entries that
|
|
2214
|
+
// currently match their record state.
|
|
2215
|
+
index = 0;
|
|
2216
|
+
while (index < this.#resumeQueue.length && this.getRunningJobs().length < this.#maxRunningJobs) {
|
|
2217
|
+
const entry = this.#resumeQueue[index];
|
|
2218
|
+
const rec = this.#subagentRecords.get(entry.subagentId);
|
|
2219
|
+
if (rec?.status !== "queued" || rec.ownerId !== entry.ownerId || rec.queued?.seq !== entry.seq) {
|
|
2220
|
+
// This should not happen because we cleaned up all stale entries
|
|
2221
|
+
// in the first pass, but skip if we encounter an unexpected state.
|
|
2222
|
+
index += 1;
|
|
2223
|
+
continue;
|
|
2224
|
+
}
|
|
2003
2225
|
if (this.#isOwnerSubagentShutdownFenced(entry.ownerId)) {
|
|
2004
2226
|
index += 1;
|
|
2005
2227
|
continue;
|
|
2006
2228
|
}
|
|
2229
|
+
let priorExecutionActive = false;
|
|
2230
|
+
for (const execution of this.#activeExecutions) {
|
|
2231
|
+
if (
|
|
2232
|
+
execution.job.ownerId === entry.ownerId &&
|
|
2233
|
+
execution.job.metadata?.subagent?.id === entry.subagentId &&
|
|
2234
|
+
!execution.physicallySettled
|
|
2235
|
+
) {
|
|
2236
|
+
priorExecutionActive = true;
|
|
2237
|
+
break;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
if (priorExecutionActive) {
|
|
2241
|
+
index += 1;
|
|
2242
|
+
continue;
|
|
2243
|
+
}
|
|
2007
2244
|
try {
|
|
2008
2245
|
const result = this.#startResume(
|
|
2009
2246
|
rec,
|
|
2010
2247
|
entry.message,
|
|
2011
2248
|
this.#descriptorForRecord(rec),
|
|
2012
2249
|
rec.queued?.resumeToolCallId,
|
|
2250
|
+
entry.admissionEndpointId,
|
|
2251
|
+
entry.ownedRegistrationEndpointId,
|
|
2013
2252
|
);
|
|
2014
2253
|
if (!result.ok) {
|
|
2015
2254
|
if (result.reason === "owner_shutdown_in_progress") {
|
|
@@ -2065,8 +2304,10 @@ export class AsyncJobManager {
|
|
|
2065
2304
|
return true;
|
|
2066
2305
|
}
|
|
2067
2306
|
if (rec.status === "queued") {
|
|
2068
|
-
const idx = this.#resumeQueue.findIndex(e => e.subagentId === rec.subagentId);
|
|
2069
2307
|
const queuedSeq = rec.queued?.seq;
|
|
2308
|
+
const idx = this.#resumeQueue.findIndex(
|
|
2309
|
+
e => e.subagentId === rec.subagentId && e.ownerId === rec.ownerId && e.seq === queuedSeq,
|
|
2310
|
+
);
|
|
2070
2311
|
if (idx !== -1) this.#resumeQueue.splice(idx, 1);
|
|
2071
2312
|
rec.status = "cancelled";
|
|
2072
2313
|
if (queuedSeq !== undefined) {
|
|
@@ -2089,16 +2330,57 @@ export class AsyncJobManager {
|
|
|
2089
2330
|
|
|
2090
2331
|
#purgeOwnerSubagentState(ownerId?: string): void {
|
|
2091
2332
|
for (let i = this.#resumeQueue.length - 1; i >= 0; i--) {
|
|
2092
|
-
|
|
2333
|
+
const entry = this.#resumeQueue[i];
|
|
2334
|
+
if (!ownerId || entry.ownerId === ownerId) {
|
|
2335
|
+
// Retire stale queued registrations BEFORE removing the queue entry:
|
|
2336
|
+
// the purge removes the entry with no start, cancellation, or delivery
|
|
2337
|
+
// boundary, so the registration would otherwise leak into the global
|
|
2338
|
+
// ownership registries and eventually make later owned aborts fail
|
|
2339
|
+
// closed (review thread P2). Use the saved ownership endpoint after
|
|
2340
|
+
// rekey, not the immutable lineage/admission endpoint.
|
|
2341
|
+
const staleQueuedGeneration = `queued:${entry.subagentId}:${entry.seq}`;
|
|
2342
|
+
const endpointId =
|
|
2343
|
+
entry.ownedRegistrationEndpointId ?? entry.admissionEndpointId ?? AsyncJobManager.endpointIdOf(this);
|
|
2344
|
+
const registration = lookupOwnedRegistration(staleQueuedGeneration, staleQueuedGeneration, endpointId);
|
|
2345
|
+
if (registration) unregisterOwnedRegistration(registration);
|
|
2346
|
+
// Also publish a terminal event for this generation so any waiters know it won't resume.
|
|
2347
|
+
if (!this.#publishedTerminalGenerations.has(staleQueuedGeneration)) {
|
|
2348
|
+
this.#publishedTerminalGenerations.add(staleQueuedGeneration);
|
|
2349
|
+
this.#terminalEvents.set(staleQueuedGeneration, {
|
|
2350
|
+
generation: staleQueuedGeneration,
|
|
2351
|
+
jobId: null,
|
|
2352
|
+
subagentId: entry.subagentId,
|
|
2353
|
+
ownerId: entry.ownerId,
|
|
2354
|
+
status: "cancelled",
|
|
2355
|
+
createdAt: Date.now(),
|
|
2356
|
+
});
|
|
2357
|
+
// Notify any waiters for this exact generation.
|
|
2358
|
+
for (const state of this.#terminalWaits.values()) {
|
|
2359
|
+
if (
|
|
2360
|
+
state.targets.some(
|
|
2361
|
+
target =>
|
|
2362
|
+
target.generation === staleQueuedGeneration ||
|
|
2363
|
+
this.#waitGenerationAliases.get(target.generation) === staleQueuedGeneration,
|
|
2364
|
+
)
|
|
2365
|
+
)
|
|
2366
|
+
this.#maybeResolveWait(state);
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
this.#resumeQueue.splice(i, 1);
|
|
2370
|
+
}
|
|
2093
2371
|
}
|
|
2094
2372
|
for (const [sid, rec] of this.#subagentRecords) {
|
|
2095
2373
|
if (!ownerId || rec.ownerId === ownerId) {
|
|
2374
|
+
const queuedSeq = rec.status === "queued" ? rec.queued?.seq : undefined;
|
|
2375
|
+
if (queuedSeq !== undefined) {
|
|
2376
|
+
rec.terminalQueuedSeq = queuedSeq;
|
|
2377
|
+
this.#publishQueuedTerminal(sid, `queued:${sid}:${queuedSeq}`, "cancelled");
|
|
2378
|
+
}
|
|
2096
2379
|
// Retire the queued resume's owned registration BEFORE deleting the
|
|
2097
|
-
// record:
|
|
2098
|
-
//
|
|
2099
|
-
//
|
|
2100
|
-
//
|
|
2101
|
-
// P2). #retireQueuedOwned is a no-op for non-queued records.
|
|
2380
|
+
// record: #publishQueuedTerminal above is the wait-notification
|
|
2381
|
+
// boundary for queued work; this removes the corresponding ownership
|
|
2382
|
+
// tuple so it cannot leak into later owned aborts. The helper is a
|
|
2383
|
+
// no-op for non-queued records.
|
|
2102
2384
|
this.#retireQueuedOwned(rec);
|
|
2103
2385
|
this.#liveHandles.delete(sid);
|
|
2104
2386
|
this.#resumeDescriptors.delete(sid);
|
|
@@ -2114,6 +2396,24 @@ export class AsyncJobManager {
|
|
|
2114
2396
|
const colon = id.lastIndexOf(":");
|
|
2115
2397
|
const subagentId = colon > "queued:".length ? id.slice("queued:".length, colon) : undefined;
|
|
2116
2398
|
if (!subagentId) return undefined;
|
|
2399
|
+
|
|
2400
|
+
// Check for stale queued tombstone in terminal events first.
|
|
2401
|
+
// If a queued generation was retired as stale, it's recorded here
|
|
2402
|
+
// for owned settlement to find even if the subagent record is gone.
|
|
2403
|
+
const tombstoneEvent = this.#terminalEvents.get(id);
|
|
2404
|
+
if (tombstoneEvent && id.startsWith(`queued:${subagentId}:`)) {
|
|
2405
|
+
return {
|
|
2406
|
+
id,
|
|
2407
|
+
generation: id,
|
|
2408
|
+
type: "task",
|
|
2409
|
+
status: tombstoneEvent.status as "cancelled" | "failed",
|
|
2410
|
+
startTime: tombstoneEvent.createdAt,
|
|
2411
|
+
label: `stale queued resume ${subagentId}`,
|
|
2412
|
+
abortController: new AbortController(),
|
|
2413
|
+
promise: Promise.resolve(),
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2117
2417
|
const rec = this.getSubagentRecord(subagentId);
|
|
2118
2418
|
if (!rec) return undefined;
|
|
2119
2419
|
const liveSeq = rec.queued?.seq;
|