@ashx-j/lunr-dev 0.2.19-dev.10.1 → 0.2.21-dev.12.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 +36 -1
- package/README.md +2 -2
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +0 -2
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-computer-use.js +15 -14
- package/dist/builtin-extensions/lunr-computer-use.js.map +1 -1
- package/dist/builtin-extensions/lunr-todos.d.ts +6 -5
- package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-todos.js +9 -7
- package/dist/builtin-extensions/lunr-todos.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts +6 -4
- package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/broker/spawn.js +114 -61
- package/dist/builtin-extensions/pi-intercom/broker/spawn.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +360 -351
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js +2 -2
- package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +8 -6
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +128 -57
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +18 -8
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +38 -13
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -3
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +24 -14
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +8 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +247 -18
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts +29 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js +179 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts +89 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js +541 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +11 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +38 -12
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +3 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +35 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +7 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +26 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +55 -38
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +100 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +46 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts +3 -6
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js +64 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts +15 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js +14 -4
- package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js.map +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 +2 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +21 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +0 -1
- 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 +71 -25
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js +75 -18
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +13 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +67 -11
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts +3 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +7 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js +13 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts +2 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js +5 -10
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js +3 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +3 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +68 -11
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +12 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +21 -2
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +21 -4
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +7 -0
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +11 -7
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +6 -0
- 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 +46 -29
- 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 +46 -4
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +407 -450
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts +2 -13
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js +5 -91
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js.map +1 -1
- package/dist/core/computer-use/image.d.ts +26 -0
- package/dist/core/computer-use/image.d.ts.map +1 -0
- package/dist/core/computer-use/image.js +64 -0
- package/dist/core/computer-use/image.js.map +1 -0
- package/dist/core/computer-use/policy.d.ts.map +1 -1
- package/dist/core/computer-use/policy.js +2 -6
- package/dist/core/computer-use/policy.js.map +1 -1
- package/dist/core/computer-use/schemas.d.ts +13 -10
- package/dist/core/computer-use/schemas.d.ts.map +1 -1
- package/dist/core/computer-use/schemas.js +26 -35
- package/dist/core/computer-use/schemas.js.map +1 -1
- package/dist/core/computer-use/workflow.d.ts +16 -8
- package/dist/core/computer-use/workflow.d.ts.map +1 -1
- package/dist/core/computer-use/workflow.js +271 -260
- package/dist/core/computer-use/workflow.js.map +1 -1
- package/dist/core/customize.d.ts +2 -0
- package/dist/core/customize.d.ts.map +1 -1
- package/dist/core/customize.js +3 -0
- package/dist/core/customize.js.map +1 -1
- package/dist/core/permissions.d.ts +1 -0
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +8 -4
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +0 -5
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/runtime-bridges.d.ts.map +1 -1
- package/dist/core/runtime-bridges.js +0 -2
- package/dist/core/runtime-bridges.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +11 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-cancellation.d.ts +21 -0
- package/dist/core/subagent-cancellation.d.ts.map +1 -0
- package/dist/core/subagent-cancellation.js +53 -0
- package/dist/core/subagent-cancellation.js.map +1 -0
- package/dist/core/subagent-spinner-setting.d.ts +6 -0
- package/dist/core/subagent-spinner-setting.d.ts.map +1 -0
- package/dist/core/subagent-spinner-setting.js +12 -0
- package/dist/core/subagent-spinner-setting.js.map +1 -0
- package/dist/core/subagent-spinner.d.ts +4 -0
- package/dist/core/subagent-spinner.d.ts.map +1 -0
- package/dist/core/subagent-spinner.js +6 -0
- package/dist/core/subagent-spinner.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +12 -18
- package/dist/modes/interactive/components/assistant-message.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 +73 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/extension-widget-order.d.ts +8 -0
- package/dist/modes/interactive/extension-widget-order.d.ts.map +1 -0
- package/dist/modes/interactive/extension-widget-order.js +37 -0
- package/dist/modes/interactive/extension-widget-order.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +44 -22
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/node-runtime/{agent-session-services-RSGP5GDN.js → agent-session-services-QANFGM2V.js} +14 -13
- package/dist/node-runtime/{builtin-extensions-7STLFW3A.js → builtin-extensions-SRCYQPZ6.js} +4 -3
- package/dist/node-runtime/{chunk-5OLJGVCB.js → chunk-3ONQTOMR.js} +2 -2
- package/dist/node-runtime/chunk-5ZH3ZFIZ.js +308 -0
- package/dist/node-runtime/{chunk-4SJWFTUP.js → chunk-7NBZ5P7I.js} +1 -1
- package/dist/node-runtime/chunk-A3KMVBZV.js +60 -0
- package/dist/node-runtime/{chunk-FLYMHKCS.js → chunk-BJ6H7DPI.js} +25 -25
- package/dist/node-runtime/{chunk-T5GM5BFH.js → chunk-BYQ2MOFI.js} +6 -6
- package/dist/node-runtime/{chunk-W7S2REEG.js → chunk-CZUBYPFP.js} +157 -74
- package/dist/node-runtime/{chunk-ZRDIMBEQ.js → chunk-D466UAID.js} +9 -6
- package/dist/node-runtime/{chunk-K72NVGUB.js → chunk-GT5F3OKM.js} +1 -1
- package/dist/node-runtime/chunk-H2BI7G7T.js +73 -0
- package/dist/node-runtime/{chunk-ISQDQIZN.js → chunk-HXJS2PJM.js} +2 -2
- package/dist/node-runtime/chunk-LISUTZS3.js +19 -0
- package/dist/node-runtime/{chunk-BKAZ6BKX.js → chunk-LOYUC2ID.js} +7 -7
- package/dist/node-runtime/{chunk-OTT6JMPU.js → chunk-LTYWTR3V.js} +1 -369
- package/dist/node-runtime/{chunk-5Q45AVQG.js → chunk-MW2XJZIK.js} +1 -1
- package/dist/node-runtime/{chunk-MUMMMTW4.js → chunk-N3PNL74S.js} +536 -265
- package/dist/node-runtime/{chunk-HQCKFM2O.js → chunk-VGYZ3APQ.js} +3 -0
- package/dist/node-runtime/{chunk-5NWQCSKR.js → chunk-VQ4UBWSA.js} +11 -1
- package/dist/node-runtime/{chunk-6SPPH4FY.js → chunk-VTGEKB7R.js} +4 -4
- package/dist/node-runtime/chunk-W3SILANP.js +24 -0
- package/dist/node-runtime/{chunk-J7QGQD45.js → chunk-WJVFOIB5.js} +1 -1
- package/dist/node-runtime/{chunk-JT6YCS5B.js → chunk-XAUQOKYK.js} +408 -45
- package/dist/node-runtime/{chunk-LCACDWHJ.js → chunk-XSRCAQK2.js} +13 -13
- package/dist/node-runtime/{chunk-TAP7QTUT.js → chunk-ZOIIMEN6.js} +24 -425
- package/dist/node-runtime/{chunk-MGSB2C3M.js → chunk-ZVSORYRM.js} +1 -1
- package/dist/node-runtime/{command-6UXQ6XZN.js → command-ZURZJDXF.js} +1 -1
- package/dist/node-runtime/{commands-LGR5VT5X.js → commands-BZGSLIP7.js} +3 -3
- package/dist/node-runtime/{customize-XEXL2DGQ.js → customize-LF5VST47.js} +1 -1
- package/dist/node-runtime/{direct-tool-executor-TDJS7TBD.js → direct-tool-executor-BSZDHIYR.js} +4 -4
- package/dist/node-runtime/{extract-IUCVZCWC.js → extract-XLQSJN4V.js} +2 -2
- package/dist/node-runtime/{gateway-3OLC4GQN.js → gateway-FWHRIBK5.js} +9 -9
- package/dist/node-runtime/{gemini-search-NCIDF7NE.js → gemini-search-U7SUMPQA.js} +1 -1
- package/dist/node-runtime/{gemini-url-context-OKIN7AIP.js → gemini-url-context-PXWCBQHK.js} +2 -2
- package/dist/node-runtime/index.js +34 -31
- package/dist/node-runtime/{init-EKE277AG.js → init-DVJUAOUO.js} +3 -3
- package/dist/node-runtime/{interactive-mode-S5M26CQ7.js → interactive-mode-BBASNWHN.js} +18 -15
- package/dist/node-runtime/lunr-computer-use-TJNDAMWM.js +135 -0
- package/dist/node-runtime/{lunr-cron-KKBOGAEF.js → lunr-cron-J64I72C6.js} +5 -5
- package/dist/node-runtime/main.js +22 -20
- package/dist/node-runtime/{package-manager-cli-C4EJOO5Q.js → package-manager-cli-VMYX4Y2S.js} +8 -7
- package/dist/node-runtime/{pi-intercom-EEMBGQID.js → pi-intercom-NV2LW6X7.js} +108 -87
- package/dist/node-runtime/{pi-mcp-adapter-DPTJGAAL.js → pi-mcp-adapter-JLB5YL2F.js} +6 -6
- package/dist/node-runtime/{pi-subagents-VKCLYIBY.js → pi-subagents-WEHV3W7H.js} +1267 -604
- package/dist/node-runtime/{pi-web-access-BV6DSZOP.js → pi-web-access-FN5YC5T6.js} +5 -5
- package/dist/node-runtime/{proxy-modes-R64LU6WZ.js → proxy-modes-OR7ISBXK.js} +7 -7
- package/dist/node-runtime/rpc-entry.js +22 -20
- package/dist/node-runtime/{runtime-bridges-UJ5WJJLN.js → runtime-bridges-2VQTLPOZ.js} +3 -5
- package/dist/node-runtime/{settings-manager-K6IPIQTI.js → settings-manager-3CCU326G.js} +3 -2
- package/dist/node-runtime/{subagent-executor-EFLTO32A.js → subagent-executor-CXKXQDHH.js} +186 -299
- package/dist/node-runtime/{update-cli-I6OVDRPL.js → update-cli-NM7B4CKL.js} +3 -2
- package/dist/node-runtime/{video-extract-ETA5RA7U.js → video-extract-OYT5WKS5.js} +2 -2
- package/dist/node-runtime/workflow-C26QCUTW.js +468 -0
- package/dist/node-runtime/{youtube-extract-SRVGNN26.js → youtube-extract-TQYKGFGS.js} +2 -2
- package/docs/computer-use.md +233 -268
- package/docs/features.md +22 -4
- package/docs/keybindings.md +3 -1
- package/docs/settings.md +3 -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/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 +24 -24
- package/install-lock/package.json +2 -2
- package/npm-shrinkwrap.json +21 -21
- package/package.json +7 -7
- package/dist/builtin-extensions/lunr-settings-tools.d.ts +0 -4
- package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +0 -1
- package/dist/builtin-extensions/lunr-settings-tools.js +0 -120
- package/dist/builtin-extensions/lunr-settings-tools.js.map +0 -1
- package/dist/core/settings-tools-bridge.d.ts +0 -35
- package/dist/core/settings-tools-bridge.d.ts.map +0 -1
- package/dist/core/settings-tools-bridge.js +0 -56
- package/dist/core/settings-tools-bridge.js.map +0 -1
- package/dist/node-runtime/chunk-EH2LJQPM.js +0 -64
- package/dist/node-runtime/image-resize-6ZPBFMWW.js +0 -11
- package/dist/node-runtime/lunr-computer-use-4X3KUNF4.js +0 -206
- package/dist/node-runtime/workflow-SA34S4VX.js +0 -325
- package/dist/node-runtime/{chunk-EHMBW7FA.js → chunk-VSELYKWU.js} +3 -3
- package/dist/node-runtime/{chunk-ZSEZY3II.js → chunk-VTPM5V35.js} +0 -0
- package/dist/node-runtime/{src-GPFAU3N6.js → src-BPQFJR76.js} +3 -3
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
ASYNC_DIR,
|
|
7
7
|
DEFAULT_ARTIFACT_CONFIG,
|
|
8
8
|
INTERCOM_DETACH_REQUEST_EVENT,
|
|
9
|
-
MAX_WIDGET_JOBS,
|
|
10
9
|
POLL_INTERVAL_MS,
|
|
11
10
|
RESULTS_DIR,
|
|
12
11
|
SLASH_RESULT_TYPE,
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
SLASH_SUBAGENT_UPDATE_EVENT,
|
|
18
17
|
SLASH_TEXT_RESULT_TYPE,
|
|
19
18
|
SUBAGENT_ASYNC_COMPLETE_EVENT,
|
|
19
|
+
SUBAGENT_ASYNC_GUIDANCE,
|
|
20
20
|
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
21
21
|
SUBAGENT_CHILD_ENV,
|
|
22
22
|
SUBAGENT_CONTROL_EVENT,
|
|
@@ -29,10 +29,10 @@ import {
|
|
|
29
29
|
SUBAGENT_STEERING_NOTICE_EVENT,
|
|
30
30
|
TEMP_ROOT_DIR,
|
|
31
31
|
WIDGET_KEY,
|
|
32
|
-
aggregateStepStatus,
|
|
33
32
|
assertJsonSchemaObject,
|
|
34
33
|
attachNestedChildrenToResultChildren,
|
|
35
34
|
buildSubagentResultIntercomPayload,
|
|
35
|
+
childRowLabel,
|
|
36
36
|
cleanupAllArtifactDirs,
|
|
37
37
|
cleanupOldArtifacts,
|
|
38
38
|
cleanupOldChainDirs,
|
|
@@ -40,16 +40,16 @@ import {
|
|
|
40
40
|
compactNestedResultChildren,
|
|
41
41
|
deliverStopRequest,
|
|
42
42
|
deliverSubagentResultIntercomEvent,
|
|
43
|
-
flatToLogicalStepIndex,
|
|
44
43
|
formatActivityLabel,
|
|
45
44
|
formatAgentRunningLabel,
|
|
46
45
|
formatAsyncRunProgressLabel,
|
|
47
46
|
formatAsyncRunTranscript,
|
|
47
|
+
formatCompactModelBadge,
|
|
48
48
|
formatControlNoticeMessage,
|
|
49
49
|
formatDuration,
|
|
50
50
|
formatModelSelection,
|
|
51
51
|
formatNestedAggregate,
|
|
52
|
-
|
|
52
|
+
formatNestedResultLines,
|
|
53
53
|
formatTokens,
|
|
54
54
|
formatToolCall,
|
|
55
55
|
formatUsage,
|
|
@@ -60,40 +60,54 @@ import {
|
|
|
60
60
|
getSingleResultOutput,
|
|
61
61
|
handleSubagentControlNotice,
|
|
62
62
|
hasLiveNestedDescendants,
|
|
63
|
+
isAsyncSubagentExecution,
|
|
63
64
|
listAsyncRuns,
|
|
65
|
+
normalizeAsyncLaunchConfig,
|
|
64
66
|
normalizeParallelGroups,
|
|
65
67
|
projectNestedRegistryForRoot,
|
|
66
68
|
readStatus,
|
|
69
|
+
readSteerCapability,
|
|
67
70
|
reconcileAsyncRun,
|
|
68
71
|
reconcileNestedAsyncDescendants,
|
|
69
72
|
registerMainWatchdog,
|
|
73
|
+
requestAsyncSteer,
|
|
70
74
|
resolveAsyncRunLocation,
|
|
71
75
|
resolveCurrentSessionId,
|
|
76
|
+
resolveSubagentRequestParams,
|
|
72
77
|
resolveSubagentResultStatus,
|
|
78
|
+
resolveSubagentRunId,
|
|
73
79
|
resolveTurnBudgetConfig,
|
|
74
80
|
resolveWaitToolConfig,
|
|
75
81
|
resolveWatchPath,
|
|
76
82
|
sanitizeChildDescription,
|
|
77
83
|
sanitizeSummary,
|
|
78
84
|
shortenPath,
|
|
85
|
+
subagentLaunchRunsAsync,
|
|
79
86
|
summarizeAsyncStatus,
|
|
80
87
|
updateAsyncJobNestedProjection,
|
|
81
88
|
validateAcceptanceInput,
|
|
82
89
|
validateExecutionAcceptance,
|
|
83
90
|
validateToolBudgetConfig,
|
|
84
91
|
writeAtomicJson
|
|
85
|
-
} from "./chunk-
|
|
86
|
-
import
|
|
87
|
-
|
|
92
|
+
} from "./chunk-N3PNL74S.js";
|
|
93
|
+
import {
|
|
94
|
+
createSubagentCancellation,
|
|
95
|
+
getSubagentSpinnerDefinition,
|
|
96
|
+
registerSubagentCancellation
|
|
97
|
+
} from "./chunk-A3KMVBZV.js";
|
|
98
|
+
import "./chunk-5ZH3ZFIZ.js";
|
|
99
|
+
import "./chunk-ZVSORYRM.js";
|
|
100
|
+
import "./chunk-GT5F3OKM.js";
|
|
88
101
|
import "./chunk-53YJI7JU.js";
|
|
89
102
|
import "./chunk-GZQHMEWL.js";
|
|
90
103
|
import "./chunk-ZCHMJ34K.js";
|
|
91
|
-
import "./chunk-
|
|
92
|
-
import "./chunk-ZRDIMBEQ.js";
|
|
93
|
-
import "./chunk-M3LZLQW2.js";
|
|
94
|
-
import "./chunk-5OLJGVCB.js";
|
|
104
|
+
import "./chunk-LISUTZS3.js";
|
|
95
105
|
import "./chunk-FVDOXRUD.js";
|
|
96
|
-
import "./chunk-
|
|
106
|
+
import "./chunk-MW2XJZIK.js";
|
|
107
|
+
import "./chunk-D466UAID.js";
|
|
108
|
+
import "./chunk-M3LZLQW2.js";
|
|
109
|
+
import "./chunk-VTPM5V35.js";
|
|
110
|
+
import "./chunk-3ONQTOMR.js";
|
|
97
111
|
import "./chunk-RQ2Q5IYR.js";
|
|
98
112
|
import {
|
|
99
113
|
keyText
|
|
@@ -106,10 +120,10 @@ import "./chunk-Q4Q6D4IY.js";
|
|
|
106
120
|
import "./chunk-BRJX2PRC.js";
|
|
107
121
|
|
|
108
122
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
|
|
109
|
-
import { randomUUID as
|
|
110
|
-
import * as
|
|
123
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
124
|
+
import * as fs10 from "node:fs";
|
|
111
125
|
import * as os from "node:os";
|
|
112
|
-
import * as
|
|
126
|
+
import * as path12 from "node:path";
|
|
113
127
|
import { Box, Container as Container2, Spacer as Spacer2, Text as Text2, truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth3, wrapTextWithAnsi as wrapTextWithAnsi2 } from "@ashx-j/lunr-tui";
|
|
114
128
|
|
|
115
129
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/tui/render.js
|
|
@@ -172,7 +186,11 @@ function wrapPlainText(text, maxWidth) {
|
|
|
172
186
|
}
|
|
173
187
|
return lines;
|
|
174
188
|
}
|
|
175
|
-
var
|
|
189
|
+
var STATIC_RUNNING_GLYPH = "\u25CF", SUBAGENT_PAINT_INTERVAL_MS = 80;
|
|
190
|
+
function selectedSubagentSpinner() {
|
|
191
|
+
let bridge = globalThis[/* @__PURE__ */ Symbol.for("@lunr/customize")];
|
|
192
|
+
return getSubagentSpinnerDefinition(bridge?.getSubagentSpinner?.() ?? "braille");
|
|
193
|
+
}
|
|
176
194
|
function runningSeed(...values) {
|
|
177
195
|
let seed;
|
|
178
196
|
for (let value of values)
|
|
@@ -180,7 +198,14 @@ function runningSeed(...values) {
|
|
|
180
198
|
return seed;
|
|
181
199
|
}
|
|
182
200
|
function runningGlyph(seed) {
|
|
183
|
-
|
|
201
|
+
if (seed === void 0)
|
|
202
|
+
return STATIC_RUNNING_GLYPH;
|
|
203
|
+
let frames = selectedSubagentSpinner().frames;
|
|
204
|
+
return frames[Math.abs(seed) % frames.length];
|
|
205
|
+
}
|
|
206
|
+
function foregroundSpinnerFrame(timerFrame) {
|
|
207
|
+
let { interval } = selectedSubagentSpinner();
|
|
208
|
+
return Math.floor(Math.max(0, timerFrame) * SUBAGENT_PAINT_INTERVAL_MS / interval);
|
|
184
209
|
}
|
|
185
210
|
function progressRunningSeed(progress) {
|
|
186
211
|
if (progress)
|
|
@@ -240,6 +265,16 @@ function statJoin(theme, parts) {
|
|
|
240
265
|
function formatTokenStat(tokens) {
|
|
241
266
|
return `${formatTokens(tokens)} token`;
|
|
242
267
|
}
|
|
268
|
+
function usageTokenTotal(usage) {
|
|
269
|
+
return usage ? (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) : 0;
|
|
270
|
+
}
|
|
271
|
+
function compactResultTokens(result2) {
|
|
272
|
+
let progress = result2.progress || result2.progressSummary;
|
|
273
|
+
if (progress && progress.tokens > 0)
|
|
274
|
+
return progress.tokens;
|
|
275
|
+
let fromUsage = usageTokenTotal(result2.usage);
|
|
276
|
+
return fromUsage > 0 ? fromUsage : progress?.tokens ?? 0;
|
|
277
|
+
}
|
|
243
278
|
function formatToolUseStat(count) {
|
|
244
279
|
return `${count} tool use${count === 1 ? "" : "s"}`;
|
|
245
280
|
}
|
|
@@ -250,7 +285,10 @@ function formatTotalCostStat(totalCost) {
|
|
|
250
285
|
return totalCost.inputTokens && parts.push(`in:${formatTokens(totalCost.inputTokens)}`), totalCost.outputTokens && parts.push(`out:${formatTokens(totalCost.outputTokens)}`), totalCost.costUsd && parts.push(`$${totalCost.costUsd.toFixed(4)}`), parts.join(" ");
|
|
251
286
|
}
|
|
252
287
|
function liveDurationMs(progress, running, now) {
|
|
253
|
-
|
|
288
|
+
if (!progress)
|
|
289
|
+
return 0;
|
|
290
|
+
let durationMs = progress.durationMs ?? 0;
|
|
291
|
+
return !running || progress.lastActivityAt === void 0 ? durationMs : durationMs + Math.max(0, now - progress.lastActivityAt);
|
|
254
292
|
}
|
|
255
293
|
function formatProgressStats(theme, progress, includeDuration = !0, options = {}) {
|
|
256
294
|
if (!progress)
|
|
@@ -290,13 +328,17 @@ function resultStatusLine(result2, output) {
|
|
|
290
328
|
function resultGlyph(result2, output, theme, running = result2.progress?.status === "running", seed = progressRunningSeed(result2.progress ?? result2.progressSummary), frame) {
|
|
291
329
|
return running ? frame !== void 0 ? theme.fg("accent", runningGlyph((seed ?? 0) + frame)) : theme.fg("accent", runningGlyph(seed)) : result2.detached || result2.stopped || result2.interrupted ? theme.fg("warning", "\u25A0") : result2.exitCode !== 0 ? theme.fg("error", "\u2717") : hasEmptyTextOutputWithoutOutputTarget(result2.task, output) ? theme.fg("warning", "\u2713") : theme.fg("success", "\u2713");
|
|
292
330
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
331
|
+
function formatCompactRowStats(tokens, durationMs) {
|
|
332
|
+
return `${formatTokenStat(tokens)} \xB7 ${durationMs > 0 ? formatDuration(durationMs) : "0ms"}`;
|
|
333
|
+
}
|
|
334
|
+
function formatCompactSubagentRow(theme, width, parts) {
|
|
335
|
+
let stats = formatCompactRowStats(parts.tokens, parts.durationMs), body = [
|
|
336
|
+
`${parts.prefix ?? ""}${parts.glyph} ${themeBold(theme, parts.description)}`,
|
|
337
|
+
parts.modelBadge ? theme.fg("dim", parts.modelBadge) : "",
|
|
338
|
+
theme.fg("dim", stats),
|
|
339
|
+
parts.terminalStatus ? theme.fg("dim", parts.terminalStatus) : ""
|
|
340
|
+
].filter(Boolean).join(` ${theme.fg("dim", "\xB7")} `);
|
|
341
|
+
return truncLine(body, width);
|
|
300
342
|
}
|
|
301
343
|
function widgetRenderKey(job) {
|
|
302
344
|
return JSON.stringify({
|
|
@@ -350,14 +392,9 @@ function widgetStepsRunningSeed(steps) {
|
|
|
350
392
|
function widgetJobRunningSeed(job) {
|
|
351
393
|
return runningSeed(job.currentStep, job.runningSteps, widgetStepsRunningSeed(job.steps));
|
|
352
394
|
}
|
|
353
|
-
function widgetJobsRunningSeed(jobs) {
|
|
354
|
-
let seed;
|
|
355
|
-
for (let job of jobs)
|
|
356
|
-
seed = runningSeed(seed, widgetJobRunningSeed(job));
|
|
357
|
-
return seed;
|
|
358
|
-
}
|
|
359
395
|
function widgetAnimFrame(now = Date.now()) {
|
|
360
|
-
|
|
396
|
+
let definition = selectedSubagentSpinner();
|
|
397
|
+
return Math.floor(now / definition.interval) % definition.frames.length;
|
|
361
398
|
}
|
|
362
399
|
function widgetStatusGlyph(job, theme, now = Date.now()) {
|
|
363
400
|
return job.status === "running" ? theme.fg("accent", runningGlyph((widgetJobRunningSeed(job) ?? 0) + widgetAnimFrame(now))) : job.status === "queued" ? theme.fg("muted", "\u25E6") : job.status === "complete" ? theme.fg("success", "\u2713") : job.status === "paused" || job.status === "stopped" ? theme.fg("warning", "\u25A0") : theme.fg("error", "\u2717");
|
|
@@ -368,48 +405,6 @@ function widgetStepGlyph(status, theme, seed) {
|
|
|
368
405
|
function widgetStepStatus(status, theme) {
|
|
369
406
|
return status === "running" ? theme.fg("accent", "running") : status === "complete" || status === "completed" ? theme.fg("success", "complete") : status === "failed" ? theme.fg("error", "failed") : status === "paused" ? theme.fg("warning", "paused") : status === "stopped" ? theme.fg("warning", "stopped") : theme.fg("dim", status);
|
|
370
407
|
}
|
|
371
|
-
function widgetStepActivity(step, snapshotNow) {
|
|
372
|
-
let facts = [];
|
|
373
|
-
step.currentTool && step.currentToolStartedAt !== void 0 && snapshotNow !== void 0 ? facts.push(`${step.currentTool} ${formatDuration(Math.max(0, snapshotNow - step.currentToolStartedAt))}`) : step.currentTool && facts.push(step.currentTool), step.currentPath && facts.push(shortenPath(step.currentPath)), step.turnCount !== void 0 && facts.push(`${step.turnCount} turns`), step.toolCount !== void 0 && facts.push(`${step.toolCount} tools`), step.tokens?.total && facts.push(formatTokenStat(step.tokens.total));
|
|
374
|
-
let activity = buildLiveStatusLine(step, snapshotNow);
|
|
375
|
-
return activity && facts.length ? `${activity} \xB7 ${facts.join(" \xB7 ")}` : activity || facts.join(" \xB7 ");
|
|
376
|
-
}
|
|
377
|
-
function widgetChainDetails(job, theme, expanded = !1, width = getTermWidth()) {
|
|
378
|
-
if (!job.steps?.length)
|
|
379
|
-
return [];
|
|
380
|
-
let total = job.chainStepCount ?? job.steps.length, lines = [];
|
|
381
|
-
for (let span of buildAsyncChainStepSpans(total, job.steps.length, job.parallelGroups)) {
|
|
382
|
-
let steps = job.steps.slice(span.start, span.start + span.count);
|
|
383
|
-
if (span.isParallel) {
|
|
384
|
-
let status = aggregateStepStatus(steps);
|
|
385
|
-
lines.push(` ${widgetStepGlyph(status, theme, widgetStepsRunningSeed(steps))} Step ${span.stepIndex + 1}/${total}: ${themeBold(theme, "parallel group")} ${theme.fg("dim", "\xB7")} ${theme.fg("dim", formatParallelOutcome(steps, span.count))}`);
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
let step = steps[0];
|
|
389
|
-
if (!step) {
|
|
390
|
-
lines.push(` ${theme.fg("dim", `\u25E6 Step ${span.stepIndex + 1}/${total}: pending`)}`);
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, "Step", span.stepIndex + 1, total, expanded, width));
|
|
394
|
-
}
|
|
395
|
-
return lines;
|
|
396
|
-
}
|
|
397
|
-
function widgetParallelAgentDetails(job, theme, expanded = !1, width = getTermWidth()) {
|
|
398
|
-
if (!job.steps?.length)
|
|
399
|
-
return [];
|
|
400
|
-
if (job.mode !== "parallel" && job.mode !== "chain")
|
|
401
|
-
return [];
|
|
402
|
-
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length)
|
|
403
|
-
return widgetChainDetails(job, theme, expanded, width);
|
|
404
|
-
let total = job.stepsTotal ?? job.steps.length, lines = [];
|
|
405
|
-
for (let [index, step] of job.steps.entries()) {
|
|
406
|
-
let marker = index === job.steps.length - 1 ? "\u2514" : "\u251C", activity = widgetStepActivity(step, job.updatedAt), itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step", modelDisplay = modelThinkingBadge(theme, step.model, step.thinking, step.modelSelection);
|
|
407
|
-
lines.push(` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${step.agent} \xB7 ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` \xB7 ${activity}` : ""}`)}`);
|
|
408
|
-
for (let nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 1))
|
|
409
|
-
lines.push(` ${nestedLine}`);
|
|
410
|
-
}
|
|
411
|
-
return lines;
|
|
412
|
-
}
|
|
413
408
|
function parseParallelGroupAgentCount(label) {
|
|
414
409
|
if (!label || !label.startsWith("[") || !label.endsWith("]"))
|
|
415
410
|
return;
|
|
@@ -445,24 +440,16 @@ function buildChainStepSpans(details) {
|
|
|
445
440
|
function isChainParallelGroupActive(details) {
|
|
446
441
|
return details.mode !== "chain" || details.currentStepIndex === void 0 ? !1 : buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
447
442
|
}
|
|
448
|
-
function buildAsyncChainStepSpans(total, stepCount, parallelGroups = []) {
|
|
449
|
-
let spans = [], flatIndex = 0;
|
|
450
|
-
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
451
|
-
let group = parallelGroups.find((candidate) => candidate.stepIndex === stepIndex);
|
|
452
|
-
if (group) {
|
|
453
|
-
spans.push({ stepIndex, start: group.start, count: group.count, isParallel: !0 }), flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
spans.push({ stepIndex, start: flatIndex, count: flatIndex < stepCount ? 1 : 0, isParallel: !1 }), flatIndex++;
|
|
457
|
-
}
|
|
458
|
-
return spans;
|
|
459
|
-
}
|
|
460
443
|
function isDoneResult(result2) {
|
|
461
444
|
let status = result2.progress?.status;
|
|
462
445
|
return status === "completed" ? !0 : status === "running" || status === "pending" || result2.interrupted || result2.detached ? !1 : result2.exitCode === 0;
|
|
463
446
|
}
|
|
464
447
|
function workflowGraphHasStatus(details, statuses) {
|
|
465
|
-
|
|
448
|
+
let matches = (nodes) => nodes.some((node) => statuses.includes(node.status) || matches(node.children ?? []));
|
|
449
|
+
return matches(details.workflowGraph?.nodes ?? []);
|
|
450
|
+
}
|
|
451
|
+
function resultAtFlatIndex(details, flatIndex) {
|
|
452
|
+
return details.results.find((result2, arrayIndex) => (result2.progress?.index ?? result2.progressSummary?.index ?? arrayIndex) === flatIndex);
|
|
466
453
|
}
|
|
467
454
|
function buildChainRenderEntries(details, label) {
|
|
468
455
|
if (details.mode !== "chain" || !label.hasParallelInChain || label.showActiveGroupOnly)
|
|
@@ -480,13 +467,17 @@ function buildChainRenderEntries(details, label) {
|
|
|
480
467
|
});
|
|
481
468
|
continue;
|
|
482
469
|
}
|
|
483
|
-
for (let index = span.start; index < span.start + span.count; index++)
|
|
470
|
+
for (let index = span.start; index < span.start + span.count; index++) {
|
|
471
|
+
let workflowStep = details.workflowGraph?.nodes.find((node) => node.stepIndex === span.stepIndex), workflowNode = workflowStep?.children?.find((node) => node.flatIndex === index) ?? (workflowStep?.flatIndex === index ? workflowStep : void 0), result2 = resultAtFlatIndex(details, index);
|
|
484
472
|
entries.push({
|
|
485
473
|
kind: "result",
|
|
486
474
|
resultIndex: index,
|
|
487
475
|
rowNumber: index + 1,
|
|
488
|
-
agentName:
|
|
476
|
+
agentName: result2?.agent ?? workflowNode?.label ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
|
|
477
|
+
status: workflowNode?.status ?? span.status,
|
|
478
|
+
error: workflowNode?.error ?? span.error
|
|
489
479
|
});
|
|
480
|
+
}
|
|
490
481
|
}
|
|
491
482
|
return entries;
|
|
492
483
|
}
|
|
@@ -551,38 +542,6 @@ function resultRowLabel(details, label, resultIndex, stepNumber) {
|
|
|
551
542
|
}
|
|
552
543
|
return label.itemTitle === "Agent" ? `Agent ${label.activeParallelGroup ? Math.max(1, stepNumber - label.groupStartIndex) : stepNumber}/${label.totalCount}` : `Step ${stepNumber}`;
|
|
553
544
|
}
|
|
554
|
-
function widgetStats(job, theme, now = Date.now()) {
|
|
555
|
-
let parts = [], stepsTotal = job.stepsTotal ?? job.agents?.length ?? 1;
|
|
556
|
-
if (job.activeParallelGroup) {
|
|
557
|
-
let running = job.runningSteps ?? (job.status === "running" ? 1 : 0), done = job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
|
|
558
|
-
if (job.mode === "parallel")
|
|
559
|
-
job.status === "running" && running > 0 && parts.push(formatAgentRunningLabel(running)), stepsTotal > 0 && parts.push(`${done}/${stepsTotal} done`);
|
|
560
|
-
else {
|
|
561
|
-
let logicalStep = (job.currentStep !== void 0 ? job.parallelGroups?.find((group) => job.currentStep >= group.start && job.currentStep < group.start + group.count) : job.parallelGroups?.find((group) => group.start === 0))?.stepIndex ?? job.currentStep ?? 0, total = job.chainStepCount ?? stepsTotal, groupParts = [`${done}/${stepsTotal} done`];
|
|
562
|
-
job.status === "running" && running > 0 && groupParts.unshift(formatAgentRunningLabel(running)), parts.push(`step ${logicalStep + 1}/${total} \xB7 parallel group: ${groupParts.join(" \xB7 ")}`);
|
|
563
|
-
}
|
|
564
|
-
} else if (job.currentStep !== void 0)
|
|
565
|
-
if (job.mode === "chain" && job.parallelGroups?.length) {
|
|
566
|
-
let total = job.chainStepCount ?? stepsTotal;
|
|
567
|
-
parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`);
|
|
568
|
-
} else
|
|
569
|
-
parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
|
|
570
|
-
else stepsTotal > 1 && parts.push(`steps ${stepsTotal}`);
|
|
571
|
-
if (job.toolCount !== void 0 && parts.push(formatToolUseStat(job.toolCount)), job.totalTokens?.total && parts.push(formatTokenStat(job.totalTokens.total)), job.startedAt !== void 0) {
|
|
572
|
-
let end = job.status === "running" ? now : job.updatedAt;
|
|
573
|
-
end !== void 0 && parts.push(`${job.status === "running" ? "" : "ran for "}${formatDuration(Math.max(0, end - job.startedAt))}`);
|
|
574
|
-
}
|
|
575
|
-
return statJoin(theme, parts);
|
|
576
|
-
}
|
|
577
|
-
function widgetStepStats(theme, step, now = Date.now()) {
|
|
578
|
-
let running = step.status === "running", durationMs = step.durationMs !== void 0 ? step.durationMs + (running && step.lastActivityAt !== void 0 ? Math.max(0, now - step.lastActivityAt) : 0) : step.startedAt !== void 0 ? Math.max(0, (running ? now : step.endedAt ?? step.lastActivityAt ?? now) - step.startedAt) : void 0;
|
|
579
|
-
return statJoin(theme, [
|
|
580
|
-
step.turnCount !== void 0 ? `${step.turnCount} turns` : "",
|
|
581
|
-
step.toolCount !== void 0 ? formatToolUseStat(step.toolCount) : "",
|
|
582
|
-
step.tokens?.total ? formatTokenStat(step.tokens.total) : "",
|
|
583
|
-
durationMs !== void 0 ? `${running ? "" : "ran for "}${formatDuration(durationMs)}` : ""
|
|
584
|
-
]);
|
|
585
|
-
}
|
|
586
545
|
function modelThinkingBadge(theme, model, thinking, selection) {
|
|
587
546
|
let label = formatModelSelection(selection, model, thinking);
|
|
588
547
|
return label ? theme.fg("dim", ` (${label})`) : "";
|
|
@@ -647,30 +606,28 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
|
|
|
647
606
|
};
|
|
648
607
|
return append(children, 0, ""), lines.map((line) => truncLine(line, width));
|
|
649
608
|
}
|
|
650
|
-
function
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
let maxArgsLen = Math.max(40, width - 30), argsPreview = tool.args.length <= maxArgsLen ? tool.args : `${tool.args.slice(0, maxArgsLen)}...`;
|
|
660
|
-
lines.push(` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`);
|
|
661
|
-
}
|
|
662
|
-
for (let line of step.recentOutput?.slice(-5) ?? [])
|
|
663
|
-
lines.push(` ${theme.fg("dim", line)}`);
|
|
664
|
-
}
|
|
665
|
-
return lines;
|
|
609
|
+
function compactTerminalStatus(status) {
|
|
610
|
+
if (status === "failed")
|
|
611
|
+
return "failed";
|
|
612
|
+
if (status === "paused")
|
|
613
|
+
return "paused";
|
|
614
|
+
if (status === "stopped")
|
|
615
|
+
return "stopped";
|
|
616
|
+
if (status === "detached")
|
|
617
|
+
return "detached";
|
|
666
618
|
}
|
|
667
619
|
function compactJobProgress(job, step, now = Date.now()) {
|
|
668
|
-
let running = (step?.status ?? job.status) === "running",
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
620
|
+
let running = (step?.status ?? job.status) === "running", tokens = step?.tokens?.total ?? job.totalTokens?.total ?? 0;
|
|
621
|
+
if (step?.durationMs !== void 0)
|
|
622
|
+
return {
|
|
623
|
+
tokens,
|
|
624
|
+
durationMs: liveDurationMs({ durationMs: step.durationMs, lastActivityAt: step.lastActivityAt }, running, now)
|
|
625
|
+
};
|
|
626
|
+
let startedAt = step?.startedAt ?? job.startedAt;
|
|
627
|
+
if (startedAt === void 0)
|
|
628
|
+
return { tokens, durationMs: 0 };
|
|
629
|
+
let endedAt = running ? now : step?.endedAt ?? step?.lastActivityAt ?? job.updatedAt ?? now;
|
|
630
|
+
return { tokens, durationMs: Math.max(0, endedAt - startedAt) };
|
|
674
631
|
}
|
|
675
632
|
function compactJobLead(job, step) {
|
|
676
633
|
return compactRowLead({
|
|
@@ -679,18 +636,15 @@ function compactJobLead(job, step) {
|
|
|
679
636
|
}) || widgetJobName(job);
|
|
680
637
|
}
|
|
681
638
|
function compactJobLines(job, step, theme, width, now = Date.now()) {
|
|
682
|
-
let running = (step?.status ?? job.status) === "running", lead = compactJobLead(job, step), modelBadge =
|
|
683
|
-
return [
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
}
|
|
692
|
-
function compactSingleWidgetLines(job, theme, width) {
|
|
693
|
-
return buildSingleWidgetLines(job, theme, width, !1);
|
|
639
|
+
let running = (step?.status ?? job.status) === "running", lead = compactJobLead(job, step), modelBadge = formatCompactModelBadge(step?.modelSelection, step?.model), glyph = step ? widgetStepGlyph(step.status, theme, running ? (widgetStepRunningSeed(step, step.index) ?? 0) + widgetAnimFrame(now) : widgetStepRunningSeed(step, step.index)) : widgetStatusGlyph(job, theme, now), progress = compactJobProgress(job, step, now);
|
|
640
|
+
return [formatCompactSubagentRow(theme, width, {
|
|
641
|
+
glyph,
|
|
642
|
+
description: lead,
|
|
643
|
+
modelBadge,
|
|
644
|
+
tokens: progress.tokens,
|
|
645
|
+
durationMs: progress.durationMs,
|
|
646
|
+
terminalStatus: compactTerminalStatus(step?.status ?? job.status)
|
|
647
|
+
})];
|
|
694
648
|
}
|
|
695
649
|
var RESERVED_NON_WIDGET_ROWS = 19, widgetLayoutSession;
|
|
696
650
|
function resetWidgetLayoutSession() {
|
|
@@ -709,20 +663,6 @@ function currentTerminalColumns() {
|
|
|
709
663
|
function widgetSessionMatches(expanded) {
|
|
710
664
|
return widgetLayoutSession?.expanded === expanded && widgetLayoutSession.rows === currentTerminalRows() && widgetLayoutSession.columns === currentTerminalColumns();
|
|
711
665
|
}
|
|
712
|
-
function widgetHeaderCounts(jobs) {
|
|
713
|
-
return {
|
|
714
|
-
running: jobs.filter((job) => job.status === "running"),
|
|
715
|
-
queued: jobs.filter((job) => job.status === "queued"),
|
|
716
|
-
complete: jobs.filter((job) => job.status === "complete"),
|
|
717
|
-
failed: jobs.filter((job) => job.status === "failed"),
|
|
718
|
-
paused: jobs.filter((job) => job.status === "paused"),
|
|
719
|
-
stopped: jobs.filter((job) => job.status === "stopped")
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
function buildSingleLineWidgetLines(jobs, theme, width) {
|
|
723
|
-
let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "\u25CF" : "\u25CB", parts = [];
|
|
724
|
-
return counts.running.length > 0 && parts.push(`${counts.running.length}/${jobs.length} running`), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`), counts.failed.length > 0 && parts.push(`${counts.failed.length} failed`), counts.stopped.length > 0 && parts.push(`${counts.stopped.length} stopped`), counts.paused.length > 0 && parts.push(`${counts.paused.length} paused`), !hasActive && counts.complete.length > 0 && parts.push(`${counts.complete.length}/${jobs.length} done`), [truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "subagents")} (${parts.join(", ") || `${jobs.length} total`})`, width)];
|
|
725
|
-
}
|
|
726
666
|
function orderedWidgetJobs(jobs) {
|
|
727
667
|
return [
|
|
728
668
|
...jobs.filter((job) => job.status === "running"),
|
|
@@ -730,73 +670,6 @@ function orderedWidgetJobs(jobs) {
|
|
|
730
670
|
...jobs.filter((job) => job.status !== "running" && job.status !== "queued")
|
|
731
671
|
];
|
|
732
672
|
}
|
|
733
|
-
function progressiveJobKey(job) {
|
|
734
|
-
return job.asyncId;
|
|
735
|
-
}
|
|
736
|
-
function isProgressiveActiveJob(job) {
|
|
737
|
-
return job?.status === "running" || job?.status === "queued";
|
|
738
|
-
}
|
|
739
|
-
function selectProgressiveJobKeys(jobs, previousKeys, bodyRows) {
|
|
740
|
-
if (bodyRows <= 0)
|
|
741
|
-
return [];
|
|
742
|
-
let jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job])), selected = [], append = (key) => {
|
|
743
|
-
selected.includes(key) || !jobsByKey.has(key) || selected.push(key);
|
|
744
|
-
};
|
|
745
|
-
for (let key of previousKeys)
|
|
746
|
-
if (isProgressiveActiveJob(jobsByKey.get(key)) && (append(key), selected.length >= bodyRows))
|
|
747
|
-
return selected;
|
|
748
|
-
for (let job of orderedWidgetJobs(jobs)) {
|
|
749
|
-
if (!isProgressiveActiveJob(job))
|
|
750
|
-
continue;
|
|
751
|
-
let key = progressiveJobKey(job);
|
|
752
|
-
if (append(key), selected.length >= bodyRows)
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
if (selected.length >= bodyRows)
|
|
756
|
-
return selected;
|
|
757
|
-
for (let key of previousKeys)
|
|
758
|
-
if (!isProgressiveActiveJob(jobsByKey.get(key)) && (append(key), selected.length >= bodyRows))
|
|
759
|
-
return selected;
|
|
760
|
-
for (let job of orderedWidgetJobs(jobs)) {
|
|
761
|
-
let key = progressiveJobKey(job);
|
|
762
|
-
if (append(key), selected.length >= bodyRows)
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
return selected;
|
|
766
|
-
}
|
|
767
|
-
function progressiveHeaderLine(jobs, theme, width) {
|
|
768
|
-
let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "\u25CF" : "\u25CB", parts = [];
|
|
769
|
-
return counts.running.length > 0 && parts.push(formatAgentRunningLabel(counts.running.length)), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`), hasActive || (counts.failed.length > 0 && parts.push(`${counts.failed.length} failed`), counts.stopped.length > 0 && parts.push(`${counts.stopped.length} stopped`), counts.paused.length > 0 && parts.push(`${counts.paused.length} paused`), counts.complete.length > 0 && parts.push(`${counts.complete.length}/${jobs.length} done`)), truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7")} ${theme.fg("dim", parts.join(", ") || `${jobs.length} total`)}`, width);
|
|
770
|
-
}
|
|
771
|
-
function progressiveJobLine(job, theme, width) {
|
|
772
|
-
let stats = widgetStats(job, theme), activity = widgetActivity(job), status = job.status === "complete" ? "done" : job.status, parts = [
|
|
773
|
-
themeBold(theme, widgetJobName(job)),
|
|
774
|
-
theme.fg("dim", status),
|
|
775
|
-
stats,
|
|
776
|
-
activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : ""
|
|
777
|
-
].filter(Boolean);
|
|
778
|
-
return truncLine(` ${widgetStatusGlyph(job, theme)} ${parts.join(` ${theme.fg("dim", "\xB7")} `)}`, width);
|
|
779
|
-
}
|
|
780
|
-
function progressiveHiddenLine(hiddenJobs, theme, width) {
|
|
781
|
-
let counts = widgetHeaderCounts(hiddenJobs), parts = [];
|
|
782
|
-
counts.running.length > 0 && parts.push(`${counts.running.length} running`), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`);
|
|
783
|
-
let finished = counts.complete.length + counts.failed.length + counts.paused.length + counts.stopped.length;
|
|
784
|
-
return finished > 0 && parts.push(`${finished} finished`), truncLine(theme.fg("dim", ` +${hiddenJobs.length} more${parts.length ? ` (${parts.join(", ")})` : ""}`), width);
|
|
785
|
-
}
|
|
786
|
-
function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKeys) {
|
|
787
|
-
let rowCount = Math.max(1, lockedRows);
|
|
788
|
-
if (rowCount === 1)
|
|
789
|
-
return { lines: buildSingleLineWidgetLines(jobs, theme, width), visibleJobKeys: [] };
|
|
790
|
-
let bodyRows = rowCount - 1, visibleJobKeys = selectProgressiveJobKeys(jobs, previousKeys, bodyRows), jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job])), visibleJobs = visibleJobKeys.map((key) => jobsByKey.get(key)).filter((job) => !!job), hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job)));
|
|
791
|
-
hiddenJobs.length > 0 && visibleJobs.length >= bodyRows && bodyRows > 0 && (visibleJobs = visibleJobs.slice(0, bodyRows - 1), visibleJobKeys = visibleJobs.map(progressiveJobKey), hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job))));
|
|
792
|
-
let lines = [
|
|
793
|
-
progressiveHeaderLine(jobs, theme, width),
|
|
794
|
-
...visibleJobs.map((job) => progressiveJobLine(job, theme, width))
|
|
795
|
-
];
|
|
796
|
-
for (hiddenJobs.length > 0 && lines.length < rowCount && lines.push(progressiveHiddenLine(hiddenJobs, theme, width)); lines.length < rowCount; )
|
|
797
|
-
lines.push(" ");
|
|
798
|
-
return { lines: lines.slice(0, rowCount), visibleJobKeys };
|
|
799
|
-
}
|
|
800
673
|
function collapsedWidgetLineBudget(rows) {
|
|
801
674
|
return Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
|
|
802
675
|
}
|
|
@@ -807,150 +680,185 @@ function fitWidgetLineBudget(lines, theme, width, expanded) {
|
|
|
807
680
|
let visibleLines = Math.max(1, budget - 1), hiddenCount = lines.length - visibleLines, hint = expanded ? `\u2026 ${hiddenCount} live-detail lines hidden` : `\u2026 ${hiddenCount} lines hidden \xB7 ${liveDetailKeyText()} expands`;
|
|
808
681
|
return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
|
|
809
682
|
}
|
|
810
|
-
function fitAdaptiveWidgetLines(
|
|
683
|
+
function fitAdaptiveWidgetLines(_jobs, lines, theme, width, expanded) {
|
|
811
684
|
if (expanded)
|
|
812
685
|
return resetWidgetLayoutSession(), fitWidgetLineBudget(lines, theme, width, !0);
|
|
813
|
-
let
|
|
814
|
-
if (hasMatchingSession && widgetLayoutSession?.tier === "single-line")
|
|
815
|
-
return buildSingleLineWidgetLines(jobs, theme, width);
|
|
816
|
-
if (hasMatchingSession && widgetLayoutSession?.tier === "progressive" && widgetLayoutSession.lockedRows !== void 0) {
|
|
817
|
-
let rendered2 = buildProgressiveWidgetLines(jobs, theme, width, widgetLayoutSession.lockedRows, widgetLayoutSession.visibleJobKeys);
|
|
818
|
-
return widgetLayoutSession.visibleJobKeys = rendered2.visibleJobKeys, rendered2.lines;
|
|
819
|
-
}
|
|
686
|
+
let rows = currentTerminalRows(), columns = currentTerminalColumns(), availableRows = estimateAvailableWidgetRows();
|
|
820
687
|
if (lines.length <= availableRows)
|
|
821
|
-
return widgetLayoutSession = { expanded, rows, columns, tier: "full"
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
return (
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
688
|
+
return widgetLayoutSession = { expanded, rows, columns, tier: "full" }, fitWidgetLineBudget(lines, theme, width, !1);
|
|
689
|
+
let lockedRows = widgetSessionMatches(expanded) && widgetLayoutSession?.tier === "progressive" ? widgetLayoutSession.lockedRows ?? 1 : Math.max(1, Math.min(availableRows, collapsedWidgetLineBudget(rows)));
|
|
690
|
+
if (widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows }, lockedRows === 1)
|
|
691
|
+
return lines.slice(0, 1);
|
|
692
|
+
let visibleCount = Math.max(1, lockedRows - 1), visible = lines.slice(0, visibleCount), hidden = lines.length - visible.length;
|
|
693
|
+
for (hidden > 0 && visible.push(truncLine(theme.fg("dim", `+${hidden} more`), width)); visible.length < lockedRows; )
|
|
694
|
+
visible.push(" ");
|
|
695
|
+
return visible.slice(0, lockedRows);
|
|
696
|
+
}
|
|
697
|
+
function widgetExpandedDiagnostics(job, step, index, theme, width, now) {
|
|
698
|
+
let lines = [], status = step?.status ?? job.status, runLabel = step ? `${job.asyncId}:${step.index ?? index}` : job.asyncId;
|
|
699
|
+
lines.push(truncLine(theme.fg("dim", ` \u23BF run ${runLabel} \xB7 ${status}`), width));
|
|
700
|
+
let error = step?.error;
|
|
701
|
+
error && lines.push(truncLine(theme.fg("error", ` ${error}`), width));
|
|
702
|
+
let snapshotNow = status === "running" ? now : step?.endedAt ?? job.updatedAt, activity = step ? widgetStepActivityLine(step, Math.max(1, width - 4), !0, snapshotNow) : widgetActivity(job, now);
|
|
703
|
+
activity && lines.push(truncLine(theme.fg("dim", ` ${activity}`), width));
|
|
704
|
+
for (let tool of step?.recentTools?.slice(-3) ?? [])
|
|
705
|
+
lines.push(truncLine(theme.fg("dim", ` ${tool.tool}${tool.args ? `: ${tool.args}` : ""}`), width));
|
|
706
|
+
for (let output of step?.recentOutput?.slice(-5) ?? [])
|
|
707
|
+
lines.push(truncLine(theme.fg("dim", ` ${output}`), width));
|
|
708
|
+
for (let nestedLine of formatNestedWidgetLines(step?.children ?? job.nestedChildren, theme, width, !0, snapshotNow))
|
|
709
|
+
lines.push(truncLine(` ${nestedLine}`, width));
|
|
710
|
+
return lines;
|
|
834
711
|
}
|
|
835
|
-
function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7 background")}`, width));
|
|
842
|
-
let items = [], hiddenRunning = 0, hiddenFinished = 0, queuedSummaryShown = !1, slots = MAX_WIDGET_JOBS;
|
|
843
|
-
for (let job of running) {
|
|
844
|
-
if (slots <= 0) {
|
|
845
|
-
hiddenRunning++;
|
|
846
|
-
continue;
|
|
847
|
-
}
|
|
848
|
-
let stats = widgetStats(job, theme);
|
|
849
|
-
items.push([
|
|
850
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
|
|
851
|
-
` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
|
|
852
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
853
|
-
]), slots--;
|
|
854
|
-
}
|
|
855
|
-
queued.length > 0 && slots > 0 && (items.push([`${theme.fg("muted", "\u25E6")} ${theme.fg("dim", `${queued.length} queued`)}`]), queuedSummaryShown = !0, slots--);
|
|
856
|
-
for (let job of finished) {
|
|
857
|
-
if (slots <= 0) {
|
|
858
|
-
hiddenFinished++;
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
861
|
-
let stats = widgetStats(job, theme);
|
|
862
|
-
items.push([
|
|
863
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
|
|
864
|
-
` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
|
|
865
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
866
|
-
]), slots--;
|
|
867
|
-
}
|
|
868
|
-
let hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0, hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
869
|
-
if (hiddenTotal > 0) {
|
|
870
|
-
let parts = [];
|
|
871
|
-
hiddenRunning > 0 && parts.push(`${hiddenRunning} running`), hiddenQueued > 0 && parts.push(`${hiddenQueued} queued`), hiddenFinished > 0 && parts.push(`${hiddenFinished} finished`), items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
|
|
872
|
-
}
|
|
873
|
-
for (let i = 0; i < items.length; i++) {
|
|
874
|
-
let item = items[i], last = i === items.length - 1, branch = last ? "\u2514\u2500" : "\u251C\u2500", continuation = last ? " " : "\u2502 ";
|
|
875
|
-
lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
|
|
876
|
-
for (let detail of item.slice(1))
|
|
877
|
-
lines.push(truncLine(`${theme.fg("dim", continuation)} ${detail}`, width));
|
|
712
|
+
function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1, now = Date.now()) {
|
|
713
|
+
let lines = [];
|
|
714
|
+
for (let job of orderedWidgetJobs(jobs)) {
|
|
715
|
+
let steps = job.steps?.length ? job.steps : job.agents?.length ? job.agents.map((agent, index) => ({ agent, description: agent, status: job.status, index })) : [void 0];
|
|
716
|
+
for (let [index, step] of steps.entries())
|
|
717
|
+
lines.push(...compactJobLines(job, step, theme, width, now)), expanded && lines.push(...widgetExpandedDiagnostics(job, step, index, theme, width, now));
|
|
878
718
|
}
|
|
879
719
|
return lines;
|
|
880
720
|
}
|
|
881
|
-
var
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
721
|
+
var activeWidget, SubagentAsyncWidget = class {
|
|
722
|
+
jobs;
|
|
723
|
+
expanded;
|
|
724
|
+
theme;
|
|
725
|
+
requestRender;
|
|
726
|
+
nowFn;
|
|
727
|
+
timer;
|
|
728
|
+
disposed = !1;
|
|
729
|
+
cachedWidth;
|
|
730
|
+
cachedLines;
|
|
731
|
+
constructor(jobs, theme, options) {
|
|
732
|
+
this.jobs = jobs, this.theme = theme, this.expanded = options?.expanded ?? !1, this.requestRender = options?.requestRender, this.nowFn = options?.now ?? Date.now, this.syncTimer();
|
|
733
|
+
}
|
|
734
|
+
setTheme(theme) {
|
|
735
|
+
this.theme = theme, this.invalidate();
|
|
736
|
+
}
|
|
737
|
+
setJobs(jobs) {
|
|
738
|
+
this.jobs = jobs, this.invalidate(), this.syncTimer();
|
|
739
|
+
}
|
|
740
|
+
setExpanded(expanded) {
|
|
741
|
+
this.expanded !== expanded && (this.expanded = expanded, this.invalidate());
|
|
742
|
+
}
|
|
743
|
+
setRequestRender(requestRender) {
|
|
744
|
+
this.requestRender = requestRender;
|
|
745
|
+
}
|
|
746
|
+
hasRunning() {
|
|
747
|
+
return this.jobs.some((job) => job.status === "running");
|
|
748
|
+
}
|
|
749
|
+
syncTimer() {
|
|
750
|
+
this.disposed || (this.hasRunning() ? this.startTimer() : this.stopTimer());
|
|
751
|
+
}
|
|
752
|
+
startTimer() {
|
|
753
|
+
this.timer || (this.timer = setInterval(() => {
|
|
754
|
+
this.disposed || (this.invalidate(), this.requestRender?.());
|
|
755
|
+
}, SUBAGENT_PAINT_INTERVAL_MS), this.timer.unref?.());
|
|
756
|
+
}
|
|
757
|
+
stopTimer() {
|
|
758
|
+
this.timer && (clearInterval(this.timer), this.timer = void 0);
|
|
889
759
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
760
|
+
invalidate() {
|
|
761
|
+
this.cachedWidth = void 0, this.cachedLines = void 0;
|
|
762
|
+
}
|
|
763
|
+
dispose() {
|
|
764
|
+
this.disposed = !0, this.stopTimer(), activeWidget === this && (activeWidget = void 0);
|
|
765
|
+
}
|
|
766
|
+
render(width) {
|
|
767
|
+
let theme = this.theme;
|
|
768
|
+
if (!theme)
|
|
769
|
+
return [];
|
|
770
|
+
if (this.cachedLines && this.cachedWidth === width && !this.hasRunning())
|
|
771
|
+
return this.cachedLines;
|
|
772
|
+
let now = this.nowFn(), innerWidth = Math.max(1, width - 2), lines = buildWidgetLines(this.jobs, theme, innerWidth, this.expanded, now), padded = fitAdaptiveWidgetLines(this.jobs, lines, theme, innerWidth, this.expanded).map((line) => {
|
|
773
|
+
let withPad = ` ${line}`, vis = visibleWidth(withPad);
|
|
774
|
+
return withPad + " ".repeat(Math.max(0, width - vis));
|
|
775
|
+
});
|
|
776
|
+
return this.cachedWidth = width, this.cachedLines = padded, padded;
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
function disposeSubagentWidget() {
|
|
780
|
+
activeWidget?.dispose(), activeWidget = void 0;
|
|
893
781
|
}
|
|
894
782
|
function renderWidget(ctx, jobs) {
|
|
895
783
|
if (jobs.length === 0) {
|
|
896
|
-
resetWidgetLayoutSession(),
|
|
784
|
+
resetWidgetLayoutSession(), disposeSubagentWidget(), ctx.hasUI && ctx.ui.setWidget(WIDGET_KEY, void 0);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (!ctx.hasUI)
|
|
788
|
+
return;
|
|
789
|
+
let expanded = ctx.ui.getToolsExpanded?.() ?? !1, requestRender = () => ctx.ui.requestRender?.();
|
|
790
|
+
if (activeWidget) {
|
|
791
|
+
activeWidget.setJobs(jobs), activeWidget.setExpanded(expanded), activeWidget.setRequestRender(requestRender), ctx.ui.requestRender?.();
|
|
897
792
|
return;
|
|
898
793
|
}
|
|
899
|
-
|
|
794
|
+
let widget = new SubagentAsyncWidget(jobs, void 0, { expanded, requestRender });
|
|
795
|
+
activeWidget = widget, ctx.ui.setWidget(WIDGET_KEY, (_tui, theme) => (widget.setTheme(theme), widget));
|
|
796
|
+
}
|
|
797
|
+
function compactResultTerminalStatus(result2) {
|
|
798
|
+
if (result2.stopped)
|
|
799
|
+
return "stopped";
|
|
800
|
+
if (result2.detached)
|
|
801
|
+
return "detached";
|
|
802
|
+
if (result2.interrupted)
|
|
803
|
+
return "paused";
|
|
804
|
+
if (result2.exitCode !== 0)
|
|
805
|
+
return "failed";
|
|
900
806
|
}
|
|
901
807
|
function renderSingleCompact(d, r, theme, frame) {
|
|
902
|
-
let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running",
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
808
|
+
let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running", width = getTermWidth() - 4, lead = compactRowLead({ description: r.description ?? r.agent, task: r.task }), modelBadge = formatCompactModelBadge(r.modelSelection ?? progress?.modelSelection, r.model ?? progress?.model);
|
|
809
|
+
return animatedLine((animationTick, now) => formatCompactSubagentRow(theme, width, {
|
|
810
|
+
glyph: resultGlyph(r, output, theme, isRunning, void 0, foregroundSpinnerFrame(animationTick)),
|
|
811
|
+
description: lead,
|
|
812
|
+
modelBadge,
|
|
813
|
+
tokens: compactResultTokens(r),
|
|
814
|
+
durationMs: liveDurationMs(progress, isRunning, now),
|
|
815
|
+
terminalStatus: compactResultTerminalStatus(r)
|
|
816
|
+
}), frame, isRunning);
|
|
817
|
+
}
|
|
818
|
+
function progressStatusGlyph(progress, theme, frame) {
|
|
819
|
+
return !progress || progress.status === "pending" ? theme.fg("muted", "\u25E6") : progress.status === "running" ? theme.fg("accent", runningGlyph((progressRunningSeed(progress) ?? 0) + (frame ?? 0))) : progress.status === "completed" ? theme.fg("success", "\u2713") : progress.status === "failed" ? theme.fg("error", "\u2717") : theme.fg("warning", "\u25A0");
|
|
820
|
+
}
|
|
821
|
+
function flattenCompactWorkflowNodes(nodes) {
|
|
822
|
+
let rows = [];
|
|
823
|
+
for (let node of nodes)
|
|
824
|
+
node.children?.length ? rows.push(...flattenCompactWorkflowNodes(node.children)) : rows.push(node);
|
|
825
|
+
return rows;
|
|
826
|
+
}
|
|
827
|
+
function compactForegroundEntries(d) {
|
|
828
|
+
let workflowNodes = flattenCompactWorkflowNodes(d.workflowGraph?.nodes ?? []);
|
|
829
|
+
if (workflowNodes.length === 0) {
|
|
830
|
+
let progressCount2 = d.progress?.reduce((count2, entry) => Math.max(count2, entry.index + 1), 0) ?? 0, chainCount = d.mode === "chain" ? buildChainStepSpans(d).reduce((count2, span) => Math.max(count2, span.start + span.count), 0) : 0, count = Math.max(d.results.length, progressCount2, d.chainChildren?.length ?? 0, d.chainAgents?.length ?? 0, chainCount);
|
|
831
|
+
return Array.from({ length: count }, (_, flatIndex) => ({ flatIndex }));
|
|
832
|
+
}
|
|
833
|
+
let entries = workflowNodes.map((node) => ({ flatIndex: node.flatIndex, node })), seen = new Set(entries.flatMap((entry) => entry.flatIndex === void 0 ? [] : [entry.flatIndex])), progressCount = d.progress?.reduce((count, entry) => Math.max(count, entry.index + 1), 0) ?? 0, dataCount = Math.max(d.results.length, progressCount, d.chainChildren?.length ?? 0);
|
|
834
|
+
for (let flatIndex = 0; flatIndex < dataCount; flatIndex++)
|
|
835
|
+
seen.has(flatIndex) || entries.push({ flatIndex });
|
|
836
|
+
return entries;
|
|
911
837
|
}
|
|
912
838
|
function renderMultiCompact(d, theme, frame) {
|
|
913
|
-
let
|
|
914
|
-
|
|
915
|
-
let
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
});
|
|
931
|
-
for (let entry of renderEntries) {
|
|
932
|
-
if (entry.kind === "placeholder") {
|
|
933
|
-
let glyph2 = widgetStepGlyph(entry.status, theme), statusLabel = widgetStepStatus(entry.status, theme);
|
|
934
|
-
c.addChild(new Text(truncLine(` ${glyph2} ${entry.stepLabel}: ${themeBold(theme, entry.agentName)} ${theme.fg("dim", "\xB7")} ${statusLabel}`, width), 0, 0)), entry.error && c.addChild(new Text(truncLine(theme.fg("error", ` \u23BF Error: ${entry.error}`), width), 0, 0));
|
|
935
|
-
continue;
|
|
936
|
-
}
|
|
937
|
-
let i = entry.resultIndex, r = d.results[i], rowNumber = entry.rowNumber, agentName = entry.agentName;
|
|
938
|
-
if (!r) {
|
|
939
|
-
let pendingLabel2 = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
|
|
940
|
-
c.addChild(new Text(truncLine(theme.fg("dim", ` \u25E6 ${pendingLabel2}: ${agentName} \xB7 pending`), width), 0, 0));
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
let output = getSingleResultOutput(r), progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running"), rProg = r.progress || progressFromArray || r.progressSummary, rRunning = rProg && "status" in rProg && rProg.status === "running", rPending = rProg && "status" in rProg && rProg.status === "pending", pendingLabel = rPending ? ` ${theme.fg("dim", "\xB7 pending")}` : "", lead = compactRowLead({ description: r.description ?? agentName, task: r.task }), permissionBadge = r.permissions ? theme.fg("dim", r.permissions) : "", modelBadge = formatModelSelection(r.modelSelection ?? (rProg && "modelSelection" in rProg ? rProg.modelSelection : void 0), r.model ?? (rProg && "model" in rProg ? rProg.model : void 0), r.thinking ?? (rProg && "thinking" in rProg ? rProg.thinking : void 0));
|
|
944
|
-
c.addChild(animatedLine((f, now) => {
|
|
945
|
-
let glyph2 = rPending ? theme.fg("dim", "\u25E6") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), f), stepStats = formatProgressStats(theme, rProg, !0, { running: !!rRunning, terminal: !rRunning && !rPending, now });
|
|
946
|
-
return truncLine(` ${glyph2} ${themeBold(theme, lead)}${modelBadge ? ` ${theme.fg("dim", "\xB7")} ${theme.fg("dim", modelBadge)}` : ""}${permissionBadge ? ` ${theme.fg("dim", "\xB7")} ${permissionBadge}` : ""}${stepStats ? ` ${theme.fg("dim", "\xB7")} ${stepStats}` : ""}${pendingLabel}`, width);
|
|
947
|
-
}, frame, !!rRunning)), rRunning && rProg && "status" in rProg ? c.addChild(animatedLine((_f, now) => formatCompactStatsHangLine(rProg, width, (s) => theme.fg("dim", s), " \u23BF ", now, !0), frame, !0)) : !rPending && (r.exitCode !== 0 || r.interrupted || r.detached || hasEmptyTextOutputWithoutOutputTarget(r.task, output)) && c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` \u23BF ${resultStatusLine(r, output)}`), width), 0, 0));
|
|
839
|
+
let c = new Container(), width = getTermWidth() - 4, entries = compactForegroundEntries(d);
|
|
840
|
+
for (let [rowIndex, entry] of entries.entries()) {
|
|
841
|
+
let flatIndex = entry.flatIndex, result2 = flatIndex === void 0 ? void 0 : resultAtFlatIndex(d, flatIndex), progress = result2?.progress ?? (flatIndex === void 0 ? void 0 : d.progress?.find((candidate) => candidate.index === flatIndex)) ?? result2?.progressSummary, status = progress?.status ?? entry.node?.status ?? "pending", description = compactRowLead({
|
|
842
|
+
description: result2?.description ?? progress?.description ?? entry.node?.label ?? (flatIndex === void 0 ? void 0 : d.chainChildren?.[flatIndex]?.description) ?? (flatIndex === void 0 ? void 0 : d.chainAgents?.[flatIndex]) ?? result2?.agent ?? progress?.agent,
|
|
843
|
+
task: result2?.task ?? progress?.task
|
|
844
|
+
}) || `child ${rowIndex + 1}`, running = status === "running", output = result2 ? getSingleResultOutput(result2) : "", modelBadge = formatCompactModelBadge(result2?.modelSelection ?? progress?.modelSelection, result2?.model ?? progress?.model);
|
|
845
|
+
c.addChild(animatedLine((animationTick, now) => {
|
|
846
|
+
let animationFrame = foregroundSpinnerFrame(animationTick);
|
|
847
|
+
return formatCompactSubagentRow(theme, width, {
|
|
848
|
+
glyph: result2 ? resultGlyph(result2, output, theme, running, progressRunningSeed(progress), animationFrame) : progressStatusGlyph({ ...progress, status, index: flatIndex }, theme, animationFrame),
|
|
849
|
+
description,
|
|
850
|
+
modelBadge,
|
|
851
|
+
tokens: result2 ? compactResultTokens(result2) : progress?.tokens ?? 0,
|
|
852
|
+
durationMs: liveDurationMs(progress, running, now),
|
|
853
|
+
terminalStatus: result2 ? compactResultTerminalStatus(result2) : compactTerminalStatus(status)
|
|
854
|
+
});
|
|
855
|
+
}, frame, running));
|
|
948
856
|
}
|
|
949
857
|
return c;
|
|
950
858
|
}
|
|
951
859
|
function renderSubagentResult(result2, options, theme, frame) {
|
|
952
|
-
let d = result2.details;
|
|
953
|
-
if (!d || !d.results.length) {
|
|
860
|
+
let d = result2.details, hasWorkflowRows = d?.mode !== "management" && !!(d?.workflowGraph?.nodes.length || d?.progress?.length || d?.chainChildren?.length);
|
|
861
|
+
if (!d || !d.results.length && !hasWorkflowRows) {
|
|
954
862
|
let t = result2.content[0], text = t?.type === "text" ? t.text : "(no output)", contextPrefix = d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "", width = getTermWidth() - 4;
|
|
955
863
|
if (!text.includes(`
|
|
956
864
|
`))
|
|
@@ -971,7 +879,7 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
971
879
|
return fit3(`${icon2} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge2}${stats ? ` | ${stats}` : ""}`);
|
|
972
880
|
}, frame, isRunning)), c2.addChild(new Spacer(1));
|
|
973
881
|
let taskMaxLen = Math.max(20, w2 - 8), taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
|
|
974
|
-
if (c2.addChild(new Text(fit3(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0)), c2.addChild(new Spacer(1)), isRunning && r.progress) {
|
|
882
|
+
if (c2.addChild(new Text(fit3(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0)), !isRunning && compactResultTerminalStatus(r) && c2.addChild(new Text(fit3(theme.fg(r.exitCode !== 0 ? "error" : "warning", resultStatusLine(r, output))), 0, 0)), c2.addChild(new Spacer(1)), isRunning && r.progress) {
|
|
975
883
|
let progressSnapshotNow = snapshotNowForProgress(r.progress), toolLine = formatCurrentToolLine(r.progress, w2, expanded, progressSnapshotNow);
|
|
976
884
|
toolLine && c2.addChild(new Text(fit3(theme.fg("warning", `> ${toolLine}`)), 0, 0));
|
|
977
885
|
let liveStatusLine = buildLiveStatusLine(r.progress, progressSnapshotNow);
|
|
@@ -993,7 +901,7 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
993
901
|
}
|
|
994
902
|
if (!expanded)
|
|
995
903
|
return renderMultiCompact(d, theme, frame);
|
|
996
|
-
let hasRunning = d.progress?.some((p) => p.status === "running") || d.results.some((r) => r.progress?.status === "running") || workflowGraphHasStatus(d, ["running"]), ok = d.results.filter((r) => r.progress?.status === "completed" || r.exitCode === 0 && r.progress?.status !== "running").length, hasEmptyWithoutTarget = d.results.some((r) => r.exitCode === 0 && r.progress?.status !== "running" && hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r))), hasWorkflowFailure = workflowGraphHasStatus(d, ["failed"]), hasWorkflowStop = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]), hasWorkflowPause = workflowGraphHasStatus(d, ["paused", "detached"]), icon = hasRunning ? theme.fg("warning", "running") : hasEmptyWithoutTarget ? theme.fg("warning", "warning") : hasWorkflowFailure ? theme.fg("error", "failed") : hasWorkflowStop ? theme.fg("warning", "stopped") : hasWorkflowPause ? theme.fg("warning", "paused") : ok === d.results.length ? theme.fg("success", "ok") : theme.fg("error", "failed"), totalSummary = d.progressSummary || d.results.reduce((acc, r) => {
|
|
904
|
+
let hasRunning = d.progress?.some((p) => p.status === "running") || d.results.some((r) => r.progress?.status === "running") || workflowGraphHasStatus(d, ["running"]), ok = d.results.filter((r) => r.progress?.status === "completed" || r.exitCode === 0 && r.progress?.status !== "running").length, hasEmptyWithoutTarget = d.results.some((r) => r.exitCode === 0 && r.progress?.status !== "running" && hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r))), hasWorkflowFailure = workflowGraphHasStatus(d, ["failed"]), hasWorkflowStop = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]), hasWorkflowPause = workflowGraphHasStatus(d, ["paused", "detached"]), hasWorkflowPending = workflowGraphHasStatus(d, ["pending"]), icon = hasRunning ? theme.fg("warning", "running") : hasEmptyWithoutTarget ? theme.fg("warning", "warning") : hasWorkflowFailure ? theme.fg("error", "failed") : hasWorkflowStop ? theme.fg("warning", "stopped") : hasWorkflowPause ? theme.fg("warning", "paused") : hasWorkflowPending ? theme.fg("dim", "pending") : ok === d.results.length ? theme.fg("success", "ok") : theme.fg("error", "failed"), totalSummary = d.progressSummary || d.results.reduce((acc, r) => {
|
|
997
905
|
let prog = r.progress || r.progressSummary;
|
|
998
906
|
return prog && (acc.toolCount += prog.toolCount, acc.tokens += prog.tokens, acc.durationMs = d.mode === "chain" ? acc.durationMs + prog.durationMs : Math.max(acc.durationMs, prog.durationMs)), acc;
|
|
999
907
|
}, { toolCount: 0, tokens: 0, durationMs: 0 }), modeLabel = d.mode, contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "", multiLabel = buildMultiProgressLabel(d, hasRunning), itemTitle = multiLabel.itemTitle, chainVis = d.chainAgents?.length && !multiLabel.hasParallelInChain ? d.chainAgents.map((agent, i) => {
|
|
@@ -1007,10 +915,26 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
1007
915
|
].filter(Boolean), summaryStr = summaryParts.length ? ` | ${summaryParts.join(", ")}` : "";
|
|
1008
916
|
return fit2(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} \xB7 ${multiLabel.headerLabel}${summaryStr}`);
|
|
1009
917
|
}, frame, hasRunning)), chainVis && c.addChild(new Text(fit2(` ${chainVis}`), 0, 0));
|
|
1010
|
-
let
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
918
|
+
let chainEntries = buildChainRenderEntries(d, multiLabel), fallbackEntries = compactForegroundEntries(d).filter((entry) => !multiLabel.showActiveGroupOnly || entry.flatIndex === void 0 || entry.flatIndex >= multiLabel.groupStartIndex && entry.flatIndex < multiLabel.groupEndIndex).map((entry, rowIndex) => {
|
|
919
|
+
if (entry.flatIndex === void 0)
|
|
920
|
+
return {
|
|
921
|
+
kind: "placeholder",
|
|
922
|
+
rowNumber: entry.node?.stepIndex !== void 0 ? entry.node.stepIndex + 1 : rowIndex + 1,
|
|
923
|
+
stepLabel: `Step ${entry.node?.stepIndex !== void 0 ? entry.node.stepIndex + 1 : rowIndex + 1}`,
|
|
924
|
+
agentName: entry.node?.label ?? `step-${rowIndex + 1}`,
|
|
925
|
+
status: entry.node?.status ?? "pending",
|
|
926
|
+
error: entry.node?.error
|
|
927
|
+
};
|
|
928
|
+
let result3 = resultAtFlatIndex(d, entry.flatIndex), rowNumber = multiLabel.showActiveGroupOnly ? entry.flatIndex - multiLabel.groupStartIndex + 1 : entry.flatIndex + 1;
|
|
929
|
+
return {
|
|
930
|
+
kind: "result",
|
|
931
|
+
resultIndex: entry.flatIndex,
|
|
932
|
+
rowNumber,
|
|
933
|
+
agentName: result3?.agent ?? entry.node?.label ?? d.chainChildren?.[entry.flatIndex]?.description ?? d.chainAgents?.[entry.flatIndex] ?? `step-${rowNumber}`,
|
|
934
|
+
status: entry.node?.status,
|
|
935
|
+
error: entry.node?.error
|
|
936
|
+
};
|
|
937
|
+
}), renderEntries = chainEntries ?? fallbackEntries;
|
|
1014
938
|
c.addChild(new Spacer(1));
|
|
1015
939
|
for (let entry of renderEntries) {
|
|
1016
940
|
if (entry.kind === "placeholder") {
|
|
@@ -1018,10 +942,10 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
1018
942
|
c.addChild(new Text(fit2(` ${statusLabel} ${entry.stepLabel}: ${theme.bold(entry.agentName)}`), 0, 0)), c.addChild(new Text(theme.fg(entry.status === "failed" ? "error" : "dim", ` status: ${entry.status}`), 0, 0)), entry.error && c.addChild(new Text(theme.fg("error", ` error: ${entry.error}`), 0, 0)), c.addChild(new Spacer(1));
|
|
1019
943
|
continue;
|
|
1020
944
|
}
|
|
1021
|
-
let i = entry.resultIndex, r = d
|
|
945
|
+
let i = entry.resultIndex, r = resultAtFlatIndex(d, i), rowNumber = entry.rowNumber, agentName = entry.agentName;
|
|
1022
946
|
if (!r) {
|
|
1023
|
-
let
|
|
1024
|
-
c.addChild(new Text(fit2(theme.fg("dim", ` ${
|
|
947
|
+
let rowLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`, status = entry.status ?? "pending";
|
|
948
|
+
c.addChild(new Text(fit2(theme.fg(status === "failed" ? "error" : "dim", ` ${rowLabel}: ${agentName}`)), 0, 0)), c.addChild(new Text(theme.fg(status === "failed" ? "error" : "dim", ` status: ${status}`), 0, 0)), entry.error && c.addChild(new Text(theme.fg("error", ` error: ${entry.error}`), 0, 0)), c.addChild(new Spacer(1));
|
|
1025
949
|
continue;
|
|
1026
950
|
}
|
|
1027
951
|
let progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running"), rProg = r.progress || progressFromArray || r.progressSummary, rRunning = rProg?.status === "running", stepNumber = typeof rProg?.index == "number" ? rProg.index + 1 : i + 1, resultOutput = getSingleResultOutput(r), statusIcon = rRunning ? theme.fg("warning", "running") : r.exitCode !== 0 ? theme.fg("error", "failed") : hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput) ? theme.fg("warning", "warning") : theme.fg("success", "done"), modelDisplay = modelThinkingBadge(theme, r.model, r.thinking ?? (rProg && "thinking" in rProg ? rProg.thinking : void 0), r.modelSelection ?? (rProg && "modelSelection" in rProg ? rProg.modelSelection : void 0)), stepLabel = resultRowLabel(d, multiLabel, i, stepNumber), toolCallLines = getToolCallLines(r, expanded);
|
|
@@ -1030,7 +954,7 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
1030
954
|
return fit2(rRunning ? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats ? ` | ${stats}` : ""}` : `${statusIcon} ${stepLabel}: ${theme.bold(r.agent)}${modelDisplay}${stats ? ` | ${stats}` : ""}`);
|
|
1031
955
|
}, frame, rRunning));
|
|
1032
956
|
let taskMaxLen = Math.max(20, w - 12), taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
|
|
1033
|
-
if (c.addChild(new Text(fit2(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0)), r.skills?.length && c.addChild(new Text(fit2(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0)), r.skillsWarning && c.addChild(new Text(fit2(theme.fg("warning", ` Warning: ${r.skillsWarning}`)), 0, 0)), r.attemptedModels && r.attemptedModels.length > 1 && c.addChild(new Text(fit2(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" \u2192 ")}`)), 0, 0)), rRunning && rProg) {
|
|
957
|
+
if (c.addChild(new Text(fit2(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0)), !rRunning && compactResultTerminalStatus(r) && c.addChild(new Text(fit2(theme.fg(r.exitCode !== 0 ? "error" : "warning", ` ${resultStatusLine(r, resultOutput)}`)), 0, 0)), r.skills?.length && c.addChild(new Text(fit2(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0)), r.skillsWarning && c.addChild(new Text(fit2(theme.fg("warning", ` Warning: ${r.skillsWarning}`)), 0, 0)), r.attemptedModels && r.attemptedModels.length > 1 && c.addChild(new Text(fit2(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" \u2192 ")}`)), 0, 0)), rRunning && rProg) {
|
|
1034
958
|
rProg.skills?.length && c.addChild(new Text(fit2(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`)), 0, 0));
|
|
1035
959
|
let progressSnapshotNow = snapshotNowForProgress(rProg), toolLine = formatCurrentToolLine(rProg, w, expanded, progressSnapshotNow);
|
|
1036
960
|
toolLine && c.addChild(new Text(fit2(theme.fg("warning", ` > ${toolLine}`)), 0, 0));
|
|
@@ -1059,15 +983,15 @@ import { Type } from "typebox";
|
|
|
1059
983
|
function keepTopLevelParameterDescriptions(schema) {
|
|
1060
984
|
return pruneNestedDescriptions(schema, []);
|
|
1061
985
|
}
|
|
1062
|
-
function pruneNestedDescriptions(value,
|
|
986
|
+
function pruneNestedDescriptions(value, path13) {
|
|
1063
987
|
if (!value || typeof value != "object")
|
|
1064
988
|
return value;
|
|
1065
989
|
let result2 = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
|
|
1066
990
|
for (let key of Reflect.ownKeys(value)) {
|
|
1067
991
|
let descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1068
|
-
if (descriptor && !(key === "description" && !isPropertiesMap(
|
|
992
|
+
if (descriptor && !(key === "description" && !isPropertiesMap(path13) && !isTopLevelParameterHelpText(path13))) {
|
|
1069
993
|
if ("value" in descriptor) {
|
|
1070
|
-
let nextPath = typeof key == "string" ? [...
|
|
994
|
+
let nextPath = typeof key == "string" ? [...path13, key] : path13;
|
|
1071
995
|
descriptor.value = pruneNestedDescriptions(descriptor.value, nextPath);
|
|
1072
996
|
}
|
|
1073
997
|
Object.defineProperty(result2, key, descriptor);
|
|
@@ -1075,11 +999,11 @@ function pruneNestedDescriptions(value, path12) {
|
|
|
1075
999
|
}
|
|
1076
1000
|
return result2;
|
|
1077
1001
|
}
|
|
1078
|
-
function isPropertiesMap(
|
|
1079
|
-
return
|
|
1002
|
+
function isPropertiesMap(path13) {
|
|
1003
|
+
return path13[path13.length - 1] === "properties";
|
|
1080
1004
|
}
|
|
1081
|
-
function isTopLevelParameterHelpText(
|
|
1082
|
-
return
|
|
1005
|
+
function isTopLevelParameterHelpText(path13) {
|
|
1006
|
+
return path13.length === 2 && path13[0] === "properties";
|
|
1083
1007
|
}
|
|
1084
1008
|
var SkillOverride = Type.Unsafe({
|
|
1085
1009
|
anyOf: [
|
|
@@ -1097,10 +1021,16 @@ var SkillOverride = Type.Unsafe({
|
|
|
1097
1021
|
}), OutputModeOverride = Type.String({
|
|
1098
1022
|
enum: ["inline", "file-only"],
|
|
1099
1023
|
description: "Return saved output inline (default) or only a concise file reference. file-only requires output to be a path."
|
|
1100
|
-
}), TierOverride = Type.String({
|
|
1024
|
+
}), TIER_SELECTION_DESCRIPTION = "Choose the lowest model tier that can reliably complete the task: 'light' for quick checks, fact gathering, and small code inspections; 'standard' for everyday coding, clearly described bugs, and focused reviews; 'heavy' for complex implementation, difficult debugging, architectural decisions, and large or high-risk reviews.", TierOverride = Type.Optional(Type.String({
|
|
1101
1025
|
enum: ["light", "standard", "heavy"],
|
|
1102
|
-
description:
|
|
1103
|
-
}),
|
|
1026
|
+
description: `Required by default. ${TIER_SELECTION_DESCRIPTION} Do not combine with model. The configured tier model must be available and authenticated.`
|
|
1027
|
+
})), ModelOverride = Type.Optional(Type.String({
|
|
1028
|
+
minLength: 1,
|
|
1029
|
+
description: "Explicit provider/model id only when the user names a model. Do not combine with tier. Optional thinking belongs in the thinking field or as a known :level suffix."
|
|
1030
|
+
})), ThinkingOverride = Type.Optional(Type.String({
|
|
1031
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1032
|
+
description: "Thinking level for an explicit model launch. Omit for tier launches (they use configured tier thinking)."
|
|
1033
|
+
})), ReadsOverride = Type.Unsafe({
|
|
1104
1034
|
anyOf: [
|
|
1105
1035
|
{ type: "array", items: { type: "string" } },
|
|
1106
1036
|
{ type: "boolean", enum: [!1] }
|
|
@@ -1147,6 +1077,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1147
1077
|
reads: Type.Optional(ReadsOverride),
|
|
1148
1078
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking for this task" })),
|
|
1149
1079
|
tier: TierOverride,
|
|
1080
|
+
model: ModelOverride,
|
|
1081
|
+
thinking: ThinkingOverride,
|
|
1150
1082
|
skill: Type.Optional(SkillOverride),
|
|
1151
1083
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1152
1084
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
@@ -1166,6 +1098,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1166
1098
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1167
1099
|
skill: Type.Optional(SkillOverride),
|
|
1168
1100
|
tier: TierOverride,
|
|
1101
|
+
model: ModelOverride,
|
|
1102
|
+
thinking: ThinkingOverride,
|
|
1169
1103
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1170
1104
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1171
1105
|
}), DynamicExpandSchema = Type.Object({
|
|
@@ -1195,6 +1129,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1195
1129
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1196
1130
|
skill: Type.Optional(SkillOverride),
|
|
1197
1131
|
tier: TierOverride,
|
|
1132
|
+
model: ModelOverride,
|
|
1133
|
+
thinking: ThinkingOverride,
|
|
1198
1134
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1199
1135
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1200
1136
|
}, { additionalProperties: !1 }), DynamicCollectSchema = Type.Object({
|
|
@@ -1217,6 +1153,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1217
1153
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1218
1154
|
skill: Type.Optional(SkillOverride),
|
|
1219
1155
|
tier: TierOverride,
|
|
1156
|
+
model: ModelOverride,
|
|
1157
|
+
thinking: ThinkingOverride,
|
|
1220
1158
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1221
1159
|
acceptance: Type.Optional(AcceptanceOverride),
|
|
1222
1160
|
parallel: Type.Optional(Type.Unsafe({
|
|
@@ -1234,7 +1172,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1234
1172
|
description: "Create isolated git worktrees for each parallel task."
|
|
1235
1173
|
}))
|
|
1236
1174
|
}, {
|
|
1237
|
-
description: "Chain step: use {task, description, tier, permissions?} for sequential, {parallel: [...]} for static concurrent execution, or {expand, parallel: {...}, collect} for dynamic fanout. Every executable child
|
|
1175
|
+
description: "Chain step: use {task, description, tier|model, permissions?} for sequential, {parallel: [...]} for static concurrent execution, or {expand, parallel: {...}, collect} for dynamic fanout. Every executable child needs exactly one of tier or model.",
|
|
1238
1176
|
additionalProperties: !1
|
|
1239
1177
|
}), ControlOverrides = Type.Object({
|
|
1240
1178
|
enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
|
|
@@ -1273,12 +1211,9 @@ var SkillOverride = Type.Unsafe({
|
|
|
1273
1211
|
})),
|
|
1274
1212
|
lines: Type.Optional(Type.Integer({ minimum: 1, maximum: 500, description: "Maximum transcript lines for action='status', view='transcript'. Defaults to 80." })),
|
|
1275
1213
|
message: Type.Optional(Type.String({ description: "Follow-up message for action='resume' or non-terminal guidance for action='steer'. Use index to choose a child from multi-child runs." })),
|
|
1276
|
-
scope: Type.Optional(Type.String({ enum: ["session", "user", "project"], description: "Scope for action='watchdog.configure'. Defaults to session to avoid persistent settings writes unless user/project is explicit." })),
|
|
1277
|
-
target: Type.Optional(Type.String({ enum: ["main", "children"], description: "Target for action='watchdog.configure'. Defaults to main." })),
|
|
1278
|
-
thinking: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "boolean", enum: [!1] }], description: "Thinking level for action='watchdog.configure' (off/minimal/low/medium/high/xhigh/max, inherit, or false for off)." })),
|
|
1279
1214
|
schedule: Type.Optional(Type.String({ description: "Explicit one-shot schedule for action='schedule'. Only honored when scheduledRuns.enabled is true. Use '+10m' or a future ISO timestamp with timezone; scheduled runs always launch async with fresh context." })),
|
|
1280
1215
|
scheduleName: Type.Optional(Type.String({ description: "Optional display name for action='schedule'." })),
|
|
1281
|
-
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
|
|
1216
|
+
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier|model, thinking?, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
|
|
1282
1217
|
concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or unlimited." })),
|
|
1283
1218
|
worktree: Type.Optional(Type.Boolean({
|
|
1284
1219
|
description: "Create isolated git worktrees for parallel tasks; requires clean git state."
|
|
@@ -1289,7 +1224,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1289
1224
|
description: "Children always start with a fresh session. Fork context is not available."
|
|
1290
1225
|
})),
|
|
1291
1226
|
chainDir: Type.Optional(Type.String({ description: "Persistent chain artifact directory; defaults to user-scoped temp storage." })),
|
|
1292
|
-
async: Type.Optional(Type.Boolean({ description: "Run in background
|
|
1227
|
+
async: Type.Optional(Type.Boolean({ description: "Run async in the background. Defaults to true when omitted; set false for immediate foreground results." })),
|
|
1293
1228
|
timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Optional run-level timeout in ms for foreground and async/background runs. Alias of maxRuntimeMs." })),
|
|
1294
1229
|
maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias of timeoutMs for optional run-level timeout in foreground and async/background runs." })),
|
|
1295
1230
|
turnBudget: Type.Optional(TurnBudgetOverride),
|
|
@@ -1300,7 +1235,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1300
1235
|
share: Type.Optional(Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)" })),
|
|
1301
1236
|
sessionDir: Type.Optional(Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" })),
|
|
1302
1237
|
// Clarification TUI
|
|
1303
|
-
clarify: Type.Optional(Type.Boolean({ description: "Show TUI
|
|
1238
|
+
clarify: Type.Optional(Type.Boolean({ description: "Show the interactive TUI preview/editor before execution. clarify:true keeps the run foreground even when async is omitted or true." })),
|
|
1304
1239
|
control: Type.Optional(ControlOverrides),
|
|
1305
1240
|
output: Type.Optional(Type.Unsafe({
|
|
1306
1241
|
anyOf: [
|
|
@@ -1313,15 +1248,26 @@ var SkillOverride = Type.Unsafe({
|
|
|
1313
1248
|
skill: Type.Optional(SkillOverride),
|
|
1314
1249
|
tier: Type.Optional(Type.String({
|
|
1315
1250
|
enum: ["light", "standard", "heavy"],
|
|
1316
|
-
description:
|
|
1251
|
+
description: `Preferred for SINGLE execution. Required by default. Omit for control actions. Do not combine with model; PARALLEL and CHAIN children carry their own tier or model. ${TIER_SELECTION_DESCRIPTION}`
|
|
1252
|
+
})),
|
|
1253
|
+
model: Type.Optional(Type.String({
|
|
1254
|
+
minLength: 1,
|
|
1255
|
+
description: "Explicit provider/model for SINGLE execution only when the user names a model. Do not combine with tier."
|
|
1256
|
+
})),
|
|
1257
|
+
thinking: Type.Optional(Type.String({
|
|
1258
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1259
|
+
description: "Thinking level for an explicit model launch. Tier launches use configured tier thinking instead."
|
|
1317
1260
|
})),
|
|
1318
1261
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1319
1262
|
}), SubagentParams = keepTopLevelParameterDescriptions(SubagentParamsSchema), SubagentWaitParamsSchema = Type.Object({
|
|
1320
1263
|
id: Type.Optional(Type.String({
|
|
1321
1264
|
description: "Async run or remembered detached foreground run id/prefix to wait for one specific run. Omit to wait across every active async run started in this session."
|
|
1322
1265
|
})),
|
|
1266
|
+
questionId: Type.Optional(Type.String({
|
|
1267
|
+
description: "Wait for one supervisor question id from subagent_supervisor action='ask'. Returns when that question is answered, expired, or cancelled. Separate from final async run results."
|
|
1268
|
+
})),
|
|
1323
1269
|
all: Type.Optional(Type.Boolean({
|
|
1324
|
-
description: "Wait for ALL active runs to finish. Default false: return as soon as the first run finishes, so a fleet manager can spawn a replacement and wait again. Ignored when id targets
|
|
1270
|
+
description: "Wait for ALL active runs to finish. Default false: return as soon as the first run finishes, so a fleet manager can spawn a replacement and wait again. Ignored when id or questionId targets one item."
|
|
1325
1271
|
})),
|
|
1326
1272
|
timeoutMs: Type.Optional(Type.Integer({
|
|
1327
1273
|
minimum: 1,
|
|
@@ -1346,28 +1292,28 @@ function typeName(value) {
|
|
|
1346
1292
|
function quoteList(keys) {
|
|
1347
1293
|
return keys.map((key) => `"${key}"`).join(", ");
|
|
1348
1294
|
}
|
|
1349
|
-
function disallowedMessage(
|
|
1295
|
+
function disallowedMessage(path13, keys, allowed, example) {
|
|
1350
1296
|
let subject = keys.length === 1 ? `property "${keys[0]}" is not allowed` : `properties ${quoteList(keys)} are not allowed`, examplePart = example ? `
|
|
1351
1297
|
|
|
1352
1298
|
Example: ${example}` : "";
|
|
1353
|
-
return `subagent ${
|
|
1299
|
+
return `subagent ${path13}: ${subject}.
|
|
1354
1300
|
Allowed properties: ${allowed.join(", ")}.${examplePart}`;
|
|
1355
1301
|
}
|
|
1356
|
-
function expectObject(value,
|
|
1302
|
+
function expectObject(value, path13, allowed, example) {
|
|
1357
1303
|
if (!isPlainObject(value)) {
|
|
1358
1304
|
let examplePart = example ? `
|
|
1359
1305
|
|
|
1360
1306
|
Example: ${example}` : "";
|
|
1361
|
-
throw new Error(`subagent ${
|
|
1307
|
+
throw new Error(`subagent ${path13}: expected an object, received ${typeName(value)}.
|
|
1362
1308
|
Allowed properties: ${allowed.join(", ")}.${examplePart}`);
|
|
1363
1309
|
}
|
|
1364
1310
|
}
|
|
1365
|
-
function checkNoExtraKeys(value,
|
|
1311
|
+
function checkNoExtraKeys(value, path13, schema, allowed, example) {
|
|
1366
1312
|
if (schema.additionalProperties !== !1)
|
|
1367
1313
|
return;
|
|
1368
1314
|
let extra = disallowedKeys(value, allowed);
|
|
1369
1315
|
if (extra.length > 0)
|
|
1370
|
-
throw new Error(disallowedMessage(
|
|
1316
|
+
throw new Error(disallowedMessage(path13, extra, allowed, example));
|
|
1371
1317
|
}
|
|
1372
1318
|
function validateChainInput(args) {
|
|
1373
1319
|
if (!isPlainObject(args))
|
|
@@ -1525,7 +1471,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
|
|
|
1525
1471
|
if (parsed.type !== "subagent.control")
|
|
1526
1472
|
return;
|
|
1527
1473
|
let record = parsed;
|
|
1528
|
-
if (!record.event || !Array.isArray(record.channels))
|
|
1474
|
+
if (!record.event || !Array.isArray(record.channels) || record.event.reason === "completion_guard")
|
|
1529
1475
|
return;
|
|
1530
1476
|
let payload = {
|
|
1531
1477
|
event: record.event,
|
|
@@ -1620,8 +1566,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
|
|
|
1620
1566
|
}
|
|
1621
1567
|
widgetRenderKey(job) !== widgetStateBefore && (widgetChanged = !0);
|
|
1622
1568
|
}
|
|
1623
|
-
|
|
1624
|
-
(widgetChanged || timerNeedsPaint) && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
|
|
1569
|
+
widgetChanged && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
|
|
1625
1570
|
}, pollIntervalMs), state.poller.unref?.());
|
|
1626
1571
|
};
|
|
1627
1572
|
return { ensurePoller, refreshWidget, handleStarted: (data) => {
|
|
@@ -1761,15 +1706,15 @@ function createFileCoalescer(handler, defaultDelayMs, timerApi = defaultTimerApi
|
|
|
1761
1706
|
|
|
1762
1707
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js
|
|
1763
1708
|
var WATCHER_RESTART_DELAY_MS = 3e3, POLL_INTERVAL_MS2 = 3e3;
|
|
1764
|
-
function sanitizeNestedResultChildren(value, resultPath, label) {
|
|
1709
|
+
function sanitizeNestedResultChildren(value, resultPath, label, report) {
|
|
1765
1710
|
if (value === void 0)
|
|
1766
1711
|
return;
|
|
1767
1712
|
if (!Array.isArray(value)) {
|
|
1768
|
-
|
|
1713
|
+
report(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
|
|
1769
1714
|
return;
|
|
1770
1715
|
}
|
|
1771
1716
|
let children = value.map((child) => sanitizeSummary(child)).filter((child) => !!child);
|
|
1772
|
-
return children.length !== value.length &&
|
|
1717
|
+
return children.length !== value.length && report(`Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`), children.length ? children : void 0;
|
|
1773
1718
|
}
|
|
1774
1719
|
function getErrorCode(error) {
|
|
1775
1720
|
return typeof error == "object" && error !== null && "code" in error ? error.code : void 0;
|
|
@@ -1782,27 +1727,27 @@ function shouldFallBackToPolling(error) {
|
|
|
1782
1727
|
return code === "EMFILE" || code === "ENOSPC";
|
|
1783
1728
|
}
|
|
1784
1729
|
function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {}) {
|
|
1785
|
-
let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval },
|
|
1786
|
-
|
|
1787
|
-
|
|
1730
|
+
let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval }, generation = 0, inFlight = /* @__PURE__ */ new Set(), attempts = /* @__PURE__ */ new Map(), report = (message, error) => {
|
|
1731
|
+
pi.appendEntry?.("subagent_result_delivery", { message, error: error instanceof Error ? error.message : error });
|
|
1732
|
+
}, handleResult = async (file) => {
|
|
1733
|
+
if (inFlight.has(file) || (attempts.get(file) ?? 0) >= 3)
|
|
1734
|
+
return;
|
|
1735
|
+
let currentGeneration = generation, sessionId = state.currentSessionId, isCurrent = () => generation === currentGeneration && state.currentSessionId === sessionId, resultPath = path2.join(resultsDir, file);
|
|
1736
|
+
if (fsApi.existsSync(resultPath)) {
|
|
1737
|
+
inFlight.add(file);
|
|
1788
1738
|
try {
|
|
1789
1739
|
let data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8"));
|
|
1790
1740
|
if (typeof data.sessionId != "string" || data.sessionId !== state.currentSessionId)
|
|
1791
1741
|
return;
|
|
1792
|
-
let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren"));
|
|
1742
|
+
let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren", report));
|
|
1793
1743
|
if (!nestedChildren?.length && !hasExplicitNestedChildren)
|
|
1794
1744
|
try {
|
|
1795
1745
|
nestedChildren = compactNestedResultChildren(projectNestedRegistryForRoot(runId)?.children);
|
|
1796
1746
|
} catch (error) {
|
|
1797
|
-
|
|
1747
|
+
report(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
|
|
1798
1748
|
return;
|
|
1799
1749
|
}
|
|
1800
|
-
let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`)
|
|
1801
|
-
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs)) {
|
|
1802
|
-
fsApi.unlinkSync(resultPath);
|
|
1803
|
-
return;
|
|
1804
|
-
}
|
|
1805
|
-
let hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
|
|
1750
|
+
let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`), hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
|
|
1806
1751
|
agent: data.agent,
|
|
1807
1752
|
output: data.summary,
|
|
1808
1753
|
success: data.success
|
|
@@ -1810,7 +1755,7 @@ function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {})
|
|
|
1810
1755
|
let baseOutput = result2.output ?? data.summary, hasRealOutput = typeof baseOutput == "string" && baseOutput.trim().length > 0, output = hasRealOutput ? baseOutput : "(no output)", summary = result2.success === !1 && result2.error ? `${result2.error}${hasRealOutput ? `
|
|
1811
1756
|
|
|
1812
1757
|
Output:
|
|
1813
|
-
${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children
|
|
1758
|
+
${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children`, report), childState = result2.state === "paused" || result2.state === "stopped" ? result2.state : result2.stopped === !0 ? "stopped" : data.state === "paused" || !hasResultChildren && (data.state === "stopped" || typeof result2.success != "boolean") ? data.state : void 0;
|
|
1814
1759
|
return {
|
|
1815
1760
|
agent: result2.agent ?? data.agent ?? `step-${index + 1}`,
|
|
1816
1761
|
status: resolveSubagentResultStatus({
|
|
@@ -1824,7 +1769,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1824
1769
|
...result2.intercomTarget ? { intercomTarget: result2.intercomTarget } : {},
|
|
1825
1770
|
...childNestedChildren ? { children: childNestedChildren } : {}
|
|
1826
1771
|
};
|
|
1827
|
-
}), nestedChildren)
|
|
1772
|
+
}), nestedChildren);
|
|
1773
|
+
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs) || pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
|
|
1774
|
+
...data,
|
|
1775
|
+
runId,
|
|
1776
|
+
...nestedChildren?.length ? { nestedChildren } : {},
|
|
1777
|
+
...Array.isArray(data.results) ? { results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
1778
|
+
...data.results[index],
|
|
1779
|
+
agent: child.agent,
|
|
1780
|
+
status: child.status,
|
|
1781
|
+
summary: child.summary,
|
|
1782
|
+
index: child.index,
|
|
1783
|
+
artifactPath: child.artifactPath,
|
|
1784
|
+
sessionPath: child.sessionPath,
|
|
1785
|
+
children: child.children
|
|
1786
|
+
})) : [] } : {}
|
|
1787
|
+
}), !isCurrent())
|
|
1788
|
+
return;
|
|
1789
|
+
let intercomTarget = data.state === "stopped" ? void 0 : data.intercomTarget?.trim();
|
|
1828
1790
|
if (intercomTarget) {
|
|
1829
1791
|
let mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain" ? data.mode : resultChildren.length > 1 ? "chain" : "single", payload = buildSubagentResultIntercomPayload({
|
|
1830
1792
|
to: intercomTarget,
|
|
@@ -1835,30 +1797,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1835
1797
|
asyncId: data.id,
|
|
1836
1798
|
asyncDir: data.asyncDir
|
|
1837
1799
|
});
|
|
1838
|
-
|
|
1800
|
+
payload.requestId = `async-result:${sessionId}:${completionKey}`, payload.ownerNotificationSessionId = sessionId, attempts.set(file, (attempts.get(file) ?? 0) + 1);
|
|
1801
|
+
let delivered = await deliverSubagentResultIntercomEvent(pi.events, payload);
|
|
1802
|
+
if (!isCurrent())
|
|
1803
|
+
return;
|
|
1804
|
+
if (!delivered) {
|
|
1805
|
+
report(`Async result delivery pending for '${runId}'; output retained.`, resultPath), attempts.get(file) < 3 && state.resultFileCoalescer.schedule(file, 1e3);
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1839
1808
|
}
|
|
1840
|
-
|
|
1841
|
-
...data,
|
|
1842
|
-
runId,
|
|
1843
|
-
...nestedChildren?.length ? { nestedChildren } : {},
|
|
1844
|
-
...Array.isArray(data.results) ? {
|
|
1845
|
-
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
1846
|
-
...data.results[index],
|
|
1847
|
-
agent: child.agent,
|
|
1848
|
-
status: child.status,
|
|
1849
|
-
summary: child.summary,
|
|
1850
|
-
index: child.index,
|
|
1851
|
-
artifactPath: child.artifactPath,
|
|
1852
|
-
sessionPath: child.sessionPath,
|
|
1853
|
-
children: child.children
|
|
1854
|
-
})) : []
|
|
1855
|
-
} : {}
|
|
1856
|
-
}), fsApi.unlinkSync(resultPath);
|
|
1809
|
+
isCurrent() && fsApi.unlinkSync(resultPath);
|
|
1857
1810
|
} catch (error) {
|
|
1858
1811
|
if (isNotFoundError(error))
|
|
1859
1812
|
return;
|
|
1860
|
-
|
|
1813
|
+
report(`Failed to process subagent result file '${resultPath}'.`, error);
|
|
1814
|
+
} finally {
|
|
1815
|
+
inFlight.delete(file);
|
|
1861
1816
|
}
|
|
1817
|
+
}
|
|
1862
1818
|
};
|
|
1863
1819
|
state.resultFileCoalescer = createFileCoalescer((file) => {
|
|
1864
1820
|
handleResult(file);
|
|
@@ -1869,10 +1825,10 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1869
1825
|
} catch (error) {
|
|
1870
1826
|
if (isNotFoundError(error))
|
|
1871
1827
|
return;
|
|
1872
|
-
|
|
1828
|
+
report(`Failed to scan subagent result directory '${resultsDir}':`, error);
|
|
1873
1829
|
}
|
|
1874
1830
|
}, startPollingFallback = (reason) => {
|
|
1875
|
-
state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (
|
|
1831
|
+
state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (report(`Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`), primeExistingResults(), state.watcherRestartTimer = timers.setInterval(primeExistingResults, POLL_INTERVAL_MS2), state.watcherRestartTimer.unref?.());
|
|
1876
1832
|
}, scheduleRestart = () => {
|
|
1877
1833
|
state.watcherRestartTimer || (state.watcherRestartTimer = timers.setTimeout(() => {
|
|
1878
1834
|
state.watcherRestartTimer = null;
|
|
@@ -1883,7 +1839,7 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1883
1839
|
startPollingFallback(error);
|
|
1884
1840
|
return;
|
|
1885
1841
|
}
|
|
1886
|
-
|
|
1842
|
+
report(`Failed to restart subagent result watcher for '${resultsDir}':`, error), scheduleRestart();
|
|
1887
1843
|
}
|
|
1888
1844
|
}, WATCHER_RESTART_DELAY_MS), state.watcherRestartTimer.unref?.());
|
|
1889
1845
|
}, startResultWatcher = () => {
|
|
@@ -1901,19 +1857,19 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1901
1857
|
startPollingFallback(error);
|
|
1902
1858
|
return;
|
|
1903
1859
|
}
|
|
1904
|
-
|
|
1860
|
+
report(`Subagent result watcher failed for '${resultsDir}':`, error), state.watcher?.close(), state.watcher = null, scheduleRestart();
|
|
1905
1861
|
}), state.watcher.unref?.();
|
|
1906
1862
|
} catch (error) {
|
|
1907
1863
|
if (shouldFallBackToPolling(error)) {
|
|
1908
1864
|
startPollingFallback(error);
|
|
1909
1865
|
return;
|
|
1910
1866
|
}
|
|
1911
|
-
|
|
1867
|
+
report(`Failed to start subagent result watcher for '${resultsDir}':`, error), state.watcher = null, scheduleRestart();
|
|
1912
1868
|
}
|
|
1913
1869
|
}
|
|
1914
1870
|
};
|
|
1915
1871
|
return { startResultWatcher, primeExistingResults, stopResultWatcher: () => {
|
|
1916
|
-
state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
|
|
1872
|
+
generation++, attempts.clear(), state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
|
|
1917
1873
|
} };
|
|
1918
1874
|
}
|
|
1919
1875
|
|
|
@@ -2446,7 +2402,7 @@ function substituteArgs(template, args) {
|
|
|
2446
2402
|
return template.replace(/\$ARGUMENTS/g, all).replace(/\$@/g, all).replace(/\$\{(\d+):-([^}]*)\}/g, (_match, index, fallback) => args[Number(index) - 1] || fallback).replace(/\$(\d+)/g, (_match, index) => args[Number(index) - 1] ?? "");
|
|
2447
2403
|
}
|
|
2448
2404
|
function parseRuntimeOptions(words) {
|
|
2449
|
-
let args = [], descriptionOverride, permissions, worktree = !1,
|
|
2405
|
+
let args = [], descriptionOverride, permissions, worktree = !1, async;
|
|
2450
2406
|
for (let i = 0; i < words.length; i++) {
|
|
2451
2407
|
let word = words[i];
|
|
2452
2408
|
if (word === "--read-only") {
|
|
@@ -2462,7 +2418,11 @@ function parseRuntimeOptions(words) {
|
|
|
2462
2418
|
continue;
|
|
2463
2419
|
}
|
|
2464
2420
|
if (word === "--bg" || word === "--async") {
|
|
2465
|
-
|
|
2421
|
+
async = !0;
|
|
2422
|
+
continue;
|
|
2423
|
+
}
|
|
2424
|
+
if (word === "--fg" || word === "--foreground") {
|
|
2425
|
+
async = !1;
|
|
2466
2426
|
continue;
|
|
2467
2427
|
}
|
|
2468
2428
|
if (word === "--description") {
|
|
@@ -2476,7 +2436,7 @@ function parseRuntimeOptions(words) {
|
|
|
2476
2436
|
}
|
|
2477
2437
|
args.push(word);
|
|
2478
2438
|
}
|
|
2479
|
-
return { args, descriptionOverride, permissions, worktree,
|
|
2439
|
+
return { args, descriptionOverride, permissions, worktree, async };
|
|
2480
2440
|
}
|
|
2481
2441
|
function splitChainDeclaration(input) {
|
|
2482
2442
|
let delimiter = input.indexOf(" -- ");
|
|
@@ -2495,7 +2455,7 @@ function workflowParams(workflow, args, runtime) {
|
|
|
2495
2455
|
...workflow.skill !== void 0 ? { skill: workflow.skill } : {},
|
|
2496
2456
|
...workflow.cwd ? { cwd: workflow.cwd } : {},
|
|
2497
2457
|
...runtime.worktree || workflow.worktree ? { worktree: !0 } : {},
|
|
2498
|
-
...runtime.
|
|
2458
|
+
...runtime.async !== void 0 ? { async: runtime.async } : {}
|
|
2499
2459
|
};
|
|
2500
2460
|
}
|
|
2501
2461
|
function workflowChainStep(workflow, args, runtime) {
|
|
@@ -2543,7 +2503,7 @@ function registerPromptWorkflowCommands(input) {
|
|
|
2543
2503
|
throw new Error(`Unknown prompt workflow in chain '${workflow.name}': ${stepName}`);
|
|
2544
2504
|
return workflowChainStep(step, runtime.args, runtime);
|
|
2545
2505
|
});
|
|
2546
|
-
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.
|
|
2506
|
+
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.async !== void 0 ? { async: runtime.async } : {} }, ctx);
|
|
2547
2507
|
return;
|
|
2548
2508
|
}
|
|
2549
2509
|
await run(workflowParams(workflow, runtime.args, runtime), ctx);
|
|
@@ -2571,7 +2531,7 @@ function registerPromptWorkflowCommands(input) {
|
|
|
2571
2531
|
throw new Error(`Unknown prompt workflow: ${name}`);
|
|
2572
2532
|
return workflowChainStep(workflow, runtime.args, runtime);
|
|
2573
2533
|
});
|
|
2574
|
-
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.
|
|
2534
|
+
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.async !== void 0 ? { async: runtime.async } : {} }, ctx);
|
|
2575
2535
|
} catch (error) {
|
|
2576
2536
|
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
2577
2537
|
}
|
|
@@ -3102,6 +3062,12 @@ var parseInlineConfig = (raw) => {
|
|
|
3102
3062
|
case "tier":
|
|
3103
3063
|
(val === "light" || val === "standard" || val === "heavy") && (config.tier = val);
|
|
3104
3064
|
break;
|
|
3065
|
+
case "model":
|
|
3066
|
+
val && (config.model = val);
|
|
3067
|
+
break;
|
|
3068
|
+
case "thinking":
|
|
3069
|
+
["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(val) && (config.thinking = val);
|
|
3070
|
+
break;
|
|
3105
3071
|
case "permissions":
|
|
3106
3072
|
(val === "full" || val === "read-only") && (config.permissions = val);
|
|
3107
3073
|
break;
|
|
@@ -3145,15 +3111,14 @@ var parseInlineConfig = (raw) => {
|
|
|
3145
3111
|
let end = token.lastIndexOf("]");
|
|
3146
3112
|
return { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : void 0)) };
|
|
3147
3113
|
}, extractExecutionFlags = (rawArgs) => {
|
|
3148
|
-
let args = rawArgs.trim(),
|
|
3114
|
+
let args = rawArgs.trim(), async;
|
|
3149
3115
|
for (; ; ) {
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
break;
|
|
3116
|
+
let match = args.match(/(?:^|\s)(--bg|--async|--fg|--foreground)$/);
|
|
3117
|
+
if (!match)
|
|
3118
|
+
break;
|
|
3119
|
+
async ??= match[1] === "--bg" || match[1] === "--async", args = args.slice(0, match.index).trim();
|
|
3155
3120
|
}
|
|
3156
|
-
return { args,
|
|
3121
|
+
return { args, async };
|
|
3157
3122
|
};
|
|
3158
3123
|
function sendSlashText(pi, text) {
|
|
3159
3124
|
pi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: !0 });
|
|
@@ -3651,12 +3616,16 @@ function loadInlineOutputSchema(baseCwd, agent, value) {
|
|
|
3651
3616
|
}
|
|
3652
3617
|
var mapParsedTaskToStepObject = (step, fallbackTask, isFirst, opts) => {
|
|
3653
3618
|
let { name, config, task: stepTask } = step;
|
|
3654
|
-
if (!config.tier)
|
|
3655
|
-
throw new SlashParseError(`Step '${name}' requires tier=light
|
|
3619
|
+
if (!config.tier && !config.model)
|
|
3620
|
+
throw new SlashParseError(`Step '${name}' requires tier=light|standard|heavy or model=provider/id.`);
|
|
3621
|
+
if (config.tier && config.model)
|
|
3622
|
+
throw new SlashParseError(`Step '${name}' must choose exactly one of tier or model.`);
|
|
3656
3623
|
return config.acceptance !== void 0 && validateInlineAcceptanceInput(config.acceptance, name), {
|
|
3657
3624
|
description: name,
|
|
3658
3625
|
permissions: config.permissions ?? "full",
|
|
3659
|
-
tier: config.tier,
|
|
3626
|
+
...config.tier ? { tier: config.tier } : {},
|
|
3627
|
+
...config.model ? { model: config.model } : {},
|
|
3628
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
3660
3629
|
...stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {},
|
|
3661
3630
|
...config.output !== void 0 ? { output: config.output } : {},
|
|
3662
3631
|
...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
|
|
@@ -3730,16 +3699,16 @@ function registerSlashCommands(pi, state) {
|
|
|
3730
3699
|
}
|
|
3731
3700
|
};
|
|
3732
3701
|
pi.registerCommand("run", {
|
|
3733
|
-
description: "Run
|
|
3702
|
+
description: "Run an async prompt-defined child; add --foreground to wait inline",
|
|
3734
3703
|
handler: async (args, ctx) => {
|
|
3735
|
-
let { args: cleanedArgs,
|
|
3704
|
+
let { args: cleanedArgs, async } = extractExecutionFlags(args), input = cleanedArgs.trim(), firstSpace = input.indexOf(" ");
|
|
3736
3705
|
if (!input) {
|
|
3737
|
-
ctx.ui.notify("Usage: /run <description> <task> [--
|
|
3706
|
+
ctx.ui.notify("Usage: /run <description> <task> [--foreground]", "error");
|
|
3738
3707
|
return;
|
|
3739
3708
|
}
|
|
3740
3709
|
let { name: description, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace)), task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
|
|
3741
3710
|
if (!description || !task || !inline.tier) {
|
|
3742
|
-
ctx.ui.notify("Usage: /run <description>[tier=light|standard|heavy] <task> [--
|
|
3711
|
+
ctx.ui.notify("Usage: /run <description>[tier=light|standard|heavy] <task> [--foreground]", "error");
|
|
3743
3712
|
return;
|
|
3744
3713
|
}
|
|
3745
3714
|
let finalTask = task;
|
|
@@ -3747,27 +3716,29 @@ function registerSlashCommands(pi, state) {
|
|
|
3747
3716
|
|
|
3748
3717
|
${finalTask}`);
|
|
3749
3718
|
let params = { description, permissions: inline.permissions ?? "full", tier: inline.tier, task: finalTask, clarify: !1 };
|
|
3750
|
-
inline.output !== void 0 && (params.output = inline.output), inline.outputMode !== void 0 && (params.outputMode = inline.outputMode), inline.skill !== void 0 && (params.skill = inline.skill),
|
|
3719
|
+
inline.output !== void 0 && (params.output = inline.output), inline.outputMode !== void 0 && (params.outputMode = inline.outputMode), inline.skill !== void 0 && (params.skill = inline.skill), async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
|
|
3751
3720
|
}
|
|
3752
3721
|
}), pi.registerCommand("chain", {
|
|
3753
|
-
description:
|
|
3722
|
+
description: "Run prompt-defined children as an async sequence; add --foreground to wait inline",
|
|
3754
3723
|
handler: async (args, ctx) => {
|
|
3755
|
-
let { args: cleanedArgs,
|
|
3724
|
+
let { args: cleanedArgs, async } = extractExecutionFlags(args), built = buildChainExpressionSteps(state, cleanedArgs, ctx);
|
|
3756
3725
|
if (!built)
|
|
3757
3726
|
return;
|
|
3758
3727
|
let params = { chain: built.chain, task: built.task, clarify: !1 };
|
|
3759
|
-
|
|
3728
|
+
async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
|
|
3760
3729
|
}
|
|
3761
3730
|
}), pi.registerCommand("parallel", {
|
|
3762
|
-
description:
|
|
3731
|
+
description: "Run prompt-defined children async in parallel; add --foreground to wait inline",
|
|
3763
3732
|
handler: async (args, ctx) => {
|
|
3764
|
-
let { args: cleanedArgs,
|
|
3733
|
+
let { args: cleanedArgs, async } = extractExecutionFlags(args), parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
|
|
3765
3734
|
if (!parsed)
|
|
3766
3735
|
return;
|
|
3767
3736
|
let tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
|
|
3768
3737
|
description: name,
|
|
3769
3738
|
permissions: config.permissions ?? "full",
|
|
3770
|
-
tier: config.tier,
|
|
3739
|
+
...config.tier ? { tier: config.tier } : {},
|
|
3740
|
+
...config.model ? { model: config.model } : {},
|
|
3741
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
3771
3742
|
task: stepTask ?? parsed.task,
|
|
3772
3743
|
...config.output !== void 0 ? { output: config.output } : {},
|
|
3773
3744
|
...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
|
|
@@ -3775,12 +3746,16 @@ ${finalTask}`);
|
|
|
3775
3746
|
...config.skill !== void 0 ? { skill: config.skill } : {},
|
|
3776
3747
|
...config.progress !== void 0 ? { progress: config.progress } : {}
|
|
3777
3748
|
}));
|
|
3778
|
-
if (tasks.some((task) => !task.tier)) {
|
|
3779
|
-
ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy].", "error");
|
|
3749
|
+
if (tasks.some((task) => !task.tier && !task.model)) {
|
|
3750
|
+
ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy] or [model=provider/id].", "error");
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3753
|
+
if (tasks.some((task) => task.tier && task.model)) {
|
|
3754
|
+
ctx.ui.notify("Each parallel child must choose exactly one of tier or model.", "error");
|
|
3780
3755
|
return;
|
|
3781
3756
|
}
|
|
3782
3757
|
let params = { tasks, clarify: !1 };
|
|
3783
|
-
|
|
3758
|
+
async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
|
|
3784
3759
|
}
|
|
3785
3760
|
}), pi.registerCommand("subagent-cost", {
|
|
3786
3761
|
description: "Show parent and subagent child usage cost for this session",
|
|
@@ -4404,28 +4379,506 @@ function registerSlashSubagentBridge(options) {
|
|
|
4404
4379
|
}
|
|
4405
4380
|
|
|
4406
4381
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
|
|
4382
|
+
import * as fs7 from "node:fs";
|
|
4383
|
+
import * as path8 from "node:path";
|
|
4384
|
+
import { Type as Type2 } from "typebox";
|
|
4385
|
+
|
|
4386
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js
|
|
4387
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
4407
4388
|
import * as fs6 from "node:fs";
|
|
4408
4389
|
import * as path7 from "node:path";
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4390
|
+
var QUESTIONS_DIR = "questions", SUPERVISOR_CHANNEL_ROOT = path7.join(TEMP_ROOT_DIR, "supervisor-channels"), SUBAGENT_SUPERVISOR_ANSWER_TYPE = "subagent_supervisor_answer", SUBAGENT_QUESTION_EVENT = "subagent:supervisor-question", MAX_MESSAGE_BYTES = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS = 600 * 1e3, activeQuestionWaits = /* @__PURE__ */ new Set();
|
|
4391
|
+
function askTimeoutMs() {
|
|
4392
|
+
let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
4393
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
|
|
4394
|
+
}
|
|
4395
|
+
function safeSegment(value) {
|
|
4396
|
+
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
4397
|
+
}
|
|
4398
|
+
function resolveSupervisorChannelDir(runId, agent, childIndex) {
|
|
4399
|
+
return path7.join(SUPERVISOR_CHANNEL_ROOT, `${safeSegment(runId)}-${safeSegment(agent)}-${childIndex}`);
|
|
4400
|
+
}
|
|
4401
|
+
function questionsDir(channelDir) {
|
|
4402
|
+
return path7.join(channelDir, QUESTIONS_DIR);
|
|
4403
|
+
}
|
|
4404
|
+
function questionFile(channelDir, questionId) {
|
|
4405
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.json`);
|
|
4406
|
+
}
|
|
4407
|
+
function deliveredFile(channelDir, questionId) {
|
|
4408
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.delivered.json`);
|
|
4409
|
+
}
|
|
4410
|
+
function terminalFile(channelDir, questionId) {
|
|
4411
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.terminal.json`);
|
|
4412
|
+
}
|
|
4413
|
+
function notifiedFile(channelDir, questionId) {
|
|
4414
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.notified.json`);
|
|
4415
|
+
}
|
|
4416
|
+
function lateFile(channelDir, questionId) {
|
|
4417
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.late.json`);
|
|
4418
|
+
}
|
|
4419
|
+
function writeExclusiveJson(filePath, payload) {
|
|
4420
|
+
fs6.mkdirSync(path7.dirname(filePath), { recursive: !0, mode: 448 });
|
|
4421
|
+
let temporary = `${filePath}.${randomUUID3()}.tmp`;
|
|
4422
|
+
try {
|
|
4423
|
+
return fs6.writeFileSync(temporary, JSON.stringify(payload), { encoding: "utf-8", flag: "wx", mode: 384 }), fs6.linkSync(temporary, filePath), !0;
|
|
4424
|
+
} catch (error) {
|
|
4425
|
+
if (error.code === "EEXIST")
|
|
4426
|
+
return !1;
|
|
4427
|
+
throw error;
|
|
4428
|
+
} finally {
|
|
4429
|
+
fs6.rmSync(temporary, { force: !0 });
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
function readJsonFile(filePath) {
|
|
4433
|
+
return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
|
|
4434
|
+
}
|
|
4435
|
+
function tryReadJson(filePath) {
|
|
4436
|
+
try {
|
|
4437
|
+
return readJsonFile(filePath);
|
|
4438
|
+
} catch (error) {
|
|
4439
|
+
return error.code === "ENOENT", void 0;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
function parseIncarnation(raw) {
|
|
4443
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4444
|
+
return;
|
|
4445
|
+
let input = raw, pid = input.pid, readyAt = input.readyAt;
|
|
4446
|
+
if (!(!Number.isInteger(pid) || pid === void 0 || pid <= 0) && !(typeof readyAt != "number" || !Number.isFinite(readyAt) || readyAt <= 0))
|
|
4447
|
+
return { pid, readyAt };
|
|
4448
|
+
}
|
|
4449
|
+
function parseQuestionRecord(raw) {
|
|
4450
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4451
|
+
return;
|
|
4452
|
+
let input = raw;
|
|
4453
|
+
if (input.type !== "subagent.supervisor.question" || typeof input.id != "string" || !input.id.trim() || typeof input.createdAt != "number" || !Number.isFinite(input.createdAt) || typeof input.expiresAt != "number" || !Number.isFinite(input.expiresAt) || typeof input.reason != "string" || !input.reason.trim() || typeof input.message != "string" || !input.message.trim() || typeof input.runId != "string" || !input.runId.trim())
|
|
4454
|
+
return;
|
|
4455
|
+
let childIndex = input.childIndex;
|
|
4456
|
+
if (!Number.isInteger(childIndex) || childIndex === void 0 || childIndex < 0 || typeof input.childId != "string" || !input.childId.trim() || typeof input.parentSessionId != "string" || !input.parentSessionId.trim())
|
|
4457
|
+
return;
|
|
4458
|
+
let parentGeneration = input.parentGeneration;
|
|
4459
|
+
if (!Number.isInteger(parentGeneration) || parentGeneration === void 0 || parentGeneration < 0)
|
|
4460
|
+
return;
|
|
4461
|
+
let incarnation = parseIncarnation(input.childIncarnation);
|
|
4462
|
+
return {
|
|
4463
|
+
type: "subagent.supervisor.question",
|
|
4464
|
+
id: input.id.trim(),
|
|
4465
|
+
createdAt: input.createdAt,
|
|
4466
|
+
expiresAt: input.expiresAt,
|
|
4467
|
+
reason: input.reason.trim(),
|
|
4468
|
+
message: input.message.trim(),
|
|
4469
|
+
runId: input.runId.trim(),
|
|
4470
|
+
childIndex,
|
|
4471
|
+
childId: input.childId.trim(),
|
|
4472
|
+
parentSessionId: input.parentSessionId.trim(),
|
|
4473
|
+
parentGeneration,
|
|
4474
|
+
...incarnation ? { childIncarnation: incarnation } : {}
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
function parseDeliveredRecord(raw) {
|
|
4478
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4479
|
+
return;
|
|
4480
|
+
let input = raw;
|
|
4481
|
+
if (input.type !== "subagent.supervisor.delivered" || typeof input.id != "string" || !input.id.trim() || typeof input.at != "number" || !Number.isFinite(input.at))
|
|
4482
|
+
return;
|
|
4483
|
+
let incarnation = parseIncarnation(input);
|
|
4484
|
+
if (incarnation)
|
|
4485
|
+
return { type: "subagent.supervisor.delivered", id: input.id.trim(), at: input.at, pid: incarnation.pid, readyAt: incarnation.readyAt };
|
|
4486
|
+
}
|
|
4487
|
+
function parseTerminalRecord(raw) {
|
|
4488
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4489
|
+
return;
|
|
4490
|
+
let input = raw;
|
|
4491
|
+
if (!(input.type !== "subagent.supervisor.terminal" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at))) {
|
|
4492
|
+
if (input.state === "answered")
|
|
4493
|
+
return typeof input.message != "string" || !input.message.trim() ? void 0 : { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "answered", at: input.at, message: input.message };
|
|
4494
|
+
if (input.state === "expired")
|
|
4495
|
+
return { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "expired", at: input.at };
|
|
4496
|
+
if (input.state === "cancelled")
|
|
4497
|
+
return typeof input.reason != "string" || !input.reason.trim() ? void 0 : { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "cancelled", at: input.at, reason: input.reason.trim() };
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
function parseNotifiedRecord(raw) {
|
|
4501
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4502
|
+
return;
|
|
4503
|
+
let input = raw;
|
|
4504
|
+
if (!(input.type !== "subagent.supervisor.notified" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at)))
|
|
4505
|
+
return { type: "subagent.supervisor.notified", id: input.id.trim(), at: input.at };
|
|
4506
|
+
}
|
|
4507
|
+
function parseLateRecord(raw) {
|
|
4508
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4509
|
+
return;
|
|
4510
|
+
let input = raw;
|
|
4511
|
+
if (!(input.type !== "subagent.supervisor.late-answer" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at) || typeof input.message != "string"))
|
|
4512
|
+
return { type: "subagent.supervisor.late-answer", id: input.id.trim(), at: input.at, message: input.message };
|
|
4513
|
+
}
|
|
4514
|
+
function composeQuestion(record, channelDir) {
|
|
4515
|
+
let delivered = parseDeliveredRecord(tryReadJson(deliveredFile(channelDir, record.id))), terminal = parseTerminalRecord(tryReadJson(terminalFile(channelDir, record.id))), notified = parseNotifiedRecord(tryReadJson(notifiedFile(channelDir, record.id))), late = parseLateRecord(tryReadJson(lateFile(channelDir, record.id)));
|
|
4516
|
+
return {
|
|
4517
|
+
...record,
|
|
4518
|
+
...delivered ? { deliveredAt: delivered.at, childIncarnation: record.childIncarnation ?? { pid: delivered.pid, readyAt: delivered.readyAt } } : {},
|
|
4519
|
+
...terminal?.state === "answered" ? { answeredAt: terminal.at, answer: terminal.message } : {},
|
|
4520
|
+
...terminal?.state === "expired" ? { expiredAt: terminal.at } : {},
|
|
4521
|
+
...terminal?.state === "cancelled" ? { cancelledAt: terminal.at, cancelReason: terminal.reason } : {},
|
|
4522
|
+
...notified ? { notifiedAt: notified.at } : {},
|
|
4523
|
+
...late ? { lateAnswerAt: late.at, ...terminal?.state === "answered" ? {} : { answer: late.message } } : {}
|
|
4524
|
+
};
|
|
4525
|
+
}
|
|
4526
|
+
function assertMessageSize(value, label) {
|
|
4527
|
+
if (Buffer.byteLength(value, "utf-8") > MAX_MESSAGE_BYTES)
|
|
4528
|
+
throw new Error(`${label} is too large.`);
|
|
4529
|
+
}
|
|
4530
|
+
function ensureQuestionsDir(channelDir) {
|
|
4531
|
+
fs6.mkdirSync(questionsDir(channelDir), { recursive: !0, mode: 448 });
|
|
4532
|
+
}
|
|
4533
|
+
function supervisorQuestionPublicState(question) {
|
|
4534
|
+
return question.answeredAt ? "answered" : question.expiredAt ? "expired" : question.cancelledAt ? "cancelled" : "pending";
|
|
4535
|
+
}
|
|
4536
|
+
function supervisorQuestionIsTerminal(question) {
|
|
4537
|
+
return supervisorQuestionPublicState(question) !== "pending";
|
|
4538
|
+
}
|
|
4539
|
+
function readSupervisorQuestion(channelDir, questionId) {
|
|
4540
|
+
let record = parseQuestionRecord(tryReadJson(questionFile(channelDir, questionId)));
|
|
4541
|
+
if (!(!record || record.id !== questionId))
|
|
4542
|
+
return composeQuestion(record, channelDir);
|
|
4543
|
+
}
|
|
4544
|
+
function listSupervisorQuestions(channelDir) {
|
|
4545
|
+
let dir = questionsDir(channelDir), entries;
|
|
4546
|
+
try {
|
|
4547
|
+
entries = fs6.readdirSync(dir).filter((name) => name.endsWith(".json") && !name.endsWith(".delivered.json") && !name.endsWith(".terminal.json") && !name.endsWith(".notified.json") && !name.endsWith(".late.json"));
|
|
4548
|
+
} catch (error) {
|
|
4549
|
+
if (error.code === "ENOENT")
|
|
4550
|
+
return [];
|
|
4551
|
+
throw error;
|
|
4552
|
+
}
|
|
4553
|
+
let questions = [];
|
|
4554
|
+
for (let entry of entries.sort()) {
|
|
4555
|
+
let record = parseQuestionRecord(tryReadJson(path7.join(dir, entry)));
|
|
4556
|
+
record && questions.push(composeQuestion(record, channelDir));
|
|
4557
|
+
}
|
|
4558
|
+
return questions;
|
|
4559
|
+
}
|
|
4560
|
+
function writeTerminal(channelDir, record) {
|
|
4561
|
+
return writeExclusiveJson(terminalFile(channelDir, record.id), record);
|
|
4562
|
+
}
|
|
4563
|
+
function createSupervisorQuestion(input) {
|
|
4564
|
+
let reason = input.reason.trim(), message = input.message.trim();
|
|
4565
|
+
if (!reason)
|
|
4566
|
+
throw new Error("reason is required and must name the concrete decision the child's answer will change.");
|
|
4567
|
+
if (!message)
|
|
4568
|
+
throw new Error("message is required for supervisor questions.");
|
|
4569
|
+
assertMessageSize(reason, "reason"), assertMessageSize(message, "message");
|
|
4570
|
+
let createdAt = input.now ?? Date.now(), timeoutMs = input.timeoutMs && input.timeoutMs > 0 ? input.timeoutMs : askTimeoutMs(), record = {
|
|
4571
|
+
type: "subagent.supervisor.question",
|
|
4572
|
+
id: input.id ?? randomUUID3(),
|
|
4573
|
+
createdAt,
|
|
4574
|
+
expiresAt: createdAt + timeoutMs,
|
|
4575
|
+
reason,
|
|
4576
|
+
message,
|
|
4577
|
+
runId: input.runId,
|
|
4578
|
+
childIndex: input.childIndex,
|
|
4579
|
+
childId: input.childId,
|
|
4580
|
+
parentSessionId: input.parentSessionId,
|
|
4581
|
+
parentGeneration: input.parentGeneration,
|
|
4582
|
+
...input.childIncarnation ? { childIncarnation: input.childIncarnation } : {}
|
|
4583
|
+
};
|
|
4584
|
+
if (assertMessageSize(JSON.stringify(record), "Supervisor question"), ensureQuestionsDir(input.channelDir), !writeExclusiveJson(questionFile(input.channelDir, record.id), record))
|
|
4585
|
+
throw new Error(`Supervisor question '${record.id}' already exists.`);
|
|
4586
|
+
return composeQuestion(record, input.channelDir);
|
|
4587
|
+
}
|
|
4588
|
+
function outstandingQuestionsForChild(channelDir, childIndex, childId) {
|
|
4589
|
+
return refreshSupervisorQuestionLifecycle(channelDir), listSupervisorQuestions(channelDir).filter((question) => !(supervisorQuestionIsTerminal(question) || question.childIndex !== childIndex || childId && question.childId !== childId));
|
|
4590
|
+
}
|
|
4591
|
+
function hasBlockingSupervisorRequest(channelDir, runId, childIndex) {
|
|
4592
|
+
let directory = path7.join(channelDir, "requests");
|
|
4593
|
+
if (!fs6.existsSync(directory))
|
|
4594
|
+
return !1;
|
|
4595
|
+
for (let name of fs6.readdirSync(directory)) {
|
|
4596
|
+
if (!name.endsWith(".json"))
|
|
4597
|
+
continue;
|
|
4598
|
+
let raw = tryReadJson(path7.join(directory, name));
|
|
4599
|
+
if (!raw || typeof raw != "object")
|
|
4600
|
+
continue;
|
|
4601
|
+
let request = raw;
|
|
4602
|
+
if (!(request.type !== "subagent.supervisor.request" || request.expectsReply !== !0) && !(request.runId !== runId || request.childIndex !== childIndex || typeof request.id != "string") && !(typeof request.expiresAt == "number" && request.expiresAt <= Date.now()) && !fs6.existsSync(path7.join(channelDir, "replies", `${safeSegment(request.id)}.json`)))
|
|
4603
|
+
return !0;
|
|
4604
|
+
}
|
|
4605
|
+
return !1;
|
|
4606
|
+
}
|
|
4607
|
+
function hasOutstandingQuestionForChild(channelDir, childIndex, childId) {
|
|
4608
|
+
return outstandingQuestionsForChild(channelDir, childIndex, childId).length > 0;
|
|
4609
|
+
}
|
|
4610
|
+
function incarnationMatches(expected, actual) {
|
|
4611
|
+
return expected ? expected.pid === actual.pid && expected.readyAt === actual.readyAt : !0;
|
|
4612
|
+
}
|
|
4613
|
+
function expireSupervisorQuestion(channelDir, questionId, now = Date.now()) {
|
|
4614
|
+
let current = readSupervisorQuestion(channelDir, questionId);
|
|
4615
|
+
return !current || supervisorQuestionIsTerminal(current) || now <= current.expiresAt ? current && supervisorQuestionIsTerminal(current) ? current : void 0 : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "expired", at: now }), readSupervisorQuestion(channelDir, questionId));
|
|
4616
|
+
}
|
|
4617
|
+
function cancelSupervisorQuestion(channelDir, questionId, reason, now = Date.now()) {
|
|
4618
|
+
let current = readSupervisorQuestion(channelDir, questionId);
|
|
4619
|
+
return !current || supervisorQuestionIsTerminal(current) ? current : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "cancelled", at: now, reason: reason.trim() || "cancelled" }), readSupervisorQuestion(channelDir, questionId));
|
|
4620
|
+
}
|
|
4621
|
+
function refreshSupervisorQuestionLifecycle(channelDir, now = Date.now()) {
|
|
4622
|
+
let changed = [];
|
|
4623
|
+
for (let question of listSupervisorQuestions(channelDir))
|
|
4624
|
+
if (!supervisorQuestionIsTerminal(question) && now > question.expiresAt) {
|
|
4625
|
+
let expired = expireSupervisorQuestion(channelDir, question.id, now);
|
|
4626
|
+
expired && expired.expiredAt && changed.push(expired);
|
|
4627
|
+
}
|
|
4628
|
+
return changed;
|
|
4629
|
+
}
|
|
4630
|
+
function formatParentQuestionForChild(question) {
|
|
4631
|
+
return [
|
|
4632
|
+
`Parent question (${question.id})`,
|
|
4633
|
+
`Decision needed: ${question.reason}`,
|
|
4634
|
+
"",
|
|
4635
|
+
question.message,
|
|
4636
|
+
"",
|
|
4637
|
+
`Reply with: contact_supervisor({ action: "reply", replyTo: "${question.id}", message: "..." })`,
|
|
4638
|
+
"Answer from current findings and uncertainty. Do not silently start extra investigation. Continue the assigned task after replying."
|
|
4639
|
+
].join(`
|
|
4640
|
+
`);
|
|
4641
|
+
}
|
|
4642
|
+
function formatSupervisorQuestionAnswer(question) {
|
|
4643
|
+
let state = supervisorQuestionPublicState(question), lines = [
|
|
4644
|
+
state === "answered" ? `Subagent answer (${question.id})` : `Subagent question ${state} (${question.id})`,
|
|
4645
|
+
`Run: ${question.runId}`,
|
|
4646
|
+
`Child: ${question.childId} [#${question.childIndex}]`,
|
|
4647
|
+
`Decision: ${question.reason}`
|
|
4648
|
+
];
|
|
4649
|
+
return state === "answered" && question.answer?.trim() ? lines.push("", question.answer.trim()) : question.cancelReason && lines.push("", question.cancelReason), lines.join(`
|
|
4650
|
+
`);
|
|
4651
|
+
}
|
|
4652
|
+
function registerQuestionWait(questionIds) {
|
|
4653
|
+
let ids = [...questionIds];
|
|
4654
|
+
for (let id of ids)
|
|
4655
|
+
activeQuestionWaits.add(id);
|
|
4656
|
+
return () => {
|
|
4657
|
+
for (let id of ids)
|
|
4658
|
+
activeQuestionWaits.delete(id);
|
|
4659
|
+
};
|
|
4660
|
+
}
|
|
4661
|
+
function isQuestionWaitActive(questionId) {
|
|
4662
|
+
return activeQuestionWaits.has(questionId);
|
|
4663
|
+
}
|
|
4664
|
+
function claimQuestionNotification(channelDir, questionId, now = Date.now()) {
|
|
4665
|
+
return writeExclusiveJson(notifiedFile(channelDir, questionId), {
|
|
4666
|
+
type: "subagent.supervisor.notified",
|
|
4667
|
+
id: questionId,
|
|
4668
|
+
at: now
|
|
4669
|
+
});
|
|
4670
|
+
}
|
|
4671
|
+
function listSupervisorChannelDirs() {
|
|
4672
|
+
let entries;
|
|
4673
|
+
try {
|
|
4674
|
+
entries = fs6.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4675
|
+
} catch (error) {
|
|
4676
|
+
if (error.code === "ENOENT")
|
|
4677
|
+
return [];
|
|
4678
|
+
throw error;
|
|
4679
|
+
}
|
|
4680
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => path7.join(SUPERVISOR_CHANNEL_ROOT, entry.name));
|
|
4681
|
+
}
|
|
4682
|
+
function findSupervisorQuestion(questionId) {
|
|
4683
|
+
let id = questionId.trim();
|
|
4684
|
+
if (id)
|
|
4685
|
+
for (let channelDir of listSupervisorChannelDirs()) {
|
|
4686
|
+
let question = readSupervisorQuestion(channelDir, id);
|
|
4687
|
+
if (question)
|
|
4688
|
+
return { channelDir, question };
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
function cancelPendingSupervisorQuestionsForOwner(input) {
|
|
4692
|
+
if (!input.parentSessionId?.trim())
|
|
4693
|
+
return [];
|
|
4694
|
+
let now = input.now ?? Date.now(), cancelled = [];
|
|
4695
|
+
for (let channelDir of listSupervisorChannelDirs())
|
|
4696
|
+
for (let question of listSupervisorQuestions(channelDir)) {
|
|
4697
|
+
if (supervisorQuestionIsTerminal(question) || question.parentSessionId !== input.parentSessionId || input.parentGeneration !== void 0 && question.parentGeneration !== input.parentGeneration || input.runId && question.runId !== input.runId)
|
|
4698
|
+
continue;
|
|
4699
|
+
let next = cancelSupervisorQuestion(channelDir, question.id, input.reason, now);
|
|
4700
|
+
next?.cancelledAt && cancelled.push(next);
|
|
4701
|
+
}
|
|
4702
|
+
return cancelled;
|
|
4703
|
+
}
|
|
4704
|
+
|
|
4705
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js
|
|
4706
|
+
function runningSteps(status) {
|
|
4707
|
+
return (status.steps ?? []).map((step, index) => ({
|
|
4708
|
+
index,
|
|
4709
|
+
childId: step.childId ?? step.agent,
|
|
4710
|
+
agent: step.agent,
|
|
4711
|
+
status: step.status
|
|
4712
|
+
})).filter((step) => step.status === "running").map((step) => ({ index: step.index, childId: step.childId, agent: step.agent }));
|
|
4713
|
+
}
|
|
4714
|
+
function resolveAskChild(status, params) {
|
|
4715
|
+
if (params.index !== void 0 && (!Number.isInteger(params.index) || params.index < 0))
|
|
4716
|
+
throw new Error("index must be a non-negative integer.");
|
|
4717
|
+
let running = runningSteps(status);
|
|
4718
|
+
if (params.childId?.trim()) {
|
|
4719
|
+
let childId = params.childId.trim(), matches = (status.steps ?? []).map((step, index) => ({ index, childId: step.childId ?? step.agent, agent: step.agent, status: step.status })).filter((step) => step.childId === childId);
|
|
4720
|
+
if (matches.length === 0)
|
|
4721
|
+
throw new Error(`Async run '${status.runId}' has no child '${childId}'.`);
|
|
4722
|
+
if (matches.length > 1)
|
|
4723
|
+
throw new Error(`Async run '${status.runId}' has multiple children named '${childId}'. Pass index.`);
|
|
4724
|
+
let match = matches[0];
|
|
4725
|
+
if (params.index !== void 0 && params.index !== match.index)
|
|
4726
|
+
throw new Error(`Child '${childId}' is index ${match.index}, not ${params.index}.`);
|
|
4727
|
+
if (match.status !== "running")
|
|
4728
|
+
throw new Error(`Async run '${status.runId}' child ${match.index} is ${match.status} and cannot be asked.`);
|
|
4729
|
+
return { index: match.index, childId: match.childId, agent: match.agent };
|
|
4730
|
+
}
|
|
4731
|
+
if (params.index !== void 0) {
|
|
4732
|
+
let steps = status.steps ?? [];
|
|
4733
|
+
if (params.index >= steps.length)
|
|
4734
|
+
throw new Error(`Async run '${status.runId}' has ${steps.length} children. Index ${params.index} is out of range.`);
|
|
4735
|
+
let step = steps[params.index];
|
|
4736
|
+
if (step.status !== "running")
|
|
4737
|
+
throw new Error(`Async run '${status.runId}' child ${params.index} is ${step.status} and cannot be asked.`);
|
|
4738
|
+
return { index: params.index, childId: step.childId ?? step.agent, agent: step.agent };
|
|
4739
|
+
}
|
|
4740
|
+
if (running.length === 1)
|
|
4741
|
+
return running[0];
|
|
4742
|
+
throw running.length === 0 ? new Error(`Async run '${status.runId}' has no running child to ask.`) : new Error(`Async run '${status.runId}' has ${running.length} running children. Pass index or childId; broadcast asks are not supported.`);
|
|
4743
|
+
}
|
|
4744
|
+
function requestMatchesChild(request, runId, child) {
|
|
4745
|
+
if (!request.expectsReply || typeof request.runId != "string" || request.runId !== runId)
|
|
4746
|
+
return !1;
|
|
4747
|
+
let indexMatches = Number.isInteger(request.childIndex) && request.childIndex === child.index, idMatches = request.agent === child.childId || request.agent === child.agent || request.childTarget === child.childId;
|
|
4748
|
+
return indexMatches && idMatches;
|
|
4749
|
+
}
|
|
4750
|
+
function reconcileAsyncQuestion(channelDir, question, state) {
|
|
4751
|
+
expireSupervisorQuestion(channelDir, question.id);
|
|
4752
|
+
let current = readSupervisorQuestion(channelDir, question.id) ?? question;
|
|
4753
|
+
if (supervisorQuestionIsTerminal(current))
|
|
4754
|
+
return current;
|
|
4755
|
+
let resolved = resolveSubagentRunId(current.runId, { state });
|
|
4756
|
+
if (resolved?.kind !== "async" || !resolved.location.asyncDir)
|
|
4757
|
+
return current;
|
|
4758
|
+
let status = readStatus(resolved.location.asyncDir);
|
|
4759
|
+
if (!status)
|
|
4760
|
+
return current;
|
|
4761
|
+
let step = status.steps?.[current.childIndex], capability = readSteerCapability(resolved.location.asyncDir, current.childIndex), reason;
|
|
4762
|
+
status.sessionId !== current.parentSessionId ? reason = "async run ownership changed" : status.state !== "running" || !step || step.status !== "running" ? reason = "target child is no longer running" : (step.childId ?? step.agent) !== current.childId ? reason = "target child identity changed" : capability && !incarnationMatches(current.childIncarnation, capability) ? reason = "target child process changed" : capability && !capability.supported && (reason = "target child no longer accepts questions");
|
|
4763
|
+
let delivery = status.steering?.recent.find((request) => request.id === current.id)?.targets.find((target) => target.index === current.childIndex);
|
|
4764
|
+
return (delivery?.state === "failed" || delivery?.state === "late") && (reason = delivery.reason ?? "question delivery failed"), reason ? cancelSupervisorQuestion(channelDir, current.id, reason) ?? current : current;
|
|
4765
|
+
}
|
|
4766
|
+
function askRunningAsyncChild(input) {
|
|
4767
|
+
let runId = input.params.id?.trim();
|
|
4768
|
+
if (!runId)
|
|
4769
|
+
throw new Error("action='ask' requires id for the async run.");
|
|
4770
|
+
let reason = input.params.reason?.trim(), message = input.params.message?.trim();
|
|
4771
|
+
if (!reason)
|
|
4772
|
+
throw new Error("action='ask' requires reason naming the concrete decision the child's answer will change.");
|
|
4773
|
+
if (!message)
|
|
4774
|
+
throw new Error("action='ask' requires message.");
|
|
4775
|
+
let sessionId = input.state.currentSessionId;
|
|
4776
|
+
if (!sessionId)
|
|
4777
|
+
throw new Error("action='ask' requires an active session identity.");
|
|
4778
|
+
let resolved = resolveSubagentRunId(runId, { state: input.state });
|
|
4779
|
+
if (!resolved)
|
|
4780
|
+
throw new Error(`No async run matched '${runId}'.`);
|
|
4781
|
+
if (resolved.kind !== "async")
|
|
4782
|
+
throw new Error("action='ask' targets a running async child only. It does not revive, reassign, or broadcast.");
|
|
4783
|
+
if (!resolved.location.asyncDir)
|
|
4784
|
+
throw new Error(`Async run '${resolved.id}' has no live run directory.`);
|
|
4785
|
+
let status = readStatus(resolved.location.asyncDir);
|
|
4786
|
+
if (!status)
|
|
4787
|
+
throw new Error(`Async run '${resolved.id}' has no status.`);
|
|
4788
|
+
if (!status.sessionId || status.sessionId !== sessionId)
|
|
4789
|
+
throw new Error(`Async run '${resolved.id}' was not found in the active session.`);
|
|
4790
|
+
if (status.state !== "running")
|
|
4791
|
+
throw new Error(`Async run '${resolved.id}' is ${status.state} and cannot be asked.`);
|
|
4792
|
+
let child = resolveAskChild(status, input.params), channelDir = resolveSupervisorChannelDir(status.runId, child.childId, child.index);
|
|
4793
|
+
if (hasOutstandingQuestionForChild(channelDir, child.index, child.childId))
|
|
4794
|
+
throw new Error(`A question is already outstanding for async run '${status.runId}' child ${child.index}. Wait for that answer or expiry before asking again.`);
|
|
4795
|
+
for (let request of input.childRequests ?? [])
|
|
4796
|
+
if (requestMatchesChild(request, status.runId, child))
|
|
4797
|
+
throw new Error(`Child ${child.index} is waiting for a supervisor reply. Asking now would deadlock. Reply to the child request first.`);
|
|
4798
|
+
if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
|
|
4799
|
+
throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
|
|
4800
|
+
let capability = readSteerCapability(resolved.location.asyncDir, child.index);
|
|
4801
|
+
if (!capability?.supported)
|
|
4802
|
+
throw new Error("This child is not ready to receive questions. Continue other work until it is ready; no question was queued.");
|
|
4803
|
+
let incarnation = { pid: capability.pid, readyAt: capability.readyAt }, question = createSupervisorQuestion({
|
|
4804
|
+
channelDir,
|
|
4805
|
+
reason,
|
|
4806
|
+
message,
|
|
4807
|
+
runId: status.runId,
|
|
4808
|
+
childIndex: child.index,
|
|
4809
|
+
childId: child.childId,
|
|
4810
|
+
parentSessionId: sessionId,
|
|
4811
|
+
parentGeneration: input.state.sessionGeneration ?? 0,
|
|
4812
|
+
childIncarnation: incarnation
|
|
4813
|
+
});
|
|
4814
|
+
try {
|
|
4815
|
+
if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
|
|
4816
|
+
throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
|
|
4817
|
+
requestAsyncSteer(resolved.location.asyncDir, {
|
|
4818
|
+
message: formatParentQuestionForChild(question),
|
|
4819
|
+
targetIndex: child.index,
|
|
4820
|
+
source: "supervisor-question",
|
|
4821
|
+
id: question.id
|
|
4822
|
+
});
|
|
4823
|
+
} catch (error) {
|
|
4824
|
+
throw cancelSupervisorQuestion(channelDir, question.id, "failed to queue delivery to child"), error instanceof Error ? error : new Error(String(error));
|
|
4825
|
+
}
|
|
4826
|
+
return {
|
|
4827
|
+
content: [{ type: "text", text: `Question ${question.id} queued for async run ${status.runId} child ${child.index}. The child's answer is delivered separately from the run result.` }],
|
|
4828
|
+
details: {
|
|
4829
|
+
questionId: question.id,
|
|
4830
|
+
state: "pending",
|
|
4831
|
+
runId: status.runId,
|
|
4832
|
+
childIndex: child.index,
|
|
4833
|
+
childId: child.childId,
|
|
4834
|
+
delivered: !1,
|
|
4835
|
+
answered: !1
|
|
4836
|
+
}
|
|
4837
|
+
};
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
|
|
4841
|
+
var REQUESTS_DIR = "requests", REPLIES_DIR = "replies", NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor";
|
|
4842
|
+
var MAX_MESSAGE_BYTES2 = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS2 = 600 * 1e3, CHANNEL_POLL_MS = Math.min(POLL_INTERVAL_MS, 500), STALE_EMPTY_CHANNEL_AGE_MS = 60 * 1e3, STALE_EMPTY_CHANNEL_CLEANUP_INTERVAL_MS = 60 * 1e3, PARENT_ASK_DESCRIPTION = [
|
|
4843
|
+
"Ask a specific running async child, or reply to a child supervisor request.",
|
|
4844
|
+
"Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks a block or rework.",
|
|
4845
|
+
"Use available results first, batch related questions, and keep working while awaiting the answer. Do not use ask for status checks, polling, duplicate queries, or step-by-step supervision. Follow up only when the answer leaves the original decision unresolved.",
|
|
4846
|
+
"Returns a question id immediately. The child's explicit answer is delivered separately from the final run result.",
|
|
4847
|
+
'Ask: { action: "ask", id: "<runId>", index?: 0, childId?: "...", reason: "<concrete decision>", message: "..." }',
|
|
4848
|
+
"Use pending or list to find child requests awaiting your reply; status reports channel availability. Free-form send is unsupported. Asking never revives a child or changes its assignment.",
|
|
4849
|
+
'Reply: { action: "reply", replyTo: "<requestId>", message: "..." }'
|
|
4850
|
+
].join(" "), CHILD_CONTACT_DESCRIPTION = [
|
|
4851
|
+
"Contact the parent/supervisor session for a blocking decision, structured interview, progress update, or reply to a parent question.",
|
|
4852
|
+
"For parent questions use action='reply' with replyTo set to the question id. Answer from current findings and uncertainty; do not start extra investigation just to reply. Continue the assigned task after replying.",
|
|
4853
|
+
"Do not open a blocking need_decision while a parent question is already outstanding for this child."
|
|
4854
|
+
].join(" "), ContactSupervisorParamsSchema = Type2.Object({
|
|
4855
|
+
reason: Type2.Optional(Type2.String({ enum: ["need_decision", "interview_request", "progress_update"] })),
|
|
4412
4856
|
message: Type2.Optional(Type2.String()),
|
|
4413
|
-
interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 }))
|
|
4857
|
+
interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 })),
|
|
4858
|
+
action: Type2.Optional(Type2.String({ enum: ["reply"] })),
|
|
4859
|
+
replyTo: Type2.Optional(Type2.String())
|
|
4414
4860
|
}, { additionalProperties: !1 }), IntercomParamsSchema = Type2.Object({
|
|
4415
4861
|
action: Type2.String({ enum: ["list", "send", "ask", "reply", "pending", "status"] }),
|
|
4416
4862
|
to: Type2.Optional(Type2.String()),
|
|
4417
4863
|
message: Type2.Optional(Type2.String()),
|
|
4418
|
-
replyTo: Type2.Optional(Type2.String())
|
|
4864
|
+
replyTo: Type2.Optional(Type2.String()),
|
|
4865
|
+
id: Type2.Optional(Type2.String({ description: "Async run id for action='ask'." })),
|
|
4866
|
+
index: Type2.Optional(Type2.Integer({ minimum: 0, description: "Zero-based child index for action='ask'. Select index or childId when more than one child is running." })),
|
|
4867
|
+
childId: Type2.Optional(Type2.String({ description: "Private child identity for action='ask'." })),
|
|
4868
|
+
reason: Type2.Optional(Type2.String({ description: "Concrete decision the child's answer will change. Required for action='ask'." }))
|
|
4869
|
+
}, { additionalProperties: !1 }), ParentSupervisorParamsSchema = Type2.Object({
|
|
4870
|
+
...IntercomParamsSchema.properties,
|
|
4871
|
+
action: Type2.String({ enum: ["list", "ask", "reply", "pending", "status"] })
|
|
4419
4872
|
}, { additionalProperties: !1 });
|
|
4420
|
-
function
|
|
4873
|
+
function safeSegment2(value) {
|
|
4421
4874
|
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
4422
4875
|
}
|
|
4423
4876
|
function replyPath(channelDir, requestId) {
|
|
4424
|
-
return
|
|
4877
|
+
return path8.join(channelDir, REPLIES_DIR, `${safeSegment2(requestId)}.json`);
|
|
4425
4878
|
}
|
|
4426
|
-
function
|
|
4879
|
+
function askTimeoutMs2() {
|
|
4427
4880
|
let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
4428
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed :
|
|
4881
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS2;
|
|
4429
4882
|
}
|
|
4430
4883
|
function hasTool(pi, name) {
|
|
4431
4884
|
try {
|
|
@@ -4436,16 +4889,26 @@ function hasTool(pi, name) {
|
|
|
4436
4889
|
}
|
|
4437
4890
|
function parseRequestFile(file, channelDir) {
|
|
4438
4891
|
try {
|
|
4439
|
-
let parsed = JSON.parse(
|
|
4440
|
-
return parsed.type !== "subagent.supervisor.request" || typeof parsed.id != "string" || !parsed.id || parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update" || typeof parsed.message != "string" || !parsed.message || typeof parsed.runId != "string" || typeof parsed.agent != "string" || typeof parsed.childIndex != "number" ? void 0 : { ...parsed, channelDir, requestFile: file };
|
|
4892
|
+
let parsed = JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
4893
|
+
return parsed.type !== "subagent.supervisor.request" || parsed.supervisorSessionId !== void 0 && (typeof parsed.supervisorSessionId != "string" || !parsed.supervisorSessionId.trim()) || typeof parsed.id != "string" || !parsed.id || parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update" || typeof parsed.message != "string" || !parsed.message || typeof parsed.runId != "string" || typeof parsed.agent != "string" || typeof parsed.childIndex != "number" ? void 0 : { ...parsed, channelDir, requestFile: file };
|
|
4441
4894
|
} catch {
|
|
4442
4895
|
return;
|
|
4443
4896
|
}
|
|
4444
4897
|
}
|
|
4898
|
+
function hasPendingBlockingSupervisorRequest(runId, sessionId, now = Date.now()) {
|
|
4899
|
+
if (!runId)
|
|
4900
|
+
return !1;
|
|
4901
|
+
for (let { channelDir, file } of listRequestFiles()) {
|
|
4902
|
+
let request = parseRequestFile(file, channelDir);
|
|
4903
|
+
if (request?.expectsReply && request.runId === runId && !(sessionId && (request.supervisorSessionId ?? request.orchestratorSessionId) !== sessionId) && !fs7.existsSync(replyPath(request.channelDir, request.id)) && !(now > requestExpiresAt(request, now)))
|
|
4904
|
+
return !0;
|
|
4905
|
+
}
|
|
4906
|
+
return !1;
|
|
4907
|
+
}
|
|
4445
4908
|
function listRequestFiles() {
|
|
4446
4909
|
let channelEntries;
|
|
4447
4910
|
try {
|
|
4448
|
-
channelEntries =
|
|
4911
|
+
channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4449
4912
|
} catch (error) {
|
|
4450
4913
|
if (error.code === "ENOENT")
|
|
4451
4914
|
return [];
|
|
@@ -4455,34 +4918,34 @@ function listRequestFiles() {
|
|
|
4455
4918
|
for (let entry of channelEntries) {
|
|
4456
4919
|
if (!entry.isDirectory())
|
|
4457
4920
|
continue;
|
|
4458
|
-
let channelDir =
|
|
4921
|
+
let channelDir = path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), requestsDir = path8.join(channelDir, REQUESTS_DIR), requestEntries;
|
|
4459
4922
|
try {
|
|
4460
|
-
requestEntries =
|
|
4923
|
+
requestEntries = fs7.readdirSync(requestsDir, { withFileTypes: !0 });
|
|
4461
4924
|
} catch {
|
|
4462
4925
|
continue;
|
|
4463
4926
|
}
|
|
4464
4927
|
for (let requestEntry of requestEntries)
|
|
4465
|
-
requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file:
|
|
4928
|
+
requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file: path8.join(requestsDir, requestEntry.name) });
|
|
4466
4929
|
}
|
|
4467
4930
|
return files;
|
|
4468
4931
|
}
|
|
4469
4932
|
function readDirectoryEntries(dir) {
|
|
4470
4933
|
try {
|
|
4471
|
-
return
|
|
4934
|
+
return fs7.readdirSync(dir, { withFileTypes: !0 });
|
|
4472
4935
|
} catch (error) {
|
|
4473
4936
|
return error.code === "ENOENT" ? [] : void 0;
|
|
4474
4937
|
}
|
|
4475
4938
|
}
|
|
4476
4939
|
function directoryMtimeMs(dir) {
|
|
4477
4940
|
try {
|
|
4478
|
-
return
|
|
4941
|
+
return fs7.statSync(dir).mtimeMs;
|
|
4479
4942
|
} catch {
|
|
4480
4943
|
return 0;
|
|
4481
4944
|
}
|
|
4482
4945
|
}
|
|
4483
4946
|
function removeEmptyDirectory(dir) {
|
|
4484
4947
|
try {
|
|
4485
|
-
return
|
|
4948
|
+
return fs7.rmdirSync(dir), !0;
|
|
4486
4949
|
} catch (error) {
|
|
4487
4950
|
let code = error.code;
|
|
4488
4951
|
if (code === "ENOENT")
|
|
@@ -4493,19 +4956,22 @@ function removeEmptyDirectory(dir) {
|
|
|
4493
4956
|
}
|
|
4494
4957
|
}
|
|
4495
4958
|
function removeStaleEmptySupervisorChannel(channelDir, nowMs) {
|
|
4496
|
-
let requestsDir =
|
|
4959
|
+
let requestsDir = path8.join(channelDir, REQUESTS_DIR), repliesDir = path8.join(channelDir, REPLIES_DIR), questionsPath = path8.join(channelDir, QUESTIONS_DIR), newestKnownMtimeMs = Math.max(directoryMtimeMs(channelDir), directoryMtimeMs(requestsDir), directoryMtimeMs(repliesDir), directoryMtimeMs(questionsPath));
|
|
4497
4960
|
if (nowMs - newestKnownMtimeMs < STALE_EMPTY_CHANNEL_AGE_MS)
|
|
4498
4961
|
return !1;
|
|
4499
4962
|
let requestEntries = readDirectoryEntries(requestsDir);
|
|
4500
4963
|
if (!requestEntries || requestEntries.length > 0)
|
|
4501
4964
|
return !1;
|
|
4502
4965
|
let replyEntries = readDirectoryEntries(repliesDir);
|
|
4503
|
-
|
|
4966
|
+
if (!replyEntries || replyEntries.length > 0)
|
|
4967
|
+
return !1;
|
|
4968
|
+
let questionEntries = readDirectoryEntries(questionsPath);
|
|
4969
|
+
return !(!questionEntries || questionEntries.length > 0 || !removeEmptyDirectory(requestsDir) || !removeEmptyDirectory(repliesDir) || !removeEmptyDirectory(questionsPath) || !removeEmptyDirectory(channelDir));
|
|
4504
4970
|
}
|
|
4505
4971
|
function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
|
|
4506
4972
|
let channelEntries;
|
|
4507
4973
|
try {
|
|
4508
|
-
channelEntries =
|
|
4974
|
+
channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4509
4975
|
} catch (error) {
|
|
4510
4976
|
if (error.code === "ENOENT")
|
|
4511
4977
|
return 0;
|
|
@@ -4515,7 +4981,7 @@ function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
|
|
|
4515
4981
|
for (let entry of channelEntries)
|
|
4516
4982
|
if (entry.isDirectory())
|
|
4517
4983
|
try {
|
|
4518
|
-
removeStaleEmptySupervisorChannel(
|
|
4984
|
+
removeStaleEmptySupervisorChannel(path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), nowMs) && removed++;
|
|
4519
4985
|
} catch {
|
|
4520
4986
|
}
|
|
4521
4987
|
return removed;
|
|
@@ -4529,19 +4995,26 @@ function currentContextSessionId(state, ctx) {
|
|
|
4529
4995
|
}
|
|
4530
4996
|
return state.currentSessionId ?? void 0;
|
|
4531
4997
|
}
|
|
4998
|
+
function currentSupervisorSessionId(state, ctx) {
|
|
4999
|
+
try {
|
|
5000
|
+
return ctx.sessionManager.getSessionFile?.() ?? currentContextSessionId(state, ctx);
|
|
5001
|
+
} catch {
|
|
5002
|
+
return state.currentSessionId ?? void 0;
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
4532
5005
|
function requestMatchesContext(request, state, ctx) {
|
|
4533
|
-
let currentSessionId = currentContextSessionId(state, ctx);
|
|
4534
|
-
return !!(currentSessionId && request.orchestratorSessionId === currentSessionId);
|
|
5006
|
+
let currentSessionId = request.supervisorSessionId !== void 0 ? currentSupervisorSessionId(state, ctx) : currentContextSessionId(state, ctx);
|
|
5007
|
+
return !!(currentSessionId && (request.supervisorSessionId ?? request.orchestratorSessionId) === currentSessionId);
|
|
4535
5008
|
}
|
|
4536
5009
|
function removeRequestFile(file) {
|
|
4537
5010
|
try {
|
|
4538
|
-
|
|
5011
|
+
fs7.rmSync(file, { force: !0 });
|
|
4539
5012
|
} catch {
|
|
4540
5013
|
}
|
|
4541
5014
|
}
|
|
4542
5015
|
function requestExpiresAt(request, now) {
|
|
4543
5016
|
let expiresAt = request.expiresAt;
|
|
4544
|
-
return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt +
|
|
5017
|
+
return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt + askTimeoutMs2() : now;
|
|
4545
5018
|
}
|
|
4546
5019
|
function requestRunInactive(request, state) {
|
|
4547
5020
|
if (state.foregroundControls.has(request.runId))
|
|
@@ -4558,7 +5031,7 @@ function requestRunInactive(request, state) {
|
|
|
4558
5031
|
return stepStatus === "complete" || stepStatus === "completed" || stepStatus === "failed" || stepStatus === "paused";
|
|
4559
5032
|
}
|
|
4560
5033
|
function requestLifecycle(request, state, ctx, now) {
|
|
4561
|
-
return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" :
|
|
5034
|
+
return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" : fs7.existsSync(request.requestFile) ? request.expectsReply && fs7.existsSync(replyPath(request.channelDir, request.id)) ? "resolved" : request.expectsReply && now > requestExpiresAt(request, now) ? "expired" : request.expectsReply && requestRunInactive(request, state) ? "inactive" : "pending" : "missing";
|
|
4562
5035
|
}
|
|
4563
5036
|
function cleanupRequestLifecycle(request, lifecycle) {
|
|
4564
5037
|
(lifecycle === "resolved" || lifecycle === "expired" || lifecycle === "inactive") && removeRequestFile(request.requestFile);
|
|
@@ -4623,8 +5096,8 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
|
|
|
4623
5096
|
return {
|
|
4624
5097
|
name,
|
|
4625
5098
|
label: name === "intercom" ? "Intercom" : "Subagent Supervisor",
|
|
4626
|
-
description:
|
|
4627
|
-
parameters:
|
|
5099
|
+
description: PARENT_ASK_DESCRIPTION,
|
|
5100
|
+
parameters: ParentSupervisorParamsSchema,
|
|
4628
5101
|
async execute(_id, params) {
|
|
4629
5102
|
refreshPendingRequests(pending, state, state.lastUiContext ?? void 0);
|
|
4630
5103
|
let input = params;
|
|
@@ -4639,10 +5112,59 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
|
|
|
4639
5112
|
let request = resolvePendingRequest(pending, input);
|
|
4640
5113
|
return writeReply(request, input.message ?? ""), pending.delete(request.id), { content: [{ type: "text", text: `Replied to supervisor request ${request.id}.` }], details: { replyTo: request.id, runId: request.runId, agent: request.agent } };
|
|
4641
5114
|
}
|
|
4642
|
-
|
|
5115
|
+
if (input.action === "ask")
|
|
5116
|
+
return askRunningAsyncChild({
|
|
5117
|
+
params: {
|
|
5118
|
+
id: input.id,
|
|
5119
|
+
index: input.index,
|
|
5120
|
+
childId: input.childId,
|
|
5121
|
+
reason: input.reason,
|
|
5122
|
+
message: input.message
|
|
5123
|
+
},
|
|
5124
|
+
state,
|
|
5125
|
+
childRequests: pending.values()
|
|
5126
|
+
});
|
|
5127
|
+
throw input.action === "send" ? new Error("Native pi-subagents intercom does not support free-form send. Use action='ask' for a running async child or action='reply' for a pending child request.") : new Error(`Unsupported intercom action: ${input.action}`);
|
|
4643
5128
|
}
|
|
4644
5129
|
};
|
|
4645
5130
|
}
|
|
5131
|
+
function questionMatchesParentContext(question, state, ctx) {
|
|
5132
|
+
let sessionId = currentSupervisorSessionId(state, ctx);
|
|
5133
|
+
return !(!sessionId || question.parentSessionId !== sessionId || (state.sessionGeneration ?? 0) !== question.parentGeneration);
|
|
5134
|
+
}
|
|
5135
|
+
function notifyQuestionTerminal(pi, question, channelDir) {
|
|
5136
|
+
if (!supervisorQuestionIsTerminal(question) || question.notifiedAt || (pi.events?.emit(SUBAGENT_QUESTION_EVENT, {
|
|
5137
|
+
questionId: question.id,
|
|
5138
|
+
state: supervisorQuestionPublicState(question),
|
|
5139
|
+
runId: question.runId
|
|
5140
|
+
}), isQuestionWaitActive(question.id)) || !claimQuestionNotification(channelDir, question.id))
|
|
5141
|
+
return;
|
|
5142
|
+
let state = supervisorQuestionPublicState(question);
|
|
5143
|
+
pi.sendMessage({
|
|
5144
|
+
customType: SUBAGENT_SUPERVISOR_ANSWER_TYPE,
|
|
5145
|
+
content: formatSupervisorQuestionAnswer(question),
|
|
5146
|
+
display: state === "answered",
|
|
5147
|
+
details: {
|
|
5148
|
+
questionId: question.id,
|
|
5149
|
+
state,
|
|
5150
|
+
runId: question.runId,
|
|
5151
|
+
childIndex: question.childIndex,
|
|
5152
|
+
childId: question.childId,
|
|
5153
|
+
reason: question.reason,
|
|
5154
|
+
answered: state === "answered",
|
|
5155
|
+
delivered: !!question.deliveredAt
|
|
5156
|
+
}
|
|
5157
|
+
}, { triggerTurn: state === "answered" });
|
|
5158
|
+
}
|
|
5159
|
+
function pollSupervisorQuestions(pi, state, ctx) {
|
|
5160
|
+
for (let channelDir of listSupervisorChannelDirs())
|
|
5161
|
+
for (let question of listSupervisorQuestions(channelDir)) {
|
|
5162
|
+
if (!questionMatchesParentContext(question, state, ctx))
|
|
5163
|
+
continue;
|
|
5164
|
+
let current = reconcileAsyncQuestion(channelDir, question, state);
|
|
5165
|
+
notifyQuestionTerminal(pi, current, channelDir);
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
4646
5168
|
function createNativeSupervisorChannel(pi, state) {
|
|
4647
5169
|
let pending = /* @__PURE__ */ new Map(), seenFiles = /* @__PURE__ */ new Set(), poller, lastStaleCleanupAt = 0, registerParentTools = () => {
|
|
4648
5170
|
hasTool(pi, NATIVE_SUPERVISOR_TOOL_NAME) || pi.registerTool(buildParentIntercomTool(pending, state, NATIVE_SUPERVISOR_TOOL_NAME)), hasTool(pi, "intercom") || pi.registerTool(buildParentIntercomTool(pending, state));
|
|
@@ -4655,12 +5177,24 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4655
5177
|
} catch {
|
|
4656
5178
|
}
|
|
4657
5179
|
}
|
|
5180
|
+
}, cancelOwnedQuestions = (reason) => {
|
|
5181
|
+
state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
|
|
5182
|
+
parentSessionId: state.currentSessionId,
|
|
5183
|
+
parentGeneration: state.sessionGeneration ?? 0,
|
|
5184
|
+
reason
|
|
5185
|
+
});
|
|
5186
|
+
}, settleRunQuestions = (runId, reason) => {
|
|
5187
|
+
state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
|
|
5188
|
+
parentSessionId: state.currentSessionId,
|
|
5189
|
+
runId,
|
|
5190
|
+
reason
|
|
5191
|
+
});
|
|
4658
5192
|
}, poll = () => {
|
|
4659
5193
|
cleanupStaleChannelsIfDue();
|
|
4660
5194
|
let ctx = state.lastUiContext;
|
|
4661
5195
|
if (!ctx)
|
|
4662
5196
|
return;
|
|
4663
|
-
refreshPendingRequests(pending, state, ctx);
|
|
5197
|
+
refreshPendingRequests(pending, state, ctx), pollSupervisorQuestions(pi, state, ctx);
|
|
4664
5198
|
let now = Date.now();
|
|
4665
5199
|
for (let { channelDir, file } of listRequestFiles()) {
|
|
4666
5200
|
if (seenFiles.has(file))
|
|
@@ -4673,7 +5207,7 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4673
5207
|
seenFiles.add(file), cleanupRequestLifecycle(request, lifecycle);
|
|
4674
5208
|
continue;
|
|
4675
5209
|
}
|
|
4676
|
-
seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
|
|
5210
|
+
if (seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
|
|
4677
5211
|
customType: "subagent_supervisor_request",
|
|
4678
5212
|
content: requestVisibleText(request),
|
|
4679
5213
|
display: !0,
|
|
@@ -4685,12 +5219,23 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4685
5219
|
agent: request.agent,
|
|
4686
5220
|
childIndex: request.childIndex
|
|
4687
5221
|
}
|
|
4688
|
-
}), request.expectsReply
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
5222
|
+
}), request.expectsReply) {
|
|
5223
|
+
let job = state.asyncJobs.get(request.runId);
|
|
5224
|
+
job && (job.activityState = "needs_attention");
|
|
5225
|
+
let events = pi.events;
|
|
5226
|
+
events?.emit(INTERCOM_DETACH_REQUEST_EVENT, {
|
|
5227
|
+
requestId: request.id,
|
|
5228
|
+
runId: request.runId,
|
|
5229
|
+
agent: request.agent,
|
|
5230
|
+
childIndex: request.childIndex
|
|
5231
|
+
}), events?.emit(SUBAGENT_CONTROL_EVENT, {
|
|
5232
|
+
type: "needs_attention",
|
|
5233
|
+
to: "needs_attention",
|
|
5234
|
+
runId: request.runId,
|
|
5235
|
+
agent: request.agent,
|
|
5236
|
+
index: request.childIndex
|
|
5237
|
+
});
|
|
5238
|
+
}
|
|
4694
5239
|
}
|
|
4695
5240
|
};
|
|
4696
5241
|
return {
|
|
@@ -4698,14 +5243,16 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4698
5243
|
poller || (registerParentTools(), poll(), poller = setInterval(poll, CHANNEL_POLL_MS), poller.unref?.());
|
|
4699
5244
|
},
|
|
4700
5245
|
dispose: () => {
|
|
4701
|
-
poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
|
|
5246
|
+
cancelOwnedQuestions("parent supervisor channel disposed"), poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
|
|
4702
5247
|
},
|
|
4703
|
-
pending
|
|
5248
|
+
pending,
|
|
5249
|
+
cancelOwnedQuestions,
|
|
5250
|
+
settleRunQuestions
|
|
4704
5251
|
};
|
|
4705
5252
|
}
|
|
4706
5253
|
|
|
4707
5254
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/rpc.js
|
|
4708
|
-
import * as
|
|
5255
|
+
import * as path9 from "node:path";
|
|
4709
5256
|
import { Compile } from "typebox/compile";
|
|
4710
5257
|
var SUBAGENT_RPC_PROTOCOL_VERSION = 1, SUBAGENT_RPC_REQUEST_EVENT = "subagents:rpc:v1:request", SUBAGENT_RPC_READY_EVENT = "subagents:rpc:v1:ready", SUBAGENT_RPC_REPLY_EVENT_PREFIX = "subagents:rpc:v1:reply:", SUBAGENT_RPC_METHODS = ["ping", "status", "spawn", "interrupt", "stop"], SubagentRpcError = class extends Error {
|
|
4711
5258
|
code;
|
|
@@ -4812,7 +5359,7 @@ function stopAsyncRun(params, options, ctx) {
|
|
|
4812
5359
|
}
|
|
4813
5360
|
if (!location.asyncDir)
|
|
4814
5361
|
throw new SubagentRpcError("not_found", "Async run not found or already completed; stop requires a live async run directory.");
|
|
4815
|
-
let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ??
|
|
5362
|
+
let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ?? path9.basename(location.asyncDir);
|
|
4816
5363
|
if (!initialStatus)
|
|
4817
5364
|
throw new SubagentRpcError("not_found", `Status file not found for async run '${initialRunId}'.`);
|
|
4818
5365
|
if (!currentSessionId || initialStatus.sessionId !== currentSessionId)
|
|
@@ -5044,7 +5591,9 @@ var ACTIVE_STATES = ["queued", "running"], DEFAULT_TIMEOUT_MS = 1800 * 1e3, MIN_
|
|
|
5044
5591
|
SUBAGENT_FOREGROUND_COMPLETE_EVENT,
|
|
5045
5592
|
SUBAGENT_CONTROL_EVENT,
|
|
5046
5593
|
SUBAGENT_CONTROL_INTERCOM_EVENT,
|
|
5047
|
-
SUBAGENT_RESULT_INTERCOM_EVENT
|
|
5594
|
+
SUBAGENT_RESULT_INTERCOM_EVENT,
|
|
5595
|
+
INTERCOM_DETACH_REQUEST_EVENT,
|
|
5596
|
+
SUBAGENT_QUESTION_EVENT
|
|
5048
5597
|
];
|
|
5049
5598
|
function defaultSleep(ms, signal) {
|
|
5050
5599
|
return new Promise((resolve3) => {
|
|
@@ -5113,8 +5662,8 @@ function summarizeForegroundChildren(run, indices) {
|
|
|
5113
5662
|
!indices.has(child.index) || child.status === "detached" || counts.set(child.status, (counts.get(child.status) ?? 0) + 1);
|
|
5114
5663
|
return [...counts.entries()].map(([status, count]) => `${count} ${status}`).join(", ");
|
|
5115
5664
|
}
|
|
5116
|
-
function needsAttention(run) {
|
|
5117
|
-
return run.activityState === "needs_attention";
|
|
5665
|
+
function needsAttention(run, sessionId) {
|
|
5666
|
+
return run.activityState === "needs_attention" || hasPendingBlockingSupervisorRequest(run.id, sessionId);
|
|
5118
5667
|
}
|
|
5119
5668
|
function backgroundWorkIdentity(item) {
|
|
5120
5669
|
return `${item.provider}\0${item.sessionId}\0${item.id}`;
|
|
@@ -5136,7 +5685,7 @@ function activeRunsForSession(params, deps) {
|
|
|
5136
5685
|
return params.id ? runs.filter((run) => matchesId(run, params.id)) : runs;
|
|
5137
5686
|
}
|
|
5138
5687
|
function attentionRunsForSession(params, deps, initialIds) {
|
|
5139
|
-
return activeRunsForSession(params, deps).filter((run) => needsAttention(run) && initialIds.has(run.id));
|
|
5688
|
+
return activeRunsForSession(params, deps).filter((run) => needsAttention(run, deps.state.currentSessionId) && initialIds.has(run.id));
|
|
5140
5689
|
}
|
|
5141
5690
|
function allRunsForSession(params, deps) {
|
|
5142
5691
|
let asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR, resultsDir = deps.resultsDir ?? RESULTS_DIR, runs = listAsyncRuns(asyncDirRoot, {
|
|
@@ -5182,12 +5731,57 @@ async function waitForDetachedForegroundRun(run, signal, deps, startedAt, now, p
|
|
|
5182
5731
|
await waitForWake(pollIntervalMs, signal, deps);
|
|
5183
5732
|
}
|
|
5184
5733
|
}
|
|
5734
|
+
async function waitForSupervisorQuestion(questionId, signal, deps, startedAt, now, pollIntervalMs, timeoutMs) {
|
|
5735
|
+
let sessionId = deps.state.currentSessionId;
|
|
5736
|
+
if (!sessionId)
|
|
5737
|
+
return result("subagent_wait requires an active session identity to scope background work safely.", !0);
|
|
5738
|
+
let releaseWait = registerQuestionWait([questionId]);
|
|
5739
|
+
try {
|
|
5740
|
+
for (; ; ) {
|
|
5741
|
+
let found = findSupervisorQuestion(questionId);
|
|
5742
|
+
if (!found)
|
|
5743
|
+
return result(`No supervisor question matched "${questionId}".`, !0);
|
|
5744
|
+
let { channelDir } = found, { question } = found;
|
|
5745
|
+
if (question.parentSessionId !== sessionId || deps.state.currentSessionId !== sessionId)
|
|
5746
|
+
return result(`Supervisor question "${questionId}" belongs to another session.`, !0);
|
|
5747
|
+
if ((deps.state.sessionGeneration ?? 0) !== question.parentGeneration)
|
|
5748
|
+
return result(`Supervisor question "${questionId}" was cancelled by a session replacement.`, !0);
|
|
5749
|
+
if (question = reconcileAsyncQuestion(channelDir, question, deps.state), supervisorQuestionIsTerminal(question)) {
|
|
5750
|
+
claimQuestionNotification(channelDir, question.id);
|
|
5751
|
+
let state = supervisorQuestionPublicState(question);
|
|
5752
|
+
return {
|
|
5753
|
+
content: [{ type: "text", text: formatSupervisorQuestionAnswer(question) }],
|
|
5754
|
+
...state === "answered" ? {} : { isError: !0 },
|
|
5755
|
+
details: {
|
|
5756
|
+
mode: "management",
|
|
5757
|
+
results: [],
|
|
5758
|
+
questionId: question.id,
|
|
5759
|
+
state,
|
|
5760
|
+
runId: question.runId,
|
|
5761
|
+
answered: state === "answered",
|
|
5762
|
+
delivered: !!question.deliveredAt
|
|
5763
|
+
}
|
|
5764
|
+
};
|
|
5765
|
+
}
|
|
5766
|
+
if (signal?.aborted)
|
|
5767
|
+
return result(`Wait aborted after ${formatDuration(now() - startedAt)} for supervisor question "${questionId}".`, !0);
|
|
5768
|
+
if (now() - startedAt >= timeoutMs)
|
|
5769
|
+
return result(`Wait timed out after ${formatDuration(timeoutMs)} for supervisor question "${questionId}". The question remains pending until answered, expired, or cancelled.`, !0);
|
|
5770
|
+
await waitForWake(pollIntervalMs, signal, deps);
|
|
5771
|
+
}
|
|
5772
|
+
} finally {
|
|
5773
|
+
releaseWait();
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5185
5776
|
async function waitForSubagents(params, signal, deps) {
|
|
5186
5777
|
if (deps.enabled === !1)
|
|
5187
5778
|
return result('subagent_wait is disabled by config.waitTool or PI_SUBAGENT_WAIT_TOOL_ENABLED; returning immediately without blocking background work. Active work keeps going, and you can inspect subagents with subagent({ action: "status" }) or rely on completion notifications.');
|
|
5188
5779
|
if (!deps.state.currentSessionId)
|
|
5189
5780
|
return result("subagent_wait requires an active session identity to scope background work safely.", !0);
|
|
5190
|
-
let now = deps.now ?? Date.now, pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS), timeoutMs = params.timeoutMs !== void 0 && params.timeoutMs > 0 ? params.timeoutMs : DEFAULT_TIMEOUT_MS, startedAt = now()
|
|
5781
|
+
let now = deps.now ?? Date.now, pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS), timeoutMs = params.timeoutMs !== void 0 && params.timeoutMs > 0 ? params.timeoutMs : DEFAULT_TIMEOUT_MS, startedAt = now();
|
|
5782
|
+
if (params.questionId?.trim())
|
|
5783
|
+
return waitForSupervisorQuestion(params.questionId.trim(), signal, deps, startedAt, now, pollIntervalMs, timeoutMs);
|
|
5784
|
+
let waitForAll = params.id ? !0 : params.all === !0, active, foreground, providerSnapshot;
|
|
5191
5785
|
try {
|
|
5192
5786
|
active = activeRunsForSession(params, deps), foreground = activeDetachedForegroundRuns(params, deps), providerSnapshot = params.id ? { providers: [], items: [] } : backgroundWorkForSession(deps, startedAt);
|
|
5193
5787
|
} catch (error) {
|
|
@@ -5208,7 +5802,7 @@ async function waitForSubagents(params, signal, deps) {
|
|
|
5208
5802
|
let providerActive = providerSnapshot.items;
|
|
5209
5803
|
if (active.length === 0 && providerActive.length === 0)
|
|
5210
5804
|
return result(params.id ? `No active run matched "${params.id}". Nothing to wait for.` : "No active async runs or registered provider work in this session. Nothing to wait for.");
|
|
5211
|
-
let waitParams = params.id ? { ...params, id: active[0].id } : params, initialAsyncIds = new Set(active.map((run) => run.id)), initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity)), initialProviderNames = new Set(providerActive.map((item) => item.provider)), initialCount = initialAsyncIds.size + initialProviderIds.size, stopOnAttention = deps.stopOnAttention !== !1, attention = active.filter((run) => needsAttention(run)), isDone = () => {
|
|
5805
|
+
let waitParams = params.id ? { ...params, id: active[0].id } : params, initialAsyncIds = new Set(active.map((run) => run.id)), initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity)), initialProviderNames = new Set(providerActive.map((item) => item.provider)), initialCount = initialAsyncIds.size + initialProviderIds.size, stopOnAttention = deps.stopOnAttention !== !1, attention = active.filter((run) => needsAttention(run, deps.state.currentSessionId)), isDone = () => {
|
|
5212
5806
|
if (stopOnAttention && attention.some((run) => initialAsyncIds.has(run.id)))
|
|
5213
5807
|
return !0;
|
|
5214
5808
|
let activeAsyncIds = new Set(active.map((run) => run.id)), activeProviderIds2 = new Set(providerActive.map(backgroundWorkIdentity));
|
|
@@ -5256,11 +5850,12 @@ function registerWaitTool(pi, state, enabled = resolveWaitToolConfig().enabled,
|
|
|
5256
5850
|
label: "Subagent Wait",
|
|
5257
5851
|
description: `Block until background work owned by this session changes, then return.
|
|
5258
5852
|
|
|
5259
|
-
|
|
5853
|
+
Call this when the current turn or skill must finish after its children. For normal interactive completion, yield and let lunR wake the session. Headless sessions auto-drain current-session work after the agent turn.
|
|
5260
5854
|
|
|
5261
5855
|
\u2022 { } \u2014 return when the first initially active async run or registered provider item finishes, or when a subagent needs attention.
|
|
5262
5856
|
\u2022 { all: true } \u2014 wait for every async run and provider item that was active when the call began.
|
|
5263
5857
|
\u2022 { id: "..." } \u2014 wait for one async or remembered detached foreground subagent run (id or prefix).
|
|
5858
|
+
\u2022 { questionId: "..." } \u2014 wait for one supervisor question from subagent_supervisor action='ask' (answered/expired/cancelled). Separate from final run results.
|
|
5264
5859
|
\u2022 { timeoutMs: 600000 } \u2014 stop waiting after N ms; active work keeps running.
|
|
5265
5860
|
|
|
5266
5861
|
Provider jobs are session-scoped and identified exactly, so replacing one job with another cannot hide a completion. Provider extensions must be explicitly loaded in this process. In a child agent, keep \`subagent_wait\` in the child tool allowlist and load each provider through the agent's extensions or subagentOnlyExtensions; this tool never loads providers or grants tools itself.${enabled ? "" : `
|
|
@@ -5395,7 +5990,7 @@ function formatSingleCompletion(details) {
|
|
|
5395
5990
|
}
|
|
5396
5991
|
function parseSubagentNotifyContent(content) {
|
|
5397
5992
|
let lines = content.split(`
|
|
5398
|
-
`), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
5993
|
+
`), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
5399
5994
|
if (!match)
|
|
5400
5995
|
return;
|
|
5401
5996
|
let body = lines.slice(2), sessionIndex = -1;
|
|
@@ -5439,7 +6034,7 @@ function sendCompletion(pi, details) {
|
|
|
5439
6034
|
customType: "subagent-notify",
|
|
5440
6035
|
content,
|
|
5441
6036
|
display: !0
|
|
5442
|
-
}, { triggerTurn:
|
|
6037
|
+
}, { triggerTurn: details.some((detail) => detail.status !== "stopped") });
|
|
5443
6038
|
}
|
|
5444
6039
|
function completionBatchKey(result2) {
|
|
5445
6040
|
let sessionId = typeof result2.sessionId == "string" ? result2.sessionId.trim() : "";
|
|
@@ -5449,13 +6044,38 @@ function completionBatchKey(result2) {
|
|
|
5449
6044
|
return cwd ? `cwd:${cwd}` : "unknown";
|
|
5450
6045
|
}
|
|
5451
6046
|
function buildCompletionDetails(result2) {
|
|
5452
|
-
let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "",
|
|
6047
|
+
let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "", paused = !result2.success && (result2.exitCode === 0 || result2.state === "paused" || summary.startsWith("Paused after interrupt.")), status = result2.state === "stopped" ? "stopped" : paused ? "paused" : result2.success ? "completed" : "failed", taskInfo = result2.taskIndex !== void 0 && result2.totalTasks !== void 0 ? ` (${result2.taskIndex + 1}/${result2.totalTasks})` : void 0, session = result2.shareUrl ? { label: "Session", value: result2.shareUrl } : result2.shareError ? { label: "Session share error", value: result2.shareError } : result2.sessionFile ? { label: "Session file", value: result2.sessionFile } : void 0;
|
|
5453
6048
|
return {
|
|
5454
6049
|
agent,
|
|
5455
6050
|
status,
|
|
5456
6051
|
...result2.source ? { source: result2.source } : {},
|
|
5457
6052
|
...taskInfo ? { taskInfo } : {},
|
|
5458
|
-
resultPreview:
|
|
6053
|
+
resultPreview: [
|
|
6054
|
+
result2.id ? `Run: ${result2.id}` : void 0,
|
|
6055
|
+
result2.asyncDir ? `Async dir: ${result2.asyncDir}` : void 0,
|
|
6056
|
+
result2.results?.length ? result2.results.map((child, index) => {
|
|
6057
|
+
let output = child.summary ?? (child.error ? `${child.error}
|
|
6058
|
+
|
|
6059
|
+
Output:
|
|
6060
|
+
${child.output}` : child.output), report = child.acceptance?.childReport, childIndex = child.index ?? index;
|
|
6061
|
+
return [
|
|
6062
|
+
`${childIndex + 1}. ${child.agent} [${child.status ?? resolveSubagentResultStatus({ success: child.success, state: result2.state })}]`,
|
|
6063
|
+
child.artifactPath ? `Output artifact: ${child.artifactPath}` : void 0,
|
|
6064
|
+
child.sessionPath ? `Session: ${child.sessionPath}` : void 0,
|
|
6065
|
+
child.sessionPath && result2.id && result2.source !== "foreground" ? `Revive: subagent({ action: "resume", id: ${JSON.stringify(result2.id)}, index: ${childIndex}, message: "..." })` : void 0,
|
|
6066
|
+
...formatNestedResultLines(child.children),
|
|
6067
|
+
output,
|
|
6068
|
+
report ? `\`\`\`acceptance-report
|
|
6069
|
+
${JSON.stringify(report, null, 2)}
|
|
6070
|
+
\`\`\`` : void 0
|
|
6071
|
+
].filter((line) => line !== void 0).join(`
|
|
6072
|
+
`);
|
|
6073
|
+
}).join(`
|
|
6074
|
+
|
|
6075
|
+
`) : summary
|
|
6076
|
+
].filter((line) => line !== void 0).join(`
|
|
6077
|
+
|
|
6078
|
+
`),
|
|
5459
6079
|
...typeof result2.durationMs == "number" ? { durationMs: result2.durationMs } : {},
|
|
5460
6080
|
...session ? { sessionLabel: session.label, sessionValue: session.value } : {}
|
|
5461
6081
|
};
|
|
@@ -5538,15 +6158,15 @@ function handleSubagentSteeringNotice(input) {
|
|
|
5538
6158
|
}
|
|
5539
6159
|
|
|
5540
6160
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/config.js
|
|
5541
|
-
import * as
|
|
5542
|
-
import * as
|
|
6161
|
+
import * as fs8 from "node:fs";
|
|
6162
|
+
import * as path10 from "node:path";
|
|
5543
6163
|
function getConfigPath() {
|
|
5544
|
-
return
|
|
6164
|
+
return path10.join(getAgentDir(), "extensions", "subagent", "config.json");
|
|
5545
6165
|
}
|
|
5546
6166
|
function readConfigForUpdate(configPath = getConfigPath()) {
|
|
5547
|
-
if (!
|
|
6167
|
+
if (!fs8.existsSync(configPath))
|
|
5548
6168
|
return {};
|
|
5549
|
-
let parsed = JSON.parse(
|
|
6169
|
+
let parsed = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
|
|
5550
6170
|
if (!parsed || typeof parsed != "object" || Array.isArray(parsed))
|
|
5551
6171
|
throw new Error(`Subagent config at '${configPath}' must be a JSON object`);
|
|
5552
6172
|
return parsed;
|
|
@@ -5562,25 +6182,31 @@ function loadConfig() {
|
|
|
5562
6182
|
}
|
|
5563
6183
|
|
|
5564
6184
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js
|
|
5565
|
-
import * as
|
|
5566
|
-
import * as
|
|
5567
|
-
var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024,
|
|
6185
|
+
import * as fs9 from "node:fs";
|
|
6186
|
+
import * as path11 from "node:path";
|
|
6187
|
+
var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024, SUBAGENT_TIER_GUIDANCE = `TIER SELECTION:
|
|
6188
|
+
\u2022 Choose the lowest tier that can reliably complete the task.
|
|
6189
|
+
\u2022 light: Use for simple, straightforward tasks that require little reasoning. Good for quick checks, finding information, collecting facts for research, inspecting a small amount of code, or verifying a specific fact.
|
|
6190
|
+
\u2022 standard: Use for moderately difficult work and everyday coding tasks. Good for writing simple code, fixing a clearly described bug, reviewing a focused change, or completing work with clear requirements. Use heavy when the task requires deep architectural understanding or complex reasoning.
|
|
6191
|
+
\u2022 heavy: Use for complex or ambiguous tasks that require deep reasoning and strong code understanding. Good for implementing complex plans, debugging difficult or poorly understood problems, reviewing large or high-risk changes, and making architectural decisions.`, SUBAGENT_SAFETY_GUIDANCE = `SAFETY-CRITICAL SUBAGENT GUIDANCE:
|
|
5568
6192
|
\u2022 Prompt children directly. There is no agent roster. Every execution task needs task (the full child prompt) and description (a concise UI label).
|
|
5569
|
-
\u2022 Every executable child
|
|
6193
|
+
\u2022 Every executable child chooses exactly one of tier: "light"|"standard"|"heavy" (default) or model: "provider/id" when the user names a model. Do not pass both, and do not use inherit. Missing, disabled, unconfigured, unavailable, or unauthenticated selections fail before launch.
|
|
6194
|
+
\u2022 Optional thinking (off/minimal/low/medium/high/xhigh/max) is only valid with an explicit model. Tier launches use the configured tier thinking level.
|
|
6195
|
+
${SUBAGENT_TIER_GUIDANCE}
|
|
5570
6196
|
\u2022 permissions is "full" or "read-only". Omitted permissions means full. Plan-mode parents must pass permissions: "read-only"; full or omitted launches are rejected.
|
|
5571
|
-
\u2022 Keep execution and control separate: omit action for SINGLE/PARALLEL/CHAIN execution; use action only for status/interrupt/stop/resume/steer/append-step/doctor/watchdog
|
|
5572
|
-
\u2022
|
|
6197
|
+
\u2022 Keep execution and control separate: omit action for SINGLE/PARALLEL/CHAIN execution; use action only for status/interrupt/stop/resume/steer/append-step/doctor/watchdog.status|check|recommend-model/schedule*.
|
|
6198
|
+
\u2022 ${SUBAGENT_ASYNC_GUIDANCE}
|
|
5573
6199
|
\u2022 Child-safety boundary: ordinary children are not orchestrators and must not run subagents. Only explicitly configured fanout children may use the child-safe subagent tool, still bounded by depth/session limits.
|
|
5574
6200
|
\u2022 Writing/review safety: keep one full-access writer for the same cwd/worktree. Use fresh-context permissions: "read-only" children for independent review, then have the parent synthesize and apply fixes as the sole writer unless an isolated worktree was intentionally requested.
|
|
5575
6201
|
\u2022 Artifacts/status essentials: chain outputs live under {chain_dir}; async runs expose asyncId/asyncDir with status.json, events.jsonl, output logs, and status via { action: "status", id }. Include output paths and residual risks when reporting results.`, FULL_SUBAGENT_TOOL_DESCRIPTION = `Delegate work to generic children by prompting them directly. There are no named agent types.
|
|
5576
6202
|
|
|
5577
6203
|
EXECUTION (use exactly ONE mode):
|
|
5578
|
-
\u2022 SINGLE: { task, description, tier, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
|
|
5579
|
-
\u2022 PARALLEL: { tasks: [{task, description, tier, permissions?, count?, output?, reads?, progress?}, ...], concurrency?: number, worktree?: true } \u2014 one-call concurrent execution (default: all tasks at once; worktree: isolate each task in a git worktree)
|
|
5580
|
-
\u2022 CHAIN: { chain: [{task, description, tier, permissions?}, {parallel:[{task, description, tier, permissions?, count:3}]}] } \u2014 sequential pipeline with optional parallel fan-out. Use chain when a later child needs an earlier result.
|
|
6204
|
+
\u2022 SINGLE: { task, description, tier|model, thinking?, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
|
|
6205
|
+
\u2022 PARALLEL: { tasks: [{task, description, tier|model, thinking?, permissions?, count?, output?, reads?, progress?}, ...], concurrency?: number, worktree?: true } \u2014 one-call concurrent execution (default: all tasks at once; worktree: isolate each task in a git worktree)
|
|
6206
|
+
\u2022 CHAIN: { chain: [{task, description, tier|model, thinking?, permissions?}, {parallel:[{task, description, tier|model, thinking?, permissions?, count:3}]}] } \u2014 sequential pipeline with optional parallel fan-out. Use chain when a later child needs an earlier result.
|
|
5581
6207
|
\u2022 description is required, single-line, max 80 characters. It is UI metadata only. task is the complete child prompt.
|
|
5582
6208
|
\u2022 permissions: "full" (default) or "read-only". Full includes coding tools (read, search, shell, edit, write, web, LSP, MCP) and excludes parent-owned tools (cron, memory, behavior, goals, nested subagents). Plan-mode parents may launch only permissions: "read-only".
|
|
5583
|
-
\u2022
|
|
6209
|
+
\u2022 Model selection: pass tier by default ("light"|"standard"|"heavy"). Pass model: "provider/id" only when the user specifies a model. Exactly one of tier or model. Optional thinking only with model; tier uses configured thinking. Direct model launches do not require tier mode to be enabled. No inherit and no silent fallback for an explicit model.
|
|
5584
6210
|
\u2022 Children always start with a fresh session (no inherited parent transcript).
|
|
5585
6211
|
\u2022 Optional timeout: { timeoutMs } or { maxRuntimeMs } sets a run-level max runtime for foreground and async/background runs
|
|
5586
6212
|
|
|
@@ -5595,8 +6221,7 @@ CHAIN EXAMPLES:
|
|
|
5595
6221
|
\u2022 Mixed: { chain: [{task:"Research {task}", description:"Research request", tier:"standard", permissions:"read-only"}, {parallel: [{task:"Review {previous}", description:"Review findings", tier:"light", permissions:"read-only", count: 2}]}, {task:"Summarize {previous}", description:"Summarize reviews", tier:"standard"}] }
|
|
5596
6222
|
|
|
5597
6223
|
CONTROL (use action field, omit task/chain/tasks):
|
|
5598
|
-
\u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation
|
|
5599
|
-
\u2022 { action: "watchdog.configure", model: "recommended" | "inherit" | "provider/model[:thinking]", scope?: "session" | "user" | "project", target?: "main" | "children", thinking?: "inherit" | "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" } - configure watchdog model selection; default scope is session, use persistent scopes only when the user asks
|
|
6224
|
+
\u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation (read-only; there is no configure action)
|
|
5600
6225
|
\u2022 { action: "status", id: "..." } - inspect an async/background run by id or prefix
|
|
5601
6226
|
\u2022 { action: "status", view: "fleet" } - read-only active foreground/async fleet view with transcript commands
|
|
5602
6227
|
\u2022 { action: "status", id: "...", view: "transcript", index?: 0, lines?: 80 } - tail a run or child output/session transcript
|
|
@@ -5604,10 +6229,12 @@ CONTROL (use action field, omit task/chain/tasks):
|
|
|
5604
6229
|
\u2022 { action: "stop", id: "..." } - stop a current-session top-level async run; stopped runs finish with state "stopped"
|
|
5605
6230
|
\u2022 { action: "resume", id: "...", message: "...", index?: 0 } - interrupt then follow up with a live async child, or revive a completed async/foreground child from its session
|
|
5606
6231
|
\u2022 { action: "steer", id: "...", message: "...", index?: 0 } - await correlated child input acceptance for up to 3 seconds; returns delivered, scheduled, pending, partial, recovered, or failed with a request id. Only top-level single runs may recover after a further 15-second pause/revival bound; chain, parallel, and nested runs never auto-interrupt.
|
|
6232
|
+
\u2022 subagent_supervisor({ action: "ask", id, reason, message, index?, childId? }) - ask one running async child; returns question id immediately. Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks block/rework. No broadcast, revive, task replacement, status polling, or micromanagement.
|
|
6233
|
+
\u2022 subagent_wait({ questionId }) - wait for that supervisor question answer/expiry/cancel. Answers stay separate from final run results.
|
|
5607
6234
|
\u2022 { action: "append-step", id: "...", chain: [{task:"Use {previous}", description:"Follow-up step", tier:"standard"}] } - append one step to the tail of a running async chain
|
|
5608
6235
|
|
|
5609
6236
|
SCHEDULE (opt-in; requires { "scheduledRuns": { "enabled": true } } in config.json):
|
|
5610
|
-
\u2022 { action: "schedule", task, description, tier, permissions?, schedule: "+10m" | "2030-01-01T09:00:00Z", scheduleName? } - defer a child launch until a future time. Also accepts tasks[] or chain[]. Scheduled runs always launch async with fresh context; they become normal tracked async runs once they fire. Only schedule explicit delayed runs the user asked for.
|
|
6237
|
+
\u2022 { action: "schedule", task, description, tier|model, thinking?, permissions?, schedule: "+10m" | "2030-01-01T09:00:00Z", scheduleName? } - defer a child launch until a future time. Also accepts tasks[] or chain[]. Scheduled runs always launch async with fresh context; they become normal tracked async runs once they fire. Only schedule explicit delayed runs the user asked for.
|
|
5611
6238
|
\u2022 { action: "schedule-list" } - list scheduled runs for this session
|
|
5612
6239
|
\u2022 { action: "schedule-status", id: "..." } - inspect one scheduled run
|
|
5613
6240
|
\u2022 { action: "schedule-cancel", id: "..." } - cancel a scheduled run before it fires
|
|
@@ -5618,19 +6245,20 @@ DIAGNOSTICS:
|
|
|
5618
6245
|
${SUBAGENT_SAFETY_GUIDANCE}`, COMPACT_SUBAGENT_TOOL_DESCRIPTION = `Delegate to generic children by prompting them. Use exactly one mode per call.
|
|
5619
6246
|
|
|
5620
6247
|
EXECUTE:
|
|
5621
|
-
\u2022 SINGLE {task, description, tier, permissions?} (same-turn singles overlap); PARALLEL {tasks:[{task,description,tier,permissions?,count?,output?,reads?,progress?}], concurrency?, worktree?}; CHAIN {chain:[{task,description,tier,permissions?},{parallel:[...]}]} for sequential work.
|
|
6248
|
+
\u2022 SINGLE {task, description, tier|model, thinking?, permissions?} (same-turn singles overlap); PARALLEL {tasks:[{task,description,tier|model,thinking?,permissions?,count?,output?,reads?,progress?}], concurrency?, worktree?}; CHAIN {chain:[{task,description,tier|model,thinking?,permissions?},{parallel:[...]}]} for sequential work.
|
|
5622
6249
|
\u2022 description is required (single-line, max 80 chars, UI only). task is the full child prompt. permissions omitted = full. Plan-mode parents must pass permissions:"read-only".
|
|
5623
|
-
\u2022
|
|
6250
|
+
\u2022 Exactly one of tier:"light"|"standard"|"heavy" (default) or model:"provider/id" when the user names a model. thinking only with model. No inherit; fail closed on unavailable selections. Children always start fresh. timeoutMs/maxRuntimeMs apply to foreground and async/background runs.
|
|
6251
|
+
${SUBAGENT_TIER_GUIDANCE}
|
|
5624
6252
|
\u2022 Chain templates may use {task}, {previous}, {chain_dir}, and named outputs. Parallel worktree isolation requires a clean git repo.
|
|
5625
6253
|
\u2022 Chain example: { chain: [{task:"Analyze {task}", description:"Analyze request", tier:"standard"}, {parallel: [{task:"Check {previous}", description:"Check prior result", tier:"light", permissions:"read-only", count: 3}]}] }
|
|
5626
6254
|
|
|
5627
6255
|
CONTROL:
|
|
5628
|
-
\u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model,
|
|
6256
|
+
\u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model, status, interrupt, stop, resume, steer, append-step.
|
|
5629
6257
|
\u2022 Async control actions: status, interrupt, stop, resume, steer, append-step. Use stop with an id for current-session top-level async runs. Use status view:"fleet" for active-run overview, view:"transcript" to tail child output, and steer for acknowledged live guidance. Steering delivery means lunR accepted the correlated user input, not model compliance; use index for a specific child.
|
|
5630
6258
|
\u2022 Opt-in schedule actions: schedule, schedule-list, schedule-status, schedule-cancel. Schedule only explicit delayed runs the user asked for.
|
|
5631
6259
|
|
|
5632
6260
|
ASYNC / WAIT:
|
|
5633
|
-
\u2022
|
|
6261
|
+
\u2022 ${SUBAGENT_ASYNC_GUIDANCE}
|
|
5634
6262
|
\u2022 Status and artifacts live under asyncId/asyncDir with status.json, events.jsonl, output logs, session files, and { action:"status", id:"..." }.
|
|
5635
6263
|
|
|
5636
6264
|
SAFETY:
|
|
@@ -5649,8 +6277,8 @@ function resolveToolDescriptionMode(config, options) {
|
|
|
5649
6277
|
function customDescriptionPaths(options) {
|
|
5650
6278
|
let cwd = options?.cwd ?? process.cwd(), agentDir = options?.agentDir ?? getAgentDir();
|
|
5651
6279
|
return [
|
|
5652
|
-
|
|
5653
|
-
|
|
6280
|
+
path11.join(getProjectConfigDir(cwd), CUSTOM_TOOL_DESCRIPTION_FILE),
|
|
6281
|
+
path11.join(agentDir, CUSTOM_TOOL_DESCRIPTION_FILE)
|
|
5654
6282
|
];
|
|
5655
6283
|
}
|
|
5656
6284
|
function renderCustomTemplate(template, options) {
|
|
@@ -5673,7 +6301,7 @@ function loadCustomToolDescription(options) {
|
|
|
5673
6301
|
for (let filePath of customDescriptionPaths(options)) {
|
|
5674
6302
|
let stat;
|
|
5675
6303
|
try {
|
|
5676
|
-
stat =
|
|
6304
|
+
stat = fs9.statSync(filePath);
|
|
5677
6305
|
} catch (error) {
|
|
5678
6306
|
if (typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT")
|
|
5679
6307
|
continue;
|
|
@@ -5689,7 +6317,7 @@ function loadCustomToolDescription(options) {
|
|
|
5689
6317
|
continue;
|
|
5690
6318
|
}
|
|
5691
6319
|
try {
|
|
5692
|
-
let template =
|
|
6320
|
+
let template = fs9.readFileSync(filePath, "utf-8").trim();
|
|
5693
6321
|
if (!template) {
|
|
5694
6322
|
warn(options, `Ignoring empty custom tool description '${filePath}'.`);
|
|
5695
6323
|
continue;
|
|
@@ -5728,24 +6356,24 @@ function buildSubagentToolDescription(config = {}, options) {
|
|
|
5728
6356
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
|
|
5729
6357
|
function getSubagentSessionRoot(parentSessionFile) {
|
|
5730
6358
|
if (parentSessionFile) {
|
|
5731
|
-
let baseName =
|
|
5732
|
-
return
|
|
6359
|
+
let baseName = path12.basename(parentSessionFile, ".jsonl"), sessionsDir = path12.dirname(parentSessionFile);
|
|
6360
|
+
return path12.join(sessionsDir, baseName);
|
|
5733
6361
|
}
|
|
5734
|
-
return
|
|
6362
|
+
return fs10.mkdtempSync(path12.join(os.tmpdir(), "pi-subagent-session-"));
|
|
5735
6363
|
}
|
|
5736
6364
|
function expandTilde(p) {
|
|
5737
|
-
return p.startsWith("~/") ?
|
|
6365
|
+
return p.startsWith("~/") ? path12.join(os.homedir(), p.slice(2)) : p;
|
|
5738
6366
|
}
|
|
5739
6367
|
function ensureAccessibleDir(dirPath) {
|
|
5740
|
-
|
|
6368
|
+
fs10.mkdirSync(dirPath, { recursive: !0 });
|
|
5741
6369
|
try {
|
|
5742
|
-
|
|
6370
|
+
fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
|
|
5743
6371
|
} catch {
|
|
5744
6372
|
try {
|
|
5745
|
-
|
|
6373
|
+
fs10.rmSync(dirPath, { recursive: !0, force: !0 });
|
|
5746
6374
|
} catch {
|
|
5747
6375
|
}
|
|
5748
|
-
|
|
6376
|
+
fs10.mkdirSync(dirPath, { recursive: !0 }), fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
|
|
5749
6377
|
}
|
|
5750
6378
|
}
|
|
5751
6379
|
function isSlashResultRunning(result2) {
|
|
@@ -5760,23 +6388,58 @@ function effectiveParallelTaskCount(tasks) {
|
|
|
5760
6388
|
return total + count;
|
|
5761
6389
|
}, 0);
|
|
5762
6390
|
}
|
|
5763
|
-
function
|
|
6391
|
+
function matchingActionJobs(state, requestedId) {
|
|
6392
|
+
let jobs = /* @__PURE__ */ new Map();
|
|
6393
|
+
for (let job of state.asyncJobs.values())
|
|
6394
|
+
jobs.set(job.asyncId, job);
|
|
6395
|
+
for (let job of state.fleetJobs?.values() ?? [])
|
|
6396
|
+
jobs.set(job.asyncId, job);
|
|
6397
|
+
let exact = jobs.get(requestedId);
|
|
6398
|
+
return exact ? [exact] : [...jobs.values()].filter((job) => job.asyncId.startsWith(requestedId));
|
|
6399
|
+
}
|
|
6400
|
+
function resolveSubagentActionDisplayTitle(args, state) {
|
|
6401
|
+
let requestedId = (args.id || args.runId || "").trim();
|
|
6402
|
+
if (!requestedId)
|
|
6403
|
+
return;
|
|
6404
|
+
let matches = matchingActionJobs(state, requestedId);
|
|
6405
|
+
if (matches.length !== 1)
|
|
6406
|
+
return;
|
|
6407
|
+
let job = matches[0], steps = job.steps ?? [];
|
|
6408
|
+
if (Number.isInteger(args.index)) {
|
|
6409
|
+
let step = steps.find((candidate) => candidate.index === args.index) ?? steps[args.index];
|
|
6410
|
+
return step ? childRowLabel(step) : void 0;
|
|
6411
|
+
}
|
|
6412
|
+
if (steps.length === 1)
|
|
6413
|
+
return childRowLabel(steps[0]);
|
|
6414
|
+
if (steps.length > 1)
|
|
6415
|
+
return `${job.mode ?? "subagent"} run`;
|
|
6416
|
+
if (job.agents?.length === 1)
|
|
6417
|
+
return job.agents[0];
|
|
6418
|
+
if (job.agents?.length)
|
|
6419
|
+
return `${job.mode ?? "subagent"} run`;
|
|
6420
|
+
}
|
|
6421
|
+
function renderSubagentCall(rawArgs, theme, context, options) {
|
|
6422
|
+
let args = resolveSubagentRequestParams(rawArgs);
|
|
5764
6423
|
if (args.action) {
|
|
5765
|
-
let target = args.id || args.runId || "";
|
|
6424
|
+
let target = context?.result?.details?.displayTitle || options?.resolveActionTitle?.(args) || args.id || args.runId || "";
|
|
5766
6425
|
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`, 0, 0);
|
|
5767
6426
|
}
|
|
5768
|
-
let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks),
|
|
6427
|
+
let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks), asyncMark = subagentLaunchRunsAsync(args) ? `${theme.fg("warning", "async")} ` : "";
|
|
5769
6428
|
if (args.chain?.length)
|
|
5770
|
-
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})
|
|
6429
|
+
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}chain (${args.chain.length})`, 0, 0);
|
|
5771
6430
|
if (isParallel)
|
|
5772
|
-
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})
|
|
5773
|
-
let description = !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "";
|
|
5774
|
-
return new Text2(
|
|
6431
|
+
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}parallel (${parallelCount})`, 0, 0);
|
|
6432
|
+
let description = !context?.result && !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "", title = theme.fg("toolTitle", theme.bold("subagent")), rest = [asyncMark.trim() ? theme.fg("warning", "async") : "", description].filter(Boolean).join(" ");
|
|
6433
|
+
return new Text2(rest ? `${title} ${rest}` : title, 0, 0);
|
|
6434
|
+
}
|
|
6435
|
+
function renderSubagentNotify(details, theme) {
|
|
6436
|
+
let status = typeof details?.status == "string" ? details.status : "", icon = status === "completed" ? theme.fg("success", "\u2713") : status === "failed" ? theme.fg("error", "\u2717") : theme.fg("warning", "\u25A0"), title = details?.agent || "child";
|
|
6437
|
+
return new Text2(`${icon} ${theme.bold(title)} ${theme.fg("dim", status)}`, 0, 0);
|
|
5775
6438
|
}
|
|
5776
6439
|
function ensureSubagentResultAnimation(context) {
|
|
5777
6440
|
let state = context.state;
|
|
5778
6441
|
state.subagentResultAnimationTimer || typeof context.invalidate == "function" && (state.frame === void 0 && (state.frame = 0), state.subagentResultAnimationTimer = setInterval(() => {
|
|
5779
|
-
state.frame = (
|
|
6442
|
+
state.frame = (state.frame ?? 0) + 1;
|
|
5780
6443
|
try {
|
|
5781
6444
|
let entries = state.animEntries;
|
|
5782
6445
|
if (entries?.length && typeof context.requestRender == "function") {
|
|
@@ -5788,7 +6451,7 @@ function ensureSubagentResultAnimation(context) {
|
|
|
5788
6451
|
context.invalidate();
|
|
5789
6452
|
} catch {
|
|
5790
6453
|
}
|
|
5791
|
-
},
|
|
6454
|
+
}, SUBAGENT_PAINT_INTERVAL_MS));
|
|
5792
6455
|
}
|
|
5793
6456
|
function isSlashResultError(result2) {
|
|
5794
6457
|
return result2.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || !1;
|
|
@@ -5818,11 +6481,12 @@ function registerSubagentExtension(pi) {
|
|
|
5818
6481
|
} catch {
|
|
5819
6482
|
}
|
|
5820
6483
|
ensureAccessibleDir(RESULTS_DIR), ensureAccessibleDir(ASYNC_DIR), cleanupOldChainDirs();
|
|
5821
|
-
let config = loadConfig(), waitToolConfig = resolveWaitToolConfig(config.waitTool),
|
|
6484
|
+
let config = normalizeAsyncLaunchConfig(loadConfig()), waitToolConfig = resolveWaitToolConfig(config.waitTool), tempArtifactsDir = getArtifactsDir(null);
|
|
5822
6485
|
cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
|
|
5823
6486
|
let state = {
|
|
5824
6487
|
baseCwd: "",
|
|
5825
6488
|
currentSessionId: null,
|
|
6489
|
+
sessionGeneration: 0,
|
|
5826
6490
|
foregroundSubagentInFlight: 0,
|
|
5827
6491
|
subagentSpawns: { sessionId: null, count: 0 },
|
|
5828
6492
|
asyncJobs: /* @__PURE__ */ new Map(),
|
|
@@ -5844,15 +6508,19 @@ function registerSubagentExtension(pi) {
|
|
|
5844
6508
|
}
|
|
5845
6509
|
}, supervisorChannel = createNativeSupervisorChannel(pi, state), mainWatchdog = registerMainWatchdog(pi), { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(pi, state, RESULTS_DIR, 600 * 1e3);
|
|
5846
6510
|
startResultWatcher(), primeExistingResults();
|
|
5847
|
-
let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), runtimeCleanup = () => {
|
|
5848
|
-
generation++, executorPromise = void 0, pendingLaunches.clear(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), state.poller && (clearInterval(state.poller), state.poller = null);
|
|
6511
|
+
let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), pendingAsyncLaunches = /* @__PURE__ */ new Set(), unregisterCancellation, runtimeCleanup = () => {
|
|
6512
|
+
generation++, supervisorChannel.cancelOwnedQuestions("parent runtime replaced"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), disposeSubagentWidget(), state.poller && (clearInterval(state.poller), state.poller = null);
|
|
5849
6513
|
};
|
|
5850
6514
|
globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
|
|
5851
|
-
let { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
|
|
6515
|
+
let { ensurePoller, refreshWidget, handleStarted, handleComplete: trackComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
|
|
5852
6516
|
widgetEnabled: config.asyncWidget !== !1
|
|
5853
|
-
}),
|
|
6517
|
+
}), handleComplete = (data) => {
|
|
6518
|
+
trackComplete(data);
|
|
6519
|
+
let result2 = data;
|
|
6520
|
+
typeof state.currentSessionId == "string" && result2.sessionId !== state.currentSessionId || typeof result2.id == "string" && result2.id.trim() && supervisorChannel.settleRunQuestions(result2.id, "async run settled");
|
|
6521
|
+
}, executorExecute, scheduledRunManager = createScheduledRunManager({
|
|
5854
6522
|
config,
|
|
5855
|
-
launch: (params, ctx, signal) => executorExecute ? executorExecute(
|
|
6523
|
+
launch: (params, ctx, signal) => executorExecute ? executorExecute(randomUUID4(), params, signal, void 0, ctx) : Promise.resolve({
|
|
5856
6524
|
content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
|
|
5857
6525
|
isError: !0,
|
|
5858
6526
|
details: { mode: "management", results: [] }
|
|
@@ -5862,20 +6530,19 @@ function registerSubagentExtension(pi) {
|
|
|
5862
6530
|
let resolveLaunchReady, launchReady = !1, launchReadyPromise = new Promise((resolve3) => {
|
|
5863
6531
|
resolveLaunchReady = resolve3;
|
|
5864
6532
|
}), markLaunchReady = () => {
|
|
5865
|
-
launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), resolveLaunchReady());
|
|
6533
|
+
launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), pendingAsyncLaunches.delete(launchReadyPromise), resolveLaunchReady());
|
|
5866
6534
|
};
|
|
5867
|
-
pendingLaunches.add(launchReadyPromise);
|
|
6535
|
+
pendingLaunches.add(launchReadyPromise), isAsyncSubagentExecution(resolveSubagentRequestParams(params)) && pendingAsyncLaunches.add(launchReadyPromise);
|
|
5868
6536
|
try {
|
|
5869
6537
|
let currentGeneration = generation;
|
|
5870
6538
|
if (signal?.throwIfAborted(), !executorPromise) {
|
|
5871
|
-
let pending = import("./subagent-executor-
|
|
6539
|
+
let pending = import("./subagent-executor-CXKXQDHH.js").then(({ createSubagentExecutor }) => {
|
|
5872
6540
|
if (generation !== currentGeneration)
|
|
5873
6541
|
throw new Error("Subagent session changed before initialization");
|
|
5874
6542
|
return createSubagentExecutor({
|
|
5875
6543
|
pi,
|
|
5876
6544
|
state,
|
|
5877
6545
|
config,
|
|
5878
|
-
asyncByDefault,
|
|
5879
6546
|
waitToolEnabled: waitToolConfig.enabled,
|
|
5880
6547
|
handleScheduledRunAction: (params2, ctx2) => scheduledRunManager.handleToolCall(params2, ctx2),
|
|
5881
6548
|
watchdog: mainWatchdog,
|
|
@@ -5906,35 +6573,20 @@ function registerSubagentExtension(pi) {
|
|
|
5906
6573
|
let content = typeof message.content == "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
|
|
5907
6574
|
`);
|
|
5908
6575
|
return new Text2(content, 0, 0);
|
|
5909
|
-
}), pi.registerMessageRenderer("subagent-notify", (message,
|
|
6576
|
+
}), pi.registerMessageRenderer("subagent-notify", (message, _options, theme) => {
|
|
5910
6577
|
let content = typeof message.content == "string" ? message.content : "", details = message.details ?? parseSubagentNotifyContent(content);
|
|
5911
|
-
|
|
5912
|
-
return new Text2(content, 0, 0);
|
|
5913
|
-
let icon = details.status === "completed" ? theme.fg("success", "\u2713") : details.status === "paused" ? theme.fg("warning", "\u25A0") : theme.fg("error", "\u2717"), parts = [];
|
|
5914
|
-
details.taskInfo && parts.push(details.taskInfo), details.durationMs !== void 0 && parts.push(formatDuration(details.durationMs));
|
|
5915
|
-
let notifyLabel = details.taskInfo || details.agent || "child", text = `${icon} ${theme.bold(notifyLabel)} ${theme.fg("dim", details.status)}`;
|
|
5916
|
-
parts.length > 0 && (text += ` ${theme.fg("dim", "\xB7")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "\xB7")} `)}`);
|
|
5917
|
-
let trimmedPreview = details.resultPreview.trim(), previewLines = options.expanded ? trimmedPreview.split(`
|
|
5918
|
-
`).filter((line) => line.trim()) : [trimmedPreview.split(`
|
|
5919
|
-
`, 1)[0] ?? ""].filter((line) => line.trim());
|
|
5920
|
-
for (let line of previewLines.length > 0 ? previewLines : ["(no output)"])
|
|
5921
|
-
text += `
|
|
5922
|
-
${theme.fg("dim", `\u23BF ${line}`)}`;
|
|
5923
|
-
if (!options.expanded && trimmedPreview.includes(`
|
|
5924
|
-
`)) {
|
|
5925
|
-
let expandKey = keyText("app.tools.expand");
|
|
5926
|
-
text += `
|
|
5927
|
-
${theme.fg("dim", `${expandKey} full notification`)}`;
|
|
5928
|
-
}
|
|
5929
|
-
return details.sessionLabel && details.sessionValue && (text += `
|
|
5930
|
-
${theme.fg("muted", `${details.sessionLabel}: ${shortenPath(details.sessionValue)}`)}`), new Text2(text, 0, 0);
|
|
6578
|
+
return details ? renderSubagentNotify(details, theme) : new Text2(content, 0, 0);
|
|
5931
6579
|
}), pi.registerMessageRenderer(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
|
|
5932
6580
|
let details = message.details;
|
|
5933
6581
|
if (details)
|
|
5934
6582
|
return new Text2(theme.fg(details.state === "recovered" ? "warning" : "error", formatSteeringNotice(details)), 0, 0);
|
|
5935
6583
|
}), pi.registerMessageRenderer(SUBAGENT_CONTROL_MESSAGE_TYPE, () => {
|
|
5936
6584
|
});
|
|
5937
|
-
let executeSubagentCollapsed = (id, params, signal, onUpdate, ctx) =>
|
|
6585
|
+
let executeSubagentCollapsed = async (id, params, signal, onUpdate, ctx) => {
|
|
6586
|
+
ctx.hasUI && ctx.ui.setToolsExpanded(!1);
|
|
6587
|
+
let displayTitle = params.action === "steer" ? resolveSubagentActionDisplayTitle(params, state) : void 0, result2 = await executor.execute(id, params, signal, onUpdate, ctx);
|
|
6588
|
+
return displayTitle && result2.details && (result2.details.displayTitle = displayTitle), result2;
|
|
6589
|
+
}, slashBridge = registerSlashSubagentBridge({
|
|
5938
6590
|
events: pi.events,
|
|
5939
6591
|
getContext: () => state.lastUiContext,
|
|
5940
6592
|
execute: (id, params, signal, onUpdate, ctx) => executeSubagentCollapsed(id, params, signal, onUpdate, ctx)
|
|
@@ -5959,7 +6611,9 @@ function registerSubagentExtension(pi) {
|
|
|
5959
6611
|
execute(id, params, signal, onUpdate, ctx) {
|
|
5960
6612
|
return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
|
|
5961
6613
|
},
|
|
5962
|
-
renderCall: renderSubagentCall,
|
|
6614
|
+
renderCall: (args, theme, context) => renderSubagentCall(args, theme, context, {
|
|
6615
|
+
resolveActionTitle: (params) => resolveSubagentActionDisplayTitle(params, state)
|
|
6616
|
+
}),
|
|
5963
6617
|
renderResult(result2, options, theme, context) {
|
|
5964
6618
|
subagentResultIsRunning(result2) ? ensureSubagentResultAnimation(context) : clearLegacyResultAnimationTimer(context);
|
|
5965
6619
|
let state2 = context.state, entries = [];
|
|
@@ -6018,16 +6672,25 @@ function registerSubagentExtension(pi) {
|
|
|
6018
6672
|
} catch {
|
|
6019
6673
|
}
|
|
6020
6674
|
}, resetSessionState = (ctx) => {
|
|
6021
|
-
if (generation++, executorPromise = void 0, pendingLaunches.clear(), state.baseCwd = ctx.cwd, state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager), state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 }, !process.env[SUBAGENT_CHILD_ENV]) {
|
|
6022
|
-
let
|
|
6023
|
-
|
|
6024
|
-
}
|
|
6025
|
-
state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx)
|
|
6675
|
+
if (generation++, supervisorChannel.cancelOwnedQuestions("parent session replaced"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), state.baseCwd = ctx.cwd, state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager), state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 }, !process.env[SUBAGENT_CHILD_ENV]) {
|
|
6676
|
+
let sessionId2 = ctx.sessionManager.getSessionId();
|
|
6677
|
+
sessionId2 && (process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId2);
|
|
6678
|
+
}
|
|
6679
|
+
state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx);
|
|
6680
|
+
let sessionId = state.currentSessionId, sessionGeneration = generation;
|
|
6681
|
+
sessionId && (unregisterCancellation = registerSubagentCancellation(sessionId, createSubagentCancellation({
|
|
6682
|
+
pendingLaunches: pendingAsyncLaunches,
|
|
6683
|
+
isCurrent: () => generation === sessionGeneration && state.currentSessionId === sessionId,
|
|
6684
|
+
getActiveRunIds: () => [...state.asyncJobs.values()].filter((job) => job.sessionId === sessionId && (job.status === "queued" || job.status === "running")).map((job) => job.asyncId),
|
|
6685
|
+
async stopRun(id) {
|
|
6686
|
+
return !(await executor.execute(randomUUID4(), { action: "stop", id }, new AbortController().signal, void 0, ctx)).isError;
|
|
6687
|
+
}
|
|
6688
|
+
}))), scheduledRunManager.bindSession(ctx), restoreSlashFinalSnapshots(ctx.sessionManager.getEntries()), primeExistingResults();
|
|
6026
6689
|
};
|
|
6027
6690
|
pi.on("session_start", (_event, ctx) => {
|
|
6028
6691
|
resetSessionState(ctx), rpcBridge.emitReady(ctx), supervisorChannel.start();
|
|
6029
6692
|
}), pi.on("session_shutdown", () => {
|
|
6030
|
-
generation++, executorPromise = void 0, pendingLaunches.clear(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
6693
|
+
generation++, supervisorChannel.cancelOwnedQuestions("parent session shutdown"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
6031
6694
|
for (let unsubscribe of eventUnsubscribes)
|
|
6032
6695
|
try {
|
|
6033
6696
|
unsubscribe();
|
|
@@ -6036,7 +6699,7 @@ function registerSubagentExtension(pi) {
|
|
|
6036
6699
|
globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes && delete globalStore[eventUnsubscribeStoreKey], stopResultWatcher(), scheduledRunManager.stop(), state.poller && clearInterval(state.poller), state.poller = null, clearPendingForegroundControlNotices(state);
|
|
6037
6700
|
for (let timer of state.cleanupTimers.values())
|
|
6038
6701
|
clearTimeout(timer);
|
|
6039
|
-
state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey];
|
|
6702
|
+
state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey], disposeSubagentWidget();
|
|
6040
6703
|
try {
|
|
6041
6704
|
state.lastUiContext?.hasUI && state.lastUiContext.ui.setWidget(WIDGET_KEY, void 0);
|
|
6042
6705
|
} catch (error) {
|