@bastani/atomic 0.8.21 → 0.8.22-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/CHANGELOG.md +40 -9
  2. package/dist/builtin/intercom/broker/broker.ts +3 -3
  3. package/dist/builtin/intercom/config.ts +3 -3
  4. package/dist/builtin/intercom/index.ts +1 -1
  5. package/dist/builtin/intercom/package.json +1 -1
  6. package/dist/builtin/intercom/ui/compose.ts +2 -2
  7. package/dist/builtin/mcp/host-html-template.ts +0 -3
  8. package/dist/builtin/mcp/package.json +1 -1
  9. package/dist/builtin/subagents/CHANGELOG.md +13 -4
  10. package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
  11. package/dist/builtin/subagents/agents/debugger.md +6 -6
  12. package/dist/builtin/subagents/package.json +1 -1
  13. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
  14. package/dist/builtin/subagents/skills/browser-use/SKILL.md +234 -0
  15. package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +76 -0
  16. package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +92 -0
  17. package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
  18. package/dist/builtin/subagents/src/agents/skills.ts +19 -1
  19. package/dist/builtin/subagents/src/extension/index.ts +24 -22
  20. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +7 -1
  21. package/dist/builtin/subagents/src/runs/background/async-execution.ts +23 -7
  22. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +98 -3
  23. package/dist/builtin/subagents/src/runs/background/async-status.ts +3 -1
  24. package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -1
  25. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +3 -0
  26. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +37 -12
  27. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +15 -15
  28. package/dist/builtin/subagents/src/runs/foreground/execution.ts +26 -2
  29. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +1 -1
  30. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +7 -0
  31. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +28 -1
  32. package/dist/builtin/subagents/src/shared/fast-mode.ts +80 -0
  33. package/dist/builtin/subagents/src/shared/formatters.ts +4 -2
  34. package/dist/builtin/subagents/src/shared/types.ts +4 -2
  35. package/dist/builtin/subagents/src/shared/utils.ts +3 -61
  36. package/dist/builtin/subagents/src/tui/render.ts +303 -157
  37. package/dist/builtin/web-access/package.json +1 -1
  38. package/dist/builtin/workflows/CHANGELOG.md +95 -35
  39. package/dist/builtin/workflows/README.md +228 -41
  40. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +535 -541
  41. package/dist/builtin/workflows/builtin/goal.ts +39 -25
  42. package/dist/builtin/workflows/builtin/open-claude-design.ts +66 -69
  43. package/dist/builtin/workflows/builtin/ralph.ts +21 -21
  44. package/dist/builtin/workflows/package.json +6 -5
  45. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  46. package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +2 -2
  47. package/dist/builtin/workflows/src/extension/discovery.ts +25 -146
  48. package/dist/builtin/workflows/src/extension/dispatcher.ts +72 -24
  49. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +363 -0
  50. package/dist/builtin/workflows/src/extension/index.ts +690 -352
  51. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +99 -62
  52. package/dist/builtin/workflows/src/extension/render-call.ts +2 -1
  53. package/dist/builtin/workflows/src/extension/render-result.ts +9 -3
  54. package/dist/builtin/workflows/src/extension/renderers.ts +5 -3
  55. package/dist/builtin/workflows/src/extension/runtime.ts +68 -33
  56. package/dist/builtin/workflows/src/extension/status-writer.ts +1 -1
  57. package/dist/builtin/workflows/src/extension/wiring.ts +34 -13
  58. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +142 -0
  59. package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -4
  60. package/dist/builtin/workflows/src/index.ts +2 -0
  61. package/dist/builtin/workflows/src/intercom/result-intercom.ts +1 -1
  62. package/dist/builtin/workflows/src/runs/background/runner.ts +6 -4
  63. package/dist/builtin/workflows/src/runs/background/status.ts +45 -21
  64. package/dist/builtin/workflows/src/runs/foreground/executor.ts +624 -52
  65. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +1 -1
  66. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +80 -24
  67. package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +61 -24
  68. package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +32 -10
  69. package/dist/builtin/workflows/src/sdk-surface.ts +6 -0
  70. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +178 -0
  71. package/dist/builtin/workflows/src/shared/persistence-restore.ts +92 -12
  72. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +21 -3
  73. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -2
  74. package/dist/builtin/workflows/src/shared/run-visibility.ts +9 -0
  75. package/dist/builtin/workflows/src/shared/schema-introspection.ts +121 -0
  76. package/dist/builtin/workflows/src/shared/serializable.ts +132 -0
  77. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +91 -9
  78. package/dist/builtin/workflows/src/shared/store-types.ts +31 -3
  79. package/dist/builtin/workflows/src/shared/store.ts +58 -14
  80. package/dist/builtin/workflows/src/shared/types.ts +105 -40
  81. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +129 -13
  82. package/dist/builtin/workflows/src/tui/chat-surface.ts +6 -1
  83. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +3 -2
  84. package/dist/builtin/workflows/src/tui/graph-canvas.ts +1 -1
  85. package/dist/builtin/workflows/src/tui/graph-view.ts +91 -65
  86. package/dist/builtin/workflows/src/tui/inline-form-card.ts +1 -1
  87. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +3 -2
  88. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +3 -2
  89. package/dist/builtin/workflows/src/tui/inputs-picker.ts +8 -7
  90. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
  91. package/dist/builtin/workflows/src/tui/node-card.ts +34 -8
  92. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -11
  93. package/dist/builtin/workflows/src/tui/prompt-card.ts +98 -50
  94. package/dist/builtin/workflows/src/tui/session-list.ts +7 -2
  95. package/dist/builtin/workflows/src/tui/session-picker.ts +2 -0
  96. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +226 -55
  97. package/dist/builtin/workflows/src/tui/status-helpers.ts +2 -0
  98. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +37 -158
  99. package/dist/builtin/workflows/src/tui/toast.ts +2 -2
  100. package/dist/builtin/workflows/src/tui/widget.ts +53 -12
  101. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +270 -19
  102. package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +184 -0
  103. package/dist/builtin/workflows/src/workflows/define-workflow.ts +138 -43
  104. package/dist/config.d.ts +9 -0
  105. package/dist/config.d.ts.map +1 -1
  106. package/dist/config.js +45 -0
  107. package/dist/config.js.map +1 -1
  108. package/dist/core/agent-session.d.ts +27 -9
  109. package/dist/core/agent-session.d.ts.map +1 -1
  110. package/dist/core/agent-session.js +196 -17
  111. package/dist/core/agent-session.js.map +1 -1
  112. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  113. package/dist/core/atomic-guide-command.js +2 -2
  114. package/dist/core/atomic-guide-command.js.map +1 -1
  115. package/dist/core/codex-fast-mode.d.ts +36 -0
  116. package/dist/core/codex-fast-mode.d.ts.map +1 -0
  117. package/dist/core/codex-fast-mode.js +117 -0
  118. package/dist/core/codex-fast-mode.js.map +1 -0
  119. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  120. package/dist/core/compaction/branch-summarization.js +1 -1
  121. package/dist/core/compaction/branch-summarization.js.map +1 -1
  122. package/dist/core/compaction/compaction.d.ts.map +1 -1
  123. package/dist/core/compaction/compaction.js +1 -1
  124. package/dist/core/compaction/compaction.js.map +1 -1
  125. package/dist/core/extensions/index.d.ts +4 -1
  126. package/dist/core/extensions/index.d.ts.map +1 -1
  127. package/dist/core/extensions/index.js +1 -0
  128. package/dist/core/extensions/index.js.map +1 -1
  129. package/dist/core/extensions/loader.d.ts +7 -2
  130. package/dist/core/extensions/loader.d.ts.map +1 -1
  131. package/dist/core/extensions/loader.js +23 -8
  132. package/dist/core/extensions/loader.js.map +1 -1
  133. package/dist/core/extensions/reactive-widget.d.ts +58 -0
  134. package/dist/core/extensions/reactive-widget.d.ts.map +1 -0
  135. package/dist/core/extensions/reactive-widget.js +182 -0
  136. package/dist/core/extensions/reactive-widget.js.map +1 -0
  137. package/dist/core/extensions/runner.d.ts.map +1 -1
  138. package/dist/core/extensions/runner.js +1 -0
  139. package/dist/core/extensions/runner.js.map +1 -1
  140. package/dist/core/extensions/types.d.ts +26 -12
  141. package/dist/core/extensions/types.d.ts.map +1 -1
  142. package/dist/core/extensions/types.js.map +1 -1
  143. package/dist/core/messages.d.ts +1 -1
  144. package/dist/core/messages.d.ts.map +1 -1
  145. package/dist/core/messages.js +8 -2
  146. package/dist/core/messages.js.map +1 -1
  147. package/dist/core/model-registry.d.ts +4 -0
  148. package/dist/core/model-registry.d.ts.map +1 -1
  149. package/dist/core/model-registry.js +11 -0
  150. package/dist/core/model-registry.js.map +1 -1
  151. package/dist/core/resource-loader.d.ts +9 -1
  152. package/dist/core/resource-loader.d.ts.map +1 -1
  153. package/dist/core/resource-loader.js +49 -21
  154. package/dist/core/resource-loader.js.map +1 -1
  155. package/dist/core/sdk.d.ts.map +1 -1
  156. package/dist/core/sdk.js +22 -13
  157. package/dist/core/sdk.js.map +1 -1
  158. package/dist/core/session-manager.d.ts +7 -5
  159. package/dist/core/session-manager.d.ts.map +1 -1
  160. package/dist/core/session-manager.js +5 -3
  161. package/dist/core/session-manager.js.map +1 -1
  162. package/dist/core/settings-manager.d.ts +16 -0
  163. package/dist/core/settings-manager.d.ts.map +1 -1
  164. package/dist/core/settings-manager.js +64 -5
  165. package/dist/core/settings-manager.js.map +1 -1
  166. package/dist/core/slash-commands.d.ts.map +1 -1
  167. package/dist/core/slash-commands.js +1 -0
  168. package/dist/core/slash-commands.js.map +1 -1
  169. package/dist/core/system-prompt.d.ts.map +1 -1
  170. package/dist/core/system-prompt.js +7 -4
  171. package/dist/core/system-prompt.js.map +1 -1
  172. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  173. package/dist/core/tools/ask-user-question/ask-user-question.js +2 -2
  174. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  175. package/dist/index.d.ts +4 -3
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +3 -2
  178. package/dist/index.js.map +1 -1
  179. package/dist/main.d.ts +3 -0
  180. package/dist/main.d.ts.map +1 -1
  181. package/dist/main.js +12 -0
  182. package/dist/main.js.map +1 -1
  183. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  184. package/dist/modes/interactive/chat-input-actions.js.map +1 -1
  185. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  186. package/dist/modes/interactive/components/diff.js +0 -1
  187. package/dist/modes/interactive/components/diff.js.map +1 -1
  188. package/dist/modes/interactive/components/fast-mode-selector.d.ts +27 -0
  189. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -0
  190. package/dist/modes/interactive/components/fast-mode-selector.js +105 -0
  191. package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -0
  192. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  193. package/dist/modes/interactive/components/footer.js +7 -12
  194. package/dist/modes/interactive/components/footer.js.map +1 -1
  195. package/dist/modes/interactive/components/index.d.ts +1 -0
  196. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  197. package/dist/modes/interactive/components/index.js +1 -0
  198. package/dist/modes/interactive/components/index.js.map +1 -1
  199. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  200. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  201. package/dist/modes/interactive/interactive-mode.js +132 -30
  202. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  203. package/dist/modes/print-mode.d.ts.map +1 -1
  204. package/dist/modes/print-mode.js +53 -6
  205. package/dist/modes/print-mode.js.map +1 -1
  206. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  207. package/dist/modes/rpc/rpc-mode.js +3 -0
  208. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  209. package/docs/compaction.md +1 -1
  210. package/docs/custom-provider.md +2 -2
  211. package/docs/development.md +2 -2
  212. package/docs/docs.json +2 -2
  213. package/docs/extensions.md +18 -13
  214. package/docs/providers.md +5 -1
  215. package/docs/quickstart.md +5 -3
  216. package/docs/rpc.md +5 -5
  217. package/docs/sdk.md +12 -12
  218. package/docs/settings.md +18 -0
  219. package/docs/themes.md +6 -6
  220. package/docs/tui.md +20 -18
  221. package/docs/usage.md +2 -0
  222. package/docs/workflows.md +403 -39
  223. package/examples/extensions/qna.ts +2 -2
  224. package/package.json +4 -4
  225. package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +0 -392
  226. package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +0 -23
  227. package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +0 -39
  228. package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +0 -87
  229. package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +0 -241
  230. package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +0 -225
  231. package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
  232. package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +0 -275
  233. package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +0 -134
  234. package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +0 -139
  235. package/dist/builtin/subagents/skills/playwright-cli/references/video-recording.md +0 -143
