@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.6
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 +69 -0
- package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-gs76k6wc.md} +69 -0
- package/dist/cli.js +4438 -3962
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/cli/args.d.ts +4 -1
- package/dist/types/cli/browser-relay-cli.d.ts +15 -0
- package/dist/types/cli/command-help.d.ts +99 -0
- package/dist/types/cli/gc-cli.d.ts +1 -0
- package/dist/types/cli/help-extra.d.ts +2 -0
- package/dist/types/cli/thinking-levels.d.ts +5 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/commands/browser-relay.d.ts +38 -0
- package/dist/types/commands/commit.d.ts +3 -0
- package/dist/types/commands/complete.d.ts +1 -1
- package/dist/types/commands/completions.d.ts +6 -0
- package/dist/types/commands/grep.d.ts +3 -0
- package/dist/types/commands/join.d.ts +4 -0
- package/dist/types/commands/launch-help.d.ts +171 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/models.d.ts +3 -0
- package/dist/types/commands/say.d.ts +10 -0
- package/dist/types/commands/worktree.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-discovery.d.ts +2 -2
- package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/models-config.d.ts +1 -0
- package/dist/types/config/service-tier.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +69 -21
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/edit/index.d.ts +3 -3
- package/dist/types/edit/modes/replace.d.ts +22 -15
- package/dist/types/edit/renderer.d.ts +1 -1
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/spawn-options.d.ts +12 -16
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +15 -4
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +1 -1
- package/dist/types/launch/protocol.d.ts +19 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/mux/daemon.d.ts +15 -0
- package/dist/types/lsp/mux/protocol.d.ts +57 -0
- package/dist/types/lsp/mux/server.d.ts +18 -0
- package/dist/types/lsp/types.d.ts +22 -2
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mnemopi/embed-client.d.ts +1 -1
- package/dist/types/mnemopi/state.d.ts +0 -23
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/btw-panel.d.ts +3 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/security/contracts/schemas.d.ts +405 -403
- package/dist/types/session/agent-session-types.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +26 -2
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/launch-completion.d.ts +10 -0
- package/dist/types/session/session-advisors.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-entries.d.ts +15 -1
- package/dist/types/session/session-maintenance.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +23 -11
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/session/yield-queue.d.ts +6 -1
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/spawn-policy.d.ts +5 -0
- package/dist/types/thinking.d.ts +1 -7
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/browser/registry.d.ts +2 -1
- package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
- package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
- package/dist/types/tools/browser/relay/kind.d.ts +19 -0
- package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
- package/dist/types/tools/browser/relay/server.d.ts +35 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -0
- package/dist/types/tools/computer/exposure.d.ts +9 -7
- package/dist/types/tools/computer/protocol.d.ts +70 -17
- package/dist/types/tools/computer/supervisor.d.ts +23 -10
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +42 -7
- package/dist/types/tools/computer-renderer.d.ts +7 -5
- package/dist/types/tools/computer.d.ts +25 -44
- package/dist/types/tools/context.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +1 -1
- package/dist/types/tools/index.d.ts +9 -0
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/tools/xdev.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +2 -2
- package/dist/types/web/kagi.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/providers/anthropic.d.ts +1 -0
- package/dist/types/web/search/providers/base.d.ts +2 -0
- package/dist/types/web/search/providers/brave.d.ts +1 -0
- package/dist/types/web/search/providers/browser-page.d.ts +1 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -0
- package/dist/types/web/search/providers/exa.d.ts +1 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/jina.d.ts +1 -0
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -0
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +1 -0
- package/dist/types/web/search/providers/searxng.d.ts +1 -0
- package/dist/types/web/search/providers/tavily.d.ts +1 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +6 -9
- package/dist/types/web/search/providers/zai.d.ts +1 -0
- package/dist/types/web/search/types.d.ts +4 -0
- package/package.json +12 -12
- package/src/cli/args.ts +7 -87
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/command-help.ts +103 -0
- package/src/cli/completion-gen.ts +5 -5
- package/src/cli/flag-tables.ts +9 -0
- package/src/cli/gc-cli.ts +641 -21
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/setup-cli.ts +13 -21
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/update-cli.ts +26 -7
- package/src/cli-commands.ts +170 -33
- package/src/cli.ts +17 -5
- package/src/commands/acp.ts +3 -1
- package/src/commands/agents.ts +3 -2
- package/src/commands/auth-broker.ts +3 -2
- package/src/commands/auth-gateway.ts +3 -2
- package/src/commands/bench.ts +2 -3
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +2 -2
- package/src/commands/commit.ts +3 -2
- package/src/commands/complete.ts +2 -1
- package/src/commands/completions.ts +3 -2
- package/src/commands/config.ts +3 -2
- package/src/commands/dry-balance.ts +2 -2
- package/src/commands/gallery.ts +3 -2
- package/src/commands/gc.ts +3 -2
- package/src/commands/grep.ts +3 -2
- package/src/commands/grievances.ts +3 -2
- package/src/commands/install.ts +2 -2
- package/src/commands/join.ts +3 -2
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +7 -191
- package/src/commands/models.ts +3 -2
- package/src/commands/plugin.ts +3 -2
- package/src/commands/read.ts +3 -2
- package/src/commands/say.ts +3 -2
- package/src/commands/setup.ts +3 -2
- package/src/commands/shell.ts +3 -2
- package/src/commands/ssh.ts +3 -2
- package/src/commands/stats.ts +3 -2
- package/src/commands/tiny-models.ts +2 -2
- package/src/commands/token.ts +2 -2
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/update.ts +3 -2
- package/src/commands/usage.ts +3 -2
- package/src/commands/web-search.ts +3 -2
- package/src/commands/worktree.ts +3 -2
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-discovery.ts +44 -27
- package/src/config/model-registry.ts +19 -13
- package/src/config/models-config-schema-bundle.ts +9 -0
- package/src/config/resolve-config-value.ts +2 -1
- package/src/config/service-tier.ts +5 -0
- package/src/config/settings-schema.ts +72 -20
- package/src/config/settings.ts +1 -1
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/edit/diff.ts +17 -8
- package/src/edit/index.ts +20 -7
- package/src/edit/modes/replace.ts +93 -40
- package/src/edit/renderer.ts +1 -1
- package/src/edit/streaming.ts +14 -17
- package/src/eval/py/kernel.ts +2 -1
- package/src/eval/py/spawn-options.ts +24 -29
- package/src/export/html/index.ts +10 -3
- package/src/export/html/template.js +8 -26
- package/src/export/share.ts +4 -0
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
- package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +223 -7
- package/src/launch/client.ts +192 -21
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +1 -1
- package/src/launch/protocol.ts +54 -2
- package/src/lsp/client.ts +23 -2
- package/src/lsp/index.ts +8 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/types.ts +28 -2
- package/src/main.ts +20 -8
- package/src/mcp/config-writer.ts +1 -1
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/btw-panel.ts +41 -4
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +9 -0
- package/src/modes/components/read-tool-group.ts +10 -0
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/tool-execution.ts +8 -0
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/btw-controller.ts +55 -7
- package/src/modes/controllers/command-controller.ts +31 -0
- package/src/modes/controllers/event-controller.ts +17 -0
- package/src/modes/controllers/input-controller.ts +63 -8
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +20 -0
- package/src/modes/interactive-mode.ts +18 -2
- package/src/modes/types.ts +9 -1
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/interactive-context-helpers.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -10
- package/src/modes/workflow.ts +8 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/session/launch-completion.md +1 -0
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +26 -52
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/computer.md +21 -21
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/replace.md +5 -4
- package/src/prompts/tools/task.md +4 -4
- package/src/sdk.ts +49 -6
- package/src/secrets/index.ts +1 -1
- package/src/security/contracts/schemas.ts +205 -183
- package/src/security/contracts/validation.ts +5 -1
- package/src/security/store.ts +2 -2
- package/src/session/agent-session-types.ts +9 -1
- package/src/session/agent-session.ts +204 -15
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/launch-completion.ts +37 -0
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +2 -2
- package/src/session/session-context.ts +42 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-entries.ts +17 -1
- package/src/session/session-maintenance.ts +38 -4
- package/src/session/session-manager.ts +123 -67
- package/src/session/session-paths.ts +118 -55
- package/src/session/session-stats.ts +48 -3
- package/src/session/session-storage.ts +30 -22
- package/src/session/stream-guards.ts +1 -1
- package/src/session/yield-queue.ts +121 -16
- package/src/slash-commands/builtin-registry.ts +37 -24
- package/src/system-prompt.ts +8 -3
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +97 -14
- package/src/tools/browser/cmux/rpc.ts +2 -8
- package/src/tools/browser/registry.ts +53 -2
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +4 -1
- package/src/tools/browser/shared-daemon.ts +7 -58
- package/src/tools/browser/tab-supervisor.ts +5 -4
- package/src/tools/browser/tab-worker.ts +33 -12
- package/src/tools/browser.ts +26 -1
- package/src/tools/computer/exposure.ts +11 -35
- package/src/tools/computer/protocol.ts +51 -10
- package/src/tools/computer/supervisor.ts +227 -124
- package/src/tools/computer/worker-entry.ts +11 -6
- package/src/tools/computer/worker.ts +695 -81
- package/src/tools/computer-renderer.ts +104 -65
- package/src/tools/computer.ts +134 -428
- package/src/tools/context.ts +3 -0
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/hub/launch.ts +122 -6
- package/src/tools/index.ts +9 -0
- package/src/tools/inspect-image.ts +32 -6
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/xdev.ts +29 -2
- package/src/utils/clipboard.ts +40 -14
- package/src/utils/command-args.ts +26 -28
- package/src/utils/tool-choice.ts +0 -13
- package/src/web/kagi.ts +2 -1
- package/src/web/search/index.ts +18 -2
- package/src/web/search/providers/anthropic.ts +4 -1
- package/src/web/search/providers/base.ts +2 -0
- package/src/web/search/providers/brave.ts +3 -1
- package/src/web/search/providers/browser-page.ts +6 -4
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/duckduckgo.ts +2 -1
- package/src/web/search/providers/ecosia.ts +2 -1
- package/src/web/search/providers/exa.ts +4 -2
- package/src/web/search/providers/firecrawl.ts +3 -1
- package/src/web/search/providers/gemini.ts +8 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +12 -2
- package/src/web/search/providers/kagi.ts +3 -0
- package/src/web/search/providers/kimi.ts +5 -1
- package/src/web/search/providers/mojeek.ts +2 -1
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +6 -3
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/searxng.ts +11 -5
- package/src/web/search/providers/startpage.ts +10 -4
- package/src/web/search/providers/synthetic.ts +11 -5
- package/src/web/search/providers/tavily.ts +3 -1
- package/src/web/search/providers/tinyfish.ts +3 -1
- package/src/web/search/providers/utils.ts +12 -10
- package/src/web/search/providers/xai.ts +2 -2
- package/src/web/search/providers/zai.ts +18 -3
- package/src/web/search/types.ts +6 -0
- package/dist/types/config/file-lock.d.ts +0 -29
- package/src/config/file-lock.ts +0 -164
|
@@ -102,39 +102,42 @@ function formatFastModeStatus(session: AgentSession): string {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/** Detailed, session-effective `/computer status` diagnostics. */
|
|
105
|
-
function formatComputerUseStatus(session: AgentSession): string {
|
|
105
|
+
async function formatComputerUseStatus(session: AgentSession): Promise<string> {
|
|
106
106
|
const enabled = session.settings.get("computer.enabled");
|
|
107
107
|
const active = session.getEnabledToolNames().includes("computer");
|
|
108
108
|
const model = session.model;
|
|
109
109
|
const modelName = model ? formatModelString(model) : "none";
|
|
110
|
-
const exposure = !enabled
|
|
111
|
-
|
|
110
|
+
const exposure = !enabled
|
|
111
|
+
? "not exposed (disabled)"
|
|
112
|
+
: !active
|
|
113
|
+
? "not exposed (tool inactive)"
|
|
114
|
+
: computerExposureMode(model);
|
|
112
115
|
const configured = {
|
|
113
|
-
backend: session.settings.get("computer.backend"),
|
|
114
116
|
display: session.settings.get("computer.display"),
|
|
115
117
|
maxWidth: session.settings.get("computer.maxWidth"),
|
|
116
118
|
maxHeight: session.settings.get("computer.maxHeight"),
|
|
117
119
|
};
|
|
118
|
-
const computerTool =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
const computerTool = active
|
|
121
|
+
? (session.getToolByName("computer") as Pick<ComputerTool, "capabilities"> | undefined)
|
|
122
|
+
: undefined;
|
|
123
|
+
const capabilities = await computerTool?.capabilities();
|
|
124
|
+
const capabilityStatus = capabilities
|
|
125
|
+
? [
|
|
126
|
+
`backend=${capabilities.backend}${capabilities.displayServer ? `/${capabilities.displayServer}` : ""}`,
|
|
127
|
+
`capture=${capabilities.capture} (${capabilities.capturePermission})`,
|
|
128
|
+
`input=${capabilities.input} (${capabilities.inputPermission})`,
|
|
129
|
+
`ax=${capabilities.ax} (${capabilities.axPermission})`,
|
|
130
|
+
`backgroundWindowInput=${capabilities.backgroundWindowInput}`,
|
|
131
|
+
`deliveryModes=${capabilities.deliveryModes.join(",") || "none"}`,
|
|
132
|
+
].join(", ")
|
|
133
|
+
: "session not started";
|
|
125
134
|
return [
|
|
126
135
|
`Computer use: ${enabled ? "enabled" : "disabled"}`,
|
|
127
|
-
`tool: ${
|
|
128
|
-
`backend: ${effective.backend}`,
|
|
129
|
-
`display: ${effective.display}`,
|
|
130
|
-
`capture: ${effective.maxWidth}×${effective.maxHeight}`,
|
|
131
|
-
...(configurationChanged
|
|
132
|
-
? [
|
|
133
|
-
`next-session settings: backend=${configured.backend}, display=${configured.display}, capture=${configured.maxWidth}×${configured.maxHeight}`,
|
|
134
|
-
]
|
|
135
|
-
: []),
|
|
136
|
-
`model: ${modelName}`,
|
|
136
|
+
`tool: ${active ? "active" : "inactive"}`,
|
|
137
137
|
`exposure: ${exposure}`,
|
|
138
|
+
`model: ${modelName}`,
|
|
139
|
+
`configured: display=${configured.display}, maxWidth=${configured.maxWidth}, maxHeight=${configured.maxHeight}`,
|
|
140
|
+
`capabilities: ${capabilityStatus}`,
|
|
138
141
|
].join(" · ");
|
|
139
142
|
}
|
|
140
143
|
|
|
@@ -151,7 +154,7 @@ async function applyComputerUseToggle(session: AgentSession, enable: boolean): P
|
|
|
151
154
|
}
|
|
152
155
|
session.settings.override("computer.enabled", enable);
|
|
153
156
|
return enable
|
|
154
|
-
? `Computer use enabled for this session. ${formatComputerUseStatus(session)}`
|
|
157
|
+
? `Computer use enabled for this session. ${await formatComputerUseStatus(session)}`
|
|
155
158
|
: "Computer use disabled for this session.";
|
|
156
159
|
}
|
|
157
160
|
|
|
@@ -673,7 +676,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
673
676
|
handle: async (command, runtime) => {
|
|
674
677
|
const arg = command.args.trim().toLowerCase();
|
|
675
678
|
if (arg === "status") {
|
|
676
|
-
await runtime.output(formatComputerUseStatus(runtime.session));
|
|
679
|
+
await runtime.output(await formatComputerUseStatus(runtime.session));
|
|
677
680
|
return commandConsumed();
|
|
678
681
|
}
|
|
679
682
|
if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
|
|
@@ -686,7 +689,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
686
689
|
handleTui: async (command, runtime) => {
|
|
687
690
|
const arg = command.args.trim().toLowerCase();
|
|
688
691
|
if (arg === "status") {
|
|
689
|
-
runtime.ctx.showStatus(formatComputerUseStatus(runtime.ctx.session));
|
|
692
|
+
runtime.ctx.showStatus(await formatComputerUseStatus(runtime.ctx.session));
|
|
690
693
|
runtime.ctx.editor.setText("");
|
|
691
694
|
return;
|
|
692
695
|
}
|
|
@@ -1716,6 +1719,16 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1716
1719
|
await runtime.ctx.handleFreshCommand();
|
|
1717
1720
|
},
|
|
1718
1721
|
},
|
|
1722
|
+
{
|
|
1723
|
+
name: "reset",
|
|
1724
|
+
description: "Reset the conversation context in place, keeping the session",
|
|
1725
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1726
|
+
runtime.ctx.session.isStreaming ? "Reset: unavailable while streaming" : "Reset: drop context, keep session",
|
|
1727
|
+
handleTui: async (_command, runtime) => {
|
|
1728
|
+
runtime.ctx.editor.setText("");
|
|
1729
|
+
await runtime.ctx.handleResetContextCommand();
|
|
1730
|
+
},
|
|
1731
|
+
},
|
|
1719
1732
|
{
|
|
1720
1733
|
name: "drop",
|
|
1721
1734
|
description: "Delete the current session and start a new one",
|
package/src/system-prompt.ts
CHANGED
|
@@ -498,7 +498,8 @@ export interface BuildSystemPromptOptions {
|
|
|
498
498
|
/**
|
|
499
499
|
* Whether provider-native tool calling is active (no owned/in-band syntax).
|
|
500
500
|
* When true and `inlineToolDescriptors` is false, the inventory renders as a
|
|
501
|
-
* compact tool-name list; otherwise it renders full
|
|
501
|
+
* compact tool-name list; otherwise it renders the full Harmony-style
|
|
502
|
+
* `namespace functions { … }` catalog. Default: true
|
|
502
503
|
*/
|
|
503
504
|
nativeTools?: boolean;
|
|
504
505
|
/** Skills settings for discovery. */
|
|
@@ -525,6 +526,9 @@ export interface BuildSystemPromptOptions {
|
|
|
525
526
|
taskMaxConcurrency?: number;
|
|
526
527
|
/** Whether IRC-backed parallel coordination can be included in delegation policy. */
|
|
527
528
|
taskIrcEnabled?: boolean;
|
|
529
|
+
/** Whether the read-only `scout` subagent is spawnable (not disabled, allowed by spawn policy). Defaults to true. */
|
|
530
|
+
scoutAvailable?: boolean;
|
|
531
|
+
|
|
528
532
|
/** Rules with alwaysApply=true — their full content is injected into the prompt. */
|
|
529
533
|
alwaysApplyRules?: AlwaysApplyRule[];
|
|
530
534
|
/** Whether secret obfuscation is active. When true, explains the redaction format in the prompt. */
|
|
@@ -599,6 +603,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
599
603
|
taskIrcEnabled = false,
|
|
600
604
|
secretsEnabled = false,
|
|
601
605
|
workspaceTree: providedWorkspaceTree,
|
|
606
|
+
scoutAvailable = true,
|
|
602
607
|
memoryRootEnabled = false,
|
|
603
608
|
securityEnabled = false,
|
|
604
609
|
model,
|
|
@@ -793,7 +798,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
793
798
|
|
|
794
799
|
// List mode shows a compact tool-name list; it only applies when descriptors
|
|
795
800
|
// stay in provider-native tool schemas AND native tool calling is active.
|
|
796
|
-
// Otherwise render full
|
|
801
|
+
// Otherwise render the full functions-namespace catalog in the system prompt.
|
|
797
802
|
const toolListMode = !inlineToolDescriptors && nativeTools;
|
|
798
803
|
// Build tool descriptions for system prompt rendering.
|
|
799
804
|
const toolPromptNames = new Map<string, string>(toolNames.map(name => [name, tools?.get(name)?.wireName ?? name]));
|
|
@@ -826,7 +831,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
826
831
|
examples: meta?.examples,
|
|
827
832
|
};
|
|
828
833
|
}),
|
|
829
|
-
model ?? "",
|
|
830
834
|
);
|
|
831
835
|
|
|
832
836
|
// Filter skills for the rendered system prompt:
|
|
@@ -879,6 +883,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
879
883
|
eagerTasksAlways,
|
|
880
884
|
taskBatch,
|
|
881
885
|
MAX_CONCURRENCY: normalizeConcurrencyLimit(taskMaxConcurrency),
|
|
886
|
+
scoutAvailable,
|
|
882
887
|
taskIrcEnabled,
|
|
883
888
|
secretsEnabled,
|
|
884
889
|
hasMemoryRoot: memoryRootEnabled,
|
package/src/task/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { TASK_EFFORTS, type TaskEffort } from "../thinking";
|
|
|
27
27
|
import { truncateForPrompt } from "../tools/approval";
|
|
28
28
|
import { isIrcEnabled } from "../tools/hub";
|
|
29
29
|
import { formatBytes, formatDuration } from "../tools/render-utils";
|
|
30
|
-
import { resolveSpawnPolicy } from "./spawn-policy";
|
|
30
|
+
import { isScoutSpawnable, resolveSpawnPolicy } from "./spawn-policy";
|
|
31
31
|
import {
|
|
32
32
|
type AgentDefinition,
|
|
33
33
|
type AgentProgress,
|
|
@@ -188,8 +188,10 @@ function renderDescription(options: TaskDescriptionOptions): string {
|
|
|
188
188
|
readOnly: isReadOnlyAgent(agent),
|
|
189
189
|
blocking: agent.blocking === true,
|
|
190
190
|
}));
|
|
191
|
+
const scoutAvailable = isScoutSpawnable(options.disabledAgents, options.parentSpawns);
|
|
191
192
|
return prompt.render(taskDescriptionTemplate, {
|
|
192
193
|
agents: renderedAgents,
|
|
194
|
+
scoutAvailable,
|
|
193
195
|
spawningDisabled,
|
|
194
196
|
defaultAgent: spawnPolicy.defaultAgent,
|
|
195
197
|
isolationEnabled: options.isolationEnabled,
|
|
@@ -398,15 +400,19 @@ const GENERIC_SPAWN_AGENTS: ReadonlySet<string> = new Set(["task", "sonic"]);
|
|
|
398
400
|
* (DepthCapacity: it currently has the `task` tool). `agentNames` are the
|
|
399
401
|
* per-item resolved agent types. Returns undefined when no nudge applies.
|
|
400
402
|
*/
|
|
401
|
-
export function buildSpecializationAdvisory(
|
|
403
|
+
export function buildSpecializationAdvisory(
|
|
404
|
+
agentNames: string[],
|
|
405
|
+
depthCapacity: boolean,
|
|
406
|
+
scoutAvailable = true,
|
|
407
|
+
): string | undefined {
|
|
402
408
|
if (!depthCapacity) return undefined;
|
|
403
409
|
const generics = agentNames.filter(name => GENERIC_SPAWN_AGENTS.has(name));
|
|
404
410
|
if (generics.length < 2) return undefined;
|
|
405
|
-
|
|
406
|
-
`
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
411
|
+
const specialist = scoutAvailable
|
|
412
|
+
? `Check the agent list for a closer specialist type — e.g. read-only research belongs on ` +
|
|
413
|
+
`\`agent: "scout"\`, which runs on a faster model.`
|
|
414
|
+
: `Check the agent list for a closer specialist type.`;
|
|
415
|
+
return `Tip: this call spawned ${generics.length} generic \`${generics[0]}\` workers. ${specialist}`;
|
|
410
416
|
}
|
|
411
417
|
|
|
412
418
|
/**
|
|
@@ -443,10 +449,11 @@ export function composeSpawnAdvisory(args: {
|
|
|
443
449
|
depthCapacity: boolean;
|
|
444
450
|
ircEnabled: boolean;
|
|
445
451
|
willRunAsync: boolean;
|
|
452
|
+
scoutAvailable?: boolean;
|
|
446
453
|
}): string | undefined {
|
|
447
454
|
return (
|
|
448
455
|
[
|
|
449
|
-
buildSpecializationAdvisory(args.agents, args.depthCapacity),
|
|
456
|
+
buildSpecializationAdvisory(args.agents, args.depthCapacity, args.scoutAvailable),
|
|
450
457
|
args.willRunAsync ? buildCoordinationAdvisory(args.items, args.depthCapacity, args.ircEnabled) : undefined,
|
|
451
458
|
]
|
|
452
459
|
.filter(Boolean)
|
|
@@ -751,6 +758,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
751
758
|
depthCapacity,
|
|
752
759
|
ircEnabled,
|
|
753
760
|
willRunAsync: false,
|
|
761
|
+
scoutAvailable: isScoutSpawnable(
|
|
762
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
763
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
764
|
+
),
|
|
754
765
|
});
|
|
755
766
|
const result = await this.#executeSyncFanout(
|
|
756
767
|
toolCallId,
|
|
@@ -784,6 +795,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
784
795
|
depthCapacity,
|
|
785
796
|
ircEnabled,
|
|
786
797
|
willRunAsync: asyncItems.length > 0,
|
|
798
|
+
scoutAvailable: isScoutSpawnable(
|
|
799
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
800
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
801
|
+
),
|
|
787
802
|
});
|
|
788
803
|
// Returns a fresh result (copied content array, copied text part) rather
|
|
789
804
|
// than mutating the caller's — task results are short-lived here, but an
|
package/src/task/spawn-policy.ts
CHANGED
|
@@ -56,3 +56,17 @@ export function resolveSpawnPolicy(parentSpawns: string | boolean | null | undef
|
|
|
56
56
|
allowedPromptText: allowedAgents.map(agent => `\`${agent}\``).join(", "),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Whether the `scout` agent is spawnable in a session: not disabled via
|
|
62
|
+
* `task.disabledAgents`, and permitted by the session spawn policy.
|
|
63
|
+
*/
|
|
64
|
+
export function isScoutSpawnable(
|
|
65
|
+
disabledAgents: readonly string[] | undefined,
|
|
66
|
+
spawns: string | boolean | null | undefined,
|
|
67
|
+
): boolean {
|
|
68
|
+
if (disabledAgents?.includes("scout")) return false;
|
|
69
|
+
const policy = resolveSpawnPolicy(spawns);
|
|
70
|
+
if (!policy.enabled) return false;
|
|
71
|
+
return policy.allowedAgents === null || policy.allowedAgents.includes("scout");
|
|
72
|
+
}
|
package/src/thinking.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { Effort, type Model, THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
|
|
|
3
3
|
import { clampThinkingLevelForModel, getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
4
4
|
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
5
5
|
|
|
6
|
+
export { CLI_THINKING_LEVELS } from "./cli/thinking-levels";
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Metadata used to render thinking selector values in the coding-agent UI.
|
|
8
10
|
*/
|
|
@@ -209,14 +211,6 @@ export function getConfiguredThinkingLevelMetadata(level: ConfiguredThinkingLeve
|
|
|
209
211
|
return level === AUTO_THINKING ? AUTO_THINKING_METADATA : getThinkingLevelMetadata(level);
|
|
210
212
|
}
|
|
211
213
|
|
|
212
|
-
/**
|
|
213
|
-
* Thinking selectors accepted by the `--thinking` CLI flag, in display order:
|
|
214
|
-
* `off`, every concrete effort (`minimal`..`max`), then `auto`. Single source
|
|
215
|
-
* for the flag's `options` list, shell completions, and the "invalid level"
|
|
216
|
-
* warning so all three stay in sync.
|
|
217
|
-
*/
|
|
218
|
-
export const CLI_THINKING_LEVELS: readonly string[] = [ThinkingLevel.Off, ...THINKING_EFFORTS, AUTO_THINKING];
|
|
219
|
-
|
|
220
214
|
/**
|
|
221
215
|
* Parses a `--thinking` CLI value. Accepts every {@link parseConfiguredThinkingLevel}
|
|
222
216
|
* selector (`off`, `auto`, `minimal`..`max`) but rejects
|
package/src/tools/ast-grep.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { recordFileSnapshot, recordSeenLinesFromBody } from "../edit/file-snapsh
|
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
12
|
import type { Theme } from "../modes/theme/theme";
|
|
13
13
|
import astGrepDescription from "../prompts/tools/ast-grep.md" with { type: "text" };
|
|
14
|
+
import { isScoutSpawnable } from "../task/spawn-policy";
|
|
14
15
|
import { Ellipsis, fileHyperlink, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
15
16
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
16
17
|
import type { ToolSession } from ".";
|
|
@@ -150,7 +151,14 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
150
151
|
readonly approval = "read" as const;
|
|
151
152
|
readonly label = "AST Grep";
|
|
152
153
|
readonly summary = "Search code with AST patterns (structural grep)";
|
|
153
|
-
|
|
154
|
+
get description(): string {
|
|
155
|
+
return prompt.render(astGrepDescription, {
|
|
156
|
+
scoutAvailable: isScoutSpawnable(
|
|
157
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
158
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
159
|
+
),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
154
162
|
readonly parameters = astGrepSchema;
|
|
155
163
|
readonly strict = true;
|
|
156
164
|
|
|
@@ -178,9 +186,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
|
|
|
178
186
|
];
|
|
179
187
|
readonly loadMode = "discoverable";
|
|
180
188
|
|
|
181
|
-
constructor(private readonly session: ToolSession) {
|
|
182
|
-
this.description = prompt.render(astGrepDescription);
|
|
183
|
-
}
|
|
189
|
+
constructor(private readonly session: ToolSession) {}
|
|
184
190
|
|
|
185
191
|
async execute(
|
|
186
192
|
_toolCallId: string,
|
|
@@ -8,16 +8,11 @@ import { resizeImage } from "../../../utils/image-resize";
|
|
|
8
8
|
import type { ToolSession } from "../../index";
|
|
9
9
|
import { resolveToCwd } from "../../path-utils";
|
|
10
10
|
import { formatScreenshot } from "../../render-utils";
|
|
11
|
+
import { bindRunFacade, resolvePredicateTimeout, type WaitPredicateOptions, waitForRun } from "../../run-scope";
|
|
11
12
|
import { ToolAbortError, ToolError, throwIfAborted } from "../../tool-errors";
|
|
12
13
|
import { type AriaSnapshotOptions, assertSelectorString, buildAriaSnapshotScript } from "../aria/aria-snapshot";
|
|
13
14
|
import { DEFAULT_VIEWPORT } from "../launch";
|
|
14
15
|
import { extractReadableFromHtml, type ReadableFormat } from "../readable";
|
|
15
|
-
import {
|
|
16
|
-
bindBrowserRunFacade,
|
|
17
|
-
resolvePredicateTimeout,
|
|
18
|
-
type WaitPredicateOptions,
|
|
19
|
-
waitForBrowserRun,
|
|
20
|
-
} from "../run-cancellation";
|
|
21
16
|
import { cloneSafe, RunOutput } from "../run-output";
|
|
22
17
|
import type { Observation, ReadyInfo, RunResultOk, ScreenshotResult, SessionSnapshot } from "../tab-protocol";
|
|
23
18
|
import {
|
|
@@ -259,6 +254,65 @@ export interface RunCmuxCodeOptions {
|
|
|
259
254
|
snapshot: SessionSnapshot;
|
|
260
255
|
}
|
|
261
256
|
|
|
257
|
+
interface ActiveCmuxRun {
|
|
258
|
+
filename: string;
|
|
259
|
+
floatingRejections: unknown[];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const RECENT_CMUX_RUN_FILES_MAX = 256;
|
|
263
|
+
const activeCmuxRuns = new Map<string, ActiveCmuxRun>();
|
|
264
|
+
const recentCmuxRunFiles = new Set<string>();
|
|
265
|
+
|
|
266
|
+
function consumeCmuxRunRejection(reason: unknown): boolean {
|
|
267
|
+
// cmux runs guest JS in the shared main-process realm (TTS/STT/MCP and other
|
|
268
|
+
// subsystems live here too), so — like the eval inline fallback — only a
|
|
269
|
+
// guest-file stack frame can safely attribute a rejection. A stackless or
|
|
270
|
+
// non-run-stack reason is indistinguishable from a subsystem failure and
|
|
271
|
+
// keeps the default fatal path; worker isolation is the long-term fix.
|
|
272
|
+
const stack = reason instanceof Error && typeof reason.stack === "string" ? reason.stack : undefined;
|
|
273
|
+
if (!stack) return false;
|
|
274
|
+
|
|
275
|
+
let owner: ActiveCmuxRun | undefined;
|
|
276
|
+
let ownerIndex = -1;
|
|
277
|
+
for (const run of activeCmuxRuns.values()) {
|
|
278
|
+
const index = stack.lastIndexOf(run.filename);
|
|
279
|
+
if (index > ownerIndex) {
|
|
280
|
+
ownerIndex = index;
|
|
281
|
+
owner = run;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (owner) {
|
|
285
|
+
owner.floatingRejections.push(reason);
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let recent: string | undefined;
|
|
290
|
+
let recentIndex = -1;
|
|
291
|
+
for (const filename of recentCmuxRunFiles) {
|
|
292
|
+
const index = stack.lastIndexOf(filename);
|
|
293
|
+
if (index > recentIndex) {
|
|
294
|
+
recentIndex = index;
|
|
295
|
+
recent = filename;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (!recent) return false;
|
|
299
|
+
logger.warn("Unhandled rejection from a finished cmux browser run (missing await?)", {
|
|
300
|
+
filename: recent,
|
|
301
|
+
error: reason,
|
|
302
|
+
});
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function rememberCmuxRunFile(filename: string): void {
|
|
307
|
+
recentCmuxRunFiles.delete(filename);
|
|
308
|
+
recentCmuxRunFiles.add(filename);
|
|
309
|
+
if (recentCmuxRunFiles.size <= RECENT_CMUX_RUN_FILES_MAX) return;
|
|
310
|
+
const oldest = recentCmuxRunFiles.values().next().value;
|
|
311
|
+
if (oldest !== undefined) recentCmuxRunFiles.delete(oldest);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
postmortem.interceptUnhandledRejections(consumeCmuxRunRejection);
|
|
315
|
+
|
|
262
316
|
export class CmuxTab {
|
|
263
317
|
readonly #client: CmuxSocketClient;
|
|
264
318
|
readonly #surfaceId: string;
|
|
@@ -1317,6 +1371,9 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1317
1371
|
const output = new RunOutput();
|
|
1318
1372
|
const screenshots: ScreenshotResult[] = [];
|
|
1319
1373
|
const runId = crypto.randomUUID();
|
|
1374
|
+
const filename = `cmux-run-${runId}.js`;
|
|
1375
|
+
const activeRun: ActiveCmuxRun = { filename, floatingRejections: [] };
|
|
1376
|
+
activeCmuxRuns.set(filename, activeRun);
|
|
1320
1377
|
tab.setRunContext({ session: opts.snapshot, output, screenshots, signal, timeoutMs: opts.timeoutMs });
|
|
1321
1378
|
|
|
1322
1379
|
const { promise: cancelRejection, reject } = Promise.withResolvers<never>();
|
|
@@ -1345,16 +1402,16 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1345
1402
|
// Keep both inside try so a concurrent in-process eval/browser run surfaces as
|
|
1346
1403
|
// a rejected promise the supervisor can report, never an unhandled rejection.
|
|
1347
1404
|
runtime.setCwd(opts.snapshot.cwd);
|
|
1348
|
-
const runTab =
|
|
1405
|
+
const runTab = bindRunFacade(tab, signal);
|
|
1349
1406
|
runtime.setRunScope({
|
|
1350
|
-
page:
|
|
1351
|
-
browser:
|
|
1407
|
+
page: bindRunFacade(tab.page, signal),
|
|
1408
|
+
browser: bindRunFacade(tab.browser, signal),
|
|
1352
1409
|
tab: runTab,
|
|
1353
1410
|
assert: (cond: unknown, text?: string): void => {
|
|
1354
1411
|
if (!cond) throw new ToolError(text ?? "Assertion failed");
|
|
1355
1412
|
},
|
|
1356
1413
|
wait: (msOrPredicate: number | (() => unknown), waitOpts?: WaitPredicateOptions): Promise<unknown> =>
|
|
1357
|
-
|
|
1414
|
+
waitForRun(
|
|
1358
1415
|
msOrPredicate,
|
|
1359
1416
|
signal,
|
|
1360
1417
|
typeof msOrPredicate === "number"
|
|
@@ -1383,14 +1440,40 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
|
|
|
1383
1440
|
};
|
|
1384
1441
|
// Like the inline worker fallback, cmux runs user JS in-process: awaited cmux/tool calls
|
|
1385
1442
|
// observe this abort signal, but a synchronous infinite loop cannot be interrupted here.
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1443
|
+
let returnValue: unknown;
|
|
1444
|
+
let runError: unknown;
|
|
1445
|
+
let runFailed = false;
|
|
1446
|
+
try {
|
|
1447
|
+
returnValue = await Promise.race([
|
|
1448
|
+
runtime.run(opts.code, filename, hooks, { runId, cwd: opts.snapshot.cwd }),
|
|
1449
|
+
cancelRejection,
|
|
1450
|
+
]);
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
runFailed = true;
|
|
1453
|
+
runError = error;
|
|
1454
|
+
}
|
|
1455
|
+
// Let rejection callbacks run while this run can still own guest-created promises.
|
|
1456
|
+
await Bun.sleep(0);
|
|
1457
|
+
if (runFailed) {
|
|
1458
|
+
for (const reason of activeRun.floatingRejections) {
|
|
1459
|
+
logger.warn("Unhandled rejection accompanied a failed cmux browser run", { filename, error: reason });
|
|
1460
|
+
}
|
|
1461
|
+
throw runError;
|
|
1462
|
+
}
|
|
1463
|
+
if (activeRun.floatingRejections.length > 0) {
|
|
1464
|
+
const messages = activeRun.floatingRejections.map(reason =>
|
|
1465
|
+
reason instanceof Error ? reason.message : String(reason),
|
|
1466
|
+
);
|
|
1467
|
+
throw new ToolError(`Unhandled rejection (missing await?): ${messages.join("\n[unhandled rejection] ")}`, {
|
|
1468
|
+
rejections: activeRun.floatingRejections,
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1390
1471
|
return { displays: output.finish(), returnValue: cloneSafe(returnValue), screenshots };
|
|
1391
1472
|
} finally {
|
|
1392
1473
|
signal.removeEventListener("abort", onAbort);
|
|
1393
1474
|
runAc.abort(postmortem.markExpectedCleanupError(new ToolAbortError("Browser run ended")));
|
|
1475
|
+
activeCmuxRuns.delete(filename);
|
|
1476
|
+
rememberCmuxRunFile(filename);
|
|
1394
1477
|
tab.clearRunContext();
|
|
1395
1478
|
}
|
|
1396
1479
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseFlag } from "@oh-my-pi/pi-utils";
|
|
1
2
|
import { ToolError } from "../../tool-errors";
|
|
2
3
|
import type { Observation, ObservationEntry } from "../tab-protocol";
|
|
3
4
|
|
|
@@ -174,13 +175,6 @@ export function mapWaitUntil(waitUntil: string | undefined): "interactive" | "co
|
|
|
174
175
|
return waitUntil === "domcontentloaded" ? "interactive" : "complete";
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
const TRUTHY_ENV_VALUES = new Set(["1", "Y", "y", "TRUE", "true", "YES", "yes", "ON", "on"]);
|
|
178
|
-
|
|
179
|
-
function resolveCmuxEnabled(envValue: string | undefined, settingEnabled: boolean): boolean {
|
|
180
|
-
if (!envValue) return settingEnabled;
|
|
181
|
-
return TRUTHY_ENV_VALUES.has(envValue);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
178
|
export interface ResolveCmuxKindOptions {
|
|
185
179
|
surface?: string;
|
|
186
180
|
settingEnabled?: boolean;
|
|
@@ -190,7 +184,7 @@ export function resolveCmuxKind(
|
|
|
190
184
|
options?: ResolveCmuxKindOptions | null,
|
|
191
185
|
env: Record<string, string | undefined> = process.env,
|
|
192
186
|
): CmuxKind | null {
|
|
193
|
-
if (!
|
|
187
|
+
if (!parseFlag(env.PI_BROWSER_CMUX, options?.settingEnabled ?? true)) {
|
|
194
188
|
return null;
|
|
195
189
|
}
|
|
196
190
|
const socketPath = env.CMUX_SOCKET_PATH;
|
|
@@ -14,12 +14,15 @@ import {
|
|
|
14
14
|
removeUserDataDir,
|
|
15
15
|
type UserAgentOverride,
|
|
16
16
|
} from "./launch";
|
|
17
|
+
import { ensureRelayDaemon, isLoopbackRelayUrl } from "./relay/daemon";
|
|
18
|
+
import type { RelayKind } from "./relay/kind";
|
|
17
19
|
import { ensureSharedBrowser } from "./shared-daemon";
|
|
18
20
|
|
|
19
21
|
export type PuppeteerBrowserKind =
|
|
20
22
|
| { kind: "headless"; headless: boolean }
|
|
21
23
|
| { kind: "spawned"; path: string }
|
|
22
|
-
| { kind: "connected"; cdpUrl: string }
|
|
24
|
+
| { kind: "connected"; cdpUrl: string }
|
|
25
|
+
| RelayKind;
|
|
23
26
|
|
|
24
27
|
export type BrowserKind = PuppeteerBrowserKind | CmuxKind;
|
|
25
28
|
|
|
@@ -31,6 +34,12 @@ export type BrowserKindTag = BrowserKind["kind"];
|
|
|
31
34
|
* forever (issue #5260), so we cap the wait and force-kill on timeout.
|
|
32
35
|
*/
|
|
33
36
|
const HEADLESS_CLOSE_TIMEOUT_MS = 5_000;
|
|
37
|
+
/**
|
|
38
|
+
* How long a relay open waits for the extension handshake (503 → 200). A
|
|
39
|
+
* reaped extension service worker is revived by its 30s keepalive alarm, so
|
|
40
|
+
* the wait must cover one full alarm period plus the dial.
|
|
41
|
+
*/
|
|
42
|
+
const RELAY_EXTENSION_WAIT_MS = 35_000;
|
|
34
43
|
|
|
35
44
|
interface BrowserHandleCommon {
|
|
36
45
|
key: string;
|
|
@@ -78,6 +87,8 @@ function browserKey(kind: BrowserKind): string {
|
|
|
78
87
|
return `spawned:${kind.path}`;
|
|
79
88
|
case "connected":
|
|
80
89
|
return `connected:${kind.cdpUrl}`;
|
|
90
|
+
case "relay":
|
|
91
|
+
return `relay:${kind.cdpUrl}`;
|
|
81
92
|
case "cmux":
|
|
82
93
|
return `cmux:${kind.socketPath}`;
|
|
83
94
|
}
|
|
@@ -201,6 +212,45 @@ async function openBrowserHandle(kind: BrowserKind, opts: AcquireBrowserOptions)
|
|
|
201
212
|
stealth: { browserSession: null, override: null },
|
|
202
213
|
};
|
|
203
214
|
}
|
|
215
|
+
if (kind.kind === "relay") {
|
|
216
|
+
const cdpUrl = normalizeConnectedCdpUrl(kind.cdpUrl);
|
|
217
|
+
// Loopback relays are owned by a machine-global broker and auto-started
|
|
218
|
+
// on demand (the extension dials in on its own). Hosts without a CLI
|
|
219
|
+
// worker entry (bun test, SDK embedding) never spawn brokers. Remote
|
|
220
|
+
// relay URLs must already be serving.
|
|
221
|
+
let autoStarted = false;
|
|
222
|
+
if (isLoopbackRelayUrl(cdpUrl) && (isCompiledBinary() || workerHostEntry() !== null)) {
|
|
223
|
+
autoStarted = await ensureRelayDaemon({ cdpUrl, signal: opts.signal });
|
|
224
|
+
}
|
|
225
|
+
// The relay answers /json/version with 503 until its extension dials in.
|
|
226
|
+
// A freshly revived extension service worker can take up to ~30s (its
|
|
227
|
+
// keepalive alarm) to reconnect, so give the handshake that long.
|
|
228
|
+
try {
|
|
229
|
+
await waitForCdp(cdpUrl, RELAY_EXTENSION_WAIT_MS, opts.signal);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
if (err instanceof ToolAbortError) throw err;
|
|
232
|
+
if (err instanceof Error && err.name === "AbortError") throw err;
|
|
233
|
+
throw new ToolError(
|
|
234
|
+
autoStarted
|
|
235
|
+
? `omp browser relay is serving at ${cdpUrl} but its extension never connected. Install it with \`omp browser-relay install\` and check the toolbar badge shows "on".`
|
|
236
|
+
: `omp browser relay is not reachable at ${cdpUrl}. Start it with \`omp browser-relay\` (or check the endpoint), and make sure the OMP Browser Relay extension is loaded in Chrome.`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
const puppeteer = await loadPuppeteer();
|
|
240
|
+
const browser = await puppeteer.connect({
|
|
241
|
+
browserURL: cdpUrl,
|
|
242
|
+
defaultViewport: null,
|
|
243
|
+
protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
key: browserKey(kind),
|
|
247
|
+
kind,
|
|
248
|
+
browser,
|
|
249
|
+
cdpUrl,
|
|
250
|
+
refCount: 0,
|
|
251
|
+
stealth: { browserSession: null, override: null },
|
|
252
|
+
};
|
|
253
|
+
}
|
|
204
254
|
|
|
205
255
|
const exe = kind.path;
|
|
206
256
|
if (!path.isAbsolute(exe)) {
|
|
@@ -320,7 +370,8 @@ async function disposeBrowserHandle(handle: BrowserHandle, opts: ReleaseBrowserO
|
|
|
320
370
|
if (handle.userDataDir) await removeUserDataDir(handle.userDataDir);
|
|
321
371
|
return;
|
|
322
372
|
}
|
|
323
|
-
|
|
373
|
+
// Connected and relay browsers belong to the user: drop our CDP link, never kill.
|
|
374
|
+
if (handle.kind.kind === "connected" || handle.kind.kind === "relay") {
|
|
324
375
|
if (handle.browser.connected) {
|
|
325
376
|
try {
|
|
326
377
|
handle.browser.disconnect();
|