nexus-ai-pro 2.6.0 → 2.7.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 (101) hide show
  1. package/API_STABILITY.md +52 -0
  2. package/CHANGELOG.md +119 -1
  3. package/README.md +8 -5
  4. package/dist/agent/create-agent.js +16 -3
  5. package/dist/agent/glob.d.ts +24 -0
  6. package/dist/agent/glob.js +72 -0
  7. package/dist/agent/index.d.ts +1 -0
  8. package/dist/agent/index.js +1 -0
  9. package/dist/agent/interpreter.d.ts +125 -0
  10. package/dist/agent/interpreter.js +270 -0
  11. package/dist/agent/sandbox-container.d.ts +48 -0
  12. package/dist/agent/sandbox-container.js +291 -0
  13. package/dist/agent/sandbox.d.ts +50 -1
  14. package/dist/agent/sandbox.js +155 -2
  15. package/dist/agent/tool-search.d.ts +39 -0
  16. package/dist/agent/tool-search.js +113 -0
  17. package/dist/agent/tool.d.ts +2 -0
  18. package/dist/agent/tool.js +1 -0
  19. package/dist/context/window.js +4 -0
  20. package/dist/deep-agent/helpers.d.ts +57 -0
  21. package/dist/deep-agent/helpers.js +235 -0
  22. package/dist/deep-agent/index.d.ts +20 -1
  23. package/dist/deep-agent/index.js +69 -51
  24. package/dist/evaluate/generate.d.ts +171 -0
  25. package/dist/evaluate/generate.js +266 -0
  26. package/dist/evaluate/index.d.ts +1 -1
  27. package/dist/evaluate/index.js +1 -1
  28. package/dist/evaluate/online.d.ts +114 -0
  29. package/dist/evaluate/online.js +261 -0
  30. package/dist/evaluate/run.d.ts +28 -0
  31. package/dist/evaluate/run.js +56 -5
  32. package/dist/graph/graph.d.ts +7 -1
  33. package/dist/graph/graph.js +12 -1
  34. package/dist/operations/runner.js +1 -1
  35. package/dist/providers/anthropic.d.ts +6 -0
  36. package/dist/providers/anthropic.js +36 -9
  37. package/dist/providers/content.d.ts +33 -1
  38. package/dist/providers/content.js +62 -0
  39. package/dist/providers/google.js +14 -5
  40. package/dist/providers/openai.d.ts +8 -4
  41. package/dist/providers/openai.js +75 -34
  42. package/dist/runtime/index.d.ts +1 -1
  43. package/dist/security/index.d.ts +1 -1
  44. package/dist/security/index.js +1 -1
  45. package/dist/security/injection-detector.d.ts +18 -0
  46. package/dist/security/injection-detector.js +39 -4
  47. package/dist/security/schema-validator.js +11 -2
  48. package/dist/types/messages.d.ts +57 -3
  49. package/dist/utils/tokenizer.js +4 -0
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/dist-cjs/agent/create-agent.js +15 -2
  53. package/dist-cjs/agent/glob.d.ts +24 -0
  54. package/dist-cjs/agent/glob.js +77 -0
  55. package/dist-cjs/agent/index.d.ts +1 -0
  56. package/dist-cjs/agent/index.js +5 -1
  57. package/dist-cjs/agent/interpreter.d.ts +125 -0
  58. package/dist-cjs/agent/interpreter.js +308 -0
  59. package/dist-cjs/agent/sandbox-container.d.ts +48 -0
  60. package/dist-cjs/agent/sandbox-container.js +327 -0
  61. package/dist-cjs/agent/sandbox.d.ts +50 -1
  62. package/dist-cjs/agent/sandbox.js +155 -2
  63. package/dist-cjs/agent/tool-search.d.ts +39 -0
  64. package/dist-cjs/agent/tool-search.js +118 -0
  65. package/dist-cjs/agent/tool.d.ts +2 -0
  66. package/dist-cjs/agent/tool.js +1 -0
  67. package/dist-cjs/context/window.js +4 -0
  68. package/dist-cjs/deep-agent/helpers.d.ts +57 -0
  69. package/dist-cjs/deep-agent/helpers.js +238 -0
  70. package/dist-cjs/deep-agent/index.d.ts +20 -1
  71. package/dist-cjs/deep-agent/index.js +68 -50
  72. package/dist-cjs/evaluate/generate.d.ts +171 -0
  73. package/dist-cjs/evaluate/generate.js +272 -0
  74. package/dist-cjs/evaluate/index.d.ts +1 -1
  75. package/dist-cjs/evaluate/index.js +2 -1
  76. package/dist-cjs/evaluate/online.d.ts +114 -0
  77. package/dist-cjs/evaluate/online.js +264 -0
  78. package/dist-cjs/evaluate/run.d.ts +28 -0
  79. package/dist-cjs/evaluate/run.js +58 -5
  80. package/dist-cjs/graph/graph.d.ts +7 -1
  81. package/dist-cjs/graph/graph.js +13 -1
  82. package/dist-cjs/operations/runner.js +1 -1
  83. package/dist-cjs/providers/anthropic.d.ts +6 -0
  84. package/dist-cjs/providers/anthropic.js +35 -8
  85. package/dist-cjs/providers/content.d.ts +33 -1
  86. package/dist-cjs/providers/content.js +69 -0
  87. package/dist-cjs/providers/google.js +13 -4
  88. package/dist-cjs/providers/openai.d.ts +8 -4
  89. package/dist-cjs/providers/openai.js +74 -33
  90. package/dist-cjs/runtime/index.d.ts +1 -1
  91. package/dist-cjs/security/index.d.ts +1 -1
  92. package/dist-cjs/security/index.js +2 -1
  93. package/dist-cjs/security/injection-detector.d.ts +18 -0
  94. package/dist-cjs/security/injection-detector.js +40 -4
  95. package/dist-cjs/security/schema-validator.js +11 -2
  96. package/dist-cjs/types/messages.d.ts +57 -3
  97. package/dist-cjs/utils/tokenizer.js +4 -0
  98. package/dist-cjs/version.d.ts +1 -1
  99. package/dist-cjs/version.js +1 -1
  100. package/llms.txt +1 -1
  101. package/package.json +53 -1
