@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.
- package/CHANGELOG.md +40 -9
- package/dist/builtin/intercom/broker/broker.ts +3 -3
- package/dist/builtin/intercom/config.ts +3 -3
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/ui/compose.ts +2 -2
- package/dist/builtin/mcp/host-html-template.ts +0 -3
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +13 -4
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
- package/dist/builtin/subagents/agents/debugger.md +6 -6
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/builtin/subagents/skills/browser-use/SKILL.md +234 -0
- package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +76 -0
- package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +92 -0
- package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
- package/dist/builtin/subagents/src/agents/skills.ts +19 -1
- package/dist/builtin/subagents/src/extension/index.ts +24 -22
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +7 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +23 -7
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +98 -3
- package/dist/builtin/subagents/src/runs/background/async-status.ts +3 -1
- package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +3 -0
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +37 -12
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +15 -15
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +26 -2
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +7 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +28 -1
- package/dist/builtin/subagents/src/shared/fast-mode.ts +80 -0
- package/dist/builtin/subagents/src/shared/formatters.ts +4 -2
- package/dist/builtin/subagents/src/shared/types.ts +4 -2
- package/dist/builtin/subagents/src/shared/utils.ts +3 -61
- package/dist/builtin/subagents/src/tui/render.ts +303 -157
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +95 -35
- package/dist/builtin/workflows/README.md +228 -41
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +535 -541
- package/dist/builtin/workflows/builtin/goal.ts +39 -25
- package/dist/builtin/workflows/builtin/open-claude-design.ts +66 -69
- package/dist/builtin/workflows/builtin/ralph.ts +21 -21
- package/dist/builtin/workflows/package.json +6 -5
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +2 -2
- package/dist/builtin/workflows/src/extension/discovery.ts +25 -146
- package/dist/builtin/workflows/src/extension/dispatcher.ts +72 -24
- package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +363 -0
- package/dist/builtin/workflows/src/extension/index.ts +690 -352
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +99 -62
- package/dist/builtin/workflows/src/extension/render-call.ts +2 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +9 -3
- package/dist/builtin/workflows/src/extension/renderers.ts +5 -3
- package/dist/builtin/workflows/src/extension/runtime.ts +68 -33
- package/dist/builtin/workflows/src/extension/status-writer.ts +1 -1
- package/dist/builtin/workflows/src/extension/wiring.ts +34 -13
- package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +142 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -4
- package/dist/builtin/workflows/src/index.ts +2 -0
- package/dist/builtin/workflows/src/intercom/result-intercom.ts +1 -1
- package/dist/builtin/workflows/src/runs/background/runner.ts +6 -4
- package/dist/builtin/workflows/src/runs/background/status.ts +45 -21
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +624 -52
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +80 -24
- package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +61 -24
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +32 -10
- package/dist/builtin/workflows/src/sdk-surface.ts +6 -0
- package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +178 -0
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +92 -12
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +21 -3
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -2
- package/dist/builtin/workflows/src/shared/run-visibility.ts +9 -0
- package/dist/builtin/workflows/src/shared/schema-introspection.ts +121 -0
- package/dist/builtin/workflows/src/shared/serializable.ts +132 -0
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +91 -9
- package/dist/builtin/workflows/src/shared/store-types.ts +31 -3
- package/dist/builtin/workflows/src/shared/store.ts +58 -14
- package/dist/builtin/workflows/src/shared/types.ts +105 -40
- package/dist/builtin/workflows/src/tui/chat-surface-message.ts +129 -13
- package/dist/builtin/workflows/src/tui/chat-surface.ts +6 -1
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +3 -2
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +1 -1
- package/dist/builtin/workflows/src/tui/graph-view.ts +91 -65
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +1 -1
- package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +3 -2
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +3 -2
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +8 -7
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +34 -8
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -11
- package/dist/builtin/workflows/src/tui/prompt-card.ts +98 -50
- package/dist/builtin/workflows/src/tui/session-list.ts +7 -2
- package/dist/builtin/workflows/src/tui/session-picker.ts +2 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +226 -55
- package/dist/builtin/workflows/src/tui/status-helpers.ts +2 -0
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +37 -158
- package/dist/builtin/workflows/src/tui/toast.ts +2 -2
- package/dist/builtin/workflows/src/tui/widget.ts +53 -12
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +270 -19
- package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +184 -0
- package/dist/builtin/workflows/src/workflows/define-workflow.ts +138 -43
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +27 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +196 -17
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +2 -2
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/codex-fast-mode.d.ts +36 -0
- package/dist/core/codex-fast-mode.d.ts.map +1 -0
- package/dist/core/codex-fast-mode.js +117 -0
- package/dist/core/codex-fast-mode.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/index.d.ts +4 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +7 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +23 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/reactive-widget.d.ts +58 -0
- package/dist/core/extensions/reactive-widget.d.ts.map +1 -0
- package/dist/core/extensions/reactive-widget.js +182 -0
- package/dist/core/extensions/reactive-widget.js.map +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +26 -12
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +8 -2
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +11 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/resource-loader.d.ts +9 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +49 -21
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +22 -13
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -5
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +5 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +16 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +64 -5
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +7 -4
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.js +2 -2
- package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +12 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
- package/dist/modes/interactive/chat-input-actions.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js +0 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/fast-mode-selector.d.ts +27 -0
- package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/fast-mode-selector.js +105 -0
- package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -12
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +132 -30
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +53 -6
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +2 -2
- package/docs/docs.json +2 -2
- package/docs/extensions.md +18 -13
- package/docs/providers.md +5 -1
- package/docs/quickstart.md +5 -3
- package/docs/rpc.md +5 -5
- package/docs/sdk.md +12 -12
- package/docs/settings.md +18 -0
- package/docs/themes.md +6 -6
- package/docs/tui.md +20 -18
- package/docs/usage.md +2 -0
- package/docs/workflows.md +403 -39
- package/examples/extensions/qna.ts +2 -2
- package/package.json +4 -4
- package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +0 -392
- package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +0 -23
- package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +0 -39
- package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +0 -87
- package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +0 -241
- package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +0 -225
- package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
- package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +0 -275
- package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +0 -134
- package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +0 -139
- 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 `
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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
|
-
|
|
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 {
|
|
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
|
|
146
|
+
if (snapshot.version !== lastVersion) {
|
|
146
147
|
lastVersion = snapshot.version;
|
|
147
|
-
|
|
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
|
|
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)
|
|
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
|
-
//
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
517
|
-
|
|
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
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
|
|
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}`);
|