nexus-ai-pro 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/API_STABILITY.md +137 -3
  2. package/CHANGELOG.md +251 -1
  3. package/README.md +15 -5
  4. package/SECURITY.md +41 -1
  5. package/dist/agent/create-agent.js +16 -3
  6. package/dist/agent/glob.d.ts +24 -0
  7. package/dist/agent/glob.js +72 -0
  8. package/dist/agent/index.d.ts +1 -0
  9. package/dist/agent/index.js +1 -0
  10. package/dist/agent/interpreter.d.ts +125 -0
  11. package/dist/agent/interpreter.js +270 -0
  12. package/dist/agent/sandbox-container.d.ts +48 -0
  13. package/dist/agent/sandbox-container.js +291 -0
  14. package/dist/agent/sandbox.d.ts +50 -1
  15. package/dist/agent/sandbox.js +155 -2
  16. package/dist/agent/tool-search.d.ts +39 -0
  17. package/dist/agent/tool-search.js +113 -0
  18. package/dist/agent/tool.d.ts +2 -0
  19. package/dist/agent/tool.js +1 -0
  20. package/dist/batch/manager.js +16 -33
  21. package/dist/batch/openai-client.d.ts +83 -0
  22. package/dist/batch/openai-client.js +120 -0
  23. package/dist/batch/openai.d.ts +12 -21
  24. package/dist/batch/openai.js +33 -125
  25. package/dist/batch/poll.d.ts +18 -0
  26. package/dist/batch/poll.js +29 -0
  27. package/dist/cli/models.d.ts +36 -0
  28. package/dist/cli/models.js +170 -0
  29. package/dist/cli.js +11 -1
  30. package/dist/context/window.js +4 -0
  31. package/dist/deep-agent/helpers.d.ts +57 -0
  32. package/dist/deep-agent/helpers.js +235 -0
  33. package/dist/deep-agent/index.d.ts +20 -1
  34. package/dist/deep-agent/index.js +69 -51
  35. package/dist/doctor/index.d.ts +16 -1
  36. package/dist/doctor/index.js +10 -5
  37. package/dist/embeddings/adapters.d.ts +2 -2
  38. package/dist/embeddings/adapters.js +24 -13
  39. package/dist/embeddings/batch-google.d.ts +40 -0
  40. package/dist/embeddings/batch-google.js +128 -0
  41. package/dist/embeddings/batch-mock.d.ts +45 -0
  42. package/dist/embeddings/batch-mock.js +71 -0
  43. package/dist/embeddings/batch-openai.d.ts +23 -0
  44. package/dist/embeddings/batch-openai.js +55 -0
  45. package/dist/embeddings/batch.d.ts +230 -0
  46. package/dist/embeddings/batch.js +282 -0
  47. package/dist/embeddings/cache.d.ts +81 -0
  48. package/dist/embeddings/cache.js +116 -0
  49. package/dist/embeddings/engine.d.ts +7 -0
  50. package/dist/embeddings/engine.js +110 -68
  51. package/dist/embeddings/google-wire.d.ts +32 -0
  52. package/dist/embeddings/google-wire.js +33 -0
  53. package/dist/embeddings/index.d.ts +1 -1
  54. package/dist/embeddings/inputs.d.ts +30 -0
  55. package/dist/embeddings/inputs.js +103 -0
  56. package/dist/embeddings/mock.d.ts +7 -1
  57. package/dist/embeddings/mock.js +8 -2
  58. package/dist/embeddings/models.js +21 -0
  59. package/dist/evaluate/generate.d.ts +171 -0
  60. package/dist/evaluate/generate.js +266 -0
  61. package/dist/evaluate/index.d.ts +1 -1
  62. package/dist/evaluate/index.js +1 -1
  63. package/dist/evaluate/online.d.ts +114 -0
  64. package/dist/evaluate/online.js +261 -0
  65. package/dist/evaluate/run.d.ts +28 -0
  66. package/dist/evaluate/run.js +56 -5
  67. package/dist/graph/graph.d.ts +7 -1
  68. package/dist/graph/graph.js +12 -1
  69. package/dist/images/asset-stores.d.ts +47 -1
  70. package/dist/images/asset-stores.js +135 -2
  71. package/dist/images/asset-support.d.ts +94 -1
  72. package/dist/images/asset-support.js +159 -3
  73. package/dist/images/assets.d.ts +10 -3
  74. package/dist/images/assets.js +15 -2
  75. package/dist/media/index.d.ts +244 -0
  76. package/dist/media/index.js +262 -0
  77. package/dist/media/mistral.d.ts +34 -0
  78. package/dist/media/mistral.js +90 -0
  79. package/dist/media/mock.d.ts +36 -0
  80. package/dist/media/mock.js +45 -0
  81. package/dist/media/rag.d.ts +141 -0
  82. package/dist/media/rag.js +198 -0
  83. package/dist/models/data.d.ts +87 -0
  84. package/dist/models/data.js +235 -0
  85. package/dist/models/registry.d.ts +2 -1
  86. package/dist/models/registry.js +3 -2
  87. package/dist/operations/runner.js +1 -1
  88. package/dist/postgres/embedding-cache.d.ts +29 -0
  89. package/dist/postgres/embedding-cache.js +58 -0
  90. package/dist/providers/anthropic.d.ts +6 -0
  91. package/dist/providers/anthropic.js +36 -9
  92. package/dist/providers/content.d.ts +33 -1
  93. package/dist/providers/content.js +62 -0
  94. package/dist/providers/google.js +14 -5
  95. package/dist/providers/openai.d.ts +8 -4
  96. package/dist/providers/openai.js +75 -34
  97. package/dist/rag/chroma.d.ts +13 -1
  98. package/dist/rag/chroma.js +36 -13
  99. package/dist/rag/pinecone.js +2 -2
  100. package/dist/rag/pipeline.d.ts +17 -2
  101. package/dist/rag/pipeline.js +95 -14
  102. package/dist/rag/qdrant.js +2 -2
  103. package/dist/rag/redis.d.ts +2 -1
  104. package/dist/rag/redis.js +56 -18
  105. package/dist/rag/vector-helpers.d.ts +5 -0
  106. package/dist/rag/vector-helpers.js +7 -0
  107. package/dist/rag/weaviate.d.ts +15 -1
  108. package/dist/rag/weaviate.js +75 -12
  109. package/dist/runtime/index.d.ts +1 -1
  110. package/dist/security/index.d.ts +1 -1
  111. package/dist/security/index.js +1 -1
  112. package/dist/security/injection-detector.d.ts +18 -0
  113. package/dist/security/injection-detector.js +39 -4
  114. package/dist/security/schema-validator.js +11 -2
  115. package/dist/sqlite/embedding-cache.d.ts +34 -0
  116. package/dist/sqlite/embedding-cache.js +61 -0
  117. package/dist/types/config.d.ts +5 -0
  118. package/dist/types/embeddings.d.ts +87 -5
  119. package/dist/types/images.d.ts +2 -2
  120. package/dist/types/lifecycle.d.ts +1 -1
  121. package/dist/types/messages.d.ts +57 -3
  122. package/dist/utils/redis-command.d.ts +15 -0
  123. package/dist/utils/redis-command.js +5 -0
  124. package/dist/utils/sha256.d.ts +9 -0
  125. package/dist/utils/sha256.js +6 -0
  126. package/dist/utils/tokenizer.js +4 -0
  127. package/dist/utils/vector-bytes.d.ts +11 -0
  128. package/dist/utils/vector-bytes.js +18 -0
  129. package/dist/version.d.ts +1 -1
  130. package/dist/version.js +1 -1
  131. package/dist-cjs/agent/create-agent.js +15 -2
  132. package/dist-cjs/agent/glob.d.ts +24 -0
  133. package/dist-cjs/agent/glob.js +77 -0
  134. package/dist-cjs/agent/index.d.ts +1 -0
  135. package/dist-cjs/agent/index.js +5 -1
  136. package/dist-cjs/agent/interpreter.d.ts +125 -0
  137. package/dist-cjs/agent/interpreter.js +308 -0
  138. package/dist-cjs/agent/sandbox-container.d.ts +48 -0
  139. package/dist-cjs/agent/sandbox-container.js +327 -0
  140. package/dist-cjs/agent/sandbox.d.ts +50 -1
  141. package/dist-cjs/agent/sandbox.js +155 -2
  142. package/dist-cjs/agent/tool-search.d.ts +39 -0
  143. package/dist-cjs/agent/tool-search.js +118 -0
  144. package/dist-cjs/agent/tool.d.ts +2 -0
  145. package/dist-cjs/agent/tool.js +1 -0
  146. package/dist-cjs/batch/manager.js +16 -33
  147. package/dist-cjs/batch/openai-client.d.ts +83 -0
  148. package/dist-cjs/batch/openai-client.js +125 -0
  149. package/dist-cjs/batch/openai.d.ts +12 -21
  150. package/dist-cjs/batch/openai.js +32 -124
  151. package/dist-cjs/batch/poll.d.ts +18 -0
  152. package/dist-cjs/batch/poll.js +32 -0
  153. package/dist-cjs/cli/models.d.ts +36 -0
  154. package/dist-cjs/cli/models.js +175 -0
  155. package/dist-cjs/context/window.js +4 -0
  156. package/dist-cjs/deep-agent/helpers.d.ts +57 -0
  157. package/dist-cjs/deep-agent/helpers.js +238 -0
  158. package/dist-cjs/deep-agent/index.d.ts +20 -1
  159. package/dist-cjs/deep-agent/index.js +68 -50
  160. package/dist-cjs/doctor/index.d.ts +16 -1
  161. package/dist-cjs/doctor/index.js +10 -5
  162. package/dist-cjs/embeddings/adapters.d.ts +2 -2
  163. package/dist-cjs/embeddings/adapters.js +23 -12
  164. package/dist-cjs/embeddings/batch-google.d.ts +40 -0
  165. package/dist-cjs/embeddings/batch-google.js +132 -0
  166. package/dist-cjs/embeddings/batch-mock.d.ts +45 -0
  167. package/dist-cjs/embeddings/batch-mock.js +75 -0
  168. package/dist-cjs/embeddings/batch-openai.d.ts +23 -0
  169. package/dist-cjs/embeddings/batch-openai.js +59 -0
  170. package/dist-cjs/embeddings/batch.d.ts +230 -0
  171. package/dist-cjs/embeddings/batch.js +320 -0
  172. package/dist-cjs/embeddings/cache.d.ts +81 -0
  173. package/dist-cjs/embeddings/cache.js +125 -0
  174. package/dist-cjs/embeddings/engine.d.ts +7 -0
  175. package/dist-cjs/embeddings/engine.js +143 -68
  176. package/dist-cjs/embeddings/google-wire.d.ts +32 -0
  177. package/dist-cjs/embeddings/google-wire.js +38 -0
  178. package/dist-cjs/embeddings/index.d.ts +1 -1
  179. package/dist-cjs/embeddings/inputs.d.ts +30 -0
  180. package/dist-cjs/embeddings/inputs.js +141 -0
  181. package/dist-cjs/embeddings/mock.d.ts +7 -1
  182. package/dist-cjs/embeddings/mock.js +8 -2
  183. package/dist-cjs/embeddings/models.js +21 -0
  184. package/dist-cjs/evaluate/generate.d.ts +171 -0
  185. package/dist-cjs/evaluate/generate.js +272 -0
  186. package/dist-cjs/evaluate/index.d.ts +1 -1
  187. package/dist-cjs/evaluate/index.js +2 -1
  188. package/dist-cjs/evaluate/online.d.ts +114 -0
  189. package/dist-cjs/evaluate/online.js +264 -0
  190. package/dist-cjs/evaluate/run.d.ts +28 -0
  191. package/dist-cjs/evaluate/run.js +58 -5
  192. package/dist-cjs/graph/graph.d.ts +7 -1
  193. package/dist-cjs/graph/graph.js +13 -1
  194. package/dist-cjs/images/asset-stores.d.ts +47 -1
  195. package/dist-cjs/images/asset-stores.js +133 -0
  196. package/dist-cjs/images/asset-support.d.ts +94 -1
  197. package/dist-cjs/images/asset-support.js +169 -3
  198. package/dist-cjs/images/assets.d.ts +10 -3
  199. package/dist-cjs/images/assets.js +17 -1
  200. package/dist-cjs/media/index.d.ts +244 -0
  201. package/dist-cjs/media/index.js +269 -0
  202. package/dist-cjs/media/mistral.d.ts +34 -0
  203. package/dist-cjs/media/mistral.js +94 -0
  204. package/dist-cjs/media/mock.d.ts +36 -0
  205. package/dist-cjs/media/mock.js +49 -0
  206. package/dist-cjs/media/rag.d.ts +141 -0
  207. package/dist-cjs/media/rag.js +202 -0
  208. package/dist-cjs/models/data.d.ts +87 -0
  209. package/dist-cjs/models/data.js +245 -0
  210. package/dist-cjs/models/registry.d.ts +2 -1
  211. package/dist-cjs/models/registry.js +3 -2
  212. package/dist-cjs/operations/runner.js +1 -1
  213. package/dist-cjs/postgres/embedding-cache.d.ts +29 -0
  214. package/dist-cjs/postgres/embedding-cache.js +96 -0
  215. package/dist-cjs/providers/anthropic.d.ts +6 -0
  216. package/dist-cjs/providers/anthropic.js +35 -8
  217. package/dist-cjs/providers/content.d.ts +33 -1
  218. package/dist-cjs/providers/content.js +69 -0
  219. package/dist-cjs/providers/google.js +13 -4
  220. package/dist-cjs/providers/openai.d.ts +8 -4
  221. package/dist-cjs/providers/openai.js +74 -33
  222. package/dist-cjs/rag/chroma.d.ts +13 -1
  223. package/dist-cjs/rag/chroma.js +35 -12
  224. package/dist-cjs/rag/pinecone.js +1 -1
  225. package/dist-cjs/rag/pipeline.d.ts +17 -2
  226. package/dist-cjs/rag/pipeline.js +95 -14
  227. package/dist-cjs/rag/qdrant.js +1 -1
  228. package/dist-cjs/rag/redis.d.ts +2 -1
  229. package/dist-cjs/rag/redis.js +55 -17
  230. package/dist-cjs/rag/vector-helpers.d.ts +5 -0
  231. package/dist-cjs/rag/vector-helpers.js +8 -0
  232. package/dist-cjs/rag/weaviate.d.ts +15 -1
  233. package/dist-cjs/rag/weaviate.js +74 -11
  234. package/dist-cjs/runtime/index.d.ts +1 -1
  235. package/dist-cjs/security/index.d.ts +1 -1
  236. package/dist-cjs/security/index.js +2 -1
  237. package/dist-cjs/security/injection-detector.d.ts +18 -0
  238. package/dist-cjs/security/injection-detector.js +40 -4
  239. package/dist-cjs/security/schema-validator.js +11 -2
  240. package/dist-cjs/sqlite/embedding-cache.d.ts +34 -0
  241. package/dist-cjs/sqlite/embedding-cache.js +99 -0
  242. package/dist-cjs/types/config.d.ts +5 -0
  243. package/dist-cjs/types/embeddings.d.ts +87 -5
  244. package/dist-cjs/types/images.d.ts +2 -2
  245. package/dist-cjs/types/lifecycle.d.ts +1 -1
  246. package/dist-cjs/types/messages.d.ts +57 -3
  247. package/dist-cjs/utils/redis-command.d.ts +15 -0
  248. package/dist-cjs/utils/redis-command.js +8 -0
  249. package/dist-cjs/utils/sha256.d.ts +9 -0
  250. package/dist-cjs/utils/sha256.js +7 -0
  251. package/dist-cjs/utils/tokenizer.js +4 -0
  252. package/dist-cjs/utils/vector-bytes.d.ts +11 -0
  253. package/dist-cjs/utils/vector-bytes.js +22 -0
  254. package/dist-cjs/version.d.ts +1 -1
  255. package/dist-cjs/version.js +1 -1
  256. package/llms.txt +2 -1
  257. package/package.json +214 -4