package/API_STABILITY.md CHANGED
@@ -41,6 +41,58 @@ Both are covered by this policy:
41
41
  Deep imports into `dist`, `dist-cjs`, or `src` are not supported. Import only from `nexus-ai-pro` or
42
42
  one of its explicit subpaths.
43
43
 
44
+ ## 2.7 stage (2.7.0)
45
+
46
+ Everything 2.7 adds is stable from the start and additive.
47
+
48
+ New and stable:
49
+ - on `nexus-ai-pro/agent/sandbox`: `Sandbox.glob()`, `grep()`, and `readBytes()`, all optional;
50
+ `SandboxGlobOptions`, `SandboxGrepOptions`, and `SandboxMatch`; `SandboxToolsOptions.maxMediaBytes`;
51
+ and the tool names `glob_files`, `grep_files`, and `read_media`;
52
+ - `DocumentContent` and the `document` member of `ContentPart`, exported from
53
+ `nexus-ai-pro/runtime`; the `base64` source of `AudioContent`;
54
+ - `nexus-ai-pro/agent/sandbox/container`: `containerSandbox()` and `ContainerSandboxOptions`, with
55
+ their defaults: no network, every capability dropped, no new privileges, 512 MB of memory and swap,
56
+ one CPU, and 256 processes;
57
+ - `nexus-ai-pro/agent/interpreter`: `Interpreter`, `InterpreterFunction`, `InterpreterRunOptions`,
58
+ `InterpreterResult`, `InterpreterCall`, `workerInterpreter()` and `WorkerInterpreterOptions`,
59
+ `codeTool()` and `CodeToolOptions` with the tool name `run_code`, and `runInterpreterConformance()`
60
+ with `InterpreterConformanceReport`; `DeepAgentOptions.interpreter`;
61
+ - `DeepAgentOptions.createHelpers` and `maxBackgroundHelpers`, `DeepHelperLimits`, the `background`
62
+ and `instructions` arguments of `delegate`, the tool name `collect_helper`, the helper thread id
63
+ `<thread>/helper/<name>@<call id>`, and the helper namespace `<name>@<call id>`;
64
+ - `ToolContext.forward`;
65
+ - `ToolDefinition.deferred` and `tool({ deferred })`; `ToolReferenceContent` and the `tool_reference`
66
+ member of `ContentPart`, exported from `nexus-ai-pro/runtime`; on `nexus-ai-pro/agent`,
67
+ `toolSearchTool()`, `ToolSearchOptions`, `searchTools()`, `ToolSearchHit`, and `TOOL_SEARCH_NAME`
68
+ with the tool name `search_tools`.
69
+
70
+ - `nexus-ai-pro/evaluate/generate`: `syntheticDataset()` with `SyntheticDatasetOptions`,
71
+ `SyntheticDocument`, `SyntheticPassage`, `SyntheticDraft`, `SyntheticCitation`, `SyntheticModelClient`,
72
+ and `SyntheticDatasetResult`; `adversarialDataset()` with `AdversarialDatasetOptions`,
73
+ `AdversarialInputs`, `AdversarialExpected`, `AttackCarrier`, and `AttackDisguise`; `resistsAttack()`;
74
+ `promoteIssue()` with `PromoteIssueOptions`;
75
+ - on `nexus-ai-pro/security`: `attackSeeds()`, `AttackSeed`, and `AttackCategory`;
76
+ - `nexus-ai-pro/evaluate/online`: `onlineEvaluationWorker()`, `OnlineEvaluationWorker`,
77
+ `OnlineEvaluationWorkerOptions`, `OnlineEvaluationWorkerReport`, and `OnlineSchedulePass`; the
78
+ operation kind `online-evaluation:<source>` and the score mark `value.evaluation`;
79
+ - `EvaluateOptions.budget` and `EvaluationBudgetError` on `nexus-ai-pro/evaluate`.
80
+
81
+ An agent with no deferred tool offers exactly the tools it did before. Adversarial example ids and
82
+ canaries are stable across runs, so experiments over the same options compare.
83
+
84
+ A graph run's first checkpoint carries the run's `metadata`, as every later checkpoint already did.
85
+
86
+ `sandboxTools()` offers the new tools only for a sandbox that implements the methods behind them, so
87
+ a sandbox of the application's own offers the same tools as before. A deep agent over
88
+ `processSandbox()` gets the three new tools and a line of its system prompt naming them.
89
+
90
+ On OpenAI, a tool message whose media is not only images is followed by a user message that starts
91
+ `Files the tools returned:`; one with only images keeps `Images the tools returned:`.
92
+
93
+ `ContentPart` and `AudioContent['source']` gain members, as the compatibility rules allow in a minor
94
+ release. Code that switches over either exhaustively should handle the new ones.
95
+
44
96
  ## 2.6 stage (2.6.0)
