@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.
Files changed (319) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-q2w43aw3.md} +51 -0
  3. package/dist/cli.js +4148 -3717
  4. package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
  5. package/dist/types/cli/args.d.ts +4 -1
  6. package/dist/types/cli/browser-relay-cli.d.ts +15 -0
  7. package/dist/types/cli/command-help.d.ts +99 -0
  8. package/dist/types/cli/help-extra.d.ts +2 -0
  9. package/dist/types/cli/thinking-levels.d.ts +5 -0
  10. package/dist/types/cli/update-cli.d.ts +13 -0
  11. package/dist/types/commands/browser-relay.d.ts +38 -0
  12. package/dist/types/commands/commit.d.ts +3 -0
  13. package/dist/types/commands/complete.d.ts +1 -1
  14. package/dist/types/commands/completions.d.ts +6 -0
  15. package/dist/types/commands/grep.d.ts +3 -0
  16. package/dist/types/commands/join.d.ts +4 -0
  17. package/dist/types/commands/launch-help.d.ts +171 -0
  18. package/dist/types/commands/launch.d.ts +5 -1
  19. package/dist/types/commands/models.d.ts +3 -0
  20. package/dist/types/commands/say.d.ts +10 -0
  21. package/dist/types/commands/worktree.d.ts +3 -0
  22. package/dist/types/config/keybindings.d.ts +5 -0
  23. package/dist/types/config/model-discovery.d.ts +2 -2
  24. package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
  25. package/dist/types/config/models-config-schema.d.ts +2 -0
  26. package/dist/types/config/models-config.d.ts +1 -0
  27. package/dist/types/config/service-tier.d.ts +2 -0
  28. package/dist/types/config/settings-schema.d.ts +69 -21
  29. package/dist/types/cursor-bridge-tools.d.ts +1 -1
  30. package/dist/types/cursor.d.ts +7 -4
  31. package/dist/types/edit/index.d.ts +3 -3
  32. package/dist/types/edit/modes/replace.d.ts +22 -15
  33. package/dist/types/edit/renderer.d.ts +1 -1
  34. package/dist/types/eval/py/kernel.d.ts +3 -1
  35. package/dist/types/eval/py/spawn-options.d.ts +12 -16
  36. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
  37. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
  38. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
  39. package/dist/types/launch/broker.d.ts +1 -1
  40. package/dist/types/launch/client.d.ts +6 -3
  41. package/dist/types/launch/ensure.d.ts +8 -0
  42. package/dist/types/launch/paths.d.ts +1 -1
  43. package/dist/types/launch/protocol.d.ts +2 -2
  44. package/dist/types/lsp/client.d.ts +2 -0
  45. package/dist/types/lsp/mux/daemon.d.ts +15 -0
  46. package/dist/types/lsp/mux/protocol.d.ts +57 -0
  47. package/dist/types/lsp/mux/server.d.ts +18 -0
  48. package/dist/types/lsp/types.d.ts +22 -2
  49. package/dist/types/main.d.ts +2 -0
  50. package/dist/types/mnemopi/embed-client.d.ts +1 -1
  51. package/dist/types/mnemopi/state.d.ts +0 -23
  52. package/dist/types/modes/components/assistant-message.d.ts +2 -0
  53. package/dist/types/modes/components/custom-editor.d.ts +2 -1
  54. package/dist/types/modes/components/read-tool-group.d.ts +2 -0
  55. package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
  56. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  57. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  58. package/dist/types/modes/interactive-mode.d.ts +1 -0
  59. package/dist/types/modes/types.d.ts +1 -0
  60. package/dist/types/modes/workflow.d.ts +2 -1
  61. package/dist/types/sdk.d.ts +3 -1
  62. package/dist/types/secrets/index.d.ts +1 -1
  63. package/dist/types/session/agent-session-types.d.ts +3 -1
  64. package/dist/types/session/indexed-session-storage.d.ts +1 -0
  65. package/dist/types/session/session-advisors.d.ts +1 -1
  66. package/dist/types/session/session-context.d.ts +2 -1
  67. package/dist/types/session/session-maintenance.d.ts +1 -0
  68. package/dist/types/session/session-manager.d.ts +16 -11
  69. package/dist/types/session/session-stats.d.ts +10 -0
  70. package/dist/types/session/session-storage.d.ts +15 -3
  71. package/dist/types/system-prompt.d.ts +4 -1
  72. package/dist/types/task/index.d.ts +2 -1
  73. package/dist/types/task/spawn-policy.d.ts +5 -0
  74. package/dist/types/thinking.d.ts +1 -7
  75. package/dist/types/tools/ast-grep.d.ts +1 -1
  76. package/dist/types/tools/browser/registry.d.ts +2 -1
  77. package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
  78. package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
  79. package/dist/types/tools/browser/relay/kind.d.ts +19 -0
  80. package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
  81. package/dist/types/tools/browser/relay/server.d.ts +35 -0
  82. package/dist/types/tools/browser/render.d.ts +1 -0
  83. package/dist/types/tools/browser.d.ts +3 -0
  84. package/dist/types/tools/computer/exposure.d.ts +9 -7
  85. package/dist/types/tools/computer/protocol.d.ts +70 -17
  86. package/dist/types/tools/computer/supervisor.d.ts +23 -10
  87. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  88. package/dist/types/tools/computer/worker.d.ts +42 -7
  89. package/dist/types/tools/computer-renderer.d.ts +7 -5
  90. package/dist/types/tools/computer.d.ts +25 -44
  91. package/dist/types/tools/context.d.ts +3 -0
  92. package/dist/types/tools/glob.d.ts +1 -1
  93. package/dist/types/tools/grep.d.ts +1 -1
  94. package/dist/types/tools/render-utils.d.ts +2 -0
  95. package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
  96. package/dist/types/tools/tool-timeouts.d.ts +5 -0
  97. package/dist/types/tools/xdev.d.ts +8 -0
  98. package/dist/types/utils/clipboard.d.ts +2 -2
  99. package/dist/types/web/kagi.d.ts +1 -0
  100. package/dist/types/web/search/index.d.ts +1 -1
  101. package/dist/types/web/search/providers/anthropic.d.ts +1 -0
  102. package/dist/types/web/search/providers/base.d.ts +2 -0
  103. package/dist/types/web/search/providers/brave.d.ts +1 -0
  104. package/dist/types/web/search/providers/browser-page.d.ts +1 -0
  105. package/dist/types/web/search/providers/codex.d.ts +1 -0
  106. package/dist/types/web/search/providers/exa.d.ts +1 -0
  107. package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
  108. package/dist/types/web/search/providers/gemini.d.ts +1 -0
  109. package/dist/types/web/search/providers/jina.d.ts +1 -0
  110. package/dist/types/web/search/providers/kagi.d.ts +1 -0
  111. package/dist/types/web/search/providers/kimi.d.ts +1 -0
  112. package/dist/types/web/search/providers/parallel.d.ts +1 -0
  113. package/dist/types/web/search/providers/perplexity.d.ts +1 -0
  114. package/dist/types/web/search/providers/searxng.d.ts +1 -0
  115. package/dist/types/web/search/providers/tavily.d.ts +1 -0
  116. package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
  117. package/dist/types/web/search/providers/utils.d.ts +6 -9
  118. package/dist/types/web/search/providers/zai.d.ts +1 -0
  119. package/dist/types/web/search/types.d.ts +4 -0
  120. package/package.json +12 -12
  121. package/src/cli/args.ts +7 -87
  122. package/src/cli/browser-relay-cli.ts +119 -0
  123. package/src/cli/command-help.ts +103 -0
  124. package/src/cli/completion-gen.ts +5 -5
  125. package/src/cli/flag-tables.ts +9 -0
  126. package/src/cli/help-extra.ts +89 -0
  127. package/src/cli/setup-cli.ts +13 -21
  128. package/src/cli/thinking-levels.ts +7 -0
  129. package/src/cli/update-cli.ts +26 -7
  130. package/src/cli-commands.ts +170 -33
  131. package/src/cli.ts +17 -5
  132. package/src/commands/acp.ts +3 -1
  133. package/src/commands/agents.ts +3 -2
  134. package/src/commands/auth-broker.ts +3 -2
  135. package/src/commands/auth-gateway.ts +3 -2
  136. package/src/commands/bench.ts +2 -3
  137. package/src/commands/browser-relay.ts +53 -0
  138. package/src/commands/cleanse.ts +2 -2
  139. package/src/commands/commit.ts +3 -2
  140. package/src/commands/complete.ts +2 -1
  141. package/src/commands/completions.ts +3 -2
  142. package/src/commands/config.ts +3 -2
  143. package/src/commands/dry-balance.ts +2 -2
  144. package/src/commands/gallery.ts +3 -2
  145. package/src/commands/gc.ts +3 -2
  146. package/src/commands/grep.ts +3 -2
  147. package/src/commands/grievances.ts +3 -2
  148. package/src/commands/install.ts +2 -2
  149. package/src/commands/join.ts +3 -2
  150. package/src/commands/launch-help.ts +116 -0
  151. package/src/commands/launch.ts +7 -191
  152. package/src/commands/models.ts +3 -2
  153. package/src/commands/plugin.ts +3 -2
  154. package/src/commands/read.ts +3 -2
  155. package/src/commands/say.ts +3 -2
  156. package/src/commands/setup.ts +3 -2
  157. package/src/commands/shell.ts +3 -2
  158. package/src/commands/ssh.ts +3 -2
  159. package/src/commands/stats.ts +3 -2
  160. package/src/commands/tiny-models.ts +2 -2
  161. package/src/commands/token.ts +2 -2
  162. package/src/commands/ttsr.ts +2 -2
  163. package/src/commands/update.ts +3 -2
  164. package/src/commands/usage.ts +3 -2
  165. package/src/commands/web-search.ts +3 -2
  166. package/src/commands/worktree.ts +3 -2
  167. package/src/config/keybindings.ts +5 -0
  168. package/src/config/model-discovery.ts +30 -13
  169. package/src/config/model-registry.ts +9 -5
  170. package/src/config/models-config-schema-bundle.ts +9 -0
  171. package/src/config/resolve-config-value.ts +2 -1
  172. package/src/config/service-tier.ts +5 -0
  173. package/src/config/settings-schema.ts +72 -20
  174. package/src/cursor-bridge-tools.ts +1 -1
  175. package/src/cursor.ts +10 -14
  176. package/src/edit/diff.ts +17 -8
  177. package/src/edit/index.ts +20 -7
  178. package/src/edit/modes/replace.ts +93 -40
  179. package/src/edit/renderer.ts +1 -1
  180. package/src/edit/streaming.ts +14 -17
  181. package/src/eval/py/kernel.ts +2 -1
  182. package/src/eval/py/spawn-options.ts +24 -29
  183. package/src/export/html/template.js +8 -26
  184. package/src/extensibility/extensions/wrapper.ts +1 -0
  185. package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
  186. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
  187. package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
  188. package/src/extensibility/skills.ts +11 -0
  189. package/src/extensibility/utils.ts +10 -2
  190. package/src/launch/broker.ts +1 -1
  191. package/src/launch/client.ts +32 -13
  192. package/src/launch/ensure.ts +73 -0
  193. package/src/launch/paths.ts +1 -1
  194. package/src/launch/protocol.ts +2 -2
  195. package/src/lsp/client.ts +23 -2
  196. package/src/lsp/index.ts +8 -0
  197. package/src/lsp/mux/daemon.ts +348 -0
  198. package/src/lsp/mux/protocol.ts +96 -0
  199. package/src/lsp/mux/server.ts +797 -0
  200. package/src/lsp/types.ts +28 -2
  201. package/src/main.ts +7 -2
  202. package/src/mcp/oauth-flow.ts +11 -1
  203. package/src/mnemopi/embed-client.ts +49 -2
  204. package/src/mnemopi/state.ts +57 -23
  205. package/src/modes/components/assistant-message.ts +11 -0
  206. package/src/modes/components/chat-transcript-builder.ts +4 -0
  207. package/src/modes/components/custom-editor.ts +9 -0
  208. package/src/modes/components/read-tool-group.ts +10 -0
  209. package/src/modes/components/status-line/component.ts +1 -1
  210. package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
  211. package/src/modes/components/tool-execution.ts +8 -0
  212. package/src/modes/components/tree-selector.ts +46 -48
  213. package/src/modes/controllers/event-controller.ts +17 -0
  214. package/src/modes/controllers/input-controller.ts +39 -1
  215. package/src/modes/controllers/mcp-command-controller.ts +11 -5
  216. package/src/modes/controllers/selector-controller.ts +20 -0
  217. package/src/modes/interactive-mode.ts +2 -0
  218. package/src/modes/types.ts +1 -0
  219. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  220. package/src/modes/utils/interactive-context-helpers.ts +1 -0
  221. package/src/modes/utils/ui-helpers.ts +6 -10
  222. package/src/modes/workflow.ts +8 -2
  223. package/src/prompts/agents/init.md +1 -1
  224. package/src/prompts/system/plan-mode-active.md +2 -2
  225. package/src/prompts/system/system-prompt.md +26 -52
  226. package/src/prompts/system/workflow-notice.md +3 -3
  227. package/src/prompts/tools/ast-grep.md +1 -1
  228. package/src/prompts/tools/bash.md +1 -1
  229. package/src/prompts/tools/browser.md +1 -0
  230. package/src/prompts/tools/computer.md +21 -21
  231. package/src/prompts/tools/glob.md +1 -1
  232. package/src/prompts/tools/grep.md +1 -1
  233. package/src/prompts/tools/replace.md +5 -4
  234. package/src/prompts/tools/task.md +4 -4
  235. package/src/sdk.ts +34 -6
  236. package/src/secrets/index.ts +1 -1
  237. package/src/session/agent-session-types.ts +3 -1
  238. package/src/session/agent-session.ts +13 -1
  239. package/src/session/indexed-session-storage.ts +9 -0
  240. package/src/session/prewalk.ts +24 -2
  241. package/src/session/session-advisors.ts +2 -2
  242. package/src/session/session-context.ts +16 -12
  243. package/src/session/session-dump-format.ts +1 -1
  244. package/src/session/session-maintenance.ts +38 -4
  245. package/src/session/session-manager.ts +102 -67
  246. package/src/session/session-paths.ts +118 -55
  247. package/src/session/session-stats.ts +48 -3
  248. package/src/session/session-storage.ts +30 -22
  249. package/src/session/stream-guards.ts +1 -1
  250. package/src/slash-commands/builtin-registry.ts +27 -24
  251. package/src/system-prompt.ts +8 -3
  252. package/src/task/index.ts +23 -8
  253. package/src/task/spawn-policy.ts +14 -0
  254. package/src/thinking.ts +2 -8
  255. package/src/tools/ast-grep.ts +10 -4
  256. package/src/tools/browser/cmux/cmux-tab.ts +5 -10
  257. package/src/tools/browser/cmux/rpc.ts +2 -8
  258. package/src/tools/browser/registry.ts +53 -2
  259. package/src/tools/browser/relay/bridge.ts +945 -0
  260. package/src/tools/browser/relay/daemon.ts +117 -0
  261. package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
  262. package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
  263. package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
  264. package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
  265. package/src/tools/browser/relay/kind.ts +41 -0
  266. package/src/tools/browser/relay/protocol.ts +54 -0
  267. package/src/tools/browser/relay/server.ts +141 -0
  268. package/src/tools/browser/render.ts +4 -1
  269. package/src/tools/browser/shared-daemon.ts +7 -58
  270. package/src/tools/browser/tab-supervisor.ts +5 -4
  271. package/src/tools/browser/tab-worker.ts +33 -12
  272. package/src/tools/browser.ts +26 -1
  273. package/src/tools/computer/exposure.ts +11 -35
  274. package/src/tools/computer/protocol.ts +51 -10
  275. package/src/tools/computer/supervisor.ts +227 -124
  276. package/src/tools/computer/worker-entry.ts +11 -6
  277. package/src/tools/computer/worker.ts +695 -81
  278. package/src/tools/computer-renderer.ts +104 -65
  279. package/src/tools/computer.ts +134 -428
  280. package/src/tools/context.ts +3 -0
  281. package/src/tools/glob.ts +9 -2
  282. package/src/tools/grep.ts +12 -6
  283. package/src/tools/inspect-image.ts +32 -6
  284. package/src/tools/render-utils.ts +2 -0
  285. package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
  286. package/src/tools/tool-timeouts.ts +1 -0
  287. package/src/tools/xdev.ts +29 -2
  288. package/src/utils/clipboard.ts +40 -14
  289. package/src/utils/command-args.ts +26 -28
  290. package/src/utils/tool-choice.ts +0 -13
  291. package/src/web/kagi.ts +2 -1
  292. package/src/web/search/index.ts +18 -2
  293. package/src/web/search/providers/anthropic.ts +4 -1
  294. package/src/web/search/providers/base.ts +2 -0
  295. package/src/web/search/providers/brave.ts +3 -1
  296. package/src/web/search/providers/browser-page.ts +6 -4
  297. package/src/web/search/providers/codex.ts +4 -1
  298. package/src/web/search/providers/duckduckgo.ts +2 -1
  299. package/src/web/search/providers/ecosia.ts +2 -1
  300. package/src/web/search/providers/exa.ts +4 -2
  301. package/src/web/search/providers/firecrawl.ts +3 -1
  302. package/src/web/search/providers/gemini.ts +8 -2
  303. package/src/web/search/providers/google.ts +2 -1
  304. package/src/web/search/providers/jina.ts +12 -2
  305. package/src/web/search/providers/kagi.ts +3 -0
  306. package/src/web/search/providers/kimi.ts +5 -1
  307. package/src/web/search/providers/mojeek.ts +2 -1
  308. package/src/web/search/providers/parallel.ts +5 -1
  309. package/src/web/search/providers/perplexity.ts +6 -3
  310. package/src/web/search/providers/public.ts +1 -1
  311. package/src/web/search/providers/searxng.ts +11 -5
  312. package/src/web/search/providers/startpage.ts +10 -4
  313. package/src/web/search/providers/synthetic.ts +11 -5
  314. package/src/web/search/providers/tavily.ts +3 -1
  315. package/src/web/search/providers/tinyfish.ts +3 -1
  316. package/src/web/search/providers/utils.ts +12 -10
  317. package/src/web/search/providers/xai.ts +2 -2
  318. package/src/web/search/providers/zai.ts +18 -3
  319. package/src/web/search/types.ts +6 -0