@@ -0,0 +1,72 @@
1
+ export function globMatcher(pattern) {
2
+ const glob = pattern.replace(/\\/g, '/').replace(/^\.\//, '');
3
+ let source = '';
4
+ let braces = 0;
5
+ for (let index = 0; index < glob.length; index += 1) {
6
+ const char = glob[index];
7
+ if (char === '*' && glob[index + 1] === '*') {
8
+ if (glob[index + 2] === '/') {
9
+ source += '(?:.*/)?';
10
+ index += 2;
11
+ }
12
+ else {
13
+ source += '.*';
14
+ index += 1;
15
+ }
16
+ }
17
+ else if (char === '*') {
18
+ source += '[^/]*';
19
+ }
20
+ else if (char === '?') {
21
+ source += '[^/]';
22
+ }
23
+ else if (char === '{') {
24
+ braces += 1;
25
+ source += '(?:';
26
+ }
27
+ else if (char === '}' && braces > 0) {
28
+ braces -= 1;
29
+ source += ')';
30
+ }
31
+ else if (char === ',' && braces > 0) {
32
+ source += '|';
33
+ }
34
+ else {
35
+ source += char.replace(/[.+^$()|[\]\\]/g, '\\$&');
36
+ }
37
+ }
38
+ source += ')'.repeat(braces);
39
+ const expression = new RegExp(`^${source}$`);
40
+ return (path) => expression.test(path.replace(/\\/g, '/'));
41
+ }
42
+ export const SKIPPED_DIRECTORIES = new Set(['.git', 'node_modules']);
43
+ export const WALK_LIMIT = 20_000;
44
+ export const GREP_FILE_BYTES = 1_048_576;
45
+ export const GREP_TOTAL_BYTES = 64 * 1_048_576;
46
+ const GREP_LINE_CHARS = 4_000;
47
+ export function matchingLines(bytes, expression, limit) {
48
+ if (bytes.subarray(0, 8_000).includes(0))
49
+ return [];
50
+ const found = [];
51
+ for (const [index, line] of new TextDecoder().decode(bytes).split(/\r?\n/).entries()) {
52
+ if (!expression.test(line.slice(0, GREP_LINE_CHARS)))
53
+ continue;
54
+ found.push({ line: index + 1, text: line.slice(0, 300) });
55
+ if (found.length >= limit)
56
+ break;
57
+ }
58
+ return found;
59
+ }
60
+ export const MEDIA_TYPES = {
61
+ png: 'image/png',
62
+ jpg: 'image/jpeg',
63
+ jpeg: 'image/jpeg',
64
+ gif: 'image/gif',
65
+ webp: 'image/webp',
66
+ pdf: 'application/pdf',
67
+ wav: 'audio/wav',
68
+ mp3: 'audio/mp3',
69
+ flac: 'audio/flac',
70
+ ogg: 'audio/ogg',
71
+ aac: 'audio/aac',
72
+ };
@@ -1,5 +1,6 @@
1
1
  export { AgentLoop, type AgentModelClient as AgentLoopModelClient } from './loop.js';
2
2
  export { isToolOutput, tool, ToolExecutor, toolMessageContent, toolOutput } from './tool.js';
3
+ export { searchTools, TOOL_SEARCH_NAME, type ToolSearchHit, type ToolSearchOptions, toolSearchTool, } from './tool-search.js';
3
4
  export type { ToolBody } from './tool.js';
4
5
  export { limitToolCalls, redactMessages, type RedactOptions, summarizeHistory, type SummarizeOptions, } from './middleware/basic.js';
5
6
  export { agentAsTool, agentInput, type AgentApproval, type AgentApprovalPolicy, type AgentChannels, type AgentGraph, type AgentMiddleware, type AgentMiddlewareContext, type AgentModelCallContext, type AgentModelClient, type AgentState, type AgentStopReason, type AgentToolCall, type AgentToolResult, type ClientToolDefinition, createAgent, type CreateAgentOptions, type ToolSourceContext, } from './create-agent.js';
@@ -1,4 +1,5 @@
1
1
  export { AgentLoop } from './loop.js';
2
2
  export { isToolOutput, tool, ToolExecutor, toolMessageContent, toolOutput } from './tool.js';
3
+ export { searchTools, TOOL_SEARCH_NAME, toolSearchTool, } from './tool-search.js';
3
4
  export { limitToolCalls, redactMessages, summarizeHistory, } from './middleware/basic.js';
4
5
  export { agentAsTool, agentInput, createAgent, } from './create-agent.js';
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The interpreter contract: a model writes a snippet of code that calls the agent's tools, and the
3
+ * snippet runs in one step, instead of one model round trip per call. A loop over fifty records, a
4
+ * filter on a large result, or three calls at once become one tool call.
5
+ *
6
+ * The package ships the contract, a reference that runs code in a worker thread with time and memory
7
+ * limits, a tool that puts an interpreter in front of a model, and a conformance suite. Like the
8
+ * reference sandbox, the reference interpreter is not a security boundary: code in a worker can do
9
+ * anything the process can. Run untrusted code in an interpreter behind a container or a VM.
10
+ */
11
+ import type { ToolDefinition } from '../types/messages.js';
12
+ import type { PermissionPolicyLike } from './permissions.js';
13
+ /** A function the code calls as `await tools.<name>(args)`. It runs outside the interpreter. */
14
+ export type InterpreterFunction = (args: Record<string, unknown>) => Promise<unknown>;
15
+ /** Options for one run. */
16
+ export interface InterpreterRunOptions {
17
+ /** The functions the code may call, by name. */
18
+ tools?: Readonly<Record<string, InterpreterFunction>>;
19
+ /** Stops the code after this many milliseconds. */
20
+ timeoutMs?: number;
21
+ /** Stops the code when aborted. */
22
+ signal?: AbortSignal;
23
+ }
24
+ /** One call the code made. */
25
+ export interface InterpreterCall {
26
+ /** The function's name. */
27
+ name: string;
28
+ /** Its arguments. */
29
+ args: unknown;
30
+ /** Whether it succeeded. */
31
+ ok: boolean;
32
+ }
33
+ /** What a run did. */
34
+ export interface InterpreterResult {
35
+ /** True when the code returned; false when it threw, was stopped, or ran out of memory. */
36
+ ok: boolean;
37
+ /** What the code returned, when it returned. */
38
+ value?: unknown;
39
+ /** What it threw, or why it was stopped. */
40
+ error?: string;
41
+ /** What it logged with `console`, one entry per call, cut to the interpreter's limit. */
42
+ logs: string[];
43
+ /** The calls it made, in the order it made them. */
44
+ calls: InterpreterCall[];
45
+ /** True when it was stopped by its timeout. */
46
+ timedOut: boolean;
47
+ /** How long it ran, in milliseconds. */
48
+ durationMs: number;
49
+ }
50
+ /** Runs code. The application provides one, or uses `workerInterpreter()`. */
51
+ export interface Interpreter {
52
+ /** Names the interpreter in reports. */
53
+ readonly name: string;
54
+ /** The language its code is written in, for the model, such as `JavaScript`. */
55
+ readonly language: string;
56
+ /**
57
+ * Runs code: the body of an async function, which can `await tools.<name>(args)` and `return` a
58
+ * value. A run never throws; what went wrong is in the result.
59
+ */
60
+ run(code: string, options?: InterpreterRunOptions): Promise<InterpreterResult>;
61
+ /** Releases the interpreter. */
62
+ dispose?(): Promise<void>;
63
+ }
64
+ /** Options for `workerInterpreter()`. */
65
+ export interface WorkerInterpreterOptions {
66
+ /** The timeout for a run that sets none. Defaults to 10 seconds. */
67
+ timeoutMs?: number;
68
+ /** The heap one run may use, in megabytes. Defaults to 64. */
69
+ memoryMb?: number;
70
+ /** What one run may log, in characters. Defaults to 64,000. */
71
+ maxLogChars?: number;
72
+ /** The most calls one run may make. Defaults to 100. */
73
+ maxCalls?: number;
74
+ }
75
+ /**
76
+ * A reference interpreter, for JavaScript: each run gets a fresh worker thread with a heap limit,
77
+ * stopped at its timeout, so an endless loop or a runaway allocation ends the run, not the process.
78
+ * Calls to `tools` cross back to the host and run there. It is not a security boundary: the code runs
79
+ * with the process's own access.
80
+ */
81
+ export declare function workerInterpreter(options?: WorkerInterpreterOptions): Interpreter;
82
+ /** Options for `codeTool()`. */
83
+ export interface CodeToolOptions {
84
+ /** The tools the code may call. A tool the agent should not call from code stays out of this list. */
85
+ tools: readonly ToolDefinition[];
86
+ /**
87
+ * Decides each call the code makes, as an agent's policy decides a model's. A call it would ask a
88
+ * person about is refused instead, since code cannot pause for one; the model can make that call
89
+ * directly. Without one, every call runs.
90
+ */
91
+ permissions?: PermissionPolicyLike;
92
+ /** The tool's name. Defaults to `run_code`. */
93
+ name?: string;
94
+ /** The timeout for one run. Defaults to the interpreter's. */
95
+ timeoutMs?: number;
96
+ }
97
+ /**
98
+ * A tool that runs a model's code in an interpreter, with `tools` callable from it, and returns what
99
+ * the code returned and logged, and the calls it made. It declares `code`, so a permission policy must
100
+ * grant code before it runs at all; each call the code makes is then decided on its own.
101
+ */
102
+ export declare function codeTool(interpreter: Interpreter, options: CodeToolOptions): ToolDefinition;
103
+ /** What `runInterpreterConformance()` found. */
104
+ export interface InterpreterConformanceReport {
105
+ /** The interpreter's name. */
106
+ interpreter: string;
107
+ /** True when every check held. */
108
+ passed: boolean;
109
+ /** Every check, in order. */
110
+ checks: Array<{
111
+ name: string;
112
+ ok: boolean;
113
+ detail?: string;
114
+ }>;
115
+ }
116
+ /**
117
+ * Checks a JavaScript interpreter against the contract, without a test framework:
118
+ *
119
+ * - code returns a value, logs, and calls tools, in order and at once;
120
+ * - a tool's failure reaches the code as a rejection it can catch, and an unknown tool fails;
121
+ * - code that throws is reported, and never throws out of `run()`;
122
+ * - an endless loop is stopped at the timeout, and the interpreter runs again afterwards;
123
+ * - a runaway allocation is stopped, and the interpreter runs again afterwards.
124
+ */
125
+ export declare function runInterpreterConformance(interpreter: Interpreter): Promise<InterpreterConformanceReport>;
@@ -0,0 +1,270 @@
1
+ import { capabilitiesOf } from './capabilities.js';
2
+ import { isToolOutput, ToolExecutor } from './tool.js';
3
+ export function workerInterpreter(options = {}) {
4
+ const maxLog = options.maxLogChars ?? 64_000;
5
+ const maxCalls = options.maxCalls ?? 100;
6
+ return {
7
+ name: 'worker (not a security boundary)',
8
+ language: 'JavaScript',
9
+ async run(code, runOptions = {}) {
10
+ const { Worker } = await import('node:worker_threads');
11
+ const started = Date.now();
12
+ const tools = runOptions.tools ?? {};
13
+ const logs = [];
14
+ const calls = [];
15
+ let logged = 0;
16
+ return new Promise((resolve) => {
17
+ const memory = options.memoryMb ?? 64;
18
+ const worker = new Worker(WORKER_SOURCE, {
19
+ eval: true,
20
+ workerData: { code },
21
+ resourceLimits: {
22
+ maxOldGenerationSizeMb: memory,
23
+ maxYoungGenerationSizeMb: Math.max(4, Math.ceil(memory / 8)),
24
+ },
25
+ stdout: true,
26
+ stderr: true,
27
+ });
28
+ let settled = false;
29
+ const finish = (result) => {
30
+ if (settled)
31
+ return;
32
+ settled = true;
33
+ clearTimeout(timer);
34
+ runOptions.signal?.removeEventListener('abort', onAbort);
35
+ void worker.terminate();
36
+ resolve({ ...result, logs, calls, durationMs: Date.now() - started });
37
+ };
38
+ const limit = runOptions.timeoutMs ?? options.timeoutMs ?? 10_000;
39
+ const timer = setTimeout(() => finish({ ok: false, error: `The code ran past its ${limit} ms timeout`, timedOut: true }), limit);
40
+ const onAbort = () => finish({ ok: false, error: 'The run was aborted', timedOut: false });
41
+ if (runOptions.signal?.aborted)
42
+ onAbort();
43
+ runOptions.signal?.addEventListener('abort', onAbort, { once: true });
44
+ worker.on('message', (message) => {
45
+ if (message.type === 'log') {
46
+ if (logged >= maxLog)
47
+ return;
48
+ const line = message.line.slice(0, maxLog - logged);
49
+ logged += line.length;
50
+ logs.push(line);
51
+ }
52
+ else if (message.type === 'call') {
53
+ void answer(message);
54
+ }
55
+ else if (message.type === 'done') {
56
+ finish(message.ok
57
+ ? { ok: true, value: message.value, timedOut: false }
58
+ : { ok: false, error: message.error, timedOut: false });
59
+ }
60
+ });
61
+ worker.on('error', (error) => {
62
+ const outOfMemory = error.code === 'ERR_WORKER_OUT_OF_MEMORY';
63
+ finish({
64
+ ok: false,
65
+ error: outOfMemory ? `The code ran out of memory, past ${memory} MB` : error.message,
66
+ timedOut: false,
67
+ });
68
+ });
69
+ worker.on('exit', () => finish({ ok: false, error: 'The interpreter stopped', timedOut: false }));
70
+ const answer = async (message) => {
71
+ const reply = (body) => {
72
+ if (settled)
73
+ return;
74
+ try {
75
+ worker.postMessage({ type: 'result', id: message.id, ...body });
76
+ }
77
+ catch {
78
+ worker.postMessage({ type: 'result', id: message.id, ...body, value: toJson(body) });
79
+ }
80
+ };
81
+ const fn = Object.hasOwn(tools, message.name) ? tools[message.name] : undefined;
82
+ if (calls.length >= maxCalls) {
83
+ calls.push({ name: message.name, args: message.args, ok: false });
84
+ reply({ ok: false, error: `The code made more than ${maxCalls} calls` });
85
+ return;
86
+ }
87
+ const call = { name: message.name, args: message.args, ok: false };
88
+ calls.push(call);
89
+ if (!fn) {
90
+ reply({ ok: false, error: `There is no tool named ${message.name}; the tools are ${namesOf(tools)}` });
91
+ return;
92
+ }
93
+ try {
94
+ const value = await fn((message.args ?? {}));
95
+ call.ok = true;
96
+ reply({ ok: true, value });
97
+ }
98
+ catch (error) {
99
+ reply({ ok: false, error: error instanceof Error ? error.message : String(error) });
100
+ }
101
+ };
102
+ });
103
+ },
104
+ };
105
+ }
106
+ const namesOf = (tools) => Object.keys(tools).join(', ') || 'none';
107
+ function toJson(body) {
108
+ try {
109
+ return JSON.parse(JSON.stringify(body.value ?? null));
110
+ }
111
+ catch {
112
+ return String(body.value);
113
+ }
114
+ }
115
+ const WORKER_SOURCE = `
116
+ const { parentPort, workerData } = require('node:worker_threads');
117
+ const pending = new Map();
118
+ let next = 0;
119
+ const show = (value) => {
120
+ if (typeof value === 'string') return value;
121
+ try { return JSON.stringify(value); } catch { return String(value); }
122
+ };
123
+ const say = (...values) => parentPort.postMessage({ type: 'log', line: values.map(show).join(' ') });
124
+ const console = { log: say, info: say, warn: say, error: say, debug: say };
125
+ const tools = new Proxy({}, {
126
+ get(_target, name) {
127
+ if (typeof name !== 'string' || name === 'then') return undefined;
128
+ return (args = {}) => new Promise((resolve, reject) => {
129
+ const id = next++;
130
+ pending.set(id, { resolve, reject });
131
+ parentPort.postMessage({ type: 'call', id, name, args });
132
+ });
133
+ },
134
+ });
135
+ parentPort.on('message', (message) => {
136
+ if (message.type !== 'result') return;
137
+ const waiting = pending.get(message.id);
138
+ pending.delete(message.id);
139
+ if (!waiting) return;
140
+ if (message.ok) waiting.resolve(message.value);
141
+ else waiting.reject(new Error(message.error));
142
+ });
143
+ const AsyncFunction = (async () => {}).constructor;
144
+ (async () => {
145
+ try {
146
+ const value = await new AsyncFunction('tools', 'console', workerData.code)(tools, console);
147
+ try {
148
+ parentPort.postMessage({ type: 'done', ok: true, value });
149
+ } catch {
150
+ parentPort.postMessage({ type: 'done', ok: true, value: show(value) });
151
+ }
152
+ } catch (error) {
153
+ parentPort.postMessage({ type: 'done', ok: false, error: error instanceof Error ? error.message : String(error) });
154
+ }
155
+ })();
156
+ `;
157
+ export function codeTool(interpreter, options) {
158
+ const executor = new ToolExecutor([...options.tools]);
159
+ const listed = options.tools
160
+ .map((tool) => `- ${tool.name}(${JSON.stringify(tool.parameters)}): ${tool.description}`)
161
+ .join('\n');
162
+ return {
163
+ name: options.name ?? 'run_code',
164
+ description: `Runs ${interpreter.language} code in one step: the body of an async function. Call the tools below with \`await tools.<name>(args)\`, \`return\` the value you need, and use console.log for notes. Use it to call tools many times, at once, or to filter what they return before you read it.\nTools:\n${listed || '- none'}`,
165
+ parameters: {
166
+ type: 'object',
167
+ properties: { code: { type: 'string', description: `The ${interpreter.language} to run` } },
168
+ required: ['code'],
169
+ },
170
+ capabilities: ['code'],
171
+ execute: async (args, context) => {
172
+ const tools = {};
173
+ for (const definition of options.tools) {
174
+ tools[definition.name] = async (callArgs) => {
175
+ if (options.permissions) {
176
+ const verdict = await options.permissions.decide({
177
+ tool: definition.name,
178
+ args: callArgs,
179
+ capabilities: capabilitiesOf(definition, callArgs),
180
+ ...(context?.principal ? { principal: context.principal } : {}),
181
+ });
182
+ if (verdict.decision === 'deny')
183
+ throw new Error(`Permission denied: ${verdict.reason}`);
184
+ if (verdict.decision === 'ask') {
185
+ throw new Error(`${definition.name} needs a person's approval, so call it directly: ${verdict.reason}`);
186
+ }
187
+ }
188
+ const result = await executor.execute(definition.name, callArgs, context);
189
+ if (!result.ok)
190
+ throw new Error(result.error);
191
+ return isToolOutput(result.result)
192
+ ? result.result.content.map((part) => (part.type === 'text' ? part.text : `[${part.type}]`)).join('\n')
193
+ : result.result;
194
+ };
195
+ }
196
+ const outcome = await interpreter.run(String(args.code ?? ''), {
197
+ tools,
198
+ ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
199
+ ...(context?.signal ? { signal: context.signal } : {}),
200
+ });
201
+ return {
202
+ ok: outcome.ok,
203
+ ...(outcome.ok ? { value: outcome.value ?? null } : { error: outcome.error }),
204
+ ...(outcome.logs.length ? { logs: outcome.logs } : {}),
205
+ calls: outcome.calls.map((call) => `${call.name}${call.ok ? '' : ' (failed)'}`),
206
+ };
207
+ },
208
+ };
209
+ }
210
+ export async function runInterpreterConformance(interpreter) {
211
+ const checks = [];
212
+ const check = async (name, run) => {
213
+ try {
214
+ const problem = await run();
215
+ checks.push(problem ? { name, ok: false, detail: problem } : { name, ok: true });
216
+ }
217
+ catch (error) {
218
+ checks.push({ name, ok: false, detail: error instanceof Error ? error.message : String(error) });
219
+ }
220
+ };
221
+ const same = (actual, expected, what) => JSON.stringify(actual) === JSON.stringify(expected)
222
+ ? undefined
223
+ : `${what}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`;
224
+ const double = async (args) => Number(args.n) * 2;
225
+ await check('code returns a value, logs, and calls tools in order and at once', async () => {
226
+ const result = await interpreter.run(`const one = await tools.double({ n: 1 });
227
+ const many = await Promise.all([2, 3].map((n) => tools.double({ n })));
228
+ console.log('doubled', one, many);
229
+ return { one, many };`, { tools: { double } });
230
+ return (same(result.ok, true, `ok (${result.error})`) ??
231
+ same(result.value, { one: 2, many: [4, 6] }, 'value') ??
232
+ same(result.logs, ['doubled 2 [4,6]'], 'logs') ??
233
+ same(result.calls.map((call) => call.name), ['double', 'double', 'double'], 'calls'));
234
+ });
235
+ await check('a failing tool rejects in the code, and an unknown tool fails', async () => {
236
+ const fail = async () => {
237
+ throw new Error('no such order');
238
+ };
239
+ const result = await interpreter.run(`let caught = '';
240
+ try { await tools.lookup({ id: 1 }); } catch (error) { caught = error.message; }
241
+ let unknown = '';
242
+ try { await tools.missing({}); } catch (error) { unknown = error.message; }
243
+ return { caught, unknown: unknown.length > 0 };`, { tools: { lookup: fail } });
244
+ return same(result.value, { caught: 'no such order', unknown: true }, 'value');
245
+ });
246
+ await check('code that throws is reported', async () => {
247
+ const result = await interpreter.run(`throw new Error('broken on purpose');`);
248
+ return (same(result.ok, false, 'ok') ??
249
+ (result.error?.includes('broken on purpose') ? undefined : `error: ${result.error}`));
250
+ });
251
+ await check('an endless loop is stopped at the timeout', async () => {
252
+ const result = await interpreter.run('while (true) {}', { timeoutMs: 300 });
253
+ if (!result.timedOut || result.ok)
254
+ return `the loop was not reported as timed out: ${JSON.stringify(result)}`;
255
+ const after = await interpreter.run('return 1 + 1;');
256
+ return same(after.value, 2, 'a run after the timeout');
257
+ });
258
+ await check('a runaway allocation is stopped', async () => {
259
+ const result = await interpreter.run('const kept = []; for (;;) kept.push(new Array(1e6).fill(Math.random()));', {
260
+ timeoutMs: 30_000,
261
+ });
262
+ if (result.ok)
263
+ return 'the allocation finished';
264
+ if (result.timedOut)
265
+ return 'the allocation ran to the timeout instead of meeting a memory limit';
266
+ const after = await interpreter.run('return 1 + 1;');
267
+ return same(after.value, 2, 'a run after the allocation');
268
+ });
269
+ return { interpreter: interpreter.name, passed: checks.every((item) => item.ok), checks };
270
+ }
@@ -0,0 +1,48 @@
1
+ import { type Sandbox } from './sandbox.js';
2
+ /** Options for `containerSandbox()`. */
3
+ export interface ContainerSandboxOptions {
4
+ /**
5
+ * The image, such as `node:22-alpine`. It needs a POSIX shell, `cat`, `find`, `tar`, and the
6
+ * commands the agent is to run.
7
+ */
8
+ image: string;
9
+ /** The binary that runs containers: `docker`, `podman`, or a path to either. Defaults to `docker`. */
10
+ binary?: string;
11
+ /** Where the files live inside the container, and where commands run. Defaults to `/workspace`. */
12
+ workdir?: string;
13
+ /**
14
+ * The network. `false`, the default, gives the container none at all; `true` gives it the
15
+ * runtime's default network; a string names a network to join.
16
+ */
17
+ network?: boolean | string;
18
+ /** The memory limit, as the runtime takes it. Swap is held to the same limit. Defaults to `512m`. */
19
+ memory?: string;
20
+ /** How many CPUs it may use. Defaults to 1. */
21
+ cpus?: number;
22
+ /** The most processes it may run at once. Defaults to 256. */
23
+ pids?: number;
24
+ /** Environment variables every command gets, on top of the image's own. */
25
+ env?: Record<string, string>;
26
+ /** The timeout for a command that sets none. Defaults to 30 seconds. */
27
+ timeoutMs?: number;
28
+ /** Output kept per stream, in bytes. Defaults to 1 MB. */
29
+ maxOutputBytes?: number;
30
+ /**
31
+ * More arguments for `run`, before the image, for what these options do not cover, such as
32
+ * `--user` with a volume that user can write.
33
+ */
34
+ runArgs?: readonly string[];
35
+ }
36
+ /**
37
+ * A sandbox in a container, for production. Every command runs through `exec` in one container per
38
+ * sandbox, and a command past its timeout is killed inside the container with everything it
39
+ * started. `isolation` reports the network as isolated only when the container has none.
40
+ *
41
+ * ```ts
42
+ * const sandbox = containerSandbox({ image: 'node:22-alpine' });
43
+ * const agent = createDeepAgent({ client: ai, sandbox, commands: ['node', 'npm test'] });
44
+ * // …
45
+ * await sandbox.dispose?.();
46
+ * ```
47
+ */
48
+ export declare function containerSandbox(options: ContainerSandboxOptions): Sandbox;