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.
- package/API_STABILITY.md +52 -0
- package/CHANGELOG.md +119 -1
- package/README.md +8 -5
- package/dist/agent/create-agent.js +16 -3
- package/dist/agent/glob.d.ts +24 -0
- package/dist/agent/glob.js +72 -0
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/interpreter.d.ts +125 -0
- package/dist/agent/interpreter.js +270 -0
- package/dist/agent/sandbox-container.d.ts +48 -0
- package/dist/agent/sandbox-container.js +291 -0
- package/dist/agent/sandbox.d.ts +50 -1
- package/dist/agent/sandbox.js +155 -2
- package/dist/agent/tool-search.d.ts +39 -0
- package/dist/agent/tool-search.js +113 -0
- package/dist/agent/tool.d.ts +2 -0
- package/dist/agent/tool.js +1 -0
- package/dist/context/window.js +4 -0
- package/dist/deep-agent/helpers.d.ts +57 -0
- package/dist/deep-agent/helpers.js +235 -0
- package/dist/deep-agent/index.d.ts +20 -1
- package/dist/deep-agent/index.js +69 -51
- package/dist/evaluate/generate.d.ts +171 -0
- package/dist/evaluate/generate.js +266 -0
- package/dist/evaluate/index.d.ts +1 -1
- package/dist/evaluate/index.js +1 -1
- package/dist/evaluate/online.d.ts +114 -0
- package/dist/evaluate/online.js +261 -0
- package/dist/evaluate/run.d.ts +28 -0
- package/dist/evaluate/run.js +56 -5
- package/dist/graph/graph.d.ts +7 -1
- package/dist/graph/graph.js +12 -1
- package/dist/operations/runner.js +1 -1
- package/dist/providers/anthropic.d.ts +6 -0
- package/dist/providers/anthropic.js +36 -9
- package/dist/providers/content.d.ts +33 -1
- package/dist/providers/content.js +62 -0
- package/dist/providers/google.js +14 -5
- package/dist/providers/openai.d.ts +8 -4
- package/dist/providers/openai.js +75 -34
- package/dist/runtime/index.d.ts +1 -1
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/injection-detector.d.ts +18 -0
- package/dist/security/injection-detector.js +39 -4
- package/dist/security/schema-validator.js +11 -2
- package/dist/types/messages.d.ts +57 -3
- package/dist/utils/tokenizer.js +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/create-agent.js +15 -2
- package/dist-cjs/agent/glob.d.ts +24 -0
- package/dist-cjs/agent/glob.js +77 -0
- package/dist-cjs/agent/index.d.ts +1 -0
- package/dist-cjs/agent/index.js +5 -1
- package/dist-cjs/agent/interpreter.d.ts +125 -0
- package/dist-cjs/agent/interpreter.js +308 -0
- package/dist-cjs/agent/sandbox-container.d.ts +48 -0
- package/dist-cjs/agent/sandbox-container.js +327 -0
- package/dist-cjs/agent/sandbox.d.ts +50 -1
- package/dist-cjs/agent/sandbox.js +155 -2
- package/dist-cjs/agent/tool-search.d.ts +39 -0
- package/dist-cjs/agent/tool-search.js +118 -0
- package/dist-cjs/agent/tool.d.ts +2 -0
- package/dist-cjs/agent/tool.js +1 -0
- package/dist-cjs/context/window.js +4 -0
- package/dist-cjs/deep-agent/helpers.d.ts +57 -0
- package/dist-cjs/deep-agent/helpers.js +238 -0
- package/dist-cjs/deep-agent/index.d.ts +20 -1
- package/dist-cjs/deep-agent/index.js +68 -50
- package/dist-cjs/evaluate/generate.d.ts +171 -0
- package/dist-cjs/evaluate/generate.js +272 -0
- package/dist-cjs/evaluate/index.d.ts +1 -1
- package/dist-cjs/evaluate/index.js +2 -1
- package/dist-cjs/evaluate/online.d.ts +114 -0
- package/dist-cjs/evaluate/online.js +264 -0
- package/dist-cjs/evaluate/run.d.ts +28 -0
- package/dist-cjs/evaluate/run.js +58 -5
- package/dist-cjs/graph/graph.d.ts +7 -1
- package/dist-cjs/graph/graph.js +13 -1
- package/dist-cjs/operations/runner.js +1 -1
- package/dist-cjs/providers/anthropic.d.ts +6 -0
- package/dist-cjs/providers/anthropic.js +35 -8
- package/dist-cjs/providers/content.d.ts +33 -1
- package/dist-cjs/providers/content.js +69 -0
- package/dist-cjs/providers/google.js +13 -4
- package/dist-cjs/providers/openai.d.ts +8 -4
- package/dist-cjs/providers/openai.js +74 -33
- package/dist-cjs/runtime/index.d.ts +1 -1
- package/dist-cjs/security/index.d.ts +1 -1
- package/dist-cjs/security/index.js +2 -1
- package/dist-cjs/security/injection-detector.d.ts +18 -0
- package/dist-cjs/security/injection-detector.js +40 -4
- package/dist-cjs/security/schema-validator.js +11 -2
- package/dist-cjs/types/messages.d.ts +57 -3
- package/dist-cjs/utils/tokenizer.js +4 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +1 -1
- 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.
|
|
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,
|
|
283
|
-
|
|
284
|
-
|
|
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
|
|
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
|
+
};
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/agent/index.js
CHANGED
|
@@ -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>;
|