nexus-ai-pro 2.5.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 +88 -0
- package/CHANGELOG.md +220 -1
- package/README.md +13 -4
- package/dist/agent/create-agent.d.ts +37 -2
- package/dist/agent/create-agent.js +77 -6
- package/dist/agent/glob.d.ts +24 -0
- package/dist/agent/glob.js +72 -0
- package/dist/agent/index.d.ts +3 -1
- 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 +21 -4
- package/dist/agent/tool.js +28 -4
- 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/event-stream.js +2 -0
- package/dist/graph/graph.d.ts +7 -1
- package/dist/graph/graph.js +16 -2
- package/dist/mcp/client.d.ts +9 -1
- package/dist/mcp/client.js +38 -8
- package/dist/mcp/server.js +42 -2
- package/dist/operations/runner.js +1 -1
- package/dist/protocols/a2a.d.ts +166 -1
- package/dist/protocols/a2a.js +499 -132
- package/dist/protocols/acp-editor.d.ts +43 -0
- package/dist/protocols/acp-editor.js +164 -0
- package/dist/protocols/acp.d.ts +76 -3
- package/dist/protocols/acp.js +180 -7
- package/dist/protocols/ag-ui.d.ts +23 -0
- package/dist/protocols/ag-ui.js +128 -8
- 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/tracing/instrument.js +14 -3
- package/dist/types/graph.d.ts +7 -0
- package/dist/types/messages.d.ts +69 -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.d.ts +37 -2
- package/dist-cjs/agent/create-agent.js +76 -5
- package/dist-cjs/agent/glob.d.ts +24 -0
- package/dist-cjs/agent/glob.js +77 -0
- package/dist-cjs/agent/index.d.ts +3 -1
- 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 +21 -4
- package/dist-cjs/agent/tool.js +28 -4
- 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/event-stream.js +2 -0
- package/dist-cjs/graph/graph.d.ts +7 -1
- package/dist-cjs/graph/graph.js +17 -2
- package/dist-cjs/mcp/client.d.ts +9 -1
- package/dist-cjs/mcp/client.js +37 -7
- package/dist-cjs/mcp/server.js +42 -2
- package/dist-cjs/operations/runner.js +1 -1
- package/dist-cjs/protocols/a2a.d.ts +166 -1
- package/dist-cjs/protocols/a2a.js +501 -132
- package/dist-cjs/protocols/acp-editor.d.ts +43 -0
- package/dist-cjs/protocols/acp-editor.js +167 -0
- package/dist-cjs/protocols/acp.d.ts +76 -3
- package/dist-cjs/protocols/acp.js +183 -8
- package/dist-cjs/protocols/ag-ui.d.ts +23 -0
- package/dist-cjs/protocols/ag-ui.js +127 -7
- 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/tracing/instrument.js +14 -3
- package/dist-cjs/types/graph.d.ts +7 -0
- package/dist-cjs/types/messages.d.ts +69 -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 +69 -2
package/API_STABILITY.md
CHANGED
|
@@ -41,6 +41,94 @@ 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
|
+
|
|
96
|
+
## 2.6 stage (2.6.0)
|
|
97
|
+
|
|
98
|
+
Everything 2.6 adds is stable from the start and additive.
|
|
99
|
+
|
|
100
|
+
New and stable:
|
|
101
|
+
- `ToolDefinition.stream`, `ToolContext.progress`, and `ToolBody`; `tool()` with `stream`. The
|
|
102
|
+
AG-UI event name `tool_progress`, and the `progress` and `callId` fields of a tool run's metadata;
|
|
103
|
+
- `McpClient.callTool()`'s `progress` option, and `notifications/progress` from `McpServer`;
|
|
104
|
+
- `AgUiOptions.stateDeltas`, `messagesSnapshot`, and `frontendTools`, and `AgUiPatchOperation`;
|
|
105
|
+
- `CreateAgentOptions.toolSource` and `ToolSourceContext`; `ClientToolDefinition`, the `clientTools`
|
|
106
|
+
channel, and the `AgentStopReason` member `client_tools`;
|
|
107
|
+
- the `step` member of `GraphEvent`;
|
|
108
|
+
- on `nexus-ai-pro/protocols/a2a`: `A2aTaskStore`, `A2aTaskRecord`, `A2aTaskQuery`,
|
|
109
|
+
`MemoryA2aTaskStore`, `storeA2aTasks()`; `A2aServerOptions.store`, `pushNotifications`,
|
|
110
|
+
`extendedCard`, `pollMs`, and `onError`; `A2aPushOptions` and `A2aPushNotificationConfig`; the
|
|
111
|
+
methods `ListTasks`, `SubscribeToTask`, the four push notification config methods, and
|
|
112
|
+
`GetExtendedAgentCard`; the client's new methods, `A2aListTasksOptions`, and `A2aTaskPage`; and
|
|
113
|
+
the error codes `pushNotificationNotSupported` and `extendedCardNotConfigured`.
|
|
114
|
+
|
|
115
|
+
- on `nexus-ai-pro/protocols/acp`: `AcpSessionStore`, `AcpSessionRecord`, `MemoryAcpSessionStore`,
|
|
116
|
+
`storeAcpSessions()`, `AcpOptions.sessions` and `editor`, `AcpEditorBridge`, `AcpConnection`,
|
|
117
|
+
`AcpClientCapabilities`, `session/load`, image and audio blocks, and the fourth argument of
|
|
118
|
+
`AcpOptions.input`;
|
|
119
|
+
- `nexus-ai-pro/protocols/acp/editor`: `acpEditor()`, `AcpEditor`, `AcpEditorOptions`, and the tool
|
|
120
|
+
names `editor_read_file`, `editor_write_file`, and `editor_run_command`.
|
|
121
|
+
|
|
122
|
+
An AG-UI run reports a node's tasks running side by side as one step, and finishes every step it
|
|
123
|
+
started before `RUN_FINISHED` or `RUN_ERROR`. A2A `ListTasks` refuses a status that is not a task
|
|
124
|
+
state, and reads `TASK_STATE_UNSPECIFIED` and `UNRECOGNIZED` as no filter.
|
|
125
|
+
|
|
126
|
+
A streamed A2A task now reports its move to `TASK_STATE_WORKING` as a status update, before its
|
|
127
|
+
other events.
|
|
128
|
+
|
|
129
|
+
`GraphEvent` and `AgentStopReason` gain members, as the compatibility rules allow in a minor
|
|
130
|
+
release. Code that switches over either exhaustively should handle the new ones.
|
|
131
|
+
|
|
44
132
|
## 2.5 stage (2.5.0)
|
|
45
133
|
|
|
46
134
|
Everything 2.5 adds is stable from the start and additive.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,223 @@ 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
|
+
|
|
124
|
+
## [2.6.0] - 2026-10-11
|
|
125
|
+
|
|
126
|
+
Protocols complete, and progress on every stream.
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
|
|
130
|
+
- **Tool progress, end to end.**
|
|
131
|
+
- A `ToolDefinition` can have `stream`: an async generator that yields progress and returns the
|
|
132
|
+
result. `tool()` takes one instead of `execute`, and gives it an `execute` that runs the stream,
|
|
133
|
+
so it works wherever a tool is executed. `ToolExecutor` runs a stream itself.
|
|
134
|
+
- `ToolContext.progress` reports progress from any tool. An agent sets it, and turns each report
|
|
135
|
+
into a `progress` tool event while the tool runs.
|
|
136
|
+
- Progress reaches the event stream and the server's run events; AG-UI, as a `CUSTOM`
|
|
137
|
+
`tool_progress` event; A2A, as a working status update with the progress as data; ACP, as an
|
|
138
|
+
update of the tool call in progress; traces, in the tool run's `metadata.progress`; and the
|
|
139
|
+
studio's run view.
|
|
140
|
+
- MCP: `McpServer` runs `stream` tools and sends `notifications/progress` for a call that names a
|
|
141
|
+
`progressToken`. `McpClient.callTool()` takes a `progress` listener, and the tools from
|
|
142
|
+
`toNexusTools()` pass a remote tool's progress to the agent.
|
|
143
|
+
|
|
144
|
+
- **AG-UI, complete**, each part opt-in on `AgUiOptions`:
|
|
145
|
+
- `stateDeltas` reports state as it changes: a `STATE_SNAPSHOT` after the first step, then a
|
|
146
|
+
`STATE_DELTA` with an RFC 6902 JSON Patch after every step that changed it, appending to arrays
|
|
147
|
+
item by item. `AgUiPatchOperation` is its type.
|
|
148
|
+
- `messagesSnapshot` sends the whole transcript as `MESSAGES_SNAPSHOT` before the run finishes.
|
|
149
|
+
- `frontendTools` offers the tools the frontend sends to the model. The frontend runs a call to one,
|
|
150
|
+
and the next run's messages carry its result.
|
|
151
|
+
- **Tools the client runs.** An agent's input may carry `clientTools`, each a `ClientToolDefinition`.
|
|
152
|
+
The model is offered them; a call to one ends the turn with the new `stopReason: 'client_tools'`
|
|
153
|
+
once the agent's own calls of that step have run, and the client's result continues it. A client
|
|
154
|
+
tool never replaces a tool of the agent's own.
|
|
155
|
+
- **Tools that depend on the run.** `CreateAgentOptions.toolSource` supplies tools per run, from a
|
|
156
|
+
`ToolSourceContext`, offered and run like `tools`, under the same permission policy.
|
|
157
|
+
- **A2A, durable and complete.**
|
|
158
|
+
- Tasks and conversations live in an `A2aTaskStore`: `MemoryA2aTaskStore` by default, or
|
|
159
|
+
`storeA2aTasks()` over any long-term `Store`, so a task paused for an answer survives a restart
|
|
160
|
+
and another replica finishes it.
|
|
161
|
+
- `ListTasks` by context and state, a page at a time; `SubscribeToTask` on any replica; and a
|
|
162
|
+
cancel that stands against the replica running the task.
|
|
163
|
+
- Push notifications, with `pushNotifications`: each state change POSTed to a task's webhook,
|
|
164
|
+
with the caller's token and signed as the server's operation webhooks are. A webhook must be
|
|
165
|
+
`https` and not a private address, unless `allowPrivateNetworks` is set.
|
|
166
|
+
- `GetExtendedAgentCard`, with `extendedCard`, for authenticated callers.
|
|
167
|
+
- The client gains `extendedCard()`, `listTasks()`, `subscribe()`, `setPushNotification()`,
|
|
168
|
+
`listPushNotifications()`, `deletePushNotification()`, and `push` and `returnImmediately` when
|
|
169
|
+
sending.
|
|
170
|
+
- **ACP, durable and complete.**
|
|
171
|
+
- Sessions live in an `AcpSessionStore`: `MemoryAcpSessionStore` by default, or
|
|
172
|
+
`storeAcpSessions()` over any long-term `Store`. The agent offers `session/load`, replays the
|
|
173
|
+
conversation, and asks again a question it was waiting on.
|
|
174
|
+
- A prompt can carry images and audio, which reach the model as content parts.
|
|
175
|
+
- `acpEditor()`, on the new `nexus-ai-pro/protocols/acp/editor`, gives an agent the editor's files,
|
|
176
|
+
terminal, and MCP servers, as tools from its `toolSource`, each declaring what it touches.
|
|
177
|
+
`serveAcp()` takes it as `editor`, and detaches it when the editor closes its end.
|
|
178
|
+
- **Interoperability, proven.**
|
|
179
|
+
- A browser in CI: `npm run test:browser` serves the build to headless Chromium, which imports
|
|
180
|
+
every kernel entry point and runs a graph, an agent, and AG-UI. It runs in `npm test`.
|
|
181
|
+
- The previous minor on the other side of the wire: a 2.5 client against a 2.6 server, and the
|
|
182
|
+
other way round, on the server's HTTP API and A2A, and one harness across both releases for
|
|
183
|
+
AG-UI and ACP.
|
|
184
|
+
- A nightly `Interoperability` workflow drives each protocol with its reference client:
|
|
185
|
+
`@ag-ui/client`, `@a2a-js/sdk`, and `@agentclientprotocol/sdk`.
|
|
186
|
+
- **A `step` graph event.** `GraphRunOptions.onEvent` receives `step` after each superstep, with the
|
|
187
|
+
state after it.
|
|
188
|
+
|
|
189
|
+
### Fixed
|
|
190
|
+
|
|
191
|
+
- AG-UI: a run that paused inside a node left that node's step open, so a strict client refused its
|
|
192
|
+
`RUN_FINISHED`; and a node's tasks running side by side each started the same step. A node's tasks
|
|
193
|
+
are now one step, and every step still open is finished before the run finishes or fails. Found by
|
|
194
|
+
the reference client.
|
|
195
|
+
- A2A: `ListTasks` without a status filter, as the reference SDK sends it (`UNRECOGNIZED`), matched
|
|
196
|
+
no task. An unset filter now filters nothing, and an unknown state is refused as invalid.
|
|
197
|
+
- MCP: the stdio transport's `close()` killed the server without waiting for it, so what the process
|
|
198
|
+
held, such as its working directory, could outlive the close. It now waits up to two seconds for
|
|
199
|
+
the process to exit.
|
|
200
|
+
|
|
201
|
+
### Proof
|
|
202
|
+
|
|
203
|
+
- Headless Chromium imports every kernel entry point and runs a graph, an agent with a streaming
|
|
204
|
+
tool, and AG-UI. A 2.5 client and a 2.6 server pass the protocol suites in both directions, and the
|
|
205
|
+
reference clients of AG-UI, A2A, and ACP each drive this release's servers through a whole run.
|
|
206
|
+
- An ACP session paused on a question survives the agent's restart, on memory and SQLite; the
|
|
207
|
+
editor reopens it, sees the conversation replayed, and answers. An image and audio reach the model.
|
|
208
|
+
Through the bridge, an agent reads, writes (after the person allows it), and runs the tests in the
|
|
209
|
+
editor, and searches docs through an MCP server the editor offered, started as a real process.
|
|
210
|
+
- An A2A task paused for an approval survives a restart and is finished by another replica, sending
|
|
211
|
+
its email once, on the memory, SQLite, Postgres, and Redis stores. A running task is followed where
|
|
212
|
+
it runs and from another replica; a cancel on one replica stands against the other. Every state
|
|
213
|
+
change reaches the webhook, signed and with the caller's token, and nine webhook URLs that point
|
|
214
|
+
into the agent's own network or are not https are refused.
|
|
215
|
+
- An AG-UI client applying a run's snapshot and its deltas rebuilds the final state exactly, with
|
|
216
|
+
each new message one `add`. A frontend tool is offered to the model beside the agent's own; the
|
|
217
|
+
agent runs its own call, waits without calling the model again, and continues from the page's
|
|
218
|
+
result in the next run. A page cannot shadow the agent's tool, and without the options the events
|
|
219
|
+
are what they were.
|
|
220
|
+
- A tool that will not go on until a reader has seen its first report finishes on every stream: the
|
|
221
|
+
event stream, the server's events, AG-UI, A2A, ACP, and MCP. Its reports arrive in order, after the
|
|
222
|
+
call and before the result, and its traced run keeps them.
|
|
223
|
+
|
|
7
224
|
## [2.5.0] - 2026-10-09
|
|
8
225
|
|
|
9
226
|
Graduation finished, and conversations evaluated.
|
|
@@ -2104,7 +2321,9 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
2104
2321
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
2105
2322
|
- CLI and audit findings no longer disclose detected secret values.
|
|
2106
2323
|
|
|
2107
|
-
[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
|
|
2326
|
+
[2.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.5.0...v2.6.0
|
|
2108
2327
|
[2.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.4.0...v2.5.0
|
|
2109
2328
|
[2.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...v2.4.0
|
|
2110
2329
|
[2.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.2.0...v2.3.0
|
package/README.md
CHANGED
|
@@ -270,13 +270,22 @@ Shipped so far:
|
|
|
270
270
|
- 2.5 graduation finished, and conversations evaluated: deployments that change atomically across
|
|
271
271
|
replicas, revision worker pools, fair canaries judged on every run, and the studio out of
|
|
272
272
|
experimental after a threat review; Postgres tenant usage; signed context named on every trace;
|
|
273
|
-
simulated users and thread evaluators; and a doctor that knows the opt-in settings
|
|
273
|
+
simulated users and thread evaluators; and a doctor that knows the opt-in settings;
|
|
274
|
+
- 2.6 protocols complete, and progress on every stream: a tool's progress reaching the event stream,
|
|
275
|
+
the protocols, traces, and the studio; AG-UI state deltas and frontend tools; A2A tasks and ACP
|
|
276
|
+
sessions that survive a restart, with push notifications, `session/load`, and the editor's files,
|
|
277
|
+
terminal, and MCP servers; and every protocol proven in a browser, against the previous release,
|
|
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.
|
|
274
283
|
|
|
275
284
|
Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
|
|
276
285
|
[MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
|
|
277
|
-
Next,
|
|
278
|
-
|
|
279
|
-
|
|
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.
|
|
280
289
|
|
|
281
290
|
## Before Production
|
|
282
291
|
|
|
@@ -163,12 +163,44 @@ export interface CreateAgentOptions {
|
|
|
163
163
|
* event once the model returns. Middleware sees the assembled response either way.
|
|
164
164
|
*/
|
|
165
165
|
streamTokens?: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Tools that depend on the run, added to `tools` on every model call and run like them: an editor's
|
|
168
|
+
* file system for one session, or the MCP servers one user connected. A tool in `tools` wins over a
|
|
169
|
+
* source's tool of the same name.
|
|
170
|
+
*/
|
|
171
|
+
toolSource?: (context: ToolSourceContext) => ToolDefinition[] | Promise<ToolDefinition[]>;
|
|
172
|
+
}
|
|
173
|
+
/** What a tool source is told about the run it supplies tools for. */
|
|
174
|
+
export interface ToolSourceContext {
|
|
175
|
+
/** The run's thread. */
|
|
176
|
+
threadId: string;
|
|
177
|
+
/** The tenant the run is for. */
|
|
178
|
+
tenantId?: string;
|
|
179
|
+
/** Who the run is for. */
|
|
180
|
+
principal?: Readonly<Principal>;
|
|
181
|
+
/** Aborted when the run is cancelled. */
|
|
182
|
+
signal: AbortSignal;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* A tool the client runs, such as one an AG-UI frontend offers: a name, a description, and a schema,
|
|
186
|
+
* with no implementation on the agent's side. Given in the input as `clientTools`, it is offered to
|
|
187
|
+
* the model like any tool. When the model calls one, the agent ends its turn with `stopReason:
|
|
188
|
+
* 'client_tools'`, and the client sends the result back in the next run's messages.
|
|
189
|
+
*/
|
|
190
|
+
export interface ClientToolDefinition {
|
|
191
|
+
/** The name the model calls it by. */
|
|
192
|
+
name: string;
|
|
193
|
+
/** What it does, written for the model. */
|
|
194
|
+
description: string;
|
|
195
|
+
/** JSON Schema for the arguments. */
|
|
196
|
+
parameters: Record<string, unknown>;
|
|
166
197
|
}
|
|
167
198
|
declare const agentChannels: () => {
|
|
168
199
|
messages: import("../types/graph.js").Channel<Message[]>;
|
|
169
200
|
iterations: import("../types/graph.js").Channel<number>;
|
|
170
201
|
answer: import("../types/graph.js").Channel<string>;
|
|
171
202
|
stopReason: import("../types/graph.js").Channel<AgentStopReason | undefined>;
|
|
203
|
+
clientTools: import("../types/graph.js").Channel<ClientToolDefinition[] | undefined>;
|
|
172
204
|
};
|
|
173
205
|
/** The agent's state channels, as a graph schema. */
|
|
174
206
|
export type AgentChannels = ReturnType<typeof agentChannels>;
|
|
@@ -182,12 +214,15 @@ export type AgentState = {
|
|
|
182
214
|
answer: string;
|
|
183
215
|
/** Why the agent stopped. */
|
|
184
216
|
stopReason?: AgentStopReason;
|
|
217
|
+
/** Tools the client runs, given in the input; see `ClientToolDefinition`. */
|
|
218
|
+
clientTools?: ClientToolDefinition[];
|
|
185
219
|
};
|
|
186
220
|
/**
|
|
187
221
|
* `completed` when the model answered, `max_iterations` when it ran out of model calls, `stopped`
|
|
188
|
-
* when a middleware ended the run through `context.stop()
|
|
222
|
+
* when a middleware ended the run through `context.stop()`, and `client_tools` when the model called
|
|
223
|
+
* a tool the client runs, which waits for the client's result.
|
|
189
224
|
*/
|
|
190
|
-
export type AgentStopReason = 'completed' | 'max_iterations' | 'stopped';
|
|
225
|
+
export type AgentStopReason = 'completed' | 'max_iterations' | 'stopped' | 'client_tools';
|
|
191
226
|
/** An agent: a compiled graph over the agent's channels. */
|
|
192
227
|
export type AgentGraph = CompiledGraph<AgentChannels>;
|
|
193
228
|
/** Wraps a question into the state an agent starts from. */
|
|
@@ -1,13 +1,15 @@
|
|
|
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(),
|
|
9
10
|
answer: lastValue(''),
|
|
10
11
|
stopReason: lastValue(undefined),
|
|
12
|
+
clientTools: lastValue(undefined),
|
|
11
13
|
});
|
|
12
14
|
export function agentInput(goal) {
|
|
13
15
|
return { messages: [{ role: 'user', content: goal }] };
|
|
@@ -17,19 +19,42 @@ export function createAgent(options) {
|
|
|
17
19
|
throw new TypeError('createAgent needs a client with a complete() method');
|
|
18
20
|
}
|
|
19
21
|
const maxIterations = Math.max(1, options.maxIterations ?? 8);
|
|
20
|
-
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 ?? []));
|
|
21
29
|
const middleware = options.middleware ?? [];
|
|
22
30
|
const approvals = options.interruptOn ?? {};
|
|
31
|
+
const toolsFor = async (context) => {
|
|
32
|
+
if (!options.toolSource)
|
|
33
|
+
return executor;
|
|
34
|
+
const supplied = await options.toolSource({
|
|
35
|
+
threadId: context.threadId,
|
|
36
|
+
...(context.tenantId !== undefined ? { tenantId: context.tenantId } : {}),
|
|
37
|
+
...(context.principal ? { principal: context.principal } : {}),
|
|
38
|
+
signal: context.signal,
|
|
39
|
+
});
|
|
40
|
+
return supplied.length ? searchable(new ToolExecutor([...supplied, ...executor.list()])) : executor;
|
|
41
|
+
};
|
|
23
42
|
const graph = createGraph({ channels: agentChannels() })
|
|
24
43
|
.addNode('model', async (context) => {
|
|
25
44
|
const state = context.state;
|
|
45
|
+
const runTools = await toolsFor(context);
|
|
46
|
+
const clientTools = clientToolsOf(state, runTools);
|
|
47
|
+
if (clientTools.length > 0 && waitingOnClient(state.messages, clientTools)) {
|
|
48
|
+
return new Command({ update: { stopReason: 'client_tools' }, goto: END });
|
|
49
|
+
}
|
|
50
|
+
const offered = [...runTools.list(), ...clientTools];
|
|
26
51
|
let request = {
|
|
27
52
|
model: options.model ?? 'auto',
|
|
28
53
|
messages: [
|
|
29
54
|
...(options.systemPrompt ? [{ role: 'system', content: options.systemPrompt }] : []),
|
|
30
55
|
...state.messages,
|
|
31
56
|
],
|
|
32
|
-
...(
|
|
57
|
+
...(offered.length ? { tools: offered } : {}),
|
|
33
58
|
...(options.temperature === undefined ? {} : { temperature: options.temperature }),
|
|
34
59
|
...(options.maxTokens === undefined ? {} : { maxTokens: options.maxTokens }),
|
|
35
60
|
...(options.responseFormat ? { responseFormat: options.responseFormat } : {}),
|
|
@@ -88,9 +113,28 @@ export function createAgent(options) {
|
|
|
88
113
|
goto: END,
|
|
89
114
|
});
|
|
90
115
|
}
|
|
116
|
+
const forClient = new Set(clientTools.map((item) => item.name));
|
|
117
|
+
const ours = calls.filter((call) => !forClient.has(call.function.name));
|
|
118
|
+
for (const call of calls) {
|
|
119
|
+
if (!forClient.has(call.function.name))
|
|
120
|
+
continue;
|
|
121
|
+
const parsed = parseArguments(call.function.arguments);
|
|
122
|
+
context.tool({
|
|
123
|
+
phase: 'start',
|
|
124
|
+
id: call.id,
|
|
125
|
+
name: call.function.name,
|
|
126
|
+
args: parsed.ok ? parsed.args : call.function.arguments,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (ours.length === 0) {
|
|
130
|
+
return new Command({
|
|
131
|
+
update: { messages: [assistant], iterations: 1, stopReason: 'client_tools' },
|
|
132
|
+
goto: END,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
91
135
|
return new Command({
|
|
92
136
|
update: { messages: [assistant], iterations: 1 },
|
|
93
|
-
goto:
|
|
137
|
+
goto: ours.map((call) => new Send('tools', call)),
|
|
94
138
|
});
|
|
95
139
|
}, { ends: ['tools', END] })
|
|
96
140
|
.addNode('tools', async (context) => {
|
|
@@ -100,7 +144,8 @@ export function createAgent(options) {
|
|
|
100
144
|
return { messages: [toolMessage(call, { ok: false, error: parsed.error })] };
|
|
101
145
|
}
|
|
102
146
|
let args = parsed.args;
|
|
103
|
-
const
|
|
147
|
+
const runTools = await toolsFor(context);
|
|
148
|
+
const definition = runTools.get(call.function.name);
|
|
104
149
|
const capabilities = capabilitiesOf(definition, args);
|
|
105
150
|
const description = {
|
|
106
151
|
id: call.id,
|
|
@@ -149,6 +194,7 @@ export function createAgent(options) {
|
|
|
149
194
|
}
|
|
150
195
|
}
|
|
151
196
|
}
|
|
197
|
+
const forward = forwardedEvents(context);
|
|
152
198
|
const run = async (current) => {
|
|
153
199
|
if (options.permissions && current.args !== args) {
|
|
154
200
|
const recheck = await options.permissions.decide({
|
|
@@ -159,13 +205,19 @@ export function createAgent(options) {
|
|
|
159
205
|
if (recheck.decision === 'deny')
|
|
160
206
|
return { ok: false, error: `Permission denied: ${recheck.reason}` };
|
|
161
207
|
}
|
|
162
|
-
return
|
|
208
|
+
return runTools.execute(description.name, current.args, {
|
|
163
209
|
toolCallId: call.id,
|
|
164
210
|
threadId: context.threadId,
|
|
165
211
|
...(context.tenantId !== undefined ? { tenantId: context.tenantId } : {}),
|
|
166
212
|
...(context.principal ? { principal: context.principal } : {}),
|
|
167
213
|
signal: context.signal,
|
|
168
214
|
...(context.store ? { store: context.store } : {}),
|
|
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
|
+
: {}),
|
|
169
221
|
});
|
|
170
222
|
};
|
|
171
223
|
context.tool({ phase: 'start', id: call.id, name: description.name, args });
|
|
@@ -250,6 +302,25 @@ function parseArguments(raw) {
|
|
|
250
302
|
return { ok: false, error: `Tool arguments are not valid JSON (${reason}); the tool was not run` };
|
|
251
303
|
}
|
|
252
304
|
}
|
|
305
|
+
function clientToolsOf(state, runTools) {
|
|
306
|
+
return (state.clientTools ?? []).filter((item) => typeof item?.name === 'string' && item.name !== '' && !runTools.has(item.name));
|
|
307
|
+
}
|
|
308
|
+
function waitingOnClient(messages, clientTools) {
|
|
309
|
+
const names = new Set(clientTools.map((item) => item.name));
|
|
310
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
311
|
+
const message = messages[index];
|
|
312
|
+
if (message.role === 'user')
|
|
313
|
+
return false;
|
|
314
|
+
if (message.role !== 'assistant' || !message.toolCalls?.length)
|
|
315
|
+
continue;
|
|
316
|
+
const answered = new Set(messages
|
|
317
|
+
.slice(index + 1)
|
|
318
|
+
.filter((item) => item.role === 'tool')
|
|
319
|
+
.map((item) => item.toolCallId));
|
|
320
|
+
return message.toolCalls.some((call) => names.has(call.function.name) && !answered.has(call.id));
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
253
324
|
function hookContext(context, state, store) {
|
|
254
325
|
return {
|
|
255
326
|
state,
|
|
@@ -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>>;
|