orbitx 0.2.6 → 0.2.7

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 (263) hide show
  1. package/dist/core/agent-providers.d.ts +15 -10
  2. package/dist/core/agent-providers.d.ts.map +1 -1
  3. package/dist/core/agent-providers.js +65 -15
  4. package/dist/core/agent-providers.js.map +1 -1
  5. package/dist/core/ai-provider.d.ts +52 -2
  6. package/dist/core/ai-provider.d.ts.map +1 -1
  7. package/dist/core/ai-provider.js.map +1 -1
  8. package/dist/core/anthropic-provider.d.ts +5 -0
  9. package/dist/core/anthropic-provider.d.ts.map +1 -1
  10. package/dist/core/anthropic-provider.js +100 -17
  11. package/dist/core/anthropic-provider.js.map +1 -1
  12. package/dist/core/base-agent.d.ts +19 -41
  13. package/dist/core/base-agent.d.ts.map +1 -1
  14. package/dist/core/base-agent.js +112 -136
  15. package/dist/core/base-agent.js.map +1 -1
  16. package/dist/core/deepseek-provider.d.ts +3 -0
  17. package/dist/core/deepseek-provider.d.ts.map +1 -1
  18. package/dist/core/deepseek-provider.js +59 -12
  19. package/dist/core/deepseek-provider.js.map +1 -1
  20. package/dist/core/mcp-client.d.ts +10 -2
  21. package/dist/core/mcp-client.d.ts.map +1 -1
  22. package/dist/core/mcp-client.js +28 -4
  23. package/dist/core/mcp-client.js.map +1 -1
  24. package/dist/core/mcp-server.d.ts +5 -1
  25. package/dist/core/mcp-server.d.ts.map +1 -1
  26. package/dist/core/mcp-server.js +31 -1
  27. package/dist/core/mcp-server.js.map +1 -1
  28. package/dist/core/mcp.d.ts +9 -23
  29. package/dist/core/mcp.d.ts.map +1 -1
  30. package/dist/core/mcp.js +1 -24
  31. package/dist/core/mcp.js.map +1 -1
  32. package/dist/core/ollama-provider.d.ts +3 -0
  33. package/dist/core/ollama-provider.d.ts.map +1 -1
  34. package/dist/core/ollama-provider.js +31 -7
  35. package/dist/core/ollama-provider.js.map +1 -1
  36. package/dist/core/openai-provider.d.ts +3 -0
  37. package/dist/core/openai-provider.d.ts.map +1 -1
  38. package/dist/core/openai-provider.js +51 -8
  39. package/dist/core/openai-provider.js.map +1 -1
  40. package/dist/core/skill.d.ts +2 -2
  41. package/dist/core/skill.d.ts.map +1 -1
  42. package/dist/index.d.ts +13 -20
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +9 -10
  45. package/dist/index.js.map +1 -1
  46. package/dist/templates/simple.d.ts +8 -12
  47. package/dist/templates/simple.d.ts.map +1 -1
  48. package/dist/templates/simple.js +6 -10
  49. package/dist/templates/simple.js.map +1 -1
  50. package/dist/tests/tools/agent/agent-tools.test.js +20 -17
  51. package/dist/tests/tools/agent/agent-tools.test.js.map +1 -1
  52. package/dist/tests/tools/question-answer/question-answer.test.js +1 -11
  53. package/dist/tests/tools/question-answer/question-answer.test.js.map +1 -1
  54. package/dist/tests/tools/utils/read-image.test.js +29 -17
  55. package/dist/tests/tools/utils/read-image.test.js.map +1 -1
  56. package/dist/tools/agent/hire.d.ts +1 -2
  57. package/dist/tools/agent/hire.d.ts.map +1 -1
  58. package/dist/tools/agent/hire.js +1 -4
  59. package/dist/tools/agent/hire.js.map +1 -1
  60. package/dist/tools/agent/index.d.ts +1 -3
  61. package/dist/tools/agent/index.d.ts.map +1 -1
  62. package/dist/tools/agent/index.js +0 -1
  63. package/dist/tools/agent/index.js.map +1 -1
  64. package/dist/tools/agent/list.d.ts +1 -1
  65. package/dist/tools/agent/list.d.ts.map +1 -1
  66. package/dist/tools/agent/prompt.d.ts +1 -2
  67. package/dist/tools/agent/prompt.d.ts.map +1 -1
  68. package/dist/tools/agent/prompt.js +11 -7
  69. package/dist/tools/agent/prompt.js.map +1 -1
  70. package/dist/tools/agent/report.d.ts +1 -1
  71. package/dist/tools/agent/report.d.ts.map +1 -1
  72. package/dist/tools/bash/index.d.ts +1 -3
  73. package/dist/tools/bash/index.d.ts.map +1 -1
  74. package/dist/tools/bash/index.js +0 -1
  75. package/dist/tools/bash/index.js.map +1 -1
  76. package/dist/tools/bash/list.d.ts +1 -1
  77. package/dist/tools/bash/list.d.ts.map +1 -1
  78. package/dist/tools/bash/list.js +1 -1
  79. package/dist/tools/bash/list.js.map +1 -1
  80. package/dist/tools/bash/logs.d.ts +1 -1
  81. package/dist/tools/bash/logs.d.ts.map +1 -1
  82. package/dist/tools/bash/logs.js +1 -1
  83. package/dist/tools/bash/logs.js.map +1 -1
  84. package/dist/tools/bash/run.d.ts +1 -2
  85. package/dist/tools/bash/run.d.ts.map +1 -1
  86. package/dist/tools/bash/run.js +1 -7
  87. package/dist/tools/bash/run.js.map +1 -1
  88. package/dist/tools/bash/terminate.d.ts +1 -2
  89. package/dist/tools/bash/terminate.d.ts.map +1 -1
  90. package/dist/tools/bash/terminate.js +1 -4
  91. package/dist/tools/bash/terminate.js.map +1 -1
  92. package/dist/tools/bash/wait.d.ts +1 -2
  93. package/dist/tools/bash/wait.d.ts.map +1 -1
  94. package/dist/tools/bash/wait.js +1 -7
  95. package/dist/tools/bash/wait.js.map +1 -1
  96. package/dist/tools/bash/write-input.d.ts +1 -2
  97. package/dist/tools/bash/write-input.d.ts.map +1 -1
  98. package/dist/tools/bash/write-input.js +1 -4
  99. package/dist/tools/bash/write-input.js.map +1 -1
  100. package/dist/tools/browser/click.d.ts +1 -2
  101. package/dist/tools/browser/click.d.ts.map +1 -1
  102. package/dist/tools/browser/click.js +1 -4
  103. package/dist/tools/browser/click.js.map +1 -1
  104. package/dist/tools/browser/console.d.ts +1 -1
  105. package/dist/tools/browser/console.d.ts.map +1 -1
  106. package/dist/tools/browser/create-session.d.ts +1 -2
  107. package/dist/tools/browser/create-session.d.ts.map +1 -1
  108. package/dist/tools/browser/create-session.js +1 -4
  109. package/dist/tools/browser/create-session.js.map +1 -1
  110. package/dist/tools/browser/fill.d.ts +1 -2
  111. package/dist/tools/browser/fill.d.ts.map +1 -1
  112. package/dist/tools/browser/fill.js +1 -4
  113. package/dist/tools/browser/fill.js.map +1 -1
  114. package/dist/tools/browser/get-sessions.d.ts +1 -1
  115. package/dist/tools/browser/get-sessions.d.ts.map +1 -1
  116. package/dist/tools/browser/index.d.ts +1 -3
  117. package/dist/tools/browser/index.d.ts.map +1 -1
  118. package/dist/tools/browser/index.js +0 -1
  119. package/dist/tools/browser/index.js.map +1 -1
  120. package/dist/tools/browser/inject.d.ts +1 -2
  121. package/dist/tools/browser/inject.d.ts.map +1 -1
  122. package/dist/tools/browser/inject.js +1 -4
  123. package/dist/tools/browser/inject.js.map +1 -1
  124. package/dist/tools/browser/navigate.d.ts +1 -2
  125. package/dist/tools/browser/navigate.d.ts.map +1 -1
  126. package/dist/tools/browser/navigate.js +1 -4
  127. package/dist/tools/browser/navigate.js.map +1 -1
  128. package/dist/tools/browser/network-status.d.ts +1 -1
  129. package/dist/tools/browser/network-status.d.ts.map +1 -1
  130. package/dist/tools/browser/network.d.ts +1 -1
  131. package/dist/tools/browser/network.d.ts.map +1 -1
  132. package/dist/tools/browser/read.d.ts +1 -2
  133. package/dist/tools/browser/read.d.ts.map +1 -1
  134. package/dist/tools/browser/read.js +1 -4
  135. package/dist/tools/browser/read.js.map +1 -1
  136. package/dist/tools/browser/remove-session.d.ts +1 -2
  137. package/dist/tools/browser/remove-session.d.ts.map +1 -1
  138. package/dist/tools/browser/remove-session.js +1 -4
  139. package/dist/tools/browser/remove-session.js.map +1 -1
  140. package/dist/tools/browser/screenshot.d.ts +1 -2
  141. package/dist/tools/browser/screenshot.d.ts.map +1 -1
  142. package/dist/tools/browser/screenshot.js +16 -15
  143. package/dist/tools/browser/screenshot.js.map +1 -1
  144. package/dist/tools/browser/scroll-info.d.ts +1 -1
  145. package/dist/tools/browser/scroll-info.d.ts.map +1 -1
  146. package/dist/tools/browser/scroll.d.ts +1 -2
  147. package/dist/tools/browser/scroll.d.ts.map +1 -1
  148. package/dist/tools/browser/scroll.js +1 -4
  149. package/dist/tools/browser/scroll.js.map +1 -1
  150. package/dist/tools/browser/submit-form.d.ts +1 -2
  151. package/dist/tools/browser/submit-form.d.ts.map +1 -1
  152. package/dist/tools/browser/submit-form.js +1 -4
  153. package/dist/tools/browser/submit-form.js.map +1 -1
  154. package/dist/tools/fs/create-dir.d.ts +1 -2
  155. package/dist/tools/fs/create-dir.d.ts.map +1 -1
  156. package/dist/tools/fs/create-dir.js +1 -4
  157. package/dist/tools/fs/create-dir.js.map +1 -1
  158. package/dist/tools/fs/delete.d.ts +1 -2
  159. package/dist/tools/fs/delete.d.ts.map +1 -1
  160. package/dist/tools/fs/delete.js +1 -4
  161. package/dist/tools/fs/delete.js.map +1 -1
  162. package/dist/tools/fs/edit-file.d.ts +1 -2
  163. package/dist/tools/fs/edit-file.d.ts.map +1 -1
  164. package/dist/tools/fs/edit-file.js +1 -4
  165. package/dist/tools/fs/edit-file.js.map +1 -1
  166. package/dist/tools/fs/index.d.ts +1 -3
  167. package/dist/tools/fs/index.d.ts.map +1 -1
  168. package/dist/tools/fs/index.js +0 -1
  169. package/dist/tools/fs/index.js.map +1 -1
  170. package/dist/tools/fs/list-dir.d.ts +1 -2
  171. package/dist/tools/fs/list-dir.d.ts.map +1 -1
  172. package/dist/tools/fs/list-dir.js +1 -4
  173. package/dist/tools/fs/list-dir.js.map +1 -1
  174. package/dist/tools/fs/move.d.ts +1 -2
  175. package/dist/tools/fs/move.d.ts.map +1 -1
  176. package/dist/tools/fs/move.js +1 -4
  177. package/dist/tools/fs/move.js.map +1 -1
  178. package/dist/tools/fs/read-file.d.ts +1 -2
  179. package/dist/tools/fs/read-file.d.ts.map +1 -1
  180. package/dist/tools/fs/read-file.js +1 -4
  181. package/dist/tools/fs/read-file.js.map +1 -1
  182. package/dist/tools/fs/stat.d.ts +1 -2
  183. package/dist/tools/fs/stat.d.ts.map +1 -1
  184. package/dist/tools/fs/stat.js +1 -4
  185. package/dist/tools/fs/stat.js.map +1 -1
  186. package/dist/tools/fs/write-file.d.ts +1 -2
  187. package/dist/tools/fs/write-file.d.ts.map +1 -1
  188. package/dist/tools/fs/write-file.js +1 -4
  189. package/dist/tools/fs/write-file.js.map +1 -1
  190. package/dist/tools/present/add.d.ts +1 -2
  191. package/dist/tools/present/add.d.ts.map +1 -1
  192. package/dist/tools/present/add.js +1 -4
  193. package/dist/tools/present/add.js.map +1 -1
  194. package/dist/tools/present/clear.d.ts +1 -2
  195. package/dist/tools/present/clear.d.ts.map +1 -1
  196. package/dist/tools/present/clear.js +1 -4
  197. package/dist/tools/present/clear.js.map +1 -1
  198. package/dist/tools/present/get-list.d.ts +1 -2
  199. package/dist/tools/present/get-list.d.ts.map +1 -1
  200. package/dist/tools/present/get-list.js +0 -2
  201. package/dist/tools/present/get-list.js.map +1 -1
  202. package/dist/tools/present/index.d.ts +1 -3
  203. package/dist/tools/present/index.d.ts.map +1 -1
  204. package/dist/tools/present/index.js +0 -1
  205. package/dist/tools/present/index.js.map +1 -1
  206. package/dist/tools/question-answer/index.d.ts +1 -3
  207. package/dist/tools/question-answer/index.d.ts.map +1 -1
  208. package/dist/tools/question-answer/index.js +0 -1
  209. package/dist/tools/question-answer/index.js.map +1 -1
  210. package/dist/tools/question-answer/question-answer.d.ts +1 -2
  211. package/dist/tools/question-answer/question-answer.d.ts.map +1 -1
  212. package/dist/tools/question-answer/question-answer.js +1 -4
  213. package/dist/tools/question-answer/question-answer.js.map +1 -1
  214. package/dist/tools/todo/check-task.d.ts +1 -2
  215. package/dist/tools/todo/check-task.d.ts.map +1 -1
  216. package/dist/tools/todo/check-task.js +1 -4
  217. package/dist/tools/todo/check-task.js.map +1 -1
  218. package/dist/tools/todo/create-list.d.ts +1 -2
  219. package/dist/tools/todo/create-list.d.ts.map +1 -1
  220. package/dist/tools/todo/create-list.js +1 -4
  221. package/dist/tools/todo/create-list.js.map +1 -1
  222. package/dist/tools/todo/create-task.d.ts +1 -2
  223. package/dist/tools/todo/create-task.d.ts.map +1 -1
  224. package/dist/tools/todo/create-task.js +1 -4
  225. package/dist/tools/todo/create-task.js.map +1 -1
  226. package/dist/tools/todo/get-list.d.ts +1 -1
  227. package/dist/tools/todo/get-list.d.ts.map +1 -1
  228. package/dist/tools/todo/get-list.js +1 -1
  229. package/dist/tools/todo/get-list.js.map +1 -1
  230. package/dist/tools/todo/get-lists.d.ts +1 -1
  231. package/dist/tools/todo/get-lists.d.ts.map +1 -1
  232. package/dist/tools/todo/get-lists.js +1 -1
  233. package/dist/tools/todo/get-lists.js.map +1 -1
  234. package/dist/tools/todo/index.d.ts +1 -3
  235. package/dist/tools/todo/index.d.ts.map +1 -1
  236. package/dist/tools/todo/index.js +0 -1
  237. package/dist/tools/todo/index.js.map +1 -1
  238. package/dist/tools/todo/remove-list.d.ts +1 -2
  239. package/dist/tools/todo/remove-list.d.ts.map +1 -1
  240. package/dist/tools/todo/remove-list.js +1 -4
  241. package/dist/tools/todo/remove-list.js.map +1 -1
  242. package/dist/tools/todo/remove-task.d.ts +1 -2
  243. package/dist/tools/todo/remove-task.d.ts.map +1 -1
  244. package/dist/tools/todo/remove-task.js +1 -4
  245. package/dist/tools/todo/remove-task.js.map +1 -1
  246. package/dist/tools/utils/delay.d.ts +1 -1
  247. package/dist/tools/utils/delay.d.ts.map +1 -1
  248. package/dist/tools/utils/get-current-time.d.ts +1 -1
  249. package/dist/tools/utils/get-current-time.d.ts.map +1 -1
  250. package/dist/tools/utils/index.d.ts +1 -1
  251. package/dist/tools/utils/index.d.ts.map +1 -1
  252. package/dist/tools/utils/read-image.d.ts +12 -7
  253. package/dist/tools/utils/read-image.d.ts.map +1 -1
  254. package/dist/tools/utils/read-image.js +27 -17
  255. package/dist/tools/utils/read-image.js.map +1 -1
  256. package/docs/agents.md +50 -25
  257. package/docs/api-reference.md +12 -13
  258. package/docs/getting-started.md +2 -2
  259. package/docs/mcp-architecture.md +14 -2
  260. package/docs/providers.md +114 -15
  261. package/docs/streaming.md +19 -2
  262. package/docs/tools.md +41 -8
  263. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  import { MCPTool } from "../../core/mcp.js";