45
97
 
46
98
  Everything 2.6 adds is stable from the start and additive.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,123 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.7.0] - 2026-10-12
8
+
9
+ Agents that do more, and evaluation that runs on its own.
10
+
11
+ ### Added
12
+
13
+ - **Workspace search.** A `Sandbox` may implement `glob()`, `grep()`, and `readBytes()`, with
14
+ `SandboxGlobOptions`, `SandboxGrepOptions`, and `SandboxMatch`. `sandboxTools()` then offers
15
+ `glob_files` and `grep_files`, bounded at 500 paths and 200 lines, each declaring the directory it
16
+ reads. `processSandbox()` implements all three: its search follows no links, skips `.git` and
17
+ `node_modules`, walks at most 20,000 entries, and skips files over 1 MB and binary files.
18
+ - **A multimodal workspace.** `read_media` reads an image, audio, or a PDF in the sandbox as content
19
+ the model sees, as base64 so it survives a checkpoint, up to `SandboxToolsOptions.maxMediaBytes`
20
+ (10 MB by default). A deep agent gets all three tools when its sandbox supports them.
21
+ - **Documents and audio reach the model.** The new `DocumentContent` part carries a document, such
22
+ as a PDF, and `AudioContent` takes base64 text. Anthropic receives a document as a document block,
23
+ inside a tool result too; OpenAI chat as a file, OpenAI Responses as an input file, and Google as
24
+ inline data. Audio reaches Google, and OpenAI chat's audio models as `input_audio`. A tool's media
25
+ follows its results on OpenAI and Google, as its images already did. Any other provider, or a
26
+ source it cannot read, receives one line naming the part.
27
+ - **A container sandbox**, on the new `nexus-ai-pro/agent/sandbox/container`: `containerSandbox()`
28
+ drives the `docker` or `podman` binary, with no dependency, one container per sandbox. By default
29
+ the container has no network, drops every capability, cannot gain privileges, and is held to
30
+ 512 MB, one CPU, and 256 processes; nothing of the host is mounted. A command past its timeout is
31
+ killed inside the container with everything it started. Files, glob, grep, and media work inside
32
+ it. `ContainerSandboxOptions` sets the image, binary, working directory, network, and limits.
33
+ - **An interpreter contract**, on the new `nexus-ai-pro/agent/interpreter`. An `Interpreter` runs a
34
+ snippet that calls the agent's tools as `await tools.<name>(args)`, so many calls take one step.
35
+ `codeTool()` offers it to a model as `run_code`: it declares `code`, and the policy decides each
36
+ call the code makes as it decides the model's own, refusing one it would ask a person about.
37
+ `workerInterpreter()` is the JavaScript reference: a fresh worker per run, a 64 MB heap, a
38
+ 10-second timeout, and caps on calls and logs. It is not a security boundary.
39
+ `runInterpreterConformance()` checks any interpreter. `DeepAgentOptions.interpreter` adds
40
+ `run_code` over the deep agent's own tools, and its default policy then grants code.
41
+ - **Helpers beside the deep agent.**
42
+ - `delegate` takes `background`: it returns a task id at once, and the new `collect_helper` tool
43
+ returns the answer later, waiting up to `wait_seconds`. `maxBackgroundHelpers` (4) bounds how
44
+ many work at once.
45
+ - A background helper keeps checkpoints in the deep agent's checkpointer, on a thread named after
46
+ the call that started it. After a crash, collecting the task, or repeating the call, carries the
47
+ same helper on from its last checkpoint.
48
+ - `createHelpers` lets the agent create a helper from instructions it writes, within
49
+ `DeepHelperLimits`: the tools, model, model calls, and number per thread.
50
+ - Streamed with `subgraphs: true`, a helper's events reach the agent's stream under its namespace,
51
+ such as `tools/researcher@call-7`. `ToolContext.forward` lets any tool do the same for a run it
52
+ starts.
53
+ - **Tool search.** `ToolDefinition.deferred` (and `tool({ deferred })`) loads a tool only once the
54
+ model finds it. An agent with a deferred tool offers `search_tools`, which ranks the deferred tools
55
+ by BM25 over their names, descriptions, and arguments, and answers with `ToolReferenceContent`
56
+ parts. On Claude 4.5 and later every tool is sent, a deferred one with `defer_loading`, and a
57
+ found tool loads through a `tool_reference` block, so the tool list never changes; every other
58
+ model, OpenAI's and Google's included, is sent a deferred tool once it has been found.
59
+ `toolSearchTool()`, `searchTools()`, and `TOOL_SEARCH_NAME` are on `nexus-ai-pro/agent`.
60
+ - **Datasets that grow**, on the new `nexus-ai-pro/evaluate/generate`:
61
+ - `syntheticDataset()` writes examples from documents, from your function or a model. Each keeps
62
+ a citation of the quote that supports it, checked against its passage; unsupported drafts and
63
+ near-duplicate questions are dropped and counted.
64
+ - `adversarialDataset()` grows attacks from the guardrails' own patterns, in the user's message, a
65
+ document, or a tool's result, plain or disguised, each after a canary in the system prompt.
66
+ `resistsAttack()` scores whether the canary stayed in.
67
+ - `promoteIssue()` turns an insights issue's runs into examples that keep their run and the issue.
68
+ - `attackSeeds()` on `nexus-ai-pro/security` returns each injection pattern's example attack.
69
+ - **Online evaluation on workers**, on the new `nexus-ai-pro/evaluate/online`.
70
+ `onlineEvaluationWorker()` samples runs by a stable hash, queues one operation per run and source
71
+ on a shared operation store, stops queuing at `maxQueued`, and scores what it claims within a
72
+ budget ledger. Each score carries a mark naming its operation, so a retry after a crash writes
73
+ only what is missing, and each run is scored exactly once per source.
74
+ - **A spend cap for experiments.** `evaluate()` takes `budget`. Each example run is reserved and
75
+ reconciled, and once the budget runs out no further example starts; `evaluate()` rejects with the
76
+ new `EvaluationBudgetError`, which carries the results it paid for.
77
+
78
+ ### Fixed
79
+
80
+ - An operation whose store failed under it, such as a database going away while it recorded its
81
+ result, left its handle pending forever, with an unhandled rejection; the handle now fails with
82
+ the store's error, and the record's lease lapses for recovery.
83
+
84
+ ### Changed
85
+
86
+ - A graph run's first checkpoint now carries the run's `metadata`, as every later checkpoint did.
87
+ - A deep agent with helpers offers `collect_helper` beside `delegate`, and its prompt says how to use
88
+ both; its default policy allows `collect_helper`.
89
+
90
+ ### Proof
91
+
92
+ - Glob and grep on the reference sandbox: alternatives, `**/` matching no directory, results relative
93
+ to the root, a limit, a path outside the root refused, a link out of the root not followed, and
94
+ binary, oversized, vendored, and past-the-line-limit matches skipped.
95
+ - An image, a PDF, and audio from a tool, after a JSON round trip, on the wire of Anthropic, OpenAI
96
+ chat (with and without an audio model), OpenAI Responses, and Google.
97
+ - A deep agent that greps its workspace, finds a PDF, and reads it as a document.
98
+ - The container sandbox passes the sandbox conformance suite against a real Docker in CI, which now
99
+ requires one. A command cannot find a host file anywhere in the container, cannot reach the host or
100
+ the internet, while the same probe from a container with a network can; a command over a 64 MB
101
+ limit is stopped; and a timeout leaves nothing it started running.
102
+ - The worker interpreter passes the interpreter conformance suite. An agent totals three orders and
103
+ describes one from code in one model round trip, while the refund in the same code, which the
104
+ policy asks about, never runs; a deep agent searches and reads its workspace from code, and a
105
+ command its policy denies is denied from code too.
106
+ - A background helper answers after the agent has moved on, with its events in the agent's stream. A
107
+ crash while the agent waits for it, and a crash before the agent saved the call that started it,
108
+ each end with one lookup, one note, and one start of the helper.
109
+ - An agent with 500 deferred tools finishes all 40 tasks on Anthropic, OpenAI, and Google, with a
110
+ model that calls a tool only once that provider's request has loaded it. Anthropic is sent all 501
111
+ tools on every call with 500 deferred; OpenAI and Google never more than 11. The search ranks each
112
+ of the 500 tools first for the step that describes it.
113
+ - A synthetic dataset written from all 39 guides cites, for every example, words that stand in its
114
+ guide where the citation says; an invented quote and a repeated question are dropped every time.
115
+ An adversarial dataset of 75 examples gets its canary out of an unguarded agent every time and
116
+ never out of a guarded one, which still answers the benign examples. Each attack the guardrails
117
+ block at the input is critical or high, and each medium one is redacted on the way out.
118
+ - Ten online-evaluation workers score 10,000 runs, each score exactly once, while one dies right
119
+ after writing a run's scores and before recording them; that run is taken back and found already
120
+ scored, and the queue stays near its limit. Workers on a budget stop at it, leave the rest queued,
121
+ and carry on the next period without scoring any run twice. Two sources sharing one store never
122
+ cross, and sampling picks the same runs on every worker. An experiment stops at its budget.
123
+
7
124
  ## [2.6.0] - 2026-10-11
