@robota-sdk/agent-framework 3.0.0-beta.100
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/CHANGELOG.md +2924 -0
- package/LICENSE +661 -0
- package/README.md +482 -0
- package/dist/node/createInteractiveRuntime-20Gv8UI1.d.cts +10425 -0
- package/dist/node/createInteractiveRuntime-20Gv8UI1.d.cts.map +1 -0
- package/dist/node/createInteractiveRuntime-vsCT07qt.d.ts +10425 -0
- package/dist/node/createInteractiveRuntime-vsCT07qt.d.ts.map +1 -0
- package/dist/node/index.cjs +31 -0
- package/dist/node/index.d.cts +2192 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +2192 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +32 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/interactive-CyPYYFZe.cjs +130 -0
- package/dist/node/interactive-JZVRVsIt.js +131 -0
- package/dist/node/interactive-JZVRVsIt.js.map +1 -0
- package/dist/node/testing/index.cjs +2 -0
- package/dist/node/testing/index.d.cts +240 -0
- package/dist/node/testing/index.d.cts.map +1 -0
- package/dist/node/testing/index.d.ts +240 -0
- package/dist/node/testing/index.d.ts.map +1 -0
- package/dist/node/testing/index.js +3 -0
- package/dist/node/testing/index.js.map +1 -0
- package/package.json +144 -0
package/README.md
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
# @robota-sdk/agent-framework
|
|
2
|
+
|
|
3
|
+
The assembly layer of the Robota SDK. It composes `@robota-sdk/agent-core`,
|
|
4
|
+
`@robota-sdk/agent-session`, `@robota-sdk/agent-tools`, `@robota-sdk/agent-tool-defaults` and
|
|
5
|
+
`@robota-sdk/agent-executor` into one provider-neutral SDK for building agents: sessions with
|
|
6
|
+
tools, permissions, hooks, streaming, context loading, commands, subagents and persistence.
|
|
7
|
+
|
|
8
|
+
It has three main entry points:
|
|
9
|
+
|
|
10
|
+
- **`InteractiveSession`** — an event-driven session that any client (terminal UI, web app, API
|
|
11
|
+
server, worker) drives by submitting prompts and listening to events.
|
|
12
|
+
- **`createQuery()`** — a one-shot function: prompt in, response text out.
|
|
13
|
+
- **`createAgentRuntime()`** — a composition root that creates many `InteractiveSession`s sharing
|
|
14
|
+
one configuration, for headless and multi-session hosts.
|
|
15
|
+
|
|
16
|
+
The package has no React dependency, and it never imports a concrete provider: you create the
|
|
17
|
+
provider (for example from `@robota-sdk/agent-provider-anthropic`) and pass it in. The configured CLI
|
|
18
|
+
(`@robota-sdk/agent-cli`) is a maintained agent interface built on this package.
|
|
19
|
+
|
|
20
|
+
For walkthroughs, see the [SDK guide](../../content/guide/sdk.md) and
|
|
21
|
+
[Building agents](../../content/guide/building-agents.md).
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @robota-sdk/agent-framework @robota-sdk/agent-provider-anthropic
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Requires Node.js 22.12 or later. `InteractiveSession` and `createQuery` work with any provider
|
|
30
|
+
package given a `model`; without one they use the settings files, or Anthropic's default model.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createQuery } from '@robota-sdk/agent-framework';
|
|
36
|
+
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
37
|
+
|
|
38
|
+
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
39
|
+
const query = createQuery({ provider });
|
|
40
|
+
|
|
41
|
+
const response = await query('List the TypeScript files in this directory');
|
|
42
|
+
|
|
43
|
+
// With options
|
|
44
|
+
const verboseQuery = createQuery({
|
|
45
|
+
provider,
|
|
46
|
+
cwd: '/path/to/project',
|
|
47
|
+
permissionMode: 'acceptEdits',
|
|
48
|
+
maxTurns: 10,
|
|
49
|
+
onTextDelta: (delta) => process.stdout.write(delta),
|
|
50
|
+
});
|
|
51
|
+
const analysis = await verboseQuery('Analyze the code');
|
|
52
|
+
console.log(response, analysis);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`createQuery()` uses the `default` permission mode. With no `permissionHandler`, a tool call that
|
|
56
|
+
would ask for approval is denied, so an unattended run only does what the mode allows without asking.
|
|
57
|
+
List the tools that may run anyway in `allowedTools` (your own `additionalTools`, say), pass a
|
|
58
|
+
`permissionHandler` to decide each request, or pass `permissionMode: 'bypassPermissions'` explicitly
|
|
59
|
+
if the run should approve everything. `deniedTools` are never offered to the model.
|
|
60
|
+
|
|
61
|
+
Pass `model` with any provider other than Anthropic: without it the model comes from the settings
|
|
62
|
+
files, and without one the session asks the provider for `claude-opus-4-5`. Calls run one at a time,
|
|
63
|
+
each answered by its own turn, and `query.shutdown()` ends the session.
|
|
64
|
+
|
|
65
|
+
Without `projectAccess`, a query runs **Restricted**: it can use its tools in `cwd`, but it loads
|
|
66
|
+
no project context, settings, memory or session files. See [Project access](#project-access).
|
|
67
|
+
|
|
68
|
+
## InteractiveSession
|
|
69
|
+
|
|
70
|
+
`InteractiveSession` wraps an `agent-session` `Session` and turns it into an event stream. It keeps
|
|
71
|
+
the prompt queue, streaming text, running-tool state, abort handling and message history, so a
|
|
72
|
+
client only renders events and forwards user input.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
76
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
77
|
+
|
|
78
|
+
declare const provider: IAIProvider;
|
|
79
|
+
|
|
80
|
+
const session = new InteractiveSession({
|
|
81
|
+
cwd: process.cwd(),
|
|
82
|
+
provider,
|
|
83
|
+
permissionMode: 'default',
|
|
84
|
+
maxTurns: 10,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
session.on('text_delta', (delta) => process.stdout.write(delta));
|
|
88
|
+
session.on('tool_start', (state) => console.log(`Running: ${state.toolName}`));
|
|
89
|
+
session.on('tool_end', (state) => console.log(`Done: ${state.toolName} (${state.result})`));
|
|
90
|
+
session.on('permission_request', ({ id, toolName }) => {
|
|
91
|
+
// Ask the user, then answer. The first answer wins, from any attached surface.
|
|
92
|
+
session.resolvePermission(id, toolName === 'Read');
|
|
93
|
+
});
|
|
94
|
+
session.on('complete', (result) => console.log('\n', result.response));
|
|
95
|
+
session.on('error', (error) => console.error(error));
|
|
96
|
+
|
|
97
|
+
// submit() resolves once the prompt is accepted; the handle identifies this turn
|
|
98
|
+
// even if it waits in the queue behind another one.
|
|
99
|
+
const handle = await session.submit('Explain the code in src/');
|
|
100
|
+
const result = await handle.completed;
|
|
101
|
+
console.log(result.toolSummaries.length, 'tool calls');
|
|
102
|
+
|
|
103
|
+
await session.shutdown();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Common options
|
|
107
|
+
|
|
108
|
+
| Option | Purpose |
|
|
109
|
+
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
110
|
+
| `cwd`, `provider` | Required. The working directory and the provider instance. |
|
|
111
|
+
| `projectAccess` | The host's trust decision for the project. Absent means Restricted. |
|
|
112
|
+
| `permissionMode` | `plan`, `default`, `acceptEdits`, `bypassPermissions` or `auto`. |
|
|
113
|
+
| `maxTurns` | Cap on agentic rounds per prompt. |
|
|
114
|
+
| `model`, `effort`, `temperature` | Model selection for this session. |
|
|
115
|
+
| `systemPrompt`, `appendSystemPrompt` | Replace or extend the assembled system prompt. |
|
|
116
|
+
| `additionalTools` | Tools added to the default set. |
|
|
117
|
+
| `defaultTools` | Replaces the default tool set from `@robota-sdk/agent-tool-defaults`; `[]` removes it. |
|
|
118
|
+
| `allowedTools`, `deniedTools` | Tool name patterns to allow or deny for this session. |
|
|
119
|
+
| `commandModules` | Command modules the session can execute (for example from `@robota-sdk/agent-command`). |
|
|
120
|
+
| `sessionStore`, `resumeSessionId`, `forkSession` | Persistence, and resuming or forking a stored session. |
|
|
121
|
+
| `userSettingsSources`, `projectSettingsPaths` | Settings files to read. Absent means no settings file is read. |
|
|
122
|
+
| `contributionSources`, `skillRoots`, `agentDefinitionRoots` | Where to discover skills and agent definitions. Absent means no discovery. |
|
|
123
|
+
| `sandboxClient`, `workspaceManifest` | Run tools in a sandbox, prepared from a manifest (see [Sandbox execution](#sandbox-execution)). |
|
|
124
|
+
| `guardrails` | Named guardrail functions for `guardrail` hooks. |
|
|
125
|
+
| `responseFormat` | Request structured output. |
|
|
126
|
+
|
|
127
|
+
Every location the framework reads or writes (settings files, skill and agent roots, plugin
|
|
128
|
+
directories, storage roots) is supplied by the host. The framework never falls back to a default
|
|
129
|
+
path; an omitted location means that feature is off.
|
|
130
|
+
|
|
131
|
+
### Events
|
|
132
|
+
|
|
133
|
+
| Event | Payload |
|
|
134
|
+
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
135
|
+
| `text_delta` | A streamed text chunk |
|
|
136
|
+
| `thinking` | `true` when a turn starts, `false` when it ends |
|
|
137
|
+
| `thinking_delta` | A chunk of reasoning text a provider streamed; never part of the reply |
|
|
138
|
+
| `tool_start`, `tool_end` | `IToolState` (`toolName`, `firstArg`, `isRunning`, `result`, …) |
|
|
139
|
+
| `complete`, `interrupted` | `IExecutionResult` (`response`, `history`, `toolSummaries`, `contextState`, `usage`) |
|
|
140
|
+
| `error` | `Error` |
|
|
141
|
+
| `context_update` | `IContextWindowState` (token usage) |
|
|
142
|
+
| `compact` | Compaction trigger and before/after context state |
|
|
143
|
+
| `permission_request`, `ask_request`, `prompt_resolved` | A pending approval or question, and its settlement |
|
|
144
|
+
| `background_task_event`, `background_job_group_event` | Subagent and background task lifecycle |
|
|
145
|
+
| `goal_event`, `plan_event`, `branch_event`, `memory_event` | Goal, plan, checkpoint-branch and memory changes |
|
|
146
|
+
| `session_renamed`, `history_cleared`, `status_changed` | Session state changes |
|
|
147
|
+
|
|
148
|
+
### Main methods
|
|
149
|
+
|
|
150
|
+
- `submit(input, displayInput?, rawInput?, options?)` returns a turn handle (`{ turnId, completed }`).
|
|
151
|
+
`completed` rejects with an error that `isTurnNotRunError()` (from
|
|
152
|
+
`@robota-sdk/agent-interface-session`) recognizes if the queued prompt never ran: replaced by a
|
|
153
|
+
later prompt, dropped from a full queue, or cleared by abort, cancel or shutdown.
|
|
154
|
+
- `abort()` stops the running turn and clears the queue; `cancelQueue()` drops queued prompts only.
|
|
155
|
+
- `executeCommand(name, args)` runs a command; `listCommands()` lists them.
|
|
156
|
+
- `resolvePermission(id, result)` and `resolveAsk(id, response)` answer pending requests.
|
|
157
|
+
- `getMessages()`, `getContextState()`, `getStreamingText()`, `getActiveTools()`, `isExecuting()`.
|
|
158
|
+
- `compactContext(instructions?)` summarizes the conversation to free context space.
|
|
159
|
+
- `setGoal(objective)` pursues an objective over several turns; `setPlan(objective)` and
|
|
160
|
+
`approvePlan()` drive explicit plan mode.
|
|
161
|
+
- `getName()` / `setName(name)`, `getSession()` (the underlying `Session`), `shutdown()`.
|
|
162
|
+
|
|
163
|
+
In a trusted project, path-like `@file` references in a prompt are read (bounded in size and
|
|
164
|
+
recursion), added to the model's input, and recorded in history as a structured event.
|
|
165
|
+
|
|
166
|
+
## Project access
|
|
167
|
+
|
|
168
|
+
A `cwd` alone gives the session no access to project content. Project context files
|
|
169
|
+
(`AGENTS.md`, `CLAUDE.md`), project settings, memory, checkpoints and stored sessions are reachable
|
|
170
|
+
only through a trusted `TWorkspaceProjectAccess` decision that the host obtains from a
|
|
171
|
+
`WorkspaceTrustService`. Without one the session is Restricted.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { homedir } from 'node:os';
|
|
175
|
+
import { join } from 'node:path';
|
|
176
|
+
import {
|
|
177
|
+
InteractiveSession,
|
|
178
|
+
createNodeWorkspaceTrustService,
|
|
179
|
+
createProjectSessionStore,
|
|
180
|
+
getWorkspaceProjectStateStorage,
|
|
181
|
+
} from '@robota-sdk/agent-framework';
|
|
182
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
183
|
+
|
|
184
|
+
declare const provider: IAIProvider;
|
|
185
|
+
const cwd = process.cwd();
|
|
186
|
+
|
|
187
|
+
// The host chooses where trust grants are stored and where project state lives
|
|
188
|
+
// (directories relative to the project root).
|
|
189
|
+
const trust = createNodeWorkspaceTrustService(join(homedir(), '.my-app', 'workspace-trust.json'), {
|
|
190
|
+
sessions: '.my-app/sessions',
|
|
191
|
+
'session-logs': '.my-app/logs',
|
|
192
|
+
memory: '.my-app/memory',
|
|
193
|
+
checkpoints: '.my-app/checkpoints',
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// inspect() reports the current decision; grant() trusts the project after the user agrees.
|
|
197
|
+
const projectAccess = await trust.inspect(cwd);
|
|
198
|
+
|
|
199
|
+
const sessionStore =
|
|
200
|
+
projectAccess.status === 'trusted'
|
|
201
|
+
? createProjectSessionStore(
|
|
202
|
+
getWorkspaceProjectStateStorage(projectAccess.authority, 'sessions'),
|
|
203
|
+
getWorkspaceProjectStateStorage(projectAccess.authority, 'session-logs'),
|
|
204
|
+
)
|
|
205
|
+
: undefined;
|
|
206
|
+
|
|
207
|
+
const session = new InteractiveSession({ cwd, provider, projectAccess, sessionStore });
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Trust is bound to the Git worktree and repository identity. Non-Git paths, a replaced repository,
|
|
211
|
+
a revoked grant or a trust-store error all resolve to Restricted, and a revoked authority stops
|
|
212
|
+
working immediately. The `cwd` must be the trusted root or inside it.
|
|
213
|
+
|
|
214
|
+
## Headless and multi-session runtime
|
|
215
|
+
|
|
216
|
+
`createAgentRuntime()` holds one configuration (provider, `cwd`, project access, command modules,
|
|
217
|
+
session store, transports) and creates `InteractiveSession`s from it:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { createAgentRuntime } from '@robota-sdk/agent-framework';
|
|
221
|
+
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
222
|
+
|
|
223
|
+
const runtime = createAgentRuntime({
|
|
224
|
+
cwd: process.cwd(),
|
|
225
|
+
provider: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Each call creates a new InteractiveSession that inherits the runtime configuration.
|
|
229
|
+
const session = runtime.createSession({ permissionMode: 'plan' });
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`buildRuntimeSession()` is the one place a session is built from resolved options, and
|
|
233
|
+
`startRuntimeHost()` adds transport start/stop and a bounded shutdown on top of it; the CLI's
|
|
234
|
+
`<cli-name> --serve` uses both. `SessionPool` keeps several live sessions for a runtime that serves more
|
|
235
|
+
than one client. Headless output is available through `createHeadlessRunner` and
|
|
236
|
+
`createHeadlessTransport`, and `createProgrammaticAgent` drives a session from code.
|
|
237
|
+
|
|
238
|
+
## Permissions
|
|
239
|
+
|
|
240
|
+
Every tool call is decided by `evaluatePermission` from `@robota-sdk/agent-core`: deny rules first,
|
|
241
|
+
then ask rules and the protections that are never auto-approved (such as deleting the workspace or
|
|
242
|
+
writing into `.git`), then allow rules (skipped in `bypassPermissions`, which approves everything
|
|
243
|
+
that got this far), then what the mode says about the kind of action:
|
|
244
|
+
|
|
245
|
+
| Mode | Read-only tools (Read, Glob, Grep) | Edits (Write, Edit) | Commands (Shell, Bash) |
|
|
246
|
+
| ------------------- | :--------------------------------: | :-----------------: | :--------------------: |
|
|
247
|
+
| `plan` | auto | deny | deny |
|
|
248
|
+
| `default` | auto | ask | ask |
|
|
249
|
+
| `acceptEdits` | auto | auto | ask |
|
|
250
|
+
| `bypassPermissions` | auto | auto | auto |
|
|
251
|
+
| `auto` | auto | auto | model classifier |
|
|
252
|
+
|
|
253
|
+
A shell command that only reads (such as `ls`, `cat` or `git status`) with every path inside the
|
|
254
|
+
workspace counts as read-only. In `auto` mode a model classifier approves or blocks what the mode
|
|
255
|
+
leaves open, and after repeated
|
|
256
|
+
refusals the session falls back to asking a person. `ask` rules ask even in `bypassPermissions`; in
|
|
257
|
+
`plan` mode anything that would change something is refused instead of asked. See the
|
|
258
|
+
[permissions and hooks guide](../../content/guide/permissions-and-hooks.md).
|
|
259
|
+
|
|
260
|
+
## Hooks
|
|
261
|
+
|
|
262
|
+
Sessions run the hooks configured in settings (or passed by the host) at lifecycle events such as
|
|
263
|
+
`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SessionStart`, `SessionEnd`,
|
|
264
|
+
`PreCompact`, `SubagentStart` and `PreModelCall`. Only `PreToolUse` can block. The full list, input
|
|
265
|
+
fields and blocking rules are in the [hook event catalog](../agent-core/docs/HOOK-CATALOG.md).
|
|
266
|
+
|
|
267
|
+
This package adds two hook executors to the `command` and `http` executors from `agent-core`:
|
|
268
|
+
|
|
269
|
+
| Executor | Hook type | What it does |
|
|
270
|
+
| ---------------- | --------- | --------------------------------------------------------------------------------- |
|
|
271
|
+
| `PromptExecutor` | `prompt` | Asks a model (from a provider factory you supply) for an `{ ok, reason }` verdict |
|
|
272
|
+
| `AgentExecutor` | `agent` | Runs a subagent session (from a session factory you supply) for a verdict |
|
|
273
|
+
|
|
274
|
+
## Commands
|
|
275
|
+
|
|
276
|
+
The framework owns the command infrastructure: `SystemCommandExecutor`, `CommandRegistry`, command
|
|
277
|
+
sources (`BuiltinCommandSource`, `SkillCommandSource`, `PluginCommandSource`) and the common APIs
|
|
278
|
+
command modules use. It does not own the user-visible built-in commands; those are command modules
|
|
279
|
+
from `@robota-sdk/agent-command` that the host composes. Command names are slash-free (`help`,
|
|
280
|
+
`compact`); a UI renders them as `/help`, `/compact`.
|
|
281
|
+
|
|
282
|
+
A command module is a plain object:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
286
|
+
import type { ICommandModule } from '@robota-sdk/agent-framework';
|
|
287
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
288
|
+
|
|
289
|
+
declare const provider: IAIProvider;
|
|
290
|
+
|
|
291
|
+
const statusModule: ICommandModule = {
|
|
292
|
+
name: 'status',
|
|
293
|
+
systemCommands: [
|
|
294
|
+
{
|
|
295
|
+
name: 'status',
|
|
296
|
+
description: 'Show agent status',
|
|
297
|
+
execute: (context, args) => ({ message: `OK ${args}`, success: true }),
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const session = new InteractiveSession({
|
|
303
|
+
cwd: process.cwd(),
|
|
304
|
+
provider,
|
|
305
|
+
commandModules: [statusModule],
|
|
306
|
+
});
|
|
307
|
+
const result = await session.executeCommand('status', '');
|
|
308
|
+
console.log(result?.message);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Skills are discovered only from the contribution sources and roots the host passes. A model can run
|
|
312
|
+
a model-invocable command through a projected tool named `command_<name>` by default;
|
|
313
|
+
`modelCommandToolPrefix` changes the prefix.
|
|
314
|
+
|
|
315
|
+
## Subagents
|
|
316
|
+
|
|
317
|
+
- `IAgentDefinition` describes a reusable agent: `name`, `description`, `systemPrompt`, and
|
|
318
|
+
optionally `model`, `effort`, `role`, `maxTurns`, `tools` and `disallowedTools`.
|
|
319
|
+
- `BUILT_IN_AGENTS` holds `general-purpose`, `Explore` and `Plan`; `Explore` and `Plan` cannot use
|
|
320
|
+
`Write` or `Edit`.
|
|
321
|
+
- Custom definitions come from `agentDefinitions` or from the host-supplied `agentDefinitionRoots`.
|
|
322
|
+
The CLI chooses which directories those are.
|
|
323
|
+
- `createAgentTool()` gives the model an `Agent` tool that starts subagents, including several in
|
|
324
|
+
parallel through its `jobs` argument. Subagents run as background tasks managed by
|
|
325
|
+
`@robota-sdk/agent-executor`; `createSubagentSession()` builds the child session from the
|
|
326
|
+
parent's resolved configuration.
|
|
327
|
+
- `runSequential`, `runParallel`, `runHandoff`, `runHierarchical` and `runGroupChat` run multi-agent
|
|
328
|
+
orchestration patterns over the contracts in `agent-core`. `runGroupChat` is a facade over
|
|
329
|
+
`@robota-sdk/agent-roundtable`'s `Roundtable`: the core runs the only turn loop, one participant per
|
|
330
|
+
step id, while the neutral contract (`IGroupChatOrchestrationSpec`, `SelectNextStep`) is unchanged.
|
|
331
|
+
|
|
332
|
+
## Sandbox execution
|
|
333
|
+
|
|
334
|
+
A session can run its commands and file tools in a sandbox through a provider-neutral
|
|
335
|
+
`ISandboxClient` from `@robota-sdk/agent-tools`. `E2BSandboxClient` adapts an E2B sandbox that your
|
|
336
|
+
application creates; `@robota-sdk/agent-framework` does not depend on E2B.
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
340
|
+
import { E2BSandboxClient } from '@robota-sdk/agent-tools';
|
|
341
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
342
|
+
import type { IE2BSandboxAdapter, IWorkspaceManifest } from '@robota-sdk/agent-tools';
|
|
343
|
+
|
|
344
|
+
declare const provider: IAIProvider;
|
|
345
|
+
declare const sandbox: IE2BSandboxAdapter; // e.g. `await Sandbox.create()` from the `e2b` package
|
|
346
|
+
|
|
347
|
+
const workspaceManifest: IWorkspaceManifest = {
|
|
348
|
+
entries: {
|
|
349
|
+
'task.md': { type: 'file', content: 'Review this repository.\n' },
|
|
350
|
+
repo: { type: 'gitRepo', url: 'https://github.com/example/project.git', ref: 'main' },
|
|
351
|
+
output: { type: 'dir' },
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const session = new InteractiveSession({
|
|
356
|
+
cwd: process.cwd(),
|
|
357
|
+
provider,
|
|
358
|
+
sandboxClient: new E2BSandboxClient({ sandbox }),
|
|
359
|
+
workspaceManifest,
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The manifest is applied once before the session starts. With a `sessionStore` and a sandbox client
|
|
364
|
+
that supports snapshots, `shutdown()` stores the sandbox snapshot ID, and resuming the session
|
|
365
|
+
(without forking) restores the sandbox before the saved messages are replayed.
|
|
366
|
+
|
|
367
|
+
## More capabilities
|
|
368
|
+
|
|
369
|
+
| Capability | Exports |
|
|
370
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
371
|
+
| Model fallback | `FallbackProvider`, `resolveModelFallbackChain`, `applyModelFallback` |
|
|
372
|
+
| Advisor (second-model tool) | `AdvisorController`, `createAdvisorTool` |
|
|
373
|
+
| Edit checkpoints and rewind | `EditCheckpointStore`, `wrapEditCheckpointTools` |
|
|
374
|
+
| Reversible execution | `evaluateReversibleToolSafety`, `wrapReversibleExecutionTools` (session option `reversibleExecution`) |
|
|
375
|
+
| Project and user memory | `ProjectMemoryStore`, `WorkspaceMemoryStore`, `SemanticMemoryStore`, `listUserLocalMemoryItems` |
|
|
376
|
+
| Evals | `defineEval`, `runEval`, `createSessionRunFn`, matchers such as `exactMatch` and `usedTool` |
|
|
377
|
+
| Bundle plugins | `BundlePluginLoader`, `BundlePluginInstaller`, `MarketplaceClient` |
|
|
378
|
+
| Settings | `readSettings`, `writeSettings`, `inspectSettingsLayers`, `createNodeHostSettingsSource` |
|
|
379
|
+
| Self-hosting verification | `planSelfHostingVerification`, `transitionSelfHostingLoop` |
|
|
380
|
+
|
|
381
|
+
Bundle plugins use the Claude Code plugin layout: a directory with `.claude-plugin/plugin.json`
|
|
382
|
+
(`name` is required; `version` and `description` are optional) that can contribute skills,
|
|
383
|
+
commands, hooks, MCP server configuration and agent definitions. The loader selects the source
|
|
384
|
+
recorded in `installed_plugins.json`, validates its cache location and refuses invalid or missing
|
|
385
|
+
selected sources. Without a registry, a single cached revision is supported; multiple revisions
|
|
386
|
+
require an explicit installation record and are reported as `revision-unselected`. Cache directory
|
|
387
|
+
sorting never selects an execution source. Missing manifest versions remain absent. Enablement
|
|
388
|
+
checks both the installed identity and the manifest name, so renaming a manifest cannot bypass a
|
|
389
|
+
disabled installation. MCP dispatch revalidates this selection; other component lifetimes remain
|
|
390
|
+
owned by their host execution surfaces.
|
|
391
|
+
|
|
392
|
+
The loader also exposes a versioned `IContributionDescriptor` on each loaded bundle. It binds
|
|
393
|
+
installed identity, cache revision and source location to namespaced contributions and required
|
|
394
|
+
host capabilities without importing contributor code or exposing MCP credential values. A
|
|
395
|
+
supported declaration still requires host admission. Agent names without execution definitions
|
|
396
|
+
are unavailable; unconsumed custom component paths and invalid transports carry compatibility
|
|
397
|
+
diagnostics. The descriptor records host ownership of active-call policy; it does not establish
|
|
398
|
+
live retirement behavior for every component kind.
|
|
399
|
+
|
|
400
|
+
The documented MCP declaration subset supports `mcpServers` as a plugin-relative `./` JSON path,
|
|
401
|
+
an inline server map, or an ordered array of either. The default `.mcp.json` is read first; later
|
|
402
|
+
declarations replace whole server entries, with each winning entry retaining its declaring file.
|
|
403
|
+
Paths are checked against the selected installed revision, including symlinks. Unsupported paths
|
|
404
|
+
and malformed declarations produce value-free compatibility codes. Binary MCP bundles are not
|
|
405
|
+
supported by this adapter. See the [foreign manifest reference](https://code.claude.com/docs/en/plugins-reference).
|
|
406
|
+
|
|
407
|
+
CLI and app startup route these definitions through the existing `plugin` MCP source tier, with
|
|
408
|
+
`plugin-name:server-name` identities, ordinary precedence, workspace trust, approval and host stdio
|
|
409
|
+
authority. Inspection never connects. `${CLAUDE_PLUGIN_ROOT}` resolves to the selected plugin root;
|
|
410
|
+
other environment materialization remains owned by the MCP definition layer. Startup honors the
|
|
411
|
+
current disable overlay. Before each MCP call the host rereads enablement and the selected source;
|
|
412
|
+
disable, uninstall, source replacement or changed trust refuses new dispatch. Already dispatched
|
|
413
|
+
calls settle with their real outcomes. Fresh startup reuses durable approvals only when their
|
|
414
|
+
source identity and trust still match; a new revision cannot silently replace an in-flight tool.
|
|
415
|
+
|
|
416
|
+
The documented MCP declaration subset supports `mcpServers` as a plugin-relative `./` JSON path,
|
|
417
|
+
an inline server map, or an ordered array of either. The default `.mcp.json` is read first; later
|
|
418
|
+
declarations replace whole server entries, with each winning entry retaining its declaring file.
|
|
419
|
+
Paths are checked against the selected installed revision, including symlinks. Unsupported paths
|
|
420
|
+
and malformed declarations produce value-free compatibility codes. Binary MCP bundles are not
|
|
421
|
+
supported by this adapter. See the [foreign manifest reference](https://code.claude.com/docs/en/plugins-reference).
|
|
422
|
+
|
|
423
|
+
CLI and app startup route these definitions through the existing `plugin` MCP source tier, with
|
|
424
|
+
`plugin-name:server-name` identities, ordinary precedence, workspace trust, approval and host stdio
|
|
425
|
+
authority. Inspection never connects. `${CLAUDE_PLUGIN_ROOT}` resolves to the selected plugin root;
|
|
426
|
+
other environment materialization remains owned by the MCP definition layer. Startup honors the
|
|
427
|
+
current disable overlay; active-chain update/disable and restored-work revalidation still require
|
|
428
|
+
their separate lifecycle implementation.
|
|
429
|
+
|
|
430
|
+
## Settings
|
|
431
|
+
|
|
432
|
+
Settings are JSON files merged in the order the host gives them, later layers winning.
|
|
433
|
+
`$ENV:NAME` values in provider credentials are replaced with the environment variable. The configured
|
|
434
|
+
CLI reads `$PRODUCT_USER_STATE_DIR/settings.json`, `~/.claude/settings.json`, `$PRODUCT_PROJECT_STATE_DIR/settings.json`,
|
|
435
|
+
`$PRODUCT_PROJECT_STATE_DIR/settings.local.json`, `.claude/settings.json` and `.claude/settings.local.json`, in that
|
|
436
|
+
order; project files are read only in a trusted project.
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
{
|
|
440
|
+
"currentProvider": "anthropic",
|
|
441
|
+
"providers": {
|
|
442
|
+
"anthropic": {
|
|
443
|
+
"type": "anthropic",
|
|
444
|
+
"model": "claude-sonnet-4-6",
|
|
445
|
+
"apiKey": "$ENV:ANTHROPIC_API_KEY"
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
"permissions": {
|
|
449
|
+
"allow": ["Bash(pnpm *)"],
|
|
450
|
+
"deny": ["Bash(rm -rf *)"]
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
`currentProvider` selects the active entry in `providers`. Other keys include `hooks`, `env`,
|
|
456
|
+
`language`, `preset`, `autoCompactThreshold`, `sandbox` and `enabledPlugins`.
|
|
457
|
+
|
|
458
|
+
## Dependencies
|
|
459
|
+
|
|
460
|
+
| Package | Used for |
|
|
461
|
+
| ----------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
462
|
+
| `@robota-sdk/agent-core` | Agent engine, provider contract, permissions, hooks |
|
|
463
|
+
| `@robota-sdk/agent-session` | `Session`, logging and persistence ports |
|
|
464
|
+
| `@robota-sdk/agent-tools` | Tool factories and sandbox clients |
|
|
465
|
+
| `@robota-sdk/agent-tool-defaults` | The default tool set (loaded on first use) |
|
|
466
|
+
| `@robota-sdk/agent-executor` | Background tasks and subagent managers |
|
|
467
|
+
| `@robota-sdk/agent-file-authority` | Bounded, root-relative project file reads |
|
|
468
|
+
| `@robota-sdk/agent-interface-*` (analytics, command, execution, session, session-mobility, transport) | Shared contracts |
|
|
469
|
+
| `yaml`, `zod` | Frontmatter parsing and settings validation |
|
|
470
|
+
|
|
471
|
+
## Documentation
|
|
472
|
+
|
|
473
|
+
- [docs/SPEC.md](./docs/SPEC.md) — package contract, boundaries and design decisions
|
|
474
|
+
- [SDK guide](../../content/guide/sdk.md) and [Building agents](../../content/guide/building-agents.md)
|
|
475
|
+
- [Hook event catalog](../agent-core/docs/HOOK-CATALOG.md)
|
|
476
|
+
|
|
477
|
+
From `packages/agent-framework` in the repository, `pnpm scenario:verify` runs the offline
|
|
478
|
+
examples (no provider credentials needed).
|
|
479
|
+
|
|
480
|
+
## License
|
|
481
|
+
|
|
482
|
+
This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a [commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
|