@@ -0,0 +1,76 @@
1
+ # Raw CDP & Python Session Reference
2
+
3
+ The CLI commands handle most browser interactions. Use `browser-use python` with raw CDP when you need browser-level control the CLI doesn't expose — activating a tab so the user sees it, intercepting network requests, emulating devices, or working with Chrome target IDs directly.
4
+
5
+ ## How the Python session works
6
+
7
+ `browser-use python "statement"` executes one Python statement per call. Variables persist across calls — set a value in one call, use it in the next.
8
+
9
+ A `browser` object is pre-injected with sync wrappers for common operations (`browser.goto()`, `browser.click()`, etc.). For anything beyond those, two internals give you full access:
10
+
11
+ - `browser._run(coroutine)` — run any async coroutine synchronously (60s timeout)
12
+ - `browser._session` — the raw `BrowserSession` with full CDP client access
13
+
14
+ ## Getting a CDP client
15
+
16
+ ```bash
17
+ browser-use python "cdp = browser._run(browser._session.get_or_create_cdp_session())"
18
+ ```
19
+
20
+ After this, `cdp` persists across calls. Use `cdp.cdp_client.send.<Domain>.<method>()` for any CDP command and `cdp.session_id` for the session parameter.
21
+
22
+ ## Recipes
23
+
24
+ ### Activate a tab (make it visible to the user)
25
+
26
+ The CLI's `tab switch` only changes the agent's internal focus — Chrome's visible tab doesn't change. To actually show the user a specific tab:
27
+
28
+ ```bash
29
+ # Get all targets to find the target ID
30
+ browser-use python "targets = browser._session.session_manager.get_all_page_targets()"
31
+ browser-use python "print([(i, t.url) for i, t in enumerate(targets)])"
32
+
33
+ # Activate target at index 1 so the user sees it
34
+ browser-use python "cdp = browser._run(browser._session.get_or_create_cdp_session(target_id=None, focus=False))"
35
+ browser-use python "browser._run(cdp.cdp_client.send.Target.activateTarget(params={'targetId': targets[1].target_id}))"
36
+ ```
37
+
38
+ ### List all tabs with target IDs
39
+
40
+ ```bash
41
+ browser-use python "targets = browser._session.session_manager.get_all_page_targets()"
42
+ browser-use python "
43
+ for i, t in enumerate(targets):
44
+ print(f'{i}: {t.target_id[:12]}... {t.url}')
45
+ "
46
+ ```
47
+
48
+ ### Run JavaScript and get the result
49
+
50
+ ```bash
51
+ browser-use python "cdp = browser._run(browser._session.get_or_create_cdp_session())"
52
+ browser-use python "result = browser._run(cdp.cdp_client.send.Runtime.evaluate(params={'expression': 'document.title', 'returnByValue': True}, session_id=cdp.session_id))"
53
+ browser-use python "print(result['result']['value'])"
54
+ ```
55
+
56
+ ### Emulate a mobile device
57
+
58
+ ```bash
59
+ browser-use python "cdp = browser._run(browser._session.get_or_create_cdp_session())"
60
+ browser-use python "browser._run(cdp.cdp_client.send.Emulation.setDeviceMetricsOverride(params={'width': 375, 'height': 812, 'deviceScaleFactor': 3, 'mobile': True}, session_id=cdp.session_id))"
61
+ ```
62
+
63
+ ### Get cookies via CDP
64
+
65
+ ```bash
66
+ browser-use python "cdp = browser._run(browser._session.get_or_create_cdp_session())"
67
+ browser-use python "cookies = browser._run(cdp.cdp_client.send.Network.getCookies(params={}, session_id=cdp.session_id))"
68
+ browser-use python "print(cookies)"
69
+ ```
70
+
71
+ ## Tips
72
+
73
+ - Each `browser-use python` call is one statement. Multi-line strings work for `for` loops and `if` blocks, but you can't mix statements and expressions. Use multiple calls.
74
+ - Variables persist: set `cdp = ...` in one call, use `cdp` in the next.
75
+ - The `browser._run()` bridge has a 60-second timeout. For long operations, increase it or use the async internals directly.
76
+ - All CDP domains are available via `cdp.cdp_client.send.<Domain>.<method>()`. See the [Chrome DevTools Protocol docs](https://chromedevtools.github.io/devtools-protocol/) for the full API.
@@ -0,0 +1,92 @@
1
+ # Multiple Browser Sessions
2
+
3
+ ## Why use multiple sessions
4
+
5
+ When you need more than one browser at a time:
6
+ - Cloud browser for scraping + local Chrome for authenticated tasks
7
+ - Two different Chrome profiles simultaneously
8
+ - Isolated browser for testing that won't affect the user's browsing
9
+ - Running a headed browser for debugging while headless runs in background
10
+
11
+ ## How sessions are isolated
12
+
13
+ Each `--session NAME` gets:
14
+ - Its own daemon process
15
+ - Its own Unix socket (`~/.browser-use/{name}.sock`)
16
+ - Its own PID file and state file
17
+ - Its own browser instance (completely independent)
18
+ - Its own tab ownership state (multi-agent locks don't cross sessions)
19
+
20
+ ## The `--session` flag
21
+
22
+ Must be passed on every command targeting that session:
23
+
24
+ ```bash
25
+ browser-use --session work open <url> # goes to 'work' daemon
26
+ browser-use --session work state # reads from 'work' daemon
27
+ browser-use state # goes to 'default' daemon (different browser)
28
+ ```
29
+
30
+ If you forget `--session`, the command goes to the `default` session. This is the most common mistake — you'll interact with the wrong browser.
31
+
32
+ ## Combining sessions with browser modes
33
+
34
+ ```bash
35
+ # Session 1: cloud browser
36
+ browser-use --session cloud cloud connect
37
+
38
+ # Session 2: connect to user's Chrome
39
+ browser-use --session chrome connect
40
+
41
+ # Session 3: headed Chromium for debugging
42
+ browser-use --session debug --headed open <url>
43
+ ```
44
+
45
+ Each session is fully independent. The cloud session talks to a remote browser, the chrome session talks to the user's Chrome, and the debug session manages its own Chromium — all running simultaneously.
46
+
47
+ ## Listing and managing sessions
48
+
49
+ ```bash
50
+ browser-use sessions
51
+ ```
52
+
53
+ Output:
54
+ ```
55
+ SESSION PHASE PID CONFIG
56
+ cloud running 12345 cloud
57
+ chrome running 12346 cdp
58
+ debug ready 12347 headed
59
+ ```
60
+
61
+ PHASE shows the daemon lifecycle state: `initializing`, `ready`, `starting`, `running`, `shutting_down`, `stopped`, `failed`.
62
+
63
+ ```bash
64
+ browser-use --session cloud close # close one session
65
+ browser-use close --all # close every session
66
+ ```
67
+
68
+ ## Common patterns
69
+
70
+ **Cloud + local authenticated:**
71
+ ```bash
72
+ browser-use --session scraper cloud connect
73
+ browser-use --session scraper open https://example.com
74
+ # ... scrape data ...
75
+
76
+ browser-use --session auth --profile "Default" open https://github.com
77
+ browser-use --session auth state
78
+ # ... interact with authenticated site ...
79
+ ```
80
+
81
+ **Throwaway test browser:**
82
+ ```bash
83
+ browser-use --session test --headed open https://localhost:3000
84
+ # ... test, debug, inspect ...
85
+ browser-use --session test close # done, clean up
86
+ ```
87
+
88
+ **Environment variable:**
89
+ ```bash
90
+ export BROWSER_USE_SESSION=work
91
+ browser-use open <url> # uses 'work' session without --session flag
92
+ ```
@@ -20,7 +20,7 @@ Use this skill when the parent orchestrator needs to launch a specialized subage
20
20
  - **Parallel codebase discovery**: combine `codebase-locator`, `codebase-analyzer`, and `codebase-pattern-finder` to map where code lives, how it works, and what existing conventions look like — concurrently, with fresh context per child.
21
21
  - **Local research mining**: pair `codebase-research-locator` with `codebase-research-analyzer` to surface prior decisions in `research/` and `specs/` and extract what still applies.
22
22
  - **External research**: use `codebase-online-researcher` for authoritative web sources, with persisted findings in `research/web/`.
23
- - **Debug and fix**: use `debugger` to reproduce, diagnose, and patch failing behavior with `tdd` and `playwright-cli` support.
23
+ - **Debug and fix**: use `debugger` to reproduce, diagnose, and patch failing behavior with `tdd` and `browser-use` support.
24
24
  - **Refinement**: use `code-simplifier` to clean up recently changed code without altering behavior.
25
25
  - **Adversarial review**: compose read-only specialists (`codebase-analyzer`, `codebase-pattern-finder`, `debugger` in inspect-only mode, `codebase-online-researcher`) into a parallel review pass — there is no generic `reviewer` agent.
26
26
  - **Long-running work**: launch async/background runs and inspect them later.
@@ -125,9 +125,9 @@ Builtin agents load at the lowest priority. Project agents override user agents,
125
125
  | `codebase-pattern-finder` | Find similar implementations or conventions | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only. Returns code snippets with `file:line` references. |
126
126
  | `codebase-research-locator` | Discover prior `research/` and `specs/` docs | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only. Sorts by date, tiers by recency, flags supersession. |
127
127
  | `codebase-research-analyzer` | Extract decisions and constraints from prior docs | `openai/gpt-5.5` | low | read, grep, find, ls, bash | Read-only. Filters aggressively for what still applies today. |
128
- | `codebase-online-researcher` | Web research with authoritative sources | `openai/gpt-5.5` | low | read, grep, find, ls, bash, write, web_search, fetch_content, get_search_content | Has the `playwright-cli` skill. Persists keepers to `research/web/`. |
128
+ | `codebase-online-researcher` | Web research with authoritative sources | `openai/gpt-5.5` | low | read, grep, find, ls, bash, write, web_search, fetch_content, get_search_content | Has the `browser-use` skill. Persists keepers to `research/web/`. |
129
129
  | `code-simplifier` | Clean up recently changed code without changing behavior | `openai/gpt-5.5` | low | read, edit, write, grep, find, ls, bash | **Writer.** Scopes to recently modified code by default; preserves all observable behavior. |
130
- | `debugger` | Reproduce, diagnose, and fix failing behavior | `openai/gpt-5.5` | high | read, edit, write, grep, find, ls, bash, web_search, fetch_content, get_search_content | **Writer.** Has the `tdd` and `playwright-cli` skills. Inspect-only mode requires an explicit instruction. |
130
+ | `debugger` | Reproduce, diagnose, and fix failing behavior | `openai/gpt-5.5` | high | read, edit, write, grep, find, ls, bash, web_search, fetch_content, get_search_content | **Writer.** Has the `tdd` and `browser-use` skills. Inspect-only mode requires an explicit instruction. |
131
131
 
132
132
  Each builtin declares an explicit `model` and `fallbackModels` chain (typically `github-copilot/<same>`, then `anthropic/claude-opus-4-8`, then `github-copilot/claude-opus-4.7`). The current user-selected model is automatically appended as the last fallback and de-duplicated. Override per run with inline config:
133
133
 
@@ -153,7 +153,7 @@ A strong subagent prompt usually includes:
153
153
  - **Output**: the expected summary shape, artifact path, or finding format.
154
154
  - **Stop rules**: when to stop after enough evidence, and when not to keep searching.
155
155
 
156
- Avoid carrying over old prompt habits that over-specify every step. Use `must`, `always`, and `never` for real invariants; for judgment calls, give decision rules. For example, tell `codebase-analyzer` to trace the staged diff directly and report only evidence-backed findings, rather than prescribing every file or command. Tell `codebase-online-researcher` the retrieval budget: start with broad targeted searches, fetch the strongest sources via `fetch_content`, fall back to `playwright-cli` only when JS execution is required, and stop when the question is answered.
156
+ Avoid carrying over old prompt habits that over-specify every step. Use `must`, `always`, and `never` for real invariants; for judgment calls, give decision rules. For example, tell `codebase-analyzer` to trace the staged diff directly and report only evidence-backed findings, rather than prescribing every file or command. Tell `codebase-online-researcher` the retrieval budget: start with broad targeted searches, fetch the strongest sources via `fetch_content`, fall back to `browser-use` only when JS execution is required, and stop when the question is answered.
157
157
 
158
158
  For implementation handoffs to `debugger` or `code-simplifier`, name the approved scope and success criteria more clearly than the process. Good prompts say what to change, what not to change, where the evidence lives, how to validate, and when to escalate. They should not ask the child to create another subagent plan or continue the parent conversation.
159
159
 
@@ -119,11 +119,20 @@ function extractSkillPathsFromPackageRoot(packageRoot: string, source: SkillSour
119
119
  }
120
120
 
121
121
  let cachedGlobalNpmRoot: string | null = null;
122
+ let execSyncGlobalNpmRoot = execSync;
123
+ const GLOBAL_NPM_ROOT_TIMEOUT_MS = 2500;
122
124
 
123
125
  function getGlobalNpmRoot(): string | null {
124
126
  if (cachedGlobalNpmRoot !== null) return cachedGlobalNpmRoot;
125
127
  try {
126
- cachedGlobalNpmRoot = execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
128
+ // Keep global package probing bounded during startup while still allowing
129
+ // slower Windows/corporate npm wrappers enough time to launch.
130
+ cachedGlobalNpmRoot = execSyncGlobalNpmRoot("npm root -g", {
131
+ encoding: "utf-8",
132
+ stdio: ["ignore", "pipe", "ignore"],
133
+ timeout: GLOBAL_NPM_ROOT_TIMEOUT_MS,
134
+ windowsHide: true,
135
+ }).trim();
127
136
  return cachedGlobalNpmRoot;
128
137
  } catch {
129
138
  // Global npm root is optional in constrained environments.
@@ -639,4 +648,13 @@ export function discoverAvailableSkills(cwd: string): Array<{
639
648
  export function clearSkillCache(): void {
640
649
  skillCache.clear();
641
650
  loadSkillsCache = null;
651
+ cachedGlobalNpmRoot = null;
652
+ }
653
+
654
+ /**
655
+ * @internal Test seam for unit tests that need to mock `npm root -g`.
656
+ */
657
+ export function __setGlobalNpmRootExecSyncForTest(execSyncImpl?: typeof execSync): void {
658
+ execSyncGlobalNpmRoot = execSyncImpl ?? execSync;
659
+ cachedGlobalNpmRoot = null;
642
660
  }
@@ -23,7 +23,7 @@ import { discoverAgents } from "../agents/agents.ts";
23
23
  import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
24
24
  import { resolveCurrentSessionId } from "../shared/session-identity.ts";
25
25
  import { cleanupOldChainDirs } from "../shared/settings.ts";
26
- import { renderWidget, renderSubagentResult, stopResultAnimations, stopWidgetAnimation, syncResultAnimation } from "../tui/render.ts";
26
+ import { renderLiveSubagentResult, renderSubagentResult, stopResultAnimations, stopWidgetAnimation, type SubagentResultRenderState } from "../tui/render.ts";
27
27
  import { SubagentParams } from "./schemas.ts";
28
28
  import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
29
29
  import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
@@ -117,10 +117,12 @@ function isStaleExtensionContextError(error: unknown): boolean {
117
117
  return error instanceof Error && error.message.includes("Extension context no longer active");
118
118
  }
119
119
 
120
+ type SubagentToolRenderState = SubagentResultRenderState;
121
+
120
122
  function rebuildSlashResultContainer(
121
123
  container: Container,
122
124
  result: AgentToolResult<Details>,
123
- options: { expanded: boolean },
125
+ options: { expanded: boolean; now?: number },
124
126
  theme: ExtensionContext["ui"]["theme"],
125
127
  ): void {
126
128
  container.clear();
@@ -135,20 +137,17 @@ function createSlashResultComponent(
135
137
  details: SlashMessageDetails,
136
138
  options: { expanded: boolean },
137
139
  theme: ExtensionContext["ui"]["theme"],
138
- requestRender: () => void,
139
140
  ): Container {
140
141
  const container = new Container();
141
- const animationState: { subagentResultAnimationTimer?: ReturnType<typeof setInterval> } = {};
142
142
  let lastVersion = -1;
143
+ let lastSnapshotNow = 0;
143
144
  container.render = (width: number): string[] => {
144
145
  const snapshot = getSlashRenderableSnapshot(details);
145
- if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
146
+ if (snapshot.version !== lastVersion) {
146
147
  lastVersion = snapshot.version;
147
- rebuildSlashResultContainer(container, snapshot.result, options, theme);
148
+ lastSnapshotNow = Date.now();
149
+ rebuildSlashResultContainer(container, snapshot.result, { ...options, now: lastSnapshotNow }, theme);
148
150
  }
149
- // Keep the live slash card's spinner animating by scheduling steady
150
- // re-renders while it runs (and tearing the timer down once it settles).
151
- syncResultAnimation(snapshot.result, { state: animationState, invalidate: requestRender });
152
151
  return Container.prototype.render.call(container, width);
153
152
  };
154
153
  return container;
@@ -281,7 +280,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
281
280
  };
282
281
  globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
283
282
 
284
- const { ensurePoller, handleStarted, handleComplete, resetJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
283
+ const { ensurePoller, handleStarted, handleComplete, resetJobs, hydrateActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
285
284
  const executor = createSubagentExecutor({
286
285
  pi,
287
286
  state,
@@ -296,7 +295,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
296
295
  pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
297
296
  const details = resolveSlashMessageDetails(message.details);
298
297
  if (!details) return undefined;
299
- return createSlashResultComponent(details, options, theme, () => state.lastUiContext?.ui.requestRender?.());
298
+ return createSlashResultComponent(details, options, theme);
300
299
  });
301
300
 
302
301
  pi.registerMessageRenderer<SubagentNotifyDetails>("subagent-notify", (message, options, theme) => {
@@ -337,7 +336,10 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
337
336
  });
338
337
 
339
338
  const executeSubagentCollapsed = (id: string, params: SubagentParamsLike, signal: AbortSignal, onUpdate: ((result: AgentToolResult<Details>) => void) | undefined, ctx: ExtensionContext) => {
340
- if (ctx.hasUI) ctx.ui.setToolsExpanded(false);
339
+ if (ctx.hasUI) {
340
+ state.lastUiContext = ctx;
341
+ ctx.ui.setToolsExpanded(false);
342
+ }
341
343
  return executor.execute(id, params, signal, onUpdate, ctx);
342
344
  };
343
345
 
@@ -394,7 +396,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
394
396
  }, 0);
395
397
  }
396
398
 
397
- const tool: ToolDefinition<typeof SubagentParams, Details> = {
399
+ const tool: ToolDefinition<typeof SubagentParams, Details, SubagentToolRenderState> = {
398
400
  name: "subagent",
399
401
  label: "Subagent",
400
402
  description: `Delegate to subagents or manage agent definitions.
@@ -465,10 +467,12 @@ DIAGNOSTICS:
465
467
  },
466
468
 
467
469
  renderResult(result, options, theme, context) {
468
- // Animate the live subagent spinner by scheduling steady re-renders
469
- // while the result is running; the timer stops once it settles.
470
- syncResultAnimation(result, context);
471
- return renderSubagentResult(result, options, theme);
470
+ // Tool-result rows live in chat scrollback. The host keeps context.state
471
+ // on the ToolExecutionComponent for this row, so foreground updates/ticks
472
+ // capture an explicit frame timestamp and arbitrary host re-renders reuse
473
+ // it. Running foreground rows intentionally advance via their own timer;
474
+ // completed scrollback remains a stable point-in-time snapshot.
475
+ return renderLiveSubagentResult(result, options, theme, context);
472
476
  },
473
477
 
474
478
  };
@@ -513,11 +517,8 @@ DIAGNOSTICS:
513
517
  if (event.toolName !== "subagent") return;
514
518
  if (!ctx.hasUI) return;
515
519
  state.lastUiContext = ctx;
516
- if (state.asyncJobs.size > 0) {
517
- renderWidget(ctx, Array.from(state.asyncJobs.values()));
518
- ctx.ui.requestRender?.();
519
- ensurePoller();
520
- }
520
+ hydrateActiveJobs(ctx);
521
+ if (state.asyncJobs.size > 0) ensurePoller();
521
522
  });
522
523
 
523
524
  const cleanupSessionArtifacts = (ctx: ExtensionContext) => {
@@ -538,6 +539,7 @@ DIAGNOSTICS:
538
539
  cleanupSessionArtifacts(ctx);
539
540
  clearPendingForegroundControlNotices(state);
540
541
  resetJobs(ctx);
542
+ hydrateActiveJobs(ctx);
541
543
  restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
542
544
  primeExistingResults();
543
545
  };
@@ -197,7 +197,12 @@ function configuredPiIntercomPackageDir(input: ResolveIntercomBridgeInput, agent
197
197
  ...(projectConfigDir ? [{ file: path.join(projectConfigDir, "settings.json"), configDir: projectConfigDir, scope: "project" as const }] : []),
198
198
  { file: path.join(agentDir, "settings.json"), configDir: agentDir, scope: "user" as const },
199
199
  ];
200
- const globalNpmRoot = input.globalNpmRoot === undefined ? getGlobalNpmRoot() : input.globalNpmRoot;
200
+ let resolvedGlobalNpmRoot = input.globalNpmRoot;
201
+ const resolveGlobalNpmRoot = (): string | null => {
202
+ if (resolvedGlobalNpmRoot !== undefined) return resolvedGlobalNpmRoot;
203
+ resolvedGlobalNpmRoot = getGlobalNpmRoot();
204
+ return resolvedGlobalNpmRoot;
205
+ };
201
206
 
202
207
  for (const { file, configDir, scope } of settingsFiles) {
203
208
  const settings = readJsonBestEffort(file);
@@ -211,6 +216,7 @@ function configuredPiIntercomPackageDir(input: ResolveIntercomBridgeInput, agent
211
216
  if (!source?.startsWith("npm:")) continue;
212
217
  const packageName = parseNpmPackageName(source);
213
218
  if (packageName !== PI_INTERCOM_PACKAGE_NAME) continue;
219
+ const globalNpmRoot = scope === "user" ? resolveGlobalNpmRoot() : null;
214
220
  const candidates = scope === "project"
215
221
  ? [path.join(configDir, "npm", "node_modules", packageName)]
216
222
  : [
@@ -4,7 +4,6 @@
4
4
 
5
5
  import { spawn } from "node:child_process";
6
6
  import * as fs from "node:fs";
7
- import * as os from "node:os";
8
7
  import * as path from "node:path";
9
8
  import { fileURLToPath } from "node:url";
10
9
  import { createRequire } from "node:module";
@@ -19,6 +18,11 @@ import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } f
19
18
  import { resolveChildCwd } from "../../shared/utils.ts";
20
19
  import { buildModelCandidates, resolveModelCandidate, type AvailableModelInfo } from "../shared/model-fallback.ts";
21
20
  import { resolveEffectiveThinking } from "../../shared/model-info.ts";
21
+ import {
22
+ getSubagentCodexFastModeSettings,
23
+ resolveSubagentCodexFastModeScope,
24
+ resolveSubagentModelFastModeMetadata,
25
+ } from "../../shared/fast-mode.ts";
22
26
  import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
23
27
  import {
24
28
  type ArtifactConfig,
@@ -254,6 +258,7 @@ export function executeAsyncChain(
254
258
  const resultMode = params.resultMode ?? "chain";
255
259
  const chainSkills = params.chainSkills ?? [];
256
260
  const availableModels = params.availableModels;
261
+ const fastModeScope = resolveSubagentCodexFastModeScope(workflowStageSubagentGuard);
257
262
  const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
258
263
  const firstStep = chain[0];
259
264
  const originalTask = params.task ?? (firstStep
@@ -329,15 +334,20 @@ export function executeAsyncChain(
329
334
 
330
335
  const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
331
336
  const model = applyThinkingSuffix(primaryModel, a.thinking);
337
+ const modelCandidates = buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel)
338
+ .map((candidate) => applyThinkingSuffix(candidate, a.thinking))
339
+ .filter((candidate): candidate is string => typeof candidate === "string");
340
+ const fastModeSettings = getSubagentCodexFastModeSettings(stepCwd);
332
341
  return {
333
342
  agent: s.agent,
334
343
  task,
335
344
  cwd: stepCwd,
336
345
  model,
337
346
  thinking: resolveEffectiveThinking(model, a.thinking),
338
- modelCandidates: buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel)
339
- .map((candidate) => applyThinkingSuffix(candidate, a.thinking))
340
- .filter((candidate): candidate is string => typeof candidate === "string"),
347
+ ...resolveSubagentModelFastModeMetadata({ model, modelCandidates, cwd: stepCwd, settings: fastModeSettings, scope: fastModeScope }),
348
+ modelCandidates,
349
+ codexFastModeSettings: fastModeSettings,
350
+ codexFastModeScope: fastModeScope,
341
351
  tools: a.tools,
342
352
  extensions: a.extensions,
343
353
  mcpDirectTools: a.mcpDirectTools,
@@ -602,6 +612,11 @@ export function executeAsyncSingle(
602
612
  resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider),
603
613
  agentConfig.thinking,
604
614
  );
615
+ const modelCandidates = buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel)
616
+ .map((candidate) => applyThinkingSuffix(candidate, agentConfig.thinking))
617
+ .filter((candidate): candidate is string => typeof candidate === "string");
618
+ const fastModeSettings = getSubagentCodexFastModeSettings(runnerCwd);
619
+ const fastModeScope = resolveSubagentCodexFastModeScope(workflowStageSubagentGuard);
605
620
  let spawnResult: { pid?: number; error?: string } = {};
606
621
  try {
607
622
  spawnResult = spawnRunner(
@@ -614,9 +629,10 @@ export function executeAsyncSingle(
614
629
  cwd: runnerCwd,
615
630
  model,
616
631
  thinking: resolveEffectiveThinking(model, agentConfig.thinking),
617
- modelCandidates: buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel)
618
- .map((candidate) => applyThinkingSuffix(candidate, agentConfig.thinking))
619
- .filter((candidate): candidate is string => typeof candidate === "string"),
632
+ ...resolveSubagentModelFastModeMetadata({ model, modelCandidates, cwd: runnerCwd, settings: fastModeSettings, scope: fastModeScope }),
633
+ modelCandidates,
634
+ codexFastModeSettings: fastModeSettings,
635
+ codexFastModeScope: fastModeScope,
620
636
  tools: agentConfig.tools,
621
637
  extensions: agentConfig.extensions,
622
638
  mcpDirectTools: agentConfig.mcpDirectTools,
@@ -14,6 +14,7 @@ import {
14
14
  SUBAGENT_CONTROL_INTERCOM_EVENT,
15
15
  } from "../../shared/types.ts";
16
16
  import { readStatus } from "../../shared/utils.ts";
17
+ import { listAsyncRuns, type AsyncRunSummary } from "./async-status.ts";
17
18
  import { normalizeParallelGroups } from "./parallel-groups.ts";
18
19
  import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.ts";
19
20
  import { hasLiveNestedDescendants, updateAsyncJobNestedProjection } from "../shared/nested-events.ts";
@@ -26,18 +27,77 @@ interface AsyncJobTrackerOptions {
26
27
  now?: () => number;
27
28
  }
28
29
 
30
+ const ACTIVE_HYDRATION_STATES: Array<AsyncRunSummary["state"]> = ["queued", "running"];
31
+
32
+ function cwdMatches(summaryCwd: string | undefined, currentCwd: string | undefined): boolean {
33
+ return Boolean(summaryCwd && currentCwd && path.resolve(summaryCwd) === path.resolve(currentCwd));
34
+ }
35
+
36
+ function shouldHydrateRunForCurrentUi(summary: AsyncRunSummary, currentSessionId: string | null, currentCwd: string | undefined): boolean {
37
+ if (summary.sessionId && currentSessionId) return summary.sessionId === currentSessionId;
38
+ return cwdMatches(summary.cwd, currentCwd);
39
+ }
40
+
41
+ function asyncRunSummaryToJobState(summary: AsyncRunSummary, existing?: AsyncJobState): AsyncJobState {
42
+ const chainStepCount = summary.chainStepCount ?? summary.steps.length;
43
+ const groups = normalizeParallelGroups(summary.parallelGroups, summary.steps.length, chainStepCount);
44
+ const activeGroup = summary.currentStep !== undefined
45
+ ? groups.find((group) => summary.currentStep! >= group.start && summary.currentStep! < group.start + group.count)
46
+ : undefined;
47
+ const steps = activeGroup
48
+ ? summary.steps.slice(activeGroup.start, activeGroup.start + activeGroup.count).map((step, index) => ({ ...step, index: activeGroup.start + index }))
49
+ : summary.steps.map((step, index) => ({ ...step, index: step.index ?? index }));
50
+ const agents = steps.map((step) => step.agent);
51
+ const stepsTotal = steps.length > 0 ? steps.length : existing?.stepsTotal ?? (agents.length > 0 ? agents.length : undefined);
52
+ const completedSteps = summary.state === "complete"
53
+ ? steps.length
54
+ : steps.filter((step) => step.status === "complete" || step.status === "completed").length;
55
+ return {
56
+ ...existing,
57
+ asyncId: summary.id,
58
+ asyncDir: summary.asyncDir,
59
+ status: summary.state,
60
+ sessionId: summary.sessionId ?? existing?.sessionId,
61
+ activityState: summary.activityState,
62
+ lastActivityAt: summary.lastActivityAt,
63
+ currentTool: summary.currentTool,
64
+ currentToolStartedAt: summary.currentToolStartedAt,
65
+ currentPath: summary.currentPath,
66
+ turnCount: summary.turnCount,
67
+ toolCount: summary.toolCount,
68
+ mode: summary.mode,
69
+ agents: agents.length > 0 ? agents : existing?.agents,
70
+ currentStep: summary.currentStep,
71
+ chainStepCount: summary.chainStepCount ?? existing?.chainStepCount,
72
+ parallelGroups: groups,
73
+ steps: steps.length > 0 ? steps : existing?.steps,
74
+ stepsTotal,
75
+ runningSteps: steps.filter((step) => step.status === "running").length,
76
+ completedSteps,
77
+ hasParallelGroups: groups.length > 0,
78
+ activeParallelGroup: Boolean(activeGroup),
79
+ startedAt: summary.startedAt,
80
+ updatedAt: summary.lastUpdate ?? summary.startedAt,
81
+ sessionDir: summary.sessionDir ?? existing?.sessionDir,
82
+ outputFile: summary.outputFile ?? existing?.outputFile,
83
+ totalTokens: summary.totalTokens,
84
+ sessionFile: summary.sessionFile ?? existing?.sessionFile,
85
+ nestedChildren: summary.nestedChildren,
86
+ };
87
+ }
88
+
29
89
  export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: SubagentState, asyncDirRoot: string, options: AsyncJobTrackerOptions = {}): {
30
90
  ensurePoller: () => void;
31
91
  handleStarted: (data: unknown) => void;
32
92
  handleComplete: (data: unknown) => void;
33
93
  resetJobs: (ctx?: ExtensionContext) => void;
94
+ hydrateActiveJobs: (ctx?: ExtensionContext) => void;
34
95
  } {
35
96
  const completionRetentionMs = options.completionRetentionMs ?? 10000;
36
97
  const pollIntervalMs = options.pollIntervalMs ?? POLL_INTERVAL_MS;
37
98
  const resultsDir = options.resultsDir ?? RESULTS_DIR;
38
99
  const rerenderWidget = (ctx: ExtensionContext, jobs = Array.from(state.asyncJobs.values())) => {
39
100
  renderWidget(ctx, jobs);
40
- ctx.ui.requestRender?.();
41
101
  };
42
102
  const cancelCleanup = (asyncId: string) => {
43
103
  const existingTimer = state.cleanupTimers.get(asyncId);
@@ -233,12 +293,47 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
233
293
  state.poller.unref?.();
234
294
  };
235
295
 
296
+ const hydrateActiveJobs = (ctx?: ExtensionContext) => {
297
+ if (ctx?.hasUI) state.lastUiContext = ctx;
298
+ const renderCtx = state.lastUiContext?.hasUI ? state.lastUiContext : undefined;
299
+ const currentCwd = (ctx?.cwd ?? state.lastUiContext?.cwd ?? state.baseCwd) || undefined;
300
+ let summaries: AsyncRunSummary[];
301
+ try {
302
+ summaries = listAsyncRuns(asyncDirRoot, {
303
+ states: ACTIVE_HYDRATION_STATES,
304
+ resultsDir,
305
+ kill: options.kill,
306
+ now: options.now,
307
+ });
308
+ } catch (error) {
309
+ console.error(`Failed to hydrate active async jobs from '${asyncDirRoot}':`, error);
310
+ if (renderCtx) rerenderWidget(renderCtx);
311
+ return;
312
+ }
313
+
314
+ for (const summary of summaries) {
315
+ if (!shouldHydrateRunForCurrentUi(summary, state.currentSessionId, currentCwd)) continue;
316
+ cancelCleanup(summary.id);
317
+ state.asyncJobs.set(summary.id, asyncRunSummaryToJobState(summary, state.asyncJobs.get(summary.id)));
318
+ }
319
+
320
+ if (state.asyncJobs.size > 0) ensurePoller();
321
+ if (renderCtx) rerenderWidget(renderCtx);
322
+ };
323
+
236
324
  const handleStarted = (data: unknown) => {
237
325
  const info = data as AsyncStartedEvent;
238
326
  if (!info.id) return;
239
327
  const now = Date.now();
240
328
  const asyncDir = info.asyncDir ?? path.join(asyncDirRoot, info.id);
241
- const rawAgents = info.agents?.length ? info.agents : info.chain && info.chain.length > 0 ? info.chain : info.agent ? [info.agent] : undefined;
329
+ let rawAgents: string[] | undefined;
330
+ if (info.agents?.length) {
331
+ rawAgents = info.agents;
332
+ } else if (info.chain && info.chain.length > 0) {
333
+ rawAgents = info.chain;
334
+ } else if (info.agent) {
335
+ rawAgents = [info.agent];
336
+ }
242
337
  const validParallelGroups = normalizeParallelGroups(info.parallelGroups, Number.MAX_SAFE_INTEGER, info.chainStepCount ?? Number.MAX_SAFE_INTEGER);
243
338
  const firstGroup = validParallelGroups.find((group) => group.start === 0);
244
339
  const firstGroupCount = firstGroup?.count;
@@ -306,5 +401,5 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
306
401
  }
307
402
  };
308
403
 
309
- return { ensurePoller, handleStarted, handleComplete, resetJobs };
404
+ return { ensurePoller, handleStarted, handleComplete, resetJobs, hydrateActiveJobs };
310
405
  }
@@ -28,6 +28,7 @@ interface AsyncRunStepSummary {
28
28
  skills?: string[];
29
29
  model?: string;
30
30
  thinking?: string;
31
+ fastMode?: boolean;
31
32
  attemptedModels?: string[];
32
33
  error?: string;
33
34
  children?: NestedRunSummary[];
@@ -155,6 +156,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
155
156
  ...(step.skills ? { skills: step.skills } : {}),
156
157
  ...(step.model ? { model: step.model } : {}),
157
158
  ...(step.thinking ? { thinking: step.thinking } : {}),
159
+ ...(step.fastMode !== undefined ? { fastMode: step.fastMode } : {}),
158
160
  ...(step.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
159
161
  ...(step.error ? { error: step.error } : {}),
160
162
  ...(step.children?.length ? { children: step.children } : {}),
@@ -262,7 +264,7 @@ function formatStepLine(step: AsyncRunStepSummary): string {
262
264
  const parts = [`${step.index + 1}. ${step.agent}`, step.status];
263
265
  const activity = formatActivityFacts(step);
264
266
  if (activity) parts.push(activity);
265
- const modelThinking = formatModelThinking(step.model, step.thinking);
267
+ const modelThinking = formatModelThinking(step.model, step.thinking, step.fastMode);
266
268
  if (modelThinking) parts.push(modelThinking);
267
269
  if (step.durationMs !== undefined) parts.push(formatDuration(step.durationMs));
268
270
  if (step.tokens) parts.push(`${formatTokens(step.tokens.total)} tok`);
@@ -213,7 +213,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
213
213
  ].filter((line): line is string => Boolean(line));
214
214
  for (const [index, step] of (status.steps ?? []).entries()) {
215
215
  const stepActivityText = step.status === "running" ? formatActivityLabel(step.lastActivityAt, step.activityState) : undefined;
216
- const modelThinking = formatModelThinking(step.model, step.thinking);
216
+ const modelThinking = formatModelThinking(step.model, step.thinking, step.fastMode);
217
217
  const modelText = modelThinking ? ` (${modelThinking})` : "";
218
218
  const errorText = step.error ? `, error: ${step.error}` : "";
219
219
  lines.push(`${stepLineLabel(status, index)}: ${step.agent} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${errorText}`);