8
125
 
9
126
  Protocols complete, and progress on every stream.
@@ -2204,7 +2321,8 @@ shared policy vocabulary is in place for a future release that revisits this.
2204
2321
  - URL fetching rejects unsafe private-network targets and limits response reads.
2205
2322
  - CLI and audit findings no longer disclose detected secret values.
2206
2323
 
2207
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.6.0...HEAD
2324
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.7.0...HEAD
2325
+ [2.7.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.6.0...v2.7.0
2208
2326
  [2.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.5.0...v2.6.0
2209
2327
  [2.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.4.0...v2.5.0
2210
2328
  [2.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...v2.4.0
package/README.md CHANGED
@@ -275,14 +275,17 @@ Shipped so far:
275
275
  the protocols, traces, and the studio; AG-UI state deltas and frontend tools; A2A tasks and ACP
276
276
  sessions that survive a restart, with push notifications, `session/load`, and the editor's files,
277
277
  terminal, and MCP servers; and every protocol proven in a browser, against the previous release,
278
- and against its reference client.
278
+ and against its reference client;
279
+ - 2.7 agents that do more, and evaluation that runs on its own: workspace search and a multimodal
280
+ workspace, a container sandbox, an interpreter, helpers that run in the background and survive a
281
+ crash, and tool search on every provider; datasets generated from documents and from the
282
+ guardrails' attacks; and online evaluation on workers, within a budget.
279
283
 
280
284
  Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
281
285
  [MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
282
- Next, 2.7 lets agents do more: provider tool search, an interpreter contract, workspace search,
283
- helpers that run beside the agent, and a container sandbox. Evaluation grows its own datasets and
284
- runs on workers, within a budget. The [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists the
285
- releases after it.
286
+ Next, the remote vector stores and images graduate once the live workflow passes against each. The
287
+ [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists what comes after, in the order
288
+ applications ask for it.
286
289
 
287
290
  ## Before Production
288
291
 
@@ -1,8 +1,9 @@
1
1
  import { appendList, counter, lastValue } from '../graph/channels.js';
2
- import { createGraph } from '../graph/graph.js';
2
+ import { createGraph, forwardedEvents } from '../graph/graph.js';
3
3
  import { Command, END, Send } from '../types/graph.js';
4
4
  import { capabilitiesOf } from './capabilities.js';
5
5
  import { ToolExecutor, toolMessageContent } from './tool.js';
6
+ import { TOOL_SEARCH_NAME, toolSearchTool } from './tool-search.js';
6
7
  const agentChannels = () => ({
7
8
  messages: appendList(),
8
9
  iterations: counter(),
@@ -18,7 +19,13 @@ export function createAgent(options) {
18
19
  throw new TypeError('createAgent needs a client with a complete() method');
19
20
  }
20
21
  const maxIterations = Math.max(1, options.maxIterations ?? 8);
21
- const executor = new ToolExecutor(options.tools ?? []);
22
+ const searchable = (tools) => {
23
+ const deferred = tools.list().filter((definition) => definition.deferred);
24
+ if (deferred.length === 0 || tools.has(TOOL_SEARCH_NAME))
25
+ return tools;
26
+ return new ToolExecutor([...tools.list(), toolSearchTool(deferred)]);
27
+ };
28
+ const executor = searchable(new ToolExecutor(options.tools ?? []));
22
29
  const middleware = options.middleware ?? [];
23
30
  const approvals = options.interruptOn ?? {};
24
31
  const toolsFor = async (context) => {
@@ -30,7 +37,7 @@ export function createAgent(options) {
30
37
  ...(context.principal ? { principal: context.principal } : {}),
31
38
  signal: context.signal,
32
39
  });
33
- return supplied.length ? new ToolExecutor([...supplied, ...executor.list()]) : executor;
40
+ return supplied.length ? searchable(new ToolExecutor([...supplied, ...executor.list()])) : executor;
34
41
  };
35
42
  const graph = createGraph({ channels: agentChannels() })
36
43
  .addNode('model', async (context) => {
@@ -187,6 +194,7 @@ export function createAgent(options) {
187
194
  }
188
195
  }
189
196
  }
197
+ const forward = forwardedEvents(context);
190
198
  const run = async (current) => {
191
199
  if (options.permissions && current.args !== args) {
192
200
  const recheck = await options.permissions.decide({
@@ -205,6 +213,11 @@ export function createAgent(options) {
205
213
  signal: context.signal,
206
214
  ...(context.store ? { store: context.store } : {}),
207
215
  progress: (progress) => context.tool({ phase: 'progress', id: call.id, name: description.name, progress }),
216
+ ...(forward
217
+ ? {
218
+ forward: (event, namespace) => forward({ ...event, namespace: [...namespace, ...(event.namespace ?? [])] }),
219
+ }
220
+ : {}),
208
221
  });
209
222
  };
210
223
  context.tool({ phase: 'start', id: call.id, name: description.name, args });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * File globs for workspace search, shared by the sandboxes: `*` matches within a path segment, `**`
3
+ * across segments (`**\/` also matches none), `?` one character, and `{a,b}` either alternative.
4
+ * Paths use `/`, are relative to the search root, and match case-sensitively.
5
+ */
6
+ export declare function globMatcher(pattern: string): (path: string) => boolean;
7
+ /** Directories workspace search never enters. */
8
+ export declare const SKIPPED_DIRECTORIES: ReadonlySet<string>;
9
+ /** The most directory entries, or files, one search walks. */
10
+ export declare const WALK_LIMIT = 20000;
11
+ /** Files larger than this are not searched. */
12
+ export declare const GREP_FILE_BYTES = 1048576;
13
+ /** One search reads no more than this, in all. */
14
+ export declare const GREP_TOTAL_BYTES: number;
15
+ /**
16
+ * The lines of a file that match, numbered from 1 and cut to 300 characters, at most `limit`; none
17
+ * for a binary file, one with a NUL byte in its first 8,000 bytes.
18
+ */
19
+ export declare function matchingLines(bytes: Uint8Array, expression: RegExp, limit: number): Array<{
20
+ line: number;
21
+ text: string;
22
+ }>;
23
+ /** MIME types for the media a workspace tool reads as content the model sees, by file extension. */
24
+ export declare const MEDIA_TYPES: Readonly<Record<string, string>>;
@@ -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>;