@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.5
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 +51 -0
- package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-q2w43aw3.md} +51 -0
- package/dist/cli.js +4148 -3717
- 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/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 +6 -3
- 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 +2 -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/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/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- 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/session/agent-session-types.d.ts +3 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -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-maintenance.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +16 -11
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- 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/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/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 +30 -13
- package/src/config/model-registry.ts +9 -5
- 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/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/template.js +8 -26
- 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 +1 -1
- package/src/launch/client.ts +32 -13
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +1 -1
- package/src/launch/protocol.ts +2 -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 +7 -2
- 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/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/event-controller.ts +17 -0
- package/src/modes/controllers/input-controller.ts +39 -1
- 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 +2 -0
- package/src/modes/types.ts +1 -0
- 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/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 +34 -6
- package/src/secrets/index.ts +1 -1
- package/src/session/agent-session-types.ts +3 -1
- package/src/session/agent-session.ts +13 -1
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +2 -2
- package/src/session/session-context.ts +16 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-maintenance.ts +38 -4
- package/src/session/session-manager.ts +102 -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/slash-commands/builtin-registry.ts +27 -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 +5 -10
- 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/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/src/tools/computer.ts
CHANGED
|
@@ -5,22 +5,20 @@ import type {
|
|
|
5
5
|
AgentToolUpdateCallback,
|
|
6
6
|
ToolApprovalDecision,
|
|
7
7
|
} from "@oh-my-pi/pi-agent-core";
|
|
8
|
-
import type {
|
|
8
|
+
import type { Model } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import { isClaudeModelId } from "@oh-my-pi/pi-catalog/identity";
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
DesktopCapabilities,
|
|
13
|
-
DesktopCapture,
|
|
14
|
-
DesktopDisplay,
|
|
15
|
-
DesktopSessionOptions,
|
|
16
|
-
} from "@oh-my-pi/pi-natives";
|
|
17
|
-
import { once, prompt, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
10
|
+
import type { DesktopCapabilities } from "@oh-my-pi/pi-natives";
|
|
11
|
+
import { once, prompt } from "@oh-my-pi/pi-utils";
|
|
18
12
|
import { type Type, type } from "arktype";
|
|
13
|
+
import { callSessionTool } from "../eval/js/tool-bridge";
|
|
19
14
|
import computerDescription from "../prompts/tools/computer.md" with { type: "text" };
|
|
15
|
+
import { enforceInlineByteCap } from "../session/streaming-output";
|
|
20
16
|
import { truncateForPrompt } from "./approval";
|
|
17
|
+
import type { ComputerScreenshot, ComputerSessionSnapshot } from "./computer/protocol";
|
|
21
18
|
import { type ComputerController, ComputerSupervisor, registerComputerController } from "./computer/supervisor";
|
|
22
19
|
import type { ToolSession } from "./index";
|
|
23
20
|
import { ToolError, throwIfAborted } from "./tool-errors";
|
|
21
|
+
import { clampTimeout } from "./tool-timeouts";
|
|
24
22
|
|
|
25
23
|
// Image transports that cannot preserve native screenshot detail resize frames
|
|
26
24
|
// without returning transformed dimensions. Keep their native coordinate frames
|
|
@@ -41,470 +39,149 @@ function usesCoordinateSafeImageSizing(model: Model | undefined): boolean {
|
|
|
41
39
|
);
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
backend: session.settings.get("computer.backend"),
|
|
49
|
-
display: session.settings.get("computer.display"),
|
|
50
|
-
maxWidth: coordinateSafeImageSizing ? Math.min(maxWidth, COORDINATE_SAFE_MAX_CAPTURE_WIDTH) : maxWidth,
|
|
51
|
-
maxHeight: coordinateSafeImageSizing ? Math.min(maxHeight, COORDINATE_SAFE_MAX_CAPTURE_HEIGHT) : maxHeight,
|
|
52
|
-
};
|
|
42
|
+
interface ComputerToolInput {
|
|
43
|
+
code: string;
|
|
44
|
+
read_only?: boolean;
|
|
45
|
+
timeout?: number;
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
// must fail closed here instead of truncating in the napi conversion.
|
|
57
|
-
const INT32_MIN = -2_147_483_648;
|
|
58
|
-
const INT32_MAX = 2_147_483_647;
|
|
59
|
-
|
|
60
|
-
type ComputerSchemaPoint = {
|
|
61
|
-
x: number;
|
|
62
|
-
y: number;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
type ComputerSchemaAction = {
|
|
66
|
-
type: ComputerAction["type"];
|
|
67
|
-
x?: number;
|
|
68
|
-
y?: number;
|
|
69
|
-
button?: "left" | "right" | "wheel" | "back" | "forward";
|
|
70
|
-
path?: ComputerSchemaPoint[];
|
|
71
|
-
keys?: string[] | null;
|
|
72
|
-
scroll_x?: number;
|
|
73
|
-
scroll_y?: number;
|
|
74
|
-
text?: string;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export type ComputerParams = {
|
|
78
|
-
actions?: ComputerSchemaAction[];
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
type IsSameType<Left, Right> = [Left] extends [Right] ? ([Right] extends [Left] ? true : false) : false;
|
|
82
|
-
type ComputerSchema<Schema extends Type = Type<ComputerParams>> =
|
|
83
|
-
IsSameType<ComputerParams, Schema["infer"]> extends true ? Schema : never;
|
|
48
|
+
type ComputerSchema = Type<ComputerToolInput>;
|
|
84
49
|
|
|
85
|
-
const getComputerSchema: () => ComputerSchema = once(() =>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const pointSchema = type({
|
|
90
|
-
x: coordinateSchema.describe("x pixel coordinate"),
|
|
91
|
-
y: coordinateSchema.describe("y pixel coordinate"),
|
|
92
|
-
"+": "reject",
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const computerActionSchema = type({
|
|
96
|
-
type: type(
|
|
97
|
-
"'click' | 'double_click' | 'drag' | 'keypress' | 'move' | 'screenshot' | 'scroll' | 'type' | 'wait'",
|
|
98
|
-
).describe("action kind"),
|
|
99
|
-
"x?": coordinateSchema.describe(
|
|
100
|
-
"x pixel coordinate in the most recent screenshot (click, double_click, move, scroll)",
|
|
101
|
-
),
|
|
102
|
-
"y?": coordinateSchema.describe(
|
|
103
|
-
"y pixel coordinate in the most recent screenshot (click, double_click, move, scroll)",
|
|
104
|
-
),
|
|
105
|
-
"button?": type("'left' | 'right' | 'wheel' | 'back' | 'forward'").describe("mouse button; required for click"),
|
|
106
|
-
"path?": pointSchema.array().atLeastLength(2).describe("waypoints from press to release; required for drag"),
|
|
107
|
-
"keys?": type("string[] | null").describe(
|
|
108
|
-
"key names (e.g. CTRL, SHIFT, ENTER, A); required chord for keypress, optional held modifiers for pointer actions",
|
|
50
|
+
const getComputerSchema: () => ComputerSchema = once(() =>
|
|
51
|
+
type({
|
|
52
|
+
code: type("string").describe(
|
|
53
|
+
"JavaScript executed in the persistent computer session; top-level await allowed; `desktop`, `wait`, `assert` in scope",
|
|
109
54
|
),
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
"vertical scroll delta in pixels, positive scrolls content down; required for scroll",
|
|
55
|
+
"read_only?": type("boolean").describe(
|
|
56
|
+
"true = inspection only: screenshots and ax reads allowed, all input/mutation blocked",
|
|
113
57
|
),
|
|
114
|
-
"
|
|
58
|
+
"timeout?": type("number").describe("run budget in seconds"),
|
|
115
59
|
"+": "reject",
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
const computerSchema = type({
|
|
119
|
-
"actions?": computerActionSchema
|
|
120
|
-
.array()
|
|
121
|
-
.describe("ordered actions executed as one batch; omit or pass [] to just capture a screenshot"),
|
|
122
|
-
"+": "reject",
|
|
123
|
-
});
|
|
124
|
-
return computerSchema satisfies ComputerSchema<typeof computerSchema>;
|
|
125
|
-
});
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
126
62
|
|
|
63
|
+
/** Renderer and artifact metadata produced by a computer tool run. */
|
|
127
64
|
export interface ComputerToolDetails {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
export type ComputerControllerFactory = (
|
|
140
|
-
|
|
141
|
-
function isInt32(value: unknown): value is number {
|
|
142
|
-
return typeof value === "number" && Number.isInteger(value) && value >= INT32_MIN && value <= INT32_MAX;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function isCoordinate(value: unknown): value is number {
|
|
146
|
-
return isInt32(value) && value >= 0;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
type AllowedFields = Record<string, true>;
|
|
150
|
-
|
|
151
|
-
const POINT_FIELDS: AllowedFields = { x: true, y: true };
|
|
152
|
-
const MOUSE_BUTTONS: AllowedFields = { left: true, right: true, wheel: true, back: true, forward: true };
|
|
153
|
-
const ACTION_FIELDS: Record<ComputerAction["type"], AllowedFields> = {
|
|
154
|
-
click: { type: true, button: true, x: true, y: true, keys: true },
|
|
155
|
-
double_click: { type: true, x: true, y: true, keys: true },
|
|
156
|
-
drag: { type: true, path: true, keys: true },
|
|
157
|
-
keypress: { type: true, keys: true },
|
|
158
|
-
move: { type: true, x: true, y: true, keys: true },
|
|
159
|
-
screenshot: { type: true },
|
|
160
|
-
scroll: { type: true, x: true, y: true, scroll_x: true, scroll_y: true, keys: true },
|
|
161
|
-
type: { type: true, text: true },
|
|
162
|
-
wait: { type: true },
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
166
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function fieldsForAction(actionType: string): AllowedFields | undefined {
|
|
170
|
-
switch (actionType) {
|
|
171
|
-
case "click":
|
|
172
|
-
case "double_click":
|
|
173
|
-
case "drag":
|
|
174
|
-
case "keypress":
|
|
175
|
-
case "move":
|
|
176
|
-
case "screenshot":
|
|
177
|
-
case "scroll":
|
|
178
|
-
case "type":
|
|
179
|
-
case "wait":
|
|
180
|
-
return ACTION_FIELDS[actionType];
|
|
181
|
-
default:
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function hasOnlyFields(value: Record<string, unknown>, allowed: AllowedFields): boolean {
|
|
187
|
-
return Object.keys(value).every(key => allowed[key] === true);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function isPoint(value: unknown): value is { x: number; y: number } {
|
|
191
|
-
return isRecord(value) && hasOnlyFields(value, POINT_FIELDS) && isCoordinate(value.x) && isCoordinate(value.y);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function isStringArray(value: unknown): value is string[] {
|
|
195
|
-
return Array.isArray(value) && value.every(item => typeof item === "string");
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function modifierBit(value: string): number {
|
|
199
|
-
switch (value.trim().toUpperCase()) {
|
|
200
|
-
case "CTRL":
|
|
201
|
-
case "CONTROL":
|
|
202
|
-
return 1;
|
|
203
|
-
case "SHIFT":
|
|
204
|
-
return 2;
|
|
205
|
-
case "ALT":
|
|
206
|
-
case "OPTION":
|
|
207
|
-
return 4;
|
|
208
|
-
case "META":
|
|
209
|
-
case "CMD":
|
|
210
|
-
case "COMMAND":
|
|
211
|
-
case "SUPER":
|
|
212
|
-
case "WINDOWS":
|
|
213
|
-
return 8;
|
|
214
|
-
default:
|
|
215
|
-
return 0;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function isModifierArray(value: unknown): value is string[] | null | undefined {
|
|
220
|
-
if (value == null) return true;
|
|
221
|
-
if (!isStringArray(value)) return false;
|
|
222
|
-
let seen = 0;
|
|
223
|
-
for (const entry of value) {
|
|
224
|
-
for (const component of entry.split("+")) {
|
|
225
|
-
const bit = modifierBit(component);
|
|
226
|
-
if (bit === 0 || (seen & bit) !== 0) return false;
|
|
227
|
-
seen |= bit;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return true;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function isKeypressArray(value: unknown): value is string[] {
|
|
234
|
-
return (
|
|
235
|
-
isStringArray(value) &&
|
|
236
|
-
value.length > 0 &&
|
|
237
|
-
value.every(key => key.split("+").every(component => component.trim().length > 0))
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function isComputerAction(value: unknown): value is ComputerAction {
|
|
242
|
-
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
243
|
-
const action = value;
|
|
244
|
-
const actionType = value.type;
|
|
245
|
-
const allowed = fieldsForAction(actionType);
|
|
246
|
-
if (!allowed || !hasOnlyFields(action, allowed)) return false;
|
|
247
|
-
switch (actionType) {
|
|
248
|
-
case "click":
|
|
249
|
-
return (
|
|
250
|
-
isCoordinate(action.x) &&
|
|
251
|
-
isCoordinate(action.y) &&
|
|
252
|
-
typeof action.button === "string" &&
|
|
253
|
-
MOUSE_BUTTONS[action.button] === true &&
|
|
254
|
-
isModifierArray(action.keys)
|
|
255
|
-
);
|
|
256
|
-
case "double_click":
|
|
257
|
-
return isCoordinate(action.x) && isCoordinate(action.y) && isModifierArray(action.keys);
|
|
258
|
-
case "drag":
|
|
259
|
-
return (
|
|
260
|
-
Array.isArray(action.path) &&
|
|
261
|
-
action.path.length >= 2 &&
|
|
262
|
-
action.path.every(isPoint) &&
|
|
263
|
-
isModifierArray(action.keys)
|
|
264
|
-
);
|
|
265
|
-
case "keypress":
|
|
266
|
-
return isKeypressArray(action.keys);
|
|
267
|
-
case "move":
|
|
268
|
-
return isCoordinate(action.x) && isCoordinate(action.y) && isModifierArray(action.keys);
|
|
269
|
-
case "screenshot":
|
|
270
|
-
case "wait":
|
|
271
|
-
return true;
|
|
272
|
-
case "scroll":
|
|
273
|
-
return (
|
|
274
|
-
isCoordinate(action.x) &&
|
|
275
|
-
isCoordinate(action.y) &&
|
|
276
|
-
isInt32(action.scroll_x) &&
|
|
277
|
-
isInt32(action.scroll_y) &&
|
|
278
|
-
isModifierArray(action.keys)
|
|
279
|
-
);
|
|
280
|
-
case "type":
|
|
281
|
-
return typeof action.text === "string";
|
|
282
|
-
default:
|
|
283
|
-
return false;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function parseActions(value: unknown): ComputerAction[] {
|
|
288
|
-
// Missing or empty action batches degrade to a plain screenshot so a
|
|
289
|
-
// function-calling model can observe the screen before acting.
|
|
290
|
-
if (value == null) return [{ type: "screenshot" }];
|
|
291
|
-
if (!Array.isArray(value)) throw new ToolError("Computer call requires an array of actions");
|
|
292
|
-
if (value.length === 0) return [{ type: "screenshot" }];
|
|
293
|
-
if (!value.every(isComputerAction)) throw new ToolError("Computer call contains an invalid action");
|
|
294
|
-
return value;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function toDesktopAction(action: ComputerAction): DesktopAction {
|
|
298
|
-
switch (action.type) {
|
|
299
|
-
case "click":
|
|
300
|
-
return {
|
|
301
|
-
type: "click",
|
|
302
|
-
x: action.x,
|
|
303
|
-
y: action.y,
|
|
304
|
-
button: action.button,
|
|
305
|
-
...(action.keys ? { keys: action.keys } : {}),
|
|
306
|
-
};
|
|
307
|
-
case "double_click":
|
|
308
|
-
return {
|
|
309
|
-
type: "double_click",
|
|
310
|
-
x: action.x,
|
|
311
|
-
y: action.y,
|
|
312
|
-
...(action.keys ? { keys: action.keys } : {}),
|
|
313
|
-
};
|
|
314
|
-
case "drag":
|
|
315
|
-
return { type: "drag", path: action.path, ...(action.keys ? { keys: action.keys } : {}) };
|
|
316
|
-
case "keypress":
|
|
317
|
-
return { type: "keypress", keys: action.keys };
|
|
318
|
-
case "move":
|
|
319
|
-
return { type: "move", x: action.x, y: action.y, ...(action.keys ? { keys: action.keys } : {}) };
|
|
320
|
-
case "screenshot":
|
|
321
|
-
return { type: "screenshot" };
|
|
322
|
-
case "scroll":
|
|
323
|
-
return {
|
|
324
|
-
type: "scroll",
|
|
325
|
-
x: action.x,
|
|
326
|
-
y: action.y,
|
|
327
|
-
scroll_x: action.scroll_x,
|
|
328
|
-
scroll_y: action.scroll_y,
|
|
329
|
-
...(action.keys ? { keys: action.keys } : {}),
|
|
330
|
-
};
|
|
331
|
-
case "type":
|
|
332
|
-
return { type: "type", text: action.text };
|
|
333
|
-
case "wait":
|
|
334
|
-
return { type: "wait" };
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function callMetadata(context: AgentToolContext | undefined): ComputerToolCallMetadata | undefined {
|
|
339
|
-
const metadata = context?.toolCall?.providerMetadata;
|
|
340
|
-
return metadata?.type === "computer" ? metadata : undefined;
|
|
341
|
-
}
|
|
342
|
-
|
|
65
|
+
code?: string;
|
|
66
|
+
readOnly?: boolean;
|
|
67
|
+
screenshots: ComputerScreenshot[];
|
|
68
|
+
returnValue?: string;
|
|
69
|
+
backend?: string;
|
|
70
|
+
capturePermission?: string;
|
|
71
|
+
inputPermission?: string;
|
|
72
|
+
axPermission?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Creates the session-scoped controller used by the computer tool. */
|
|
76
|
+
export type ComputerControllerFactory = (session: ToolSession) => ComputerController;
|
|
77
|
+
/** Maps inspection-only runs to read approval and all other runs to execution approval. */
|
|
343
78
|
export function computerApproval(args: unknown): ToolApprovalDecision {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (actions == null) return "read";
|
|
347
|
-
if (!Array.isArray(actions)) return "exec";
|
|
348
|
-
return actions.every(action => {
|
|
349
|
-
if (!action || typeof action !== "object") return false;
|
|
350
|
-
const actionType = (action as { type?: unknown }).type;
|
|
351
|
-
return actionType === "screenshot" || actionType === "wait";
|
|
352
|
-
})
|
|
353
|
-
? "read"
|
|
354
|
-
: "exec";
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function modifierSummary(keys: unknown): string {
|
|
358
|
-
return isStringArray(keys) && keys.length > 0 ? ` keys=${JSON.stringify(keys)}` : "";
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function approvalActionSummary(actions: unknown): string[] {
|
|
362
|
-
if (!Array.isArray(actions)) return ["Actions: unavailable"];
|
|
363
|
-
const lines = actions.slice(0, 12).map((value, index) => {
|
|
364
|
-
if (!value || typeof value !== "object") return `${index + 1}. invalid`;
|
|
365
|
-
const action = value as Record<string, unknown>;
|
|
366
|
-
const type = typeof action.type === "string" ? action.type : "invalid";
|
|
367
|
-
let detail: string;
|
|
368
|
-
switch (type) {
|
|
369
|
-
case "click":
|
|
370
|
-
detail = `click button=${String(action.button)} at (${String(action.x)}, ${String(action.y)})${modifierSummary(action.keys)}`;
|
|
371
|
-
break;
|
|
372
|
-
case "double_click":
|
|
373
|
-
detail = `double_click at (${String(action.x)}, ${String(action.y)})${modifierSummary(action.keys)}`;
|
|
374
|
-
break;
|
|
375
|
-
case "drag":
|
|
376
|
-
detail = `drag path=${Array.isArray(action.path) ? action.path.map(point => (isPoint(point) ? `(${point.x}, ${point.y})` : "invalid")).join(" -> ") : "invalid"}${modifierSummary(action.keys)}`;
|
|
377
|
-
break;
|
|
378
|
-
case "keypress":
|
|
379
|
-
detail = `keypress keys=${JSON.stringify(action.keys)}`;
|
|
380
|
-
break;
|
|
381
|
-
case "move":
|
|
382
|
-
detail = `move to (${String(action.x)}, ${String(action.y)})${modifierSummary(action.keys)}`;
|
|
383
|
-
break;
|
|
384
|
-
case "scroll":
|
|
385
|
-
detail = `scroll at (${String(action.x)}, ${String(action.y)}) delta=(${String(action.scroll_x)}, ${String(action.scroll_y)})${modifierSummary(action.keys)}`;
|
|
386
|
-
break;
|
|
387
|
-
case "type":
|
|
388
|
-
detail = `type text=${JSON.stringify(action.text)}`;
|
|
389
|
-
break;
|
|
390
|
-
case "screenshot":
|
|
391
|
-
case "wait":
|
|
392
|
-
detail = type;
|
|
393
|
-
break;
|
|
394
|
-
default:
|
|
395
|
-
detail = type;
|
|
396
|
-
}
|
|
397
|
-
return truncateForPrompt(sanitizeText(`${index + 1}. ${detail}`).replace(/[\r\n\t]+/g, " "), 240);
|
|
398
|
-
});
|
|
399
|
-
if (actions.length > 12) lines.push(`+${actions.length - 12} more actions`);
|
|
400
|
-
return truncateForPrompt(lines.join("\n"), 2_000).split("\n");
|
|
79
|
+
if (args === null || typeof args !== "object" || !("read_only" in args)) return "exec";
|
|
80
|
+
return args.read_only === true ? "read" : "exec";
|
|
401
81
|
}
|
|
402
82
|
|
|
83
|
+
/** Executes persistent desktop JavaScript through one lazy worker session. */
|
|
403
84
|
export class ComputerTool implements AgentTool<ComputerSchema, ComputerToolDetails> {
|
|
404
85
|
readonly name = "computer";
|
|
405
|
-
readonly native = { type: "computer" } as const;
|
|
406
86
|
readonly label = "Computer";
|
|
407
87
|
readonly loadMode = "essential" as const;
|
|
408
88
|
readonly concurrency = "exclusive" as const;
|
|
409
|
-
readonly summary = "
|
|
410
|
-
get parameters(): ComputerSchema {
|
|
411
|
-
return getComputerSchema();
|
|
412
|
-
}
|
|
89
|
+
readonly summary = "Control the host desktop with persistent JavaScript and OS accessibility APIs";
|
|
413
90
|
readonly strict = false;
|
|
414
91
|
readonly approval = computerApproval;
|
|
415
92
|
readonly formatApprovalDetails = (args: unknown): string[] => {
|
|
416
|
-
|
|
417
|
-
|
|
93
|
+
if (args === null || typeof args !== "object") return [""];
|
|
94
|
+
const code = "code" in args && typeof args.code === "string" ? args.code : "";
|
|
95
|
+
return [
|
|
96
|
+
...("read_only" in args && args.read_only === true ? ["read-only"] : []),
|
|
97
|
+
...truncateForPrompt(code, 2_000).split("\n"),
|
|
98
|
+
];
|
|
418
99
|
};
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
* by `/computer status`.
|
|
423
|
-
*/
|
|
424
|
-
effectiveConfiguration: Readonly<DesktopSessionOptions>;
|
|
425
|
-
readonly #createController: ComputerControllerFactory;
|
|
426
|
-
#controller: ComputerController;
|
|
427
|
-
#unregisterOwner: () => void;
|
|
428
|
-
#usesCoordinateSafeImageSizing: boolean;
|
|
100
|
+
|
|
101
|
+
readonly #controller: ComputerController;
|
|
102
|
+
readonly #unregisterOwner: () => void;
|
|
429
103
|
#closed = false;
|
|
430
104
|
#description?: string;
|
|
431
105
|
|
|
432
106
|
constructor(
|
|
433
107
|
readonly session: ToolSession,
|
|
434
|
-
createController: ComputerControllerFactory =
|
|
108
|
+
createController: ComputerControllerFactory = currentSession =>
|
|
109
|
+
new ComputerSupervisor(currentSession, undefined, undefined, callSessionTool),
|
|
435
110
|
) {
|
|
436
|
-
this.#
|
|
437
|
-
this.#usesCoordinateSafeImageSizing = usesCoordinateSafeImageSizing(session.getActiveModel?.());
|
|
438
|
-
this.effectiveConfiguration = Object.freeze(captureOptions(session, this.#usesCoordinateSafeImageSizing));
|
|
439
|
-
this.#controller = createController(this.effectiveConfiguration);
|
|
111
|
+
this.#controller = createController(session);
|
|
440
112
|
this.#unregisterOwner = registerComputerController(
|
|
441
113
|
session.getEvalKernelOwnerId?.() ?? undefined,
|
|
442
114
|
this.#controller,
|
|
443
115
|
);
|
|
444
116
|
}
|
|
117
|
+
|
|
118
|
+
get parameters(): ComputerSchema {
|
|
119
|
+
return getComputerSchema();
|
|
120
|
+
}
|
|
121
|
+
|
|
445
122
|
get description(): string {
|
|
446
123
|
this.#description ??= prompt.render(computerDescription);
|
|
447
124
|
return this.#description;
|
|
448
125
|
}
|
|
449
126
|
|
|
450
|
-
async #refreshControllerForModel(): Promise<void> {
|
|
451
|
-
const nextUsesCoordinateSafeImageSizing = usesCoordinateSafeImageSizing(this.session.getActiveModel?.());
|
|
452
|
-
if (nextUsesCoordinateSafeImageSizing === this.#usesCoordinateSafeImageSizing) return;
|
|
453
|
-
|
|
454
|
-
const previous = this.#controller;
|
|
455
|
-
const nextOptions = Object.freeze(captureOptions(this.session, nextUsesCoordinateSafeImageSizing));
|
|
456
|
-
const next = this.#createController(nextOptions);
|
|
457
|
-
this.#unregisterOwner();
|
|
458
|
-
this.#controller = next;
|
|
459
|
-
this.#unregisterOwner = registerComputerController(this.session.getEvalKernelOwnerId?.() ?? undefined, next);
|
|
460
|
-
this.#usesCoordinateSafeImageSizing = nextUsesCoordinateSafeImageSizing;
|
|
461
|
-
this.effectiveConfiguration = nextOptions;
|
|
462
|
-
await previous.close();
|
|
463
|
-
}
|
|
464
|
-
|
|
465
127
|
async execute(
|
|
466
128
|
_toolCallId: string,
|
|
467
|
-
params:
|
|
129
|
+
params: ComputerToolInput,
|
|
468
130
|
signal?: AbortSignal,
|
|
469
131
|
_onUpdate?: AgentToolUpdateCallback<ComputerToolDetails>,
|
|
470
|
-
|
|
132
|
+
_context?: AgentToolContext,
|
|
471
133
|
): Promise<AgentToolResult<ComputerToolDetails>> {
|
|
472
134
|
throwIfAborted(signal);
|
|
473
135
|
if (this.#closed) throw new ToolError("Computer session is closed");
|
|
474
|
-
|
|
475
|
-
const
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
136
|
+
|
|
137
|
+
const timeoutSeconds = clampTimeout("computer", params.timeout, this.session.settings.get("tools.maxTimeout"));
|
|
138
|
+
const coordinateSafe = usesCoordinateSafeImageSizing(this.session.getActiveModel?.());
|
|
139
|
+
const configuredMaxWidth = this.session.settings.get("computer.maxWidth");
|
|
140
|
+
const configuredMaxHeight = this.session.settings.get("computer.maxHeight");
|
|
141
|
+
const snapshot: ComputerSessionSnapshot = {
|
|
142
|
+
cwd: this.session.cwd,
|
|
143
|
+
sessionId: this.session.getEvalSessionId?.() ?? this.session.getSessionId?.() ?? "computer",
|
|
144
|
+
captureMaxWidth: coordinateSafe
|
|
145
|
+
? Math.min(configuredMaxWidth, COORDINATE_SAFE_MAX_CAPTURE_WIDTH)
|
|
146
|
+
: configuredMaxWidth,
|
|
147
|
+
captureMaxHeight: coordinateSafe
|
|
148
|
+
? Math.min(configuredMaxHeight, COORDINATE_SAFE_MAX_CAPTURE_HEIGHT)
|
|
149
|
+
: configuredMaxHeight,
|
|
150
|
+
display: this.session.settings.get("computer.display") ?? "all",
|
|
151
|
+
readOnly: !!params.read_only,
|
|
152
|
+
};
|
|
153
|
+
const run = await this.#controller.run(params.code, timeoutSeconds * 1000, snapshot, signal);
|
|
483
154
|
throwIfAborted(signal);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
height: capture.height,
|
|
490
|
-
backend: capture.backend,
|
|
491
|
-
displayServer: capture.displayServer,
|
|
492
|
-
capturePermission: capture.capturePermission,
|
|
493
|
-
inputPermission: capture.inputPermission,
|
|
494
|
-
displays: capture.displays,
|
|
495
|
-
capabilities: this.#controller.capabilities,
|
|
496
|
-
actions: actions.map(action => action.type),
|
|
497
|
-
},
|
|
498
|
-
...(metadata
|
|
499
|
-
? {
|
|
500
|
-
providerMetadata: {
|
|
501
|
-
type: "computer" as const,
|
|
502
|
-
screenshot: { type: "computer_screenshot" as const, image_url: `data:image/png;base64,${data}` },
|
|
503
|
-
acknowledgedSafetyChecks: pendingSafetyChecks,
|
|
504
|
-
},
|
|
505
|
-
}
|
|
506
|
-
: {}),
|
|
155
|
+
|
|
156
|
+
const details: ComputerToolDetails = {
|
|
157
|
+
code: params.code,
|
|
158
|
+
readOnly: snapshot.readOnly,
|
|
159
|
+
screenshots: run.screenshots,
|
|
507
160
|
};
|
|
161
|
+
if (run.returnValue !== undefined) details.returnValue = stringifyReturnValue(run.returnValue);
|
|
162
|
+
populateCapabilityDetails(details, run.capabilities);
|
|
163
|
+
|
|
164
|
+
const textBlocks = run.displays
|
|
165
|
+
.filter((content): content is { type: "text"; text: string } => content.type === "text")
|
|
166
|
+
.map(content => content.text);
|
|
167
|
+
if (details.returnValue !== undefined) textBlocks.push(details.returnValue);
|
|
168
|
+
if (!textBlocks.length && !run.displays.some(content => content.type === "image")) {
|
|
169
|
+
textBlocks.push("Ran computer code");
|
|
170
|
+
}
|
|
171
|
+
const textOnly = textBlocks.join("\n");
|
|
172
|
+
const cappedText = await enforceInlineByteCap(textOnly, {
|
|
173
|
+
saveArtifact: full => saveComputerOutputArtifact(this.session, full),
|
|
174
|
+
});
|
|
175
|
+
const images = run.displays
|
|
176
|
+
.filter(content => content.type === "image")
|
|
177
|
+
.map(content => ({ ...content, detail: "original" as const }));
|
|
178
|
+
const content = [...(cappedText ? [{ type: "text" as const, text: cappedText }] : []), ...images];
|
|
179
|
+
return { content, details };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async capabilities(): Promise<DesktopCapabilities | undefined> {
|
|
183
|
+
if (this.#closed) return undefined;
|
|
184
|
+
return await this.#controller.capabilities();
|
|
508
185
|
}
|
|
509
186
|
|
|
510
187
|
async close(): Promise<void> {
|
|
@@ -514,3 +191,32 @@ export class ComputerTool implements AgentTool<ComputerSchema, ComputerToolDetai
|
|
|
514
191
|
await this.#controller.close();
|
|
515
192
|
}
|
|
516
193
|
}
|
|
194
|
+
|
|
195
|
+
function stringifyReturnValue(value: unknown): string {
|
|
196
|
+
if (typeof value === "string") return value;
|
|
197
|
+
try {
|
|
198
|
+
return JSON.stringify(value, null, 2) ?? String(value);
|
|
199
|
+
} catch {
|
|
200
|
+
return String(value);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function populateCapabilityDetails(details: ComputerToolDetails, capabilities: DesktopCapabilities | undefined): void {
|
|
205
|
+
if (!capabilities) return;
|
|
206
|
+
details.backend = capabilities.backend;
|
|
207
|
+
details.capturePermission = capabilities.capturePermission;
|
|
208
|
+
details.inputPermission = capabilities.inputPermission;
|
|
209
|
+
details.axPermission = capabilities.axPermission;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Persist over-cap computer run output as a session artifact; mirrors the browser run save path. */
|
|
213
|
+
async function saveComputerOutputArtifact(session: ToolSession, fullText: string): Promise<string | undefined> {
|
|
214
|
+
try {
|
|
215
|
+
const alloc = await session.allocateOutputArtifact?.("computer-original");
|
|
216
|
+
if (!alloc?.path || !alloc.id) return undefined;
|
|
217
|
+
await Bun.write(alloc.path, fullText);
|
|
218
|
+
return alloc.id;
|
|
219
|
+
} catch {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
package/src/tools/context.ts
CHANGED
|
@@ -13,6 +13,9 @@ declare module "@oh-my-pi/pi-agent-core" {
|
|
|
13
13
|
* wrapper must not re-prompt for the same action (explicit per-tool
|
|
14
14
|
* policies and overrides still apply). */
|
|
15
15
|
xdevApproved?: boolean;
|
|
16
|
+
/** Reports the approval tier resolved after an extension rewrites an
|
|
17
|
+
* xd:// device call, so dispatch metadata describes the input that ran. */
|
|
18
|
+
xdevTierResolved?(tier: "read" | "write" | "exec"): void;
|
|
16
19
|
/** Set only after an interactive prompt approves provider computer safety checks. */
|
|
17
20
|
providerSafetyApproved?: boolean;
|
|
18
21
|
}
|
package/src/tools/glob.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { splitMemoryGlobPattern } from "../internal-urls/memory-protocol";
|
|
|
13
13
|
import type { Theme } from "../modes/theme/theme";
|
|
14
14
|
import globDescription from "../prompts/tools/glob.md" with { type: "text" };
|
|
15
15
|
import { type TruncationResult, truncateHead } from "../session/streaming-output";
|
|
16
|
+
import { isScoutSpawnable } from "../task/spawn-policy";
|
|
16
17
|
import { Ellipsis, fileHyperlink, renderFileList, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
17
18
|
import type { ToolSession } from ".";
|
|
18
19
|
import { applyListLimit } from "./list-limit";
|
|
@@ -106,7 +107,14 @@ export class GlobTool implements AgentTool<typeof findSchema, GlobToolDetails> {
|
|
|
106
107
|
readonly approval = "read" as const;
|
|
107
108
|
readonly loadMode = "essential";
|
|
108
109
|
readonly label = "Glob";
|
|
109
|
-
|
|
110
|
+
get description(): string {
|
|
111
|
+
return prompt.render(globDescription, {
|
|
112
|
+
scoutAvailable: isScoutSpawnable(
|
|
113
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
114
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
115
|
+
),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
110
118
|
readonly parameters = findSchema;
|
|
111
119
|
|
|
112
120
|
readonly examples: readonly ToolExample<typeof findSchema.infer>[] = [
|
|
@@ -138,7 +146,6 @@ export class GlobTool implements AgentTool<typeof findSchema, GlobToolDetails> {
|
|
|
138
146
|
) {
|
|
139
147
|
this.#customOps = options?.operations;
|
|
140
148
|
this.#rootPathAlias = options?.rootPathAlias === true;
|
|
141
|
-
this.description = prompt.render(globDescription);
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
async execute(
|