@@ -5,22 +5,20 @@ import type {
5
5
  AgentToolUpdateCallback,
6
6
  ToolApprovalDecision,
7
7
  } from "@oh-my-pi/pi-agent-core";
8
- import type { ComputerAction, ComputerSafetyCheck, ComputerToolCallMetadata, Model } from "@oh-my-pi/pi-ai";
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
- DesktopAction,
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
- function captureOptions(session: ToolSession, coordinateSafeImageSizing: boolean): DesktopSessionOptions {
45
- const maxWidth = session.settings.get("computer.maxWidth");
46
- const maxHeight = session.settings.get("computer.maxHeight");
47
- return {
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
- // Desktop actions cross the N-API boundary as i32; out-of-range JS numbers
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
- const coordinateSchema = type("0 <= number.integer <= 2147483647");
87
- const scrollDeltaSchema = type("-2147483648 <= number.integer <= 2147483647");
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
- "scroll_x?": scrollDeltaSchema.describe("horizontal scroll delta in pixels; required for scroll"),
111
- "scroll_y?": scrollDeltaSchema.describe(
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
- "text?": type("string").describe("literal text to type; required for type"),
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
- width: number;
129
- height: number;
130
- backend: DesktopCapture["backend"];
131
- displayServer?: string;
132
- capturePermission: string;
133
- inputPermission: string;
134
- displays: DesktopDisplay[];
135
- capabilities?: DesktopCapabilities;
136
- actions: ComputerAction["type"][];
137
- }
138
-
139
- export type ComputerControllerFactory = (options: DesktopSessionOptions) => ComputerController;
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
- const actions =
345
- args && typeof args === "object" && "actions" in args ? (args as { actions?: unknown }).actions : undefined;
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 = "Capture and control the host desktop through native OS APIs";
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
- const actions = args && typeof args === "object" ? (args as { actions?: unknown }).actions : undefined;
417
- return approvalActionSummary(actions);
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
- * Settings snapshot used to create the tool's current controller; refreshed
421
- * when a model switch crosses the coordinate-safe sizing boundary. Surfaced
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 = options => new ComputerSupervisor(options),
108
+ createController: ComputerControllerFactory = currentSession =>
109
+ new ComputerSupervisor(currentSession, undefined, undefined, callSessionTool),
435
110
  ) {
436
- this.#createController = createController;
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: ComputerParams,
129
+ params: ComputerToolInput,
468
130
  signal?: AbortSignal,
469
131
  _onUpdate?: AgentToolUpdateCallback<ComputerToolDetails>,
470
- context?: AgentToolContext,
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
- const metadata = callMetadata(context);
475
- const actions = parseActions(metadata?.actions ?? params.actions);
476
- const pendingSafetyChecks: ComputerSafetyCheck[] = metadata?.pendingSafetyChecks ?? [];
477
- if (pendingSafetyChecks.length > 0 && context?.providerSafetyApproved !== true) {
478
- throw new ToolError("Provider safety checks require interactive approval before computer input");
479
- }
480
- await this.#refreshControllerForModel();
481
- throwIfAborted(signal);
482
- const capture = await this.#controller.execute(actions.map(toDesktopAction), signal);
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
- const data = Buffer.from(capture.data).toBase64();
485
- return {
486
- content: [{ type: "image", data, mimeType: "image/png", detail: "original" }],
487
- details: {
488
- width: capture.width,
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
+ }
@@ -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
- readonly description: string;
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(