2
- import { TodoInteraction } from "./interaction.js";
3
- export declare const TodoRemoveTaskTool: () => MCPTool<TodoInteraction>;
2
+ export declare const TodoRemoveTaskTool: () => MCPTool;
4
3
  export default TodoRemoveTaskTool;
5
4
  //# sourceMappingURL=remove-task.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove-task.d.ts","sourceRoot":"","sources":["../../../src/tools/todo/remove-task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD,eAAO,MAAM,kBAAkB,gCAkD7B,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"remove-task.d.ts","sourceRoot":"","sources":["../../../src/tools/todo/remove-task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,mBAAmB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,eA+C7B,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -1,5 +1,4 @@
1
1
  import { MCPTool } from "../../core/mcp.js";
2
- import { TodoInteraction } from "./interaction.js";
3
2
  import { getTask, getList, setList, deleteTaskKey } from "./storage.js";
4
3
  export const TodoRemoveTaskTool = () => new MCPTool({
5
4
  name: "todo-remove-task",
@@ -12,8 +11,7 @@ export const TodoRemoveTaskTool = () => new MCPTool({
12
11
  required: true,
13
12
  },
14
13
  ],
15
- customClass: new TodoInteraction(),
16
- execute: async (_envID, inputs, mcp, customClass) => {
14
+ execute: async (_envID, inputs, _toolCallId, mcp) => {
17
15
  const { tasks } = inputs;
18
16
  if (!Array.isArray(tasks) || tasks.some((t) => typeof t !== "string")) {
19
17
  throw new Error("tasks must be an array of task id strings");
@@ -35,7 +33,6 @@ export const TodoRemoveTaskTool = () => new MCPTool({
35
33
  await setList(mcp, list);
36
34
  }
37
35
  }
38
- customClass?.emitTodoEvent({ type: "task-removed", taskIds: tasks });
39
36
  return {
40
37
  message: "success",
41
38
  };
@@ -1 +1 @@
1
- {"version":3,"file":"remove-task.js","sourceRoot":"","sources":["../../../src/tools/todo/remove-task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,EAAE,CAAC,IAAI,OAAO,CAAkB;IACjE,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,gCAAgC;IAC7C,MAAM,EAAE;QACJ;YACI,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,+BAA+B;YAC5C,QAAQ,EAAE,IAAI;SACjB;KACJ;IACD,WAAW,EAAE,IAAI,eAAe,EAAE;IAClC,OAAO,EAAE,KAAK,EACV,MAAc,EACd,MAA2B,EAC3B,GAAS,EACT,WAA6B,EACjB,EAAE;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;QAEzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;QAE1C,KAAK,MAAM,MAAM,IAAI,KAAiB,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,IAAI;gBAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,IAAI,EAAE,CAAC;gBACP,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChE,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;QAED,WAAW,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAErE,OAAO;YACH,OAAO,EAAE,SAAS;SACrB,CAAC;IACN,CAAC;CACJ,CAAC,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"remove-task.js","sourceRoot":"","sources":["../../../src/tools/todo/remove-task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC;IAChD,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,gCAAgC;IAC7C,MAAM,EAAE;QACJ;YACI,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,+BAA+B;YAC5C,QAAQ,EAAE,IAAI;SACjB;KACJ;IACD,OAAO,EAAE,KAAK,EACV,MAAc,EACd,MAA2B,EAC3B,WAAoB,EACpB,GAAS,EACG,EAAE;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;QAEzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;QAE1C,KAAK,MAAM,MAAM,IAAI,KAAiB,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,IAAI;gBAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,IAAI,EAAE,CAAC;gBACP,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChE,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;QAED,OAAO;YACH,OAAO,EAAE,SAAS;SACrB,CAAC;IACN,CAAC;CACJ,CAAC,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -5,6 +5,6 @@ import { MCPTool } from "../../core/mcp.js";
5
5
  * a frontend time to render a status update, or throttling itself between
6
6
  * repeated tool calls) rather than firing calls back-to-back.
7
7
  */
8
- export declare const DelayTool: () => MCPTool<undefined>;
8
+ export declare const DelayTool: () => MCPTool;
9
9
  export default DelayTool;
10
10
  //# sourceMappingURL=delay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delay.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/delay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI5C;;;;;GAKG;AACH,eAAO,MAAM,SAAS,0BA0BpB,CAAC;AAEH,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"delay.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/delay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI5C;;;;;GAKG;AACH,eAAO,MAAM,SAAS,eA0BpB,CAAC;AAEH,eAAe,SAAS,CAAC"}
@@ -4,6 +4,6 @@ import { MCPTool } from "../../core/mcp.js";
4
4
  * time-sensitive search queries or reasoning about "latest"/"current" facts,
5
5
  * since the model's own training data has no reliable sense of "now".
6
6
  */
7
- export declare const GetCurrentTimeTool: () => MCPTool<undefined>;
7
+ export declare const GetCurrentTimeTool: () => MCPTool;
8
8
  export default GetCurrentTimeTool;
9
9
  //# sourceMappingURL=get-current-time.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-current-time.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/get-current-time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,0BAa7B,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"get-current-time.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/get-current-time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,eAa7B,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { GetCurrentTimeTool } from "./get-current-time.js";
2
2
  export { DelayTool } from "./delay.js";
3
3
  export { ReadImageTool } from "./read-image.js";
4
- export declare const UtilTools: () => import("../../index.js").MCPTool<undefined>[];
4
+ export declare const UtilTools: () => import("../../index.js").MCPTool[];
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAQhD,eAAO,MAAM,SAAS,qDAIrB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAQhD,eAAO,MAAM,SAAS,0CAIrB,CAAC"}
@@ -1,12 +1,17 @@
1
1
  import { MCPTool } from "../../core/mcp.js";
2
2
  /**
3
- * Reads an image straight off disk and hands it to the agent as an image
4
- * tool-output — same shape and same downstream handling as
5
- * `browser-screenshot` (see BaseAgent#resolveToolOutputForModel): depending
6
- * on how the agent is configured, the raw bytes go straight to the main
7
- * model, or a separate image-capable provider describes it first so the
8
- * main conversation doesn't have to carry image bytes.
3
+ * Reads an image straight off disk and describes it itself, via
4
+ * `mcp.executeProvider(toolCallId, "image-describer", { parts })` — works
5
+ * identically whether this tool is registered directly on an `MCPClient` or
6
+ * on a sandboxed/remote `MCPServer` (see `MCP#executeProvider` in mcp.ts). The
7
+ * raw image bytes never leave this tool; only the resulting text
8
+ * description is returned. Usage is recorded on the trusted side of that
9
+ * call, not by this tool — see `BaseAgent#executeProvider`. `parts` (not a
10
+ * plain `content` string) is what actually reaches the provider as a real
11
+ * image — every built-in `AIProvider` only builds a multimodal image block
12
+ * from `Message.parts`, so a base64 string folded into `content` would be
13
+ * sent as literal text instead.
9
14
  */
10
- export declare const ReadImageTool: () => MCPTool<undefined>;
15
+ export declare const ReadImageTool: () => MCPTool;
11
16
  export default ReadImageTool;
12
17
  //# sourceMappingURL=read-image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"read-image.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/read-image.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAgB5C;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,0BA8CxB,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"read-image.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/read-image.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAY,MAAM,mBAAmB,CAAC;AAgBtD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,eAsDxB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -14,17 +14,22 @@ function mimeTypeForPath(path) {
14
14
  return MIME_TYPES[extname(path).toLowerCase()] ?? "image/png";
15
15
  }
16
16
  /**
17
- * Reads an image straight off disk and hands it to the agent as an image
18
- * tool-output — same shape and same downstream handling as
19
- * `browser-screenshot` (see BaseAgent#resolveToolOutputForModel): depending
20
- * on how the agent is configured, the raw bytes go straight to the main
21
- * model, or a separate image-capable provider describes it first so the
22
- * main conversation doesn't have to carry image bytes.
17
+ * Reads an image straight off disk and describes it itself, via
18
+ * `mcp.executeProvider(toolCallId, "image-describer", { parts })` — works
19
+ * identically whether this tool is registered directly on an `MCPClient` or
20
+ * on a sandboxed/remote `MCPServer` (see `MCP#executeProvider` in mcp.ts). The
21
+ * raw image bytes never leave this tool; only the resulting text
22
+ * description is returned. Usage is recorded on the trusted side of that
23
+ * call, not by this tool — see `BaseAgent#executeProvider`. `parts` (not a
24
+ * plain `content` string) is what actually reaches the provider as a real
25
+ * image — every built-in `AIProvider` only builds a multimodal image block
26
+ * from `Message.parts`, so a base64 string folded into `content` would be
27
+ * sent as literal text instead.
23
28
  */
24
29
  export const ReadImageTool = () => new MCPTool({
25
30
  name: "read-image",
26
- description: "read an image file from disk and hand it to the agent. works just like browser-screenshot: returns an image tool-output — depending on how the agent is configured, the raw image may be handed directly to the model, or first described by a separate image-capable AI so the main conversation doesn't have to carry image bytes. " +
27
- "use `focusHint` to tell that description step what you actually care about (e.g. 'read out any visible text' or 'check whether the chart trend is up or down'), so the resulting description is useful for your task instead of a generic caption.",
31
+ description: "read an image file from disk and get back a text description of it. " +
32
+ "use `focusHint` to tell the description step what you actually care about (e.g. 'read out any visible text' or 'check whether the chart trend is up or down'), so the resulting description is useful for your task instead of a generic caption.",
28
33
  inputs: [
29
34
  {
30
35
  name: "path",
@@ -39,7 +44,7 @@ export const ReadImageTool = () => new MCPTool({
39
44
  required: false,
40
45
  },
41
46
  ],
42
- execute: async (_envID, inputs) => {
47
+ execute: async (_envID, inputs, toolCallId, mcp) => {
43
48
  const { path, focusHint } = inputs;
44
49
  if (!path || typeof path !== "string") {
45
50
  throw new Error("path must be a non-empty string");
@@ -56,14 +61,19 @@ export const ReadImageTool = () => new MCPTool({
56
61
  throw new Error(`"${path}" is a directory, not a file`);
57
62
  throw err;
58
63
  }
59
- return {
60
- type: "image",
61
- output: {
62
- image,
63
- mimeType: mimeTypeForPath(fullPath),
64
- ...(focusHint ? { focusHint } : {}),
65
- },
66
- };
64
+ if (!mcp || !toolCallId) {
65
+ throw new Error("read-image requires an MCP context");
66
+ }
67
+ const instruction = focusHint
68
+ ? `Describe this image concisely for another AI agent that cannot see it. Focus specifically on: ${focusHint}`
69
+ : "Describe this image concisely for another AI agent that cannot see it. Mention layout, visible text, colors, and anything that looks unusual or broken.";
70
+ const { output } = await mcp.executeProvider(toolCallId, "image-describer", {
71
+ parts: [
72
+ { type: "text", text: instruction },
73
+ { type: "image", image, mimeType: mimeTypeForPath(fullPath) },
74
+ ],
75
+ });
76
+ return { description: output.content };
67
77
  },
68
78
  });
69
79
  export default ReadImageTool;
@@ -1 +1 @@
1
- {"version":3,"file":"read-image.js","sourceRoot":"","sources":["../../../src/tools/utils/read-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,UAAU,GAA2B;IACvC,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;CACtB,CAAC;AAEF,SAAS,eAAe,CAAC,IAAY;IACjC,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,WAAW,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC;IAC3C,IAAI,EAAE,YAAY;IAClB,WAAW,EACP,uUAAuU;QACvU,oPAAoP;IACxP,MAAM,EAAE;QACJ;YACI,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,4EAA4E;YACzF,QAAQ,EAAE,IAAI;SACjB;QACD;YACI,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,4CAA4C;YACzD,QAAQ,EAAE,KAAK;SAClB;KACJ;IACD,OAAO,EAAE,KAAK,EAAE,MAAc,EAAE,MAA2B,EAAgB,EAAE;QACzE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;QAEnC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACD,KAAK,GAAG,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAChB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,GAAG,CAAC,CAAC;YACzE,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,8BAA8B,CAAC,CAAC;YACnF,MAAM,GAAG,CAAC;QACd,CAAC;QAED,OAAO;YACH,IAAI,EAAE,OAAO;YACb,MAAM,EAAE;gBACJ,KAAK;gBACL,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC;gBACnC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"read-image.js","sourceRoot":"","sources":["../../../src/tools/utils/read-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAY,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,UAAU,GAA2B;IACvC,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;CACtB,CAAC;AAEF,SAAS,eAAe,CAAC,IAAY;IACjC,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,WAAW,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC;IAC3C,IAAI,EAAE,YAAY;IAClB,WAAW,EACP,sEAAsE;QACtE,mPAAmP;IACvP,MAAM,EAAE;QACJ;YACI,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,4EAA4E;YACzF,QAAQ,EAAE,IAAI;SACjB;QACD;YACI,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,4CAA4C;YACzD,QAAQ,EAAE,KAAK;SAClB;KACJ;IACD,OAAO,EAAE,KAAK,EAAE,MAAc,EAAE,MAA2B,EAAE,UAAmB,EAAE,GAAS,EAAgB,EAAE;QACzG,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;QAEnC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACD,KAAK,GAAG,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAChB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,GAAG,CAAC,CAAC;YACzE,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,8BAA8B,CAAC,CAAC;YACnF,MAAM,GAAG,CAAC;QACd,CAAC;QAED,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,WAAW,GAAG,SAAS;YACzB,CAAC,CAAC,iGAAiG,SAAS,EAAE;YAC9G,CAAC,CAAC,yJAAyJ,CAAC;QAEhK,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,eAAe,CAAC,UAAU,EAAE,iBAAiB,EAAE;YACxE,KAAK,EAAE;gBACH,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE;gBACnC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE;aAChE;SACJ,CAAC,CAAC;QAEH,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IAC3C,CAAC;CACJ,CAAC,CAAC;AAEH,eAAe,aAAa,CAAC"}
package/docs/agents.md CHANGED
@@ -11,26 +11,36 @@
11
11
  ```ts
12
12
  new BaseAgent({
13
13
  instruction: string;
14
+ safetyPolicies?: string; // default: "" — see "Guardrail system prompt" below
14
15
  allowedTools: MCPTool<any>[];
15
16
  aiProvider: AIProvider | AgentProviderEntry[]; // see Providers doc
16
17
  mcpClient: MCPClient;
17
18
  skills?: Skill[];
18
19
  maxMemorizeToken?: number; // default: derived from the main provider's contextWindow * safeUsageRatio
19
20
  initData?: {
20
- memory: string;
21
- messagesFull: Message[];
22
- fullInputMissTokens: number;
23
- fullInputHitTokens: number;
24
- fullOutputTokens: number;
25
- messagesCompact: Message[];
26
- currentInputMissTokens: number;
27
- currentInputHitTokens: number;
28
- currentOutputTokens: number;
21
+ compactMemory: string;
22
+ retiredMessages: Message[]; // complete, ever-appended history of retired messages — each Message carries its own usage/timestamp
23
+ messagesCompact: Message[]; // the working context currently replayed to the provider every turn
24
+ };
25
+ features?: {
26
+ executeProviderFromMCPTool?: boolean; // default: false — see "executeProvider feature flag" below
29
27
  };
30
28
  });
31
29
  ```
32
30
 
33
- `SimpleAgent`'s constructor is the same shape minus `mcpClient`/`allowedTools` (it builds those from `tools`), plus `tools?: MCPTool<any>[]` and `maxMemorizeToken` defaulting to `16000` instead of being derived.
31
+ ### Guardrail system prompt
32
+
33
+ `safetyPolicies` is a separate `role: "system"` message, sent as its own leading entry ahead of the regular system prompt (see [Instructions, skills, and memory](#instructions-skills-and-memory-in-the-system-prompt)) on every main-provider `chat()` call in the run loop. Keep house rules/guardrails here rather than folding them into `instruction`, since it's a separate, cache-stable prefix ahead of the rest of the system prompt.
34
+
35
+ It is **not** prepended on an `executeProvider` call (see below) — stuffing a policy paragraph ahead of a narrow, single-purpose call (e.g. "describe this image") measurably degrades that call's output, and not every registered provider role even accepts a `system` message the way the main LLM does. A pre-dispatch content-safety check is the intended replacement there; not implemented yet.
36
+
37
+ ### `executeProvider` feature flag
38
+
39
+ A tool's `execute()` can reach a registered `AIProvider` directly via `mcp.executeProvider(toolCallId, type, input)` (see [Tools](./tools.md#mcpexecuteprovider--calling-a-provider-from-inside-a-tool)) — a bigger trust surface than a tool that only returns data to the model, since it lets a tool spend real provider calls (and, per the point above, without a guardrail system prompt ahead of them). It's off by default: `BaseAgent#executeProvider` throws immediately unless `features.executeProviderFromMCPTool` was set `true` on construction. This gates every role uniformly (`image-describer` included) — enable it once you're ready to trust the tools you've allowed with direct provider access.
40
+
41
+ `SimpleAgent`'s constructor is the same shape minus `mcpClient`/`allowedTools` (it builds those from `tools`), plus `tools?: MCPTool<any>[]` and `maxMemorizeToken` defaulting to `16000` instead of being derived. `safetyPolicies` and `features` pass straight through to the underlying `BaseAgent`.
42
+
43
+ Token accounting no longer lives in `initData` as separate counters — every `Message` carries its own `usage` (see [Token accounting](#token-accounting)), so `retiredMessages`/`messagesCompact` alone are enough to resume with full token history intact.
34
44
 
35
45
  ## The run loop
36
46
 
@@ -60,6 +70,8 @@ Practical implication: don't treat an early queued call's resolved `run()` promi
60
70
 
61
71
  `immediateStop()` requests the same stop but returns right away without waiting for confirmation, **and** aborts the in-flight `chat()` call to the main provider immediately — the underlying HTTP request is cancelled rather than left to run to completion, so no further output tokens get generated (and billed) past that point. Input tokens, and any output already generated before the abort landed, are already committed — cancelling can't retroactively make those free. This is the one to reach for when a call has to be killed regardless of how far through it is (e.g. a credit balance that just went negative). A tool dispatch already in progress isn't aborted (tools have no generic cancellation hook) — the loop stops as soon as that call returns. Abort support is real for Anthropic, OpenAI, and DeepSeek (both streaming and non-streaming); for Ollama it only takes effect on streaming calls — see the note in `ollama-provider.ts`.
62
72
 
73
+ If you're deciding *when* to call `immediateStop()` from outside — e.g. against an external credit/cost system — watch `usage` on the `StreamCallback` chunks (see [Streaming](./streaming.md)) rather than waiting for `run()` to resolve: it's the same callback passed into `run()`, forwarded straight through to the provider, so external code sees the same numbers `BaseAgent` itself uses to build `Message.usage`. Note this is only genuinely live for Anthropic — the other three providers only report usage once a turn is already complete, so for them there's no earlier signal to react to, cancellation-timing-wise, than the final chunk.
74
+
63
75
  For a **streaming** call, an abort doesn't throw the turn away: `chat()` resolves normally with whatever text, thinking, and tool calls were already accumulated before the cutoff — the same as if the model had naturally stopped there — so anything already sent to `streamCallback` still ends up recorded in message history instead of vanishing. A tool call the model was still in the middle of generating when the abort hit is dropped even then (its arguments may be truncated JSON); only tool calls that had already fully finished are included — e.g. if the model requested 3 tool calls and the abort landed while the 3rd was still streaming, only the first 2 come back. For a **non-streaming** call there's nothing to salvage (the API only returns a response once, in full) — abort there still surfaces as a thrown error, and `BaseAgent` discards that turn entirely rather than recording a broken one.
64
76
 
65
77
  `stop()` is a deprecated alias for `safeStop()` and will be removed in the 1.0.0 major release.
@@ -74,37 +86,50 @@ OrbitX never truncates or deletes conversation history. Compaction happens throu
74
86
 
75
87
  Calling `compact_memory`:
76
88
 
77
- 1. Replaces the running `memory` string with `new_memory`, which is injected into every future system prompt (`MEMORY:` section).
78
- 2. Clears `messagesCompact` — the *working* context sent to the provider each turn.
79
- 3. Leaves `messagesFull` — the complete, ever-appended history — untouched, so nothing is lost; it's just no longer replayed to the model every turn.
80
- 4. Rolls the current token counters into the lifetime totals (see [Token accounting](#token-accounting)) and resets them to zero.
89
+ 1. Replaces the running `compactMemory` string with `new_memory`, which is injected into every future system prompt (`MEMORY:` section). The cached system prompt is invalidated at the same time, so the very next turn's system prompt is rebuilt with the new summary rather than replaying the stale one.
90
+ 2. Moves everything currently in `messagesCompact` — the *working* context sent to the provider each turn — into `retiredMessages`, then clears `messagesCompact`. Nothing is lost, it's just retired from what gets replayed to the model every turn. Nothing needs to be separately "rolled into a lifetime total" — every message already carries its own `usage`, so `getFullTotalToken()` (see [Token accounting](#token-accounting)) just sums `retiredMessages` + `messagesCompact` directly.
91
+
92
+ Before each turn, `BaseAgent` also checks the current context size: the summed `inputMissTokens` of every message's `"main"` usage entry across `messagesCompact` (see [Token accounting](#token-accounting)) — a cheap proxy for context size, not a precise replay-cost measurement. When that sum exceeds `maxMemorizeToken`, the agent doesn't compact on the model's behalf — instead it appends a `role: "user"` message telling the model to call `compact_memory` before doing anything else, and loops back for another turn. This keeps compaction on the same native tool-call path the model already uses for everything else, rather than a side-channel the model never sees; a model that ignores the nudge (rare, since the instruction is explicit) will simply see it repeated on the next turn until it complies.
81
93
 
82
- `BaseAgent` also tracks two token totals per run: `currentInputMissTokens` (uncached input) and `currentLastOutputTokens` (the last turn's output). When their sum exceeds `maxMemorizeToken`, the agent doesn't compact on the model's behalf — instead it appends a `role: "user"` message telling the model to call `compact_memory` before doing anything else, and loops back for another turn. This keeps compaction on the same native tool-call path the model already uses for everything else, rather than a hidden side-channel; a model that ignores the nudge (rare, since the instruction is explicit) will simply see it repeated on the next turn until it complies.
94
+ This nudge message (and its matching `StreamCallback` chunk) is marked `hidden: true`, since it's `BaseAgent` talking to the model on the caller's behalf, not something a human said or the model produced — a chat UI built on this SDK should filter it out of what it shows an end user, the same way it wouldn't show raw system-prompt text. See [Streaming](./streaming.md) for the full `Message`/chunk field reference.
83
95
 
84
96
  `maxMemorizeToken`, if not passed explicitly, is derived as `contextWindow * safeUsageRatio` (default ratio `0.5`) from the main provider's `getCapabilities()` — so it scales automatically with whatever model you plug in.
85
97
 
86
98
  ## Persisting and resuming state
87
99
 
88
- `getCurrentAgentStates()` returns exactly the shape expected by the `initData` constructor option — `memory`, `messagesFull`, `messagesCompact`, the six main-loop token counters, and the two image-usage counters (`imageInputMissTokens`, `imageOutputTokens` — see [Token accounting](#token-accounting)). Serialize it (it's plain JSON) after any `run()` call, and pass it back into a fresh `BaseAgent`/`SimpleAgent` to resume — same conversation, same memory, same token history. See the example in [Getting Started](./getting-started.md#recovering-an-agents-state).
100
+ `getCurrentAgentStates()` returns exactly the shape expected by the `initData` constructor option — `compactMemory`, `retiredMessages`, `messagesCompact`. Serialize it (it's plain JSON) after any `run()` call, and pass it back into a fresh `BaseAgent`/`SimpleAgent` to resume — same conversation, same memory, same token history, since every message already carries its own `usage`. See the example in [Getting Started](./getting-started.md#recovering-an-agents-state).
89
101
 
90
- The two image counters are optional on `initData` — a snapshot taken before image-usage tracking existed still loads, with both defaulting to `0`.
102
+ A snapshot taken with an older SDK version, before per-message `usage` existed, still loads fine — messages with no `usage` field just contribute `0` to every token total below.
91
103
 
92
104
  ## Token accounting
93
105
 
94
- Four read methods, all returning `{ total, inputHit, inputMiss, output }`:
106
+ Token usage lives on `Message.usage` (`MessageUsage[]`), not on separate counters — `BaseAgent` populates it as `chat()` responses come back, and the four read methods below are pure derived sums over `retiredMessages`/`messagesCompact`. There's no rollover bookkeeping to keep in sync on compaction (see [Memory compaction](#memory-compaction)): a message lives in exactly one of `retiredMessages` (retired) or `messagesCompact` (active) at a time — they're disjoint, not overlapping — so "already compacted away" is simply `retiredMessages` itself, with no slicing required. The complete history at any point is the concatenation of the two, in that order, not `retiredMessages` alone.
107
+
108
+ Each entry in `Message.usage` is one of:
109
+
110
+ ```ts
111
+ { type: ProviderType; unit: "tokens"; inputMissTokens: number; inputCacheTokens: number; outputTokens: number }
112
+ { type: ProviderType; unit: "cost"; cost: number }
113
+ ```
114
+
115
+ `type` is the provider role that produced the entry (see [Provider roles](./providers.md#provider-roles) — `"main"`, `"image-describer"`, or anything else registered/looked up). `unit` distinguishes real token billing from anything that isn't naturally token-shaped — an image/audio/3D-generation call billed as a flat price reports `unit: "cost"` instead.
116
+
117
+ **Attribution** — a `type: "main"` entry is set only on the assistant message a `chat()` call produced, straight from that call's own `inputMissTokens`/`inputCacheTokens`/`outputTokens`. There's no splitting or attribution onto the user/tool messages that triggered the call, and no per-message context-size measurement — a user or tool message never carries a `"main"` entry. These are real, provider-reported numbers (Anthropic's `cache_read_input_tokens`/`cache_creation_input_tokens`, DeepSeek's `prompt_cache_hit_tokens`/`prompt_cache_miss_tokens`, OpenAI's `prompt_tokens_details.cached_tokens`), not a client-side guess — see [Providers](./providers.md) for what each one actually reports and Ollama's lack of any cache concept (`inputCacheTokens` always `0` there).
118
+
119
+ The compaction trigger (see [Memory compaction](#memory-compaction)) sums `inputMissTokens` across every assistant message's `"main"` entry in `messagesCompact` as a cheap proxy for context size — not a precise measurement of what replaying `messagesCompact` right now would actually cost, since each entry reflects the call that produced that one assistant message, not the whole context at that point.
120
+
121
+ Every other `type` entry comes from a tool calling `mcp.executeProvider(toolCallId, type, input)` (see [Tools](./tools.md#mcpexecuteprovider--calling-a-provider-from-inside-a-tool)) — but the entry itself is recorded by `BaseAgent`, the instant the real provider call resolves, never by the tool's own return value. This is deliberate: a tool can't under-report (or hide) what it spent, because it never holds the usage number to begin with — `BaseAgent` keys it by `toolCallId` and moves it onto that call's result `Message.usage` only after `execute()` returns, regardless of what the tool itself returns. `read-image`/`browser-screenshot`, for example, trigger a `type: "image-describer", unit: "tokens"` entry with no hit/miss split (each call is a fresh, standalone chat issued directly against the resolved provider by `BaseAgent`, so every input token counts as `inputMissTokens`).
95
122
 
96
- - `getCurrentTotalTokens()` — main-loop tokens used since the last memory compaction.
97
- - `getFullTotalTokens()` — main-loop tokens used across all *prior* compacted segments.
98
- - `getImageTotalTokens()` — tokens used by every `ImageDescriber` call this agent has made (see [Providers](./providers.md#two-provider-roles-main--image)), tracked separately since a one-off image-description call has no conversation history to compact and so doesn't fit the current/full split.
99
- - `getTotalTokens()` — the sum of all three (lifetime total for this agent instance, main loop and image calls combined).
123
+ `executeProvider` picks `unit: "tokens"` vs `unit: "cost"` dynamically from what the provider's `ChatResponse` actually reported, rather than assuming one or the other: if the response set `cost` (a flat, non-token price — see [`ChatResponse`](./providers.md#writing-a-custom-provider)), the entry is `unit: "cost"`; otherwise it falls back to the token fields (`inputMissTokens`/`inputCacheTokens`/`outputTokens`, each defaulting to `0` if the provider omitted them). Every built-in provider (Anthropic/OpenAI/DeepSeek/Ollama) is token-billed and always takes the token path — `cost` only comes into play for a custom provider registered under an image/video/audio-generation-style role that bills per-request instead of per-token.
100
124
 
101
- `inputHit`/`inputMiss` on the main-loop methods track cached vs. uncached input tokens, since the compacted-context-replay pattern means most of a long conversation's prefix is a cache hit on providers that support prompt caching — this is a heuristic `BaseAgent` computes itself from the growing/reset conversation history: whatever was `inputMiss` last turn rolls into `inputHit` this turn, and `inputMiss` becomes this turn's raw input token count.
125
+ **The four read methods:**
102
126
 
103
- `getImageTotalTokens()`'s `inputHit` is always `0` — each `ImageDescriber` call is a fresh, standalone chat with no prior turns to replay, so there's nothing for that heuristic to apply to. Every input token an image call reports counts as `inputMiss`.
127
+ - `getCompactTotalTokens(type: ProviderType)` / `getFullTotalToken(type: ProviderType)` — return `{ total, inputHit, inputMiss, output }`, summing `unit: "tokens"` entries matching `type` over `messagesCompact` only, or `retiredMessages` + `messagesCompact` (this agent's whole lifetime), respectively.
128
+ - `getCompactTotalCost(type: ProviderType)` / `getFullTotalCost(type: ProviderType)` — return a plain `number`, summing `unit: "cost"` entries matching `type` over the same two scopes.
104
129
 
105
130
  ## Instructions, skills, and memory in the system prompt
106
131
 
107
- The system prompt is built once (lazily, on first use) and never changes for the lifetime of an agent instance — a stable prefix, which matters for provider-side prompt caching. It's assembled from: a fixed "you have tools" preamble, each skill's name/description/instructions (see [Skills](./skills.md)), the current `MEMORY:` block, and your `instruction` string, in that order.
132
+ The system prompt is built lazily on first use and cached — a stable prefix between compactions, which matters for provider-side prompt caching. It's assembled from: a fixed "you have tools" preamble, each skill's name/description/instructions (see [Skills](./skills.md)), the current `MEMORY:` block (sourced from `compactMemory`), and your `instruction` string, in that order. Each `compact_memory` call invalidates the cache (see [Memory compaction](#memory-compaction)), so the next turn's system prompt is rebuilt with the fresh `MEMORY:` block instead of the one that was current when the prompt was first built.
108
133
 
109
134
  ## Multi-agent: `WorkerAgent` *(experimental)*
110
135
 
@@ -6,12 +6,11 @@ Flat index of every public export from `orbitx` (see `src/index.ts`), grouped by
6
6
 
7
7
  - `AIProvider` (abstract base class)
8
8
  - `OpenAIProvider`, `AnthropicProvider`, `DeepSeekProvider`, `OllamaProvider`
9
- - `resolveAgentProviders()`
10
- - Types: `AgentProviderRole`, `AgentProviderEntry`, `AgentProvidersInput`, `ResolvedAgentProviders`
11
- - Types: `ChatResponse`, `Message`, `StreamCallback`, `ToolSchema`, `ToolCallRequest`, `MessageContentPart`, `ProviderCapabilities`
9
+ - `resolveAgentProviders()`, `ProviderRegistry`
10
+ - Types: `AgentProviderEntry`, `AgentProvidersInput`
11
+ - Types: `ChatResponse`, `Message`, `MessageUsage`, `MessageUsageTokens`, `MessageUsageCost`, `StreamCallback`, `ToolSchema`, `ToolCallRequest`, `MessageContentPart`, `ProviderCapabilities`, `ProviderType` — see [Providers](./providers.md#provider-roles)
12
12
  - `resolveThinkEffortLevel()`, `clampThinkEffort()` — the universal 0-1 `thinkEffort` → provider-native-level mapping helpers; see [Providers](./providers.md#think-effort)
13
13
  - Type: `ThinkEffortLevel`
14
- - `ImageDescriber`, type `ImageDescription` — used internally by `BaseAgent` to describe image tool output via the `image` provider role; exported for direct use if you want to describe an image outside the agent loop. `describe()` returns an `ImageDescription` (`{ description, inputTokens, outputTokens }`), not a bare string.
15
14
  - `toOpenAIFunctionTools()` — translates a `ToolSchema[]` into OpenAI's function-calling format; exported for building custom providers.
16
15
 
17
16
  ## Agents — see [Agents](./agents.md)
@@ -23,21 +22,21 @@ Flat index of every public export from `orbitx` (see `src/index.ts`), grouped by
23
22
 
24
23
  ## Tools — see [Tools](./tools.md)
25
24
 
26
- - `MCPTool`, `MCPCustomClass`, `generateRefId()`, `normalizeToolOutput()`
25
+ - `MCPTool`, `generateRefId()`, `normalizeToolOutput()`
27
26
  - Types: `MCP`, `MCPToolOutput`
28
27
 
29
- Built-in tool factories, grouped by domain (each domain also exports a `*Tools()` array and a `*Interaction` EventEmitter):
28
+ Built-in tool factories, grouped by domain (each domain also exports a `*Tools()` array):
30
29
 
31
30
  | Domain | Exports |
32
31
  |---|---|
33
- | Filesystem | `FsReadFileTool`, `FsWriteFileTool`, `FsEditFileTool`, `FsListDirTool`, `FsCreateDirTool`, `FsDeleteTool`, `FsMoveTool`, `FsStatTool`, `FsTools`, `FsInteraction`, type `FsEvent` |
34
- | Bash | `BashRunTool`, `BashWaitTool`, `BashLogsTool`, `BashListTool`, `BashWriteInputTool`, `BashTerminateTool`, `BashTools`, `BashInteraction`, type `BashEvent` |
35
- | Browser | `BrowserCreateSessionTool`, `BrowserRemoveSessionTool`, `BrowserGetSessionsTool`, `BrowserNavigateTool`, `BrowserConsoleTool`, `BrowserInjectTool`, `BrowserReadTool`, `BrowserClickTool`, `BrowserFillTool`, `BrowserScrollInfoTool`, `BrowserScrollTool`, `BrowserNetworkStatusTool`, `BrowserNetworkTool`, `BrowserSubmitFormTool`, `BrowserScreenshotTool`, `BrowserTools`, `BrowserInteraction`, type `BrowserEvent` |
36
- | Todo | `TodoCreateListTool`, `TodoRemoveListTool`, `TodoGetListsTool`, `TodoGetListTool`, `TodoCreateTaskTool`, `TodoRemoveTaskTool`, `TodoCheckTaskTool`, `TodoTools`, `TodoInteraction`, types `TodoTask`, `TodoList`, `TodoEvent` |
37
- | Present | `PresentAddTool`, `PresentClearTool`, `PresentGetListTool`, `PresentTools`, `getPresentFolder()`, `PresentInteraction`, type `PresentEvent` |
38
- | Question/Answer | `QuestionAnswerTool`, `QuestionAnswerTools`, `QuestionAnswerInteraction`, type `QuestionAnswerEvent` |
32
+ | Filesystem | `FsReadFileTool`, `FsWriteFileTool`, `FsEditFileTool`, `FsListDirTool`, `FsCreateDirTool`, `FsDeleteTool`, `FsMoveTool`, `FsStatTool`, `FsTools` |
33
+ | Bash | `BashRunTool`, `BashWaitTool`, `BashLogsTool`, `BashListTool`, `BashWriteInputTool`, `BashTerminateTool`, `BashTools` |
34
+ | Browser | `BrowserCreateSessionTool`, `BrowserRemoveSessionTool`, `BrowserGetSessionsTool`, `BrowserNavigateTool`, `BrowserConsoleTool`, `BrowserInjectTool`, `BrowserReadTool`, `BrowserClickTool`, `BrowserFillTool`, `BrowserScrollInfoTool`, `BrowserScrollTool`, `BrowserNetworkStatusTool`, `BrowserNetworkTool`, `BrowserSubmitFormTool`, `BrowserScreenshotTool`, `BrowserTools` |
35
+ | Todo | `TodoCreateListTool`, `TodoRemoveListTool`, `TodoGetListsTool`, `TodoGetListTool`, `TodoCreateTaskTool`, `TodoRemoveTaskTool`, `TodoCheckTaskTool`, `TodoTools`, types `TodoTask`, `TodoList` |
36
+ | Present | `PresentAddTool`, `PresentClearTool`, `PresentGetListTool`, `PresentTools`, `getPresentFolder()` |
37
+ | Question/Answer | `QuestionAnswerTool`, `QuestionAnswerTools` |
39
38
  | Utility | `GetCurrentTimeTool`, `DelayTool`, `ReadImageTool`, `UtilTools` |
40
- | Multi-agent *(experimental)* | `AgentTools(availableAgents, options?)`, `AgentListTool`, `AgentHireTool`, `AgentPromptTool`, `AgentReportTool`, `AgentRegistry`, `AgentInteraction`, type `AgentInfo`, type `AgentEvent`, type `AgentToolsOptions` — see [Agents](./agents.md#multi-agent-workeragent-experimental) |
39
+ | Multi-agent *(experimental)* | `AgentTools(availableAgents, options?)`, `AgentListTool`, `AgentHireTool`, `AgentPromptTool`, `AgentReportTool`, `AgentRegistry`, type `AgentInfo`, type `AgentToolsOptions` — see [Agents](./agents.md#multi-agent-workeragent-experimental) |
41
40
 
42
41
  ## Skills — see [Skills](./skills.md)
43
42
 
@@ -49,7 +49,7 @@ Every provider implements the same `AIProvider` interface, so switching models i
49
49
 
50
50
  ## Recovering an agent's state
51
51
 
52
- `getCurrentAgentStates()` returns a plain JSON-serializable snapshot: message history, running memory summary, and token counters. Persist it anywhere; pass it back in as `initData` to resume the exact same agent later (after a process restart, in a new request handler, etc.):
52
+ `getCurrentAgentStates()` returns a plain JSON-serializable snapshot: message history (each message carrying its own token usage) and running memory summary. Persist it anywhere; pass it back in as `initData` to resume the exact same agent later (after a process restart, in a new request handler, etc.):
53
53
 
54
54
  ```ts
55
55
  import { writeFileSync, existsSync, readFileSync } from "fs";
@@ -69,7 +69,7 @@ const agent = new SimpleAgent({
69
69
  writeFileSync("./state.json", JSON.stringify(agent.getCurrentAgentStates()));
70
70
  ```
71
71
 
72
- Messages are only ever appended, never rewritten. When the conversation grows past the memory threshold, OrbitX keeps the full history on disk (`messagesFull`) but resets the model's *working* context from a running summary, so an agent can keep going indefinitely without losing the ability to act on what happened earlier. Mechanics are covered in [Agents](./agents.md#memory-compaction).
72
+ Messages are only ever appended, never rewritten. When the conversation grows past the memory threshold, OrbitX retires the working context into `retiredMessages` and resets the model's *active* context (`messagesCompact`) from a running summary (`compactMemory`), so an agent can keep going indefinitely without losing the ability to act on what happened earlier — nothing on disk is ever deleted, it just moves from one array to the other. Mechanics are covered in [Agents](./agents.md#memory-compaction).
73
73
 
74
74
  ## Next steps
75
75
 
@@ -26,9 +26,12 @@ Registers tools and listens on its `connection` for incoming tool-call requests,
26
26
  ```ts
27
27
  new MCPClient(envID: string, connection: MCPConnection | MCPConnection[], storage?: MCPStorage, rng?: MCPRNG, mcpFilter?: MCPFilter);
28
28
  mcpClient.getTools(): Promise<ToolSchema[]>;
29
- mcpClient.callTool(name: string, inputs: Record<string, any>): Promise<MCPToolOutput>;
29
+ mcpClient.callTool(name: string, inputs: Record<string, any>, toolCallId?: string): Promise<MCPToolOutput>;
30
+ mcpClient.setExecuteProviderHandler(handler: (toolCallId: string, type: ProviderType, input: Record<string, any>) => Promise<{ output: Record<string, any> }>): void;
30
31
  ```
31
32
 
33
+ `toolCallId` on `callTool()` and the handler installed via `setExecuteProviderHandler()` are what `BaseAgent` wires up automatically so `mcp.executeProvider(...)` works inside a tool's `execute()` — see [Custom `MCP` subclasses](#custom-mcp-subclasses) below. You don't need to call either yourself unless you're driving `MCPClient` outside of `BaseAgent`.
34
+
32
35
  This is what you pass into `BaseAgent({ mcpClient, ... })`. `envID` scopes storage/RNG state per logical environment/session — pass a stable id (e.g. a user or conversation id) if you want isolated tool state per agent instance sharing a server. Accepting an array of connections lets one client fan out across multiple servers.
33
36
 
34
37
  `mcpFilter` (an `MCPFilter`) redacts sensitive substrings/patterns from tool output before it reaches the model — see [`MCPFilter`](#mcpfilter-redacting-tool-output) below.
@@ -100,4 +103,13 @@ Pass literal strings (e.g. an API key you never want echoed back to the model) o
100
103
 
101
104
  ## Custom `MCP` subclasses
102
105
 
103
- Both `MCPServer` and `MCPClient` extend the abstract `MCP` class (`getStorage()`, `getRNG()`). Tool `execute` functions receive the calling `MCP` instance as their third argument, so a custom `MCPCustomClass`-based tool (see [Tools](./tools.md#the-mcptool-shape)) can reach storage/RNG scoped to whichever client dispatched the call.
106
+ Both `MCPServer` and `MCPClient` extend the abstract `MCP` class — `getStorage()`, `getRNG()`, and `executeProvider(toolCallId, type, input)`. Tool `execute` functions receive the calling `MCP` instance directly, as their 4th argument (after a `toolCallId` — see [Tools](./tools.md#the-mcptool-shape)), so a tool can reach storage/RNG scoped to whichever client dispatched the call.
107
+
108
+ `executeProvider` is where the two subclasses genuinely differ:
109
+
110
+ - **`MCPClient`** answers it in-process, by delegating to a handler `BaseAgent` installs on the client via `setExecuteProviderHandler(...)` at construction time. `MCPClient` itself never imports anything from `ai-provider.ts` — it just holds an opaque callback.
111
+ - **`MCPServer`** has no such handler — it never touches a provider directly. It proxies the request over its `MCPConnection` (an `executeProvider`/`executeProviderResponse` message pair, mirroring `toolCall`/`toolCallCallback` but initiated by the server instead of the client) to whichever `MCPClient` is attached, which runs the same installed handler and answers back.
112
+
113
+ So a **server-registered** tool — the kind meant to run inside `MCPComputer`'s sandbox, or in a genuinely separate process over IPC/WS — can still call `mcp.executeProvider(...)` and get a real result, without a provider instance (or the credentials it holds) ever crossing into that process. Only a scoped request/response for one call does. This is deliberate: filesystem/browser-touching tools like `ReadImageTool`/`BrowserScreenshotTool` are exactly the kind this doc already recommends sandboxing (see above), and they both need a provider to do their job.
114
+
115
+ That accessor also carries provider access (`agent.getProvider(type)`/`getProviders(type)`) — but **only for a tool dispatched by `MCPClient.callTool`'s local branch** (a tool registered directly on the client, per the section above). `MCPServer`'s dispatch (both the `toolCall` handler above and, transitively, anything a remote/sandboxed tool triggers) builds an accessor with no provider registry behind it at all, so `getProvider`/`getProviders` there always resolve `undefined`. This is deliberate, not a gap to work around: provider instances hold API keys, and `MCPServer` is precisely the thing that runs inside a separate process or an `MCPComputer` sandbox — credentials never need to, and never do, cross that boundary. A tool that needs to call a provider directly (e.g. to describe an image it just read) has to be registered on the `MCPClient`, in the same process as the agent that owns those providers.