@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.12
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 +142 -7
- package/dist/cli.js +23108 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +2 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/cli-commands.d.ts +12 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/api-key-resolver.d.ts +3 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +8 -219
- package/dist/types/config/model-resolver.d.ts +34 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +41 -8
- package/dist/types/config/settings.d.ts +8 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/eval/py/executor.d.ts +5 -0
- package/dist/types/eval/py/kernel.d.ts +6 -1
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -2
- package/dist/types/extensibility/extensions/types.d.ts +6 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/runtime.d.ts +4 -0
- package/dist/types/memory-backend/types.d.ts +66 -1
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +25 -6
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/index.d.ts +3 -3
- package/dist/types/modes/interactive-mode.d.ts +8 -3
- package/dist/types/modes/oauth-manual-input.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
- package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
- package/dist/types/modes/setup-wizard/index.d.ts +5 -1
- package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +8 -2
- package/dist/types/session/agent-session.d.ts +15 -3
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/slash-commands/types.d.ts +1 -1
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/task/index.d.ts +2 -2
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/thinking.d.ts +4 -0
- package/dist/types/tiny/title-client.d.ts +11 -0
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/index.d.ts +6 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/utils/git.d.ts +15 -2
- package/dist/types/utils/title-generator.d.ts +3 -2
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/auto-thinking/classifier.ts +1 -0
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +4 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +2 -1
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +30 -0
- package/src/cli.ts +76 -13
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +5 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +4 -3
- package/src/config/api-key-resolver.ts +8 -6
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +320 -1041
- package/src/config/model-resolver.ts +173 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +61 -19
- package/src/config/settings.ts +98 -4
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/edit/diff.ts +47 -3
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +47 -4
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +3 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +40 -22
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/executor.ts +29 -7
- package/src/eval/py/index.ts +6 -3
- package/src/eval/py/kernel.ts +43 -4
- package/src/eval/py/runner.py +107 -3
- package/src/eval/py/runtime.ts +37 -0
- package/src/exec/bash-executor.ts +85 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/get-commands-handler.ts +2 -1
- package/src/extensibility/extensions/runner.ts +6 -1
- package/src/extensibility/extensions/types.ts +6 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/bank.ts +17 -2
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +183 -23
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +4 -1
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/local-backend.ts +9 -0
- package/src/memory-backend/off-backend.ts +9 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +81 -1
- package/src/mnemopi/backend.ts +176 -7
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/acp/acp-agent.ts +119 -11
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +32 -28
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +4 -2
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +119 -35
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +258 -53
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +11 -4
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +2 -1
- package/src/modes/controllers/mcp-command-controller.ts +39 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/index.ts +3 -21
- package/src/modes/interactive-mode.ts +42 -18
- package/src/modes/oauth-manual-input.ts +30 -3
- package/src/modes/rpc/rpc-client.ts +154 -3
- package/src/modes/rpc/rpc-mode.ts +97 -12
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +81 -1
- package/src/modes/setup-wizard/index.ts +12 -2
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +4 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +13 -24
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +1 -1
- package/src/sdk.ts +31 -11
- package/src/secrets/index.ts +8 -1
- package/src/secrets/obfuscator.ts +39 -18
- package/src/session/agent-session.ts +223 -64
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +188 -11
- package/src/slash-commands/acp-builtins.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +40 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/slash-commands/types.ts +1 -1
- package/src/ssh/connection-manager.ts +27 -0
- package/src/system-prompt.ts +14 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +74 -65
- package/src/task/index.ts +146 -68
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +20 -5
- package/src/task/types.ts +9 -0
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +9 -1
- package/src/tiny/title-client.ts +60 -16
- package/src/tiny/title-protocol.ts +1 -1
- package/src/tiny/worker.ts +6 -4
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +100 -18
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +14 -13
- package/src/tools/index.ts +13 -1
- package/src/tools/inspect-image.ts +1 -0
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +38 -8
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/file-mentions.ts +2 -1
- package/src/utils/git.ts +267 -13
- package/src/utils/title-generator.ts +24 -5
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
package/src/task/executor.ts
CHANGED
|
@@ -162,6 +162,7 @@ export interface ExecutorOptions {
|
|
|
162
162
|
description?: string;
|
|
163
163
|
index: number;
|
|
164
164
|
id: string;
|
|
165
|
+
parentToolCallId?: string;
|
|
165
166
|
modelOverride?: string | string[];
|
|
166
167
|
/**
|
|
167
168
|
* Active model selector of the parent session, used as an auth-aware fallback
|
|
@@ -840,6 +841,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
840
841
|
agent: agent.name,
|
|
841
842
|
agentSource: agent.source,
|
|
842
843
|
task,
|
|
844
|
+
parentToolCallId: options.parentToolCallId,
|
|
843
845
|
assignment,
|
|
844
846
|
progress: { ...progress },
|
|
845
847
|
sessionFile: subtaskSessionFile,
|
|
@@ -922,20 +924,16 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
922
924
|
progress.recentOutput = [];
|
|
923
925
|
};
|
|
924
926
|
|
|
927
|
+
const emitSubagentEvent = (event: AgentSessionEvent) => {
|
|
928
|
+
if (!options.eventBus) return;
|
|
929
|
+
options.eventBus.emit(TASK_SUBAGENT_EVENT_CHANNEL, {
|
|
930
|
+
id,
|
|
931
|
+
event,
|
|
932
|
+
});
|
|
933
|
+
};
|
|
934
|
+
|
|
925
935
|
const processEvent = (event: AgentEvent) => {
|
|
926
936
|
if (resolved) return;
|
|
927
|
-
|
|
928
|
-
if (options.eventBus) {
|
|
929
|
-
options.eventBus.emit(TASK_SUBAGENT_EVENT_CHANNEL, {
|
|
930
|
-
index,
|
|
931
|
-
agent: agent.name,
|
|
932
|
-
agentSource: agent.source,
|
|
933
|
-
task,
|
|
934
|
-
assignment,
|
|
935
|
-
event,
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
|
|
939
937
|
const now = Date.now();
|
|
940
938
|
let flushProgress = false;
|
|
941
939
|
|
|
@@ -1285,59 +1283,67 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1285
1283
|
|
|
1286
1284
|
const { normalized: normalizedOutputSchema } = normalizeSchema(outputSchema);
|
|
1287
1285
|
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1286
|
+
const sessionPromise = createAgentSession({
|
|
1287
|
+
cwd: worktree ?? cwd,
|
|
1288
|
+
authStorage,
|
|
1289
|
+
modelRegistry,
|
|
1290
|
+
settings: subagentSettings,
|
|
1291
|
+
model,
|
|
1292
|
+
thinkingLevel: effectiveThinkingLevel,
|
|
1293
|
+
toolNames,
|
|
1294
|
+
outputSchema,
|
|
1295
|
+
requireYieldTool: true,
|
|
1296
|
+
contextFiles: options.contextFiles,
|
|
1297
|
+
skills: options.skills,
|
|
1298
|
+
promptTemplates: options.promptTemplates,
|
|
1299
|
+
workspaceTree: options.workspaceTree,
|
|
1300
|
+
rules: options.rules,
|
|
1301
|
+
preloadedExtensionPaths: options.preloadedExtensionPaths,
|
|
1302
|
+
preloadedCustomToolPaths: options.preloadedCustomToolPaths,
|
|
1303
|
+
systemPrompt: defaultPrompt => {
|
|
1304
|
+
const subagentPrompt = prompt.render(subagentSystemPromptTemplate, {
|
|
1305
|
+
agent: agent.systemPrompt,
|
|
1306
|
+
context: options.context?.trim() ?? "",
|
|
1307
|
+
planReference: options.planReference?.content ?? "",
|
|
1308
|
+
planReferencePath: options.planReference?.path ?? "",
|
|
1309
|
+
worktree: worktree ?? "",
|
|
1310
|
+
outputSchema: normalizedOutputSchema,
|
|
1311
|
+
contextFile: contextFileForPrompt,
|
|
1312
|
+
ircPeers: ircEnabled ? renderIrcPeerRoster(id) : "",
|
|
1313
|
+
ircSelfId: ircEnabled ? id : "",
|
|
1314
|
+
});
|
|
1315
|
+
return defaultPrompt.length === 0
|
|
1316
|
+
? [subagentPrompt]
|
|
1317
|
+
: [...defaultPrompt.slice(0, -1), subagentPrompt, defaultPrompt[defaultPrompt.length - 1]];
|
|
1318
|
+
},
|
|
1319
|
+
sessionManager,
|
|
1320
|
+
hasUI: false,
|
|
1321
|
+
spawns: spawnsEnv,
|
|
1322
|
+
taskDepth: childDepth,
|
|
1323
|
+
parentHindsightSessionState: options.parentHindsightSessionState,
|
|
1324
|
+
parentMnemopiSessionState: options.parentMnemopiSessionState,
|
|
1325
|
+
parentTaskPrefix: id,
|
|
1326
|
+
agentId: id,
|
|
1327
|
+
agentDisplayName: agent.name,
|
|
1328
|
+
enableLsp: lspEnabled,
|
|
1329
|
+
skipPythonPreflight,
|
|
1330
|
+
enableMCP,
|
|
1331
|
+
mcpManager: options.mcpManager,
|
|
1332
|
+
customTools: mcpProxyTools.length > 0 ? mcpProxyTools : undefined,
|
|
1333
|
+
localProtocolOptions: options.localProtocolOptions,
|
|
1334
|
+
telemetry: subagentTelemetry,
|
|
1335
|
+
parentEvalSessionId: options.parentEvalSessionId,
|
|
1336
|
+
});
|
|
1337
|
+
let session: AgentSession;
|
|
1338
|
+
try {
|
|
1339
|
+
({ session } = await awaitAbortable(sessionPromise));
|
|
1340
|
+
} catch (err) {
|
|
1341
|
+
// Abort raced session startup. The session may still resolve later
|
|
1342
|
+
// holding live LSP/MCP child processes — dispose it when it does so
|
|
1343
|
+
// a cancelled subagent cannot leak them.
|
|
1344
|
+
void sessionPromise.then(created => created.session.dispose()).catch(() => {});
|
|
1345
|
+
throw err;
|
|
1346
|
+
}
|
|
1341
1347
|
|
|
1342
1348
|
activeSession = session;
|
|
1343
1349
|
|
|
@@ -1346,6 +1352,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1346
1352
|
options.eventBus.emit(TASK_SUBAGENT_LIFECYCLE_CHANNEL, {
|
|
1347
1353
|
id,
|
|
1348
1354
|
agent: agent.name,
|
|
1355
|
+
parentToolCallId: options.parentToolCallId,
|
|
1349
1356
|
agentSource: agent.source,
|
|
1350
1357
|
description: options.description,
|
|
1351
1358
|
status: "started",
|
|
@@ -1444,6 +1451,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1444
1451
|
|
|
1445
1452
|
const MAX_YIELD_RETRIES = 3;
|
|
1446
1453
|
unsubscribe = session.subscribe(event => {
|
|
1454
|
+
emitSubagentEvent(event);
|
|
1447
1455
|
if (event.type === "auto_retry_start") {
|
|
1448
1456
|
progress.retryState = {
|
|
1449
1457
|
attempt: event.attempt,
|
|
@@ -1696,6 +1704,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1696
1704
|
options.eventBus.emit(TASK_SUBAGENT_LIFECYCLE_CHANNEL, {
|
|
1697
1705
|
id,
|
|
1698
1706
|
agent: agent.name,
|
|
1707
|
+
parentToolCallId: options.parentToolCallId,
|
|
1699
1708
|
agentSource: agent.source,
|
|
1700
1709
|
description: options.description,
|
|
1701
1710
|
status: progress.status as "completed" | "failed" | "aborted",
|
package/src/task/index.ts
CHANGED
|
@@ -43,7 +43,7 @@ import type { LocalProtocolOptions } from "../internal-urls";
|
|
|
43
43
|
import { loadOverallPlanReference } from "../plan-mode/plan-handoff";
|
|
44
44
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
45
45
|
import * as git from "../utils/git";
|
|
46
|
-
import { discoverAgents, getAgent } from "./discovery";
|
|
46
|
+
import { type DiscoveryResult, discoverAgents, getAgent } from "./discovery";
|
|
47
47
|
import { runSubprocess } from "./executor";
|
|
48
48
|
import { AgentOutputManager } from "./output-manager";
|
|
49
49
|
import { mapWithConcurrencyLimit, Semaphore } from "./parallel";
|
|
@@ -119,6 +119,7 @@ export type {
|
|
|
119
119
|
AgentDefinition,
|
|
120
120
|
AgentProgress,
|
|
121
121
|
SingleResult,
|
|
122
|
+
SubagentEventPayload,
|
|
122
123
|
SubagentLifecyclePayload,
|
|
123
124
|
SubagentProgressPayload,
|
|
124
125
|
TaskParams,
|
|
@@ -242,6 +243,88 @@ function validateTaskModeParams(simpleMode: TaskSimpleMode, params: TaskParams):
|
|
|
242
243
|
return "task.simple is set to independent, so the task tool does not accept `context` or `schema`. Put all required background and output expectations inside each task assignment or the selected agent definition.";
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
/** Sentinel for async jobs whose subagent finished with a failing result; batch counters are already updated. */
|
|
247
|
+
class TaskJobError extends Error {}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Validate task ids: every task needs a non-empty id and ids must be unique
|
|
251
|
+
* (case-insensitive). Returns a problem description, or undefined when valid.
|
|
252
|
+
*/
|
|
253
|
+
function validateTaskIds(tasks: TaskParams["tasks"]): string | undefined {
|
|
254
|
+
const missingTaskIndexes: number[] = [];
|
|
255
|
+
const idIndexes = new Map<string, number[]>();
|
|
256
|
+
|
|
257
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
258
|
+
const id = tasks[i]?.id;
|
|
259
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
260
|
+
missingTaskIndexes.push(i);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const normalizedId = id.toLowerCase();
|
|
264
|
+
const indexes = idIndexes.get(normalizedId);
|
|
265
|
+
if (indexes) {
|
|
266
|
+
indexes.push(i);
|
|
267
|
+
} else {
|
|
268
|
+
idIndexes.set(normalizedId, [i]);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const duplicateIds: Array<{ id: string; indexes: number[] }> = [];
|
|
273
|
+
for (const [normalizedId, indexes] of idIndexes.entries()) {
|
|
274
|
+
if (indexes.length > 1) {
|
|
275
|
+
duplicateIds.push({
|
|
276
|
+
id: tasks[indexes[0]]?.id ?? normalizedId,
|
|
277
|
+
indexes,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (missingTaskIndexes.length === 0 && duplicateIds.length === 0) {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const problems: string[] = [];
|
|
287
|
+
if (missingTaskIndexes.length > 0) {
|
|
288
|
+
problems.push(`Missing task ids at indexes: ${missingTaskIndexes.join(", ")}`);
|
|
289
|
+
}
|
|
290
|
+
if (duplicateIds.length > 0) {
|
|
291
|
+
const details = duplicateIds.map(entry => `${entry.id} (indexes ${entry.indexes.join(", ")})`).join("; ");
|
|
292
|
+
problems.push(`Duplicate task ids detected (case-insensitive): ${details}`);
|
|
293
|
+
}
|
|
294
|
+
return `Invalid tasks: ${problems.join(". ")}`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Process-level memo for create-time agent discovery, keyed by resolved cwd.
|
|
299
|
+
*
|
|
300
|
+
* `TaskTool.create` runs for every (sub)agent session in this process and the
|
|
301
|
+
* walk-up + plugin-registry scan in `discoverAgents` is identical for a given
|
|
302
|
+
* cwd, so repeat creations reuse the first scan. Execution-time discovery
|
|
303
|
+
* (`#executeSync`) intentionally stays fresh. The memo also tracks the live
|
|
304
|
+
* `discoverAgents` binding: test spies swap that binding, which invalidates
|
|
305
|
+
* the memo automatically.
|
|
306
|
+
*/
|
|
307
|
+
const discoveryMemo = new Map<string, Promise<DiscoveryResult>>();
|
|
308
|
+
let discoveryMemoFn: typeof discoverAgents | undefined;
|
|
309
|
+
|
|
310
|
+
function discoverAgentsForCreate(cwd: string): Promise<DiscoveryResult> {
|
|
311
|
+
const fn = discoverAgents;
|
|
312
|
+
if (discoveryMemoFn !== fn) {
|
|
313
|
+
discoveryMemoFn = fn;
|
|
314
|
+
discoveryMemo.clear();
|
|
315
|
+
}
|
|
316
|
+
const key = path.resolve(cwd);
|
|
317
|
+
let pending = discoveryMemo.get(key);
|
|
318
|
+
if (!pending) {
|
|
319
|
+
pending = fn(cwd);
|
|
320
|
+
discoveryMemo.set(key, pending);
|
|
321
|
+
pending.catch(() => {
|
|
322
|
+
if (discoveryMemo.get(key) === pending) discoveryMemo.delete(key);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return pending;
|
|
326
|
+
}
|
|
327
|
+
|
|
245
328
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
246
329
|
// Tool Class
|
|
247
330
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -325,12 +408,12 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
325
408
|
* Create a TaskTool instance with async agent discovery.
|
|
326
409
|
*/
|
|
327
410
|
static async create(session: ToolSession): Promise<TaskTool> {
|
|
328
|
-
const { agents } = await
|
|
411
|
+
const { agents } = await discoverAgentsForCreate(session.cwd);
|
|
329
412
|
return new TaskTool(session, agents);
|
|
330
413
|
}
|
|
331
414
|
|
|
332
415
|
async execute(
|
|
333
|
-
|
|
416
|
+
toolCallId: string,
|
|
334
417
|
rawParams: unknown,
|
|
335
418
|
signal?: AbortSignal,
|
|
336
419
|
onUpdate?: AgentToolUpdateCallback<TaskToolDetails>,
|
|
@@ -345,7 +428,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
345
428
|
const asyncEnabled = this.session.settings.get("async.enabled");
|
|
346
429
|
const selectedAgent = this.#discoveredAgents.find(agent => agent.name === params.agent);
|
|
347
430
|
if (!asyncEnabled || selectedAgent?.blocking === true) {
|
|
348
|
-
return this.#executeSync(
|
|
431
|
+
return this.#executeSync(toolCallId, params, signal, onUpdate);
|
|
349
432
|
}
|
|
350
433
|
|
|
351
434
|
const manager = this.session.asyncJobManager;
|
|
@@ -355,12 +438,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
355
438
|
// to the sync path keeps the tool usable; only background/job-poll
|
|
356
439
|
// semantics are lost.
|
|
357
440
|
logger.warn("task: async.enabled but no AsyncJobManager registered; falling back to sync execution");
|
|
358
|
-
return this.#executeSync(
|
|
441
|
+
return this.#executeSync(toolCallId, params, signal, onUpdate);
|
|
359
442
|
}
|
|
360
443
|
|
|
361
444
|
const taskItems = params.tasks ?? [];
|
|
362
445
|
if (taskItems.length === 0) {
|
|
363
|
-
return this.#executeSync(
|
|
446
|
+
return this.#executeSync(toolCallId, params, signal, onUpdate);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const taskIdProblem = validateTaskIds(taskItems);
|
|
450
|
+
if (taskIdProblem) {
|
|
451
|
+
return createTaskModeError(taskIdProblem);
|
|
364
452
|
}
|
|
365
453
|
|
|
366
454
|
const outputManager =
|
|
@@ -396,9 +484,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
396
484
|
let failedJobs = 0;
|
|
397
485
|
|
|
398
486
|
const getProgressSnapshot = (): AgentProgress[] => {
|
|
487
|
+
// Shallow copies: top-level fields are reassigned (never mutated in
|
|
488
|
+
// place) and the large nested payloads (extractedToolData) are
|
|
489
|
+
// immutable once attached — structuredClone here cost O(batch × payload)
|
|
490
|
+
// per progress event.
|
|
399
491
|
return Array.from(progressByTaskId.values())
|
|
400
492
|
.sort((a, b) => a.index - b.index)
|
|
401
|
-
.map(progress =>
|
|
493
|
+
.map(progress => ({ ...progress }));
|
|
402
494
|
};
|
|
403
495
|
|
|
404
496
|
const buildAsyncDetails = (state: "running" | "completed" | "failed", jobId: string): TaskToolDetails => ({
|
|
@@ -424,6 +516,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
424
516
|
const taskItem = taskItems[i];
|
|
425
517
|
if (signal?.aborted) {
|
|
426
518
|
failedSchedules.push(`${taskItem.id}: cancelled before scheduling`);
|
|
519
|
+
completedJobs += 1;
|
|
427
520
|
const progress = progressByTaskId.get(taskItem.id);
|
|
428
521
|
if (progress) {
|
|
429
522
|
progress.status = "aborted";
|
|
@@ -438,7 +531,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
438
531
|
const jobId = manager.register(
|
|
439
532
|
"task",
|
|
440
533
|
label,
|
|
441
|
-
async ({ signal: runSignal, reportProgress }) => {
|
|
534
|
+
async ({ signal: runSignal, reportProgress, markRunning }) => {
|
|
442
535
|
const startedAt = Date.now();
|
|
443
536
|
const progress = progressByTaskId.get(taskItem.id);
|
|
444
537
|
await semaphore.acquire();
|
|
@@ -447,8 +540,11 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
447
540
|
if (progress) {
|
|
448
541
|
progress.status = "aborted";
|
|
449
542
|
}
|
|
543
|
+
completedJobs += 1;
|
|
544
|
+
failedJobs += 1;
|
|
450
545
|
throw new Error("Aborted before execution");
|
|
451
546
|
}
|
|
547
|
+
markRunning();
|
|
452
548
|
if (progress) {
|
|
453
549
|
progress.status = "running";
|
|
454
550
|
}
|
|
@@ -457,17 +553,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
457
553
|
buildAsyncDetails("running", startedJobs[0]?.jobId ?? label) as unknown as Record<string, unknown>,
|
|
458
554
|
);
|
|
459
555
|
try {
|
|
460
|
-
const result = await this.#executeSync(
|
|
461
|
-
uniqueId,
|
|
462
|
-
]);
|
|
556
|
+
const result = await this.#executeSync(toolCallId, singleParams, runSignal, undefined, [uniqueId]);
|
|
463
557
|
const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
|
|
464
558
|
const singleResult = result.details?.results[0];
|
|
559
|
+
// A missing per-task result means #executeSync failed at the
|
|
560
|
+
// tool level (results: []) — treat it as a failure, not success.
|
|
561
|
+
const resultFailed =
|
|
562
|
+
!singleResult || (singleResult.aborted ?? false) || singleResult.exitCode !== 0;
|
|
465
563
|
if (progress) {
|
|
466
|
-
progress.status = singleResult?.aborted
|
|
467
|
-
? "aborted"
|
|
468
|
-
: (singleResult?.exitCode ?? 0) === 0
|
|
469
|
-
? "completed"
|
|
470
|
-
: "failed";
|
|
564
|
+
progress.status = singleResult?.aborted ? "aborted" : resultFailed ? "failed" : "completed";
|
|
471
565
|
progress.durationMs = singleResult?.durationMs ?? Math.max(0, Date.now() - startedAt);
|
|
472
566
|
progress.tokens = singleResult?.tokens ?? 0;
|
|
473
567
|
progress.contextTokens = singleResult?.contextTokens;
|
|
@@ -478,7 +572,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
478
572
|
progress.retryState = undefined;
|
|
479
573
|
}
|
|
480
574
|
completedJobs += 1;
|
|
481
|
-
if (
|
|
575
|
+
if (resultFailed) {
|
|
482
576
|
failedJobs += 1;
|
|
483
577
|
}
|
|
484
578
|
const remaining = taskItems.length - completedJobs;
|
|
@@ -498,8 +592,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
498
592
|
`Background task batch complete: ${completedJobs}/${taskItems.length} finished.`,
|
|
499
593
|
);
|
|
500
594
|
}
|
|
595
|
+
if (resultFailed) {
|
|
596
|
+
// Mark the job itself failed; counters above are already updated.
|
|
597
|
+
throw new TaskJobError(finalText);
|
|
598
|
+
}
|
|
501
599
|
return finalText;
|
|
502
600
|
} catch (error) {
|
|
601
|
+
if (error instanceof TaskJobError) {
|
|
602
|
+
throw error;
|
|
603
|
+
}
|
|
503
604
|
if (progress) {
|
|
504
605
|
progress.status = "failed";
|
|
505
606
|
progress.durationMs = Math.max(0, Date.now() - startedAt);
|
|
@@ -530,6 +631,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
530
631
|
},
|
|
531
632
|
{
|
|
532
633
|
id: label,
|
|
634
|
+
queued: true,
|
|
533
635
|
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
534
636
|
onProgress: (text, details) => {
|
|
535
637
|
const progressDetails =
|
|
@@ -543,6 +645,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
543
645
|
} catch (error) {
|
|
544
646
|
const message = error instanceof Error ? error.message : String(error);
|
|
545
647
|
failedSchedules.push(`${taskItem.id}: ${message}`);
|
|
648
|
+
completedJobs += 1;
|
|
546
649
|
const progress = progressByTaskId.get(taskItem.id);
|
|
547
650
|
if (progress) {
|
|
548
651
|
progress.status = "failed";
|
|
@@ -603,7 +706,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
603
706
|
}
|
|
604
707
|
|
|
605
708
|
async #executeSync(
|
|
606
|
-
|
|
709
|
+
toolCallId: string,
|
|
607
710
|
params: TaskParams,
|
|
608
711
|
signal?: AbortSignal,
|
|
609
712
|
onUpdate?: AgentToolUpdateCallback<TaskToolDetails>,
|
|
@@ -734,45 +837,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
734
837
|
}
|
|
735
838
|
|
|
736
839
|
const tasks = params.tasks;
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
741
|
-
const id = tasks[i]?.id;
|
|
742
|
-
if (typeof id !== "string" || id.trim() === "") {
|
|
743
|
-
missingTaskIndexes.push(i);
|
|
744
|
-
continue;
|
|
745
|
-
}
|
|
746
|
-
const normalizedId = id.toLowerCase();
|
|
747
|
-
const indexes = idIndexes.get(normalizedId);
|
|
748
|
-
if (indexes) {
|
|
749
|
-
indexes.push(i);
|
|
750
|
-
} else {
|
|
751
|
-
idIndexes.set(normalizedId, [i]);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
const duplicateIds: Array<{ id: string; indexes: number[] }> = [];
|
|
756
|
-
for (const [normalizedId, indexes] of idIndexes.entries()) {
|
|
757
|
-
if (indexes.length > 1) {
|
|
758
|
-
duplicateIds.push({
|
|
759
|
-
id: tasks[indexes[0]]?.id ?? normalizedId,
|
|
760
|
-
indexes,
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
if (missingTaskIndexes.length > 0 || duplicateIds.length > 0) {
|
|
766
|
-
const problems: string[] = [];
|
|
767
|
-
if (missingTaskIndexes.length > 0) {
|
|
768
|
-
problems.push(`Missing task ids at indexes: ${missingTaskIndexes.join(", ")}`);
|
|
769
|
-
}
|
|
770
|
-
if (duplicateIds.length > 0) {
|
|
771
|
-
const details = duplicateIds.map(entry => `${entry.id} (indexes ${entry.indexes.join(", ")})`).join("; ");
|
|
772
|
-
problems.push(`Duplicate task ids detected (case-insensitive): ${details}`);
|
|
773
|
-
}
|
|
840
|
+
const taskIdProblem = validateTaskIds(tasks);
|
|
841
|
+
if (taskIdProblem) {
|
|
774
842
|
return {
|
|
775
|
-
content: [{ type: "text", text:
|
|
843
|
+
content: [{ type: "text", text: taskIdProblem }],
|
|
776
844
|
details: {
|
|
777
845
|
projectAgentsDir,
|
|
778
846
|
results: [],
|
|
@@ -951,7 +1019,11 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
951
1019
|
}
|
|
952
1020
|
emitProgress();
|
|
953
1021
|
|
|
954
|
-
const runTask = async (
|
|
1022
|
+
const runTask = async (
|
|
1023
|
+
task: (typeof tasksWithUniqueIds)[number],
|
|
1024
|
+
index: number,
|
|
1025
|
+
workerSignal?: AbortSignal,
|
|
1026
|
+
) => {
|
|
955
1027
|
if (!isIsolated) {
|
|
956
1028
|
return runSubprocess({
|
|
957
1029
|
cwd: this.session.cwd,
|
|
@@ -962,6 +1034,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
962
1034
|
planReference,
|
|
963
1035
|
description: task.description,
|
|
964
1036
|
index,
|
|
1037
|
+
parentToolCallId: toolCallId,
|
|
965
1038
|
id: task.id,
|
|
966
1039
|
taskDepth,
|
|
967
1040
|
modelOverride,
|
|
@@ -973,12 +1046,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
973
1046
|
artifactsDir: effectiveArtifactsDir,
|
|
974
1047
|
contextFile: contextFilePath,
|
|
975
1048
|
enableLsp: subagentLspEnabled,
|
|
976
|
-
signal,
|
|
1049
|
+
signal: workerSignal ?? signal,
|
|
977
1050
|
eventBus: this.session.eventBus,
|
|
978
1051
|
onProgress: progress => {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1052
|
+
// Shallow snapshot; recentTools is mutated in place by the
|
|
1053
|
+
// executor, the rest is reassigned or immutable. A deep clone
|
|
1054
|
+
// here cost O(extractedToolData) per progress event.
|
|
1055
|
+
progressMap.set(index, { ...progress, recentTools: progress.recentTools.slice() });
|
|
982
1056
|
emitProgress();
|
|
983
1057
|
},
|
|
984
1058
|
authStorage: this.session.authStorage,
|
|
@@ -1023,6 +1097,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1023
1097
|
planReference,
|
|
1024
1098
|
description: task.description,
|
|
1025
1099
|
index,
|
|
1100
|
+
parentToolCallId: toolCallId,
|
|
1026
1101
|
id: task.id,
|
|
1027
1102
|
taskDepth,
|
|
1028
1103
|
modelOverride,
|
|
@@ -1034,12 +1109,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1034
1109
|
artifactsDir: effectiveArtifactsDir,
|
|
1035
1110
|
contextFile: contextFilePath,
|
|
1036
1111
|
enableLsp: subagentLspEnabled,
|
|
1037
|
-
signal,
|
|
1112
|
+
signal: workerSignal ?? signal,
|
|
1038
1113
|
eventBus: this.session.eventBus,
|
|
1039
1114
|
onProgress: progress => {
|
|
1040
|
-
progressMap.set(index, {
|
|
1041
|
-
...structuredClone(progress),
|
|
1042
|
-
});
|
|
1115
|
+
progressMap.set(index, { ...progress, recentTools: progress.recentTools.slice() });
|
|
1043
1116
|
emitProgress();
|
|
1044
1117
|
},
|
|
1045
1118
|
authStorage: this.session.authStorage,
|
|
@@ -1226,6 +1299,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1226
1299
|
const conflictPart = mergeResult.conflict ? `\nConflict: ${mergeResult.conflict}` : "";
|
|
1227
1300
|
mergeSummary = `\n\n<system-notification>Branch merge failed. ${mergedPart}${failedPart}${conflictPart}\nUnmerged branches remain for manual resolution.</system-notification>`;
|
|
1228
1301
|
}
|
|
1302
|
+
if (mergeResult.stashConflict) {
|
|
1303
|
+
mergeSummary += `\n\n<system-notification>${mergeResult.stashConflict}</system-notification>`;
|
|
1304
|
+
}
|
|
1229
1305
|
}
|
|
1230
1306
|
|
|
1231
1307
|
// Clean up merged branches (keep failed ones for manual resolution)
|
|
@@ -1234,9 +1310,11 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1234
1310
|
await cleanupTaskBranches(repoRoot, allBranches);
|
|
1235
1311
|
}
|
|
1236
1312
|
} else {
|
|
1237
|
-
// Patch mode:
|
|
1238
|
-
|
|
1239
|
-
const
|
|
1313
|
+
// Patch mode: apply patches from successful tasks. Failed or
|
|
1314
|
+
// aborted siblings must not block completed work from landing.
|
|
1315
|
+
const successfulResults = results.filter(r => r.exitCode === 0 && !r.error && !r.aborted);
|
|
1316
|
+
const patchesInOrder = successfulResults.map(result => result.patchPath).filter(Boolean) as string[];
|
|
1317
|
+
const missingPatch = successfulResults.some(result => !result.patchPath);
|
|
1240
1318
|
if (missingPatch) {
|
|
1241
1319
|
changesApplied = false;
|
|
1242
1320
|
hadAnyChanges = false;
|
package/src/task/parallel.ts
CHANGED
|
@@ -20,13 +20,13 @@ export interface ParallelResult<R> {
|
|
|
20
20
|
*
|
|
21
21
|
* @param items - Items to process
|
|
22
22
|
* @param concurrency - Maximum concurrent operations
|
|
23
|
-
* @param fn - Async function to execute for each item
|
|
23
|
+
* @param fn - Async function to execute for each item; receives a worker signal that fires on abort or fail-fast so in-flight siblings can cancel
|
|
24
24
|
* @param signal - Optional abort signal to stop scheduling new work
|
|
25
25
|
*/
|
|
26
26
|
export async function mapWithConcurrencyLimit<T, R>(
|
|
27
27
|
items: T[],
|
|
28
28
|
concurrency: number,
|
|
29
|
-
fn: (item: T, index: number) => Promise<R>,
|
|
29
|
+
fn: (item: T, index: number, signal: AbortSignal) => Promise<R>,
|
|
30
30
|
signal?: AbortSignal,
|
|
31
31
|
): Promise<ParallelResult<R>> {
|
|
32
32
|
const normalizedConcurrency = Number.isFinite(concurrency) ? Math.floor(concurrency) : items.length;
|
|
@@ -52,7 +52,7 @@ export async function mapWithConcurrencyLimit<T, R>(
|
|
|
52
52
|
const index = nextIndex++;
|
|
53
53
|
if (index >= items.length) return;
|
|
54
54
|
try {
|
|
55
|
-
results[index] = await fn(items[index], index);
|
|
55
|
+
results[index] = await fn(items[index], index, workerSignal);
|
|
56
56
|
} catch (error) {
|
|
57
57
|
// On abort, the fn itself handles it and returns a result
|
|
58
58
|
// Only propagate non-abort errors
|
package/src/task/render.ts
CHANGED
|
@@ -541,7 +541,7 @@ function renderTaskItemLines(tasks: TaskItem[] | undefined, expanded: boolean, t
|
|
|
541
541
|
* the merged result frame so the brief stays visible for the whole task
|
|
542
542
|
* lifecycle — not just until the first progress snapshot replaces the call view.
|
|
543
543
|
*/
|
|
544
|
-
type TaskRenderSection = { lines: string[] };
|
|
544
|
+
type TaskRenderSection = { lines: readonly string[] };
|
|
545
545
|
type ContextSectionRenderer = (width: number) => TaskRenderSection;
|
|
546
546
|
|
|
547
547
|
// Default output-block layout is: left border + one-cell content inset + right
|
|
@@ -578,7 +578,7 @@ export function renderCall(
|
|
|
578
578
|
const header = renderStatusLine({ icon: "pending", title: "Task", description: args.agent }, theme);
|
|
579
579
|
const contextSectionRenderer = createContextSectionRenderer(args, theme);
|
|
580
580
|
return framedBlock(theme, width => {
|
|
581
|
-
const sections: Array<{ label?: string; lines: string[]; separator?: boolean }> = [];
|
|
581
|
+
const sections: Array<{ label?: string; lines: readonly string[]; separator?: boolean }> = [];
|
|
582
582
|
|
|
583
583
|
if (contextSectionRenderer) sections.push(contextSectionRenderer(width));
|
|
584
584
|
|
|
@@ -1072,6 +1072,20 @@ function renderAgentResult(
|
|
|
1072
1072
|
return lines;
|
|
1073
1073
|
}
|
|
1074
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* Order live progress entries so finished agents render first and unfinished
|
|
1077
|
+
* (pending/running) ones stay pinned at the bottom as tasks complete. Stable
|
|
1078
|
+
* within each group, so agents keep their dispatch order.
|
|
1079
|
+
*/
|
|
1080
|
+
function orderProgressForDisplay(progress: readonly AgentProgress[]): AgentProgress[] {
|
|
1081
|
+
const finished: AgentProgress[] = [];
|
|
1082
|
+
const unfinished: AgentProgress[] = [];
|
|
1083
|
+
for (const p of progress) {
|
|
1084
|
+
(p.status === "pending" || p.status === "running" ? unfinished : finished).push(p);
|
|
1085
|
+
}
|
|
1086
|
+
return finished.concat(unfinished);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1075
1089
|
/**
|
|
1076
1090
|
* Render the tool result.
|
|
1077
1091
|
*/
|
|
@@ -1140,7 +1154,7 @@ export function renderResult(
|
|
|
1140
1154
|
const shouldRenderProgress =
|
|
1141
1155
|
Boolean(details.progress && details.progress.length > 0) && (isPartial || details.results.length === 0);
|
|
1142
1156
|
if (shouldRenderProgress && details.progress) {
|
|
1143
|
-
details.progress.forEach(progress => {
|
|
1157
|
+
orderProgressForDisplay(details.progress).forEach(progress => {
|
|
1144
1158
|
lines.push(...renderAgentProgress(progress, "", " ", expanded, theme, spinnerFrame));
|
|
1145
1159
|
});
|
|
1146
1160
|
} else if (details.results && details.results.length > 0) {
|
|
@@ -1269,8 +1283,9 @@ function renderNestedTaskTree(
|
|
|
1269
1283
|
}
|
|
1270
1284
|
const inflight = details.progress;
|
|
1271
1285
|
if (inflight && inflight.length > 0) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1286
|
+
const ordered = orderProgressForDisplay(inflight);
|
|
1287
|
+
ordered.forEach((prog, index) => {
|
|
1288
|
+
const { prefix, continuePrefix } = nestedMarkers(index === ordered.length - 1, theme);
|
|
1274
1289
|
lines.push(...renderAgentProgress(prog, prefix, continuePrefix, expanded, theme, spinnerFrame));
|
|
1275
1290
|
});
|
|
1276
1291
|
}
|