@ashx-j/lunr-dev 0.2.13-dev.8.1 → 0.2.19-dev.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -1
- package/README.md +7 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +44 -19
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +14 -0
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +37 -3
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts +1 -15
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +8 -21
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-computer-use.d.ts +3 -0
- package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -0
- package/dist/builtin-extensions/lunr-computer-use.js +157 -0
- package/dist/builtin-extensions/lunr-computer-use.js.map +1 -0
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +6 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts +4 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +172 -171
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts +50 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js +131 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts +7 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +211 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts +0 -5
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js +0 -202
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +276 -109
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts +4 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +10 -3
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js +6 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts +6 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +33 -8
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +2 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +68 -15
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +6 -2
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +5 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.d.ts +10 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.js +38 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/request-params.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +3 -16
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +72 -31
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +105 -54
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.js +82 -65
- package/dist/builtin-extensions/pi-web-access/gemini-search.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.js +2 -0
- package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +259 -168
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/lazy.d.ts +18 -0
- package/dist/builtin-extensions/pi-web-access/lazy.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/lazy.js +19 -0
- package/dist/builtin-extensions/pi-web-access/lazy.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +6 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +17 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -0
- package/dist/catalog/publication.json +12 -12
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.d.ts.map +1 -0
- package/dist/cli-runtime.js +73 -0
- package/dist/cli-runtime.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -53
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +56 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +10 -5
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/computer-use/adapter.d.ts +23 -0
- package/dist/core/computer-use/adapter.d.ts.map +1 -0
- package/dist/core/computer-use/adapter.js +116 -0
- package/dist/core/computer-use/adapter.js.map +1 -0
- package/dist/core/computer-use/lease.d.ts +18 -0
- package/dist/core/computer-use/lease.d.ts.map +1 -0
- package/dist/core/computer-use/lease.js +129 -0
- package/dist/core/computer-use/lease.js.map +1 -0
- package/dist/core/computer-use/macos-runtime.d.ts +14 -0
- package/dist/core/computer-use/macos-runtime.d.ts.map +1 -0
- package/dist/core/computer-use/macos-runtime.js +148 -0
- package/dist/core/computer-use/macos-runtime.js.map +1 -0
- package/dist/core/computer-use/policy.d.ts +19 -0
- package/dist/core/computer-use/policy.d.ts.map +1 -0
- package/dist/core/computer-use/policy.js +66 -0
- package/dist/core/computer-use/policy.js.map +1 -0
- package/dist/core/computer-use/release.generated.d.ts +31 -0
- package/dist/core/computer-use/release.generated.d.ts.map +1 -0
- package/dist/core/computer-use/release.generated.js +36 -0
- package/dist/core/computer-use/release.generated.js.map +1 -0
- package/dist/core/computer-use/runtime.d.ts +13 -0
- package/dist/core/computer-use/runtime.d.ts.map +1 -0
- package/dist/core/computer-use/runtime.js +158 -0
- package/dist/core/computer-use/runtime.js.map +1 -0
- package/dist/core/computer-use/schemas.d.ts +88 -0
- package/dist/core/computer-use/schemas.d.ts.map +1 -0
- package/dist/core/computer-use/schemas.js +80 -0
- package/dist/core/computer-use/schemas.js.map +1 -0
- package/dist/core/computer-use/windows-desktop.d.ts +3 -0
- package/dist/core/computer-use/windows-desktop.d.ts.map +1 -0
- package/dist/core/computer-use/windows-desktop.js +48 -0
- package/dist/core/computer-use/windows-desktop.js.map +1 -0
- package/dist/core/computer-use/workflow.d.ts +24 -0
- package/dist/core/computer-use/workflow.d.ts.map +1 -0
- package/dist/core/computer-use/workflow.js +330 -0
- package/dist/core/computer-use/workflow.js.map +1 -0
- package/dist/core/extensions/host-static.d.ts.map +1 -1
- package/dist/core/extensions/host-static.js +34 -12
- package/dist/core/extensions/host-static.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -64
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +2 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.d.ts +2 -2
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-runtime.d.ts +0 -3
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +1 -10
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions.d.ts +1 -3
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +33 -25
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +9 -0
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/rollback.d.ts.map +1 -1
- package/dist/core/rollback.js +13 -90
- package/dist/core/rollback.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +0 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +21 -79
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +19 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/timings.d.ts +2 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +17 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/dev-cli.js +0 -0
- package/dist/gateway/agent-bridge.d.ts +6 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +23 -6
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -1
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +1 -18
- package/dist/gateway/commands.js.map +1 -1
- package/dist/gateway/cron.d.ts +1 -1
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +23 -7
- package/dist/gateway/cron.js.map +1 -1
- package/dist/main.d.ts +2 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +57 -48
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +4 -2
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +23 -32
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/processes-selector.d.ts +2 -3
- package/dist/modes/interactive/components/processes-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/processes-selector.js +5 -10
- package/dist/modes/interactive/components/processes-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +20 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +12 -21
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +138 -152
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +95 -84
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/thinking-level-slash.d.ts +4 -0
- package/dist/modes/interactive/thinking-level-slash.d.ts.map +1 -0
- package/dist/modes/interactive/thinking-level-slash.js +23 -0
- package/dist/modes/interactive/thinking-level-slash.js.map +1 -0
- package/dist/node-runtime/adapter-AZMY335J.js +279 -0
- package/dist/node-runtime/agent-session-services-RSGP5GDN.js +39 -0
- package/dist/node-runtime/benchmark-request-UC7PD3V3.js +57 -0
- package/dist/node-runtime/brave-UWDA3I22.js +143 -0
- package/dist/node-runtime/builtin-extensions-7STLFW3A.js +29 -0
- package/dist/node-runtime/chunk-2IDTZAG5.js +1415 -0
- package/dist/node-runtime/chunk-3GHXVUAK.js +21 -0
- package/dist/node-runtime/chunk-3RCWXWAY.js +466 -0
- package/dist/node-runtime/chunk-3WFAEVKS.js +115 -0
- package/dist/node-runtime/chunk-3X3KZKMI.js +49 -0
- package/dist/node-runtime/chunk-4HXHDDCF.js +16 -0
- package/dist/node-runtime/chunk-4SJWFTUP.js +24 -0
- package/dist/node-runtime/chunk-4ZWGEEAS.js +69 -0
- package/dist/node-runtime/chunk-53YJI7JU.js +137 -0
- package/dist/node-runtime/chunk-5NWQCSKR.js +19218 -0
- package/dist/node-runtime/chunk-5OLJGVCB.js +68 -0
- package/dist/node-runtime/chunk-5Q45AVQG.js +108 -0
- package/dist/node-runtime/chunk-5SQDLSZJ.js +949 -0
- package/dist/node-runtime/chunk-6SPPH4FY.js +1269 -0
- package/dist/node-runtime/chunk-6UB6NKA3.js +860 -0
- package/dist/node-runtime/chunk-7AFOFSDY.js +105 -0
- package/dist/node-runtime/chunk-7N3YHTYF.js +26 -0
- package/dist/node-runtime/chunk-ARC4KOTI.js +11178 -0
- package/dist/node-runtime/chunk-AUXVGBO4.js +19 -0
- package/dist/node-runtime/chunk-AY6CAJSL.js +30 -0
- package/dist/node-runtime/chunk-BHO7O6E5.js +296 -0
- package/dist/node-runtime/chunk-BI3DHYCN.js +8845 -0
- package/dist/node-runtime/chunk-BKAZ6BKX.js +2185 -0
- package/dist/node-runtime/chunk-BR7YDUKA.js +109 -0
- package/dist/node-runtime/chunk-BRJX2PRC.js +53 -0
- package/dist/node-runtime/chunk-C4O2RSQQ.js +37 -0
- package/dist/node-runtime/chunk-CCZJ3PYY.js +17 -0
- package/dist/node-runtime/chunk-CGBXPL3E.js +1171 -0
- package/dist/node-runtime/chunk-CH6OL6P2.js +295 -0
- package/dist/node-runtime/chunk-CJECDQKP.js +19 -0
- package/dist/node-runtime/chunk-DAPBPCBP.js +53 -0
- package/dist/node-runtime/chunk-DDGMHUOW.js +305 -0
- package/dist/node-runtime/chunk-DMMTJQMQ.js +43 -0
- package/dist/node-runtime/chunk-DVGWF6A2.js +1180 -0
- package/dist/node-runtime/chunk-EH2LJQPM.js +64 -0
- package/dist/node-runtime/chunk-EHMBW7FA.js +772 -0
- package/dist/node-runtime/chunk-FLYMHKCS.js +966 -0
- package/dist/node-runtime/chunk-FVDOXRUD.js +96 -0
- package/dist/node-runtime/chunk-GZQHMEWL.js +126 -0
- package/dist/node-runtime/chunk-H2EOZYMP.js +57 -0
- package/dist/node-runtime/chunk-HQCKFM2O.js +56 -0
- package/dist/node-runtime/chunk-HRS4HMDX.js +610 -0
- package/dist/node-runtime/chunk-HYHJIRDW.js +19 -0
- package/dist/node-runtime/chunk-I6R76AG7.js +22 -0
- package/dist/node-runtime/chunk-ISQDQIZN.js +296 -0
- package/dist/node-runtime/chunk-J7QGQD45.js +24921 -0
- package/dist/node-runtime/chunk-JHYKXIMM.js +378 -0
- package/dist/node-runtime/chunk-JT6YCS5B.js +5648 -0
- package/dist/node-runtime/chunk-JZHEUQRL.js +180 -0
- package/dist/node-runtime/chunk-K72NVGUB.js +2834 -0
- package/dist/node-runtime/chunk-KKSIVROT.js +88 -0
- package/dist/node-runtime/chunk-LCACDWHJ.js +1743 -0
- package/dist/node-runtime/chunk-LTXUQJI4.js +173 -0
- package/dist/node-runtime/chunk-LWJXTA6Z.js +6814 -0
- package/dist/node-runtime/chunk-M3LZLQW2.js +98 -0
- package/dist/node-runtime/chunk-MGSB2C3M.js +46 -0
- package/dist/node-runtime/chunk-MGWCVY6Y.js +47 -0
- package/dist/node-runtime/chunk-MJQDX3BA.js +72 -0
- package/dist/node-runtime/chunk-MUMMMTW4.js +8392 -0
- package/dist/node-runtime/chunk-MX3GJNXJ.js +986 -0
- package/dist/node-runtime/chunk-NUIFPZWX.js +209 -0
- package/dist/node-runtime/chunk-OBMWP3I5.js +68 -0
- package/dist/node-runtime/chunk-OR4L3IB2.js +42 -0
- package/dist/node-runtime/chunk-OTT6JMPU.js +807 -0
- package/dist/node-runtime/chunk-PCIGF2XK.js +250 -0
- package/dist/node-runtime/chunk-PL6JKXWI.js +3622 -0
- package/dist/node-runtime/chunk-PVCDKGR6.js +55 -0
- package/dist/node-runtime/chunk-Q4Q6D4IY.js +364 -0
- package/dist/node-runtime/chunk-Q5ZU2VRT.js +83 -0
- package/dist/node-runtime/chunk-QFWG2RVL.js +5477 -0
- package/dist/node-runtime/chunk-RQ2Q5IYR.js +72 -0
- package/dist/node-runtime/chunk-RQ5DNUNG.js +429 -0
- package/dist/node-runtime/chunk-RRIZE6QQ.js +17 -0
- package/dist/node-runtime/chunk-SN6G4OU6.js +4753 -0
- package/dist/node-runtime/chunk-SWSN6HAB.js +53 -0
- package/dist/node-runtime/chunk-T5GM5BFH.js +7558 -0
- package/dist/node-runtime/chunk-TAP7QTUT.js +1346 -0
- package/dist/node-runtime/chunk-U6C7VTR5.js +2033 -0
- package/dist/node-runtime/chunk-URD2SLIT.js +1019 -0
- package/dist/node-runtime/chunk-VRB2IYGG.js +53 -0
- package/dist/node-runtime/chunk-W3CVH5RX.js +62 -0
- package/dist/node-runtime/chunk-W7S2REEG.js +9478 -0
- package/dist/node-runtime/chunk-XBWPBZBN.js +125 -0
- package/dist/node-runtime/chunk-XTHQ7XR7.js +509 -0
- package/dist/node-runtime/chunk-ZCHMJ34K.js +130 -0
- package/dist/node-runtime/chunk-ZRDIMBEQ.js +651 -0
- package/dist/node-runtime/chunk-ZSEZY3II.js +247 -0
- package/dist/node-runtime/cli-runtime.js +81 -0
- package/dist/node-runtime/clipboard-OZ63BBC6.js +12 -0
- package/dist/node-runtime/clipboard-image-7QR6GXVA.js +13 -0
- package/dist/node-runtime/command-6UXQ6XZN.js +13 -0
- package/dist/node-runtime/commands-LGR5VT5X.js +293 -0
- package/dist/node-runtime/config-FMEOZSWE.js +77 -0
- package/dist/node-runtime/curator-server-XCM43VEF.js +3816 -0
- package/dist/node-runtime/customize-XEXL2DGQ.js +12 -0
- package/dist/node-runtime/direct-tool-executor-TDJS7TBD.js +191 -0
- package/dist/node-runtime/exa-LL64QZR2.js +329 -0
- package/dist/node-runtime/export-html-E4DT6ZJQ.js +150 -0
- package/dist/node-runtime/extension-input-RV5N6APL.js +14 -0
- package/dist/node-runtime/extension-selector-CPD5LWDD.js +14 -0
- package/dist/node-runtime/extract-IUCVZCWC.js +17 -0
- package/dist/node-runtime/file-sync-HCOP2QVF.js +117 -0
- package/dist/node-runtime/gateway-3OLC4GQN.js +2139 -0
- package/dist/node-runtime/gemini-api-QE7ANVDZ.js +29 -0
- package/dist/node-runtime/gemini-search-NCIDF7NE.js +330 -0
- package/dist/node-runtime/gemini-url-context-OKIN7AIP.js +103 -0
- package/dist/node-runtime/gemini-web-MM446SEH.js +14 -0
- package/dist/node-runtime/github-extract-F64RZOEA.js +619 -0
- package/dist/node-runtime/goal-CZQ7FFQJ.js +1831 -0
- package/dist/node-runtime/image-resize-6ZPBFMWW.js +11 -0
- package/dist/node-runtime/index.js +863 -0
- package/dist/node-runtime/init-EKE277AG.js +30 -0
- package/dist/node-runtime/install-cli-TH7IJJQQ.js +308 -0
- package/dist/node-runtime/interactive-mode-S5M26CQ7.js +59 -0
- package/dist/node-runtime/interactive-view-N6F4KUIT.js +247 -0
- package/dist/node-runtime/lsp-manager-3ASUSO4T.js +1104 -0
- package/dist/node-runtime/lunr-computer-use-4X3KUNF4.js +206 -0
- package/dist/node-runtime/lunr-cron-KKBOGAEF.js +274 -0
- package/dist/node-runtime/main.js +57 -0
- package/dist/node-runtime/mcp-auth-flow-YL6RWPYG.js +33 -0
- package/dist/node-runtime/mcp-panel-IIGMXQGP.js +501 -0
- package/dist/node-runtime/mcp-setup-panel-FEK7663I.js +355 -0
- package/dist/node-runtime/memory-cap-3S4XRLNP.js +20 -0
- package/dist/node-runtime/model-tiers-NJLHUEGK.js +20 -0
- package/dist/node-runtime/openai-search-B3MSNCQA.js +288 -0
- package/dist/node-runtime/package-manager-cli-C4EJOO5Q.js +751 -0
- package/dist/node-runtime/parallel-AI2MNSCU.js +265 -0
- package/dist/node-runtime/parser-manager-TQHWYDH7.js +152 -0
- package/dist/node-runtime/pdf-extract-IPE6H4HP.js +57 -0
- package/dist/node-runtime/perplexity-QVJW3I35.js +12 -0
- package/dist/node-runtime/pi-intercom-EEMBGQID.js +2215 -0
- package/dist/node-runtime/pi-mcp-adapter-DPTJGAAL.js +428 -0
- package/dist/node-runtime/pi-ollama-cloud-4VZRMULP.js +1440 -0
- package/dist/node-runtime/pi-prompt-template-model-4QT4LLXF.js +3946 -0
- package/dist/node-runtime/pi-subagents-VKCLYIBY.js +6050 -0
- package/dist/node-runtime/pi-web-access-BV6DSZOP.js +1867 -0
- package/dist/node-runtime/print-mode-VWIRFDAC.js +12 -0
- package/dist/node-runtime/proxy-modes-R64LU6WZ.js +688 -0
- package/dist/node-runtime/rpc-entry.js +65 -0
- package/dist/node-runtime/rpc-mode-SJNSOA4G.js +13 -0
- package/dist/node-runtime/runtime-3SB5ATHQ.js +21 -0
- package/dist/node-runtime/runtime-bridges-UJ5WJJLN.js +14 -0
- package/dist/node-runtime/session-manager-QEMZBMTM.js +33 -0
- package/dist/node-runtime/session-selector-URVJLOTK.js +14 -0
- package/dist/node-runtime/settings-manager-K6IPIQTI.js +23 -0
- package/dist/node-runtime/src-GPFAU3N6.js +2427 -0
- package/dist/node-runtime/subagent-executor-EFLTO32A.js +8501 -0
- package/dist/node-runtime/summary-review-YSMXKITO.js +228 -0
- package/dist/node-runtime/tavily-WEZFVFP7.js +154 -0
- package/dist/node-runtime/timings-UAD5BEAS.js +16 -0
- package/dist/node-runtime/tool-renderer-MV5O452K.js +204 -0
- package/dist/node-runtime/update-cli-I6OVDRPL.js +75 -0
- package/dist/node-runtime/v4-5MNC3H7G.js +424 -0
- package/dist/node-runtime/video-extract-ETA5RA7U.js +304 -0
- package/dist/node-runtime/workflow-SA34S4VX.js +325 -0
- package/dist/node-runtime/workspace-index-6DBXSTJ6.js +163 -0
- package/dist/node-runtime/youtube-extract-SRVGNN26.js +264 -0
- package/dist/rpc.d.ts +3 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +18 -0
- package/dist/rpc.js.map +1 -0
- package/dist/startup/benchmark-request.d.ts +3 -0
- package/dist/startup/benchmark-request.d.ts.map +1 -0
- package/dist/startup/benchmark-request.js +65 -0
- package/dist/startup/benchmark-request.js.map +1 -0
- package/dist/startup/{interactive-shell.d.ts → interactive-view.d.ts} +18 -18
- package/dist/startup/interactive-view.d.ts.map +1 -0
- package/dist/startup/{interactive-shell.js → interactive-view.js} +119 -52
- package/dist/startup/interactive-view.js.map +1 -0
- package/dist/startup/launch-routing.d.ts.map +1 -1
- package/dist/startup/launch-routing.js +9 -10
- package/dist/startup/launch-routing.js.map +1 -1
- package/dist/startup/startup-milestones.d.ts +1 -1
- package/dist/startup/startup-milestones.d.ts.map +1 -1
- package/dist/startup/startup-milestones.js +3 -2
- package/dist/startup/startup-milestones.js.map +1 -1
- package/dist/utils/await-with-abort.d.ts +2 -0
- package/dist/utils/await-with-abort.d.ts.map +1 -0
- package/dist/utils/await-with-abort.js +19 -0
- package/dist/utils/await-with-abort.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +10 -4
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/docs/compaction.md +5 -3
- package/docs/computer-use.md +277 -0
- package/docs/development.md +5 -33
- package/docs/features.md +2 -3
- package/docs/interactive-startup.md +105 -0
- package/docs/keybindings.md +2 -2
- package/docs/terminal-setup.md +1 -1
- package/docs/usage.md +1 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/install-lock/package-lock.json +3827 -0
- package/install-lock/package.json +18 -0
- package/native/computer-use/LICENSE.md +21 -0
- package/npm-shrinkwrap.json +3808 -0
- package/package.json +30 -24
- package/dist/core/swarm.d.ts +0 -23
- package/dist/core/swarm.d.ts.map +0 -1
- package/dist/core/swarm.js +0 -45
- package/dist/core/swarm.js.map +0 -1
- package/dist/startup/interactive-shell.d.ts.map +0 -1
- package/dist/startup/interactive-shell.js.map +0 -1
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { ChildPermission } from "../../../../../core/subagent-permission-inherit.ts";
|
|
13
13
|
/** Parent-owned / persistent tools excluded even from full children. */
|
|
14
|
-
export declare const PARENT_OWNED_CHILD_TOOLS: readonly ["cron", "memory_add", "memory_remove", "memory_load", "present_plan", "goal_complete", "goal_blocked", "subagent", "subagent_wait"];
|
|
14
|
+
export declare const PARENT_OWNED_CHILD_TOOLS: readonly ["computer_load", "computer_apps", "computer_observe", "computer_click", "computer_drag", "computer_key", "computer_text", "computer_window", "computer_launch", "computer_scroll", "computer_end", "cron", "memory_add", "memory_remove", "memory_load", "present_plan", "goal_complete", "goal_blocked", "subagent", "subagent_wait"];
|
|
15
15
|
/** Mutating coding tools omitted from the read-only child registry. */
|
|
16
16
|
export declare const READ_ONLY_EXCLUDED_CHILD_TOOLS: readonly ["edit", "write", "code_rewrite", "lsp_rename"];
|
|
17
17
|
export interface ResolveChildExcludeToolsInput {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child-tools.d.ts","sourceRoot":"","sources":["../../../../../../src/builtin-extensions/pi-subagents/src/runs/shared/child-tools.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"child-tools.d.ts","sourceRoot":"","sources":["../../../../../../src/builtin-extensions/pi-subagents/src/runs/shared/child-tools.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oDAAoD,CAAC;AAE1F,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,kVAW3B,CAAC;AAEX,uEAAuE;AACvE,eAAO,MAAM,8BAA8B,0DAKjC,CAAC;AAKX,MAAM,WAAW,6BAA6B;IAC7C,WAAW,EAAE,eAAe,CAAC;IAC7B,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,EAAE,CASvF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjE","sourcesContent":["// @ts-nocheck\n/**\n * Central permission-to-tool resolver for prompt-driven children.\n *\n * Full access is the coding-tool set (read/search/shell/edit/write/web/LSP/MCP\n * plus output and supervisor coordination). It is not parent-equivalent: cron,\n * memory, goals, plan approval, nested subagent management, and other\n * persistent/user-config tools stay excluded.\n *\n * Read-only drops mutating coding tools from the registry and still runs under\n * Plan mode as defense in depth.\n */\n\nimport { COMPUTER_TOOLS } from \"../../../../../core/computer-use/policy.ts\";\nimport type { ChildPermission } from \"../../../../../core/subagent-permission-inherit.ts\";\n\n/** Parent-owned / persistent tools excluded even from full children. */\nexport const PARENT_OWNED_CHILD_TOOLS = [\n\t...COMPUTER_TOOLS,\n\t\"cron\",\n\t\"memory_add\",\n\t\"memory_remove\",\n\t\"memory_load\",\n\t\"present_plan\",\n\t\"goal_complete\",\n\t\"goal_blocked\",\n\t\"subagent\",\n\t\"subagent_wait\",\n] as const;\n\n/** Mutating coding tools omitted from the read-only child registry. */\nexport const READ_ONLY_EXCLUDED_CHILD_TOOLS = [\n\t\"edit\",\n\t\"write\",\n\t\"code_rewrite\",\n\t\"lsp_rename\",\n] as const;\n\nconst PARENT_OWNED_SET = new Set<string>(PARENT_OWNED_CHILD_TOOLS);\nconst READ_ONLY_SET = new Set<string>(READ_ONLY_EXCLUDED_CHILD_TOOLS);\n\nexport interface ResolveChildExcludeToolsInput {\n\tpermissions: ChildPermission;\n\t/** Dynamic fanout children may keep the child-safe subagent tool. */\n\tfanoutAuthorized?: boolean;\n}\n\nexport function resolveChildExcludeTools(input: ResolveChildExcludeToolsInput): string[] {\n\tconst excluded = new Set<string>(PARENT_OWNED_SET);\n\tif (input.fanoutAuthorized) {\n\t\texcluded.delete(\"subagent\");\n\t}\n\tif (input.permissions === \"read-only\") {\n\t\tfor (const tool of READ_ONLY_SET) excluded.add(tool);\n\t}\n\treturn [...excluded];\n}\n\nexport function isParentOwnedChildTool(name: string): boolean {\n\treturn PARENT_OWNED_SET.has(name);\n}\n\nexport function isReadOnlyExcludedChildTool(name: string): boolean {\n\treturn READ_ONLY_SET.has(name);\n}\n"]}
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* Read-only drops mutating coding tools from the registry and still runs under
|
|
11
11
|
* Plan mode as defense in depth.
|
|
12
12
|
*/
|
|
13
|
+
import { COMPUTER_TOOLS } from "../../../../../core/computer-use/policy.js";
|
|
13
14
|
/** Parent-owned / persistent tools excluded even from full children. */
|
|
14
15
|
export const PARENT_OWNED_CHILD_TOOLS = [
|
|
16
|
+
...COMPUTER_TOOLS,
|
|
15
17
|
"cron",
|
|
16
18
|
"memory_add",
|
|
17
19
|
"memory_remove",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child-tools.js","sourceRoot":"","sources":["../../../../../../src/builtin-extensions/pi-subagents/src/runs/shared/child-tools.ts"],"names":[],"mappings":"AAAA,cAAc;AACd;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"child-tools.js","sourceRoot":"","sources":["../../../../../../src/builtin-extensions/pi-subagents/src/runs/shared/child-tools.ts"],"names":[],"mappings":"AAAA,cAAc;AACd;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAG5E,wEAAwE;AACxE,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACvC,GAAG,cAAc;IACjB,MAAM;IACN,YAAY;IACZ,eAAe;IACf,aAAa;IACb,cAAc;IACd,eAAe;IACf,cAAc;IACd,UAAU;IACV,eAAe;CACN,CAAC;AAEX,uEAAuE;AACvE,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC7C,MAAM;IACN,OAAO;IACP,cAAc;IACd,YAAY;CACH,CAAC;AAEX,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAS,wBAAwB,CAAC,CAAC;AACnE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAS,8BAA8B,CAAC,CAAC;AAQtE,MAAM,UAAU,wBAAwB,CAAC,KAAoC,EAAY;IACxF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,gBAAgB,CAAC,CAAC;IACnD,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC5B,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,aAAa;YAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AAAA,CACrB;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAW;IAC7D,OAAO,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAAA,CAClC;AAED,MAAM,UAAU,2BAA2B,CAAC,IAAY,EAAW;IAClE,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAAA,CAC/B","sourcesContent":["// @ts-nocheck\n/**\n * Central permission-to-tool resolver for prompt-driven children.\n *\n * Full access is the coding-tool set (read/search/shell/edit/write/web/LSP/MCP\n * plus output and supervisor coordination). It is not parent-equivalent: cron,\n * memory, goals, plan approval, nested subagent management, and other\n * persistent/user-config tools stay excluded.\n *\n * Read-only drops mutating coding tools from the registry and still runs under\n * Plan mode as defense in depth.\n */\n\nimport { COMPUTER_TOOLS } from \"../../../../../core/computer-use/policy.ts\";\nimport type { ChildPermission } from \"../../../../../core/subagent-permission-inherit.ts\";\n\n/** Parent-owned / persistent tools excluded even from full children. */\nexport const PARENT_OWNED_CHILD_TOOLS = [\n\t...COMPUTER_TOOLS,\n\t\"cron\",\n\t\"memory_add\",\n\t\"memory_remove\",\n\t\"memory_load\",\n\t\"present_plan\",\n\t\"goal_complete\",\n\t\"goal_blocked\",\n\t\"subagent\",\n\t\"subagent_wait\",\n] as const;\n\n/** Mutating coding tools omitted from the read-only child registry. */\nexport const READ_ONLY_EXCLUDED_CHILD_TOOLS = [\n\t\"edit\",\n\t\"write\",\n\t\"code_rewrite\",\n\t\"lsp_rename\",\n] as const;\n\nconst PARENT_OWNED_SET = new Set<string>(PARENT_OWNED_CHILD_TOOLS);\nconst READ_ONLY_SET = new Set<string>(READ_ONLY_EXCLUDED_CHILD_TOOLS);\n\nexport interface ResolveChildExcludeToolsInput {\n\tpermissions: ChildPermission;\n\t/** Dynamic fanout children may keep the child-safe subagent tool. */\n\tfanoutAuthorized?: boolean;\n}\n\nexport function resolveChildExcludeTools(input: ResolveChildExcludeToolsInput): string[] {\n\tconst excluded = new Set<string>(PARENT_OWNED_SET);\n\tif (input.fanoutAuthorized) {\n\t\texcluded.delete(\"subagent\");\n\t}\n\tif (input.permissions === \"read-only\") {\n\t\tfor (const tool of READ_ONLY_SET) excluded.add(tool);\n\t}\n\treturn [...excluded];\n}\n\nexport function isParentOwnedChildTool(name: string): boolean {\n\treturn PARENT_OWNED_SET.has(name);\n}\n\nexport function isReadOnlyExcludedChildTool(name: string): boolean {\n\treturn READ_ONLY_SET.has(name);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fork-context.d.ts","sourceRoot":"","sources":["../../../../../src/builtin-extensions/pi-subagents/src/shared/fork-context.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEhE,KAAK,wBAAwB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,UAAU,kBAAkB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,oBAAoB;IAC7B,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,kBAAkB,EAAE,CAAC;CACxC;AAED,UAAU,sBAAsB;IAC/B,cAAc,IAAI,MAAM,GAAG,SAAS,CAAC;IACrC,SAAS,IAAI,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,IAAI,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,oBAAoB,CAAC;CAC1E;AAED,UAAU,0BAA0B;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,oBAAoB,CAAC;IAC1E;2FACuF;IACvF,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACtD;AAOD,UAAU,mBAAmB;IAC5B,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxD,wBAAwB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAE/E;AAED;wFACwF;AACxF,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,eAAe,CAAC,EAAE,SAAS,EAAE,EAC7B,iBAAiB,CAAC,EAAE,MAAM,GACxB,OAAO,CAMT;AA4DD,wBAAgB,yBAAyB,CACxC,cAAc,EAAE,sBAAsB,EACtC,gBAAgB,EAAE,OAAO,EACzB,OAAO,GAAE,0BAA+B,GACtC,mBAAmB,CA8ErB","sourcesContent":["// @ts-nocheck\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { SessionManager } from \"@ashx-j/lunr-dev\";\nimport { findModelInfo, type ModelInfo } from \"./model-info.ts\";\n\ntype SubagentExecutionContext = \"fresh\" | \"fork\";\n\ninterface BranchSessionEntry {\n\ttype: string;\n\tid?: string;\n\tparentId?: string | null;\n\ttimestamp?: string;\n\tmessage?: {\n\t\trole?: string;\n\t\tcontent?: unknown;\n\t\tprovider?: string;\n\t\tapi?: string;\n\t\tmodel?: string;\n\t};\n\tthinkingLevel?: string;\n}\n\ninterface BranchSessionManager {\n\tcreateBranchedSession(leafId: string): string | undefined;\n\tgetHeader?: () => BranchSessionEntry | null;\n\tgetEntries?: () => BranchSessionEntry[];\n}\n\ninterface ForkableSessionManager {\n\tgetSessionFile(): string | undefined;\n\tgetLeafId(): string | null;\n\tgetSessionDir?(): string;\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n}\n\ninterface ForkContextResolverOptions {\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n\t/** Decide per child index whether a sanitized transcript must also disable the child's\n\t * thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */\n\tforceThinkingOffForIndex?: (index: number) => boolean;\n}\n\ninterface ForkContextResolution {\n\tsessionFile: string;\n\tthinkingOverride?: \"off\";\n}\n\ninterface ForkContextResolver {\n\tsessionFileForIndex(index?: number): string | undefined;\n\tthinkingOverrideForIndex(index?: number): \"off\" | undefined;\n}\n\nexport function resolveSubagentContext(value: unknown): SubagentExecutionContext {\n\treturn value === \"fork\" ? \"fork\" : \"fresh\";\n}\n\n/** Decide whether a resolved child model uses Anthropic's provider or message API, which\n * requires the sanitized fork to disable thinking. Unknown models stay conservative. */\nexport function forkedChildRequiresThinkingOff(\n\tmodel: string | undefined,\n\tavailableModels?: ModelInfo[],\n\tpreferredProvider?: string,\n): boolean {\n\tif (!model) return true;\n\tconst info = findModelInfo(model, availableModels, preferredProvider);\n\tif (!info) return true;\n\treturn info.provider.toLowerCase() === \"anthropic\"\n\t\t|| info.api?.toLowerCase() === \"anthropic-messages\";\n}\n\nfunction isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry[\"message\"], block: unknown): boolean {\n\tif (!message || !block || typeof block !== \"object\" || !(\"type\" in block)) return false;\n\tconst provider = typeof message.provider === \"string\" ? message.provider.toLowerCase() : \"\";\n\tconst api = typeof message.api === \"string\" ? message.api.toLowerCase() : \"\";\n\tconst model = typeof message.model === \"string\" ? message.model.toLowerCase() : \"\";\n\tconst isAnthropic = provider === \"anthropic\" || api === \"anthropic-messages\" || model.startsWith(\"anthropic/\");\n\tif (block.type === \"redacted_thinking\") return true;\n\tif (block.type !== \"thinking\" || !isAnthropic) return false;\n\tconst signature = \"thinkingSignature\" in block ? block.thinkingSignature : \"signature\" in block ? block.signature : undefined;\n\treturn block.redacted === true || (typeof signature === \"string\" && signature.length > 0);\n}\n\nfunction createEntryId(entries: BranchSessionEntry[]): string {\n\tconst ids = new Set(entries.map((entry) => entry.id).filter((id): id is string => typeof id === \"string\"));\n\tfor (let attempt = 0; attempt < 100; attempt++) {\n\t\tconst id = randomUUID().slice(0, 8);\n\t\tif (!ids.has(id)) return id;\n\t}\n\treturn randomUUID();\n}\n\nfunction appendThinkingOffEntry(entries: BranchSessionEntry[]): void {\n\tconst last = entries[entries.length - 1];\n\tif (last?.type === \"thinking_level_change\" && last.thinkingLevel === \"off\") return;\n\tconst parent = [...entries].reverse().find((entry) => typeof entry.id === \"string\");\n\tentries.push({\n\t\ttype: \"thinking_level_change\",\n\t\tid: createEntryId(entries),\n\t\tparentId: parent?.id ?? null,\n\t\ttimestamp: new Date().toISOString(),\n\t\tthinkingLevel: \"off\",\n\t});\n}\n\nfunction sanitizeUnsafeThinkingBlocks(entries: BranchSessionEntry[]): boolean {\n\tlet sanitized = false;\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\" || entry.message?.role !== \"assistant\" || !Array.isArray(entry.message.content)) continue;\n\t\tconst filtered = entry.message.content.filter((block) => !isUnsafeAnthropicThinkingBlock(entry.message, block));\n\t\tif (filtered.length === entry.message.content.length) continue;\n\t\tentry.message.content = filtered;\n\t\tsanitized = true;\n\t}\n\treturn sanitized;\n}\n\nfunction readSessionEntries(sessionFile: string): BranchSessionEntry[] {\n\tconst lines = fs.readFileSync(sessionFile, \"utf-8\").split(\"\\n\").filter((line) => line.trim().length > 0);\n\treturn lines.map((line, index) => {\n\t\ttry {\n\t\t\treturn JSON.parse(line) as BranchSessionEntry;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Unable to inspect forked session ${sessionFile}: invalid JSONL on line ${index + 1}: ${cause.message}`, { cause });\n\t\t}\n\t});\n}\n\nexport function createForkContextResolver(\n\tsessionManager: ForkableSessionManager,\n\trequestedContext: unknown,\n\toptions: ForkContextResolverOptions = {},\n): ForkContextResolver {\n\tif (resolveSubagentContext(requestedContext) !== \"fork\") {\n\t\treturn {\n\t\t\tsessionFileForIndex: () => undefined,\n\t\t\tthinkingOverrideForIndex: () => undefined,\n\t\t};\n\t}\n\n\tconst parentSessionFile = sessionManager.getSessionFile();\n\tif (!parentSessionFile) {\n\t\tthrow new Error(\"Forked subagent context requires a persisted parent session.\");\n\t}\n\n\tconst leafId = sessionManager.getLeafId();\n\tif (!leafId) {\n\t\tthrow new Error(\"Forked subagent context requires a current leaf to fork from.\");\n\t}\n\n\tconst openSession = options.openSession\n\t\t?? sessionManager.openSession\n\t\t?? ((file: string, dir?: string) => SessionManager.open(file, dir));\n\tconst sessionDir = sessionManager.getSessionDir?.();\n\tconst cachedResolutions = new Map<number, ForkContextResolution>();\n\n\tconst resolveFork = (index = 0): ForkContextResolution => {\n\t\tconst cached = cachedResolutions.get(index);\n\t\tif (cached) return cached;\n\t\ttry {\n\t\t\tif (!fs.existsSync(parentSessionFile)) {\n\t\t\t\tthrow new Error(`Parent session file does not exist: ${parentSessionFile}. Pi has not persisted enough history to fork yet.`);\n\t\t\t}\n\t\t\tconst sourceManager = openSession(parentSessionFile, sessionDir);\n\t\t\tconst sessionFile = sourceManager.createBranchedSession(leafId);\n\t\t\tif (!sessionFile) {\n\t\t\t\tthrow new Error(\"Session manager did not return a forked session file.\");\n\t\t\t}\n\t\t\tconst forceThinkingOff = (sanitized: boolean): boolean =>\n\t\t\t\tsanitized && (options.forceThinkingOffForIndex?.(index) ?? true);\n\t\t\tlet thinkingOverride: \"off\" | undefined;\n\t\t\tif (!fs.existsSync(sessionFile)) {\n\t\t\t\tconst header = sourceManager.getHeader?.();\n\t\t\t\tconst entries = sourceManager.getEntries?.();\n\t\t\t\tif (!header || !entries) {\n\t\t\t\t\tthrow new Error(`Session manager returned a forked session file that does not exist and cannot be persisted by fallback: ${sessionFile}`);\n\t\t\t\t}\n\t\t\t\tif (forceThinkingOff(sanitizeUnsafeThinkingBlocks(entries))) {\n\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t}\n\t\t\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\t\t\tfs.writeFileSync(sessionFile, `${[header, ...entries].map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t} else {\n\t\t\t\tconst entries = readSessionEntries(sessionFile);\n\t\t\t\tif (sanitizeUnsafeThinkingBlocks(entries)) {\n\t\t\t\t\tif (forceThinkingOff(true)) {\n\t\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t\t}\n\t\t\t\t\tfs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resolution = { sessionFile, ...(thinkingOverride ? { thinkingOverride } : {}) };\n\t\t\tcachedResolutions.set(index, resolution);\n\t\t\treturn resolution;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Failed to create forked subagent session: ${cause.message}`, { cause });\n\t\t}\n\t};\n\n\treturn {\n\t\tsessionFileForIndex(index = 0): string | undefined {\n\t\t\treturn resolveFork(index).sessionFile;\n\t\t},\n\t\tthinkingOverrideForIndex(index = 0): \"off\" | undefined {\n\t\t\treturn resolveFork(index).thinkingOverride;\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fork-context.d.ts","sourceRoot":"","sources":["../../../../../src/builtin-extensions/pi-subagents/src/shared/fork-context.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEhE,KAAK,wBAAwB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,UAAU,kBAAkB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,oBAAoB;IAC7B,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,kBAAkB,EAAE,CAAC;CACxC;AAED,UAAU,sBAAsB;IAC/B,cAAc,IAAI,MAAM,GAAG,SAAS,CAAC;IACrC,SAAS,IAAI,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,IAAI,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,oBAAoB,CAAC;CAC1E;AAED,UAAU,0BAA0B;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,oBAAoB,CAAC;IAC1E;2FACuF;IACvF,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACtD;AAOD,UAAU,mBAAmB;IAC5B,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxD,wBAAwB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAE/E;AAED;wFACwF;AACxF,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,eAAe,CAAC,EAAE,SAAS,EAAE,EAC7B,iBAAiB,CAAC,EAAE,MAAM,GACxB,OAAO,CAMT;AA4DD,wBAAgB,yBAAyB,CACxC,cAAc,EAAE,sBAAsB,EACtC,gBAAgB,EAAE,OAAO,EACzB,OAAO,GAAE,0BAA+B,GACtC,mBAAmB,CA8ErB","sourcesContent":["// @ts-nocheck\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { SessionManager } from \"../../../../core/session-manager.ts\";\nimport { findModelInfo, type ModelInfo } from \"./model-info.ts\";\n\ntype SubagentExecutionContext = \"fresh\" | \"fork\";\n\ninterface BranchSessionEntry {\n\ttype: string;\n\tid?: string;\n\tparentId?: string | null;\n\ttimestamp?: string;\n\tmessage?: {\n\t\trole?: string;\n\t\tcontent?: unknown;\n\t\tprovider?: string;\n\t\tapi?: string;\n\t\tmodel?: string;\n\t};\n\tthinkingLevel?: string;\n}\n\ninterface BranchSessionManager {\n\tcreateBranchedSession(leafId: string): string | undefined;\n\tgetHeader?: () => BranchSessionEntry | null;\n\tgetEntries?: () => BranchSessionEntry[];\n}\n\ninterface ForkableSessionManager {\n\tgetSessionFile(): string | undefined;\n\tgetLeafId(): string | null;\n\tgetSessionDir?(): string;\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n}\n\ninterface ForkContextResolverOptions {\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n\t/** Decide per child index whether a sanitized transcript must also disable the child's\n\t * thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */\n\tforceThinkingOffForIndex?: (index: number) => boolean;\n}\n\ninterface ForkContextResolution {\n\tsessionFile: string;\n\tthinkingOverride?: \"off\";\n}\n\ninterface ForkContextResolver {\n\tsessionFileForIndex(index?: number): string | undefined;\n\tthinkingOverrideForIndex(index?: number): \"off\" | undefined;\n}\n\nexport function resolveSubagentContext(value: unknown): SubagentExecutionContext {\n\treturn value === \"fork\" ? \"fork\" : \"fresh\";\n}\n\n/** Decide whether a resolved child model uses Anthropic's provider or message API, which\n * requires the sanitized fork to disable thinking. Unknown models stay conservative. */\nexport function forkedChildRequiresThinkingOff(\n\tmodel: string | undefined,\n\tavailableModels?: ModelInfo[],\n\tpreferredProvider?: string,\n): boolean {\n\tif (!model) return true;\n\tconst info = findModelInfo(model, availableModels, preferredProvider);\n\tif (!info) return true;\n\treturn info.provider.toLowerCase() === \"anthropic\"\n\t\t|| info.api?.toLowerCase() === \"anthropic-messages\";\n}\n\nfunction isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry[\"message\"], block: unknown): boolean {\n\tif (!message || !block || typeof block !== \"object\" || !(\"type\" in block)) return false;\n\tconst provider = typeof message.provider === \"string\" ? message.provider.toLowerCase() : \"\";\n\tconst api = typeof message.api === \"string\" ? message.api.toLowerCase() : \"\";\n\tconst model = typeof message.model === \"string\" ? message.model.toLowerCase() : \"\";\n\tconst isAnthropic = provider === \"anthropic\" || api === \"anthropic-messages\" || model.startsWith(\"anthropic/\");\n\tif (block.type === \"redacted_thinking\") return true;\n\tif (block.type !== \"thinking\" || !isAnthropic) return false;\n\tconst signature = \"thinkingSignature\" in block ? block.thinkingSignature : \"signature\" in block ? block.signature : undefined;\n\treturn block.redacted === true || (typeof signature === \"string\" && signature.length > 0);\n}\n\nfunction createEntryId(entries: BranchSessionEntry[]): string {\n\tconst ids = new Set(entries.map((entry) => entry.id).filter((id): id is string => typeof id === \"string\"));\n\tfor (let attempt = 0; attempt < 100; attempt++) {\n\t\tconst id = randomUUID().slice(0, 8);\n\t\tif (!ids.has(id)) return id;\n\t}\n\treturn randomUUID();\n}\n\nfunction appendThinkingOffEntry(entries: BranchSessionEntry[]): void {\n\tconst last = entries[entries.length - 1];\n\tif (last?.type === \"thinking_level_change\" && last.thinkingLevel === \"off\") return;\n\tconst parent = [...entries].reverse().find((entry) => typeof entry.id === \"string\");\n\tentries.push({\n\t\ttype: \"thinking_level_change\",\n\t\tid: createEntryId(entries),\n\t\tparentId: parent?.id ?? null,\n\t\ttimestamp: new Date().toISOString(),\n\t\tthinkingLevel: \"off\",\n\t});\n}\n\nfunction sanitizeUnsafeThinkingBlocks(entries: BranchSessionEntry[]): boolean {\n\tlet sanitized = false;\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\" || entry.message?.role !== \"assistant\" || !Array.isArray(entry.message.content)) continue;\n\t\tconst filtered = entry.message.content.filter((block) => !isUnsafeAnthropicThinkingBlock(entry.message, block));\n\t\tif (filtered.length === entry.message.content.length) continue;\n\t\tentry.message.content = filtered;\n\t\tsanitized = true;\n\t}\n\treturn sanitized;\n}\n\nfunction readSessionEntries(sessionFile: string): BranchSessionEntry[] {\n\tconst lines = fs.readFileSync(sessionFile, \"utf-8\").split(\"\\n\").filter((line) => line.trim().length > 0);\n\treturn lines.map((line, index) => {\n\t\ttry {\n\t\t\treturn JSON.parse(line) as BranchSessionEntry;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Unable to inspect forked session ${sessionFile}: invalid JSONL on line ${index + 1}: ${cause.message}`, { cause });\n\t\t}\n\t});\n}\n\nexport function createForkContextResolver(\n\tsessionManager: ForkableSessionManager,\n\trequestedContext: unknown,\n\toptions: ForkContextResolverOptions = {},\n): ForkContextResolver {\n\tif (resolveSubagentContext(requestedContext) !== \"fork\") {\n\t\treturn {\n\t\t\tsessionFileForIndex: () => undefined,\n\t\t\tthinkingOverrideForIndex: () => undefined,\n\t\t};\n\t}\n\n\tconst parentSessionFile = sessionManager.getSessionFile();\n\tif (!parentSessionFile) {\n\t\tthrow new Error(\"Forked subagent context requires a persisted parent session.\");\n\t}\n\n\tconst leafId = sessionManager.getLeafId();\n\tif (!leafId) {\n\t\tthrow new Error(\"Forked subagent context requires a current leaf to fork from.\");\n\t}\n\n\tconst openSession = options.openSession\n\t\t?? sessionManager.openSession\n\t\t?? ((file: string, dir?: string) => SessionManager.open(file, dir));\n\tconst sessionDir = sessionManager.getSessionDir?.();\n\tconst cachedResolutions = new Map<number, ForkContextResolution>();\n\n\tconst resolveFork = (index = 0): ForkContextResolution => {\n\t\tconst cached = cachedResolutions.get(index);\n\t\tif (cached) return cached;\n\t\ttry {\n\t\t\tif (!fs.existsSync(parentSessionFile)) {\n\t\t\t\tthrow new Error(`Parent session file does not exist: ${parentSessionFile}. Pi has not persisted enough history to fork yet.`);\n\t\t\t}\n\t\t\tconst sourceManager = openSession(parentSessionFile, sessionDir);\n\t\t\tconst sessionFile = sourceManager.createBranchedSession(leafId);\n\t\t\tif (!sessionFile) {\n\t\t\t\tthrow new Error(\"Session manager did not return a forked session file.\");\n\t\t\t}\n\t\t\tconst forceThinkingOff = (sanitized: boolean): boolean =>\n\t\t\t\tsanitized && (options.forceThinkingOffForIndex?.(index) ?? true);\n\t\t\tlet thinkingOverride: \"off\" | undefined;\n\t\t\tif (!fs.existsSync(sessionFile)) {\n\t\t\t\tconst header = sourceManager.getHeader?.();\n\t\t\t\tconst entries = sourceManager.getEntries?.();\n\t\t\t\tif (!header || !entries) {\n\t\t\t\t\tthrow new Error(`Session manager returned a forked session file that does not exist and cannot be persisted by fallback: ${sessionFile}`);\n\t\t\t\t}\n\t\t\t\tif (forceThinkingOff(sanitizeUnsafeThinkingBlocks(entries))) {\n\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t}\n\t\t\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\t\t\tfs.writeFileSync(sessionFile, `${[header, ...entries].map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t} else {\n\t\t\t\tconst entries = readSessionEntries(sessionFile);\n\t\t\t\tif (sanitizeUnsafeThinkingBlocks(entries)) {\n\t\t\t\t\tif (forceThinkingOff(true)) {\n\t\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t\t}\n\t\t\t\t\tfs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resolution = { sessionFile, ...(thinkingOverride ? { thinkingOverride } : {}) };\n\t\t\tcachedResolutions.set(index, resolution);\n\t\t\treturn resolution;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Failed to create forked subagent session: ${cause.message}`, { cause });\n\t\t}\n\t};\n\n\treturn {\n\t\tsessionFileForIndex(index = 0): string | undefined {\n\t\t\treturn resolveFork(index).sessionFile;\n\t\t},\n\t\tthinkingOverrideForIndex(index = 0): \"off\" | undefined {\n\t\t\treturn resolveFork(index).thinkingOverride;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import { SessionManager } from "
|
|
5
|
+
import { SessionManager } from "../../../../core/session-manager.js";
|
|
6
6
|
import { findModelInfo } from "./model-info.js";
|
|
7
7
|
export function resolveSubagentContext(value) {
|
|
8
8
|
return value === "fork" ? "fork" : "fresh";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fork-context.js","sourceRoot":"","sources":["../../../../../src/builtin-extensions/pi-subagents/src/shared/fork-context.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAkB,MAAM,iBAAiB,CAAC;AAiDhE,MAAM,UAAU,sBAAsB,CAAC,KAAc,EAA4B;IAChF,OAAO,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AAAA,CAC3C;AAED;wFACwF;AACxF,MAAM,UAAU,8BAA8B,CAC7C,KAAyB,EACzB,eAA6B,EAC7B,iBAA0B,EAChB;IACV,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,iBAAiB,CAAC,CAAC;IACtE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,WAAW;WAC9C,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,oBAAoB,CAAC;AAAA,CACrD;AAED,SAAS,8BAA8B,CAAC,OAAsC,EAAE,KAAc,EAAW;IACxG,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxF,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5F,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,MAAM,KAAK,GAAG,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnF,MAAM,WAAW,GAAG,QAAQ,KAAK,WAAW,IAAI,GAAG,KAAK,oBAAoB,IAAI,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAC/G,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,SAAS,GAAG,mBAAmB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9H,OAAO,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA,CAC1F;AAED,SAAS,aAAa,CAAC,OAA6B,EAAU;IAC7D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC;IAC3G,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC;QAChD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,UAAU,EAAE,CAAC;AAAA,CACpB;AAED,SAAS,sBAAsB,CAAC,OAA6B,EAAQ;IACpE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,IAAI,EAAE,IAAI,KAAK,uBAAuB,IAAI,IAAI,CAAC,aAAa,KAAK,KAAK;QAAE,OAAO;IACnF,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IACpF,OAAO,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,uBAAuB;QAC7B,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC;QAC1B,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,IAAI;QAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,aAAa,EAAE,KAAK;KACpB,CAAC,CAAC;AAAA,CACH;AAED,SAAS,4BAA4B,CAAC,OAA6B,EAAW;IAC7E,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,SAAS;QACvH,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,8BAA8B,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAChH,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM;YAAE,SAAS;QAC/D,KAAK,CAAC,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAC;QACjC,SAAS,GAAG,IAAI,CAAC;IAClB,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,SAAS,kBAAkB,CAAC,WAAmB,EAAwB;IACtE,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzG,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjC,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAuB,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,MAAM,IAAI,KAAK,CAAC,oCAAoC,WAAW,2BAA2B,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACrI,CAAC;IAAA,CACD,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,yBAAyB,CACxC,cAAsC,EACtC,gBAAyB,EACzB,OAAO,GAA+B,EAAE,EAClB;IACtB,IAAI,sBAAsB,CAAC,gBAAgB,CAAC,KAAK,MAAM,EAAE,CAAC;QACzD,OAAO;YACN,mBAAmB,EAAE,GAAG,EAAE,CAAC,SAAS;YACpC,wBAAwB,EAAE,GAAG,EAAE,CAAC,SAAS;SACzC,CAAC;IACH,CAAC;IAED,MAAM,iBAAiB,GAAG,cAAc,CAAC,cAAc,EAAE,CAAC;IAC1D,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW;WACnC,cAAc,CAAC,WAAW;WAC1B,CAAC,CAAC,IAAY,EAAE,GAAY,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,cAAc,CAAC,aAAa,EAAE,EAAE,CAAC;IACpD,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEnE,MAAM,WAAW,GAAG,CAAC,KAAK,GAAG,CAAC,EAAyB,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,IAAI,CAAC;YACJ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,uCAAuC,iBAAiB,oDAAoD,CAAC,CAAC;YAC/H,CAAC;YACD,MAAM,aAAa,GAAG,WAAW,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;YACjE,MAAM,WAAW,GAAG,aAAa,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;YAChE,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,gBAAgB,GAAG,CAAC,SAAkB,EAAW,EAAE,CACxD,SAAS,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;YAClE,IAAI,gBAAmC,CAAC;YACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC3C,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,EAAE,CAAC;gBAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;oBACzB,MAAM,IAAI,KAAK,CAAC,2GAA2G,WAAW,EAAE,CAAC,CAAC;gBAC3I,CAAC;gBACD,IAAI,gBAAgB,CAAC,4BAA4B,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;oBAC7D,sBAAsB,CAAC,OAAO,CAAC,CAAC;oBAChC,gBAAgB,GAAG,KAAK,CAAC;gBAC1B,CAAC;gBACD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC7D,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACtH,CAAC;iBAAM,CAAC;gBACP,MAAM,OAAO,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;gBAChD,IAAI,4BAA4B,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3C,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC5B,sBAAsB,CAAC,OAAO,CAAC,CAAC;wBAChC,gBAAgB,GAAG,KAAK,CAAC;oBAC1B,CAAC;oBACD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACzG,CAAC;YACF,CAAC;YACD,MAAM,UAAU,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YACtF,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YACzC,OAAO,UAAU,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,MAAM,IAAI,KAAK,CAAC,6CAA6C,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1F,CAAC;IAAA,CACD,CAAC;IAEF,OAAO;QACN,mBAAmB,CAAC,KAAK,GAAG,CAAC,EAAsB;YAClD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC;QAAA,CACtC;QACD,wBAAwB,CAAC,KAAK,GAAG,CAAC,EAAqB;YACtD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC;QAAA,CAC3C;KACD,CAAC;AAAA,CACF","sourcesContent":["// @ts-nocheck\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { SessionManager } from \"@ashx-j/lunr-dev\";\nimport { findModelInfo, type ModelInfo } from \"./model-info.ts\";\n\ntype SubagentExecutionContext = \"fresh\" | \"fork\";\n\ninterface BranchSessionEntry {\n\ttype: string;\n\tid?: string;\n\tparentId?: string | null;\n\ttimestamp?: string;\n\tmessage?: {\n\t\trole?: string;\n\t\tcontent?: unknown;\n\t\tprovider?: string;\n\t\tapi?: string;\n\t\tmodel?: string;\n\t};\n\tthinkingLevel?: string;\n}\n\ninterface BranchSessionManager {\n\tcreateBranchedSession(leafId: string): string | undefined;\n\tgetHeader?: () => BranchSessionEntry | null;\n\tgetEntries?: () => BranchSessionEntry[];\n}\n\ninterface ForkableSessionManager {\n\tgetSessionFile(): string | undefined;\n\tgetLeafId(): string | null;\n\tgetSessionDir?(): string;\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n}\n\ninterface ForkContextResolverOptions {\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n\t/** Decide per child index whether a sanitized transcript must also disable the child's\n\t * thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */\n\tforceThinkingOffForIndex?: (index: number) => boolean;\n}\n\ninterface ForkContextResolution {\n\tsessionFile: string;\n\tthinkingOverride?: \"off\";\n}\n\ninterface ForkContextResolver {\n\tsessionFileForIndex(index?: number): string | undefined;\n\tthinkingOverrideForIndex(index?: number): \"off\" | undefined;\n}\n\nexport function resolveSubagentContext(value: unknown): SubagentExecutionContext {\n\treturn value === \"fork\" ? \"fork\" : \"fresh\";\n}\n\n/** Decide whether a resolved child model uses Anthropic's provider or message API, which\n * requires the sanitized fork to disable thinking. Unknown models stay conservative. */\nexport function forkedChildRequiresThinkingOff(\n\tmodel: string | undefined,\n\tavailableModels?: ModelInfo[],\n\tpreferredProvider?: string,\n): boolean {\n\tif (!model) return true;\n\tconst info = findModelInfo(model, availableModels, preferredProvider);\n\tif (!info) return true;\n\treturn info.provider.toLowerCase() === \"anthropic\"\n\t\t|| info.api?.toLowerCase() === \"anthropic-messages\";\n}\n\nfunction isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry[\"message\"], block: unknown): boolean {\n\tif (!message || !block || typeof block !== \"object\" || !(\"type\" in block)) return false;\n\tconst provider = typeof message.provider === \"string\" ? message.provider.toLowerCase() : \"\";\n\tconst api = typeof message.api === \"string\" ? message.api.toLowerCase() : \"\";\n\tconst model = typeof message.model === \"string\" ? message.model.toLowerCase() : \"\";\n\tconst isAnthropic = provider === \"anthropic\" || api === \"anthropic-messages\" || model.startsWith(\"anthropic/\");\n\tif (block.type === \"redacted_thinking\") return true;\n\tif (block.type !== \"thinking\" || !isAnthropic) return false;\n\tconst signature = \"thinkingSignature\" in block ? block.thinkingSignature : \"signature\" in block ? block.signature : undefined;\n\treturn block.redacted === true || (typeof signature === \"string\" && signature.length > 0);\n}\n\nfunction createEntryId(entries: BranchSessionEntry[]): string {\n\tconst ids = new Set(entries.map((entry) => entry.id).filter((id): id is string => typeof id === \"string\"));\n\tfor (let attempt = 0; attempt < 100; attempt++) {\n\t\tconst id = randomUUID().slice(0, 8);\n\t\tif (!ids.has(id)) return id;\n\t}\n\treturn randomUUID();\n}\n\nfunction appendThinkingOffEntry(entries: BranchSessionEntry[]): void {\n\tconst last = entries[entries.length - 1];\n\tif (last?.type === \"thinking_level_change\" && last.thinkingLevel === \"off\") return;\n\tconst parent = [...entries].reverse().find((entry) => typeof entry.id === \"string\");\n\tentries.push({\n\t\ttype: \"thinking_level_change\",\n\t\tid: createEntryId(entries),\n\t\tparentId: parent?.id ?? null,\n\t\ttimestamp: new Date().toISOString(),\n\t\tthinkingLevel: \"off\",\n\t});\n}\n\nfunction sanitizeUnsafeThinkingBlocks(entries: BranchSessionEntry[]): boolean {\n\tlet sanitized = false;\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\" || entry.message?.role !== \"assistant\" || !Array.isArray(entry.message.content)) continue;\n\t\tconst filtered = entry.message.content.filter((block) => !isUnsafeAnthropicThinkingBlock(entry.message, block));\n\t\tif (filtered.length === entry.message.content.length) continue;\n\t\tentry.message.content = filtered;\n\t\tsanitized = true;\n\t}\n\treturn sanitized;\n}\n\nfunction readSessionEntries(sessionFile: string): BranchSessionEntry[] {\n\tconst lines = fs.readFileSync(sessionFile, \"utf-8\").split(\"\\n\").filter((line) => line.trim().length > 0);\n\treturn lines.map((line, index) => {\n\t\ttry {\n\t\t\treturn JSON.parse(line) as BranchSessionEntry;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Unable to inspect forked session ${sessionFile}: invalid JSONL on line ${index + 1}: ${cause.message}`, { cause });\n\t\t}\n\t});\n}\n\nexport function createForkContextResolver(\n\tsessionManager: ForkableSessionManager,\n\trequestedContext: unknown,\n\toptions: ForkContextResolverOptions = {},\n): ForkContextResolver {\n\tif (resolveSubagentContext(requestedContext) !== \"fork\") {\n\t\treturn {\n\t\t\tsessionFileForIndex: () => undefined,\n\t\t\tthinkingOverrideForIndex: () => undefined,\n\t\t};\n\t}\n\n\tconst parentSessionFile = sessionManager.getSessionFile();\n\tif (!parentSessionFile) {\n\t\tthrow new Error(\"Forked subagent context requires a persisted parent session.\");\n\t}\n\n\tconst leafId = sessionManager.getLeafId();\n\tif (!leafId) {\n\t\tthrow new Error(\"Forked subagent context requires a current leaf to fork from.\");\n\t}\n\n\tconst openSession = options.openSession\n\t\t?? sessionManager.openSession\n\t\t?? ((file: string, dir?: string) => SessionManager.open(file, dir));\n\tconst sessionDir = sessionManager.getSessionDir?.();\n\tconst cachedResolutions = new Map<number, ForkContextResolution>();\n\n\tconst resolveFork = (index = 0): ForkContextResolution => {\n\t\tconst cached = cachedResolutions.get(index);\n\t\tif (cached) return cached;\n\t\ttry {\n\t\t\tif (!fs.existsSync(parentSessionFile)) {\n\t\t\t\tthrow new Error(`Parent session file does not exist: ${parentSessionFile}. Pi has not persisted enough history to fork yet.`);\n\t\t\t}\n\t\t\tconst sourceManager = openSession(parentSessionFile, sessionDir);\n\t\t\tconst sessionFile = sourceManager.createBranchedSession(leafId);\n\t\t\tif (!sessionFile) {\n\t\t\t\tthrow new Error(\"Session manager did not return a forked session file.\");\n\t\t\t}\n\t\t\tconst forceThinkingOff = (sanitized: boolean): boolean =>\n\t\t\t\tsanitized && (options.forceThinkingOffForIndex?.(index) ?? true);\n\t\t\tlet thinkingOverride: \"off\" | undefined;\n\t\t\tif (!fs.existsSync(sessionFile)) {\n\t\t\t\tconst header = sourceManager.getHeader?.();\n\t\t\t\tconst entries = sourceManager.getEntries?.();\n\t\t\t\tif (!header || !entries) {\n\t\t\t\t\tthrow new Error(`Session manager returned a forked session file that does not exist and cannot be persisted by fallback: ${sessionFile}`);\n\t\t\t\t}\n\t\t\t\tif (forceThinkingOff(sanitizeUnsafeThinkingBlocks(entries))) {\n\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t}\n\t\t\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\t\t\tfs.writeFileSync(sessionFile, `${[header, ...entries].map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t} else {\n\t\t\t\tconst entries = readSessionEntries(sessionFile);\n\t\t\t\tif (sanitizeUnsafeThinkingBlocks(entries)) {\n\t\t\t\t\tif (forceThinkingOff(true)) {\n\t\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t\t}\n\t\t\t\t\tfs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resolution = { sessionFile, ...(thinkingOverride ? { thinkingOverride } : {}) };\n\t\t\tcachedResolutions.set(index, resolution);\n\t\t\treturn resolution;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Failed to create forked subagent session: ${cause.message}`, { cause });\n\t\t}\n\t};\n\n\treturn {\n\t\tsessionFileForIndex(index = 0): string | undefined {\n\t\t\treturn resolveFork(index).sessionFile;\n\t\t},\n\t\tthinkingOverrideForIndex(index = 0): \"off\" | undefined {\n\t\t\treturn resolveFork(index).thinkingOverride;\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fork-context.js","sourceRoot":"","sources":["../../../../../src/builtin-extensions/pi-subagents/src/shared/fork-context.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { SessionManager } from \"../../../../core/session-manager.ts\";\nimport { findModelInfo, type ModelInfo } from \"./model-info.ts\";\n\ntype SubagentExecutionContext = \"fresh\" | \"fork\";\n\ninterface BranchSessionEntry {\n\ttype: string;\n\tid?: string;\n\tparentId?: string | null;\n\ttimestamp?: string;\n\tmessage?: {\n\t\trole?: string;\n\t\tcontent?: unknown;\n\t\tprovider?: string;\n\t\tapi?: string;\n\t\tmodel?: string;\n\t};\n\tthinkingLevel?: string;\n}\n\ninterface BranchSessionManager {\n\tcreateBranchedSession(leafId: string): string | undefined;\n\tgetHeader?: () => BranchSessionEntry | null;\n\tgetEntries?: () => BranchSessionEntry[];\n}\n\ninterface ForkableSessionManager {\n\tgetSessionFile(): string | undefined;\n\tgetLeafId(): string | null;\n\tgetSessionDir?(): string;\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n}\n\ninterface ForkContextResolverOptions {\n\topenSession?: (path: string, sessionDir?: string) => BranchSessionManager;\n\t/** Decide per child index whether a sanitized transcript must also disable the child's\n\t * thinking. Defaults to true (the pre-existing conservative behavior) when omitted. */\n\tforceThinkingOffForIndex?: (index: number) => boolean;\n}\n\ninterface ForkContextResolution {\n\tsessionFile: string;\n\tthinkingOverride?: \"off\";\n}\n\ninterface ForkContextResolver {\n\tsessionFileForIndex(index?: number): string | undefined;\n\tthinkingOverrideForIndex(index?: number): \"off\" | undefined;\n}\n\nexport function resolveSubagentContext(value: unknown): SubagentExecutionContext {\n\treturn value === \"fork\" ? \"fork\" : \"fresh\";\n}\n\n/** Decide whether a resolved child model uses Anthropic's provider or message API, which\n * requires the sanitized fork to disable thinking. Unknown models stay conservative. */\nexport function forkedChildRequiresThinkingOff(\n\tmodel: string | undefined,\n\tavailableModels?: ModelInfo[],\n\tpreferredProvider?: string,\n): boolean {\n\tif (!model) return true;\n\tconst info = findModelInfo(model, availableModels, preferredProvider);\n\tif (!info) return true;\n\treturn info.provider.toLowerCase() === \"anthropic\"\n\t\t|| info.api?.toLowerCase() === \"anthropic-messages\";\n}\n\nfunction isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry[\"message\"], block: unknown): boolean {\n\tif (!message || !block || typeof block !== \"object\" || !(\"type\" in block)) return false;\n\tconst provider = typeof message.provider === \"string\" ? message.provider.toLowerCase() : \"\";\n\tconst api = typeof message.api === \"string\" ? message.api.toLowerCase() : \"\";\n\tconst model = typeof message.model === \"string\" ? message.model.toLowerCase() : \"\";\n\tconst isAnthropic = provider === \"anthropic\" || api === \"anthropic-messages\" || model.startsWith(\"anthropic/\");\n\tif (block.type === \"redacted_thinking\") return true;\n\tif (block.type !== \"thinking\" || !isAnthropic) return false;\n\tconst signature = \"thinkingSignature\" in block ? block.thinkingSignature : \"signature\" in block ? block.signature : undefined;\n\treturn block.redacted === true || (typeof signature === \"string\" && signature.length > 0);\n}\n\nfunction createEntryId(entries: BranchSessionEntry[]): string {\n\tconst ids = new Set(entries.map((entry) => entry.id).filter((id): id is string => typeof id === \"string\"));\n\tfor (let attempt = 0; attempt < 100; attempt++) {\n\t\tconst id = randomUUID().slice(0, 8);\n\t\tif (!ids.has(id)) return id;\n\t}\n\treturn randomUUID();\n}\n\nfunction appendThinkingOffEntry(entries: BranchSessionEntry[]): void {\n\tconst last = entries[entries.length - 1];\n\tif (last?.type === \"thinking_level_change\" && last.thinkingLevel === \"off\") return;\n\tconst parent = [...entries].reverse().find((entry) => typeof entry.id === \"string\");\n\tentries.push({\n\t\ttype: \"thinking_level_change\",\n\t\tid: createEntryId(entries),\n\t\tparentId: parent?.id ?? null,\n\t\ttimestamp: new Date().toISOString(),\n\t\tthinkingLevel: \"off\",\n\t});\n}\n\nfunction sanitizeUnsafeThinkingBlocks(entries: BranchSessionEntry[]): boolean {\n\tlet sanitized = false;\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\" || entry.message?.role !== \"assistant\" || !Array.isArray(entry.message.content)) continue;\n\t\tconst filtered = entry.message.content.filter((block) => !isUnsafeAnthropicThinkingBlock(entry.message, block));\n\t\tif (filtered.length === entry.message.content.length) continue;\n\t\tentry.message.content = filtered;\n\t\tsanitized = true;\n\t}\n\treturn sanitized;\n}\n\nfunction readSessionEntries(sessionFile: string): BranchSessionEntry[] {\n\tconst lines = fs.readFileSync(sessionFile, \"utf-8\").split(\"\\n\").filter((line) => line.trim().length > 0);\n\treturn lines.map((line, index) => {\n\t\ttry {\n\t\t\treturn JSON.parse(line) as BranchSessionEntry;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Unable to inspect forked session ${sessionFile}: invalid JSONL on line ${index + 1}: ${cause.message}`, { cause });\n\t\t}\n\t});\n}\n\nexport function createForkContextResolver(\n\tsessionManager: ForkableSessionManager,\n\trequestedContext: unknown,\n\toptions: ForkContextResolverOptions = {},\n): ForkContextResolver {\n\tif (resolveSubagentContext(requestedContext) !== \"fork\") {\n\t\treturn {\n\t\t\tsessionFileForIndex: () => undefined,\n\t\t\tthinkingOverrideForIndex: () => undefined,\n\t\t};\n\t}\n\n\tconst parentSessionFile = sessionManager.getSessionFile();\n\tif (!parentSessionFile) {\n\t\tthrow new Error(\"Forked subagent context requires a persisted parent session.\");\n\t}\n\n\tconst leafId = sessionManager.getLeafId();\n\tif (!leafId) {\n\t\tthrow new Error(\"Forked subagent context requires a current leaf to fork from.\");\n\t}\n\n\tconst openSession = options.openSession\n\t\t?? sessionManager.openSession\n\t\t?? ((file: string, dir?: string) => SessionManager.open(file, dir));\n\tconst sessionDir = sessionManager.getSessionDir?.();\n\tconst cachedResolutions = new Map<number, ForkContextResolution>();\n\n\tconst resolveFork = (index = 0): ForkContextResolution => {\n\t\tconst cached = cachedResolutions.get(index);\n\t\tif (cached) return cached;\n\t\ttry {\n\t\t\tif (!fs.existsSync(parentSessionFile)) {\n\t\t\t\tthrow new Error(`Parent session file does not exist: ${parentSessionFile}. Pi has not persisted enough history to fork yet.`);\n\t\t\t}\n\t\t\tconst sourceManager = openSession(parentSessionFile, sessionDir);\n\t\t\tconst sessionFile = sourceManager.createBranchedSession(leafId);\n\t\t\tif (!sessionFile) {\n\t\t\t\tthrow new Error(\"Session manager did not return a forked session file.\");\n\t\t\t}\n\t\t\tconst forceThinkingOff = (sanitized: boolean): boolean =>\n\t\t\t\tsanitized && (options.forceThinkingOffForIndex?.(index) ?? true);\n\t\t\tlet thinkingOverride: \"off\" | undefined;\n\t\t\tif (!fs.existsSync(sessionFile)) {\n\t\t\t\tconst header = sourceManager.getHeader?.();\n\t\t\t\tconst entries = sourceManager.getEntries?.();\n\t\t\t\tif (!header || !entries) {\n\t\t\t\t\tthrow new Error(`Session manager returned a forked session file that does not exist and cannot be persisted by fallback: ${sessionFile}`);\n\t\t\t\t}\n\t\t\t\tif (forceThinkingOff(sanitizeUnsafeThinkingBlocks(entries))) {\n\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t}\n\t\t\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\t\t\tfs.writeFileSync(sessionFile, `${[header, ...entries].map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t} else {\n\t\t\t\tconst entries = readSessionEntries(sessionFile);\n\t\t\t\tif (sanitizeUnsafeThinkingBlocks(entries)) {\n\t\t\t\t\tif (forceThinkingOff(true)) {\n\t\t\t\t\t\tappendThinkingOffEntry(entries);\n\t\t\t\t\t\tthinkingOverride = \"off\";\n\t\t\t\t\t}\n\t\t\t\t\tfs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`, \"utf-8\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resolution = { sessionFile, ...(thinkingOverride ? { thinkingOverride } : {}) };\n\t\t\tcachedResolutions.set(index, resolution);\n\t\t\treturn resolution;\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthrow new Error(`Failed to create forked subagent session: ${cause.message}`, { cause });\n\t\t}\n\t};\n\n\treturn {\n\t\tsessionFileForIndex(index = 0): string | undefined {\n\t\t\treturn resolveFork(index).sessionFile;\n\t\t},\n\t\tthinkingOverrideForIndex(index = 0): \"off\" | undefined {\n\t\t\treturn resolveFork(index).thinkingOverride;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@ashx-j/lunr-dev";
|
|
2
2
|
import type { ChainStep } from "../shared/settings.ts";
|
|
3
3
|
import { type SubagentState } from "../shared/types.ts";
|
|
4
4
|
interface InlineConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../../../../src/builtin-extensions/pi-subagents/src/slash/slash-commands.ts"],"names":[],"mappings":"AAIA,OAAO,EAAW,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGpG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAiBvD,OAAO,EAYN,KAAK,aAAa,EAElB,MAAM,oBAAoB,CAAC;AAE5B,UAAU,YAAY;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,UAAU,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AA4fD,MAAM,WAAW,WAAW;IAAG,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AAC7F,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC/F,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE;AACxF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,CAAC;AAEvD,eAAO,MAAM,oBAAoB,kFACyC,CAAC;AAE3E,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AA4D7C,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAiB9D;AA0CD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAW9D;AAOD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,CAyBhF;AAqID,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,gBAAgB,GACnB;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAoE7C;AAED,wBAAgB,qBAAqB,CACpC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,aAAa,GAClB,IAAI,CAyJN","sourcesContent":["// @ts-nocheck\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { keyText, type ExtensionAPI, type ExtensionContext } from \"@ashx-j/lunr-dev\";\nimport { Key, matchesKey, type Component, type TUI } from \"@ashx-j/lunr-tui\";\nimport type { SubagentParamsLike } from \"../runs/foreground/subagent-executor.ts\";\nimport type { ChainStep } from \"../shared/settings.ts\";\nimport { formatTokens, shortenPath } from \"../shared/formatters.ts\";\nimport { listAsyncRuns, formatAsyncRunProgressLabel, type AsyncRunSummary } from \"../runs/background/async-status.ts\";\nimport { scheduledRunStorePath } from \"../runs/background/scheduled-runs.ts\";\nimport { SUBAGENT_FANOUT_CHILD_ENV } from \"../runs/shared/pi-args.ts\";\nimport { assertJsonSchemaObject } from \"../runs/shared/structured-output.ts\";\nimport { validateAcceptanceInput } from \"../runs/shared/acceptance.ts\";\nimport type { SlashSubagentResponse, SlashSubagentUpdate } from \"./slash-bridge.ts\";\nimport { registerPromptWorkflowCommands } from \"./prompt-workflows.ts\";\nimport { openSubagentFleet } from \"../tui/fleet.ts\";\nimport {\n\tapplySlashUpdate,\n\tbuildSlashInitialResult,\n\tfailSlashResult,\n\tfinalizeSlashResult,\n\tresolveSlashMessageDetails,\n} from \"./slash-live-state.ts\";\nimport {\n\tSLASH_RESULT_TYPE,\n\tSLASH_TEXT_RESULT_TYPE,\n\tSLASH_SUBAGENT_CANCEL_EVENT,\n\tSLASH_SUBAGENT_REQUEST_EVENT,\n\tSLASH_SUBAGENT_RESPONSE_EVENT,\n\tSLASH_SUBAGENT_STARTED_EVENT,\n\tSLASH_SUBAGENT_UPDATE_EVENT,\n\tASYNC_DIR,\n\ttype Details,\n\ttype JsonSchemaObject,\n\ttype SingleResult,\n\ttype SubagentState,\n\ttype Usage,\n} from \"../shared/types.ts\";\n\ninterface InlineConfig {\n\toutput?: string | false;\n\toutputMode?: \"inline\" | \"file-only\";\n\treads?: string[] | false;\n\ttier?: \"light\" | \"standard\" | \"heavy\";\n\tpermissions?: \"full\" | \"read-only\";\n\tskill?: string[] | false;\n\tprogress?: boolean;\n\tas?: string;\n\tlabel?: string;\n\tphase?: string;\n\tcwd?: string;\n\tcount?: number;\n\toutputSchema?: string;\n\tacceptance?: string;\n}\n\nconst parseInlineConfig = (raw: string): InlineConfig => {\n\tconst config: InlineConfig = {};\n\tfor (const part of raw.split(\",\")) {\n\t\tconst trimmed = part.trim();\n\t\tif (!trimmed) continue;\n\t\tconst eq = trimmed.indexOf(\"=\");\n\t\tif (eq === -1) {\n\t\t\tif (trimmed === \"progress\") config.progress = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = trimmed.slice(0, eq).trim();\n\t\tconst val = trimmed.slice(eq + 1).trim();\n\t\tswitch (key) {\n\t\t\tcase \"output\": config.output = val === \"false\" ? false : val; break;\n\t\t\tcase \"outputMode\": if (val === \"inline\" || val === \"file-only\") config.outputMode = val; break;\n\t\t\tcase \"reads\": config.reads = val === \"false\" ? false : val.split(\"+\").filter(Boolean); break;\n\t\t\tcase \"tier\": if (val === \"light\" || val === \"standard\" || val === \"heavy\") config.tier = val; break;\n\t\t\tcase \"permissions\": if (val === \"full\" || val === \"read-only\") config.permissions = val; break;\n\t\t\tcase \"skill\": case \"skills\": config.skill = val === \"false\" ? false : val.split(\"+\").filter(Boolean); break;\n\t\t\tcase \"progress\": config.progress = val !== \"false\"; break;\n\t\t\tcase \"as\": config.as = val || undefined; break;\n\t\t\tcase \"label\": config.label = val || undefined; break;\n\t\t\tcase \"phase\": config.phase = val || undefined; break;\n\t\t\tcase \"cwd\": config.cwd = val || undefined; break;\n\t\t\tcase \"count\": { const n = Number(val); if (Number.isInteger(n) && n > 0) config.count = n; break; }\n\t\t\tcase \"outputSchema\": config.outputSchema = val || undefined; break;\n\t\t\tcase \"acceptance\": config.acceptance = val || undefined; break;\n\t\t}\n\t}\n\treturn config;\n};\n\nconst parseAgentToken = (token: string): { name: string; config: InlineConfig } => {\n\tconst bracket = token.indexOf(\"[\");\n\tif (bracket === -1) return { name: token, config: {} };\n\tconst end = token.lastIndexOf(\"]\");\n\treturn { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : undefined)) };\n};\n\nconst extractExecutionFlags = (rawArgs: string): { args: string; bg: boolean } => {\n\tlet args = rawArgs.trim();\n\tlet bg = false;\n\n\twhile (true) {\n\t\tif (args.endsWith(\" --bg\") || args === \"--bg\") {\n\t\t\tbg = true;\n\t\t\targs = args === \"--bg\" ? \"\" : args.slice(0, -5).trim();\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn { args, bg };\n};\n\nfunction sendSlashText(pi: ExtensionAPI, text: string): void {\n\tpi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: true });\n}\n\ntype Theme = ExtensionContext[\"ui\"][\"theme\"];\n\ntype StopSelectorTarget = {\n\tkind: \"async\" | \"scheduled\";\n\tid: string;\n\tlabel: string;\n\tdetail: string;\n\tactionLabel: string;\n};\n\ntype StopSelectorResult = { confirmed: boolean; target?: StopSelectorTarget };\n\nfunction commandForTarget(target: StopSelectorTarget): string {\n\treturn target.kind === \"scheduled\"\n\t\t? `subagent({ action: \"schedule-cancel\", id: ${JSON.stringify(target.id)} })`\n\t\t: `subagent({ action: \"stop\", id: ${JSON.stringify(target.id)} })`;\n}\n\nfunction formatAsyncStopTarget(run: AsyncRunSummary): StopSelectorTarget {\n\tconst progress = formatAsyncRunProgressLabel(run);\n\tconst cwd = run.cwd ? shortenPath(run.cwd) : shortenPath(run.asyncDir);\n\treturn {\n\t\tkind: \"async\",\n\t\tid: run.id,\n\t\tlabel: `${run.id} · ${run.mode} · ${progress}`,\n\t\tdetail: `${run.state} · ${cwd}`,\n\t\tactionLabel: \"stop async run\",\n\t};\n}\n\nfunction scheduledStopTargets(ctx: ExtensionContext, state: SubagentState): StopSelectorTarget[] {\n\tconst sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId();\n\tif (!sessionId) return [];\n\tconst storePath = scheduledRunStorePath(ctx.cwd, sessionId);\n\tif (!fs.existsSync(storePath)) return [];\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(fs.readFileSync(storePath, \"utf-8\"));\n\t} catch {\n\t\treturn [];\n\t}\n\tconst jobs = parsed && typeof parsed === \"object\" && Array.isArray((parsed as { jobs?: unknown }).jobs)\n\t\t? (parsed as { jobs: Array<Record<string, unknown>> }).jobs\n\t\t: [];\n\treturn jobs\n\t\t.filter((job) => job.state === \"scheduled\" && typeof job.id === \"string\" && typeof job.name === \"string\" && typeof job.runAt === \"number\")\n\t\t.sort((left, right) => Number(left.runAt) - Number(right.runAt))\n\t\t.map((job) => ({\n\t\t\tkind: \"scheduled\" as const,\n\t\t\tid: String(job.id),\n\t\t\tlabel: `${String(job.id)} · ${String(job.name)}`,\n\t\t\tdetail: `scheduled · ${new Date(Number(job.runAt)).toISOString()}`,\n\t\t\tactionLabel: \"cancel scheduled run\",\n\t\t}));\n}\n\nfunction discoverStopTargets(ctx: ExtensionContext, state: SubagentState): StopSelectorTarget[] {\n\tconst sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined;\n\tconst asyncTargets = listAsyncRuns(ASYNC_DIR, {\n\t\tstates: [\"queued\", \"running\"],\n\t\t...(sessionId ? { sessionId } : {}),\n\t}).map(formatAsyncStopTarget);\n\treturn [...asyncTargets, ...scheduledStopTargets(ctx, state)];\n}\n\nfunction stopFallbackText(targets: StopSelectorTarget[]): string {\n\tif (targets.length === 0) return \"No active current-session async runs or scheduled subagent runs to stop.\";\n\tconst lines = [\"Subagent stop targets:\", \"\"];\n\tfor (const target of targets) {\n\t\tlines.push(`- ${target.label}`);\n\t\tlines.push(` ${target.detail}`);\n\t\tlines.push(` ${target.actionLabel}: ${commandForTarget(target)}`);\n\t\tif (target.kind === \"async\") lines.push(` slash: /subagents-stop ${target.id}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nclass SubagentsStopSelector implements Component {\n\treadonly width = 84;\n\tprivate selected = 0;\n\tprivate confirming = false;\n\tprivate readonly tui: TUI;\n\tprivate readonly theme: Theme;\n\tprivate readonly targets: StopSelectorTarget[];\n\tprivate readonly done: (result: StopSelectorResult) => void;\n\n\tconstructor(tui: TUI, theme: Theme, targets: StopSelectorTarget[], done: (result: StopSelectorResult) => void) {\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.targets = targets;\n\t\tthis.done = done;\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (matchesKey(data, \"escape\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.done({ confirmed: false });\n\t\t\treturn;\n\t\t}\n\t\tif (this.confirming) {\n\t\t\tif (matchesKey(data, \"return\") || data.toLowerCase() === \"y\") {\n\t\t\t\tthis.done({ confirmed: true, target: this.targets[this.selected] });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (data.toLowerCase() === \"n\" || matchesKey(data, \"backspace\")) {\n\t\t\t\tthis.confirming = false;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"up\")) {\n\t\t\tthis.selected = Math.max(0, this.selected - 1);\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"down\")) {\n\t\t\tthis.selected = Math.min(this.targets.length - 1, this.selected + 1);\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"return\")) {\n\t\t\tthis.confirming = true;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst contentWidth = Math.max(40, Math.min(this.width, width || this.width));\n\t\tconst lines = [this.theme.bold(\"Stop subagent run\"), this.theme.fg(\"dim\", \"Select a current-session async run to stop, or a scheduled run to cancel.\"), \"\"];\n\t\tconst maxRows = 10;\n\t\tconst start = Math.max(0, Math.min(this.selected - maxRows + 1, Math.max(0, this.targets.length - maxRows)));\n\t\tfor (let index = start; index < Math.min(this.targets.length, start + maxRows); index++) {\n\t\t\tconst target = this.targets[index]!;\n\t\t\tconst selected = index === this.selected;\n\t\t\tconst marker = selected ? \"›\" : \" \";\n\t\t\t\tconst actionLabel = target.actionLabel;\n\t\t\tconst action = target.kind === \"scheduled\" ? this.theme.fg(\"warning\", actionLabel) : this.theme.fg(\"accent\", actionLabel);\n\t\t\tconst labelWidth = Math.max(0, contentWidth - marker.length - actionLabel.length - 2);\n\t\t\tlines.push(`${marker} ${action} ${target.label.slice(0, labelWidth)}`);\n\t\t\tif (selected) lines.push(this.theme.fg(\"dim\", ` ${target.detail}`.slice(0, contentWidth)));\n\t\t}\n\t\tif (this.targets.length > maxRows) lines.push(this.theme.fg(\"dim\", `Showing ${start + 1}-${Math.min(this.targets.length, start + maxRows)} of ${this.targets.length}`));\n\t\tlines.push(\"\");\n\t\tif (this.confirming) {\n\t\t\tconst target = this.targets[this.selected]!;\n\t\t\tlines.push(this.theme.fg(\"warning\", `Confirm: ${target.actionLabel} ${target.id}?`));\n\t\t\tif (target.kind === \"async\") lines.push(this.theme.fg(\"dim\", \"Stop ends this run; use interrupt for a resumable pause.\"));\n\t\t\tlines.push(this.theme.fg(\"dim\", \"Enter/Y confirms · N returns · Esc cancels\"));\n\t\t} else {\n\t\t\tlines.push(this.theme.fg(\"dim\", \"↑/↓ select · Enter confirm · Esc cancel\"));\n\t\t}\n\t\treturn lines;\n\t}\n}\n\nfunction emptyUsage(): Usage {\n\treturn { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };\n}\n\nfunction addUsage(target: Usage, source: Usage): void {\n\ttarget.input += source.input;\n\ttarget.output += source.output;\n\ttarget.cacheRead += source.cacheRead;\n\ttarget.cacheWrite += source.cacheWrite;\n\ttarget.cost += source.cost;\n\ttarget.turns += source.turns;\n}\n\nfunction usageHasValue(usage: Usage): boolean {\n\treturn usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0;\n}\n\nfunction assistantUsageFromMessage(message: unknown): Usage | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst msg = message as { role?: unknown; usage?: unknown };\n\tif (msg.role !== \"assistant\" || !msg.usage || typeof msg.usage !== \"object\") return undefined;\n\tconst usage = msg.usage as {\n\t\tinput?: unknown;\n\t\toutput?: unknown;\n\t\tcacheRead?: unknown;\n\t\tcacheWrite?: unknown;\n\t\tcost?: { total?: unknown };\n\t};\n\treturn {\n\t\tinput: typeof usage.input === \"number\" ? usage.input : 0,\n\t\toutput: typeof usage.output === \"number\" ? usage.output : 0,\n\t\tcacheRead: typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0,\n\t\tcacheWrite: typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0,\n\t\tcost: typeof usage.cost?.total === \"number\" ? usage.cost.total : 0,\n\t\tturns: 1,\n\t};\n}\n\nfunction isSubagentDetails(value: unknown): value is Details {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst details = value as { mode?: unknown; results?: unknown };\n\treturn typeof details.mode === \"string\" && Array.isArray(details.results);\n}\n\nfunction detailsFromSessionEntry(entry: unknown): Details | undefined {\n\tif (!entry || typeof entry !== \"object\") return undefined;\n\tconst record = entry as { type?: unknown; customType?: unknown; details?: unknown; message?: unknown };\n\tif (record.type === \"custom_message\" && record.customType === SLASH_RESULT_TYPE) {\n\t\tconst details = resolveSlashMessageDetails(record.details)?.result.details;\n\t\treturn isSubagentDetails(details) ? details : undefined;\n\t}\n\tif (record.type !== \"message\" || !record.message || typeof record.message !== \"object\") return undefined;\n\tconst message = record.message as { role?: unknown; toolName?: unknown; details?: unknown };\n\tif (message.role !== \"toolResult\" || message.toolName !== \"subagent\") return undefined;\n\treturn isSubagentDetails(message.details) ? message.details : undefined;\n}\n\nfunction formatCostUsage(label: string, usage: Usage): string {\n\tconst extras = [\n\t\tusage.cacheRead ? `cache read ${formatTokens(usage.cacheRead)}` : \"\",\n\t\tusage.cacheWrite ? `cache write ${formatTokens(usage.cacheWrite)}` : \"\",\n\t\tusage.turns ? `${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}` : \"\",\n\t].filter(Boolean);\n\treturn `${label}: ↑${formatTokens(usage.input)} ↓${formatTokens(usage.output)} $${usage.cost.toFixed(4)}${extras.length ? ` (${extras.join(\", \")})` : \"\"}`;\n}\n\nfunction buildSubagentCostReport(ctx: ExtensionContext): string {\n\tconst parent = emptyUsage();\n\tconst childTotal = emptyUsage();\n\tconst total = emptyUsage();\n\tconst children: Array<{ label: string; usage: Usage; sessionFile?: string }> = [];\n\tfor (const entry of ctx.sessionManager.getBranch()) {\n\t\tconst message = entry.type === \"message\" ? (entry as { message?: unknown }).message : undefined;\n\t\tconst parentUsage = assistantUsageFromMessage(message);\n\t\tif (parentUsage) addUsage(parent, parentUsage);\n\t\tconst details = detailsFromSessionEntry(entry);\n\t\tif (!details) continue;\n\t\tfor (const result of details.results) {\n\t\t\tif (!usageHasValue(result.usage)) continue;\n\t\t\tconst usage = { ...result.usage };\n\t\t\tchildren.push({\n\t\t\t\tlabel: `Child ${children.length + 1} (${result.agent})`,\n\t\t\t\tusage,\n\t\t\t\t...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),\n\t\t\t});\n\t\t\taddUsage(childTotal, usage);\n\t\t}\n\t}\n\taddUsage(total, parent);\n\taddUsage(total, childTotal);\n\tconst lines = [\n\t\t\"Subagent cost\",\n\t\t\"\",\n\t\tformatCostUsage(\"Parent\", parent),\n\t];\n\tif (children.length === 0) {\n\t\tlines.push(\"No subagent child usage found in this session.\");\n\t} else {\n\t\tfor (const child of children) {\n\t\t\tlines.push(formatCostUsage(child.label, child.usage));\n\t\t\tif (child.sessionFile) lines.push(` Session: ${child.sessionFile}`);\n\t\t}\n\t}\n\tlines.push(\"────────────────────────────\", formatCostUsage(\"Children\", childTotal), formatCostUsage(\"Total\", total));\n\treturn lines.join(\"\\n\");\n}\n\nasync function requestSlashRun(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\trequestId: string,\n\tparams: SubagentParamsLike,\n): Promise<SlashSubagentResponse> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet done = false;\n\t\tlet started = false;\n\n\t\tconst startTimeoutMs = 15_000;\n\t\tconst startTimeout = setTimeout(() => {\n\t\t\tfinish(() => reject(new Error(\n\t\t\t\t\"Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.\",\n\t\t\t)));\n\t\t}, startTimeoutMs);\n\n\t\tconst onStarted = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tif ((data as { requestId?: unknown }).requestId !== requestId) return;\n\t\t\tstarted = true;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tif (ctx.hasUI) ctx.ui.setStatus(\"subagent-slash\", \"running...\");\n\t\t};\n\n\t\tconst onResponse = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tconst response = data as Partial<SlashSubagentResponse>;\n\t\t\tif (response.requestId !== requestId) return;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tfinish(() => resolve(response as SlashSubagentResponse));\n\t\t};\n\n\t\tconst onUpdate = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tconst update = data as SlashSubagentUpdate;\n\t\t\tif (update.requestId !== requestId) return;\n\t\t\tapplySlashUpdate(requestId, update);\n\t\t\tif (!ctx.hasUI) return;\n\t\t\tconst tool = update.currentTool ? ` ${update.currentTool}` : \"\";\n\t\t\tconst count = update.toolCount ?? 0;\n\t\t\tconst liveDetailKey = keyText(\"app.tools.expand\");\n\t\t\tctx.ui.setStatus(\"subagent-slash\", `${count} tools${tool} | ${liveDetailKey} live detail`);\n\t\t};\n\n\t\tconst onTerminalInput = ctx.hasUI\n\t\t\t? ctx.ui.onTerminalInput((input) => {\n\t\t\t\tif (!matchesKey(input, Key.escape)) return undefined;\n\t\t\t\tpi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });\n\t\t\t\tfinish(() => reject(new Error(\"Cancelled\")));\n\t\t\t\treturn { consume: true };\n\t\t\t})\n\t\t\t: undefined;\n\n\t\tconst unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);\n\t\tconst unsubResponse = pi.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);\n\t\tconst unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);\n\n\t\tconst finish = (next: () => void) => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tunsubStarted();\n\t\t\tunsubResponse();\n\t\t\tunsubUpdate();\n\t\t\tonTerminalInput?.();\n\t\t\tnext();\n\t\t};\n\n\t\tpi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params, ctx });\n\n\t\t// Bridge emits STARTED synchronously during REQUEST emit.\n\t\t// If not started, no bridge received the request.\n\t\tif (!started && done) return;\n\t\tif (!started) {\n\t\t\tfinish(() => reject(new Error(\n\t\t\t\t\"No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.\",\n\t\t\t)));\n\t\t}\n\t});\n}\n\nfunction extractSlashMessageText(content: string | Array<{ type?: string; text?: string }>): string {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part?.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\\n\");\n}\n\nfunction formatExportPathList(paths: string[]): string {\n\treturn paths.map((file) => `- \\`${file}\\``).join(\"\\n\");\n}\n\nfunction collectResultPaths(results: SingleResult[], getPath: (result: SingleResult) => string | undefined): string[] {\n\treturn results\n\t\t.map(getPath)\n\t\t.filter((file): file is string => typeof file === \"string\" && file.length > 0);\n}\n\nfunction buildSlashExportText(response: SlashSubagentResponse): string {\n\tconst output = extractSlashMessageText(response.result.content) || response.errorText || \"(no output)\";\n\tconst results = response.result.details?.results ?? [];\n\tconst sessionFiles = collectResultPaths(results, (result) => result.sessionFile);\n\tconst savedOutputs = collectResultPaths(results, (result) => result.savedOutputPath);\n\tconst artifactOutputs = collectResultPaths(results, (result) => result.artifactPaths?.outputPath);\n\tconst sections = [\"## Subagent result\", output];\n\tif (sessionFiles.length > 0) sections.push(\"## Child session exports\", formatExportPathList(sessionFiles));\n\tif (savedOutputs.length > 0) sections.push(\"## Saved outputs\", formatExportPathList(savedOutputs));\n\tif (artifactOutputs.length > 0) sections.push(\"## Artifact outputs\", formatExportPathList(artifactOutputs));\n\treturn sections.join(\"\\n\\n\");\n}\n\nfunction persistSlashSessionSnapshot(ctx: ExtensionContext): void {\n\ttry {\n\t\tif (!ctx.sessionManager) return;\n\t\tconst sessionManager = ctx.sessionManager as typeof ctx.sessionManager & {\n\t\t\t_rewriteFile?: () => void;\n\t\t\tflushed?: boolean;\n\t\t};\n\t\tconst sessionFile = sessionManager.getSessionFile();\n\t\tif (!sessionFile || typeof sessionManager._rewriteFile !== \"function\") return;\n\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\tsessionManager._rewriteFile();\n\t\tsessionManager.flushed = true;\n\t} catch (error) {\n\t\tconsole.error(\"Failed to persist slash session snapshot for export:\", error);\n\t}\n}\n\nasync function runSlashSubagent(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tparams: SubagentParamsLike,\n): Promise<void> {\n\tif (ctx.hasUI) ctx.ui.setToolsExpanded(false);\n\tconst requestId = randomUUID();\n\tconst initialDetails = buildSlashInitialResult(requestId, params);\n\tconst initialText = extractSlashMessageText(initialDetails.result.content) || \"Running subagent...\";\n\tpi.sendMessage({\n\t\tcustomType: SLASH_RESULT_TYPE,\n\t\tcontent: initialText,\n\t\tdisplay: true,\n\t\tdetails: initialDetails,\n\t});\n\tpersistSlashSessionSnapshot(ctx);\n\n\ttry {\n\t\tconst response = await requestSlashRun(pi, ctx, requestId, params);\n\t\tconst finalDetails = finalizeSlashResult(response);\n\t\tpi.sendMessage({\n\t\t\tcustomType: SLASH_RESULT_TYPE,\n\t\t\tcontent: buildSlashExportText(response),\n\t\t\tdisplay: !ctx.hasUI,\n\t\t\tdetails: finalDetails,\n\t\t});\n\t\tpersistSlashSessionSnapshot(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setStatus(\"subagent-slash\", undefined);\n\t\t}\n\t\tif (response.isError && ctx.hasUI) {\n\t\t\tctx.ui.notify(response.errorText || \"Subagent failed\", \"error\");\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst failedDetails = failSlashResult(requestId, params, message);\n\t\tpi.sendMessage({\n\t\t\tcustomType: SLASH_RESULT_TYPE,\n\t\t\tcontent: `## Subagent result\\n\\n${message}`,\n\t\t\tdisplay: !ctx.hasUI,\n\t\t\tdetails: failedDetails,\n\t\t});\n\t\tpersistSlashSessionSnapshot(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setStatus(\"subagent-slash\", undefined);\n\t\t}\n\t\tif (message === \"Cancelled\") {\n\t\t\tif (ctx.hasUI) ctx.ui.notify(\"Cancelled\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tif (ctx.hasUI) ctx.ui.notify(message, \"error\");\n\t}\n}\n\n\nexport interface GroupConfig { concurrency?: number; failFast?: boolean; worktree?: boolean }\nexport interface ParsedStep { kind: \"step\"; name: string; config: InlineConfig; task?: string }\nexport interface ParsedGroup { kind: \"group\"; tasks: ParsedStep[]; config: GroupConfig }\nexport type ParsedGroupStep = ParsedStep | ParsedGroup;\n\nexport const PARALLEL_GROUP_USAGE =\n\t'Usage: /chain agent \"task\" -> (agent2 \"task\" | agent3 \"task\") -> agent4';\n\nexport class SlashParseError extends Error {}\n\n// Walk `input` tracking quote/paren state; returns true if parens are unbalanced.\nfunction findUnmatchedCloseParen(input: string): boolean {\n\tlet depth = 0, inSingle = false, inDouble = false;\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") { depth--; if (depth < 0) return true; }\n\t}\n\treturn depth !== 0;\n}\n\n// Split on top-level \" -> \", ignoring arrows inside quotes or parentheses.\nfunction splitOnArrow(input: string): string[] {\n\tconst segments: string[] = [];\n\tlet depth = 0, inSingle = false, inDouble = false, start = 0;\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth--;\n\t\telse if (depth === 0 && ch === \"-\" && input[i + 1] === \">\" && input[i + 2] === \" \") {\n\t\t\tsegments.push(input.slice(start, i));\n\t\t\ti += 2;\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\tsegments.push(input.slice(start));\n\treturn segments;\n}\n\n// Split a group's inner text on top-level \" | \", ignoring pipes inside quotes/parens.\nfunction splitGroupTasks(inner: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0, inSingle = false, inDouble = false, start = 0;\n\tfor (let i = 0; i < inner.length; i++) {\n\t\tconst ch = inner[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth--;\n\t\telse if (ch === \"|\" && depth === 0) {\n\t\t\tparts.push(inner.slice(start, i));\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\tparts.push(inner.slice(start));\n\treturn parts;\n}\n\nexport function parseSingleTaskToken(token: string): ParsedStep {\n\tlet agentPart: string;\n\tlet task: string | undefined;\n\tconst qMatch = token.match(/^(\\S+(?:\\[[^\\]]*\\])?)\\s+(?:\"([^\"]*)\"|'([^']*)')$/);\n\tif (qMatch) {\n\t\tagentPart = qMatch[1]!;\n\t\ttask = (qMatch[2] ?? qMatch[3]) || undefined;\n\t} else {\n\t\tconst dashIdx = token.indexOf(\" -- \");\n\t\tif (dashIdx !== -1) {\n\t\t\tagentPart = token.slice(0, dashIdx).trim();\n\t\t\ttask = token.slice(dashIdx + 4).trim() || undefined;\n\t\t} else {\n\t\t\tagentPart = token;\n\t\t}\n\t}\n\treturn { kind: \"step\", ...parseAgentToken(agentPart), task };\n}\n\nconst parseGroupConfig = (raw: string): GroupConfig => {\n\tconst config: GroupConfig = {};\n\tfor (const part of raw.split(\",\")) {\n\t\tconst trimmed = part.trim();\n\t\tif (!trimmed) continue;\n\t\tconst eq = trimmed.indexOf(\"=\");\n\t\tconst key = eq === -1 ? trimmed : trimmed.slice(0, eq).trim();\n\t\tconst val = eq === -1 ? \"\" : trimmed.slice(eq + 1).trim();\n\t\tswitch (key) {\n\t\t\tcase \"concurrency\": { const n = Number(val); if (Number.isInteger(n) && n > 0) config.concurrency = n; break; }\n\t\t\tcase \"failFast\": config.failFast = eq === -1 ? true : val !== \"false\"; break;\n\t\t\tcase \"worktree\": config.worktree = eq === -1 ? true : val !== \"false\"; break;\n\t\t}\n\t}\n\treturn config;\n};\n\n// Split `(...)` from an optional trailing `[...]` group-config suffix, respecting\n// quotes and nested parens. Returns the inner group text and the parsed config.\nconst splitGroupBody = (trimmed: string): { inner: string; config: GroupConfig } => {\n\tlet depth = 0, inSingle = false, inDouble = false, closeIdx = -1;\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") { depth--; if (depth === 0) { closeIdx = i; break; } }\n\t}\n\tif (closeIdx === -1) throw new SlashParseError(`Unmatched parentheses in group: '${trimmed}'`);\n\tconst inner = trimmed.slice(1, closeIdx);\n\tconst suffix = trimmed.slice(closeIdx + 1).trim();\n\tif (!suffix) return { inner, config: {} };\n\tif (!suffix.startsWith(\"[\") || !suffix.endsWith(\"]\")) {\n\t\tthrow new SlashParseError(`Group options must be wrapped in [...]: '${suffix}'`);\n\t}\n\treturn { inner, config: parseGroupConfig(suffix.slice(1, -1)) };\n};\n\nexport function parseGroupSegment(segment: string): ParsedGroup {\n\tconst trimmed = segment.trim();\n\tif (!trimmed.startsWith(\"(\")) {\n\t\tthrow new SlashParseError(`Parallel group must be wrapped in parentheses: '${trimmed}'`);\n\t}\n\tconst { inner, config } = splitGroupBody(trimmed);\n\tconst rawParts = splitGroupTasks(inner).map((p) => p.trim()).filter((p) => p.length > 0);\n\tif (rawParts.length < 2) {\n\t\tthrow new SlashParseError(\"Parallel group must contain at least two tasks separated by ' | '\");\n\t}\n\treturn { kind: \"group\", tasks: rawParts.map((part) => parseSingleTaskToken(part)), config };\n}\n\n// True if `input` uses inline parallel-group syntax. A group is a *step* that begins\n// with `(` at the top level, so we split on top-level ` -> ` arrows and look for a\n// segment that opens with `(`. Parentheses appearing inside a task (e.g.\n// `scout -- inspect auth (backend)`) do not count, which keeps legacy shared-task\n// `-- task` parsing — including a bare `|` inside the task — on the single-agent path.\nexport function hasGroupSyntax(input: string): boolean {\n\treturn splitOnArrow(input).some((seg) => seg.trim().startsWith(\"(\"));\n}\n\nexport function parseChainExpression(input: string): { steps: ParsedGroupStep[] } {\n\tconst trimmed = input.trim();\n\tif (!trimmed.includes(\" -> \")) {\n\t\tthrow new SlashParseError('Parallel groups in /chain require \" -> \" between steps');\n\t}\n\tif (findUnmatchedCloseParen(trimmed)) {\n\t\tthrow new SlashParseError(\"Unmatched parentheses in /chain expression\");\n\t}\n\tconst steps: ParsedGroupStep[] = [];\n\tfor (const seg of splitOnArrow(trimmed)) {\n\t\tconst t = seg.trim();\n\t\tif (!t) continue;\n\t\tif (t.startsWith(\"(\")) {\n\t\t\tsteps.push(parseGroupSegment(t));\n\t\t\tcontinue;\n\t\t}\n\t\tif (findUnmatchedCloseParen(t)) {\n\t\t\tthrow new SlashParseError(`Unmatched parentheses in chain segment: '${t}'`);\n\t\t}\n\t\tsteps.push(parseSingleTaskToken(t));\n\t}\n\tif (steps.length === 0) {\n\t\tthrow new SlashParseError(\"/chain expression must include at least one step\");\n\t}\n\treturn { steps };\n}\n\nconst parseAgentArgs = (\n\tstate: SubagentState,\n\targs: string,\n\tcommand: string,\n\tctx: ExtensionContext,\n): { steps: ParsedStep[]; task: string } | null => {\n\tconst input = args.trim();\n\tconst usage = `Usage: /${command} description1 \"task1\" -> description2 \"task2\"`;\n\tlet steps: ParsedStep[];\n\tlet sharedTask: string;\n\tlet perStep = false;\n\n\tif (input.includes(\" -> \")) {\n\t\tperStep = true;\n\t\tconst segments = input.split(\" -> \");\n\t\tsteps = [];\n\t\tfor (const seg of segments) {\n\t\t\tconst trimmed = seg.trim();\n\t\t\tif (!trimmed) continue;\n\t\t\tsteps.push(parseSingleTaskToken(trimmed));\n\t\t}\n\t\tsharedTask = steps.find((s) => s.task)?.task ?? \"\";\n\t} else {\n\t\tconst delimiterIndex = input.indexOf(\" -- \");\n\t\tif (delimiterIndex === -1) {\n\t\t\tctx.ui.notify(usage, \"error\");\n\t\t\treturn null;\n\t\t}\n\t\tconst agentsPart = input.slice(0, delimiterIndex).trim();\n\t\tsharedTask = input.slice(delimiterIndex + 4).trim();\n\t\tif (!agentsPart || !sharedTask) {\n\t\t\tctx.ui.notify(usage, \"error\");\n\t\t\treturn null;\n\t\t}\n\t\tsteps = agentsPart.split(/\\s+/).filter(Boolean).map((t) => parseSingleTaskToken(t));\n\t}\n\n\tif (steps.length === 0) {\n\t\tctx.ui.notify(usage, \"error\");\n\t\treturn null;\n\t}\n\tif (!state.baseCwd) {\n\t\tctx.ui.notify(\"Subagent session cwd is not initialized yet\", \"error\");\n\t\treturn null;\n\t}\n\tif (command === \"chain\" && !steps[0]?.task && (perStep || !sharedTask)) {\n\t\tctx.ui.notify(`First step must have a task: /chain description \"task\" -> description2`, \"error\");\n\t\treturn null;\n\t}\n\tif (command === \"parallel\" && !steps.some((s) => s.task) && !sharedTask) {\n\t\tctx.ui.notify(\"At least one step must have a task\", \"error\");\n\t\treturn null;\n\t}\n\treturn { steps, task: sharedTask };\n};\n\ntype ChainStepObject = {\n\tdescription: string;\n\tpermissions: \"full\" | \"read-only\";\n\ttier: \"light\" | \"standard\" | \"heavy\";\n\ttask?: string;\n\toutput?: string | false;\n\toutputMode?: \"inline\" | \"file-only\";\n\treads?: string[] | false;\n\tskill?: string[] | false;\n\tprogress?: boolean;\n\tas?: string;\n\tlabel?: string;\n\tphase?: string;\n\tcwd?: string;\n\tcount?: number;\n\toutputSchema?: JsonSchemaObject;\n\tacceptance?: string;\n};\n\nconst INLINE_ACCEPTANCE_LEVELS = new Set([\"auto\", \"attested\", \"checked\"]);\n\nfunction validateInlineAcceptanceInput(value: string, agent: string): void {\n\tconst errors = validateAcceptanceInput(value, `acceptance for step '${agent}'`);\n\tif (errors.length > 0) throw new SlashParseError(errors[0]!);\n\tif (!INLINE_ACCEPTANCE_LEVELS.has(value)) {\n\t\tthrow new SlashParseError(`Inline acceptance for step '${agent}' supports auto, attested, or checked. Use the subagent tool API or a saved .chain.json file for none or verified acceptance contracts; reviewed is inferred-only.`);\n\t}\n}\n\n// Load an inline `outputSchema=<path>` JSON file, resolved against the session cwd.\n// Throws (SlashParseError / fs / JSON) on a missing or malformed schema.\nfunction loadInlineOutputSchema(baseCwd: string, agent: string, value: string): JsonSchemaObject {\n\tconst schemaPath = path.isAbsolute(value) ? value : path.join(baseCwd, value);\n\tconst label = `outputSchema for step '${agent}' (${schemaPath})`;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(fs.readFileSync(schemaPath, \"utf-8\"));\n\t} catch (error) {\n\t\tthrow new SlashParseError(`Cannot read ${label}: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tassertJsonSchemaObject(parsed, label);\n\treturn parsed;\n}\n\n// Build a ChainStep object from a parsed token. `inGroup` enables `count` (parallel-only).\n// May throw SlashParseError for an invalid acceptance level or outputSchema path.\nconst mapParsedTaskToStepObject = (\n\tstep: ParsedStep,\n\tfallbackTask: string | undefined,\n\tisFirst: boolean,\n\topts: { baseCwd: string; inGroup: boolean },\n): ChainStepObject => {\n\tconst { name, config, task: stepTask } = step;\n\tif (!config.tier) throw new SlashParseError(`Step '${name}' requires tier=light, tier=standard, or tier=heavy.`);\n\tif (config.acceptance !== undefined) validateInlineAcceptanceInput(config.acceptance, name);\n\treturn {\n\t\tdescription: name,\n\t\tpermissions: config.permissions ?? \"full\",\n\t\ttier: config.tier,\n\t\t...(stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {}),\n\t\t...(config.output !== undefined ? { output: config.output } : {}),\n\t\t...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),\n\t\t...(config.reads !== undefined ? { reads: config.reads } : {}),\n\t\t...(config.skill !== undefined ? { skill: config.skill } : {}),\n\t\t...(config.progress !== undefined ? { progress: config.progress } : {}),\n\t\t...(config.as ? { as: config.as } : {}),\n\t\t...(config.label ? { label: config.label } : {}),\n\t\t...(config.phase ? { phase: config.phase } : {}),\n\t\t...(config.cwd ? { cwd: config.cwd } : {}),\n\t\t...(opts.inGroup && config.count !== undefined ? { count: config.count } : {}),\n\t\t...(config.outputSchema ? { outputSchema: loadInlineOutputSchema(opts.baseCwd, name, config.outputSchema) } : {}),\n\t\t...(config.acceptance ? { acceptance: config.acceptance } : {}),\n\t};\n};\n\nexport function buildChainExpressionSteps(\n\tstate: SubagentState,\n\tinput: string,\n\tctx: ExtensionContext,\n): { chain: ChainStep[]; task: string } | null {\n\tconst notify = (message: string) => ctx.ui.notify(message, \"error\");\n\tif (!hasGroupSyntax(input)) {\n\t\tconst parsed = parseAgentArgs(state, input, \"chain\", ctx);\n\t\tif (!parsed) return null;\n\t\tconst baseCwd = state.baseCwd!; // parseAgentArgs already verified baseCwd is set\n\t\ttry {\n\t\t\tconst chain: ChainStep[] = parsed.steps.map((step, i) =>\n\t\t\t\tmapParsedTaskToStepObject(step, parsed.task || undefined, i === 0, { baseCwd, inGroup: false }),\n\t\t\t);\n\t\t\treturn { chain, task: parsed.task };\n\t\t} catch (error) {\n\t\t\tnotify(error instanceof Error ? error.message : String(error));\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tlet expression: { steps: ParsedGroupStep[] };\n\ttry {\n\t\texpression = parseChainExpression(input);\n\t} catch (error) {\n\t\tnotify(error instanceof Error ? error.message : String(error));\n\t\treturn null;\n\t}\n\tif (!state.baseCwd) {\n\t\tnotify(\"Subagent session cwd is not initialized yet\");\n\t\treturn null;\n\t}\n\t// Every task inside a parallel group needs its own task; there is no shared-task fallback.\n\tfor (const step of expression.steps) {\n\t\tif (step.kind === \"group\" && step.tasks.some((t) => !t.task)) {\n\t\t\tnotify('Each child in a parallel group needs a task: (description \"a\" | description2 \"b\")');\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst firstStep = expression.steps[0]!;\n\tconst firstHasTask =\n\t\tfirstStep.kind === \"group\"\n\t\t\t? firstStep.tasks.some((t) => Boolean(t.task))\n\t\t\t: Boolean(firstStep.task);\n\tif (!firstHasTask) {\n\t\tnotify('First step must have a task: /chain description \"task\" -> description2');\n\t\treturn null;\n\t}\n\tconst sharedTask =\n\t\tfirstStep.kind === \"group\"\n\t\t\t? (firstStep.tasks.find((t) => t.task)?.task ?? \"\")\n\t\t\t: (firstStep.task ?? \"\");\n\tconst baseCwd = state.baseCwd;\n\tlet chain: ChainStep[];\n\ttry {\n\t\tchain = expression.steps.map((step) => {\n\t\t\tif (step.kind === \"group\") {\n\t\t\t\tconst parallel = step.tasks.map((t) => mapParsedTaskToStepObject(t, undefined, false, { baseCwd, inGroup: true }));\n\t\t\t\treturn {\n\t\t\t\t\tparallel,\n\t\t\t\t\t...(step.config.concurrency !== undefined ? { concurrency: step.config.concurrency } : {}),\n\t\t\t\t\t...(step.config.failFast !== undefined ? { failFast: step.config.failFast } : {}),\n\t\t\t\t\t...(step.config.worktree !== undefined ? { worktree: step.config.worktree } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn mapParsedTaskToStepObject(step, sharedTask || undefined, false, { baseCwd, inGroup: false });\n\t\t});\n\t} catch (error) {\n\t\tnotify(error instanceof Error ? error.message : String(error));\n\t\treturn null;\n\t}\n\treturn { chain, task: sharedTask };\n}\n\nexport function registerSlashCommands(\n\tpi: ExtensionAPI,\n\tstate: SubagentState,\n): void {\n\tlet fleetOpen = false;\n\tconst showFleet = async (ctx: ExtensionContext) => {\n\t\tstate.lastUiContext = ctx;\n\t\tif (!ctx.hasUI) {\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"status\", view: \"fleet\" });\n\t\t\treturn;\n\t\t}\n\t\tif (fleetOpen) {\n\t\t\tctx.ui.notify(\"Subagent fleet inspector is already open.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tfleetOpen = true;\n\t\ttry {\n\t\t\tawait openSubagentFleet(ctx, state);\n\t\t} finally {\n\t\t\tfleetOpen = false;\n\t\t}\n\t};\n\n\tpi.registerCommand(\"run\", {\n\t\tdescription: \"Run a prompt-defined child: /run description[tier=standard] task [--bg]\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg } = extractExecutionFlags(args);\n\t\t\tconst input = cleanedArgs.trim();\n\t\t\tconst firstSpace = input.indexOf(\" \");\n\t\t\tif (!input) { ctx.ui.notify(\"Usage: /run <description> <task> [--bg]\", \"error\"); return; }\n\t\t\tconst { name: description, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace));\n\t\t\tconst task = firstSpace === -1 ? \"\" : input.slice(firstSpace + 1).trim();\n\t\t\tif (!description || !task || !inline.tier) { ctx.ui.notify(\"Usage: /run <description>[tier=light|standard|heavy] <task> [--bg]\", \"error\"); return; }\n\n\t\t\tlet finalTask = task;\n\t\t\tif (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {\n\t\t\t\tfinalTask = `[Read from: ${inline.reads.join(\", \")}]\\n\\n${finalTask}`;\n\t\t\t}\n\t\t\tconst params: SubagentParamsLike = { description, permissions: inline.permissions ?? \"full\", tier: inline.tier, task: finalTask, clarify: false };\n\t\t\tif (inline.output !== undefined) params.output = inline.output;\n\t\t\tif (inline.outputMode !== undefined) params.outputMode = inline.outputMode;\n\t\t\tif (inline.skill !== undefined) params.skill = inline.skill;\n\t\t\tif (bg) params.async = true;\n\t\t\tawait runSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"chain\", {\n\t\tdescription: \"Run prompt-defined children in sequence: /chain inspect \\\"task\\\" -> implement [--bg]\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg } = extractExecutionFlags(args);\n\t\t\tconst built = buildChainExpressionSteps(state, cleanedArgs, ctx);\n\t\t\tif (!built) return;\n\t\t\tconst params: SubagentParamsLike = { chain: built.chain, task: built.task, clarify: false };\n\t\t\tif (bg) params.async = true;\n\t\t\tawait runSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"parallel\", {\n\t\tdescription: \"Run prompt-defined children in parallel: /parallel inspect \\\"task1\\\" -> review \\\"task2\\\" [--bg]\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg } = extractExecutionFlags(args);\n\t\t\tconst parsed = parseAgentArgs(state, cleanedArgs, \"parallel\", ctx);\n\t\t\tif (!parsed) return;\n\t\t\tconst tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({\n\t\t\t\tdescription: name,\n\t\t\t\tpermissions: config.permissions ?? \"full\" as const,\n\t\t\t\ttier: config.tier,\n\t\t\t\ttask: stepTask ?? parsed.task,\n\t\t\t\t...(config.output !== undefined ? { output: config.output } : {}),\n\t\t\t\t...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),\n\t\t\t\t...(config.reads !== undefined ? { reads: config.reads } : {}),\n\t\t\t\t...(config.skill !== undefined ? { skill: config.skill } : {}),\n\t\t\t\t...(config.progress !== undefined ? { progress: config.progress } : {}),\n\t\t\t}));\n\t\t\tif (tasks.some((task) => !task.tier)) { ctx.ui.notify(\"Every parallel child requires [tier=light|standard|heavy].\", \"error\"); return; }\n\t\t\tconst params: SubagentParamsLike = { tasks, clarify: false };\n\t\t\tif (bg) params.async = true;\n\t\t\tawait runSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagent-cost\", {\n\t\tdescription: \"Show parent and subagent child usage cost for this session\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tsendSlashText(pi, buildSubagentCostReport(ctx));\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-doctor\", {\n\t\tdescription: \"Show subagent diagnostics\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"doctor\" });\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-fleet\", {\n\t\tdescription: \"Open the live, inspection-only subagent fleet\",\n\t\thandler: async (_args, ctx) => showFleet(ctx),\n\t});\n\n\tpi.registerShortcut(Key.ctrlAlt(\"f\"), {\n\t\tdescription: \"Open subagent fleet inspector\",\n\t\thandler: async (ctx) => showFleet(ctx),\n\t});\n\n\tpi.registerCommand(\"subagents-stop\", {\n\t\tdescription: \"Stop a current-session async subagent run\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst id = args.trim();\n\t\t\tif (id) {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"stop\", id });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (process.env[SUBAGENT_FANOUT_CHILD_ENV] === \"1\") {\n\t\t\t\tsendSlashText(pi, \"Selector unavailable in child-safe fanout mode. Pass an explicit current-session top-level async run id, for example `/subagents-stop <run-id>` or `subagent({ action: \\\"stop\\\", id: \\\"<run-id>\\\" })`.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet targets: StopSelectorTarget[];\n\t\t\ttry {\n\t\t\t\ttargets = discoverStopTargets(ctx, state);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tctx.ui.notify(message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tsendSlashText(pi, stopFallbackText(targets));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (targets.length === 0) {\n\t\t\t\tctx.ui.notify(\"No active current-session async runs or scheduled subagent runs to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst result = await ctx.ui.custom<StopSelectorResult>(\n\t\t\t\t(tui, theme, _kb, done) => new SubagentsStopSelector(tui, theme, targets, done),\n\t\t\t\t{ overlay: true, overlayOptions: { anchor: \"center\", width: 88, maxHeight: \"80%\" } },\n\t\t\t);\n\t\t\tif (!result?.confirmed || !result.target) return;\n\t\t\tif (result.target.kind === \"scheduled\") {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"schedule-cancel\", id: result.target.id });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"stop\", id: result.target.id });\n\t\t},\n\t});\n\n\tregisterPromptWorkflowCommands({\n\t\tpi,\n\t\trun: (params, ctx) => runSlashSubagent(pi, ctx, params),\n\t});\n\n}\n"]}
|
|
1
|
+
{"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../../../../src/builtin-extensions/pi-subagents/src/slash/slash-commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAItF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAiBvD,OAAO,EAYN,KAAK,aAAa,EAElB,MAAM,oBAAoB,CAAC;AAE5B,UAAU,YAAY;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,UAAU,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AA4fD,MAAM,WAAW,WAAW;IAAG,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AAC7F,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC/F,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE;AACxF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,CAAC;AAEvD,eAAO,MAAM,oBAAoB,kFACyC,CAAC;AAE3E,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AA4D7C,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAiB9D;AA0CD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAW9D;AAOD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,CAyBhF;AAqID,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,gBAAgB,GACnB;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAoE7C;AAED,wBAAgB,qBAAqB,CACpC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,aAAa,GAClB,IAAI,CAyJN","sourcesContent":["// @ts-nocheck\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ExtensionAPI, ExtensionContext } from \"@ashx-j/lunr-dev\";\nimport { keyText } from \"../../../../modes/interactive/components/keybinding-hints.ts\";\nimport { Key, matchesKey, type Component, type TUI } from \"@ashx-j/lunr-tui\";\nimport type { SubagentParamsLike } from \"../runs/foreground/subagent-executor.ts\";\nimport type { ChainStep } from \"../shared/settings.ts\";\nimport { formatTokens, shortenPath } from \"../shared/formatters.ts\";\nimport { listAsyncRuns, formatAsyncRunProgressLabel, type AsyncRunSummary } from \"../runs/background/async-status.ts\";\nimport { scheduledRunStorePath } from \"../runs/background/scheduled-runs.ts\";\nimport { SUBAGENT_FANOUT_CHILD_ENV } from \"../runs/shared/pi-args.ts\";\nimport { assertJsonSchemaObject } from \"../runs/shared/structured-output.ts\";\nimport { validateAcceptanceInput } from \"../runs/shared/acceptance.ts\";\nimport type { SlashSubagentResponse, SlashSubagentUpdate } from \"./slash-bridge.ts\";\nimport { registerPromptWorkflowCommands } from \"./prompt-workflows.ts\";\nimport { openSubagentFleet } from \"../tui/fleet.ts\";\nimport {\n\tapplySlashUpdate,\n\tbuildSlashInitialResult,\n\tfailSlashResult,\n\tfinalizeSlashResult,\n\tresolveSlashMessageDetails,\n} from \"./slash-live-state.ts\";\nimport {\n\tSLASH_RESULT_TYPE,\n\tSLASH_TEXT_RESULT_TYPE,\n\tSLASH_SUBAGENT_CANCEL_EVENT,\n\tSLASH_SUBAGENT_REQUEST_EVENT,\n\tSLASH_SUBAGENT_RESPONSE_EVENT,\n\tSLASH_SUBAGENT_STARTED_EVENT,\n\tSLASH_SUBAGENT_UPDATE_EVENT,\n\tASYNC_DIR,\n\ttype Details,\n\ttype JsonSchemaObject,\n\ttype SingleResult,\n\ttype SubagentState,\n\ttype Usage,\n} from \"../shared/types.ts\";\n\ninterface InlineConfig {\n\toutput?: string | false;\n\toutputMode?: \"inline\" | \"file-only\";\n\treads?: string[] | false;\n\ttier?: \"light\" | \"standard\" | \"heavy\";\n\tpermissions?: \"full\" | \"read-only\";\n\tskill?: string[] | false;\n\tprogress?: boolean;\n\tas?: string;\n\tlabel?: string;\n\tphase?: string;\n\tcwd?: string;\n\tcount?: number;\n\toutputSchema?: string;\n\tacceptance?: string;\n}\n\nconst parseInlineConfig = (raw: string): InlineConfig => {\n\tconst config: InlineConfig = {};\n\tfor (const part of raw.split(\",\")) {\n\t\tconst trimmed = part.trim();\n\t\tif (!trimmed) continue;\n\t\tconst eq = trimmed.indexOf(\"=\");\n\t\tif (eq === -1) {\n\t\t\tif (trimmed === \"progress\") config.progress = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = trimmed.slice(0, eq).trim();\n\t\tconst val = trimmed.slice(eq + 1).trim();\n\t\tswitch (key) {\n\t\t\tcase \"output\": config.output = val === \"false\" ? false : val; break;\n\t\t\tcase \"outputMode\": if (val === \"inline\" || val === \"file-only\") config.outputMode = val; break;\n\t\t\tcase \"reads\": config.reads = val === \"false\" ? false : val.split(\"+\").filter(Boolean); break;\n\t\t\tcase \"tier\": if (val === \"light\" || val === \"standard\" || val === \"heavy\") config.tier = val; break;\n\t\t\tcase \"permissions\": if (val === \"full\" || val === \"read-only\") config.permissions = val; break;\n\t\t\tcase \"skill\": case \"skills\": config.skill = val === \"false\" ? false : val.split(\"+\").filter(Boolean); break;\n\t\t\tcase \"progress\": config.progress = val !== \"false\"; break;\n\t\t\tcase \"as\": config.as = val || undefined; break;\n\t\t\tcase \"label\": config.label = val || undefined; break;\n\t\t\tcase \"phase\": config.phase = val || undefined; break;\n\t\t\tcase \"cwd\": config.cwd = val || undefined; break;\n\t\t\tcase \"count\": { const n = Number(val); if (Number.isInteger(n) && n > 0) config.count = n; break; }\n\t\t\tcase \"outputSchema\": config.outputSchema = val || undefined; break;\n\t\t\tcase \"acceptance\": config.acceptance = val || undefined; break;\n\t\t}\n\t}\n\treturn config;\n};\n\nconst parseAgentToken = (token: string): { name: string; config: InlineConfig } => {\n\tconst bracket = token.indexOf(\"[\");\n\tif (bracket === -1) return { name: token, config: {} };\n\tconst end = token.lastIndexOf(\"]\");\n\treturn { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : undefined)) };\n};\n\nconst extractExecutionFlags = (rawArgs: string): { args: string; bg: boolean } => {\n\tlet args = rawArgs.trim();\n\tlet bg = false;\n\n\twhile (true) {\n\t\tif (args.endsWith(\" --bg\") || args === \"--bg\") {\n\t\t\tbg = true;\n\t\t\targs = args === \"--bg\" ? \"\" : args.slice(0, -5).trim();\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn { args, bg };\n};\n\nfunction sendSlashText(pi: ExtensionAPI, text: string): void {\n\tpi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: true });\n}\n\ntype Theme = ExtensionContext[\"ui\"][\"theme\"];\n\ntype StopSelectorTarget = {\n\tkind: \"async\" | \"scheduled\";\n\tid: string;\n\tlabel: string;\n\tdetail: string;\n\tactionLabel: string;\n};\n\ntype StopSelectorResult = { confirmed: boolean; target?: StopSelectorTarget };\n\nfunction commandForTarget(target: StopSelectorTarget): string {\n\treturn target.kind === \"scheduled\"\n\t\t? `subagent({ action: \"schedule-cancel\", id: ${JSON.stringify(target.id)} })`\n\t\t: `subagent({ action: \"stop\", id: ${JSON.stringify(target.id)} })`;\n}\n\nfunction formatAsyncStopTarget(run: AsyncRunSummary): StopSelectorTarget {\n\tconst progress = formatAsyncRunProgressLabel(run);\n\tconst cwd = run.cwd ? shortenPath(run.cwd) : shortenPath(run.asyncDir);\n\treturn {\n\t\tkind: \"async\",\n\t\tid: run.id,\n\t\tlabel: `${run.id} · ${run.mode} · ${progress}`,\n\t\tdetail: `${run.state} · ${cwd}`,\n\t\tactionLabel: \"stop async run\",\n\t};\n}\n\nfunction scheduledStopTargets(ctx: ExtensionContext, state: SubagentState): StopSelectorTarget[] {\n\tconst sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId();\n\tif (!sessionId) return [];\n\tconst storePath = scheduledRunStorePath(ctx.cwd, sessionId);\n\tif (!fs.existsSync(storePath)) return [];\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(fs.readFileSync(storePath, \"utf-8\"));\n\t} catch {\n\t\treturn [];\n\t}\n\tconst jobs = parsed && typeof parsed === \"object\" && Array.isArray((parsed as { jobs?: unknown }).jobs)\n\t\t? (parsed as { jobs: Array<Record<string, unknown>> }).jobs\n\t\t: [];\n\treturn jobs\n\t\t.filter((job) => job.state === \"scheduled\" && typeof job.id === \"string\" && typeof job.name === \"string\" && typeof job.runAt === \"number\")\n\t\t.sort((left, right) => Number(left.runAt) - Number(right.runAt))\n\t\t.map((job) => ({\n\t\t\tkind: \"scheduled\" as const,\n\t\t\tid: String(job.id),\n\t\t\tlabel: `${String(job.id)} · ${String(job.name)}`,\n\t\t\tdetail: `scheduled · ${new Date(Number(job.runAt)).toISOString()}`,\n\t\t\tactionLabel: \"cancel scheduled run\",\n\t\t}));\n}\n\nfunction discoverStopTargets(ctx: ExtensionContext, state: SubagentState): StopSelectorTarget[] {\n\tconst sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined;\n\tconst asyncTargets = listAsyncRuns(ASYNC_DIR, {\n\t\tstates: [\"queued\", \"running\"],\n\t\t...(sessionId ? { sessionId } : {}),\n\t}).map(formatAsyncStopTarget);\n\treturn [...asyncTargets, ...scheduledStopTargets(ctx, state)];\n}\n\nfunction stopFallbackText(targets: StopSelectorTarget[]): string {\n\tif (targets.length === 0) return \"No active current-session async runs or scheduled subagent runs to stop.\";\n\tconst lines = [\"Subagent stop targets:\", \"\"];\n\tfor (const target of targets) {\n\t\tlines.push(`- ${target.label}`);\n\t\tlines.push(` ${target.detail}`);\n\t\tlines.push(` ${target.actionLabel}: ${commandForTarget(target)}`);\n\t\tif (target.kind === \"async\") lines.push(` slash: /subagents-stop ${target.id}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nclass SubagentsStopSelector implements Component {\n\treadonly width = 84;\n\tprivate selected = 0;\n\tprivate confirming = false;\n\tprivate readonly tui: TUI;\n\tprivate readonly theme: Theme;\n\tprivate readonly targets: StopSelectorTarget[];\n\tprivate readonly done: (result: StopSelectorResult) => void;\n\n\tconstructor(tui: TUI, theme: Theme, targets: StopSelectorTarget[], done: (result: StopSelectorResult) => void) {\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.targets = targets;\n\t\tthis.done = done;\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (matchesKey(data, \"escape\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.done({ confirmed: false });\n\t\t\treturn;\n\t\t}\n\t\tif (this.confirming) {\n\t\t\tif (matchesKey(data, \"return\") || data.toLowerCase() === \"y\") {\n\t\t\t\tthis.done({ confirmed: true, target: this.targets[this.selected] });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (data.toLowerCase() === \"n\" || matchesKey(data, \"backspace\")) {\n\t\t\t\tthis.confirming = false;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"up\")) {\n\t\t\tthis.selected = Math.max(0, this.selected - 1);\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"down\")) {\n\t\t\tthis.selected = Math.min(this.targets.length - 1, this.selected + 1);\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"return\")) {\n\t\t\tthis.confirming = true;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst contentWidth = Math.max(40, Math.min(this.width, width || this.width));\n\t\tconst lines = [this.theme.bold(\"Stop subagent run\"), this.theme.fg(\"dim\", \"Select a current-session async run to stop, or a scheduled run to cancel.\"), \"\"];\n\t\tconst maxRows = 10;\n\t\tconst start = Math.max(0, Math.min(this.selected - maxRows + 1, Math.max(0, this.targets.length - maxRows)));\n\t\tfor (let index = start; index < Math.min(this.targets.length, start + maxRows); index++) {\n\t\t\tconst target = this.targets[index]!;\n\t\t\tconst selected = index === this.selected;\n\t\t\tconst marker = selected ? \"›\" : \" \";\n\t\t\t\tconst actionLabel = target.actionLabel;\n\t\t\tconst action = target.kind === \"scheduled\" ? this.theme.fg(\"warning\", actionLabel) : this.theme.fg(\"accent\", actionLabel);\n\t\t\tconst labelWidth = Math.max(0, contentWidth - marker.length - actionLabel.length - 2);\n\t\t\tlines.push(`${marker} ${action} ${target.label.slice(0, labelWidth)}`);\n\t\t\tif (selected) lines.push(this.theme.fg(\"dim\", ` ${target.detail}`.slice(0, contentWidth)));\n\t\t}\n\t\tif (this.targets.length > maxRows) lines.push(this.theme.fg(\"dim\", `Showing ${start + 1}-${Math.min(this.targets.length, start + maxRows)} of ${this.targets.length}`));\n\t\tlines.push(\"\");\n\t\tif (this.confirming) {\n\t\t\tconst target = this.targets[this.selected]!;\n\t\t\tlines.push(this.theme.fg(\"warning\", `Confirm: ${target.actionLabel} ${target.id}?`));\n\t\t\tif (target.kind === \"async\") lines.push(this.theme.fg(\"dim\", \"Stop ends this run; use interrupt for a resumable pause.\"));\n\t\t\tlines.push(this.theme.fg(\"dim\", \"Enter/Y confirms · N returns · Esc cancels\"));\n\t\t} else {\n\t\t\tlines.push(this.theme.fg(\"dim\", \"↑/↓ select · Enter confirm · Esc cancel\"));\n\t\t}\n\t\treturn lines;\n\t}\n}\n\nfunction emptyUsage(): Usage {\n\treturn { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };\n}\n\nfunction addUsage(target: Usage, source: Usage): void {\n\ttarget.input += source.input;\n\ttarget.output += source.output;\n\ttarget.cacheRead += source.cacheRead;\n\ttarget.cacheWrite += source.cacheWrite;\n\ttarget.cost += source.cost;\n\ttarget.turns += source.turns;\n}\n\nfunction usageHasValue(usage: Usage): boolean {\n\treturn usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0;\n}\n\nfunction assistantUsageFromMessage(message: unknown): Usage | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst msg = message as { role?: unknown; usage?: unknown };\n\tif (msg.role !== \"assistant\" || !msg.usage || typeof msg.usage !== \"object\") return undefined;\n\tconst usage = msg.usage as {\n\t\tinput?: unknown;\n\t\toutput?: unknown;\n\t\tcacheRead?: unknown;\n\t\tcacheWrite?: unknown;\n\t\tcost?: { total?: unknown };\n\t};\n\treturn {\n\t\tinput: typeof usage.input === \"number\" ? usage.input : 0,\n\t\toutput: typeof usage.output === \"number\" ? usage.output : 0,\n\t\tcacheRead: typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0,\n\t\tcacheWrite: typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0,\n\t\tcost: typeof usage.cost?.total === \"number\" ? usage.cost.total : 0,\n\t\tturns: 1,\n\t};\n}\n\nfunction isSubagentDetails(value: unknown): value is Details {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst details = value as { mode?: unknown; results?: unknown };\n\treturn typeof details.mode === \"string\" && Array.isArray(details.results);\n}\n\nfunction detailsFromSessionEntry(entry: unknown): Details | undefined {\n\tif (!entry || typeof entry !== \"object\") return undefined;\n\tconst record = entry as { type?: unknown; customType?: unknown; details?: unknown; message?: unknown };\n\tif (record.type === \"custom_message\" && record.customType === SLASH_RESULT_TYPE) {\n\t\tconst details = resolveSlashMessageDetails(record.details)?.result.details;\n\t\treturn isSubagentDetails(details) ? details : undefined;\n\t}\n\tif (record.type !== \"message\" || !record.message || typeof record.message !== \"object\") return undefined;\n\tconst message = record.message as { role?: unknown; toolName?: unknown; details?: unknown };\n\tif (message.role !== \"toolResult\" || message.toolName !== \"subagent\") return undefined;\n\treturn isSubagentDetails(message.details) ? message.details : undefined;\n}\n\nfunction formatCostUsage(label: string, usage: Usage): string {\n\tconst extras = [\n\t\tusage.cacheRead ? `cache read ${formatTokens(usage.cacheRead)}` : \"\",\n\t\tusage.cacheWrite ? `cache write ${formatTokens(usage.cacheWrite)}` : \"\",\n\t\tusage.turns ? `${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}` : \"\",\n\t].filter(Boolean);\n\treturn `${label}: ↑${formatTokens(usage.input)} ↓${formatTokens(usage.output)} $${usage.cost.toFixed(4)}${extras.length ? ` (${extras.join(\", \")})` : \"\"}`;\n}\n\nfunction buildSubagentCostReport(ctx: ExtensionContext): string {\n\tconst parent = emptyUsage();\n\tconst childTotal = emptyUsage();\n\tconst total = emptyUsage();\n\tconst children: Array<{ label: string; usage: Usage; sessionFile?: string }> = [];\n\tfor (const entry of ctx.sessionManager.getBranch()) {\n\t\tconst message = entry.type === \"message\" ? (entry as { message?: unknown }).message : undefined;\n\t\tconst parentUsage = assistantUsageFromMessage(message);\n\t\tif (parentUsage) addUsage(parent, parentUsage);\n\t\tconst details = detailsFromSessionEntry(entry);\n\t\tif (!details) continue;\n\t\tfor (const result of details.results) {\n\t\t\tif (!usageHasValue(result.usage)) continue;\n\t\t\tconst usage = { ...result.usage };\n\t\t\tchildren.push({\n\t\t\t\tlabel: `Child ${children.length + 1} (${result.agent})`,\n\t\t\t\tusage,\n\t\t\t\t...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),\n\t\t\t});\n\t\t\taddUsage(childTotal, usage);\n\t\t}\n\t}\n\taddUsage(total, parent);\n\taddUsage(total, childTotal);\n\tconst lines = [\n\t\t\"Subagent cost\",\n\t\t\"\",\n\t\tformatCostUsage(\"Parent\", parent),\n\t];\n\tif (children.length === 0) {\n\t\tlines.push(\"No subagent child usage found in this session.\");\n\t} else {\n\t\tfor (const child of children) {\n\t\t\tlines.push(formatCostUsage(child.label, child.usage));\n\t\t\tif (child.sessionFile) lines.push(` Session: ${child.sessionFile}`);\n\t\t}\n\t}\n\tlines.push(\"────────────────────────────\", formatCostUsage(\"Children\", childTotal), formatCostUsage(\"Total\", total));\n\treturn lines.join(\"\\n\");\n}\n\nasync function requestSlashRun(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\trequestId: string,\n\tparams: SubagentParamsLike,\n): Promise<SlashSubagentResponse> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet done = false;\n\t\tlet started = false;\n\n\t\tconst startTimeoutMs = 15_000;\n\t\tconst startTimeout = setTimeout(() => {\n\t\t\tfinish(() => reject(new Error(\n\t\t\t\t\"Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.\",\n\t\t\t)));\n\t\t}, startTimeoutMs);\n\n\t\tconst onStarted = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tif ((data as { requestId?: unknown }).requestId !== requestId) return;\n\t\t\tstarted = true;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tif (ctx.hasUI) ctx.ui.setStatus(\"subagent-slash\", \"running...\");\n\t\t};\n\n\t\tconst onResponse = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tconst response = data as Partial<SlashSubagentResponse>;\n\t\t\tif (response.requestId !== requestId) return;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tfinish(() => resolve(response as SlashSubagentResponse));\n\t\t};\n\n\t\tconst onUpdate = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tconst update = data as SlashSubagentUpdate;\n\t\t\tif (update.requestId !== requestId) return;\n\t\t\tapplySlashUpdate(requestId, update);\n\t\t\tif (!ctx.hasUI) return;\n\t\t\tconst tool = update.currentTool ? ` ${update.currentTool}` : \"\";\n\t\t\tconst count = update.toolCount ?? 0;\n\t\t\tconst liveDetailKey = keyText(\"app.tools.expand\");\n\t\t\tctx.ui.setStatus(\"subagent-slash\", `${count} tools${tool} | ${liveDetailKey} live detail`);\n\t\t};\n\n\t\tconst onTerminalInput = ctx.hasUI\n\t\t\t? ctx.ui.onTerminalInput((input) => {\n\t\t\t\tif (!matchesKey(input, Key.escape)) return undefined;\n\t\t\t\tpi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });\n\t\t\t\tfinish(() => reject(new Error(\"Cancelled\")));\n\t\t\t\treturn { consume: true };\n\t\t\t})\n\t\t\t: undefined;\n\n\t\tconst unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);\n\t\tconst unsubResponse = pi.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);\n\t\tconst unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);\n\n\t\tconst finish = (next: () => void) => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tunsubStarted();\n\t\t\tunsubResponse();\n\t\t\tunsubUpdate();\n\t\t\tonTerminalInput?.();\n\t\t\tnext();\n\t\t};\n\n\t\tpi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params, ctx });\n\n\t\t// Bridge emits STARTED synchronously during REQUEST emit.\n\t\t// If not started, no bridge received the request.\n\t\tif (!started && done) return;\n\t\tif (!started) {\n\t\t\tfinish(() => reject(new Error(\n\t\t\t\t\"No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.\",\n\t\t\t)));\n\t\t}\n\t});\n}\n\nfunction extractSlashMessageText(content: string | Array<{ type?: string; text?: string }>): string {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part?.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\\n\");\n}\n\nfunction formatExportPathList(paths: string[]): string {\n\treturn paths.map((file) => `- \\`${file}\\``).join(\"\\n\");\n}\n\nfunction collectResultPaths(results: SingleResult[], getPath: (result: SingleResult) => string | undefined): string[] {\n\treturn results\n\t\t.map(getPath)\n\t\t.filter((file): file is string => typeof file === \"string\" && file.length > 0);\n}\n\nfunction buildSlashExportText(response: SlashSubagentResponse): string {\n\tconst output = extractSlashMessageText(response.result.content) || response.errorText || \"(no output)\";\n\tconst results = response.result.details?.results ?? [];\n\tconst sessionFiles = collectResultPaths(results, (result) => result.sessionFile);\n\tconst savedOutputs = collectResultPaths(results, (result) => result.savedOutputPath);\n\tconst artifactOutputs = collectResultPaths(results, (result) => result.artifactPaths?.outputPath);\n\tconst sections = [\"## Subagent result\", output];\n\tif (sessionFiles.length > 0) sections.push(\"## Child session exports\", formatExportPathList(sessionFiles));\n\tif (savedOutputs.length > 0) sections.push(\"## Saved outputs\", formatExportPathList(savedOutputs));\n\tif (artifactOutputs.length > 0) sections.push(\"## Artifact outputs\", formatExportPathList(artifactOutputs));\n\treturn sections.join(\"\\n\\n\");\n}\n\nfunction persistSlashSessionSnapshot(ctx: ExtensionContext): void {\n\ttry {\n\t\tif (!ctx.sessionManager) return;\n\t\tconst sessionManager = ctx.sessionManager as typeof ctx.sessionManager & {\n\t\t\t_rewriteFile?: () => void;\n\t\t\tflushed?: boolean;\n\t\t};\n\t\tconst sessionFile = sessionManager.getSessionFile();\n\t\tif (!sessionFile || typeof sessionManager._rewriteFile !== \"function\") return;\n\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\tsessionManager._rewriteFile();\n\t\tsessionManager.flushed = true;\n\t} catch (error) {\n\t\tconsole.error(\"Failed to persist slash session snapshot for export:\", error);\n\t}\n}\n\nasync function runSlashSubagent(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tparams: SubagentParamsLike,\n): Promise<void> {\n\tif (ctx.hasUI) ctx.ui.setToolsExpanded(false);\n\tconst requestId = randomUUID();\n\tconst initialDetails = buildSlashInitialResult(requestId, params);\n\tconst initialText = extractSlashMessageText(initialDetails.result.content) || \"Running subagent...\";\n\tpi.sendMessage({\n\t\tcustomType: SLASH_RESULT_TYPE,\n\t\tcontent: initialText,\n\t\tdisplay: true,\n\t\tdetails: initialDetails,\n\t});\n\tpersistSlashSessionSnapshot(ctx);\n\n\ttry {\n\t\tconst response = await requestSlashRun(pi, ctx, requestId, params);\n\t\tconst finalDetails = finalizeSlashResult(response);\n\t\tpi.sendMessage({\n\t\t\tcustomType: SLASH_RESULT_TYPE,\n\t\t\tcontent: buildSlashExportText(response),\n\t\t\tdisplay: !ctx.hasUI,\n\t\t\tdetails: finalDetails,\n\t\t});\n\t\tpersistSlashSessionSnapshot(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setStatus(\"subagent-slash\", undefined);\n\t\t}\n\t\tif (response.isError && ctx.hasUI) {\n\t\t\tctx.ui.notify(response.errorText || \"Subagent failed\", \"error\");\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst failedDetails = failSlashResult(requestId, params, message);\n\t\tpi.sendMessage({\n\t\t\tcustomType: SLASH_RESULT_TYPE,\n\t\t\tcontent: `## Subagent result\\n\\n${message}`,\n\t\t\tdisplay: !ctx.hasUI,\n\t\t\tdetails: failedDetails,\n\t\t});\n\t\tpersistSlashSessionSnapshot(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setStatus(\"subagent-slash\", undefined);\n\t\t}\n\t\tif (message === \"Cancelled\") {\n\t\t\tif (ctx.hasUI) ctx.ui.notify(\"Cancelled\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tif (ctx.hasUI) ctx.ui.notify(message, \"error\");\n\t}\n}\n\n\nexport interface GroupConfig { concurrency?: number; failFast?: boolean; worktree?: boolean }\nexport interface ParsedStep { kind: \"step\"; name: string; config: InlineConfig; task?: string }\nexport interface ParsedGroup { kind: \"group\"; tasks: ParsedStep[]; config: GroupConfig }\nexport type ParsedGroupStep = ParsedStep | ParsedGroup;\n\nexport const PARALLEL_GROUP_USAGE =\n\t'Usage: /chain agent \"task\" -> (agent2 \"task\" | agent3 \"task\") -> agent4';\n\nexport class SlashParseError extends Error {}\n\n// Walk `input` tracking quote/paren state; returns true if parens are unbalanced.\nfunction findUnmatchedCloseParen(input: string): boolean {\n\tlet depth = 0, inSingle = false, inDouble = false;\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") { depth--; if (depth < 0) return true; }\n\t}\n\treturn depth !== 0;\n}\n\n// Split on top-level \" -> \", ignoring arrows inside quotes or parentheses.\nfunction splitOnArrow(input: string): string[] {\n\tconst segments: string[] = [];\n\tlet depth = 0, inSingle = false, inDouble = false, start = 0;\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth--;\n\t\telse if (depth === 0 && ch === \"-\" && input[i + 1] === \">\" && input[i + 2] === \" \") {\n\t\t\tsegments.push(input.slice(start, i));\n\t\t\ti += 2;\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\tsegments.push(input.slice(start));\n\treturn segments;\n}\n\n// Split a group's inner text on top-level \" | \", ignoring pipes inside quotes/parens.\nfunction splitGroupTasks(inner: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0, inSingle = false, inDouble = false, start = 0;\n\tfor (let i = 0; i < inner.length; i++) {\n\t\tconst ch = inner[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") depth--;\n\t\telse if (ch === \"|\" && depth === 0) {\n\t\t\tparts.push(inner.slice(start, i));\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\tparts.push(inner.slice(start));\n\treturn parts;\n}\n\nexport function parseSingleTaskToken(token: string): ParsedStep {\n\tlet agentPart: string;\n\tlet task: string | undefined;\n\tconst qMatch = token.match(/^(\\S+(?:\\[[^\\]]*\\])?)\\s+(?:\"([^\"]*)\"|'([^']*)')$/);\n\tif (qMatch) {\n\t\tagentPart = qMatch[1]!;\n\t\ttask = (qMatch[2] ?? qMatch[3]) || undefined;\n\t} else {\n\t\tconst dashIdx = token.indexOf(\" -- \");\n\t\tif (dashIdx !== -1) {\n\t\t\tagentPart = token.slice(0, dashIdx).trim();\n\t\t\ttask = token.slice(dashIdx + 4).trim() || undefined;\n\t\t} else {\n\t\t\tagentPart = token;\n\t\t}\n\t}\n\treturn { kind: \"step\", ...parseAgentToken(agentPart), task };\n}\n\nconst parseGroupConfig = (raw: string): GroupConfig => {\n\tconst config: GroupConfig = {};\n\tfor (const part of raw.split(\",\")) {\n\t\tconst trimmed = part.trim();\n\t\tif (!trimmed) continue;\n\t\tconst eq = trimmed.indexOf(\"=\");\n\t\tconst key = eq === -1 ? trimmed : trimmed.slice(0, eq).trim();\n\t\tconst val = eq === -1 ? \"\" : trimmed.slice(eq + 1).trim();\n\t\tswitch (key) {\n\t\t\tcase \"concurrency\": { const n = Number(val); if (Number.isInteger(n) && n > 0) config.concurrency = n; break; }\n\t\t\tcase \"failFast\": config.failFast = eq === -1 ? true : val !== \"false\"; break;\n\t\t\tcase \"worktree\": config.worktree = eq === -1 ? true : val !== \"false\"; break;\n\t\t}\n\t}\n\treturn config;\n};\n\n// Split `(...)` from an optional trailing `[...]` group-config suffix, respecting\n// quotes and nested parens. Returns the inner group text and the parsed config.\nconst splitGroupBody = (trimmed: string): { inner: string; config: GroupConfig } => {\n\tlet depth = 0, inSingle = false, inDouble = false, closeIdx = -1;\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (inSingle) { if (ch === \"'\") inSingle = false; continue; }\n\t\tif (inDouble) { if (ch === '\"') inDouble = false; continue; }\n\t\tif (ch === \"'\") { inSingle = true; continue; }\n\t\tif (ch === '\"') { inDouble = true; continue; }\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") { depth--; if (depth === 0) { closeIdx = i; break; } }\n\t}\n\tif (closeIdx === -1) throw new SlashParseError(`Unmatched parentheses in group: '${trimmed}'`);\n\tconst inner = trimmed.slice(1, closeIdx);\n\tconst suffix = trimmed.slice(closeIdx + 1).trim();\n\tif (!suffix) return { inner, config: {} };\n\tif (!suffix.startsWith(\"[\") || !suffix.endsWith(\"]\")) {\n\t\tthrow new SlashParseError(`Group options must be wrapped in [...]: '${suffix}'`);\n\t}\n\treturn { inner, config: parseGroupConfig(suffix.slice(1, -1)) };\n};\n\nexport function parseGroupSegment(segment: string): ParsedGroup {\n\tconst trimmed = segment.trim();\n\tif (!trimmed.startsWith(\"(\")) {\n\t\tthrow new SlashParseError(`Parallel group must be wrapped in parentheses: '${trimmed}'`);\n\t}\n\tconst { inner, config } = splitGroupBody(trimmed);\n\tconst rawParts = splitGroupTasks(inner).map((p) => p.trim()).filter((p) => p.length > 0);\n\tif (rawParts.length < 2) {\n\t\tthrow new SlashParseError(\"Parallel group must contain at least two tasks separated by ' | '\");\n\t}\n\treturn { kind: \"group\", tasks: rawParts.map((part) => parseSingleTaskToken(part)), config };\n}\n\n// True if `input` uses inline parallel-group syntax. A group is a *step* that begins\n// with `(` at the top level, so we split on top-level ` -> ` arrows and look for a\n// segment that opens with `(`. Parentheses appearing inside a task (e.g.\n// `scout -- inspect auth (backend)`) do not count, which keeps legacy shared-task\n// `-- task` parsing — including a bare `|` inside the task — on the single-agent path.\nexport function hasGroupSyntax(input: string): boolean {\n\treturn splitOnArrow(input).some((seg) => seg.trim().startsWith(\"(\"));\n}\n\nexport function parseChainExpression(input: string): { steps: ParsedGroupStep[] } {\n\tconst trimmed = input.trim();\n\tif (!trimmed.includes(\" -> \")) {\n\t\tthrow new SlashParseError('Parallel groups in /chain require \" -> \" between steps');\n\t}\n\tif (findUnmatchedCloseParen(trimmed)) {\n\t\tthrow new SlashParseError(\"Unmatched parentheses in /chain expression\");\n\t}\n\tconst steps: ParsedGroupStep[] = [];\n\tfor (const seg of splitOnArrow(trimmed)) {\n\t\tconst t = seg.trim();\n\t\tif (!t) continue;\n\t\tif (t.startsWith(\"(\")) {\n\t\t\tsteps.push(parseGroupSegment(t));\n\t\t\tcontinue;\n\t\t}\n\t\tif (findUnmatchedCloseParen(t)) {\n\t\t\tthrow new SlashParseError(`Unmatched parentheses in chain segment: '${t}'`);\n\t\t}\n\t\tsteps.push(parseSingleTaskToken(t));\n\t}\n\tif (steps.length === 0) {\n\t\tthrow new SlashParseError(\"/chain expression must include at least one step\");\n\t}\n\treturn { steps };\n}\n\nconst parseAgentArgs = (\n\tstate: SubagentState,\n\targs: string,\n\tcommand: string,\n\tctx: ExtensionContext,\n): { steps: ParsedStep[]; task: string } | null => {\n\tconst input = args.trim();\n\tconst usage = `Usage: /${command} description1 \"task1\" -> description2 \"task2\"`;\n\tlet steps: ParsedStep[];\n\tlet sharedTask: string;\n\tlet perStep = false;\n\n\tif (input.includes(\" -> \")) {\n\t\tperStep = true;\n\t\tconst segments = input.split(\" -> \");\n\t\tsteps = [];\n\t\tfor (const seg of segments) {\n\t\t\tconst trimmed = seg.trim();\n\t\t\tif (!trimmed) continue;\n\t\t\tsteps.push(parseSingleTaskToken(trimmed));\n\t\t}\n\t\tsharedTask = steps.find((s) => s.task)?.task ?? \"\";\n\t} else {\n\t\tconst delimiterIndex = input.indexOf(\" -- \");\n\t\tif (delimiterIndex === -1) {\n\t\t\tctx.ui.notify(usage, \"error\");\n\t\t\treturn null;\n\t\t}\n\t\tconst agentsPart = input.slice(0, delimiterIndex).trim();\n\t\tsharedTask = input.slice(delimiterIndex + 4).trim();\n\t\tif (!agentsPart || !sharedTask) {\n\t\t\tctx.ui.notify(usage, \"error\");\n\t\t\treturn null;\n\t\t}\n\t\tsteps = agentsPart.split(/\\s+/).filter(Boolean).map((t) => parseSingleTaskToken(t));\n\t}\n\n\tif (steps.length === 0) {\n\t\tctx.ui.notify(usage, \"error\");\n\t\treturn null;\n\t}\n\tif (!state.baseCwd) {\n\t\tctx.ui.notify(\"Subagent session cwd is not initialized yet\", \"error\");\n\t\treturn null;\n\t}\n\tif (command === \"chain\" && !steps[0]?.task && (perStep || !sharedTask)) {\n\t\tctx.ui.notify(`First step must have a task: /chain description \"task\" -> description2`, \"error\");\n\t\treturn null;\n\t}\n\tif (command === \"parallel\" && !steps.some((s) => s.task) && !sharedTask) {\n\t\tctx.ui.notify(\"At least one step must have a task\", \"error\");\n\t\treturn null;\n\t}\n\treturn { steps, task: sharedTask };\n};\n\ntype ChainStepObject = {\n\tdescription: string;\n\tpermissions: \"full\" | \"read-only\";\n\ttier: \"light\" | \"standard\" | \"heavy\";\n\ttask?: string;\n\toutput?: string | false;\n\toutputMode?: \"inline\" | \"file-only\";\n\treads?: string[] | false;\n\tskill?: string[] | false;\n\tprogress?: boolean;\n\tas?: string;\n\tlabel?: string;\n\tphase?: string;\n\tcwd?: string;\n\tcount?: number;\n\toutputSchema?: JsonSchemaObject;\n\tacceptance?: string;\n};\n\nconst INLINE_ACCEPTANCE_LEVELS = new Set([\"auto\", \"attested\", \"checked\"]);\n\nfunction validateInlineAcceptanceInput(value: string, agent: string): void {\n\tconst errors = validateAcceptanceInput(value, `acceptance for step '${agent}'`);\n\tif (errors.length > 0) throw new SlashParseError(errors[0]!);\n\tif (!INLINE_ACCEPTANCE_LEVELS.has(value)) {\n\t\tthrow new SlashParseError(`Inline acceptance for step '${agent}' supports auto, attested, or checked. Use the subagent tool API or a saved .chain.json file for none or verified acceptance contracts; reviewed is inferred-only.`);\n\t}\n}\n\n// Load an inline `outputSchema=<path>` JSON file, resolved against the session cwd.\n// Throws (SlashParseError / fs / JSON) on a missing or malformed schema.\nfunction loadInlineOutputSchema(baseCwd: string, agent: string, value: string): JsonSchemaObject {\n\tconst schemaPath = path.isAbsolute(value) ? value : path.join(baseCwd, value);\n\tconst label = `outputSchema for step '${agent}' (${schemaPath})`;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(fs.readFileSync(schemaPath, \"utf-8\"));\n\t} catch (error) {\n\t\tthrow new SlashParseError(`Cannot read ${label}: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tassertJsonSchemaObject(parsed, label);\n\treturn parsed;\n}\n\n// Build a ChainStep object from a parsed token. `inGroup` enables `count` (parallel-only).\n// May throw SlashParseError for an invalid acceptance level or outputSchema path.\nconst mapParsedTaskToStepObject = (\n\tstep: ParsedStep,\n\tfallbackTask: string | undefined,\n\tisFirst: boolean,\n\topts: { baseCwd: string; inGroup: boolean },\n): ChainStepObject => {\n\tconst { name, config, task: stepTask } = step;\n\tif (!config.tier) throw new SlashParseError(`Step '${name}' requires tier=light, tier=standard, or tier=heavy.`);\n\tif (config.acceptance !== undefined) validateInlineAcceptanceInput(config.acceptance, name);\n\treturn {\n\t\tdescription: name,\n\t\tpermissions: config.permissions ?? \"full\",\n\t\ttier: config.tier,\n\t\t...(stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {}),\n\t\t...(config.output !== undefined ? { output: config.output } : {}),\n\t\t...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),\n\t\t...(config.reads !== undefined ? { reads: config.reads } : {}),\n\t\t...(config.skill !== undefined ? { skill: config.skill } : {}),\n\t\t...(config.progress !== undefined ? { progress: config.progress } : {}),\n\t\t...(config.as ? { as: config.as } : {}),\n\t\t...(config.label ? { label: config.label } : {}),\n\t\t...(config.phase ? { phase: config.phase } : {}),\n\t\t...(config.cwd ? { cwd: config.cwd } : {}),\n\t\t...(opts.inGroup && config.count !== undefined ? { count: config.count } : {}),\n\t\t...(config.outputSchema ? { outputSchema: loadInlineOutputSchema(opts.baseCwd, name, config.outputSchema) } : {}),\n\t\t...(config.acceptance ? { acceptance: config.acceptance } : {}),\n\t};\n};\n\nexport function buildChainExpressionSteps(\n\tstate: SubagentState,\n\tinput: string,\n\tctx: ExtensionContext,\n): { chain: ChainStep[]; task: string } | null {\n\tconst notify = (message: string) => ctx.ui.notify(message, \"error\");\n\tif (!hasGroupSyntax(input)) {\n\t\tconst parsed = parseAgentArgs(state, input, \"chain\", ctx);\n\t\tif (!parsed) return null;\n\t\tconst baseCwd = state.baseCwd!; // parseAgentArgs already verified baseCwd is set\n\t\ttry {\n\t\t\tconst chain: ChainStep[] = parsed.steps.map((step, i) =>\n\t\t\t\tmapParsedTaskToStepObject(step, parsed.task || undefined, i === 0, { baseCwd, inGroup: false }),\n\t\t\t);\n\t\t\treturn { chain, task: parsed.task };\n\t\t} catch (error) {\n\t\t\tnotify(error instanceof Error ? error.message : String(error));\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tlet expression: { steps: ParsedGroupStep[] };\n\ttry {\n\t\texpression = parseChainExpression(input);\n\t} catch (error) {\n\t\tnotify(error instanceof Error ? error.message : String(error));\n\t\treturn null;\n\t}\n\tif (!state.baseCwd) {\n\t\tnotify(\"Subagent session cwd is not initialized yet\");\n\t\treturn null;\n\t}\n\t// Every task inside a parallel group needs its own task; there is no shared-task fallback.\n\tfor (const step of expression.steps) {\n\t\tif (step.kind === \"group\" && step.tasks.some((t) => !t.task)) {\n\t\t\tnotify('Each child in a parallel group needs a task: (description \"a\" | description2 \"b\")');\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst firstStep = expression.steps[0]!;\n\tconst firstHasTask =\n\t\tfirstStep.kind === \"group\"\n\t\t\t? firstStep.tasks.some((t) => Boolean(t.task))\n\t\t\t: Boolean(firstStep.task);\n\tif (!firstHasTask) {\n\t\tnotify('First step must have a task: /chain description \"task\" -> description2');\n\t\treturn null;\n\t}\n\tconst sharedTask =\n\t\tfirstStep.kind === \"group\"\n\t\t\t? (firstStep.tasks.find((t) => t.task)?.task ?? \"\")\n\t\t\t: (firstStep.task ?? \"\");\n\tconst baseCwd = state.baseCwd;\n\tlet chain: ChainStep[];\n\ttry {\n\t\tchain = expression.steps.map((step) => {\n\t\t\tif (step.kind === \"group\") {\n\t\t\t\tconst parallel = step.tasks.map((t) => mapParsedTaskToStepObject(t, undefined, false, { baseCwd, inGroup: true }));\n\t\t\t\treturn {\n\t\t\t\t\tparallel,\n\t\t\t\t\t...(step.config.concurrency !== undefined ? { concurrency: step.config.concurrency } : {}),\n\t\t\t\t\t...(step.config.failFast !== undefined ? { failFast: step.config.failFast } : {}),\n\t\t\t\t\t...(step.config.worktree !== undefined ? { worktree: step.config.worktree } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn mapParsedTaskToStepObject(step, sharedTask || undefined, false, { baseCwd, inGroup: false });\n\t\t});\n\t} catch (error) {\n\t\tnotify(error instanceof Error ? error.message : String(error));\n\t\treturn null;\n\t}\n\treturn { chain, task: sharedTask };\n}\n\nexport function registerSlashCommands(\n\tpi: ExtensionAPI,\n\tstate: SubagentState,\n): void {\n\tlet fleetOpen = false;\n\tconst showFleet = async (ctx: ExtensionContext) => {\n\t\tstate.lastUiContext = ctx;\n\t\tif (!ctx.hasUI) {\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"status\", view: \"fleet\" });\n\t\t\treturn;\n\t\t}\n\t\tif (fleetOpen) {\n\t\t\tctx.ui.notify(\"Subagent fleet inspector is already open.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tfleetOpen = true;\n\t\ttry {\n\t\t\tawait openSubagentFleet(ctx, state);\n\t\t} finally {\n\t\t\tfleetOpen = false;\n\t\t}\n\t};\n\n\tpi.registerCommand(\"run\", {\n\t\tdescription: \"Run a prompt-defined child: /run description[tier=standard] task [--bg]\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg } = extractExecutionFlags(args);\n\t\t\tconst input = cleanedArgs.trim();\n\t\t\tconst firstSpace = input.indexOf(\" \");\n\t\t\tif (!input) { ctx.ui.notify(\"Usage: /run <description> <task> [--bg]\", \"error\"); return; }\n\t\t\tconst { name: description, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace));\n\t\t\tconst task = firstSpace === -1 ? \"\" : input.slice(firstSpace + 1).trim();\n\t\t\tif (!description || !task || !inline.tier) { ctx.ui.notify(\"Usage: /run <description>[tier=light|standard|heavy] <task> [--bg]\", \"error\"); return; }\n\n\t\t\tlet finalTask = task;\n\t\t\tif (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {\n\t\t\t\tfinalTask = `[Read from: ${inline.reads.join(\", \")}]\\n\\n${finalTask}`;\n\t\t\t}\n\t\t\tconst params: SubagentParamsLike = { description, permissions: inline.permissions ?? \"full\", tier: inline.tier, task: finalTask, clarify: false };\n\t\t\tif (inline.output !== undefined) params.output = inline.output;\n\t\t\tif (inline.outputMode !== undefined) params.outputMode = inline.outputMode;\n\t\t\tif (inline.skill !== undefined) params.skill = inline.skill;\n\t\t\tif (bg) params.async = true;\n\t\t\tawait runSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"chain\", {\n\t\tdescription: \"Run prompt-defined children in sequence: /chain inspect \\\"task\\\" -> implement [--bg]\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg } = extractExecutionFlags(args);\n\t\t\tconst built = buildChainExpressionSteps(state, cleanedArgs, ctx);\n\t\t\tif (!built) return;\n\t\t\tconst params: SubagentParamsLike = { chain: built.chain, task: built.task, clarify: false };\n\t\t\tif (bg) params.async = true;\n\t\t\tawait runSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"parallel\", {\n\t\tdescription: \"Run prompt-defined children in parallel: /parallel inspect \\\"task1\\\" -> review \\\"task2\\\" [--bg]\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg } = extractExecutionFlags(args);\n\t\t\tconst parsed = parseAgentArgs(state, cleanedArgs, \"parallel\", ctx);\n\t\t\tif (!parsed) return;\n\t\t\tconst tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({\n\t\t\t\tdescription: name,\n\t\t\t\tpermissions: config.permissions ?? \"full\" as const,\n\t\t\t\ttier: config.tier,\n\t\t\t\ttask: stepTask ?? parsed.task,\n\t\t\t\t...(config.output !== undefined ? { output: config.output } : {}),\n\t\t\t\t...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),\n\t\t\t\t...(config.reads !== undefined ? { reads: config.reads } : {}),\n\t\t\t\t...(config.skill !== undefined ? { skill: config.skill } : {}),\n\t\t\t\t...(config.progress !== undefined ? { progress: config.progress } : {}),\n\t\t\t}));\n\t\t\tif (tasks.some((task) => !task.tier)) { ctx.ui.notify(\"Every parallel child requires [tier=light|standard|heavy].\", \"error\"); return; }\n\t\t\tconst params: SubagentParamsLike = { tasks, clarify: false };\n\t\t\tif (bg) params.async = true;\n\t\t\tawait runSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagent-cost\", {\n\t\tdescription: \"Show parent and subagent child usage cost for this session\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tsendSlashText(pi, buildSubagentCostReport(ctx));\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-doctor\", {\n\t\tdescription: \"Show subagent diagnostics\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"doctor\" });\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-fleet\", {\n\t\tdescription: \"Open the live, inspection-only subagent fleet\",\n\t\thandler: async (_args, ctx) => showFleet(ctx),\n\t});\n\n\tpi.registerShortcut(Key.ctrlAlt(\"f\"), {\n\t\tdescription: \"Open subagent fleet inspector\",\n\t\thandler: async (ctx) => showFleet(ctx),\n\t});\n\n\tpi.registerCommand(\"subagents-stop\", {\n\t\tdescription: \"Stop a current-session async subagent run\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst id = args.trim();\n\t\t\tif (id) {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"stop\", id });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (process.env[SUBAGENT_FANOUT_CHILD_ENV] === \"1\") {\n\t\t\t\tsendSlashText(pi, \"Selector unavailable in child-safe fanout mode. Pass an explicit current-session top-level async run id, for example `/subagents-stop <run-id>` or `subagent({ action: \\\"stop\\\", id: \\\"<run-id>\\\" })`.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet targets: StopSelectorTarget[];\n\t\t\ttry {\n\t\t\t\ttargets = discoverStopTargets(ctx, state);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tctx.ui.notify(message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tsendSlashText(pi, stopFallbackText(targets));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (targets.length === 0) {\n\t\t\t\tctx.ui.notify(\"No active current-session async runs or scheduled subagent runs to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst result = await ctx.ui.custom<StopSelectorResult>(\n\t\t\t\t(tui, theme, _kb, done) => new SubagentsStopSelector(tui, theme, targets, done),\n\t\t\t\t{ overlay: true, overlayOptions: { anchor: \"center\", width: 88, maxHeight: \"80%\" } },\n\t\t\t);\n\t\t\tif (!result?.confirmed || !result.target) return;\n\t\t\tif (result.target.kind === \"scheduled\") {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"schedule-cancel\", id: result.target.id });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"stop\", id: result.target.id });\n\t\t},\n\t});\n\n\tregisterPromptWorkflowCommands({\n\t\tpi,\n\t\trun: (params, ctx) => runSlashSubagent(pi, ctx, params),\n\t});\n\n}\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import { keyText } from "
|
|
5
|
+
import { keyText } from "../../../../modes/interactive/components/keybinding-hints.js";
|
|
6
6
|
import { Key, matchesKey } from "@ashx-j/lunr-tui";
|
|
7
7
|
import { formatTokens, shortenPath } from "../shared/formatters.js";
|
|
8
8
|
import { listAsyncRuns, formatAsyncRunProgressLabel } from "../runs/background/async-status.js";
|