@ashx-j/lunr 0.2.19 → 0.2.21
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-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/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-35WNVGMR.js → agent-session-services-C743TYQE.js} +6 -5
- package/dist/node-runtime/{builtin-extensions-322XDDEM.js → builtin-extensions-JCOPAAB5.js} +3 -2
- package/dist/node-runtime/{chunk-XATUM2HK.js → chunk-25I2BHAA.js} +1 -1
- package/dist/node-runtime/{chunk-TXHKBRHZ.js → chunk-2BUAYZ7K.js} +136 -53
- package/dist/node-runtime/chunk-5ZH3ZFIZ.js +308 -0
- package/dist/node-runtime/{chunk-J26TSGDL.js → chunk-6N22HULO.js} +8 -5
- package/dist/node-runtime/chunk-A3KMVBZV.js +60 -0
- package/dist/node-runtime/{chunk-4FX25LWP.js → chunk-AWVD5IEY.js} +20 -421
- package/dist/node-runtime/{chunk-ZRCBN365.js → chunk-GB2MGFLI.js} +12 -12
- package/dist/node-runtime/chunk-LISUTZS3.js +19 -0
- package/dist/node-runtime/{chunk-PNJSG3IZ.js → chunk-LTYWTR3V.js} +1 -369
- package/dist/node-runtime/{chunk-TFBIJTM3.js → chunk-PDNXVXMD.js} +13 -13
- package/dist/node-runtime/{chunk-MCUNIRD5.js → chunk-SY2TFMLO.js} +532 -261
- package/dist/node-runtime/{chunk-HQCKFM2O.js → chunk-VGYZ3APQ.js} +3 -0
- package/dist/node-runtime/{chunk-4DJ6HEGH.js → chunk-VZR3NCM7.js} +3 -3
- package/dist/node-runtime/chunk-W3SILANP.js +24 -0
- package/dist/node-runtime/{chunk-CBGB57XT.js → chunk-WLLDXJRT.js} +10 -0
- package/dist/node-runtime/{chunk-ATGA2WLC.js → chunk-XI77KVKA.js} +399 -36
- package/dist/node-runtime/{chunk-4CSFRBCN.js → chunk-Y5KOEMBH.js} +1 -1
- package/dist/node-runtime/{chunk-AROOY4BB.js → chunk-YMEAYRBE.js} +1 -1
- package/dist/node-runtime/{command-DWUM5X3U.js → command-P4NYCJ2M.js} +1 -1
- package/dist/node-runtime/{customize-XEXL2DGQ.js → customize-LF5VST47.js} +1 -1
- package/dist/node-runtime/{gateway-UZXMFGWY.js → gateway-BM6NLOH4.js} +2 -2
- package/dist/node-runtime/index.js +16 -13
- package/dist/node-runtime/{interactive-mode-QT5IKGQ5.js → interactive-mode-EVVBA64D.js} +11 -8
- package/dist/node-runtime/{lunr-cron-QPBTKBGI.js → lunr-cron-4VHP4E4B.js} +2 -2
- package/dist/node-runtime/main.js +14 -12
- package/dist/node-runtime/{package-manager-cli-7RKSZGEN.js → package-manager-cli-N37546V5.js} +5 -4
- package/dist/node-runtime/{pi-intercom-EEMBGQID.js → pi-intercom-NV2LW6X7.js} +108 -87
- package/dist/node-runtime/{pi-subagents-7YJ3I4VE.js → pi-subagents-6MUIFNDM.js} +1262 -599
- package/dist/node-runtime/rpc-entry.js +14 -12
- package/dist/node-runtime/{runtime-bridges-3RKBDLNA.js → runtime-bridges-2VQTLPOZ.js} +3 -5
- package/dist/node-runtime/{settings-manager-GHPTUN7R.js → settings-manager-CUGAZWAB.js} +2 -1
- package/dist/node-runtime/{subagent-executor-5Q5A4HJ2.js → subagent-executor-MH4A7CCF.js} +181 -294
- package/dist/node-runtime/{update-cli-L3JIBH2U.js → update-cli-XTZMZGOL.js} +2 -1
- 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/package.json +4 -4
- 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-3NMWLTPV.js +0 -64
|
@@ -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,37 +60,51 @@ 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
|
|
92
|
+
} from "./chunk-SY2TFMLO.js";
|
|
93
|
+
import {
|
|
94
|
+
createSubagentCancellation,
|
|
95
|
+
getSubagentSpinnerDefinition,
|
|
96
|
+
registerSubagentCancellation
|
|
97
|
+
} from "./chunk-A3KMVBZV.js";
|
|
98
|
+
import "./chunk-5ZH3ZFIZ.js";
|
|
99
|
+
import "./chunk-25I2BHAA.js";
|
|
87
100
|
import "./chunk-WKDK4C5P.js";
|
|
88
101
|
import "./chunk-53YJI7JU.js";
|
|
89
102
|
import "./chunk-KBKMCJDU.js";
|
|
90
103
|
import "./chunk-CSFI6YXL.js";
|
|
104
|
+
import "./chunk-LISUTZS3.js";
|
|
91
105
|
import "./chunk-FVDOXRUD.js";
|
|
92
106
|
import "./chunk-AD6VXAO7.js";
|
|
93
|
-
import "./chunk-
|
|
107
|
+
import "./chunk-6N22HULO.js";
|
|
94
108
|
import "./chunk-M3LZLQW2.js";
|
|
95
109
|
import "./chunk-RQ2Q5IYR.js";
|
|
96
110
|
import {
|
|
@@ -104,10 +118,10 @@ import "./chunk-Q4Q6D4IY.js";
|
|
|
104
118
|
import "./chunk-BRJX2PRC.js";
|
|
105
119
|
|
|
106
120
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
|
|
107
|
-
import { randomUUID as
|
|
108
|
-
import * as
|
|
121
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
122
|
+
import * as fs10 from "node:fs";
|
|
109
123
|
import * as os from "node:os";
|
|
110
|
-
import * as
|
|
124
|
+
import * as path12 from "node:path";
|
|
111
125
|
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";
|
|
112
126
|
|
|
113
127
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/tui/render.js
|
|
@@ -170,7 +184,11 @@ function wrapPlainText(text, maxWidth) {
|
|
|
170
184
|
}
|
|
171
185
|
return lines;
|
|
172
186
|
}
|
|
173
|
-
var
|
|
187
|
+
var STATIC_RUNNING_GLYPH = "\u25CF", SUBAGENT_PAINT_INTERVAL_MS = 80;
|
|
188
|
+
function selectedSubagentSpinner() {
|
|
189
|
+
let bridge = globalThis[/* @__PURE__ */ Symbol.for("@lunr/customize")];
|
|
190
|
+
return getSubagentSpinnerDefinition(bridge?.getSubagentSpinner?.() ?? "braille");
|
|
191
|
+
}
|
|
174
192
|
function runningSeed(...values) {
|
|
175
193
|
let seed;
|
|
176
194
|
for (let value of values)
|
|
@@ -178,7 +196,14 @@ function runningSeed(...values) {
|
|
|
178
196
|
return seed;
|
|
179
197
|
}
|
|
180
198
|
function runningGlyph(seed) {
|
|
181
|
-
|
|
199
|
+
if (seed === void 0)
|
|
200
|
+
return STATIC_RUNNING_GLYPH;
|
|
201
|
+
let frames = selectedSubagentSpinner().frames;
|
|
202
|
+
return frames[Math.abs(seed) % frames.length];
|
|
203
|
+
}
|
|
204
|
+
function foregroundSpinnerFrame(timerFrame) {
|
|
205
|
+
let { interval } = selectedSubagentSpinner();
|
|
206
|
+
return Math.floor(Math.max(0, timerFrame) * SUBAGENT_PAINT_INTERVAL_MS / interval);
|
|
182
207
|
}
|
|
183
208
|
function progressRunningSeed(progress) {
|
|
184
209
|
if (progress)
|
|
@@ -238,6 +263,16 @@ function statJoin(theme, parts) {
|
|
|
238
263
|
function formatTokenStat(tokens) {
|
|
239
264
|
return `${formatTokens(tokens)} token`;
|
|
240
265
|
}
|
|
266
|
+
function usageTokenTotal(usage) {
|
|
267
|
+
return usage ? (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) : 0;
|
|
268
|
+
}
|
|
269
|
+
function compactResultTokens(result2) {
|
|
270
|
+
let progress = result2.progress || result2.progressSummary;
|
|
271
|
+
if (progress && progress.tokens > 0)
|
|
272
|
+
return progress.tokens;
|
|
273
|
+
let fromUsage = usageTokenTotal(result2.usage);
|
|
274
|
+
return fromUsage > 0 ? fromUsage : progress?.tokens ?? 0;
|
|
275
|
+
}
|
|
241
276
|
function formatToolUseStat(count) {
|
|
242
277
|
return `${count} tool use${count === 1 ? "" : "s"}`;
|
|
243
278
|
}
|
|
@@ -248,7 +283,10 @@ function formatTotalCostStat(totalCost) {
|
|
|
248
283
|
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(" ");
|
|
249
284
|
}
|
|
250
285
|
function liveDurationMs(progress, running, now) {
|
|
251
|
-
|
|
286
|
+
if (!progress)
|
|
287
|
+
return 0;
|
|
288
|
+
let durationMs = progress.durationMs ?? 0;
|
|
289
|
+
return !running || progress.lastActivityAt === void 0 ? durationMs : durationMs + Math.max(0, now - progress.lastActivityAt);
|
|
252
290
|
}
|
|
253
291
|
function formatProgressStats(theme, progress, includeDuration = !0, options = {}) {
|
|
254
292
|
if (!progress)
|
|
@@ -288,13 +326,17 @@ function resultStatusLine(result2, output) {
|
|
|
288
326
|
function resultGlyph(result2, output, theme, running = result2.progress?.status === "running", seed = progressRunningSeed(result2.progress ?? result2.progressSummary), frame) {
|
|
289
327
|
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");
|
|
290
328
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
329
|
+
function formatCompactRowStats(tokens, durationMs) {
|
|
330
|
+
return `${formatTokenStat(tokens)} \xB7 ${durationMs > 0 ? formatDuration(durationMs) : "0ms"}`;
|
|
331
|
+
}
|
|
332
|
+
function formatCompactSubagentRow(theme, width, parts) {
|
|
333
|
+
let stats = formatCompactRowStats(parts.tokens, parts.durationMs), body = [
|
|
334
|
+
`${parts.prefix ?? ""}${parts.glyph} ${themeBold(theme, parts.description)}`,
|
|
335
|
+
parts.modelBadge ? theme.fg("dim", parts.modelBadge) : "",
|
|
336
|
+
theme.fg("dim", stats),
|
|
337
|
+
parts.terminalStatus ? theme.fg("dim", parts.terminalStatus) : ""
|
|
338
|
+
].filter(Boolean).join(` ${theme.fg("dim", "\xB7")} `);
|
|
339
|
+
return truncLine(body, width);
|
|
298
340
|
}
|
|
299
341
|
function widgetRenderKey(job) {
|
|
300
342
|
return JSON.stringify({
|
|
@@ -348,14 +390,9 @@ function widgetStepsRunningSeed(steps) {
|
|
|
348
390
|
function widgetJobRunningSeed(job) {
|
|
349
391
|
return runningSeed(job.currentStep, job.runningSteps, widgetStepsRunningSeed(job.steps));
|
|
350
392
|
}
|
|
351
|
-
function widgetJobsRunningSeed(jobs) {
|
|
352
|
-
let seed;
|
|
353
|
-
for (let job of jobs)
|
|
354
|
-
seed = runningSeed(seed, widgetJobRunningSeed(job));
|
|
355
|
-
return seed;
|
|
356
|
-
}
|
|
357
393
|
function widgetAnimFrame(now = Date.now()) {
|
|
358
|
-
|
|
394
|
+
let definition = selectedSubagentSpinner();
|
|
395
|
+
return Math.floor(now / definition.interval) % definition.frames.length;
|
|
359
396
|
}
|
|
360
397
|
function widgetStatusGlyph(job, theme, now = Date.now()) {
|
|
361
398
|
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");
|
|
@@ -366,48 +403,6 @@ function widgetStepGlyph(status, theme, seed) {
|
|
|
366
403
|
function widgetStepStatus(status, theme) {
|
|
367
404
|
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);
|
|
368
405
|
}
|
|
369
|
-
function widgetStepActivity(step, snapshotNow) {
|
|
370
|
-
let facts = [];
|
|
371
|
-
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));
|
|
372
|
-
let activity = buildLiveStatusLine(step, snapshotNow);
|
|
373
|
-
return activity && facts.length ? `${activity} \xB7 ${facts.join(" \xB7 ")}` : activity || facts.join(" \xB7 ");
|
|
374
|
-
}
|
|
375
|
-
function widgetChainDetails(job, theme, expanded = !1, width = getTermWidth()) {
|
|
376
|
-
if (!job.steps?.length)
|
|
377
|
-
return [];
|
|
378
|
-
let total = job.chainStepCount ?? job.steps.length, lines = [];
|
|
379
|
-
for (let span of buildAsyncChainStepSpans(total, job.steps.length, job.parallelGroups)) {
|
|
380
|
-
let steps = job.steps.slice(span.start, span.start + span.count);
|
|
381
|
-
if (span.isParallel) {
|
|
382
|
-
let status = aggregateStepStatus(steps);
|
|
383
|
-
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))}`);
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
let step = steps[0];
|
|
387
|
-
if (!step) {
|
|
388
|
-
lines.push(` ${theme.fg("dim", `\u25E6 Step ${span.stepIndex + 1}/${total}: pending`)}`);
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, "Step", span.stepIndex + 1, total, expanded, width));
|
|
392
|
-
}
|
|
393
|
-
return lines;
|
|
394
|
-
}
|
|
395
|
-
function widgetParallelAgentDetails(job, theme, expanded = !1, width = getTermWidth()) {
|
|
396
|
-
if (!job.steps?.length)
|
|
397
|
-
return [];
|
|
398
|
-
if (job.mode !== "parallel" && job.mode !== "chain")
|
|
399
|
-
return [];
|
|
400
|
-
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length)
|
|
401
|
-
return widgetChainDetails(job, theme, expanded, width);
|
|
402
|
-
let total = job.stepsTotal ?? job.steps.length, lines = [];
|
|
403
|
-
for (let [index, step] of job.steps.entries()) {
|
|
404
|
-
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);
|
|
405
|
-
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}` : ""}`)}`);
|
|
406
|
-
for (let nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 1))
|
|
407
|
-
lines.push(` ${nestedLine}`);
|
|
408
|
-
}
|
|
409
|
-
return lines;
|
|
410
|
-
}
|
|
411
406
|
function parseParallelGroupAgentCount(label) {
|
|
412
407
|
if (!label || !label.startsWith("[") || !label.endsWith("]"))
|
|
413
408
|
return;
|
|
@@ -443,24 +438,16 @@ function buildChainStepSpans(details) {
|
|
|
443
438
|
function isChainParallelGroupActive(details) {
|
|
444
439
|
return details.mode !== "chain" || details.currentStepIndex === void 0 ? !1 : buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
445
440
|
}
|
|
446
|
-
function buildAsyncChainStepSpans(total, stepCount, parallelGroups = []) {
|
|
447
|
-
let spans = [], flatIndex = 0;
|
|
448
|
-
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
449
|
-
let group = parallelGroups.find((candidate) => candidate.stepIndex === stepIndex);
|
|
450
|
-
if (group) {
|
|
451
|
-
spans.push({ stepIndex, start: group.start, count: group.count, isParallel: !0 }), flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
452
|
-
continue;
|
|
453
|
-
}
|
|
454
|
-
spans.push({ stepIndex, start: flatIndex, count: flatIndex < stepCount ? 1 : 0, isParallel: !1 }), flatIndex++;
|
|
455
|
-
}
|
|
456
|
-
return spans;
|
|
457
|
-
}
|
|
458
441
|
function isDoneResult(result2) {
|
|
459
442
|
let status = result2.progress?.status;
|
|
460
443
|
return status === "completed" ? !0 : status === "running" || status === "pending" || result2.interrupted || result2.detached ? !1 : result2.exitCode === 0;
|
|
461
444
|
}
|
|
462
445
|
function workflowGraphHasStatus(details, statuses) {
|
|
463
|
-
|
|
446
|
+
let matches = (nodes) => nodes.some((node) => statuses.includes(node.status) || matches(node.children ?? []));
|
|
447
|
+
return matches(details.workflowGraph?.nodes ?? []);
|
|
448
|
+
}
|
|
449
|
+
function resultAtFlatIndex(details, flatIndex) {
|
|
450
|
+
return details.results.find((result2, arrayIndex) => (result2.progress?.index ?? result2.progressSummary?.index ?? arrayIndex) === flatIndex);
|
|
464
451
|
}
|
|
465
452
|
function buildChainRenderEntries(details, label) {
|
|
466
453
|
if (details.mode !== "chain" || !label.hasParallelInChain || label.showActiveGroupOnly)
|
|
@@ -478,13 +465,17 @@ function buildChainRenderEntries(details, label) {
|
|
|
478
465
|
});
|
|
479
466
|
continue;
|
|
480
467
|
}
|
|
481
|
-
for (let index = span.start; index < span.start + span.count; index++)
|
|
468
|
+
for (let index = span.start; index < span.start + span.count; index++) {
|
|
469
|
+
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);
|
|
482
470
|
entries.push({
|
|
483
471
|
kind: "result",
|
|
484
472
|
resultIndex: index,
|
|
485
473
|
rowNumber: index + 1,
|
|
486
|
-
agentName:
|
|
474
|
+
agentName: result2?.agent ?? workflowNode?.label ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
|
|
475
|
+
status: workflowNode?.status ?? span.status,
|
|
476
|
+
error: workflowNode?.error ?? span.error
|
|
487
477
|
});
|
|
478
|
+
}
|
|
488
479
|
}
|
|
489
480
|
return entries;
|
|
490
481
|
}
|
|
@@ -549,38 +540,6 @@ function resultRowLabel(details, label, resultIndex, stepNumber) {
|
|
|
549
540
|
}
|
|
550
541
|
return label.itemTitle === "Agent" ? `Agent ${label.activeParallelGroup ? Math.max(1, stepNumber - label.groupStartIndex) : stepNumber}/${label.totalCount}` : `Step ${stepNumber}`;
|
|
551
542
|
}
|
|
552
|
-
function widgetStats(job, theme, now = Date.now()) {
|
|
553
|
-
let parts = [], stepsTotal = job.stepsTotal ?? job.agents?.length ?? 1;
|
|
554
|
-
if (job.activeParallelGroup) {
|
|
555
|
-
let running = job.runningSteps ?? (job.status === "running" ? 1 : 0), done = job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
|
|
556
|
-
if (job.mode === "parallel")
|
|
557
|
-
job.status === "running" && running > 0 && parts.push(formatAgentRunningLabel(running)), stepsTotal > 0 && parts.push(`${done}/${stepsTotal} done`);
|
|
558
|
-
else {
|
|
559
|
-
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`];
|
|
560
|
-
job.status === "running" && running > 0 && groupParts.unshift(formatAgentRunningLabel(running)), parts.push(`step ${logicalStep + 1}/${total} \xB7 parallel group: ${groupParts.join(" \xB7 ")}`);
|
|
561
|
-
}
|
|
562
|
-
} else if (job.currentStep !== void 0)
|
|
563
|
-
if (job.mode === "chain" && job.parallelGroups?.length) {
|
|
564
|
-
let total = job.chainStepCount ?? stepsTotal;
|
|
565
|
-
parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`);
|
|
566
|
-
} else
|
|
567
|
-
parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
|
|
568
|
-
else stepsTotal > 1 && parts.push(`steps ${stepsTotal}`);
|
|
569
|
-
if (job.toolCount !== void 0 && parts.push(formatToolUseStat(job.toolCount)), job.totalTokens?.total && parts.push(formatTokenStat(job.totalTokens.total)), job.startedAt !== void 0) {
|
|
570
|
-
let end = job.status === "running" ? now : job.updatedAt;
|
|
571
|
-
end !== void 0 && parts.push(`${job.status === "running" ? "" : "ran for "}${formatDuration(Math.max(0, end - job.startedAt))}`);
|
|
572
|
-
}
|
|
573
|
-
return statJoin(theme, parts);
|
|
574
|
-
}
|
|
575
|
-
function widgetStepStats(theme, step, now = Date.now()) {
|
|
576
|
-
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;
|
|
577
|
-
return statJoin(theme, [
|
|
578
|
-
step.turnCount !== void 0 ? `${step.turnCount} turns` : "",
|
|
579
|
-
step.toolCount !== void 0 ? formatToolUseStat(step.toolCount) : "",
|
|
580
|
-
step.tokens?.total ? formatTokenStat(step.tokens.total) : "",
|
|
581
|
-
durationMs !== void 0 ? `${running ? "" : "ran for "}${formatDuration(durationMs)}` : ""
|
|
582
|
-
]);
|
|
583
|
-
}
|
|
584
543
|
function modelThinkingBadge(theme, model, thinking, selection) {
|
|
585
544
|
let label = formatModelSelection(selection, model, thinking);
|
|
586
545
|
return label ? theme.fg("dim", ` (${label})`) : "";
|
|
@@ -645,30 +604,28 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
|
|
|
645
604
|
};
|
|
646
605
|
return append(children, 0, ""), lines.map((line) => truncLine(line, width));
|
|
647
606
|
}
|
|
648
|
-
function
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
let maxArgsLen = Math.max(40, width - 30), argsPreview = tool.args.length <= maxArgsLen ? tool.args : `${tool.args.slice(0, maxArgsLen)}...`;
|
|
658
|
-
lines.push(` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`);
|
|
659
|
-
}
|
|
660
|
-
for (let line of step.recentOutput?.slice(-5) ?? [])
|
|
661
|
-
lines.push(` ${theme.fg("dim", line)}`);
|
|
662
|
-
}
|
|
663
|
-
return lines;
|
|
607
|
+
function compactTerminalStatus(status) {
|
|
608
|
+
if (status === "failed")
|
|
609
|
+
return "failed";
|
|
610
|
+
if (status === "paused")
|
|
611
|
+
return "paused";
|
|
612
|
+
if (status === "stopped")
|
|
613
|
+
return "stopped";
|
|
614
|
+
if (status === "detached")
|
|
615
|
+
return "detached";
|
|
664
616
|
}
|
|
665
617
|
function compactJobProgress(job, step, now = Date.now()) {
|
|
666
|
-
let running = (step?.status ?? job.status) === "running",
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
618
|
+
let running = (step?.status ?? job.status) === "running", tokens = step?.tokens?.total ?? job.totalTokens?.total ?? 0;
|
|
619
|
+
if (step?.durationMs !== void 0)
|
|
620
|
+
return {
|
|
621
|
+
tokens,
|
|
622
|
+
durationMs: liveDurationMs({ durationMs: step.durationMs, lastActivityAt: step.lastActivityAt }, running, now)
|
|
623
|
+
};
|
|
624
|
+
let startedAt = step?.startedAt ?? job.startedAt;
|
|
625
|
+
if (startedAt === void 0)
|
|
626
|
+
return { tokens, durationMs: 0 };
|
|
627
|
+
let endedAt = running ? now : step?.endedAt ?? step?.lastActivityAt ?? job.updatedAt ?? now;
|
|
628
|
+
return { tokens, durationMs: Math.max(0, endedAt - startedAt) };
|
|
672
629
|
}
|
|
673
630
|
function compactJobLead(job, step) {
|
|
674
631
|
return compactRowLead({
|
|
@@ -677,18 +634,15 @@ function compactJobLead(job, step) {
|
|
|
677
634
|
}) || widgetJobName(job);
|
|
678
635
|
}
|
|
679
636
|
function compactJobLines(job, step, theme, width, now = Date.now()) {
|
|
680
|
-
let running = (step?.status ?? job.status) === "running", lead = compactJobLead(job, step), modelBadge =
|
|
681
|
-
return [
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
function compactSingleWidgetLines(job, theme, width) {
|
|
691
|
-
return buildSingleWidgetLines(job, theme, width, !1);
|
|
637
|
+
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);
|
|
638
|
+
return [formatCompactSubagentRow(theme, width, {
|
|
639
|
+
glyph,
|
|
640
|
+
description: lead,
|
|
641
|
+
modelBadge,
|
|
642
|
+
tokens: progress.tokens,
|
|
643
|
+
durationMs: progress.durationMs,
|
|
644
|
+
terminalStatus: compactTerminalStatus(step?.status ?? job.status)
|
|
645
|
+
})];
|
|
692
646
|
}
|
|
693
647
|
var RESERVED_NON_WIDGET_ROWS = 19, widgetLayoutSession;
|
|
694
648
|
function resetWidgetLayoutSession() {
|
|
@@ -707,20 +661,6 @@ function currentTerminalColumns() {
|
|
|
707
661
|
function widgetSessionMatches(expanded) {
|
|
708
662
|
return widgetLayoutSession?.expanded === expanded && widgetLayoutSession.rows === currentTerminalRows() && widgetLayoutSession.columns === currentTerminalColumns();
|
|
709
663
|
}
|
|
710
|
-
function widgetHeaderCounts(jobs) {
|
|
711
|
-
return {
|
|
712
|
-
running: jobs.filter((job) => job.status === "running"),
|
|
713
|
-
queued: jobs.filter((job) => job.status === "queued"),
|
|
714
|
-
complete: jobs.filter((job) => job.status === "complete"),
|
|
715
|
-
failed: jobs.filter((job) => job.status === "failed"),
|
|
716
|
-
paused: jobs.filter((job) => job.status === "paused"),
|
|
717
|
-
stopped: jobs.filter((job) => job.status === "stopped")
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
function buildSingleLineWidgetLines(jobs, theme, width) {
|
|
721
|
-
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 = [];
|
|
722
|
-
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)];
|
|
723
|
-
}
|
|
724
664
|
function orderedWidgetJobs(jobs) {
|
|
725
665
|
return [
|
|
726
666
|
...jobs.filter((job) => job.status === "running"),
|
|
@@ -728,73 +668,6 @@ function orderedWidgetJobs(jobs) {
|
|
|
728
668
|
...jobs.filter((job) => job.status !== "running" && job.status !== "queued")
|
|
729
669
|
];
|
|
730
670
|
}
|
|
731
|
-
function progressiveJobKey(job) {
|
|
732
|
-
return job.asyncId;
|
|
733
|
-
}
|
|
734
|
-
function isProgressiveActiveJob(job) {
|
|
735
|
-
return job?.status === "running" || job?.status === "queued";
|
|
736
|
-
}
|
|
737
|
-
function selectProgressiveJobKeys(jobs, previousKeys, bodyRows) {
|
|
738
|
-
if (bodyRows <= 0)
|
|
739
|
-
return [];
|
|
740
|
-
let jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job])), selected = [], append = (key) => {
|
|
741
|
-
selected.includes(key) || !jobsByKey.has(key) || selected.push(key);
|
|
742
|
-
};
|
|
743
|
-
for (let key of previousKeys)
|
|
744
|
-
if (isProgressiveActiveJob(jobsByKey.get(key)) && (append(key), selected.length >= bodyRows))
|
|
745
|
-
return selected;
|
|
746
|
-
for (let job of orderedWidgetJobs(jobs)) {
|
|
747
|
-
if (!isProgressiveActiveJob(job))
|
|
748
|
-
continue;
|
|
749
|
-
let key = progressiveJobKey(job);
|
|
750
|
-
if (append(key), selected.length >= bodyRows)
|
|
751
|
-
break;
|
|
752
|
-
}
|
|
753
|
-
if (selected.length >= bodyRows)
|
|
754
|
-
return selected;
|
|
755
|
-
for (let key of previousKeys)
|
|
756
|
-
if (!isProgressiveActiveJob(jobsByKey.get(key)) && (append(key), selected.length >= bodyRows))
|
|
757
|
-
return selected;
|
|
758
|
-
for (let job of orderedWidgetJobs(jobs)) {
|
|
759
|
-
let key = progressiveJobKey(job);
|
|
760
|
-
if (append(key), selected.length >= bodyRows)
|
|
761
|
-
break;
|
|
762
|
-
}
|
|
763
|
-
return selected;
|
|
764
|
-
}
|
|
765
|
-
function progressiveHeaderLine(jobs, theme, width) {
|
|
766
|
-
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 = [];
|
|
767
|
-
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);
|
|
768
|
-
}
|
|
769
|
-
function progressiveJobLine(job, theme, width) {
|
|
770
|
-
let stats = widgetStats(job, theme), activity = widgetActivity(job), status = job.status === "complete" ? "done" : job.status, parts = [
|
|
771
|
-
themeBold(theme, widgetJobName(job)),
|
|
772
|
-
theme.fg("dim", status),
|
|
773
|
-
stats,
|
|
774
|
-
activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : ""
|
|
775
|
-
].filter(Boolean);
|
|
776
|
-
return truncLine(` ${widgetStatusGlyph(job, theme)} ${parts.join(` ${theme.fg("dim", "\xB7")} `)}`, width);
|
|
777
|
-
}
|
|
778
|
-
function progressiveHiddenLine(hiddenJobs, theme, width) {
|
|
779
|
-
let counts = widgetHeaderCounts(hiddenJobs), parts = [];
|
|
780
|
-
counts.running.length > 0 && parts.push(`${counts.running.length} running`), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`);
|
|
781
|
-
let finished = counts.complete.length + counts.failed.length + counts.paused.length + counts.stopped.length;
|
|
782
|
-
return finished > 0 && parts.push(`${finished} finished`), truncLine(theme.fg("dim", ` +${hiddenJobs.length} more${parts.length ? ` (${parts.join(", ")})` : ""}`), width);
|
|
783
|
-
}
|
|
784
|
-
function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKeys) {
|
|
785
|
-
let rowCount = Math.max(1, lockedRows);
|
|
786
|
-
if (rowCount === 1)
|
|
787
|
-
return { lines: buildSingleLineWidgetLines(jobs, theme, width), visibleJobKeys: [] };
|
|
788
|
-
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)));
|
|
789
|
-
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))));
|
|
790
|
-
let lines = [
|
|
791
|
-
progressiveHeaderLine(jobs, theme, width),
|
|
792
|
-
...visibleJobs.map((job) => progressiveJobLine(job, theme, width))
|
|
793
|
-
];
|
|
794
|
-
for (hiddenJobs.length > 0 && lines.length < rowCount && lines.push(progressiveHiddenLine(hiddenJobs, theme, width)); lines.length < rowCount; )
|
|
795
|
-
lines.push(" ");
|
|
796
|
-
return { lines: lines.slice(0, rowCount), visibleJobKeys };
|
|
797
|
-
}
|
|
798
671
|
function collapsedWidgetLineBudget(rows) {
|
|
799
672
|
return Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
|
|
800
673
|
}
|
|
@@ -805,150 +678,185 @@ function fitWidgetLineBudget(lines, theme, width, expanded) {
|
|
|
805
678
|
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`;
|
|
806
679
|
return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
|
|
807
680
|
}
|
|
808
|
-
function fitAdaptiveWidgetLines(
|
|
681
|
+
function fitAdaptiveWidgetLines(_jobs, lines, theme, width, expanded) {
|
|
809
682
|
if (expanded)
|
|
810
683
|
return resetWidgetLayoutSession(), fitWidgetLineBudget(lines, theme, width, !0);
|
|
811
|
-
let
|
|
812
|
-
if (hasMatchingSession && widgetLayoutSession?.tier === "single-line")
|
|
813
|
-
return buildSingleLineWidgetLines(jobs, theme, width);
|
|
814
|
-
if (hasMatchingSession && widgetLayoutSession?.tier === "progressive" && widgetLayoutSession.lockedRows !== void 0) {
|
|
815
|
-
let rendered2 = buildProgressiveWidgetLines(jobs, theme, width, widgetLayoutSession.lockedRows, widgetLayoutSession.visibleJobKeys);
|
|
816
|
-
return widgetLayoutSession.visibleJobKeys = rendered2.visibleJobKeys, rendered2.lines;
|
|
817
|
-
}
|
|
684
|
+
let rows = currentTerminalRows(), columns = currentTerminalColumns(), availableRows = estimateAvailableWidgetRows();
|
|
818
685
|
if (lines.length <= availableRows)
|
|
819
|
-
return widgetLayoutSession = { expanded, rows, columns, tier: "full"
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
return (
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
686
|
+
return widgetLayoutSession = { expanded, rows, columns, tier: "full" }, fitWidgetLineBudget(lines, theme, width, !1);
|
|
687
|
+
let lockedRows = widgetSessionMatches(expanded) && widgetLayoutSession?.tier === "progressive" ? widgetLayoutSession.lockedRows ?? 1 : Math.max(1, Math.min(availableRows, collapsedWidgetLineBudget(rows)));
|
|
688
|
+
if (widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows }, lockedRows === 1)
|
|
689
|
+
return lines.slice(0, 1);
|
|
690
|
+
let visibleCount = Math.max(1, lockedRows - 1), visible = lines.slice(0, visibleCount), hidden = lines.length - visible.length;
|
|
691
|
+
for (hidden > 0 && visible.push(truncLine(theme.fg("dim", `+${hidden} more`), width)); visible.length < lockedRows; )
|
|
692
|
+
visible.push(" ");
|
|
693
|
+
return visible.slice(0, lockedRows);
|
|
694
|
+
}
|
|
695
|
+
function widgetExpandedDiagnostics(job, step, index, theme, width, now) {
|
|
696
|
+
let lines = [], status = step?.status ?? job.status, runLabel = step ? `${job.asyncId}:${step.index ?? index}` : job.asyncId;
|
|
697
|
+
lines.push(truncLine(theme.fg("dim", ` \u23BF run ${runLabel} \xB7 ${status}`), width));
|
|
698
|
+
let error = step?.error;
|
|
699
|
+
error && lines.push(truncLine(theme.fg("error", ` ${error}`), width));
|
|
700
|
+
let snapshotNow = status === "running" ? now : step?.endedAt ?? job.updatedAt, activity = step ? widgetStepActivityLine(step, Math.max(1, width - 4), !0, snapshotNow) : widgetActivity(job, now);
|
|
701
|
+
activity && lines.push(truncLine(theme.fg("dim", ` ${activity}`), width));
|
|
702
|
+
for (let tool of step?.recentTools?.slice(-3) ?? [])
|
|
703
|
+
lines.push(truncLine(theme.fg("dim", ` ${tool.tool}${tool.args ? `: ${tool.args}` : ""}`), width));
|
|
704
|
+
for (let output of step?.recentOutput?.slice(-5) ?? [])
|
|
705
|
+
lines.push(truncLine(theme.fg("dim", ` ${output}`), width));
|
|
706
|
+
for (let nestedLine of formatNestedWidgetLines(step?.children ?? job.nestedChildren, theme, width, !0, snapshotNow))
|
|
707
|
+
lines.push(truncLine(` ${nestedLine}`, width));
|
|
708
|
+
return lines;
|
|
832
709
|
}
|
|
833
|
-
function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7 background")}`, width));
|
|
840
|
-
let items = [], hiddenRunning = 0, hiddenFinished = 0, queuedSummaryShown = !1, slots = MAX_WIDGET_JOBS;
|
|
841
|
-
for (let job of running) {
|
|
842
|
-
if (slots <= 0) {
|
|
843
|
-
hiddenRunning++;
|
|
844
|
-
continue;
|
|
845
|
-
}
|
|
846
|
-
let stats = widgetStats(job, theme);
|
|
847
|
-
items.push([
|
|
848
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
|
|
849
|
-
` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
|
|
850
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
851
|
-
]), slots--;
|
|
852
|
-
}
|
|
853
|
-
queued.length > 0 && slots > 0 && (items.push([`${theme.fg("muted", "\u25E6")} ${theme.fg("dim", `${queued.length} queued`)}`]), queuedSummaryShown = !0, slots--);
|
|
854
|
-
for (let job of finished) {
|
|
855
|
-
if (slots <= 0) {
|
|
856
|
-
hiddenFinished++;
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
let stats = widgetStats(job, theme);
|
|
860
|
-
items.push([
|
|
861
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
|
|
862
|
-
` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
|
|
863
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
864
|
-
]), slots--;
|
|
865
|
-
}
|
|
866
|
-
let hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0, hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
867
|
-
if (hiddenTotal > 0) {
|
|
868
|
-
let parts = [];
|
|
869
|
-
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(", ")})`)]);
|
|
870
|
-
}
|
|
871
|
-
for (let i = 0; i < items.length; i++) {
|
|
872
|
-
let item = items[i], last = i === items.length - 1, branch = last ? "\u2514\u2500" : "\u251C\u2500", continuation = last ? " " : "\u2502 ";
|
|
873
|
-
lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
|
|
874
|
-
for (let detail of item.slice(1))
|
|
875
|
-
lines.push(truncLine(`${theme.fg("dim", continuation)} ${detail}`, width));
|
|
710
|
+
function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1, now = Date.now()) {
|
|
711
|
+
let lines = [];
|
|
712
|
+
for (let job of orderedWidgetJobs(jobs)) {
|
|
713
|
+
let steps = job.steps?.length ? job.steps : job.agents?.length ? job.agents.map((agent, index) => ({ agent, description: agent, status: job.status, index })) : [void 0];
|
|
714
|
+
for (let [index, step] of steps.entries())
|
|
715
|
+
lines.push(...compactJobLines(job, step, theme, width, now)), expanded && lines.push(...widgetExpandedDiagnostics(job, step, index, theme, width, now));
|
|
876
716
|
}
|
|
877
717
|
return lines;
|
|
878
718
|
}
|
|
879
|
-
var
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
719
|
+
var activeWidget, SubagentAsyncWidget = class {
|
|
720
|
+
jobs;
|
|
721
|
+
expanded;
|
|
722
|
+
theme;
|
|
723
|
+
requestRender;
|
|
724
|
+
nowFn;
|
|
725
|
+
timer;
|
|
726
|
+
disposed = !1;
|
|
727
|
+
cachedWidth;
|
|
728
|
+
cachedLines;
|
|
729
|
+
constructor(jobs, theme, options) {
|
|
730
|
+
this.jobs = jobs, this.theme = theme, this.expanded = options?.expanded ?? !1, this.requestRender = options?.requestRender, this.nowFn = options?.now ?? Date.now, this.syncTimer();
|
|
731
|
+
}
|
|
732
|
+
setTheme(theme) {
|
|
733
|
+
this.theme = theme, this.invalidate();
|
|
734
|
+
}
|
|
735
|
+
setJobs(jobs) {
|
|
736
|
+
this.jobs = jobs, this.invalidate(), this.syncTimer();
|
|
737
|
+
}
|
|
738
|
+
setExpanded(expanded) {
|
|
739
|
+
this.expanded !== expanded && (this.expanded = expanded, this.invalidate());
|
|
740
|
+
}
|
|
741
|
+
setRequestRender(requestRender) {
|
|
742
|
+
this.requestRender = requestRender;
|
|
743
|
+
}
|
|
744
|
+
hasRunning() {
|
|
745
|
+
return this.jobs.some((job) => job.status === "running");
|
|
746
|
+
}
|
|
747
|
+
syncTimer() {
|
|
748
|
+
this.disposed || (this.hasRunning() ? this.startTimer() : this.stopTimer());
|
|
749
|
+
}
|
|
750
|
+
startTimer() {
|
|
751
|
+
this.timer || (this.timer = setInterval(() => {
|
|
752
|
+
this.disposed || (this.invalidate(), this.requestRender?.());
|
|
753
|
+
}, SUBAGENT_PAINT_INTERVAL_MS), this.timer.unref?.());
|
|
754
|
+
}
|
|
755
|
+
stopTimer() {
|
|
756
|
+
this.timer && (clearInterval(this.timer), this.timer = void 0);
|
|
757
|
+
}
|
|
758
|
+
invalidate() {
|
|
759
|
+
this.cachedWidth = void 0, this.cachedLines = void 0;
|
|
760
|
+
}
|
|
761
|
+
dispose() {
|
|
762
|
+
this.disposed = !0, this.stopTimer(), activeWidget === this && (activeWidget = void 0);
|
|
887
763
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
764
|
+
render(width) {
|
|
765
|
+
let theme = this.theme;
|
|
766
|
+
if (!theme)
|
|
767
|
+
return [];
|
|
768
|
+
if (this.cachedLines && this.cachedWidth === width && !this.hasRunning())
|
|
769
|
+
return this.cachedLines;
|
|
770
|
+
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) => {
|
|
771
|
+
let withPad = ` ${line}`, vis = visibleWidth(withPad);
|
|
772
|
+
return withPad + " ".repeat(Math.max(0, width - vis));
|
|
773
|
+
});
|
|
774
|
+
return this.cachedWidth = width, this.cachedLines = padded, padded;
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
function disposeSubagentWidget() {
|
|
778
|
+
activeWidget?.dispose(), activeWidget = void 0;
|
|
891
779
|
}
|
|
892
780
|
function renderWidget(ctx, jobs) {
|
|
893
781
|
if (jobs.length === 0) {
|
|
894
|
-
resetWidgetLayoutSession(),
|
|
782
|
+
resetWidgetLayoutSession(), disposeSubagentWidget(), ctx.hasUI && ctx.ui.setWidget(WIDGET_KEY, void 0);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (!ctx.hasUI)
|
|
786
|
+
return;
|
|
787
|
+
let expanded = ctx.ui.getToolsExpanded?.() ?? !1, requestRender = () => ctx.ui.requestRender?.();
|
|
788
|
+
if (activeWidget) {
|
|
789
|
+
activeWidget.setJobs(jobs), activeWidget.setExpanded(expanded), activeWidget.setRequestRender(requestRender), ctx.ui.requestRender?.();
|
|
895
790
|
return;
|
|
896
791
|
}
|
|
897
|
-
|
|
792
|
+
let widget = new SubagentAsyncWidget(jobs, void 0, { expanded, requestRender });
|
|
793
|
+
activeWidget = widget, ctx.ui.setWidget(WIDGET_KEY, (_tui, theme) => (widget.setTheme(theme), widget));
|
|
794
|
+
}
|
|
795
|
+
function compactResultTerminalStatus(result2) {
|
|
796
|
+
if (result2.stopped)
|
|
797
|
+
return "stopped";
|
|
798
|
+
if (result2.detached)
|
|
799
|
+
return "detached";
|
|
800
|
+
if (result2.interrupted)
|
|
801
|
+
return "paused";
|
|
802
|
+
if (result2.exitCode !== 0)
|
|
803
|
+
return "failed";
|
|
898
804
|
}
|
|
899
805
|
function renderSingleCompact(d, r, theme, frame) {
|
|
900
|
-
let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running",
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
806
|
+
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);
|
|
807
|
+
return animatedLine((animationTick, now) => formatCompactSubagentRow(theme, width, {
|
|
808
|
+
glyph: resultGlyph(r, output, theme, isRunning, void 0, foregroundSpinnerFrame(animationTick)),
|
|
809
|
+
description: lead,
|
|
810
|
+
modelBadge,
|
|
811
|
+
tokens: compactResultTokens(r),
|
|
812
|
+
durationMs: liveDurationMs(progress, isRunning, now),
|
|
813
|
+
terminalStatus: compactResultTerminalStatus(r)
|
|
814
|
+
}), frame, isRunning);
|
|
815
|
+
}
|
|
816
|
+
function progressStatusGlyph(progress, theme, frame) {
|
|
817
|
+
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");
|
|
818
|
+
}
|
|
819
|
+
function flattenCompactWorkflowNodes(nodes) {
|
|
820
|
+
let rows = [];
|
|
821
|
+
for (let node of nodes)
|
|
822
|
+
node.children?.length ? rows.push(...flattenCompactWorkflowNodes(node.children)) : rows.push(node);
|
|
823
|
+
return rows;
|
|
824
|
+
}
|
|
825
|
+
function compactForegroundEntries(d) {
|
|
826
|
+
let workflowNodes = flattenCompactWorkflowNodes(d.workflowGraph?.nodes ?? []);
|
|
827
|
+
if (workflowNodes.length === 0) {
|
|
828
|
+
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);
|
|
829
|
+
return Array.from({ length: count }, (_, flatIndex) => ({ flatIndex }));
|
|
830
|
+
}
|
|
831
|
+
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);
|
|
832
|
+
for (let flatIndex = 0; flatIndex < dataCount; flatIndex++)
|
|
833
|
+
seen.has(flatIndex) || entries.push({ flatIndex });
|
|
834
|
+
return entries;
|
|
909
835
|
}
|
|
910
836
|
function renderMultiCompact(d, theme, frame) {
|
|
911
|
-
let
|
|
912
|
-
|
|
913
|
-
let
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
});
|
|
929
|
-
for (let entry of renderEntries) {
|
|
930
|
-
if (entry.kind === "placeholder") {
|
|
931
|
-
let glyph2 = widgetStepGlyph(entry.status, theme), statusLabel = widgetStepStatus(entry.status, theme);
|
|
932
|
-
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));
|
|
933
|
-
continue;
|
|
934
|
-
}
|
|
935
|
-
let i = entry.resultIndex, r = d.results[i], rowNumber = entry.rowNumber, agentName = entry.agentName;
|
|
936
|
-
if (!r) {
|
|
937
|
-
let pendingLabel2 = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
|
|
938
|
-
c.addChild(new Text(truncLine(theme.fg("dim", ` \u25E6 ${pendingLabel2}: ${agentName} \xB7 pending`), width), 0, 0));
|
|
939
|
-
continue;
|
|
940
|
-
}
|
|
941
|
-
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));
|
|
942
|
-
c.addChild(animatedLine((f, now) => {
|
|
943
|
-
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 });
|
|
944
|
-
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);
|
|
945
|
-
}, 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));
|
|
837
|
+
let c = new Container(), width = getTermWidth() - 4, entries = compactForegroundEntries(d);
|
|
838
|
+
for (let [rowIndex, entry] of entries.entries()) {
|
|
839
|
+
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({
|
|
840
|
+
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,
|
|
841
|
+
task: result2?.task ?? progress?.task
|
|
842
|
+
}) || `child ${rowIndex + 1}`, running = status === "running", output = result2 ? getSingleResultOutput(result2) : "", modelBadge = formatCompactModelBadge(result2?.modelSelection ?? progress?.modelSelection, result2?.model ?? progress?.model);
|
|
843
|
+
c.addChild(animatedLine((animationTick, now) => {
|
|
844
|
+
let animationFrame = foregroundSpinnerFrame(animationTick);
|
|
845
|
+
return formatCompactSubagentRow(theme, width, {
|
|
846
|
+
glyph: result2 ? resultGlyph(result2, output, theme, running, progressRunningSeed(progress), animationFrame) : progressStatusGlyph({ ...progress, status, index: flatIndex }, theme, animationFrame),
|
|
847
|
+
description,
|
|
848
|
+
modelBadge,
|
|
849
|
+
tokens: result2 ? compactResultTokens(result2) : progress?.tokens ?? 0,
|
|
850
|
+
durationMs: liveDurationMs(progress, running, now),
|
|
851
|
+
terminalStatus: result2 ? compactResultTerminalStatus(result2) : compactTerminalStatus(status)
|
|
852
|
+
});
|
|
853
|
+
}, frame, running));
|
|
946
854
|
}
|
|
947
855
|
return c;
|
|
948
856
|
}
|
|
949
857
|
function renderSubagentResult(result2, options, theme, frame) {
|
|
950
|
-
let d = result2.details;
|
|
951
|
-
if (!d || !d.results.length) {
|
|
858
|
+
let d = result2.details, hasWorkflowRows = d?.mode !== "management" && !!(d?.workflowGraph?.nodes.length || d?.progress?.length || d?.chainChildren?.length);
|
|
859
|
+
if (!d || !d.results.length && !hasWorkflowRows) {
|
|
952
860
|
let t = result2.content[0], text = t?.type === "text" ? t.text : "(no output)", contextPrefix = d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "", width = getTermWidth() - 4;
|
|
953
861
|
if (!text.includes(`
|
|
954
862
|
`))
|
|
@@ -969,7 +877,7 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
969
877
|
return fit3(`${icon2} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge2}${stats ? ` | ${stats}` : ""}`);
|
|
970
878
|
}, frame, isRunning)), c2.addChild(new Spacer(1));
|
|
971
879
|
let taskMaxLen = Math.max(20, w2 - 8), taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
|
|
972
|
-
if (c2.addChild(new Text(fit3(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0)), c2.addChild(new Spacer(1)), isRunning && r.progress) {
|
|
880
|
+
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) {
|
|
973
881
|
let progressSnapshotNow = snapshotNowForProgress(r.progress), toolLine = formatCurrentToolLine(r.progress, w2, expanded, progressSnapshotNow);
|
|
974
882
|
toolLine && c2.addChild(new Text(fit3(theme.fg("warning", `> ${toolLine}`)), 0, 0));
|
|
975
883
|
let liveStatusLine = buildLiveStatusLine(r.progress, progressSnapshotNow);
|
|
@@ -991,7 +899,7 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
991
899
|
}
|
|
992
900
|
if (!expanded)
|
|
993
901
|
return renderMultiCompact(d, theme, frame);
|
|
994
|
-
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) => {
|
|
902
|
+
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) => {
|
|
995
903
|
let prog = r.progress || r.progressSummary;
|
|
996
904
|
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;
|
|
997
905
|
}, { 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) => {
|
|
@@ -1005,10 +913,26 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
1005
913
|
].filter(Boolean), summaryStr = summaryParts.length ? ` | ${summaryParts.join(", ")}` : "";
|
|
1006
914
|
return fit2(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} \xB7 ${multiLabel.headerLabel}${summaryStr}`);
|
|
1007
915
|
}, frame, hasRunning)), chainVis && c.addChild(new Text(fit2(` ${chainVis}`), 0, 0));
|
|
1008
|
-
let
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
916
|
+
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) => {
|
|
917
|
+
if (entry.flatIndex === void 0)
|
|
918
|
+
return {
|
|
919
|
+
kind: "placeholder",
|
|
920
|
+
rowNumber: entry.node?.stepIndex !== void 0 ? entry.node.stepIndex + 1 : rowIndex + 1,
|
|
921
|
+
stepLabel: `Step ${entry.node?.stepIndex !== void 0 ? entry.node.stepIndex + 1 : rowIndex + 1}`,
|
|
922
|
+
agentName: entry.node?.label ?? `step-${rowIndex + 1}`,
|
|
923
|
+
status: entry.node?.status ?? "pending",
|
|
924
|
+
error: entry.node?.error
|
|
925
|
+
};
|
|
926
|
+
let result3 = resultAtFlatIndex(d, entry.flatIndex), rowNumber = multiLabel.showActiveGroupOnly ? entry.flatIndex - multiLabel.groupStartIndex + 1 : entry.flatIndex + 1;
|
|
927
|
+
return {
|
|
928
|
+
kind: "result",
|
|
929
|
+
resultIndex: entry.flatIndex,
|
|
930
|
+
rowNumber,
|
|
931
|
+
agentName: result3?.agent ?? entry.node?.label ?? d.chainChildren?.[entry.flatIndex]?.description ?? d.chainAgents?.[entry.flatIndex] ?? `step-${rowNumber}`,
|
|
932
|
+
status: entry.node?.status,
|
|
933
|
+
error: entry.node?.error
|
|
934
|
+
};
|
|
935
|
+
}), renderEntries = chainEntries ?? fallbackEntries;
|
|
1012
936
|
c.addChild(new Spacer(1));
|
|
1013
937
|
for (let entry of renderEntries) {
|
|
1014
938
|
if (entry.kind === "placeholder") {
|
|
@@ -1016,10 +940,10 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
1016
940
|
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));
|
|
1017
941
|
continue;
|
|
1018
942
|
}
|
|
1019
|
-
let i = entry.resultIndex, r = d
|
|
943
|
+
let i = entry.resultIndex, r = resultAtFlatIndex(d, i), rowNumber = entry.rowNumber, agentName = entry.agentName;
|
|
1020
944
|
if (!r) {
|
|
1021
|
-
let
|
|
1022
|
-
c.addChild(new Text(fit2(theme.fg("dim", ` ${
|
|
945
|
+
let rowLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`, status = entry.status ?? "pending";
|
|
946
|
+
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));
|
|
1023
947
|
continue;
|
|
1024
948
|
}
|
|
1025
949
|
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);
|
|
@@ -1028,7 +952,7 @@ function renderSubagentResult(result2, options, theme, frame) {
|
|
|
1028
952
|
return fit2(rRunning ? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats ? ` | ${stats}` : ""}` : `${statusIcon} ${stepLabel}: ${theme.bold(r.agent)}${modelDisplay}${stats ? ` | ${stats}` : ""}`);
|
|
1029
953
|
}, frame, rRunning));
|
|
1030
954
|
let taskMaxLen = Math.max(20, w - 12), taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
|
|
1031
|
-
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) {
|
|
955
|
+
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) {
|
|
1032
956
|
rProg.skills?.length && c.addChild(new Text(fit2(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`)), 0, 0));
|
|
1033
957
|
let progressSnapshotNow = snapshotNowForProgress(rProg), toolLine = formatCurrentToolLine(rProg, w, expanded, progressSnapshotNow);
|
|
1034
958
|
toolLine && c.addChild(new Text(fit2(theme.fg("warning", ` > ${toolLine}`)), 0, 0));
|
|
@@ -1057,15 +981,15 @@ import { Type } from "typebox";
|
|
|
1057
981
|
function keepTopLevelParameterDescriptions(schema) {
|
|
1058
982
|
return pruneNestedDescriptions(schema, []);
|
|
1059
983
|
}
|
|
1060
|
-
function pruneNestedDescriptions(value,
|
|
984
|
+
function pruneNestedDescriptions(value, path13) {
|
|
1061
985
|
if (!value || typeof value != "object")
|
|
1062
986
|
return value;
|
|
1063
987
|
let result2 = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
|
|
1064
988
|
for (let key of Reflect.ownKeys(value)) {
|
|
1065
989
|
let descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1066
|
-
if (descriptor && !(key === "description" && !isPropertiesMap(
|
|
990
|
+
if (descriptor && !(key === "description" && !isPropertiesMap(path13) && !isTopLevelParameterHelpText(path13))) {
|
|
1067
991
|
if ("value" in descriptor) {
|
|
1068
|
-
let nextPath = typeof key == "string" ? [...
|
|
992
|
+
let nextPath = typeof key == "string" ? [...path13, key] : path13;
|
|
1069
993
|
descriptor.value = pruneNestedDescriptions(descriptor.value, nextPath);
|
|
1070
994
|
}
|
|
1071
995
|
Object.defineProperty(result2, key, descriptor);
|
|
@@ -1073,11 +997,11 @@ function pruneNestedDescriptions(value, path12) {
|
|
|
1073
997
|
}
|
|
1074
998
|
return result2;
|
|
1075
999
|
}
|
|
1076
|
-
function isPropertiesMap(
|
|
1077
|
-
return
|
|
1000
|
+
function isPropertiesMap(path13) {
|
|
1001
|
+
return path13[path13.length - 1] === "properties";
|
|
1078
1002
|
}
|
|
1079
|
-
function isTopLevelParameterHelpText(
|
|
1080
|
-
return
|
|
1003
|
+
function isTopLevelParameterHelpText(path13) {
|
|
1004
|
+
return path13.length === 2 && path13[0] === "properties";
|
|
1081
1005
|
}
|
|
1082
1006
|
var SkillOverride = Type.Unsafe({
|
|
1083
1007
|
anyOf: [
|
|
@@ -1095,10 +1019,16 @@ var SkillOverride = Type.Unsafe({
|
|
|
1095
1019
|
}), OutputModeOverride = Type.String({
|
|
1096
1020
|
enum: ["inline", "file-only"],
|
|
1097
1021
|
description: "Return saved output inline (default) or only a concise file reference. file-only requires output to be a path."
|
|
1098
|
-
}), TierOverride = Type.String({
|
|
1022
|
+
}), 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({
|
|
1099
1023
|
enum: ["light", "standard", "heavy"],
|
|
1100
|
-
description:
|
|
1101
|
-
}),
|
|
1024
|
+
description: `Required by default. ${TIER_SELECTION_DESCRIPTION} Do not combine with model. The configured tier model must be available and authenticated.`
|
|
1025
|
+
})), ModelOverride = Type.Optional(Type.String({
|
|
1026
|
+
minLength: 1,
|
|
1027
|
+
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."
|
|
1028
|
+
})), ThinkingOverride = Type.Optional(Type.String({
|
|
1029
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1030
|
+
description: "Thinking level for an explicit model launch. Omit for tier launches (they use configured tier thinking)."
|
|
1031
|
+
})), ReadsOverride = Type.Unsafe({
|
|
1102
1032
|
anyOf: [
|
|
1103
1033
|
{ type: "array", items: { type: "string" } },
|
|
1104
1034
|
{ type: "boolean", enum: [!1] }
|
|
@@ -1145,6 +1075,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1145
1075
|
reads: Type.Optional(ReadsOverride),
|
|
1146
1076
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking for this task" })),
|
|
1147
1077
|
tier: TierOverride,
|
|
1078
|
+
model: ModelOverride,
|
|
1079
|
+
thinking: ThinkingOverride,
|
|
1148
1080
|
skill: Type.Optional(SkillOverride),
|
|
1149
1081
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1150
1082
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
@@ -1164,6 +1096,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1164
1096
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1165
1097
|
skill: Type.Optional(SkillOverride),
|
|
1166
1098
|
tier: TierOverride,
|
|
1099
|
+
model: ModelOverride,
|
|
1100
|
+
thinking: ThinkingOverride,
|
|
1167
1101
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1168
1102
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1169
1103
|
}), DynamicExpandSchema = Type.Object({
|
|
@@ -1193,6 +1127,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1193
1127
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1194
1128
|
skill: Type.Optional(SkillOverride),
|
|
1195
1129
|
tier: TierOverride,
|
|
1130
|
+
model: ModelOverride,
|
|
1131
|
+
thinking: ThinkingOverride,
|
|
1196
1132
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1197
1133
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1198
1134
|
}, { additionalProperties: !1 }), DynamicCollectSchema = Type.Object({
|
|
@@ -1215,6 +1151,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1215
1151
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1216
1152
|
skill: Type.Optional(SkillOverride),
|
|
1217
1153
|
tier: TierOverride,
|
|
1154
|
+
model: ModelOverride,
|
|
1155
|
+
thinking: ThinkingOverride,
|
|
1218
1156
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1219
1157
|
acceptance: Type.Optional(AcceptanceOverride),
|
|
1220
1158
|
parallel: Type.Optional(Type.Unsafe({
|
|
@@ -1232,7 +1170,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1232
1170
|
description: "Create isolated git worktrees for each parallel task."
|
|
1233
1171
|
}))
|
|
1234
1172
|
}, {
|
|
1235
|
-
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
|
|
1173
|
+
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.",
|
|
1236
1174
|
additionalProperties: !1
|
|
1237
1175
|
}), ControlOverrides = Type.Object({
|
|
1238
1176
|
enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
|
|
@@ -1271,12 +1209,9 @@ var SkillOverride = Type.Unsafe({
|
|
|
1271
1209
|
})),
|
|
1272
1210
|
lines: Type.Optional(Type.Integer({ minimum: 1, maximum: 500, description: "Maximum transcript lines for action='status', view='transcript'. Defaults to 80." })),
|
|
1273
1211
|
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." })),
|
|
1274
|
-
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." })),
|
|
1275
|
-
target: Type.Optional(Type.String({ enum: ["main", "children"], description: "Target for action='watchdog.configure'. Defaults to main." })),
|
|
1276
|
-
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)." })),
|
|
1277
1212
|
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." })),
|
|
1278
1213
|
scheduleName: Type.Optional(Type.String({ description: "Optional display name for action='schedule'." })),
|
|
1279
|
-
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
|
|
1214
|
+
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier|model, thinking?, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
|
|
1280
1215
|
concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or unlimited." })),
|
|
1281
1216
|
worktree: Type.Optional(Type.Boolean({
|
|
1282
1217
|
description: "Create isolated git worktrees for parallel tasks; requires clean git state."
|
|
@@ -1287,7 +1222,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1287
1222
|
description: "Children always start with a fresh session. Fork context is not available."
|
|
1288
1223
|
})),
|
|
1289
1224
|
chainDir: Type.Optional(Type.String({ description: "Persistent chain artifact directory; defaults to user-scoped temp storage." })),
|
|
1290
|
-
async: Type.Optional(Type.Boolean({ description: "Run in background
|
|
1225
|
+
async: Type.Optional(Type.Boolean({ description: "Run async in the background. Defaults to true when omitted; set false for immediate foreground results." })),
|
|
1291
1226
|
timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Optional run-level timeout in ms for foreground and async/background runs. Alias of maxRuntimeMs." })),
|
|
1292
1227
|
maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias of timeoutMs for optional run-level timeout in foreground and async/background runs." })),
|
|
1293
1228
|
turnBudget: Type.Optional(TurnBudgetOverride),
|
|
@@ -1298,7 +1233,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1298
1233
|
share: Type.Optional(Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)" })),
|
|
1299
1234
|
sessionDir: Type.Optional(Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" })),
|
|
1300
1235
|
// Clarification TUI
|
|
1301
|
-
clarify: Type.Optional(Type.Boolean({ description: "Show TUI
|
|
1236
|
+
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." })),
|
|
1302
1237
|
control: Type.Optional(ControlOverrides),
|
|
1303
1238
|
output: Type.Optional(Type.Unsafe({
|
|
1304
1239
|
anyOf: [
|
|
@@ -1311,15 +1246,26 @@ var SkillOverride = Type.Unsafe({
|
|
|
1311
1246
|
skill: Type.Optional(SkillOverride),
|
|
1312
1247
|
tier: Type.Optional(Type.String({
|
|
1313
1248
|
enum: ["light", "standard", "heavy"],
|
|
1314
|
-
description:
|
|
1249
|
+
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}`
|
|
1250
|
+
})),
|
|
1251
|
+
model: Type.Optional(Type.String({
|
|
1252
|
+
minLength: 1,
|
|
1253
|
+
description: "Explicit provider/model for SINGLE execution only when the user names a model. Do not combine with tier."
|
|
1254
|
+
})),
|
|
1255
|
+
thinking: Type.Optional(Type.String({
|
|
1256
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1257
|
+
description: "Thinking level for an explicit model launch. Tier launches use configured tier thinking instead."
|
|
1315
1258
|
})),
|
|
1316
1259
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1317
1260
|
}), SubagentParams = keepTopLevelParameterDescriptions(SubagentParamsSchema), SubagentWaitParamsSchema = Type.Object({
|
|
1318
1261
|
id: Type.Optional(Type.String({
|
|
1319
1262
|
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."
|
|
1320
1263
|
})),
|
|
1264
|
+
questionId: Type.Optional(Type.String({
|
|
1265
|
+
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."
|
|
1266
|
+
})),
|
|
1321
1267
|
all: Type.Optional(Type.Boolean({
|
|
1322
|
-
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
|
|
1268
|
+
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."
|
|
1323
1269
|
})),
|
|
1324
1270
|
timeoutMs: Type.Optional(Type.Integer({
|
|
1325
1271
|
minimum: 1,
|
|
@@ -1344,28 +1290,28 @@ function typeName(value) {
|
|
|
1344
1290
|
function quoteList(keys) {
|
|
1345
1291
|
return keys.map((key) => `"${key}"`).join(", ");
|
|
1346
1292
|
}
|
|
1347
|
-
function disallowedMessage(
|
|
1293
|
+
function disallowedMessage(path13, keys, allowed, example) {
|
|
1348
1294
|
let subject = keys.length === 1 ? `property "${keys[0]}" is not allowed` : `properties ${quoteList(keys)} are not allowed`, examplePart = example ? `
|
|
1349
1295
|
|
|
1350
1296
|
Example: ${example}` : "";
|
|
1351
|
-
return `subagent ${
|
|
1297
|
+
return `subagent ${path13}: ${subject}.
|
|
1352
1298
|
Allowed properties: ${allowed.join(", ")}.${examplePart}`;
|
|
1353
1299
|
}
|
|
1354
|
-
function expectObject(value,
|
|
1300
|
+
function expectObject(value, path13, allowed, example) {
|
|
1355
1301
|
if (!isPlainObject(value)) {
|
|
1356
1302
|
let examplePart = example ? `
|
|
1357
1303
|
|
|
1358
1304
|
Example: ${example}` : "";
|
|
1359
|
-
throw new Error(`subagent ${
|
|
1305
|
+
throw new Error(`subagent ${path13}: expected an object, received ${typeName(value)}.
|
|
1360
1306
|
Allowed properties: ${allowed.join(", ")}.${examplePart}`);
|
|
1361
1307
|
}
|
|
1362
1308
|
}
|
|
1363
|
-
function checkNoExtraKeys(value,
|
|
1309
|
+
function checkNoExtraKeys(value, path13, schema, allowed, example) {
|
|
1364
1310
|
if (schema.additionalProperties !== !1)
|
|
1365
1311
|
return;
|
|
1366
1312
|
let extra = disallowedKeys(value, allowed);
|
|
1367
1313
|
if (extra.length > 0)
|
|
1368
|
-
throw new Error(disallowedMessage(
|
|
1314
|
+
throw new Error(disallowedMessage(path13, extra, allowed, example));
|
|
1369
1315
|
}
|
|
1370
1316
|
function validateChainInput(args) {
|
|
1371
1317
|
if (!isPlainObject(args))
|
|
@@ -1523,7 +1469,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
|
|
|
1523
1469
|
if (parsed.type !== "subagent.control")
|
|
1524
1470
|
return;
|
|
1525
1471
|
let record = parsed;
|
|
1526
|
-
if (!record.event || !Array.isArray(record.channels))
|
|
1472
|
+
if (!record.event || !Array.isArray(record.channels) || record.event.reason === "completion_guard")
|
|
1527
1473
|
return;
|
|
1528
1474
|
let payload = {
|
|
1529
1475
|
event: record.event,
|
|
@@ -1618,8 +1564,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
|
|
|
1618
1564
|
}
|
|
1619
1565
|
widgetRenderKey(job) !== widgetStateBefore && (widgetChanged = !0);
|
|
1620
1566
|
}
|
|
1621
|
-
|
|
1622
|
-
(widgetChanged || timerNeedsPaint) && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
|
|
1567
|
+
widgetChanged && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
|
|
1623
1568
|
}, pollIntervalMs), state.poller.unref?.());
|
|
1624
1569
|
};
|
|
1625
1570
|
return { ensurePoller, refreshWidget, handleStarted: (data) => {
|
|
@@ -1759,15 +1704,15 @@ function createFileCoalescer(handler, defaultDelayMs, timerApi = defaultTimerApi
|
|
|
1759
1704
|
|
|
1760
1705
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js
|
|
1761
1706
|
var WATCHER_RESTART_DELAY_MS = 3e3, POLL_INTERVAL_MS2 = 3e3;
|
|
1762
|
-
function sanitizeNestedResultChildren(value, resultPath, label) {
|
|
1707
|
+
function sanitizeNestedResultChildren(value, resultPath, label, report) {
|
|
1763
1708
|
if (value === void 0)
|
|
1764
1709
|
return;
|
|
1765
1710
|
if (!Array.isArray(value)) {
|
|
1766
|
-
|
|
1711
|
+
report(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
|
|
1767
1712
|
return;
|
|
1768
1713
|
}
|
|
1769
1714
|
let children = value.map((child) => sanitizeSummary(child)).filter((child) => !!child);
|
|
1770
|
-
return children.length !== value.length &&
|
|
1715
|
+
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;
|
|
1771
1716
|
}
|
|
1772
1717
|
function getErrorCode(error) {
|
|
1773
1718
|
return typeof error == "object" && error !== null && "code" in error ? error.code : void 0;
|
|
@@ -1780,27 +1725,27 @@ function shouldFallBackToPolling(error) {
|
|
|
1780
1725
|
return code === "EMFILE" || code === "ENOSPC";
|
|
1781
1726
|
}
|
|
1782
1727
|
function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {}) {
|
|
1783
|
-
let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval },
|
|
1784
|
-
|
|
1785
|
-
|
|
1728
|
+
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) => {
|
|
1729
|
+
pi.appendEntry?.("subagent_result_delivery", { message, error: error instanceof Error ? error.message : error });
|
|
1730
|
+
}, handleResult = async (file) => {
|
|
1731
|
+
if (inFlight.has(file) || (attempts.get(file) ?? 0) >= 3)
|
|
1732
|
+
return;
|
|
1733
|
+
let currentGeneration = generation, sessionId = state.currentSessionId, isCurrent = () => generation === currentGeneration && state.currentSessionId === sessionId, resultPath = path2.join(resultsDir, file);
|
|
1734
|
+
if (fsApi.existsSync(resultPath)) {
|
|
1735
|
+
inFlight.add(file);
|
|
1786
1736
|
try {
|
|
1787
1737
|
let data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8"));
|
|
1788
1738
|
if (typeof data.sessionId != "string" || data.sessionId !== state.currentSessionId)
|
|
1789
1739
|
return;
|
|
1790
|
-
let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren"));
|
|
1740
|
+
let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren", report));
|
|
1791
1741
|
if (!nestedChildren?.length && !hasExplicitNestedChildren)
|
|
1792
1742
|
try {
|
|
1793
1743
|
nestedChildren = compactNestedResultChildren(projectNestedRegistryForRoot(runId)?.children);
|
|
1794
1744
|
} catch (error) {
|
|
1795
|
-
|
|
1745
|
+
report(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
|
|
1796
1746
|
return;
|
|
1797
1747
|
}
|
|
1798
|
-
let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`)
|
|
1799
|
-
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs)) {
|
|
1800
|
-
fsApi.unlinkSync(resultPath);
|
|
1801
|
-
return;
|
|
1802
|
-
}
|
|
1803
|
-
let hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
|
|
1748
|
+
let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`), hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
|
|
1804
1749
|
agent: data.agent,
|
|
1805
1750
|
output: data.summary,
|
|
1806
1751
|
success: data.success
|
|
@@ -1808,7 +1753,7 @@ function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {})
|
|
|
1808
1753
|
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 ? `
|
|
1809
1754
|
|
|
1810
1755
|
Output:
|
|
1811
|
-
${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children
|
|
1756
|
+
${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;
|
|
1812
1757
|
return {
|
|
1813
1758
|
agent: result2.agent ?? data.agent ?? `step-${index + 1}`,
|
|
1814
1759
|
status: resolveSubagentResultStatus({
|
|
@@ -1822,7 +1767,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1822
1767
|
...result2.intercomTarget ? { intercomTarget: result2.intercomTarget } : {},
|
|
1823
1768
|
...childNestedChildren ? { children: childNestedChildren } : {}
|
|
1824
1769
|
};
|
|
1825
|
-
}), nestedChildren)
|
|
1770
|
+
}), nestedChildren);
|
|
1771
|
+
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs) || pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
|
|
1772
|
+
...data,
|
|
1773
|
+
runId,
|
|
1774
|
+
...nestedChildren?.length ? { nestedChildren } : {},
|
|
1775
|
+
...Array.isArray(data.results) ? { results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
1776
|
+
...data.results[index],
|
|
1777
|
+
agent: child.agent,
|
|
1778
|
+
status: child.status,
|
|
1779
|
+
summary: child.summary,
|
|
1780
|
+
index: child.index,
|
|
1781
|
+
artifactPath: child.artifactPath,
|
|
1782
|
+
sessionPath: child.sessionPath,
|
|
1783
|
+
children: child.children
|
|
1784
|
+
})) : [] } : {}
|
|
1785
|
+
}), !isCurrent())
|
|
1786
|
+
return;
|
|
1787
|
+
let intercomTarget = data.state === "stopped" ? void 0 : data.intercomTarget?.trim();
|
|
1826
1788
|
if (intercomTarget) {
|
|
1827
1789
|
let mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain" ? data.mode : resultChildren.length > 1 ? "chain" : "single", payload = buildSubagentResultIntercomPayload({
|
|
1828
1790
|
to: intercomTarget,
|
|
@@ -1833,30 +1795,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1833
1795
|
asyncId: data.id,
|
|
1834
1796
|
asyncDir: data.asyncDir
|
|
1835
1797
|
});
|
|
1836
|
-
|
|
1798
|
+
payload.requestId = `async-result:${sessionId}:${completionKey}`, payload.ownerNotificationSessionId = sessionId, attempts.set(file, (attempts.get(file) ?? 0) + 1);
|
|
1799
|
+
let delivered = await deliverSubagentResultIntercomEvent(pi.events, payload);
|
|
1800
|
+
if (!isCurrent())
|
|
1801
|
+
return;
|
|
1802
|
+
if (!delivered) {
|
|
1803
|
+
report(`Async result delivery pending for '${runId}'; output retained.`, resultPath), attempts.get(file) < 3 && state.resultFileCoalescer.schedule(file, 1e3);
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1837
1806
|
}
|
|
1838
|
-
|
|
1839
|
-
...data,
|
|
1840
|
-
runId,
|
|
1841
|
-
...nestedChildren?.length ? { nestedChildren } : {},
|
|
1842
|
-
...Array.isArray(data.results) ? {
|
|
1843
|
-
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
1844
|
-
...data.results[index],
|
|
1845
|
-
agent: child.agent,
|
|
1846
|
-
status: child.status,
|
|
1847
|
-
summary: child.summary,
|
|
1848
|
-
index: child.index,
|
|
1849
|
-
artifactPath: child.artifactPath,
|
|
1850
|
-
sessionPath: child.sessionPath,
|
|
1851
|
-
children: child.children
|
|
1852
|
-
})) : []
|
|
1853
|
-
} : {}
|
|
1854
|
-
}), fsApi.unlinkSync(resultPath);
|
|
1807
|
+
isCurrent() && fsApi.unlinkSync(resultPath);
|
|
1855
1808
|
} catch (error) {
|
|
1856
1809
|
if (isNotFoundError(error))
|
|
1857
1810
|
return;
|
|
1858
|
-
|
|
1811
|
+
report(`Failed to process subagent result file '${resultPath}'.`, error);
|
|
1812
|
+
} finally {
|
|
1813
|
+
inFlight.delete(file);
|
|
1859
1814
|
}
|
|
1815
|
+
}
|
|
1860
1816
|
};
|
|
1861
1817
|
state.resultFileCoalescer = createFileCoalescer((file) => {
|
|
1862
1818
|
handleResult(file);
|
|
@@ -1867,10 +1823,10 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1867
1823
|
} catch (error) {
|
|
1868
1824
|
if (isNotFoundError(error))
|
|
1869
1825
|
return;
|
|
1870
|
-
|
|
1826
|
+
report(`Failed to scan subagent result directory '${resultsDir}':`, error);
|
|
1871
1827
|
}
|
|
1872
1828
|
}, startPollingFallback = (reason) => {
|
|
1873
|
-
state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (
|
|
1829
|
+
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?.());
|
|
1874
1830
|
}, scheduleRestart = () => {
|
|
1875
1831
|
state.watcherRestartTimer || (state.watcherRestartTimer = timers.setTimeout(() => {
|
|
1876
1832
|
state.watcherRestartTimer = null;
|
|
@@ -1881,7 +1837,7 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1881
1837
|
startPollingFallback(error);
|
|
1882
1838
|
return;
|
|
1883
1839
|
}
|
|
1884
|
-
|
|
1840
|
+
report(`Failed to restart subagent result watcher for '${resultsDir}':`, error), scheduleRestart();
|
|
1885
1841
|
}
|
|
1886
1842
|
}, WATCHER_RESTART_DELAY_MS), state.watcherRestartTimer.unref?.());
|
|
1887
1843
|
}, startResultWatcher = () => {
|
|
@@ -1899,19 +1855,19 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1899
1855
|
startPollingFallback(error);
|
|
1900
1856
|
return;
|
|
1901
1857
|
}
|
|
1902
|
-
|
|
1858
|
+
report(`Subagent result watcher failed for '${resultsDir}':`, error), state.watcher?.close(), state.watcher = null, scheduleRestart();
|
|
1903
1859
|
}), state.watcher.unref?.();
|
|
1904
1860
|
} catch (error) {
|
|
1905
1861
|
if (shouldFallBackToPolling(error)) {
|
|
1906
1862
|
startPollingFallback(error);
|
|
1907
1863
|
return;
|
|
1908
1864
|
}
|
|
1909
|
-
|
|
1865
|
+
report(`Failed to start subagent result watcher for '${resultsDir}':`, error), state.watcher = null, scheduleRestart();
|
|
1910
1866
|
}
|
|
1911
1867
|
}
|
|
1912
1868
|
};
|
|
1913
1869
|
return { startResultWatcher, primeExistingResults, stopResultWatcher: () => {
|
|
1914
|
-
state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
|
|
1870
|
+
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();
|
|
1915
1871
|
} };
|
|
1916
1872
|
}
|
|
1917
1873
|
|
|
@@ -2444,7 +2400,7 @@ function substituteArgs(template, args) {
|
|
|
2444
2400
|
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] ?? "");
|
|
2445
2401
|
}
|
|
2446
2402
|
function parseRuntimeOptions(words) {
|
|
2447
|
-
let args = [], descriptionOverride, permissions, worktree = !1,
|
|
2403
|
+
let args = [], descriptionOverride, permissions, worktree = !1, async;
|
|
2448
2404
|
for (let i = 0; i < words.length; i++) {
|
|
2449
2405
|
let word = words[i];
|
|
2450
2406
|
if (word === "--read-only") {
|
|
@@ -2460,7 +2416,11 @@ function parseRuntimeOptions(words) {
|
|
|
2460
2416
|
continue;
|
|
2461
2417
|
}
|
|
2462
2418
|
if (word === "--bg" || word === "--async") {
|
|
2463
|
-
|
|
2419
|
+
async = !0;
|
|
2420
|
+
continue;
|
|
2421
|
+
}
|
|
2422
|
+
if (word === "--fg" || word === "--foreground") {
|
|
2423
|
+
async = !1;
|
|
2464
2424
|
continue;
|
|
2465
2425
|
}
|
|
2466
2426
|
if (word === "--description") {
|
|
@@ -2474,7 +2434,7 @@ function parseRuntimeOptions(words) {
|
|
|
2474
2434
|
}
|
|
2475
2435
|
args.push(word);
|
|
2476
2436
|
}
|
|
2477
|
-
return { args, descriptionOverride, permissions, worktree,
|
|
2437
|
+
return { args, descriptionOverride, permissions, worktree, async };
|
|
2478
2438
|
}
|
|
2479
2439
|
function splitChainDeclaration(input) {
|
|
2480
2440
|
let delimiter = input.indexOf(" -- ");
|
|
@@ -2493,7 +2453,7 @@ function workflowParams(workflow, args, runtime) {
|
|
|
2493
2453
|
...workflow.skill !== void 0 ? { skill: workflow.skill } : {},
|
|
2494
2454
|
...workflow.cwd ? { cwd: workflow.cwd } : {},
|
|
2495
2455
|
...runtime.worktree || workflow.worktree ? { worktree: !0 } : {},
|
|
2496
|
-
...runtime.
|
|
2456
|
+
...runtime.async !== void 0 ? { async: runtime.async } : {}
|
|
2497
2457
|
};
|
|
2498
2458
|
}
|
|
2499
2459
|
function workflowChainStep(workflow, args, runtime) {
|
|
@@ -2541,7 +2501,7 @@ function registerPromptWorkflowCommands(input) {
|
|
|
2541
2501
|
throw new Error(`Unknown prompt workflow in chain '${workflow.name}': ${stepName}`);
|
|
2542
2502
|
return workflowChainStep(step, runtime.args, runtime);
|
|
2543
2503
|
});
|
|
2544
|
-
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.
|
|
2504
|
+
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.async !== void 0 ? { async: runtime.async } : {} }, ctx);
|
|
2545
2505
|
return;
|
|
2546
2506
|
}
|
|
2547
2507
|
await run(workflowParams(workflow, runtime.args, runtime), ctx);
|
|
@@ -2569,7 +2529,7 @@ function registerPromptWorkflowCommands(input) {
|
|
|
2569
2529
|
throw new Error(`Unknown prompt workflow: ${name}`);
|
|
2570
2530
|
return workflowChainStep(workflow, runtime.args, runtime);
|
|
2571
2531
|
});
|
|
2572
|
-
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.
|
|
2532
|
+
await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.async !== void 0 ? { async: runtime.async } : {} }, ctx);
|
|
2573
2533
|
} catch (error) {
|
|
2574
2534
|
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
2575
2535
|
}
|
|
@@ -3100,6 +3060,12 @@ var parseInlineConfig = (raw) => {
|
|
|
3100
3060
|
case "tier":
|
|
3101
3061
|
(val === "light" || val === "standard" || val === "heavy") && (config.tier = val);
|
|
3102
3062
|
break;
|
|
3063
|
+
case "model":
|
|
3064
|
+
val && (config.model = val);
|
|
3065
|
+
break;
|
|
3066
|
+
case "thinking":
|
|
3067
|
+
["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(val) && (config.thinking = val);
|
|
3068
|
+
break;
|
|
3103
3069
|
case "permissions":
|
|
3104
3070
|
(val === "full" || val === "read-only") && (config.permissions = val);
|
|
3105
3071
|
break;
|
|
@@ -3143,15 +3109,14 @@ var parseInlineConfig = (raw) => {
|
|
|
3143
3109
|
let end = token.lastIndexOf("]");
|
|
3144
3110
|
return { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : void 0)) };
|
|
3145
3111
|
}, extractExecutionFlags = (rawArgs) => {
|
|
3146
|
-
let args = rawArgs.trim(),
|
|
3112
|
+
let args = rawArgs.trim(), async;
|
|
3147
3113
|
for (; ; ) {
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
break;
|
|
3114
|
+
let match = args.match(/(?:^|\s)(--bg|--async|--fg|--foreground)$/);
|
|
3115
|
+
if (!match)
|
|
3116
|
+
break;
|
|
3117
|
+
async ??= match[1] === "--bg" || match[1] === "--async", args = args.slice(0, match.index).trim();
|
|
3153
3118
|
}
|
|
3154
|
-
return { args,
|
|
3119
|
+
return { args, async };
|
|
3155
3120
|
};
|
|
3156
3121
|
function sendSlashText(pi, text) {
|
|
3157
3122
|
pi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: !0 });
|
|
@@ -3649,12 +3614,16 @@ function loadInlineOutputSchema(baseCwd, agent, value) {
|
|
|
3649
3614
|
}
|
|
3650
3615
|
var mapParsedTaskToStepObject = (step, fallbackTask, isFirst, opts) => {
|
|
3651
3616
|
let { name, config, task: stepTask } = step;
|
|
3652
|
-
if (!config.tier)
|
|
3653
|
-
throw new SlashParseError(`Step '${name}' requires tier=light
|
|
3617
|
+
if (!config.tier && !config.model)
|
|
3618
|
+
throw new SlashParseError(`Step '${name}' requires tier=light|standard|heavy or model=provider/id.`);
|
|
3619
|
+
if (config.tier && config.model)
|
|
3620
|
+
throw new SlashParseError(`Step '${name}' must choose exactly one of tier or model.`);
|
|
3654
3621
|
return config.acceptance !== void 0 && validateInlineAcceptanceInput(config.acceptance, name), {
|
|
3655
3622
|
description: name,
|
|
3656
3623
|
permissions: config.permissions ?? "full",
|
|
3657
|
-
tier: config.tier,
|
|
3624
|
+
...config.tier ? { tier: config.tier } : {},
|
|
3625
|
+
...config.model ? { model: config.model } : {},
|
|
3626
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
3658
3627
|
...stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {},
|
|
3659
3628
|
...config.output !== void 0 ? { output: config.output } : {},
|
|
3660
3629
|
...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
|
|
@@ -3728,16 +3697,16 @@ function registerSlashCommands(pi, state) {
|
|
|
3728
3697
|
}
|
|
3729
3698
|
};
|
|
3730
3699
|
pi.registerCommand("run", {
|
|
3731
|
-
description: "Run
|
|
3700
|
+
description: "Run an async prompt-defined child; add --foreground to wait inline",
|
|
3732
3701
|
handler: async (args, ctx) => {
|
|
3733
|
-
let { args: cleanedArgs,
|
|
3702
|
+
let { args: cleanedArgs, async } = extractExecutionFlags(args), input = cleanedArgs.trim(), firstSpace = input.indexOf(" ");
|
|
3734
3703
|
if (!input) {
|
|
3735
|
-
ctx.ui.notify("Usage: /run <description> <task> [--
|
|
3704
|
+
ctx.ui.notify("Usage: /run <description> <task> [--foreground]", "error");
|
|
3736
3705
|
return;
|
|
3737
3706
|
}
|
|
3738
3707
|
let { name: description, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace)), task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
|
|
3739
3708
|
if (!description || !task || !inline.tier) {
|
|
3740
|
-
ctx.ui.notify("Usage: /run <description>[tier=light|standard|heavy] <task> [--
|
|
3709
|
+
ctx.ui.notify("Usage: /run <description>[tier=light|standard|heavy] <task> [--foreground]", "error");
|
|
3741
3710
|
return;
|
|
3742
3711
|
}
|
|
3743
3712
|
let finalTask = task;
|
|
@@ -3745,27 +3714,29 @@ function registerSlashCommands(pi, state) {
|
|
|
3745
3714
|
|
|
3746
3715
|
${finalTask}`);
|
|
3747
3716
|
let params = { description, permissions: inline.permissions ?? "full", tier: inline.tier, task: finalTask, clarify: !1 };
|
|
3748
|
-
inline.output !== void 0 && (params.output = inline.output), inline.outputMode !== void 0 && (params.outputMode = inline.outputMode), inline.skill !== void 0 && (params.skill = inline.skill),
|
|
3717
|
+
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);
|
|
3749
3718
|
}
|
|
3750
3719
|
}), pi.registerCommand("chain", {
|
|
3751
|
-
description:
|
|
3720
|
+
description: "Run prompt-defined children as an async sequence; add --foreground to wait inline",
|
|
3752
3721
|
handler: async (args, ctx) => {
|
|
3753
|
-
let { args: cleanedArgs,
|
|
3722
|
+
let { args: cleanedArgs, async } = extractExecutionFlags(args), built = buildChainExpressionSteps(state, cleanedArgs, ctx);
|
|
3754
3723
|
if (!built)
|
|
3755
3724
|
return;
|
|
3756
3725
|
let params = { chain: built.chain, task: built.task, clarify: !1 };
|
|
3757
|
-
|
|
3726
|
+
async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
|
|
3758
3727
|
}
|
|
3759
3728
|
}), pi.registerCommand("parallel", {
|
|
3760
|
-
description:
|
|
3729
|
+
description: "Run prompt-defined children async in parallel; add --foreground to wait inline",
|
|
3761
3730
|
handler: async (args, ctx) => {
|
|
3762
|
-
let { args: cleanedArgs,
|
|
3731
|
+
let { args: cleanedArgs, async } = extractExecutionFlags(args), parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
|
|
3763
3732
|
if (!parsed)
|
|
3764
3733
|
return;
|
|
3765
3734
|
let tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
|
|
3766
3735
|
description: name,
|
|
3767
3736
|
permissions: config.permissions ?? "full",
|
|
3768
|
-
tier: config.tier,
|
|
3737
|
+
...config.tier ? { tier: config.tier } : {},
|
|
3738
|
+
...config.model ? { model: config.model } : {},
|
|
3739
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
3769
3740
|
task: stepTask ?? parsed.task,
|
|
3770
3741
|
...config.output !== void 0 ? { output: config.output } : {},
|
|
3771
3742
|
...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
|
|
@@ -3773,12 +3744,16 @@ ${finalTask}`);
|
|
|
3773
3744
|
...config.skill !== void 0 ? { skill: config.skill } : {},
|
|
3774
3745
|
...config.progress !== void 0 ? { progress: config.progress } : {}
|
|
3775
3746
|
}));
|
|
3776
|
-
if (tasks.some((task) => !task.tier)) {
|
|
3777
|
-
ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy].", "error");
|
|
3747
|
+
if (tasks.some((task) => !task.tier && !task.model)) {
|
|
3748
|
+
ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy] or [model=provider/id].", "error");
|
|
3749
|
+
return;
|
|
3750
|
+
}
|
|
3751
|
+
if (tasks.some((task) => task.tier && task.model)) {
|
|
3752
|
+
ctx.ui.notify("Each parallel child must choose exactly one of tier or model.", "error");
|
|
3778
3753
|
return;
|
|
3779
3754
|
}
|
|
3780
3755
|
let params = { tasks, clarify: !1 };
|
|
3781
|
-
|
|
3756
|
+
async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
|
|
3782
3757
|
}
|
|
3783
3758
|
}), pi.registerCommand("subagent-cost", {
|
|
3784
3759
|
description: "Show parent and subagent child usage cost for this session",
|
|
@@ -4402,28 +4377,506 @@ function registerSlashSubagentBridge(options) {
|
|
|
4402
4377
|
}
|
|
4403
4378
|
|
|
4404
4379
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
|
|
4380
|
+
import * as fs7 from "node:fs";
|
|
4381
|
+
import * as path8 from "node:path";
|
|
4382
|
+
import { Type as Type2 } from "typebox";
|
|
4383
|
+
|
|
4384
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js
|
|
4385
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
4405
4386
|
import * as fs6 from "node:fs";
|
|
4406
4387
|
import * as path7 from "node:path";
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4388
|
+
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();
|
|
4389
|
+
function askTimeoutMs() {
|
|
4390
|
+
let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
4391
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
|
|
4392
|
+
}
|
|
4393
|
+
function safeSegment(value) {
|
|
4394
|
+
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
4395
|
+
}
|
|
4396
|
+
function resolveSupervisorChannelDir(runId, agent, childIndex) {
|
|
4397
|
+
return path7.join(SUPERVISOR_CHANNEL_ROOT, `${safeSegment(runId)}-${safeSegment(agent)}-${childIndex}`);
|
|
4398
|
+
}
|
|
4399
|
+
function questionsDir(channelDir) {
|
|
4400
|
+
return path7.join(channelDir, QUESTIONS_DIR);
|
|
4401
|
+
}
|
|
4402
|
+
function questionFile(channelDir, questionId) {
|
|
4403
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.json`);
|
|
4404
|
+
}
|
|
4405
|
+
function deliveredFile(channelDir, questionId) {
|
|
4406
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.delivered.json`);
|
|
4407
|
+
}
|
|
4408
|
+
function terminalFile(channelDir, questionId) {
|
|
4409
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.terminal.json`);
|
|
4410
|
+
}
|
|
4411
|
+
function notifiedFile(channelDir, questionId) {
|
|
4412
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.notified.json`);
|
|
4413
|
+
}
|
|
4414
|
+
function lateFile(channelDir, questionId) {
|
|
4415
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.late.json`);
|
|
4416
|
+
}
|
|
4417
|
+
function writeExclusiveJson(filePath, payload) {
|
|
4418
|
+
fs6.mkdirSync(path7.dirname(filePath), { recursive: !0, mode: 448 });
|
|
4419
|
+
let temporary = `${filePath}.${randomUUID3()}.tmp`;
|
|
4420
|
+
try {
|
|
4421
|
+
return fs6.writeFileSync(temporary, JSON.stringify(payload), { encoding: "utf-8", flag: "wx", mode: 384 }), fs6.linkSync(temporary, filePath), !0;
|
|
4422
|
+
} catch (error) {
|
|
4423
|
+
if (error.code === "EEXIST")
|
|
4424
|
+
return !1;
|
|
4425
|
+
throw error;
|
|
4426
|
+
} finally {
|
|
4427
|
+
fs6.rmSync(temporary, { force: !0 });
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4430
|
+
function readJsonFile(filePath) {
|
|
4431
|
+
return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
|
|
4432
|
+
}
|
|
4433
|
+
function tryReadJson(filePath) {
|
|
4434
|
+
try {
|
|
4435
|
+
return readJsonFile(filePath);
|
|
4436
|
+
} catch (error) {
|
|
4437
|
+
return error.code === "ENOENT", void 0;
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
function parseIncarnation(raw) {
|
|
4441
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4442
|
+
return;
|
|
4443
|
+
let input = raw, pid = input.pid, readyAt = input.readyAt;
|
|
4444
|
+
if (!(!Number.isInteger(pid) || pid === void 0 || pid <= 0) && !(typeof readyAt != "number" || !Number.isFinite(readyAt) || readyAt <= 0))
|
|
4445
|
+
return { pid, readyAt };
|
|
4446
|
+
}
|
|
4447
|
+
function parseQuestionRecord(raw) {
|
|
4448
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4449
|
+
return;
|
|
4450
|
+
let input = raw;
|
|
4451
|
+
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())
|
|
4452
|
+
return;
|
|
4453
|
+
let childIndex = input.childIndex;
|
|
4454
|
+
if (!Number.isInteger(childIndex) || childIndex === void 0 || childIndex < 0 || typeof input.childId != "string" || !input.childId.trim() || typeof input.parentSessionId != "string" || !input.parentSessionId.trim())
|
|
4455
|
+
return;
|
|
4456
|
+
let parentGeneration = input.parentGeneration;
|
|
4457
|
+
if (!Number.isInteger(parentGeneration) || parentGeneration === void 0 || parentGeneration < 0)
|
|
4458
|
+
return;
|
|
4459
|
+
let incarnation = parseIncarnation(input.childIncarnation);
|
|
4460
|
+
return {
|
|
4461
|
+
type: "subagent.supervisor.question",
|
|
4462
|
+
id: input.id.trim(),
|
|
4463
|
+
createdAt: input.createdAt,
|
|
4464
|
+
expiresAt: input.expiresAt,
|
|
4465
|
+
reason: input.reason.trim(),
|
|
4466
|
+
message: input.message.trim(),
|
|
4467
|
+
runId: input.runId.trim(),
|
|
4468
|
+
childIndex,
|
|
4469
|
+
childId: input.childId.trim(),
|
|
4470
|
+
parentSessionId: input.parentSessionId.trim(),
|
|
4471
|
+
parentGeneration,
|
|
4472
|
+
...incarnation ? { childIncarnation: incarnation } : {}
|
|
4473
|
+
};
|
|
4474
|
+
}
|
|
4475
|
+
function parseDeliveredRecord(raw) {
|
|
4476
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4477
|
+
return;
|
|
4478
|
+
let input = raw;
|
|
4479
|
+
if (input.type !== "subagent.supervisor.delivered" || typeof input.id != "string" || !input.id.trim() || typeof input.at != "number" || !Number.isFinite(input.at))
|
|
4480
|
+
return;
|
|
4481
|
+
let incarnation = parseIncarnation(input);
|
|
4482
|
+
if (incarnation)
|
|
4483
|
+
return { type: "subagent.supervisor.delivered", id: input.id.trim(), at: input.at, pid: incarnation.pid, readyAt: incarnation.readyAt };
|
|
4484
|
+
}
|
|
4485
|
+
function parseTerminalRecord(raw) {
|
|
4486
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4487
|
+
return;
|
|
4488
|
+
let input = raw;
|
|
4489
|
+
if (!(input.type !== "subagent.supervisor.terminal" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at))) {
|
|
4490
|
+
if (input.state === "answered")
|
|
4491
|
+
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 };
|
|
4492
|
+
if (input.state === "expired")
|
|
4493
|
+
return { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "expired", at: input.at };
|
|
4494
|
+
if (input.state === "cancelled")
|
|
4495
|
+
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() };
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
function parseNotifiedRecord(raw) {
|
|
4499
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4500
|
+
return;
|
|
4501
|
+
let input = raw;
|
|
4502
|
+
if (!(input.type !== "subagent.supervisor.notified" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at)))
|
|
4503
|
+
return { type: "subagent.supervisor.notified", id: input.id.trim(), at: input.at };
|
|
4504
|
+
}
|
|
4505
|
+
function parseLateRecord(raw) {
|
|
4506
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4507
|
+
return;
|
|
4508
|
+
let input = raw;
|
|
4509
|
+
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"))
|
|
4510
|
+
return { type: "subagent.supervisor.late-answer", id: input.id.trim(), at: input.at, message: input.message };
|
|
4511
|
+
}
|
|
4512
|
+
function composeQuestion(record, channelDir) {
|
|
4513
|
+
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)));
|
|
4514
|
+
return {
|
|
4515
|
+
...record,
|
|
4516
|
+
...delivered ? { deliveredAt: delivered.at, childIncarnation: record.childIncarnation ?? { pid: delivered.pid, readyAt: delivered.readyAt } } : {},
|
|
4517
|
+
...terminal?.state === "answered" ? { answeredAt: terminal.at, answer: terminal.message } : {},
|
|
4518
|
+
...terminal?.state === "expired" ? { expiredAt: terminal.at } : {},
|
|
4519
|
+
...terminal?.state === "cancelled" ? { cancelledAt: terminal.at, cancelReason: terminal.reason } : {},
|
|
4520
|
+
...notified ? { notifiedAt: notified.at } : {},
|
|
4521
|
+
...late ? { lateAnswerAt: late.at, ...terminal?.state === "answered" ? {} : { answer: late.message } } : {}
|
|
4522
|
+
};
|
|
4523
|
+
}
|
|
4524
|
+
function assertMessageSize(value, label) {
|
|
4525
|
+
if (Buffer.byteLength(value, "utf-8") > MAX_MESSAGE_BYTES)
|
|
4526
|
+
throw new Error(`${label} is too large.`);
|
|
4527
|
+
}
|
|
4528
|
+
function ensureQuestionsDir(channelDir) {
|
|
4529
|
+
fs6.mkdirSync(questionsDir(channelDir), { recursive: !0, mode: 448 });
|
|
4530
|
+
}
|
|
4531
|
+
function supervisorQuestionPublicState(question) {
|
|
4532
|
+
return question.answeredAt ? "answered" : question.expiredAt ? "expired" : question.cancelledAt ? "cancelled" : "pending";
|
|
4533
|
+
}
|
|
4534
|
+
function supervisorQuestionIsTerminal(question) {
|
|
4535
|
+
return supervisorQuestionPublicState(question) !== "pending";
|
|
4536
|
+
}
|
|
4537
|
+
function readSupervisorQuestion(channelDir, questionId) {
|
|
4538
|
+
let record = parseQuestionRecord(tryReadJson(questionFile(channelDir, questionId)));
|
|
4539
|
+
if (!(!record || record.id !== questionId))
|
|
4540
|
+
return composeQuestion(record, channelDir);
|
|
4541
|
+
}
|
|
4542
|
+
function listSupervisorQuestions(channelDir) {
|
|
4543
|
+
let dir = questionsDir(channelDir), entries;
|
|
4544
|
+
try {
|
|
4545
|
+
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"));
|
|
4546
|
+
} catch (error) {
|
|
4547
|
+
if (error.code === "ENOENT")
|
|
4548
|
+
return [];
|
|
4549
|
+
throw error;
|
|
4550
|
+
}
|
|
4551
|
+
let questions = [];
|
|
4552
|
+
for (let entry of entries.sort()) {
|
|
4553
|
+
let record = parseQuestionRecord(tryReadJson(path7.join(dir, entry)));
|
|
4554
|
+
record && questions.push(composeQuestion(record, channelDir));
|
|
4555
|
+
}
|
|
4556
|
+
return questions;
|
|
4557
|
+
}
|
|
4558
|
+
function writeTerminal(channelDir, record) {
|
|
4559
|
+
return writeExclusiveJson(terminalFile(channelDir, record.id), record);
|
|
4560
|
+
}
|
|
4561
|
+
function createSupervisorQuestion(input) {
|
|
4562
|
+
let reason = input.reason.trim(), message = input.message.trim();
|
|
4563
|
+
if (!reason)
|
|
4564
|
+
throw new Error("reason is required and must name the concrete decision the child's answer will change.");
|
|
4565
|
+
if (!message)
|
|
4566
|
+
throw new Error("message is required for supervisor questions.");
|
|
4567
|
+
assertMessageSize(reason, "reason"), assertMessageSize(message, "message");
|
|
4568
|
+
let createdAt = input.now ?? Date.now(), timeoutMs = input.timeoutMs && input.timeoutMs > 0 ? input.timeoutMs : askTimeoutMs(), record = {
|
|
4569
|
+
type: "subagent.supervisor.question",
|
|
4570
|
+
id: input.id ?? randomUUID3(),
|
|
4571
|
+
createdAt,
|
|
4572
|
+
expiresAt: createdAt + timeoutMs,
|
|
4573
|
+
reason,
|
|
4574
|
+
message,
|
|
4575
|
+
runId: input.runId,
|
|
4576
|
+
childIndex: input.childIndex,
|
|
4577
|
+
childId: input.childId,
|
|
4578
|
+
parentSessionId: input.parentSessionId,
|
|
4579
|
+
parentGeneration: input.parentGeneration,
|
|
4580
|
+
...input.childIncarnation ? { childIncarnation: input.childIncarnation } : {}
|
|
4581
|
+
};
|
|
4582
|
+
if (assertMessageSize(JSON.stringify(record), "Supervisor question"), ensureQuestionsDir(input.channelDir), !writeExclusiveJson(questionFile(input.channelDir, record.id), record))
|
|
4583
|
+
throw new Error(`Supervisor question '${record.id}' already exists.`);
|
|
4584
|
+
return composeQuestion(record, input.channelDir);
|
|
4585
|
+
}
|
|
4586
|
+
function outstandingQuestionsForChild(channelDir, childIndex, childId) {
|
|
4587
|
+
return refreshSupervisorQuestionLifecycle(channelDir), listSupervisorQuestions(channelDir).filter((question) => !(supervisorQuestionIsTerminal(question) || question.childIndex !== childIndex || childId && question.childId !== childId));
|
|
4588
|
+
}
|
|
4589
|
+
function hasBlockingSupervisorRequest(channelDir, runId, childIndex) {
|
|
4590
|
+
let directory = path7.join(channelDir, "requests");
|
|
4591
|
+
if (!fs6.existsSync(directory))
|
|
4592
|
+
return !1;
|
|
4593
|
+
for (let name of fs6.readdirSync(directory)) {
|
|
4594
|
+
if (!name.endsWith(".json"))
|
|
4595
|
+
continue;
|
|
4596
|
+
let raw = tryReadJson(path7.join(directory, name));
|
|
4597
|
+
if (!raw || typeof raw != "object")
|
|
4598
|
+
continue;
|
|
4599
|
+
let request = raw;
|
|
4600
|
+
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`)))
|
|
4601
|
+
return !0;
|
|
4602
|
+
}
|
|
4603
|
+
return !1;
|
|
4604
|
+
}
|
|
4605
|
+
function hasOutstandingQuestionForChild(channelDir, childIndex, childId) {
|
|
4606
|
+
return outstandingQuestionsForChild(channelDir, childIndex, childId).length > 0;
|
|
4607
|
+
}
|
|
4608
|
+
function incarnationMatches(expected, actual) {
|
|
4609
|
+
return expected ? expected.pid === actual.pid && expected.readyAt === actual.readyAt : !0;
|
|
4610
|
+
}
|
|
4611
|
+
function expireSupervisorQuestion(channelDir, questionId, now = Date.now()) {
|
|
4612
|
+
let current = readSupervisorQuestion(channelDir, questionId);
|
|
4613
|
+
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));
|
|
4614
|
+
}
|
|
4615
|
+
function cancelSupervisorQuestion(channelDir, questionId, reason, now = Date.now()) {
|
|
4616
|
+
let current = readSupervisorQuestion(channelDir, questionId);
|
|
4617
|
+
return !current || supervisorQuestionIsTerminal(current) ? current : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "cancelled", at: now, reason: reason.trim() || "cancelled" }), readSupervisorQuestion(channelDir, questionId));
|
|
4618
|
+
}
|
|
4619
|
+
function refreshSupervisorQuestionLifecycle(channelDir, now = Date.now()) {
|
|
4620
|
+
let changed = [];
|
|
4621
|
+
for (let question of listSupervisorQuestions(channelDir))
|
|
4622
|
+
if (!supervisorQuestionIsTerminal(question) && now > question.expiresAt) {
|
|
4623
|
+
let expired = expireSupervisorQuestion(channelDir, question.id, now);
|
|
4624
|
+
expired && expired.expiredAt && changed.push(expired);
|
|
4625
|
+
}
|
|
4626
|
+
return changed;
|
|
4627
|
+
}
|
|
4628
|
+
function formatParentQuestionForChild(question) {
|
|
4629
|
+
return [
|
|
4630
|
+
`Parent question (${question.id})`,
|
|
4631
|
+
`Decision needed: ${question.reason}`,
|
|
4632
|
+
"",
|
|
4633
|
+
question.message,
|
|
4634
|
+
"",
|
|
4635
|
+
`Reply with: contact_supervisor({ action: "reply", replyTo: "${question.id}", message: "..." })`,
|
|
4636
|
+
"Answer from current findings and uncertainty. Do not silently start extra investigation. Continue the assigned task after replying."
|
|
4637
|
+
].join(`
|
|
4638
|
+
`);
|
|
4639
|
+
}
|
|
4640
|
+
function formatSupervisorQuestionAnswer(question) {
|
|
4641
|
+
let state = supervisorQuestionPublicState(question), lines = [
|
|
4642
|
+
state === "answered" ? `Subagent answer (${question.id})` : `Subagent question ${state} (${question.id})`,
|
|
4643
|
+
`Run: ${question.runId}`,
|
|
4644
|
+
`Child: ${question.childId} [#${question.childIndex}]`,
|
|
4645
|
+
`Decision: ${question.reason}`
|
|
4646
|
+
];
|
|
4647
|
+
return state === "answered" && question.answer?.trim() ? lines.push("", question.answer.trim()) : question.cancelReason && lines.push("", question.cancelReason), lines.join(`
|
|
4648
|
+
`);
|
|
4649
|
+
}
|
|
4650
|
+
function registerQuestionWait(questionIds) {
|
|
4651
|
+
let ids = [...questionIds];
|
|
4652
|
+
for (let id of ids)
|
|
4653
|
+
activeQuestionWaits.add(id);
|
|
4654
|
+
return () => {
|
|
4655
|
+
for (let id of ids)
|
|
4656
|
+
activeQuestionWaits.delete(id);
|
|
4657
|
+
};
|
|
4658
|
+
}
|
|
4659
|
+
function isQuestionWaitActive(questionId) {
|
|
4660
|
+
return activeQuestionWaits.has(questionId);
|
|
4661
|
+
}
|
|
4662
|
+
function claimQuestionNotification(channelDir, questionId, now = Date.now()) {
|
|
4663
|
+
return writeExclusiveJson(notifiedFile(channelDir, questionId), {
|
|
4664
|
+
type: "subagent.supervisor.notified",
|
|
4665
|
+
id: questionId,
|
|
4666
|
+
at: now
|
|
4667
|
+
});
|
|
4668
|
+
}
|
|
4669
|
+
function listSupervisorChannelDirs() {
|
|
4670
|
+
let entries;
|
|
4671
|
+
try {
|
|
4672
|
+
entries = fs6.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4673
|
+
} catch (error) {
|
|
4674
|
+
if (error.code === "ENOENT")
|
|
4675
|
+
return [];
|
|
4676
|
+
throw error;
|
|
4677
|
+
}
|
|
4678
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => path7.join(SUPERVISOR_CHANNEL_ROOT, entry.name));
|
|
4679
|
+
}
|
|
4680
|
+
function findSupervisorQuestion(questionId) {
|
|
4681
|
+
let id = questionId.trim();
|
|
4682
|
+
if (id)
|
|
4683
|
+
for (let channelDir of listSupervisorChannelDirs()) {
|
|
4684
|
+
let question = readSupervisorQuestion(channelDir, id);
|
|
4685
|
+
if (question)
|
|
4686
|
+
return { channelDir, question };
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
function cancelPendingSupervisorQuestionsForOwner(input) {
|
|
4690
|
+
if (!input.parentSessionId?.trim())
|
|
4691
|
+
return [];
|
|
4692
|
+
let now = input.now ?? Date.now(), cancelled = [];
|
|
4693
|
+
for (let channelDir of listSupervisorChannelDirs())
|
|
4694
|
+
for (let question of listSupervisorQuestions(channelDir)) {
|
|
4695
|
+
if (supervisorQuestionIsTerminal(question) || question.parentSessionId !== input.parentSessionId || input.parentGeneration !== void 0 && question.parentGeneration !== input.parentGeneration || input.runId && question.runId !== input.runId)
|
|
4696
|
+
continue;
|
|
4697
|
+
let next = cancelSupervisorQuestion(channelDir, question.id, input.reason, now);
|
|
4698
|
+
next?.cancelledAt && cancelled.push(next);
|
|
4699
|
+
}
|
|
4700
|
+
return cancelled;
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js
|
|
4704
|
+
function runningSteps(status) {
|
|
4705
|
+
return (status.steps ?? []).map((step, index) => ({
|
|
4706
|
+
index,
|
|
4707
|
+
childId: step.childId ?? step.agent,
|
|
4708
|
+
agent: step.agent,
|
|
4709
|
+
status: step.status
|
|
4710
|
+
})).filter((step) => step.status === "running").map((step) => ({ index: step.index, childId: step.childId, agent: step.agent }));
|
|
4711
|
+
}
|
|
4712
|
+
function resolveAskChild(status, params) {
|
|
4713
|
+
if (params.index !== void 0 && (!Number.isInteger(params.index) || params.index < 0))
|
|
4714
|
+
throw new Error("index must be a non-negative integer.");
|
|
4715
|
+
let running = runningSteps(status);
|
|
4716
|
+
if (params.childId?.trim()) {
|
|
4717
|
+
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);
|
|
4718
|
+
if (matches.length === 0)
|
|
4719
|
+
throw new Error(`Async run '${status.runId}' has no child '${childId}'.`);
|
|
4720
|
+
if (matches.length > 1)
|
|
4721
|
+
throw new Error(`Async run '${status.runId}' has multiple children named '${childId}'. Pass index.`);
|
|
4722
|
+
let match = matches[0];
|
|
4723
|
+
if (params.index !== void 0 && params.index !== match.index)
|
|
4724
|
+
throw new Error(`Child '${childId}' is index ${match.index}, not ${params.index}.`);
|
|
4725
|
+
if (match.status !== "running")
|
|
4726
|
+
throw new Error(`Async run '${status.runId}' child ${match.index} is ${match.status} and cannot be asked.`);
|
|
4727
|
+
return { index: match.index, childId: match.childId, agent: match.agent };
|
|
4728
|
+
}
|
|
4729
|
+
if (params.index !== void 0) {
|
|
4730
|
+
let steps = status.steps ?? [];
|
|
4731
|
+
if (params.index >= steps.length)
|
|
4732
|
+
throw new Error(`Async run '${status.runId}' has ${steps.length} children. Index ${params.index} is out of range.`);
|
|
4733
|
+
let step = steps[params.index];
|
|
4734
|
+
if (step.status !== "running")
|
|
4735
|
+
throw new Error(`Async run '${status.runId}' child ${params.index} is ${step.status} and cannot be asked.`);
|
|
4736
|
+
return { index: params.index, childId: step.childId ?? step.agent, agent: step.agent };
|
|
4737
|
+
}
|
|
4738
|
+
if (running.length === 1)
|
|
4739
|
+
return running[0];
|
|
4740
|
+
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.`);
|
|
4741
|
+
}
|
|
4742
|
+
function requestMatchesChild(request, runId, child) {
|
|
4743
|
+
if (!request.expectsReply || typeof request.runId != "string" || request.runId !== runId)
|
|
4744
|
+
return !1;
|
|
4745
|
+
let indexMatches = Number.isInteger(request.childIndex) && request.childIndex === child.index, idMatches = request.agent === child.childId || request.agent === child.agent || request.childTarget === child.childId;
|
|
4746
|
+
return indexMatches && idMatches;
|
|
4747
|
+
}
|
|
4748
|
+
function reconcileAsyncQuestion(channelDir, question, state) {
|
|
4749
|
+
expireSupervisorQuestion(channelDir, question.id);
|
|
4750
|
+
let current = readSupervisorQuestion(channelDir, question.id) ?? question;
|
|
4751
|
+
if (supervisorQuestionIsTerminal(current))
|
|
4752
|
+
return current;
|
|
4753
|
+
let resolved = resolveSubagentRunId(current.runId, { state });
|
|
4754
|
+
if (resolved?.kind !== "async" || !resolved.location.asyncDir)
|
|
4755
|
+
return current;
|
|
4756
|
+
let status = readStatus(resolved.location.asyncDir);
|
|
4757
|
+
if (!status)
|
|
4758
|
+
return current;
|
|
4759
|
+
let step = status.steps?.[current.childIndex], capability = readSteerCapability(resolved.location.asyncDir, current.childIndex), reason;
|
|
4760
|
+
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");
|
|
4761
|
+
let delivery = status.steering?.recent.find((request) => request.id === current.id)?.targets.find((target) => target.index === current.childIndex);
|
|
4762
|
+
return (delivery?.state === "failed" || delivery?.state === "late") && (reason = delivery.reason ?? "question delivery failed"), reason ? cancelSupervisorQuestion(channelDir, current.id, reason) ?? current : current;
|
|
4763
|
+
}
|
|
4764
|
+
function askRunningAsyncChild(input) {
|
|
4765
|
+
let runId = input.params.id?.trim();
|
|
4766
|
+
if (!runId)
|
|
4767
|
+
throw new Error("action='ask' requires id for the async run.");
|
|
4768
|
+
let reason = input.params.reason?.trim(), message = input.params.message?.trim();
|
|
4769
|
+
if (!reason)
|
|
4770
|
+
throw new Error("action='ask' requires reason naming the concrete decision the child's answer will change.");
|
|
4771
|
+
if (!message)
|
|
4772
|
+
throw new Error("action='ask' requires message.");
|
|
4773
|
+
let sessionId = input.state.currentSessionId;
|
|
4774
|
+
if (!sessionId)
|
|
4775
|
+
throw new Error("action='ask' requires an active session identity.");
|
|
4776
|
+
let resolved = resolveSubagentRunId(runId, { state: input.state });
|
|
4777
|
+
if (!resolved)
|
|
4778
|
+
throw new Error(`No async run matched '${runId}'.`);
|
|
4779
|
+
if (resolved.kind !== "async")
|
|
4780
|
+
throw new Error("action='ask' targets a running async child only. It does not revive, reassign, or broadcast.");
|
|
4781
|
+
if (!resolved.location.asyncDir)
|
|
4782
|
+
throw new Error(`Async run '${resolved.id}' has no live run directory.`);
|
|
4783
|
+
let status = readStatus(resolved.location.asyncDir);
|
|
4784
|
+
if (!status)
|
|
4785
|
+
throw new Error(`Async run '${resolved.id}' has no status.`);
|
|
4786
|
+
if (!status.sessionId || status.sessionId !== sessionId)
|
|
4787
|
+
throw new Error(`Async run '${resolved.id}' was not found in the active session.`);
|
|
4788
|
+
if (status.state !== "running")
|
|
4789
|
+
throw new Error(`Async run '${resolved.id}' is ${status.state} and cannot be asked.`);
|
|
4790
|
+
let child = resolveAskChild(status, input.params), channelDir = resolveSupervisorChannelDir(status.runId, child.childId, child.index);
|
|
4791
|
+
if (hasOutstandingQuestionForChild(channelDir, child.index, child.childId))
|
|
4792
|
+
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.`);
|
|
4793
|
+
for (let request of input.childRequests ?? [])
|
|
4794
|
+
if (requestMatchesChild(request, status.runId, child))
|
|
4795
|
+
throw new Error(`Child ${child.index} is waiting for a supervisor reply. Asking now would deadlock. Reply to the child request first.`);
|
|
4796
|
+
if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
|
|
4797
|
+
throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
|
|
4798
|
+
let capability = readSteerCapability(resolved.location.asyncDir, child.index);
|
|
4799
|
+
if (!capability?.supported)
|
|
4800
|
+
throw new Error("This child is not ready to receive questions. Continue other work until it is ready; no question was queued.");
|
|
4801
|
+
let incarnation = { pid: capability.pid, readyAt: capability.readyAt }, question = createSupervisorQuestion({
|
|
4802
|
+
channelDir,
|
|
4803
|
+
reason,
|
|
4804
|
+
message,
|
|
4805
|
+
runId: status.runId,
|
|
4806
|
+
childIndex: child.index,
|
|
4807
|
+
childId: child.childId,
|
|
4808
|
+
parentSessionId: sessionId,
|
|
4809
|
+
parentGeneration: input.state.sessionGeneration ?? 0,
|
|
4810
|
+
childIncarnation: incarnation
|
|
4811
|
+
});
|
|
4812
|
+
try {
|
|
4813
|
+
if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
|
|
4814
|
+
throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
|
|
4815
|
+
requestAsyncSteer(resolved.location.asyncDir, {
|
|
4816
|
+
message: formatParentQuestionForChild(question),
|
|
4817
|
+
targetIndex: child.index,
|
|
4818
|
+
source: "supervisor-question",
|
|
4819
|
+
id: question.id
|
|
4820
|
+
});
|
|
4821
|
+
} catch (error) {
|
|
4822
|
+
throw cancelSupervisorQuestion(channelDir, question.id, "failed to queue delivery to child"), error instanceof Error ? error : new Error(String(error));
|
|
4823
|
+
}
|
|
4824
|
+
return {
|
|
4825
|
+
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.` }],
|
|
4826
|
+
details: {
|
|
4827
|
+
questionId: question.id,
|
|
4828
|
+
state: "pending",
|
|
4829
|
+
runId: status.runId,
|
|
4830
|
+
childIndex: child.index,
|
|
4831
|
+
childId: child.childId,
|
|
4832
|
+
delivered: !1,
|
|
4833
|
+
answered: !1
|
|
4834
|
+
}
|
|
4835
|
+
};
|
|
4836
|
+
}
|
|
4837
|
+
|
|
4838
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
|
|
4839
|
+
var REQUESTS_DIR = "requests", REPLIES_DIR = "replies", NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor";
|
|
4840
|
+
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 = [
|
|
4841
|
+
"Ask a specific running async child, or reply to a child supervisor request.",
|
|
4842
|
+
"Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks a block or rework.",
|
|
4843
|
+
"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.",
|
|
4844
|
+
"Returns a question id immediately. The child's explicit answer is delivered separately from the final run result.",
|
|
4845
|
+
'Ask: { action: "ask", id: "<runId>", index?: 0, childId?: "...", reason: "<concrete decision>", message: "..." }',
|
|
4846
|
+
"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.",
|
|
4847
|
+
'Reply: { action: "reply", replyTo: "<requestId>", message: "..." }'
|
|
4848
|
+
].join(" "), CHILD_CONTACT_DESCRIPTION = [
|
|
4849
|
+
"Contact the parent/supervisor session for a blocking decision, structured interview, progress update, or reply to a parent question.",
|
|
4850
|
+
"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.",
|
|
4851
|
+
"Do not open a blocking need_decision while a parent question is already outstanding for this child."
|
|
4852
|
+
].join(" "), ContactSupervisorParamsSchema = Type2.Object({
|
|
4853
|
+
reason: Type2.Optional(Type2.String({ enum: ["need_decision", "interview_request", "progress_update"] })),
|
|
4410
4854
|
message: Type2.Optional(Type2.String()),
|
|
4411
|
-
interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 }))
|
|
4855
|
+
interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 })),
|
|
4856
|
+
action: Type2.Optional(Type2.String({ enum: ["reply"] })),
|
|
4857
|
+
replyTo: Type2.Optional(Type2.String())
|
|
4412
4858
|
}, { additionalProperties: !1 }), IntercomParamsSchema = Type2.Object({
|
|
4413
4859
|
action: Type2.String({ enum: ["list", "send", "ask", "reply", "pending", "status"] }),
|
|
4414
4860
|
to: Type2.Optional(Type2.String()),
|
|
4415
4861
|
message: Type2.Optional(Type2.String()),
|
|
4416
|
-
replyTo: Type2.Optional(Type2.String())
|
|
4862
|
+
replyTo: Type2.Optional(Type2.String()),
|
|
4863
|
+
id: Type2.Optional(Type2.String({ description: "Async run id for action='ask'." })),
|
|
4864
|
+
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." })),
|
|
4865
|
+
childId: Type2.Optional(Type2.String({ description: "Private child identity for action='ask'." })),
|
|
4866
|
+
reason: Type2.Optional(Type2.String({ description: "Concrete decision the child's answer will change. Required for action='ask'." }))
|
|
4867
|
+
}, { additionalProperties: !1 }), ParentSupervisorParamsSchema = Type2.Object({
|
|
4868
|
+
...IntercomParamsSchema.properties,
|
|
4869
|
+
action: Type2.String({ enum: ["list", "ask", "reply", "pending", "status"] })
|
|
4417
4870
|
}, { additionalProperties: !1 });
|
|
4418
|
-
function
|
|
4871
|
+
function safeSegment2(value) {
|
|
4419
4872
|
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
4420
4873
|
}
|
|
4421
4874
|
function replyPath(channelDir, requestId) {
|
|
4422
|
-
return
|
|
4875
|
+
return path8.join(channelDir, REPLIES_DIR, `${safeSegment2(requestId)}.json`);
|
|
4423
4876
|
}
|
|
4424
|
-
function
|
|
4877
|
+
function askTimeoutMs2() {
|
|
4425
4878
|
let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
4426
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed :
|
|
4879
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS2;
|
|
4427
4880
|
}
|
|
4428
4881
|
function hasTool(pi, name) {
|
|
4429
4882
|
try {
|
|
@@ -4434,16 +4887,26 @@ function hasTool(pi, name) {
|
|
|
4434
4887
|
}
|
|
4435
4888
|
function parseRequestFile(file, channelDir) {
|
|
4436
4889
|
try {
|
|
4437
|
-
let parsed = JSON.parse(
|
|
4438
|
-
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 };
|
|
4890
|
+
let parsed = JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
4891
|
+
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 };
|
|
4439
4892
|
} catch {
|
|
4440
4893
|
return;
|
|
4441
4894
|
}
|
|
4442
4895
|
}
|
|
4896
|
+
function hasPendingBlockingSupervisorRequest(runId, sessionId, now = Date.now()) {
|
|
4897
|
+
if (!runId)
|
|
4898
|
+
return !1;
|
|
4899
|
+
for (let { channelDir, file } of listRequestFiles()) {
|
|
4900
|
+
let request = parseRequestFile(file, channelDir);
|
|
4901
|
+
if (request?.expectsReply && request.runId === runId && !(sessionId && (request.supervisorSessionId ?? request.orchestratorSessionId) !== sessionId) && !fs7.existsSync(replyPath(request.channelDir, request.id)) && !(now > requestExpiresAt(request, now)))
|
|
4902
|
+
return !0;
|
|
4903
|
+
}
|
|
4904
|
+
return !1;
|
|
4905
|
+
}
|
|
4443
4906
|
function listRequestFiles() {
|
|
4444
4907
|
let channelEntries;
|
|
4445
4908
|
try {
|
|
4446
|
-
channelEntries =
|
|
4909
|
+
channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4447
4910
|
} catch (error) {
|
|
4448
4911
|
if (error.code === "ENOENT")
|
|
4449
4912
|
return [];
|
|
@@ -4453,34 +4916,34 @@ function listRequestFiles() {
|
|
|
4453
4916
|
for (let entry of channelEntries) {
|
|
4454
4917
|
if (!entry.isDirectory())
|
|
4455
4918
|
continue;
|
|
4456
|
-
let channelDir =
|
|
4919
|
+
let channelDir = path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), requestsDir = path8.join(channelDir, REQUESTS_DIR), requestEntries;
|
|
4457
4920
|
try {
|
|
4458
|
-
requestEntries =
|
|
4921
|
+
requestEntries = fs7.readdirSync(requestsDir, { withFileTypes: !0 });
|
|
4459
4922
|
} catch {
|
|
4460
4923
|
continue;
|
|
4461
4924
|
}
|
|
4462
4925
|
for (let requestEntry of requestEntries)
|
|
4463
|
-
requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file:
|
|
4926
|
+
requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file: path8.join(requestsDir, requestEntry.name) });
|
|
4464
4927
|
}
|
|
4465
4928
|
return files;
|
|
4466
4929
|
}
|
|
4467
4930
|
function readDirectoryEntries(dir) {
|
|
4468
4931
|
try {
|
|
4469
|
-
return
|
|
4932
|
+
return fs7.readdirSync(dir, { withFileTypes: !0 });
|
|
4470
4933
|
} catch (error) {
|
|
4471
4934
|
return error.code === "ENOENT" ? [] : void 0;
|
|
4472
4935
|
}
|
|
4473
4936
|
}
|
|
4474
4937
|
function directoryMtimeMs(dir) {
|
|
4475
4938
|
try {
|
|
4476
|
-
return
|
|
4939
|
+
return fs7.statSync(dir).mtimeMs;
|
|
4477
4940
|
} catch {
|
|
4478
4941
|
return 0;
|
|
4479
4942
|
}
|
|
4480
4943
|
}
|
|
4481
4944
|
function removeEmptyDirectory(dir) {
|
|
4482
4945
|
try {
|
|
4483
|
-
return
|
|
4946
|
+
return fs7.rmdirSync(dir), !0;
|
|
4484
4947
|
} catch (error) {
|
|
4485
4948
|
let code = error.code;
|
|
4486
4949
|
if (code === "ENOENT")
|
|
@@ -4491,19 +4954,22 @@ function removeEmptyDirectory(dir) {
|
|
|
4491
4954
|
}
|
|
4492
4955
|
}
|
|
4493
4956
|
function removeStaleEmptySupervisorChannel(channelDir, nowMs) {
|
|
4494
|
-
let requestsDir =
|
|
4957
|
+
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));
|
|
4495
4958
|
if (nowMs - newestKnownMtimeMs < STALE_EMPTY_CHANNEL_AGE_MS)
|
|
4496
4959
|
return !1;
|
|
4497
4960
|
let requestEntries = readDirectoryEntries(requestsDir);
|
|
4498
4961
|
if (!requestEntries || requestEntries.length > 0)
|
|
4499
4962
|
return !1;
|
|
4500
4963
|
let replyEntries = readDirectoryEntries(repliesDir);
|
|
4501
|
-
|
|
4964
|
+
if (!replyEntries || replyEntries.length > 0)
|
|
4965
|
+
return !1;
|
|
4966
|
+
let questionEntries = readDirectoryEntries(questionsPath);
|
|
4967
|
+
return !(!questionEntries || questionEntries.length > 0 || !removeEmptyDirectory(requestsDir) || !removeEmptyDirectory(repliesDir) || !removeEmptyDirectory(questionsPath) || !removeEmptyDirectory(channelDir));
|
|
4502
4968
|
}
|
|
4503
4969
|
function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
|
|
4504
4970
|
let channelEntries;
|
|
4505
4971
|
try {
|
|
4506
|
-
channelEntries =
|
|
4972
|
+
channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4507
4973
|
} catch (error) {
|
|
4508
4974
|
if (error.code === "ENOENT")
|
|
4509
4975
|
return 0;
|
|
@@ -4513,7 +4979,7 @@ function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
|
|
|
4513
4979
|
for (let entry of channelEntries)
|
|
4514
4980
|
if (entry.isDirectory())
|
|
4515
4981
|
try {
|
|
4516
|
-
removeStaleEmptySupervisorChannel(
|
|
4982
|
+
removeStaleEmptySupervisorChannel(path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), nowMs) && removed++;
|
|
4517
4983
|
} catch {
|
|
4518
4984
|
}
|
|
4519
4985
|
return removed;
|
|
@@ -4527,19 +4993,26 @@ function currentContextSessionId(state, ctx) {
|
|
|
4527
4993
|
}
|
|
4528
4994
|
return state.currentSessionId ?? void 0;
|
|
4529
4995
|
}
|
|
4996
|
+
function currentSupervisorSessionId(state, ctx) {
|
|
4997
|
+
try {
|
|
4998
|
+
return ctx.sessionManager.getSessionFile?.() ?? currentContextSessionId(state, ctx);
|
|
4999
|
+
} catch {
|
|
5000
|
+
return state.currentSessionId ?? void 0;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
4530
5003
|
function requestMatchesContext(request, state, ctx) {
|
|
4531
|
-
let currentSessionId = currentContextSessionId(state, ctx);
|
|
4532
|
-
return !!(currentSessionId && request.orchestratorSessionId === currentSessionId);
|
|
5004
|
+
let currentSessionId = request.supervisorSessionId !== void 0 ? currentSupervisorSessionId(state, ctx) : currentContextSessionId(state, ctx);
|
|
5005
|
+
return !!(currentSessionId && (request.supervisorSessionId ?? request.orchestratorSessionId) === currentSessionId);
|
|
4533
5006
|
}
|
|
4534
5007
|
function removeRequestFile(file) {
|
|
4535
5008
|
try {
|
|
4536
|
-
|
|
5009
|
+
fs7.rmSync(file, { force: !0 });
|
|
4537
5010
|
} catch {
|
|
4538
5011
|
}
|
|
4539
5012
|
}
|
|
4540
5013
|
function requestExpiresAt(request, now) {
|
|
4541
5014
|
let expiresAt = request.expiresAt;
|
|
4542
|
-
return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt +
|
|
5015
|
+
return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt + askTimeoutMs2() : now;
|
|
4543
5016
|
}
|
|
4544
5017
|
function requestRunInactive(request, state) {
|
|
4545
5018
|
if (state.foregroundControls.has(request.runId))
|
|
@@ -4556,7 +5029,7 @@ function requestRunInactive(request, state) {
|
|
|
4556
5029
|
return stepStatus === "complete" || stepStatus === "completed" || stepStatus === "failed" || stepStatus === "paused";
|
|
4557
5030
|
}
|
|
4558
5031
|
function requestLifecycle(request, state, ctx, now) {
|
|
4559
|
-
return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" :
|
|
5032
|
+
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";
|
|
4560
5033
|
}
|
|
4561
5034
|
function cleanupRequestLifecycle(request, lifecycle) {
|
|
4562
5035
|
(lifecycle === "resolved" || lifecycle === "expired" || lifecycle === "inactive") && removeRequestFile(request.requestFile);
|
|
@@ -4621,8 +5094,8 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
|
|
|
4621
5094
|
return {
|
|
4622
5095
|
name,
|
|
4623
5096
|
label: name === "intercom" ? "Intercom" : "Subagent Supervisor",
|
|
4624
|
-
description:
|
|
4625
|
-
parameters:
|
|
5097
|
+
description: PARENT_ASK_DESCRIPTION,
|
|
5098
|
+
parameters: ParentSupervisorParamsSchema,
|
|
4626
5099
|
async execute(_id, params) {
|
|
4627
5100
|
refreshPendingRequests(pending, state, state.lastUiContext ?? void 0);
|
|
4628
5101
|
let input = params;
|
|
@@ -4637,10 +5110,59 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
|
|
|
4637
5110
|
let request = resolvePendingRequest(pending, input);
|
|
4638
5111
|
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 } };
|
|
4639
5112
|
}
|
|
4640
|
-
|
|
5113
|
+
if (input.action === "ask")
|
|
5114
|
+
return askRunningAsyncChild({
|
|
5115
|
+
params: {
|
|
5116
|
+
id: input.id,
|
|
5117
|
+
index: input.index,
|
|
5118
|
+
childId: input.childId,
|
|
5119
|
+
reason: input.reason,
|
|
5120
|
+
message: input.message
|
|
5121
|
+
},
|
|
5122
|
+
state,
|
|
5123
|
+
childRequests: pending.values()
|
|
5124
|
+
});
|
|
5125
|
+
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}`);
|
|
4641
5126
|
}
|
|
4642
5127
|
};
|
|
4643
5128
|
}
|
|
5129
|
+
function questionMatchesParentContext(question, state, ctx) {
|
|
5130
|
+
let sessionId = currentSupervisorSessionId(state, ctx);
|
|
5131
|
+
return !(!sessionId || question.parentSessionId !== sessionId || (state.sessionGeneration ?? 0) !== question.parentGeneration);
|
|
5132
|
+
}
|
|
5133
|
+
function notifyQuestionTerminal(pi, question, channelDir) {
|
|
5134
|
+
if (!supervisorQuestionIsTerminal(question) || question.notifiedAt || (pi.events?.emit(SUBAGENT_QUESTION_EVENT, {
|
|
5135
|
+
questionId: question.id,
|
|
5136
|
+
state: supervisorQuestionPublicState(question),
|
|
5137
|
+
runId: question.runId
|
|
5138
|
+
}), isQuestionWaitActive(question.id)) || !claimQuestionNotification(channelDir, question.id))
|
|
5139
|
+
return;
|
|
5140
|
+
let state = supervisorQuestionPublicState(question);
|
|
5141
|
+
pi.sendMessage({
|
|
5142
|
+
customType: SUBAGENT_SUPERVISOR_ANSWER_TYPE,
|
|
5143
|
+
content: formatSupervisorQuestionAnswer(question),
|
|
5144
|
+
display: state === "answered",
|
|
5145
|
+
details: {
|
|
5146
|
+
questionId: question.id,
|
|
5147
|
+
state,
|
|
5148
|
+
runId: question.runId,
|
|
5149
|
+
childIndex: question.childIndex,
|
|
5150
|
+
childId: question.childId,
|
|
5151
|
+
reason: question.reason,
|
|
5152
|
+
answered: state === "answered",
|
|
5153
|
+
delivered: !!question.deliveredAt
|
|
5154
|
+
}
|
|
5155
|
+
}, { triggerTurn: state === "answered" });
|
|
5156
|
+
}
|
|
5157
|
+
function pollSupervisorQuestions(pi, state, ctx) {
|
|
5158
|
+
for (let channelDir of listSupervisorChannelDirs())
|
|
5159
|
+
for (let question of listSupervisorQuestions(channelDir)) {
|
|
5160
|
+
if (!questionMatchesParentContext(question, state, ctx))
|
|
5161
|
+
continue;
|
|
5162
|
+
let current = reconcileAsyncQuestion(channelDir, question, state);
|
|
5163
|
+
notifyQuestionTerminal(pi, current, channelDir);
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
4644
5166
|
function createNativeSupervisorChannel(pi, state) {
|
|
4645
5167
|
let pending = /* @__PURE__ */ new Map(), seenFiles = /* @__PURE__ */ new Set(), poller, lastStaleCleanupAt = 0, registerParentTools = () => {
|
|
4646
5168
|
hasTool(pi, NATIVE_SUPERVISOR_TOOL_NAME) || pi.registerTool(buildParentIntercomTool(pending, state, NATIVE_SUPERVISOR_TOOL_NAME)), hasTool(pi, "intercom") || pi.registerTool(buildParentIntercomTool(pending, state));
|
|
@@ -4653,12 +5175,24 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4653
5175
|
} catch {
|
|
4654
5176
|
}
|
|
4655
5177
|
}
|
|
5178
|
+
}, cancelOwnedQuestions = (reason) => {
|
|
5179
|
+
state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
|
|
5180
|
+
parentSessionId: state.currentSessionId,
|
|
5181
|
+
parentGeneration: state.sessionGeneration ?? 0,
|
|
5182
|
+
reason
|
|
5183
|
+
});
|
|
5184
|
+
}, settleRunQuestions = (runId, reason) => {
|
|
5185
|
+
state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
|
|
5186
|
+
parentSessionId: state.currentSessionId,
|
|
5187
|
+
runId,
|
|
5188
|
+
reason
|
|
5189
|
+
});
|
|
4656
5190
|
}, poll = () => {
|
|
4657
5191
|
cleanupStaleChannelsIfDue();
|
|
4658
5192
|
let ctx = state.lastUiContext;
|
|
4659
5193
|
if (!ctx)
|
|
4660
5194
|
return;
|
|
4661
|
-
refreshPendingRequests(pending, state, ctx);
|
|
5195
|
+
refreshPendingRequests(pending, state, ctx), pollSupervisorQuestions(pi, state, ctx);
|
|
4662
5196
|
let now = Date.now();
|
|
4663
5197
|
for (let { channelDir, file } of listRequestFiles()) {
|
|
4664
5198
|
if (seenFiles.has(file))
|
|
@@ -4671,7 +5205,7 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4671
5205
|
seenFiles.add(file), cleanupRequestLifecycle(request, lifecycle);
|
|
4672
5206
|
continue;
|
|
4673
5207
|
}
|
|
4674
|
-
seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
|
|
5208
|
+
if (seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
|
|
4675
5209
|
customType: "subagent_supervisor_request",
|
|
4676
5210
|
content: requestVisibleText(request),
|
|
4677
5211
|
display: !0,
|
|
@@ -4683,12 +5217,23 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4683
5217
|
agent: request.agent,
|
|
4684
5218
|
childIndex: request.childIndex
|
|
4685
5219
|
}
|
|
4686
|
-
}), request.expectsReply
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
5220
|
+
}), request.expectsReply) {
|
|
5221
|
+
let job = state.asyncJobs.get(request.runId);
|
|
5222
|
+
job && (job.activityState = "needs_attention");
|
|
5223
|
+
let events = pi.events;
|
|
5224
|
+
events?.emit(INTERCOM_DETACH_REQUEST_EVENT, {
|
|
5225
|
+
requestId: request.id,
|
|
5226
|
+
runId: request.runId,
|
|
5227
|
+
agent: request.agent,
|
|
5228
|
+
childIndex: request.childIndex
|
|
5229
|
+
}), events?.emit(SUBAGENT_CONTROL_EVENT, {
|
|
5230
|
+
type: "needs_attention",
|
|
5231
|
+
to: "needs_attention",
|
|
5232
|
+
runId: request.runId,
|
|
5233
|
+
agent: request.agent,
|
|
5234
|
+
index: request.childIndex
|
|
5235
|
+
});
|
|
5236
|
+
}
|
|
4692
5237
|
}
|
|
4693
5238
|
};
|
|
4694
5239
|
return {
|
|
@@ -4696,14 +5241,16 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4696
5241
|
poller || (registerParentTools(), poll(), poller = setInterval(poll, CHANNEL_POLL_MS), poller.unref?.());
|
|
4697
5242
|
},
|
|
4698
5243
|
dispose: () => {
|
|
4699
|
-
poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
|
|
5244
|
+
cancelOwnedQuestions("parent supervisor channel disposed"), poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
|
|
4700
5245
|
},
|
|
4701
|
-
pending
|
|
5246
|
+
pending,
|
|
5247
|
+
cancelOwnedQuestions,
|
|
5248
|
+
settleRunQuestions
|
|
4702
5249
|
};
|
|
4703
5250
|
}
|
|
4704
5251
|
|
|
4705
5252
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/rpc.js
|
|
4706
|
-
import * as
|
|
5253
|
+
import * as path9 from "node:path";
|
|
4707
5254
|
import { Compile } from "typebox/compile";
|
|
4708
5255
|
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 {
|
|
4709
5256
|
code;
|
|
@@ -4810,7 +5357,7 @@ function stopAsyncRun(params, options, ctx) {
|
|
|
4810
5357
|
}
|
|
4811
5358
|
if (!location.asyncDir)
|
|
4812
5359
|
throw new SubagentRpcError("not_found", "Async run not found or already completed; stop requires a live async run directory.");
|
|
4813
|
-
let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ??
|
|
5360
|
+
let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ?? path9.basename(location.asyncDir);
|
|
4814
5361
|
if (!initialStatus)
|
|
4815
5362
|
throw new SubagentRpcError("not_found", `Status file not found for async run '${initialRunId}'.`);
|
|
4816
5363
|
if (!currentSessionId || initialStatus.sessionId !== currentSessionId)
|
|
@@ -5042,7 +5589,9 @@ var ACTIVE_STATES = ["queued", "running"], DEFAULT_TIMEOUT_MS = 1800 * 1e3, MIN_
|
|
|
5042
5589
|
SUBAGENT_FOREGROUND_COMPLETE_EVENT,
|
|
5043
5590
|
SUBAGENT_CONTROL_EVENT,
|
|
5044
5591
|
SUBAGENT_CONTROL_INTERCOM_EVENT,
|
|
5045
|
-
SUBAGENT_RESULT_INTERCOM_EVENT
|
|
5592
|
+
SUBAGENT_RESULT_INTERCOM_EVENT,
|
|
5593
|
+
INTERCOM_DETACH_REQUEST_EVENT,
|
|
5594
|
+
SUBAGENT_QUESTION_EVENT
|
|
5046
5595
|
];
|
|
5047
5596
|
function defaultSleep(ms, signal) {
|
|
5048
5597
|
return new Promise((resolve3) => {
|
|
@@ -5111,8 +5660,8 @@ function summarizeForegroundChildren(run, indices) {
|
|
|
5111
5660
|
!indices.has(child.index) || child.status === "detached" || counts.set(child.status, (counts.get(child.status) ?? 0) + 1);
|
|
5112
5661
|
return [...counts.entries()].map(([status, count]) => `${count} ${status}`).join(", ");
|
|
5113
5662
|
}
|
|
5114
|
-
function needsAttention(run) {
|
|
5115
|
-
return run.activityState === "needs_attention";
|
|
5663
|
+
function needsAttention(run, sessionId) {
|
|
5664
|
+
return run.activityState === "needs_attention" || hasPendingBlockingSupervisorRequest(run.id, sessionId);
|
|
5116
5665
|
}
|
|
5117
5666
|
function backgroundWorkIdentity(item) {
|
|
5118
5667
|
return `${item.provider}\0${item.sessionId}\0${item.id}`;
|
|
@@ -5134,7 +5683,7 @@ function activeRunsForSession(params, deps) {
|
|
|
5134
5683
|
return params.id ? runs.filter((run) => matchesId(run, params.id)) : runs;
|
|
5135
5684
|
}
|
|
5136
5685
|
function attentionRunsForSession(params, deps, initialIds) {
|
|
5137
|
-
return activeRunsForSession(params, deps).filter((run) => needsAttention(run) && initialIds.has(run.id));
|
|
5686
|
+
return activeRunsForSession(params, deps).filter((run) => needsAttention(run, deps.state.currentSessionId) && initialIds.has(run.id));
|
|
5138
5687
|
}
|
|
5139
5688
|
function allRunsForSession(params, deps) {
|
|
5140
5689
|
let asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR, resultsDir = deps.resultsDir ?? RESULTS_DIR, runs = listAsyncRuns(asyncDirRoot, {
|
|
@@ -5180,12 +5729,57 @@ async function waitForDetachedForegroundRun(run, signal, deps, startedAt, now, p
|
|
|
5180
5729
|
await waitForWake(pollIntervalMs, signal, deps);
|
|
5181
5730
|
}
|
|
5182
5731
|
}
|
|
5732
|
+
async function waitForSupervisorQuestion(questionId, signal, deps, startedAt, now, pollIntervalMs, timeoutMs) {
|
|
5733
|
+
let sessionId = deps.state.currentSessionId;
|
|
5734
|
+
if (!sessionId)
|
|
5735
|
+
return result("subagent_wait requires an active session identity to scope background work safely.", !0);
|
|
5736
|
+
let releaseWait = registerQuestionWait([questionId]);
|
|
5737
|
+
try {
|
|
5738
|
+
for (; ; ) {
|
|
5739
|
+
let found = findSupervisorQuestion(questionId);
|
|
5740
|
+
if (!found)
|
|
5741
|
+
return result(`No supervisor question matched "${questionId}".`, !0);
|
|
5742
|
+
let { channelDir } = found, { question } = found;
|
|
5743
|
+
if (question.parentSessionId !== sessionId || deps.state.currentSessionId !== sessionId)
|
|
5744
|
+
return result(`Supervisor question "${questionId}" belongs to another session.`, !0);
|
|
5745
|
+
if ((deps.state.sessionGeneration ?? 0) !== question.parentGeneration)
|
|
5746
|
+
return result(`Supervisor question "${questionId}" was cancelled by a session replacement.`, !0);
|
|
5747
|
+
if (question = reconcileAsyncQuestion(channelDir, question, deps.state), supervisorQuestionIsTerminal(question)) {
|
|
5748
|
+
claimQuestionNotification(channelDir, question.id);
|
|
5749
|
+
let state = supervisorQuestionPublicState(question);
|
|
5750
|
+
return {
|
|
5751
|
+
content: [{ type: "text", text: formatSupervisorQuestionAnswer(question) }],
|
|
5752
|
+
...state === "answered" ? {} : { isError: !0 },
|
|
5753
|
+
details: {
|
|
5754
|
+
mode: "management",
|
|
5755
|
+
results: [],
|
|
5756
|
+
questionId: question.id,
|
|
5757
|
+
state,
|
|
5758
|
+
runId: question.runId,
|
|
5759
|
+
answered: state === "answered",
|
|
5760
|
+
delivered: !!question.deliveredAt
|
|
5761
|
+
}
|
|
5762
|
+
};
|
|
5763
|
+
}
|
|
5764
|
+
if (signal?.aborted)
|
|
5765
|
+
return result(`Wait aborted after ${formatDuration(now() - startedAt)} for supervisor question "${questionId}".`, !0);
|
|
5766
|
+
if (now() - startedAt >= timeoutMs)
|
|
5767
|
+
return result(`Wait timed out after ${formatDuration(timeoutMs)} for supervisor question "${questionId}". The question remains pending until answered, expired, or cancelled.`, !0);
|
|
5768
|
+
await waitForWake(pollIntervalMs, signal, deps);
|
|
5769
|
+
}
|
|
5770
|
+
} finally {
|
|
5771
|
+
releaseWait();
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5183
5774
|
async function waitForSubagents(params, signal, deps) {
|
|
5184
5775
|
if (deps.enabled === !1)
|
|
5185
5776
|
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.');
|
|
5186
5777
|
if (!deps.state.currentSessionId)
|
|
5187
5778
|
return result("subagent_wait requires an active session identity to scope background work safely.", !0);
|
|
5188
|
-
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()
|
|
5779
|
+
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();
|
|
5780
|
+
if (params.questionId?.trim())
|
|
5781
|
+
return waitForSupervisorQuestion(params.questionId.trim(), signal, deps, startedAt, now, pollIntervalMs, timeoutMs);
|
|
5782
|
+
let waitForAll = params.id ? !0 : params.all === !0, active, foreground, providerSnapshot;
|
|
5189
5783
|
try {
|
|
5190
5784
|
active = activeRunsForSession(params, deps), foreground = activeDetachedForegroundRuns(params, deps), providerSnapshot = params.id ? { providers: [], items: [] } : backgroundWorkForSession(deps, startedAt);
|
|
5191
5785
|
} catch (error) {
|
|
@@ -5206,7 +5800,7 @@ async function waitForSubagents(params, signal, deps) {
|
|
|
5206
5800
|
let providerActive = providerSnapshot.items;
|
|
5207
5801
|
if (active.length === 0 && providerActive.length === 0)
|
|
5208
5802
|
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.");
|
|
5209
|
-
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 = () => {
|
|
5803
|
+
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 = () => {
|
|
5210
5804
|
if (stopOnAttention && attention.some((run) => initialAsyncIds.has(run.id)))
|
|
5211
5805
|
return !0;
|
|
5212
5806
|
let activeAsyncIds = new Set(active.map((run) => run.id)), activeProviderIds2 = new Set(providerActive.map(backgroundWorkIdentity));
|
|
@@ -5254,11 +5848,12 @@ function registerWaitTool(pi, state, enabled = resolveWaitToolConfig().enabled,
|
|
|
5254
5848
|
label: "Subagent Wait",
|
|
5255
5849
|
description: `Block until background work owned by this session changes, then return.
|
|
5256
5850
|
|
|
5257
|
-
|
|
5851
|
+
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.
|
|
5258
5852
|
|
|
5259
5853
|
\u2022 { } \u2014 return when the first initially active async run or registered provider item finishes, or when a subagent needs attention.
|
|
5260
5854
|
\u2022 { all: true } \u2014 wait for every async run and provider item that was active when the call began.
|
|
5261
5855
|
\u2022 { id: "..." } \u2014 wait for one async or remembered detached foreground subagent run (id or prefix).
|
|
5856
|
+
\u2022 { questionId: "..." } \u2014 wait for one supervisor question from subagent_supervisor action='ask' (answered/expired/cancelled). Separate from final run results.
|
|
5262
5857
|
\u2022 { timeoutMs: 600000 } \u2014 stop waiting after N ms; active work keeps running.
|
|
5263
5858
|
|
|
5264
5859
|
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 ? "" : `
|
|
@@ -5393,7 +5988,7 @@ function formatSingleCompletion(details) {
|
|
|
5393
5988
|
}
|
|
5394
5989
|
function parseSubagentNotifyContent(content) {
|
|
5395
5990
|
let lines = content.split(`
|
|
5396
|
-
`), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
5991
|
+
`), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
5397
5992
|
if (!match)
|
|
5398
5993
|
return;
|
|
5399
5994
|
let body = lines.slice(2), sessionIndex = -1;
|
|
@@ -5437,7 +6032,7 @@ function sendCompletion(pi, details) {
|
|
|
5437
6032
|
customType: "subagent-notify",
|
|
5438
6033
|
content,
|
|
5439
6034
|
display: !0
|
|
5440
|
-
}, { triggerTurn:
|
|
6035
|
+
}, { triggerTurn: details.some((detail) => detail.status !== "stopped") });
|
|
5441
6036
|
}
|
|
5442
6037
|
function completionBatchKey(result2) {
|
|
5443
6038
|
let sessionId = typeof result2.sessionId == "string" ? result2.sessionId.trim() : "";
|
|
@@ -5447,13 +6042,38 @@ function completionBatchKey(result2) {
|
|
|
5447
6042
|
return cwd ? `cwd:${cwd}` : "unknown";
|
|
5448
6043
|
}
|
|
5449
6044
|
function buildCompletionDetails(result2) {
|
|
5450
|
-
let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "",
|
|
6045
|
+
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;
|
|
5451
6046
|
return {
|
|
5452
6047
|
agent,
|
|
5453
6048
|
status,
|
|
5454
6049
|
...result2.source ? { source: result2.source } : {},
|
|
5455
6050
|
...taskInfo ? { taskInfo } : {},
|
|
5456
|
-
resultPreview:
|
|
6051
|
+
resultPreview: [
|
|
6052
|
+
result2.id ? `Run: ${result2.id}` : void 0,
|
|
6053
|
+
result2.asyncDir ? `Async dir: ${result2.asyncDir}` : void 0,
|
|
6054
|
+
result2.results?.length ? result2.results.map((child, index) => {
|
|
6055
|
+
let output = child.summary ?? (child.error ? `${child.error}
|
|
6056
|
+
|
|
6057
|
+
Output:
|
|
6058
|
+
${child.output}` : child.output), report = child.acceptance?.childReport, childIndex = child.index ?? index;
|
|
6059
|
+
return [
|
|
6060
|
+
`${childIndex + 1}. ${child.agent} [${child.status ?? resolveSubagentResultStatus({ success: child.success, state: result2.state })}]`,
|
|
6061
|
+
child.artifactPath ? `Output artifact: ${child.artifactPath}` : void 0,
|
|
6062
|
+
child.sessionPath ? `Session: ${child.sessionPath}` : void 0,
|
|
6063
|
+
child.sessionPath && result2.id && result2.source !== "foreground" ? `Revive: subagent({ action: "resume", id: ${JSON.stringify(result2.id)}, index: ${childIndex}, message: "..." })` : void 0,
|
|
6064
|
+
...formatNestedResultLines(child.children),
|
|
6065
|
+
output,
|
|
6066
|
+
report ? `\`\`\`acceptance-report
|
|
6067
|
+
${JSON.stringify(report, null, 2)}
|
|
6068
|
+
\`\`\`` : void 0
|
|
6069
|
+
].filter((line) => line !== void 0).join(`
|
|
6070
|
+
`);
|
|
6071
|
+
}).join(`
|
|
6072
|
+
|
|
6073
|
+
`) : summary
|
|
6074
|
+
].filter((line) => line !== void 0).join(`
|
|
6075
|
+
|
|
6076
|
+
`),
|
|
5457
6077
|
...typeof result2.durationMs == "number" ? { durationMs: result2.durationMs } : {},
|
|
5458
6078
|
...session ? { sessionLabel: session.label, sessionValue: session.value } : {}
|
|
5459
6079
|
};
|
|
@@ -5536,15 +6156,15 @@ function handleSubagentSteeringNotice(input) {
|
|
|
5536
6156
|
}
|
|
5537
6157
|
|
|
5538
6158
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/config.js
|
|
5539
|
-
import * as
|
|
5540
|
-
import * as
|
|
6159
|
+
import * as fs8 from "node:fs";
|
|
6160
|
+
import * as path10 from "node:path";
|
|
5541
6161
|
function getConfigPath() {
|
|
5542
|
-
return
|
|
6162
|
+
return path10.join(getAgentDir(), "extensions", "subagent", "config.json");
|
|
5543
6163
|
}
|
|
5544
6164
|
function readConfigForUpdate(configPath = getConfigPath()) {
|
|
5545
|
-
if (!
|
|
6165
|
+
if (!fs8.existsSync(configPath))
|
|
5546
6166
|
return {};
|
|
5547
|
-
let parsed = JSON.parse(
|
|
6167
|
+
let parsed = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
|
|
5548
6168
|
if (!parsed || typeof parsed != "object" || Array.isArray(parsed))
|
|
5549
6169
|
throw new Error(`Subagent config at '${configPath}' must be a JSON object`);
|
|
5550
6170
|
return parsed;
|
|
@@ -5560,25 +6180,31 @@ function loadConfig() {
|
|
|
5560
6180
|
}
|
|
5561
6181
|
|
|
5562
6182
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js
|
|
5563
|
-
import * as
|
|
5564
|
-
import * as
|
|
5565
|
-
var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024,
|
|
6183
|
+
import * as fs9 from "node:fs";
|
|
6184
|
+
import * as path11 from "node:path";
|
|
6185
|
+
var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024, SUBAGENT_TIER_GUIDANCE = `TIER SELECTION:
|
|
6186
|
+
\u2022 Choose the lowest tier that can reliably complete the task.
|
|
6187
|
+
\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.
|
|
6188
|
+
\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.
|
|
6189
|
+
\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:
|
|
5566
6190
|
\u2022 Prompt children directly. There is no agent roster. Every execution task needs task (the full child prompt) and description (a concise UI label).
|
|
5567
|
-
\u2022 Every executable child
|
|
6191
|
+
\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.
|
|
6192
|
+
\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.
|
|
6193
|
+
${SUBAGENT_TIER_GUIDANCE}
|
|
5568
6194
|
\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.
|
|
5569
|
-
\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
|
|
5570
|
-
\u2022
|
|
6195
|
+
\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*.
|
|
6196
|
+
\u2022 ${SUBAGENT_ASYNC_GUIDANCE}
|
|
5571
6197
|
\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.
|
|
5572
6198
|
\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.
|
|
5573
6199
|
\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.
|
|
5574
6200
|
|
|
5575
6201
|
EXECUTION (use exactly ONE mode):
|
|
5576
|
-
\u2022 SINGLE: { task, description, tier, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
|
|
5577
|
-
\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)
|
|
5578
|
-
\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.
|
|
6202
|
+
\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.
|
|
6203
|
+
\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)
|
|
6204
|
+
\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.
|
|
5579
6205
|
\u2022 description is required, single-line, max 80 characters. It is UI metadata only. task is the complete child prompt.
|
|
5580
6206
|
\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".
|
|
5581
|
-
\u2022
|
|
6207
|
+
\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.
|
|
5582
6208
|
\u2022 Children always start with a fresh session (no inherited parent transcript).
|
|
5583
6209
|
\u2022 Optional timeout: { timeoutMs } or { maxRuntimeMs } sets a run-level max runtime for foreground and async/background runs
|
|
5584
6210
|
|
|
@@ -5593,8 +6219,7 @@ CHAIN EXAMPLES:
|
|
|
5593
6219
|
\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"}] }
|
|
5594
6220
|
|
|
5595
6221
|
CONTROL (use action field, omit task/chain/tasks):
|
|
5596
|
-
\u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation
|
|
5597
|
-
\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
|
|
6222
|
+
\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)
|
|
5598
6223
|
\u2022 { action: "status", id: "..." } - inspect an async/background run by id or prefix
|
|
5599
6224
|
\u2022 { action: "status", view: "fleet" } - read-only active foreground/async fleet view with transcript commands
|
|
5600
6225
|
\u2022 { action: "status", id: "...", view: "transcript", index?: 0, lines?: 80 } - tail a run or child output/session transcript
|
|
@@ -5602,10 +6227,12 @@ CONTROL (use action field, omit task/chain/tasks):
|
|
|
5602
6227
|
\u2022 { action: "stop", id: "..." } - stop a current-session top-level async run; stopped runs finish with state "stopped"
|
|
5603
6228
|
\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
|
|
5604
6229
|
\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.
|
|
6230
|
+
\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.
|
|
6231
|
+
\u2022 subagent_wait({ questionId }) - wait for that supervisor question answer/expiry/cancel. Answers stay separate from final run results.
|
|
5605
6232
|
\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
|
|
5606
6233
|
|
|
5607
6234
|
SCHEDULE (opt-in; requires { "scheduledRuns": { "enabled": true } } in config.json):
|
|
5608
|
-
\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.
|
|
6235
|
+
\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.
|
|
5609
6236
|
\u2022 { action: "schedule-list" } - list scheduled runs for this session
|
|
5610
6237
|
\u2022 { action: "schedule-status", id: "..." } - inspect one scheduled run
|
|
5611
6238
|
\u2022 { action: "schedule-cancel", id: "..." } - cancel a scheduled run before it fires
|
|
@@ -5616,19 +6243,20 @@ DIAGNOSTICS:
|
|
|
5616
6243
|
${SUBAGENT_SAFETY_GUIDANCE}`, COMPACT_SUBAGENT_TOOL_DESCRIPTION = `Delegate to generic children by prompting them. Use exactly one mode per call.
|
|
5617
6244
|
|
|
5618
6245
|
EXECUTE:
|
|
5619
|
-
\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.
|
|
6246
|
+
\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.
|
|
5620
6247
|
\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".
|
|
5621
|
-
\u2022
|
|
6248
|
+
\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.
|
|
6249
|
+
${SUBAGENT_TIER_GUIDANCE}
|
|
5622
6250
|
\u2022 Chain templates may use {task}, {previous}, {chain_dir}, and named outputs. Parallel worktree isolation requires a clean git repo.
|
|
5623
6251
|
\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}]}] }
|
|
5624
6252
|
|
|
5625
6253
|
CONTROL:
|
|
5626
|
-
\u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model,
|
|
6254
|
+
\u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model, status, interrupt, stop, resume, steer, append-step.
|
|
5627
6255
|
\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.
|
|
5628
6256
|
\u2022 Opt-in schedule actions: schedule, schedule-list, schedule-status, schedule-cancel. Schedule only explicit delayed runs the user asked for.
|
|
5629
6257
|
|
|
5630
6258
|
ASYNC / WAIT:
|
|
5631
|
-
\u2022
|
|
6259
|
+
\u2022 ${SUBAGENT_ASYNC_GUIDANCE}
|
|
5632
6260
|
\u2022 Status and artifacts live under asyncId/asyncDir with status.json, events.jsonl, output logs, session files, and { action:"status", id:"..." }.
|
|
5633
6261
|
|
|
5634
6262
|
SAFETY:
|
|
@@ -5647,8 +6275,8 @@ function resolveToolDescriptionMode(config, options) {
|
|
|
5647
6275
|
function customDescriptionPaths(options) {
|
|
5648
6276
|
let cwd = options?.cwd ?? process.cwd(), agentDir = options?.agentDir ?? getAgentDir();
|
|
5649
6277
|
return [
|
|
5650
|
-
|
|
5651
|
-
|
|
6278
|
+
path11.join(getProjectConfigDir(cwd), CUSTOM_TOOL_DESCRIPTION_FILE),
|
|
6279
|
+
path11.join(agentDir, CUSTOM_TOOL_DESCRIPTION_FILE)
|
|
5652
6280
|
];
|
|
5653
6281
|
}
|
|
5654
6282
|
function renderCustomTemplate(template, options) {
|
|
@@ -5671,7 +6299,7 @@ function loadCustomToolDescription(options) {
|
|
|
5671
6299
|
for (let filePath of customDescriptionPaths(options)) {
|
|
5672
6300
|
let stat;
|
|
5673
6301
|
try {
|
|
5674
|
-
stat =
|
|
6302
|
+
stat = fs9.statSync(filePath);
|
|
5675
6303
|
} catch (error) {
|
|
5676
6304
|
if (typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT")
|
|
5677
6305
|
continue;
|
|
@@ -5687,7 +6315,7 @@ function loadCustomToolDescription(options) {
|
|
|
5687
6315
|
continue;
|
|
5688
6316
|
}
|
|
5689
6317
|
try {
|
|
5690
|
-
let template =
|
|
6318
|
+
let template = fs9.readFileSync(filePath, "utf-8").trim();
|
|
5691
6319
|
if (!template) {
|
|
5692
6320
|
warn(options, `Ignoring empty custom tool description '${filePath}'.`);
|
|
5693
6321
|
continue;
|
|
@@ -5726,24 +6354,24 @@ function buildSubagentToolDescription(config = {}, options) {
|
|
|
5726
6354
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
|
|
5727
6355
|
function getSubagentSessionRoot(parentSessionFile) {
|
|
5728
6356
|
if (parentSessionFile) {
|
|
5729
|
-
let baseName =
|
|
5730
|
-
return
|
|
6357
|
+
let baseName = path12.basename(parentSessionFile, ".jsonl"), sessionsDir = path12.dirname(parentSessionFile);
|
|
6358
|
+
return path12.join(sessionsDir, baseName);
|
|
5731
6359
|
}
|
|
5732
|
-
return
|
|
6360
|
+
return fs10.mkdtempSync(path12.join(os.tmpdir(), "pi-subagent-session-"));
|
|
5733
6361
|
}
|
|
5734
6362
|
function expandTilde(p) {
|
|
5735
|
-
return p.startsWith("~/") ?
|
|
6363
|
+
return p.startsWith("~/") ? path12.join(os.homedir(), p.slice(2)) : p;
|
|
5736
6364
|
}
|
|
5737
6365
|
function ensureAccessibleDir(dirPath) {
|
|
5738
|
-
|
|
6366
|
+
fs10.mkdirSync(dirPath, { recursive: !0 });
|
|
5739
6367
|
try {
|
|
5740
|
-
|
|
6368
|
+
fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
|
|
5741
6369
|
} catch {
|
|
5742
6370
|
try {
|
|
5743
|
-
|
|
6371
|
+
fs10.rmSync(dirPath, { recursive: !0, force: !0 });
|
|
5744
6372
|
} catch {
|
|
5745
6373
|
}
|
|
5746
|
-
|
|
6374
|
+
fs10.mkdirSync(dirPath, { recursive: !0 }), fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
|
|
5747
6375
|
}
|
|
5748
6376
|
}
|
|
5749
6377
|
function isSlashResultRunning(result2) {
|
|
@@ -5758,23 +6386,58 @@ function effectiveParallelTaskCount(tasks) {
|
|
|
5758
6386
|
return total + count;
|
|
5759
6387
|
}, 0);
|
|
5760
6388
|
}
|
|
5761
|
-
function
|
|
6389
|
+
function matchingActionJobs(state, requestedId) {
|
|
6390
|
+
let jobs = /* @__PURE__ */ new Map();
|
|
6391
|
+
for (let job of state.asyncJobs.values())
|
|
6392
|
+
jobs.set(job.asyncId, job);
|
|
6393
|
+
for (let job of state.fleetJobs?.values() ?? [])
|
|
6394
|
+
jobs.set(job.asyncId, job);
|
|
6395
|
+
let exact = jobs.get(requestedId);
|
|
6396
|
+
return exact ? [exact] : [...jobs.values()].filter((job) => job.asyncId.startsWith(requestedId));
|
|
6397
|
+
}
|
|
6398
|
+
function resolveSubagentActionDisplayTitle(args, state) {
|
|
6399
|
+
let requestedId = (args.id || args.runId || "").trim();
|
|
6400
|
+
if (!requestedId)
|
|
6401
|
+
return;
|
|
6402
|
+
let matches = matchingActionJobs(state, requestedId);
|
|
6403
|
+
if (matches.length !== 1)
|
|
6404
|
+
return;
|
|
6405
|
+
let job = matches[0], steps = job.steps ?? [];
|
|
6406
|
+
if (Number.isInteger(args.index)) {
|
|
6407
|
+
let step = steps.find((candidate) => candidate.index === args.index) ?? steps[args.index];
|
|
6408
|
+
return step ? childRowLabel(step) : void 0;
|
|
6409
|
+
}
|
|
6410
|
+
if (steps.length === 1)
|
|
6411
|
+
return childRowLabel(steps[0]);
|
|
6412
|
+
if (steps.length > 1)
|
|
6413
|
+
return `${job.mode ?? "subagent"} run`;
|
|
6414
|
+
if (job.agents?.length === 1)
|
|
6415
|
+
return job.agents[0];
|
|
6416
|
+
if (job.agents?.length)
|
|
6417
|
+
return `${job.mode ?? "subagent"} run`;
|
|
6418
|
+
}
|
|
6419
|
+
function renderSubagentCall(rawArgs, theme, context, options) {
|
|
6420
|
+
let args = resolveSubagentRequestParams(rawArgs);
|
|
5762
6421
|
if (args.action) {
|
|
5763
|
-
let target = args.id || args.runId || "";
|
|
6422
|
+
let target = context?.result?.details?.displayTitle || options?.resolveActionTitle?.(args) || args.id || args.runId || "";
|
|
5764
6423
|
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`, 0, 0);
|
|
5765
6424
|
}
|
|
5766
|
-
let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks),
|
|
6425
|
+
let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks), asyncMark = subagentLaunchRunsAsync(args) ? `${theme.fg("warning", "async")} ` : "";
|
|
5767
6426
|
if (args.chain?.length)
|
|
5768
|
-
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})
|
|
6427
|
+
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}chain (${args.chain.length})`, 0, 0);
|
|
5769
6428
|
if (isParallel)
|
|
5770
|
-
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})
|
|
5771
|
-
let description = !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "";
|
|
5772
|
-
return new Text2(
|
|
6429
|
+
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}parallel (${parallelCount})`, 0, 0);
|
|
6430
|
+
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(" ");
|
|
6431
|
+
return new Text2(rest ? `${title} ${rest}` : title, 0, 0);
|
|
6432
|
+
}
|
|
6433
|
+
function renderSubagentNotify(details, theme) {
|
|
6434
|
+
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";
|
|
6435
|
+
return new Text2(`${icon} ${theme.bold(title)} ${theme.fg("dim", status)}`, 0, 0);
|
|
5773
6436
|
}
|
|
5774
6437
|
function ensureSubagentResultAnimation(context) {
|
|
5775
6438
|
let state = context.state;
|
|
5776
6439
|
state.subagentResultAnimationTimer || typeof context.invalidate == "function" && (state.frame === void 0 && (state.frame = 0), state.subagentResultAnimationTimer = setInterval(() => {
|
|
5777
|
-
state.frame = (
|
|
6440
|
+
state.frame = (state.frame ?? 0) + 1;
|
|
5778
6441
|
try {
|
|
5779
6442
|
let entries = state.animEntries;
|
|
5780
6443
|
if (entries?.length && typeof context.requestRender == "function") {
|
|
@@ -5786,7 +6449,7 @@ function ensureSubagentResultAnimation(context) {
|
|
|
5786
6449
|
context.invalidate();
|
|
5787
6450
|
} catch {
|
|
5788
6451
|
}
|
|
5789
|
-
},
|
|
6452
|
+
}, SUBAGENT_PAINT_INTERVAL_MS));
|
|
5790
6453
|
}
|
|
5791
6454
|
function isSlashResultError(result2) {
|
|
5792
6455
|
return result2.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || !1;
|
|
@@ -5816,11 +6479,12 @@ function registerSubagentExtension(pi) {
|
|
|
5816
6479
|
} catch {
|
|
5817
6480
|
}
|
|
5818
6481
|
ensureAccessibleDir(RESULTS_DIR), ensureAccessibleDir(ASYNC_DIR), cleanupOldChainDirs();
|
|
5819
|
-
let config = loadConfig(), waitToolConfig = resolveWaitToolConfig(config.waitTool),
|
|
6482
|
+
let config = normalizeAsyncLaunchConfig(loadConfig()), waitToolConfig = resolveWaitToolConfig(config.waitTool), tempArtifactsDir = getArtifactsDir(null);
|
|
5820
6483
|
cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
|
|
5821
6484
|
let state = {
|
|
5822
6485
|
baseCwd: "",
|
|
5823
6486
|
currentSessionId: null,
|
|
6487
|
+
sessionGeneration: 0,
|
|
5824
6488
|
foregroundSubagentInFlight: 0,
|
|
5825
6489
|
subagentSpawns: { sessionId: null, count: 0 },
|
|
5826
6490
|
asyncJobs: /* @__PURE__ */ new Map(),
|
|
@@ -5842,15 +6506,19 @@ function registerSubagentExtension(pi) {
|
|
|
5842
6506
|
}
|
|
5843
6507
|
}, supervisorChannel = createNativeSupervisorChannel(pi, state), mainWatchdog = registerMainWatchdog(pi), { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(pi, state, RESULTS_DIR, 600 * 1e3);
|
|
5844
6508
|
startResultWatcher(), primeExistingResults();
|
|
5845
|
-
let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), runtimeCleanup = () => {
|
|
5846
|
-
generation++, executorPromise = void 0, pendingLaunches.clear(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), state.poller && (clearInterval(state.poller), state.poller = null);
|
|
6509
|
+
let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), pendingAsyncLaunches = /* @__PURE__ */ new Set(), unregisterCancellation, runtimeCleanup = () => {
|
|
6510
|
+
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);
|
|
5847
6511
|
};
|
|
5848
6512
|
globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
|
|
5849
|
-
let { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
|
|
6513
|
+
let { ensurePoller, refreshWidget, handleStarted, handleComplete: trackComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
|
|
5850
6514
|
widgetEnabled: config.asyncWidget !== !1
|
|
5851
|
-
}),
|
|
6515
|
+
}), handleComplete = (data) => {
|
|
6516
|
+
trackComplete(data);
|
|
6517
|
+
let result2 = data;
|
|
6518
|
+
typeof state.currentSessionId == "string" && result2.sessionId !== state.currentSessionId || typeof result2.id == "string" && result2.id.trim() && supervisorChannel.settleRunQuestions(result2.id, "async run settled");
|
|
6519
|
+
}, executorExecute, scheduledRunManager = createScheduledRunManager({
|
|
5852
6520
|
config,
|
|
5853
|
-
launch: (params, ctx, signal) => executorExecute ? executorExecute(
|
|
6521
|
+
launch: (params, ctx, signal) => executorExecute ? executorExecute(randomUUID4(), params, signal, void 0, ctx) : Promise.resolve({
|
|
5854
6522
|
content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
|
|
5855
6523
|
isError: !0,
|
|
5856
6524
|
details: { mode: "management", results: [] }
|
|
@@ -5860,20 +6528,19 @@ function registerSubagentExtension(pi) {
|
|
|
5860
6528
|
let resolveLaunchReady, launchReady = !1, launchReadyPromise = new Promise((resolve3) => {
|
|
5861
6529
|
resolveLaunchReady = resolve3;
|
|
5862
6530
|
}), markLaunchReady = () => {
|
|
5863
|
-
launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), resolveLaunchReady());
|
|
6531
|
+
launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), pendingAsyncLaunches.delete(launchReadyPromise), resolveLaunchReady());
|
|
5864
6532
|
};
|
|
5865
|
-
pendingLaunches.add(launchReadyPromise);
|
|
6533
|
+
pendingLaunches.add(launchReadyPromise), isAsyncSubagentExecution(resolveSubagentRequestParams(params)) && pendingAsyncLaunches.add(launchReadyPromise);
|
|
5866
6534
|
try {
|
|
5867
6535
|
let currentGeneration = generation;
|
|
5868
6536
|
if (signal?.throwIfAborted(), !executorPromise) {
|
|
5869
|
-
let pending = import("./subagent-executor-
|
|
6537
|
+
let pending = import("./subagent-executor-MH4A7CCF.js").then(({ createSubagentExecutor }) => {
|
|
5870
6538
|
if (generation !== currentGeneration)
|
|
5871
6539
|
throw new Error("Subagent session changed before initialization");
|
|
5872
6540
|
return createSubagentExecutor({
|
|
5873
6541
|
pi,
|
|
5874
6542
|
state,
|
|
5875
6543
|
config,
|
|
5876
|
-
asyncByDefault,
|
|
5877
6544
|
waitToolEnabled: waitToolConfig.enabled,
|
|
5878
6545
|
handleScheduledRunAction: (params2, ctx2) => scheduledRunManager.handleToolCall(params2, ctx2),
|
|
5879
6546
|
watchdog: mainWatchdog,
|
|
@@ -5904,35 +6571,20 @@ function registerSubagentExtension(pi) {
|
|
|
5904
6571
|
let content = typeof message.content == "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
|
|
5905
6572
|
`);
|
|
5906
6573
|
return new Text2(content, 0, 0);
|
|
5907
|
-
}), pi.registerMessageRenderer("subagent-notify", (message,
|
|
6574
|
+
}), pi.registerMessageRenderer("subagent-notify", (message, _options, theme) => {
|
|
5908
6575
|
let content = typeof message.content == "string" ? message.content : "", details = message.details ?? parseSubagentNotifyContent(content);
|
|
5909
|
-
|
|
5910
|
-
return new Text2(content, 0, 0);
|
|
5911
|
-
let icon = details.status === "completed" ? theme.fg("success", "\u2713") : details.status === "paused" ? theme.fg("warning", "\u25A0") : theme.fg("error", "\u2717"), parts = [];
|
|
5912
|
-
details.taskInfo && parts.push(details.taskInfo), details.durationMs !== void 0 && parts.push(formatDuration(details.durationMs));
|
|
5913
|
-
let notifyLabel = details.taskInfo || details.agent || "child", text = `${icon} ${theme.bold(notifyLabel)} ${theme.fg("dim", details.status)}`;
|
|
5914
|
-
parts.length > 0 && (text += ` ${theme.fg("dim", "\xB7")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "\xB7")} `)}`);
|
|
5915
|
-
let trimmedPreview = details.resultPreview.trim(), previewLines = options.expanded ? trimmedPreview.split(`
|
|
5916
|
-
`).filter((line) => line.trim()) : [trimmedPreview.split(`
|
|
5917
|
-
`, 1)[0] ?? ""].filter((line) => line.trim());
|
|
5918
|
-
for (let line of previewLines.length > 0 ? previewLines : ["(no output)"])
|
|
5919
|
-
text += `
|
|
5920
|
-
${theme.fg("dim", `\u23BF ${line}`)}`;
|
|
5921
|
-
if (!options.expanded && trimmedPreview.includes(`
|
|
5922
|
-
`)) {
|
|
5923
|
-
let expandKey = keyText("app.tools.expand");
|
|
5924
|
-
text += `
|
|
5925
|
-
${theme.fg("dim", `${expandKey} full notification`)}`;
|
|
5926
|
-
}
|
|
5927
|
-
return details.sessionLabel && details.sessionValue && (text += `
|
|
5928
|
-
${theme.fg("muted", `${details.sessionLabel}: ${shortenPath(details.sessionValue)}`)}`), new Text2(text, 0, 0);
|
|
6576
|
+
return details ? renderSubagentNotify(details, theme) : new Text2(content, 0, 0);
|
|
5929
6577
|
}), pi.registerMessageRenderer(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
|
|
5930
6578
|
let details = message.details;
|
|
5931
6579
|
if (details)
|
|
5932
6580
|
return new Text2(theme.fg(details.state === "recovered" ? "warning" : "error", formatSteeringNotice(details)), 0, 0);
|
|
5933
6581
|
}), pi.registerMessageRenderer(SUBAGENT_CONTROL_MESSAGE_TYPE, () => {
|
|
5934
6582
|
});
|
|
5935
|
-
let executeSubagentCollapsed = (id, params, signal, onUpdate, ctx) =>
|
|
6583
|
+
let executeSubagentCollapsed = async (id, params, signal, onUpdate, ctx) => {
|
|
6584
|
+
ctx.hasUI && ctx.ui.setToolsExpanded(!1);
|
|
6585
|
+
let displayTitle = params.action === "steer" ? resolveSubagentActionDisplayTitle(params, state) : void 0, result2 = await executor.execute(id, params, signal, onUpdate, ctx);
|
|
6586
|
+
return displayTitle && result2.details && (result2.details.displayTitle = displayTitle), result2;
|
|
6587
|
+
}, slashBridge = registerSlashSubagentBridge({
|
|
5936
6588
|
events: pi.events,
|
|
5937
6589
|
getContext: () => state.lastUiContext,
|
|
5938
6590
|
execute: (id, params, signal, onUpdate, ctx) => executeSubagentCollapsed(id, params, signal, onUpdate, ctx)
|
|
@@ -5957,7 +6609,9 @@ function registerSubagentExtension(pi) {
|
|
|
5957
6609
|
execute(id, params, signal, onUpdate, ctx) {
|
|
5958
6610
|
return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
|
|
5959
6611
|
},
|
|
5960
|
-
renderCall: renderSubagentCall,
|
|
6612
|
+
renderCall: (args, theme, context) => renderSubagentCall(args, theme, context, {
|
|
6613
|
+
resolveActionTitle: (params) => resolveSubagentActionDisplayTitle(params, state)
|
|
6614
|
+
}),
|
|
5961
6615
|
renderResult(result2, options, theme, context) {
|
|
5962
6616
|
subagentResultIsRunning(result2) ? ensureSubagentResultAnimation(context) : clearLegacyResultAnimationTimer(context);
|
|
5963
6617
|
let state2 = context.state, entries = [];
|
|
@@ -6016,16 +6670,25 @@ function registerSubagentExtension(pi) {
|
|
|
6016
6670
|
} catch {
|
|
6017
6671
|
}
|
|
6018
6672
|
}, resetSessionState = (ctx) => {
|
|
6019
|
-
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]) {
|
|
6020
|
-
let
|
|
6021
|
-
|
|
6022
|
-
}
|
|
6023
|
-
state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx)
|
|
6673
|
+
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]) {
|
|
6674
|
+
let sessionId2 = ctx.sessionManager.getSessionId();
|
|
6675
|
+
sessionId2 && (process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId2);
|
|
6676
|
+
}
|
|
6677
|
+
state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx);
|
|
6678
|
+
let sessionId = state.currentSessionId, sessionGeneration = generation;
|
|
6679
|
+
sessionId && (unregisterCancellation = registerSubagentCancellation(sessionId, createSubagentCancellation({
|
|
6680
|
+
pendingLaunches: pendingAsyncLaunches,
|
|
6681
|
+
isCurrent: () => generation === sessionGeneration && state.currentSessionId === sessionId,
|
|
6682
|
+
getActiveRunIds: () => [...state.asyncJobs.values()].filter((job) => job.sessionId === sessionId && (job.status === "queued" || job.status === "running")).map((job) => job.asyncId),
|
|
6683
|
+
async stopRun(id) {
|
|
6684
|
+
return !(await executor.execute(randomUUID4(), { action: "stop", id }, new AbortController().signal, void 0, ctx)).isError;
|
|
6685
|
+
}
|
|
6686
|
+
}))), scheduledRunManager.bindSession(ctx), restoreSlashFinalSnapshots(ctx.sessionManager.getEntries()), primeExistingResults();
|
|
6024
6687
|
};
|
|
6025
6688
|
pi.on("session_start", (_event, ctx) => {
|
|
6026
6689
|
resetSessionState(ctx), rpcBridge.emitReady(ctx), supervisorChannel.start();
|
|
6027
6690
|
}), pi.on("session_shutdown", () => {
|
|
6028
|
-
generation++, executorPromise = void 0, pendingLaunches.clear(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
6691
|
+
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];
|
|
6029
6692
|
for (let unsubscribe of eventUnsubscribes)
|
|
6030
6693
|
try {
|
|
6031
6694
|
unsubscribe();
|
|
@@ -6034,7 +6697,7 @@ function registerSubagentExtension(pi) {
|
|
|
6034
6697
|
globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes && delete globalStore[eventUnsubscribeStoreKey], stopResultWatcher(), scheduledRunManager.stop(), state.poller && clearInterval(state.poller), state.poller = null, clearPendingForegroundControlNotices(state);
|
|
6035
6698
|
for (let timer of state.cleanupTimers.values())
|
|
6036
6699
|
clearTimeout(timer);
|
|
6037
|
-
state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey];
|
|
6700
|
+
state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey], disposeSubagentWidget();
|
|
6038
6701
|
try {
|
|
6039
6702
|
state.lastUiContext?.hasUI && state.lastUiContext.ui.setWidget(WIDGET_KEY, void 0);
|
|
6040
6703
|
} catch (error) {
|