@robota-sdk/agent-framework 3.0.0-beta.82 → 3.0.0-beta.84
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 +2309 -0
- package/README.md +285 -531
- package/dist/node/{createInteractiveRuntime-Bmh2KYP6.d.ts → createInteractiveRuntime-3MZpPTdw.d.ts} +547 -92
- package/dist/node/createInteractiveRuntime-3MZpPTdw.d.ts.map +1 -0
- package/dist/node/{createInteractiveRuntime-BDuatsdk.d.cts → createInteractiveRuntime-C6lQfeH_.d.cts} +547 -92
- package/dist/node/createInteractiveRuntime-C6lQfeH_.d.cts.map +1 -0
- package/dist/node/index.cjs +17 -17
- package/dist/node/index.d.cts +512 -14
- package/dist/node/index.d.cts.map +1 -1
- package/dist/node/index.d.ts +512 -14
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +24 -24
- package/dist/node/index.js.map +1 -1
- package/dist/node/interactive-CPam6fsH.js +128 -0
- package/dist/node/interactive-CPam6fsH.js.map +1 -0
- package/dist/node/interactive-CeUdJtQB.cjs +127 -0
- package/dist/node/testing/index.cjs +2 -2
- package/dist/node/testing/index.d.cts +1 -1
- package/dist/node/testing/index.d.cts.map +1 -1
- package/dist/node/testing/index.d.ts +1 -1
- package/dist/node/testing/index.d.ts.map +1 -1
- package/dist/node/testing/index.js +2 -2
- package/dist/node/testing/index.js.map +1 -1
- package/package.json +27 -21
- package/dist/node/createInteractiveRuntime-BDuatsdk.d.cts.map +0 -1
- package/dist/node/createInteractiveRuntime-Bmh2KYP6.d.ts.map +0 -1
- package/dist/node/interactive-D5K9ak2P.cjs +0 -122
- package/dist/node/interactive-DnPmdeU0.js +0 -123
- package/dist/node/interactive-DnPmdeU0.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
# @robota-sdk/agent-framework
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`
|
|
6
|
-
|
|
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
7
|
|
|
8
|
-
|
|
8
|
+
It has three main entry points:
|
|
9
9
|
|
|
10
|
-
|
|
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 `robota` CLI
|
|
18
|
+
(`@robota-sdk/agent-cli`) is a reference app 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).
|
|
11
22
|
|
|
12
23
|
## Installation
|
|
13
24
|
|
|
14
25
|
```bash
|
|
15
|
-
npm install @robota-sdk/agent-framework
|
|
16
|
-
# or
|
|
17
|
-
pnpm add @robota-sdk/agent-framework
|
|
26
|
+
npm install @robota-sdk/agent-framework @robota-sdk/agent-provider-anthropic
|
|
18
27
|
```
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
Requires Node.js 22.12 or later. `InteractiveSession` and `createQuery` work with any Robota provider
|
|
30
|
+
package given a `model`; without one they use the settings files, or Anthropic's default model.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
21
33
|
|
|
22
34
|
```typescript
|
|
23
35
|
import { createQuery } from '@robota-sdk/agent-framework';
|
|
@@ -26,165 +38,163 @@ import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
|
26
38
|
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
27
39
|
const query = createQuery({ provider });
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
const response = await query('Show me the file list');
|
|
41
|
+
const response = await query('List the TypeScript files in this directory');
|
|
31
42
|
|
|
32
43
|
// With options
|
|
33
|
-
const
|
|
44
|
+
const verboseQuery = createQuery({
|
|
34
45
|
provider,
|
|
35
46
|
cwd: '/path/to/project',
|
|
36
|
-
// A bare cwd is provenance only. Pass a host-issued TWorkspaceProjectAccess decision
|
|
37
|
-
// when this query may consume project context or settings.
|
|
38
47
|
permissionMode: 'acceptEdits',
|
|
39
48
|
maxTurns: 10,
|
|
40
49
|
onTextDelta: (delta) => process.stdout.write(delta),
|
|
41
50
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Headless / multi-session runtime
|
|
47
|
-
|
|
48
|
-
For headless or multi-session consumers, `createAgentRuntime()` builds a composition root that
|
|
49
|
-
spawns `InteractiveSession`s sharing one configuration (provider, cwd, command modules, session
|
|
50
|
-
store, transports):
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
import { createAgentRuntime } from '@robota-sdk/agent-framework';
|
|
54
|
-
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
55
|
-
|
|
56
|
-
const runtime = createAgentRuntime({
|
|
57
|
-
cwd: process.cwd(),
|
|
58
|
-
provider: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// Each call composes a fresh InteractiveSession that inherits the runtime config.
|
|
62
|
-
const session = runtime.createSession({});
|
|
51
|
+
const analysis = await verboseQuery('Analyze the code');
|
|
52
|
+
console.log(response, analysis);
|
|
63
53
|
```
|
|
64
54
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`sessionStore: undefined` disables persistence. Trusted runtime/query construction accepts the frozen
|
|
71
|
-
workspace root and its real descendants while rejecting a working directory outside that identity; a
|
|
72
|
-
completed revoke makes the previously issued authority and all of its facets unusable. Explicit host
|
|
73
|
-
contribution sources also remain root-bounded and refuse links at every path component.
|
|
74
|
-
|
|
75
|
-
Authority-backed project mutations share one stable-root boundary: on Linux, open root and parent
|
|
76
|
-
directory descriptors prevent parent renames or symlink swaps from redirecting writes, replacements,
|
|
77
|
-
appends, or deletes. Hosts without equivalent stable handle semantics fail closed with
|
|
78
|
-
`WorkspaceAuthorityRequiredError` instead of falling back to pathname mutation.
|
|
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.
|
|
79
60
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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.
|
|
83
64
|
|
|
84
|
-
|
|
85
|
-
|
|
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).
|
|
86
67
|
|
|
87
|
-
|
|
88
|
-
pnpm --filter @robota-sdk/agent-framework scenario:verify
|
|
89
|
-
```
|
|
68
|
+
## InteractiveSession
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
and the headless `robota --serve` entry — builds its `InteractiveSession` from resolved options through
|
|
95
|
-
it, instead of calling `new InteractiveSession` directly. `startRuntimeHost()` adds the transport
|
|
96
|
-
`startAll`/`stopAll` lifecycle plus a bounded shutdown handle on top, and is used by the headless
|
|
97
|
-
`--serve` path (which the desktop GUI spawns as a loopback-WS sidecar). It lives in `agent-framework`,
|
|
98
|
-
not the product shell — it takes already-resolved options and is presentation-neutral (RUNTIME-001).
|
|
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.
|
|
99
73
|
|
|
100
74
|
```typescript
|
|
101
|
-
import {
|
|
102
|
-
import type {
|
|
103
|
-
|
|
104
|
-
// `session` holds the resolved session options; `transportRegistry` is the loopback WS sidecar.
|
|
105
|
-
declare const options: IRuntimeHostOptions;
|
|
106
|
-
const host: IRuntimeHostHandle = await startRuntimeHost(options);
|
|
107
|
-
// host.session — the live runtime session every presentation drives
|
|
108
|
-
// await host.shutdown() — bounded transport teardown + session shutdown
|
|
109
|
-
```
|
|
75
|
+
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
76
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
110
77
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
- **InteractiveSession** — Event-driven session wrapper (composition over Session). Central client-facing API for CLI, web, API server, or any other client
|
|
114
|
-
- **SystemCommandExecutor + ISystemCommand** — SDK-level command execution infrastructure for product-composed command modules
|
|
115
|
-
- **CommandRegistry, BuiltinCommandSource, SkillCommandSource** — Command registry and SDK common discovery APIs. User-visible built-ins are composed through `agent-command` packages.
|
|
116
|
-
- **Model Command Common APIs** — Provider-neutral `/model` helpers that resolve active provider catalogs and optionally invoke provider-owned refresh hooks
|
|
117
|
-
- **Model effort selection** — A typed provider-neutral selection record and command-host adapter keep source and provisional display state consistent across startup, live commands, and headless output; `auto` remains unpinned until the provider adapter reports its outcome
|
|
118
|
-
- **createQuery()** — Provider-bound factory for one-shot AI agent interactions with streaming support
|
|
119
|
-
- **Runtime host (RUNTIME-001)** — `startRuntimeHost()` builds and serves a headless session over a loopback WS (used by `robota --serve` and the desktop GUI sidecar); `buildRuntimeSession()` is the shared session-construction seam every presentation builds its `InteractiveSession` through
|
|
120
|
-
- **Session assembly** — Internal factory wires tools, provider, config, and context for `InteractiveSession`
|
|
121
|
-
- **Built-in Tools** — Shell/Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, AskUserQuestion are assembled for SDK sessions. The set itself lives in `@robota-sdk/agent-tool-defaults` (a composition leaf, ARCH-035) and is loaded lazily; import it from there to compose your own tier, or `@robota-sdk/agent-tools` for individual tools
|
|
122
|
-
- **Sandbox Execution** — Optional `sandboxClient` injection routes Bash and core file tools through a provider-backed execution plane; `workspaceManifest` can prepare a fresh sandbox workspace before session creation
|
|
123
|
-
- **Sandbox Hydration** — Snapshot-capable sandbox clients persist `sandboxSnapshotId` on shutdown and restore it before saved message replay on non-fork resume
|
|
124
|
-
- **Agent Tool** — Sub-agent session creation for multi-agent workflows
|
|
125
|
-
- **Permissions** — one evaluation order for every caller (deny, ask and allow lists, never-auto-approved calls, mode policy) with four modes: `plan`, `default`, `acceptEdits`, `bypassPermissions`
|
|
126
|
-
- **Hooks** — `PreToolUse`, `PostToolUse`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `Stop` events with shell command execution
|
|
127
|
-
- **Streaming** — Real-time text delta callbacks via `onTextDelta`
|
|
128
|
-
- **Context Loading** — AGENTS.md / CLAUDE.md walk-up discovery and system prompt assembly
|
|
129
|
-
- **Prompt File References** — Path-like `@file` prompt references are resolved by the SDK under the session `cwd`, bounded by size/recursion limits, recorded as structured history events, and registered as observed context references
|
|
130
|
-
- **Stable Project Reads** — Project byte and text reads use a bounded retained-root authority on qualified Linux, macOS, and Windows hosts; unsafe link/reparse replacement fails closed while project purpose, liveness, generation, and identity checks remain framework-owned
|
|
131
|
-
- **Context Reference Inventory** — Manual `/context add` references are stored by `InteractiveSession`, included in future prompt model input, and exposed through SDK command common APIs
|
|
132
|
-
- **Config Loading** — 6-file settings merge with provider profiles, legacy provider compatibility, and `$ENV:VAR` substitution for provider credentials
|
|
133
|
-
- **Context Window Management** — Token tracking, configurable auto-compaction (default ~83.5%), manual `session.compact()`
|
|
134
|
-
- **Background Jobs** — Runtime-managed subagent tasks with transcripts and task snapshots
|
|
135
|
-
- **Agent Batch Jobs** — the model-facing Agent tool's `jobs` argument (`Agent` invoked with `{ jobs: [...] }`) starts explicit parallel subagent requests deterministically
|
|
136
|
-
- **Edit Checkpoints** — Checkpoint/rewind support for safer edit workflows
|
|
137
|
-
- **Project Memory** — Command-driven memory capture and retrieval surfaces
|
|
138
|
-
- **Replay Events** — Session execution can forward provider/tool boundary events and provider-native raw payload events into append-only logs
|
|
139
|
-
- **Usage observations** — Each accepted top-level turn records one content-free outcome observation, including the active model and driver surface when known, even when token usage is absent
|
|
140
|
-
- **Bundle Plugin System** — Install and manage reusable extensions packaged as bundle plugins
|
|
141
|
-
|
|
142
|
-
## Architecture
|
|
78
|
+
declare const provider: IAIProvider;
|
|
143
79
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
├── Agent runtime dependencies and background orchestration
|
|
151
|
-
├── Edit checkpoints and command-driven memory
|
|
152
|
-
├── createQuery() ← one-shot entry point factory
|
|
153
|
-
├── createSession() ← internal assembly factory
|
|
154
|
-
└── deps:
|
|
155
|
-
agent-session (Session, neutral session ports, explicit Node host adapters)
|
|
156
|
-
agent-file-authority (stable bounded project byte reads over retained native handles)
|
|
157
|
-
agent-tools (tool infrastructure + 9 built-in tools)
|
|
158
|
-
agent-provider-* (per-vendor AI providers: -anthropic, -openai, -gemini, …)
|
|
159
|
-
agent-core (Robota engine, providers, permissions, hooks)
|
|
160
|
-
|
|
161
|
-
agent-cli (TUI layer — bridges InteractiveSession events to React/Ink state)
|
|
162
|
-
→ agent-framework
|
|
163
|
-
```
|
|
80
|
+
const session = new InteractiveSession({
|
|
81
|
+
cwd: process.cwd(),
|
|
82
|
+
provider,
|
|
83
|
+
permissionMode: 'default',
|
|
84
|
+
maxTurns: 10,
|
|
85
|
+
});
|
|
164
86
|
|
|
165
|
-
|
|
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));
|
|
166
96
|
|
|
167
|
-
|
|
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');
|
|
168
102
|
|
|
169
|
-
|
|
103
|
+
await session.shutdown();
|
|
104
|
+
```
|
|
170
105
|
|
|
171
|
-
|
|
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
|
+
| `tool_start`, `tool_end` | `IToolState` (`toolName`, `firstArg`, `isRunning`, `result`, …) |
|
|
138
|
+
| `complete`, `interrupted` | `IExecutionResult` (`response`, `history`, `toolSummaries`, `contextState`, `usage`) |
|
|
139
|
+
| `error` | `Error` |
|
|
140
|
+
| `context_update` | `IContextWindowState` (token usage) |
|
|
141
|
+
| `compact` | Compaction trigger and before/after context state |
|
|
142
|
+
| `permission_request`, `ask_request`, `prompt_resolved` | A pending approval or question, and its settlement |
|
|
143
|
+
| `background_task_event`, `background_job_group_event` | Subagent and background task lifecycle |
|
|
144
|
+
| `goal_event`, `plan_event`, `branch_event`, `memory_event` | Goal, plan, checkpoint-branch and memory changes |
|
|
145
|
+
| `session_renamed`, `history_cleared`, `status_changed` | Session state changes |
|
|
146
|
+
|
|
147
|
+
### Main methods
|
|
148
|
+
|
|
149
|
+
- `submit(input, displayInput?, rawInput?, options?)` returns a turn handle (`{ turnId, completed }`).
|
|
150
|
+
`completed` rejects with an error that `isTurnNotRunError()` (from
|
|
151
|
+
`@robota-sdk/agent-interface-session`) recognizes if the queued prompt never ran: replaced by a
|
|
152
|
+
later prompt, dropped from a full queue, or cleared by abort, cancel or shutdown.
|
|
153
|
+
- `abort()` stops the running turn and clears the queue; `cancelQueue()` drops queued prompts only.
|
|
154
|
+
- `executeCommand(name, args)` runs a command; `listCommands()` lists them.
|
|
155
|
+
- `resolvePermission(id, result)` and `resolveAsk(id, response)` answer pending requests.
|
|
156
|
+
- `getMessages()`, `getContextState()`, `getStreamingText()`, `getActiveTools()`, `isExecuting()`.
|
|
157
|
+
- `compactContext(instructions?)` summarizes the conversation to free context space.
|
|
158
|
+
- `setGoal(objective)` pursues an objective over several turns; `setPlan(objective)` and
|
|
159
|
+
`approvePlan()` drive explicit plan mode.
|
|
160
|
+
- `getName()` / `setName(name)`, `getSession()` (the underlying `Session`), `shutdown()`.
|
|
161
|
+
|
|
162
|
+
In a trusted project, path-like `@file` references in a prompt are read (bounded in size and
|
|
163
|
+
recursion), added to the model's input, and recorded in history as a structured event.
|
|
164
|
+
|
|
165
|
+
## Project access
|
|
166
|
+
|
|
167
|
+
A `cwd` alone gives the session no access to project content. Project context files
|
|
168
|
+
(`AGENTS.md`, `CLAUDE.md`), project settings, memory, checkpoints and stored sessions are reachable
|
|
169
|
+
only through a trusted `TWorkspaceProjectAccess` decision that the host obtains from a
|
|
170
|
+
`WorkspaceTrustService`. Without one the session is Restricted.
|
|
172
171
|
|
|
173
172
|
```typescript
|
|
173
|
+
import { homedir } from 'node:os';
|
|
174
|
+
import { join } from 'node:path';
|
|
174
175
|
import {
|
|
175
176
|
InteractiveSession,
|
|
177
|
+
createNodeWorkspaceTrustService,
|
|
176
178
|
createProjectSessionStore,
|
|
177
179
|
getWorkspaceProjectStateStorage,
|
|
178
180
|
} from '@robota-sdk/agent-framework';
|
|
179
181
|
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
180
|
-
import type { TWorkspaceProjectAccess } from '@robota-sdk/agent-framework';
|
|
181
182
|
|
|
182
183
|
declare const provider: IAIProvider;
|
|
183
|
-
// Supplied by a host-owned WorkspaceTrustService configured with projectStateDirectories
|
|
184
|
-
// after identity/trust validation.
|
|
185
|
-
declare const projectAccess: TWorkspaceProjectAccess;
|
|
186
184
|
const cwd = process.cwd();
|
|
187
185
|
|
|
186
|
+
// The host chooses where trust grants are stored and where project state lives
|
|
187
|
+
// (directories relative to the project root).
|
|
188
|
+
const trust = createNodeWorkspaceTrustService(join(homedir(), '.my-app', 'workspace-trust.json'), {
|
|
189
|
+
sessions: '.my-app/sessions',
|
|
190
|
+
'session-logs': '.my-app/logs',
|
|
191
|
+
memory: '.my-app/memory',
|
|
192
|
+
checkpoints: '.my-app/checkpoints',
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// inspect() reports the current decision; grant() trusts the project after the user agrees.
|
|
196
|
+
const projectAccess = await trust.inspect(cwd);
|
|
197
|
+
|
|
188
198
|
const sessionStore =
|
|
189
199
|
projectAccess.status === 'trusted'
|
|
190
200
|
? createProjectSessionStore(
|
|
@@ -193,282 +203,144 @@ const sessionStore =
|
|
|
193
203
|
)
|
|
194
204
|
: undefined;
|
|
195
205
|
|
|
196
|
-
const session = new InteractiveSession({
|
|
197
|
-
cwd,
|
|
198
|
-
provider,
|
|
199
|
-
projectAccess,
|
|
200
|
-
sessionStore,
|
|
201
|
-
resumeSessionId: 'sess_123', // Session ID to restore, incl. sandbox snapshot (optional)
|
|
202
|
-
forkSession: false, // Fork the resumed session into a new one (optional)
|
|
203
|
-
permissionMode: 'default',
|
|
204
|
-
maxTurns: 10,
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Subscribe to events
|
|
208
|
-
session.on('text_delta', (delta: string) => {
|
|
209
|
-
process.stdout.write(delta); // streaming text chunk
|
|
210
|
-
});
|
|
211
|
-
session.on('tool_start', (state) => {
|
|
212
|
-
console.log(`Running: ${state.toolName}`);
|
|
213
|
-
});
|
|
214
|
-
session.on('tool_end', (state) => {
|
|
215
|
-
console.log(`Done: ${state.toolName} — ${state.result}`);
|
|
216
|
-
});
|
|
217
|
-
session.on('thinking', (isThinking: boolean) => {
|
|
218
|
-
// show/hide spinner
|
|
219
|
-
});
|
|
220
|
-
session.on('complete', (result) => {
|
|
221
|
-
console.log(result.response);
|
|
222
|
-
});
|
|
223
|
-
session.on('error', (error: Error) => {
|
|
224
|
-
console.error(error);
|
|
225
|
-
});
|
|
226
|
-
session.on('context_update', (state) => {
|
|
227
|
-
// token usage updated
|
|
228
|
-
});
|
|
229
|
-
session.on('interrupted', (result) => {
|
|
230
|
-
// abort completed
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// Submit a prompt. The handle identifies this accepted turn even if it waits in the queue.
|
|
234
|
-
const handle = await session.submit('Explain this code', undefined, undefined, {
|
|
235
|
-
driverId: 'owner',
|
|
236
|
-
});
|
|
237
|
-
await handle.completed;
|
|
238
|
-
|
|
239
|
-
// Path-like @file references are expanded into model-only prompt context by the SDK.
|
|
240
|
-
// The user-visible history keeps the original prompt plus a structured file-reference event.
|
|
241
|
-
await session.submit('Explain @AGENTS.md and @docs/SPEC.md');
|
|
242
|
-
|
|
243
|
-
// Submit with display override (shown in UI) and raw input (for hook matching)
|
|
244
|
-
await session.submit('full expanded prompt…', '/audit', '/rulebased-harness:audit');
|
|
245
|
-
|
|
246
|
-
// Execute slash commands through the command layer. With the skills command module composed,
|
|
247
|
-
// `/audit src/index.ts` is normalized by SDK to command "skills" with args "audit src/index.ts".
|
|
248
|
-
await session.executeCommand('audit', 'src/index.ts');
|
|
249
|
-
|
|
250
|
-
// Abort current execution and clear queue
|
|
251
|
-
session.abort();
|
|
252
|
-
|
|
253
|
-
// Cancel queued prompt without aborting current execution
|
|
254
|
-
session.cancelQueue();
|
|
255
|
-
|
|
256
|
-
// Execute system commands
|
|
257
|
-
const result = await session.executeCommand('context', '');
|
|
258
|
-
// result.message, result.success, result.data
|
|
259
|
-
|
|
260
|
-
// List all registered system commands
|
|
261
|
-
session.listCommands(); // Array<{ name, description }>
|
|
262
|
-
|
|
263
|
-
// State queries
|
|
264
|
-
session.isExecuting(); // boolean
|
|
265
|
-
session.getPendingPrompt(); // string | null
|
|
266
|
-
session.getMessages(); // TUniversalMessage[]
|
|
267
|
-
session.getContextState(); // IContextWindowState
|
|
268
|
-
session.getStreamingText(); // string (accumulated so far)
|
|
269
|
-
session.getActiveTools(); // IToolState[]
|
|
270
|
-
|
|
271
|
-
// Session naming
|
|
272
|
-
session.getName(); // string | undefined
|
|
273
|
-
session.setName('my-task'); // sets the session name
|
|
274
|
-
|
|
275
|
-
// Access underlying Session for advanced use
|
|
276
|
-
session.getSession(); // Session
|
|
206
|
+
const session = new InteractiveSession({ cwd, provider, projectAccess, sessionStore });
|
|
277
207
|
```
|
|
278
208
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
composition and Restricted-versus-trusted observable.
|
|
283
|
-
|
|
284
|
-
The Node host supplies the production lifecycle through
|
|
285
|
-
`createNodeWorkspaceTrustService(trustStorePath, projectStateDirectories)`. The caller chooses the
|
|
286
|
-
user-owned store path and all four project-state directories; the Robota CLI uses
|
|
287
|
-
`~/.robota/workspace-trust.json` for the trust store. Omitting project-state directories still permits
|
|
288
|
-
trust inspection, but deriving project-state storage then fails closed.
|
|
289
|
-
The service binds grants to the canonical Git worktree and repository common-directory identity,
|
|
290
|
-
stores only owner-readable generation records, and treats non-Git paths, repository replacement,
|
|
291
|
-
revocation, and trust-store errors as Restricted. A later provider settings layer that changes `baseURL`
|
|
292
|
-
without its own credential also clears inherited `apiKey` and `apiKeyEnv` fields; endpoint provenance
|
|
293
|
-
diagnostics can report the quarantine without exposing the credential.
|
|
209
|
+
Trust is bound to the Git worktree and repository identity. Non-Git paths, a replaced repository,
|
|
210
|
+
a revoked grant or a trust-store error all resolve to Restricted, and a revoked authority stops
|
|
211
|
+
working immediately. The `cwd` must be the trusted root or inside it.
|
|
294
212
|
|
|
295
|
-
|
|
213
|
+
## Headless and multi-session runtime
|
|
296
214
|
|
|
297
|
-
`
|
|
215
|
+
`createAgentRuntime()` holds one configuration (provider, `cwd`, project access, command modules,
|
|
216
|
+
session store, transports) and creates `InteractiveSession`s from it:
|
|
298
217
|
|
|
299
218
|
```typescript
|
|
300
|
-
import {
|
|
301
|
-
import
|
|
302
|
-
import type { ICommandResult } from '@robota-sdk/agent-interface-command';
|
|
303
|
-
|
|
304
|
-
declare const session: ICommandHostContext;
|
|
305
|
-
const executor = new SystemCommandExecutor(); // starts empty unless commands are supplied
|
|
306
|
-
|
|
307
|
-
// Execute a command
|
|
308
|
-
const result: ICommandResult | null = await executor.execute('status', session, '');
|
|
309
|
-
if (result) {
|
|
310
|
-
console.log(result.message); // "OK"
|
|
311
|
-
}
|
|
219
|
+
import { createAgentRuntime } from '@robota-sdk/agent-framework';
|
|
220
|
+
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
312
221
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
description: 'Show agent status',
|
|
317
|
-
execute: (session, args) => ({ message: 'OK', success: true }),
|
|
222
|
+
const runtime = createAgentRuntime({
|
|
223
|
+
cwd: process.cwd(),
|
|
224
|
+
provider: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
|
|
318
225
|
});
|
|
319
226
|
|
|
320
|
-
//
|
|
321
|
-
|
|
322
|
-
executor.hasCommand('permissions'); // boolean
|
|
227
|
+
// Each call creates a new InteractiveSession that inherits the runtime configuration.
|
|
228
|
+
const session = runtime.createSession({ permissionMode: 'plan' });
|
|
323
229
|
```
|
|
324
230
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
`
|
|
330
|
-
only render command results and must not own provider model lists.
|
|
231
|
+
`buildRuntimeSession()` is the one place a session is built from resolved options, and
|
|
232
|
+
`startRuntimeHost()` adds transport start/stop and a bounded shutdown on top of it; the CLI's
|
|
233
|
+
`robota --serve` uses both. `SessionPool` keeps several live sessions for a runtime that serves more
|
|
234
|
+
than one client. Headless output is available through `createHeadlessRunner` and
|
|
235
|
+
`createHeadlessTransport`, and `createProgrammaticAgent` drives a session from code.
|
|
331
236
|
|
|
332
|
-
|
|
333
|
-
records into generic prompt descriptions. The CLI/TUI renders those descriptions without owning
|
|
334
|
-
provider-specific API key or console URLs.
|
|
237
|
+
## Permissions
|
|
335
238
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
239
|
+
Every tool call is decided by `evaluatePermission` from `@robota-sdk/agent-core`: deny rules first,
|
|
240
|
+
then ask rules and the protections that are never auto-approved (such as deleting the workspace or
|
|
241
|
+
writing into `.git`), then allow rules (skipped in `bypassPermissions`, which approves everything
|
|
242
|
+
that got this far), then what the mode says about the kind of action:
|
|
339
243
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
`
|
|
244
|
+
| Mode | Read-only tools (Read, Glob, Grep) | Edits (Write, Edit) | Commands (Shell, Bash) |
|
|
245
|
+
| ------------------- | :--------------------------------: | :-----------------: | :--------------------: |
|
|
246
|
+
| `plan` | auto | deny | deny |
|
|
247
|
+
| `default` | auto | ask | ask |
|
|
248
|
+
| `acceptEdits` | auto | auto | ask |
|
|
249
|
+
| `bypassPermissions` | auto | auto | auto |
|
|
250
|
+
| `auto` | auto | auto | model classifier |
|
|
343
251
|
|
|
344
|
-
|
|
252
|
+
A shell command that only reads (such as `ls`, `cat` or `git status`) with every path inside the
|
|
253
|
+
workspace counts as read-only. In `auto` mode a model classifier approves or blocks what the mode
|
|
254
|
+
leaves open, and after repeated
|
|
255
|
+
refusals the session falls back to asking a person. `ask` rules ask even in `bypassPermissions`; in
|
|
256
|
+
`plan` mode anything that would change something is refused instead of asked. See the
|
|
257
|
+
[permissions and hooks guide](../../content/guide/permissions-and-hooks.md).
|
|
345
258
|
|
|
346
|
-
|
|
259
|
+
## Hooks
|
|
347
260
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
261
|
+
Sessions run the hooks configured in settings (or passed by the host) at lifecycle events such as
|
|
262
|
+
`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SessionStart`, `SessionEnd`,
|
|
263
|
+
`PreCompact`, `SubagentStart` and `PreModelCall`. Only `PreToolUse` can block. The full list, input
|
|
264
|
+
fields and blocking rules are in the [hook event catalog](../agent-core/docs/HOOK-CATALOG.md).
|
|
351
265
|
|
|
352
|
-
|
|
353
|
-
registry.addModule(
|
|
354
|
-
createSkillsCommandModule({
|
|
355
|
-
contributionSources: [createNodeHostContributionSource(process.cwd())],
|
|
356
|
-
}),
|
|
357
|
-
);
|
|
266
|
+
This package adds two hook executors to the `command` and `http` executors from `agent-core`:
|
|
358
267
|
|
|
359
|
-
|
|
360
|
-
|
|
268
|
+
| Executor | Hook type | What it does |
|
|
269
|
+
| ---------------- | --------- | --------------------------------------------------------------------------------- |
|
|
270
|
+
| `PromptExecutor` | `prompt` | Asks a model (from a provider factory you supply) for an `{ ok, reason }` verdict |
|
|
271
|
+
| `AgentExecutor` | `agent` | Runs a subagent session (from a session factory you supply) for a verdict |
|
|
361
272
|
|
|
362
|
-
|
|
363
|
-
const filtered = registry.getCommands('mod'); // matches "mode", "model"
|
|
273
|
+
## Commands
|
|
364
274
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
275
|
+
The framework owns the command infrastructure: `SystemCommandExecutor`, `CommandRegistry`, command
|
|
276
|
+
sources (`BuiltinCommandSource`, `SkillCommandSource`, `PluginCommandSource`) and the common APIs
|
|
277
|
+
command modules use. It does not own the user-visible built-in commands; those are command modules
|
|
278
|
+
from `@robota-sdk/agent-command` that the host composes. Command names are slash-free (`help`,
|
|
279
|
+
`compact`); a UI renders them as `/help`, `/compact`.
|
|
368
280
|
|
|
369
|
-
|
|
370
|
-
the contribution sources and ordered root descriptors; absent sources or roots means no skill file
|
|
371
|
-
discovery. This keeps product directory choices out of the framework and lets inspection use the same
|
|
372
|
-
roots as activation.
|
|
373
|
-
|
|
374
|
-
Model-invocable skills are exposed to the model as metadata only when the session has a composed
|
|
375
|
-
model-invocable `skills` command descriptor. `@robota-sdk/agent-command` owns `skills` and
|
|
376
|
-
activates skills through the SDK host API. Models use the SDK-projected `command_skills`
|
|
377
|
-
tool by default, with skill arguments in `args`. A product host can set
|
|
378
|
-
`modelCommandToolPrefix` on its session options to choose another prefix; Robota sets
|
|
379
|
-
`robota_command_`. Mentioning a skill in ordinary prose,
|
|
380
|
-
recommending a skill in assistant text, or matching a natural-language phrase in SDK/TUI code does
|
|
381
|
-
not activate the skill.
|
|
382
|
-
|
|
383
|
-
The framework's default prompt enclosure for attached `@file` content is
|
|
384
|
-
`<file_references>`. Product hosts can set `promptFileReferenceTag` on session options to
|
|
385
|
-
choose a different enclosure; Robota sets `robota_file_references`. SDK consumers that
|
|
386
|
-
need the previous identifiers should pass `modelCommandToolPrefix: 'robota_command_'`
|
|
387
|
-
and `promptFileReferenceTag: 'robota_file_references'` explicitly. Direct calls to
|
|
388
|
-
`createProviderSafeModelCommandToolName`, `createModelCommandToolProjection`, and
|
|
389
|
-
`buildPromptWithFileReferences` can pass the same values as their optional arguments.
|
|
390
|
-
|
|
391
|
-
### createQuery()
|
|
281
|
+
A command module is a plain object:
|
|
392
282
|
|
|
393
283
|
```typescript
|
|
394
|
-
import {
|
|
395
|
-
import {
|
|
284
|
+
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
285
|
+
import type { ICommandModule } from '@robota-sdk/agent-framework';
|
|
286
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
396
287
|
|
|
397
|
-
const provider
|
|
398
|
-
const query = createQuery({ provider });
|
|
288
|
+
declare const provider: IAIProvider;
|
|
399
289
|
|
|
400
|
-
const
|
|
290
|
+
const statusModule: ICommandModule = {
|
|
291
|
+
name: 'status',
|
|
292
|
+
systemCommands: [
|
|
293
|
+
{
|
|
294
|
+
name: 'status',
|
|
295
|
+
description: 'Show agent status',
|
|
296
|
+
execute: (context, args) => ({ message: `OK ${args}`, success: true }),
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
};
|
|
401
300
|
|
|
402
|
-
const
|
|
301
|
+
const session = new InteractiveSession({
|
|
302
|
+
cwd: process.cwd(),
|
|
403
303
|
provider,
|
|
404
|
-
|
|
405
|
-
permissionMode: 'acceptEdits',
|
|
406
|
-
maxTurns: 10,
|
|
407
|
-
onTextDelta: (delta) => process.stdout.write(delta),
|
|
304
|
+
commandModules: [statusModule],
|
|
408
305
|
});
|
|
409
|
-
|
|
410
|
-
|
|
306
|
+
const result = await session.executeCommand('status', '');
|
|
307
|
+
console.log(result?.message);
|
|
411
308
|
```
|
|
412
309
|
|
|
413
|
-
|
|
310
|
+
Skills are discovered only from the contribution sources and roots the host passes. A model can run
|
|
311
|
+
a model-invocable command through a projected tool named `command_<name>` by default;
|
|
312
|
+
`modelCommandToolPrefix` changes the prefix.
|
|
414
313
|
|
|
415
|
-
|
|
416
|
-
details. Use `InteractiveSession` for event-driven sessions or `createQuery()` for prompt-only
|
|
417
|
-
one-shot calls.
|
|
314
|
+
## Subagents
|
|
418
315
|
|
|
419
|
-
|
|
316
|
+
- `IAgentDefinition` describes a reusable agent: `name`, `description`, `systemPrompt`, and
|
|
317
|
+
optionally `model`, `effort`, `role`, `maxTurns`, `tools` and `disallowedTools`.
|
|
318
|
+
- `BUILT_IN_AGENTS` holds `general-purpose`, `Explore` and `Plan`; `Explore` and `Plan` cannot use
|
|
319
|
+
`Write` or `Edit`.
|
|
320
|
+
- Custom definitions come from `agentDefinitions` or from the host-supplied `agentDefinitionRoots`.
|
|
321
|
+
The CLI chooses which directories those are.
|
|
322
|
+
- `createAgentTool()` gives the model an `Agent` tool that starts subagents, including several in
|
|
323
|
+
parallel through its `jobs` argument. Subagents run as background tasks managed by
|
|
324
|
+
`@robota-sdk/agent-executor`; `createSubagentSession()` builds the child session from the
|
|
325
|
+
parent's resolved configuration.
|
|
326
|
+
- `runSequential`, `runParallel`, `runHandoff`, `runHierarchical` and `runGroupChat` run multi-agent
|
|
327
|
+
orchestration patterns over the contracts in `agent-core`.
|
|
420
328
|
|
|
421
|
-
|
|
422
|
-
from the owner package:
|
|
329
|
+
## Sandbox execution
|
|
423
330
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
```typescript
|
|
429
|
-
import {
|
|
430
|
-
createShellTool,
|
|
431
|
-
createBashTool,
|
|
432
|
-
createEditTool,
|
|
433
|
-
createGlobTool,
|
|
434
|
-
createGrepTool,
|
|
435
|
-
createReadTool,
|
|
436
|
-
createWriteTool,
|
|
437
|
-
webFetchTool,
|
|
438
|
-
webSearchTool,
|
|
439
|
-
askUserQuestionTool,
|
|
440
|
-
} from '@robota-sdk/agent-tools';
|
|
441
|
-
|
|
442
|
-
const cwd = process.cwd();
|
|
443
|
-
const fileTools = [
|
|
444
|
-
createShellTool({ cwd }),
|
|
445
|
-
createBashTool({ cwd }),
|
|
446
|
-
createEditTool({ cwd }),
|
|
447
|
-
createGlobTool({ cwd }),
|
|
448
|
-
createGrepTool({ cwd }),
|
|
449
|
-
createReadTool({ cwd }),
|
|
450
|
-
createWriteTool({ cwd }),
|
|
451
|
-
];
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
`webFetchTool`, `webSearchTool` and `askUserQuestionTool` remain instances: they touch no filesystem,
|
|
455
|
-
so there is no root for them to be contained by.
|
|
456
|
-
|
|
457
|
-
### Sandbox Execution
|
|
458
|
-
|
|
459
|
-
SDK sessions can receive a provider-neutral sandbox client. When provided, Bash, Read, Write, and Edit use the sandbox execution plane instead of the host process/filesystem:
|
|
460
|
-
|
|
461
|
-
<!-- doc-example-skip: requires the optional e2b dependency -->
|
|
331
|
+
A session can run its commands and file tools in a sandbox through a provider-neutral
|
|
332
|
+
`ISandboxClient` from `@robota-sdk/agent-tools`. `E2BSandboxClient` adapts an E2B sandbox that your
|
|
333
|
+
application creates; `@robota-sdk/agent-framework` does not depend on E2B.
|
|
462
334
|
|
|
463
335
|
```typescript
|
|
464
336
|
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
465
|
-
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
466
337
|
import { E2BSandboxClient } from '@robota-sdk/agent-tools';
|
|
467
|
-
import type {
|
|
468
|
-
import {
|
|
338
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
339
|
+
import type { IE2BSandboxAdapter, IWorkspaceManifest } from '@robota-sdk/agent-tools';
|
|
340
|
+
|
|
341
|
+
declare const provider: IAIProvider;
|
|
342
|
+
declare const sandbox: IE2BSandboxAdapter; // e.g. `await Sandbox.create()` from the `e2b` package
|
|
469
343
|
|
|
470
|
-
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
471
|
-
const sandbox = await Sandbox.create();
|
|
472
344
|
const workspaceManifest: IWorkspaceManifest = {
|
|
473
345
|
entries: {
|
|
474
346
|
'task.md': { type: 'file', content: 'Review this repository.\n' },
|
|
@@ -482,154 +354,43 @@ const session = new InteractiveSession({
|
|
|
482
354
|
provider,
|
|
483
355
|
sandboxClient: new E2BSandboxClient({ sandbox }),
|
|
484
356
|
workspaceManifest,
|
|
485
|
-
reversibleExecution: { mode: 'local-first' },
|
|
486
357
|
});
|
|
487
358
|
```
|
|
488
359
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
## Subagent Sessions
|
|
360
|
+
The manifest is applied once before the session starts. With a `sessionStore` and a sandbox client
|
|
361
|
+
that supports snapshots, `shutdown()` stores the sandbox snapshot ID, and resuming the session
|
|
362
|
+
(without forking) restores the sandbox before the saved messages are replayed.
|
|
494
363
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
```typescript
|
|
498
|
-
import { createSubagentSession } from '@robota-sdk/agent-framework';
|
|
499
|
-
import type { ISubagentOptions } from '@robota-sdk/agent-framework';
|
|
500
|
-
|
|
501
|
-
// agentDefinition, parentConfig/parentContext/parentTools, provider, terminal, …
|
|
502
|
-
declare const options: ISubagentOptions;
|
|
503
|
-
const subSession = createSubagentSession(options);
|
|
504
|
-
const result = await subSession.run('Analyze the test coverage gaps');
|
|
505
|
-
```
|
|
364
|
+
## More capabilities
|
|
506
365
|
|
|
507
|
-
|
|
366
|
+
| Capability | Exports |
|
|
367
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
368
|
+
| Model fallback | `FallbackProvider`, `resolveModelFallbackChain`, `applyModelFallback` |
|
|
369
|
+
| Advisor (second-model tool) | `AdvisorController`, `createAdvisorTool` |
|
|
370
|
+
| Edit checkpoints and rewind | `EditCheckpointStore`, `wrapEditCheckpointTools` |
|
|
371
|
+
| Reversible execution | `evaluateReversibleToolSafety`, `wrapReversibleExecutionTools` (session option `reversibleExecution`) |
|
|
372
|
+
| Project and user memory | `ProjectMemoryStore`, `WorkspaceMemoryStore`, `SemanticMemoryStore`, `listUserLocalMemoryItems` |
|
|
373
|
+
| Evals | `defineEval`, `runEval`, `createSessionRunFn`, matchers such as `exactMatch` and `usedTool` |
|
|
374
|
+
| Bundle plugins | `BundlePluginLoader`, `BundlePluginInstaller`, `MarketplaceClient` |
|
|
375
|
+
| Settings | `readSettings`, `writeSettings`, `inspectSettingsLayers`, `createNodeHostSettingsSource` |
|
|
376
|
+
| Self-hosting verification | `planSelfHostingVerification`, `transitionSelfHostingLoop` |
|
|
508
377
|
|
|
509
|
-
|
|
378
|
+
Bundle plugins use the Claude Code plugin layout: a directory with `.claude-plugin/plugin.json`
|
|
379
|
+
(`name`, `version`, `description`, `features`) that can contribute skills, commands, hooks, MCP
|
|
380
|
+
server configuration and agent definitions.
|
|
510
381
|
|
|
511
|
-
|
|
382
|
+
## Settings
|
|
512
383
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
both the child session ID and the persistence key, together with the same session store that holds
|
|
519
|
-
the copied record. Each completed child turn then updates that copied record, so `attach` sees the
|
|
520
|
-
conversation after the fork as well as the conversation copied at fork time. Ordinary subagent jobs
|
|
521
|
-
remain transient when they do not carry `resumeSessionId`; attaching is still a view switch, never a
|
|
522
|
-
merge with the parent record.
|
|
523
|
-
|
|
524
|
-
Background subagent lifecycle events are persisted through `InteractiveSession` when an SDK session persistence facade is configured. Streaming chunks are written to append-only JSONL logs/transcripts rather than rewriting the main session JSON per token.
|
|
525
|
-
|
|
526
|
-
## Replay-Grade Session Events
|
|
527
|
-
|
|
528
|
-
`Session.run()` forwards core execution events through the session logger. Current events include provider request envelopes, provider-native raw request/response/stream payloads, provider-normalized responses, assistant message commits, tool batch starts, tool execution requests, and tool execution results.
|
|
529
|
-
|
|
530
|
-
Provider-native payload events are emitted by concrete provider packages through `IChatOptions.onProviderNativeRawPayload`, then redacted and externalized by the session logger before they are written to disk. The SDK exposes session command APIs so command modules such as `/validate-session` can validate replay coverage without adding file-log logic to CLI/TUI hosts.
|
|
531
|
-
|
|
532
|
-
## Interactive Prompt Settlement
|
|
533
|
-
|
|
534
|
-
`InteractiveSession` emits transport-neutral `permission_request` and `ask_request` events. Attached
|
|
535
|
-
surfaces answer through `resolvePermission()` and `resolveAsk()`; the first answer wins and produces one
|
|
536
|
-
`prompt_resolved` event. Session construction does not expose parallel `permissionHandler` or `askHandler`
|
|
537
|
-
options, so local and remote surfaces share the same request/settlement path.
|
|
538
|
-
|
|
539
|
-
Persisted checkpoint operations emit `branch_event` only after the transition and session save succeed.
|
|
540
|
-
Transport-owned listeners must isolate their own delivery failures without changing arbitrary SDK listener
|
|
541
|
-
exception behavior.
|
|
542
|
-
|
|
543
|
-
## Hook Executors (SDK-Specific)
|
|
544
|
-
|
|
545
|
-
`agent-framework` provides two `IHookTypeExecutor` implementations beyond the `command` and `http` executors in `agent-core`:
|
|
546
|
-
|
|
547
|
-
| Executor | Hook Type | Description |
|
|
548
|
-
| ---------------- | --------- | ------------------------------------------------------------------------- |
|
|
549
|
-
| `PromptExecutor` | `prompt` | Injects the hook's prompt text into the session as a system instruction |
|
|
550
|
-
| `AgentExecutor` | `agent` | Creates a sub-agent session to process the hook input and return a result |
|
|
551
|
-
|
|
552
|
-
## Bundle Plugin System
|
|
553
|
-
|
|
554
|
-
Bundle plugins package reusable extensions (tools, hooks, permissions, system prompt additions) into installable units.
|
|
555
|
-
|
|
556
|
-
### Types
|
|
557
|
-
|
|
558
|
-
| Type | Description |
|
|
559
|
-
| ----------------------- | --------------------------------------------------------------- |
|
|
560
|
-
| `IBundlePluginManifest` | Plugin metadata: name, version, description, author, keywords |
|
|
561
|
-
| `ILoadedBundlePlugin` | Full bundle: manifest + tools, hooks, permissions, systemPrompt |
|
|
562
|
-
|
|
563
|
-
### BundlePluginLoader
|
|
564
|
-
|
|
565
|
-
Loads a bundle plugin from a directory path. Reads the manifest, resolves tool/hook definitions, and validates the bundle structure.
|
|
566
|
-
|
|
567
|
-
### BundlePluginInstaller
|
|
568
|
-
|
|
569
|
-
Manages plugin installation and uninstallation:
|
|
570
|
-
|
|
571
|
-
- Installs bundles to `~/.robota/plugins/` (user) or `.robota/plugins/` (project)
|
|
572
|
-
- Tracks installed plugins in a registry file
|
|
573
|
-
- Handles enable/disable state per plugin
|
|
574
|
-
|
|
575
|
-
## Plugins
|
|
576
|
-
|
|
577
|
-
`@robota-sdk/agent-plugin` plugins are **consumer opt-in** — they are not built into the CLI or SDK by default. Application consumers register plugins at composition time by passing plugin instances to the SDK assembly API.
|
|
578
|
-
|
|
579
|
-
```typescript
|
|
580
|
-
import { Robota } from '@robota-sdk/agent-core';
|
|
581
|
-
import { ConversationHistoryPlugin, LoggingPlugin } from '@robota-sdk/agent-plugin';
|
|
582
|
-
import type { IAgentConfig } from '@robota-sdk/agent-core';
|
|
583
|
-
|
|
584
|
-
declare const base: IAgentConfig;
|
|
585
|
-
const agent = new Robota({
|
|
586
|
-
...base,
|
|
587
|
-
plugins: [
|
|
588
|
-
new ConversationHistoryPlugin({ storage: 'memory' }),
|
|
589
|
-
new LoggingPlugin({ strategy: 'console', level: 'info' }),
|
|
590
|
-
],
|
|
591
|
-
});
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
Each plugin implements `AbstractPlugin` from `@robota-sdk/agent-core` and depends only on `agent-core`. The 8 plugins ship consolidated in a single package, `@robota-sdk/agent-plugin`: `ConversationHistoryPlugin`, `ErrorHandlingPlugin`, `ExecutionAnalyticsPlugin`, `LimitsPlugin`, `LoggingPlugin`, `PerformancePlugin`, `UsagePlugin`, `WebhookPlugin`.
|
|
595
|
-
|
|
596
|
-
## Configuration
|
|
597
|
-
|
|
598
|
-
Settings are merged from lowest to highest priority:
|
|
599
|
-
|
|
600
|
-
| Layer | Path | Scope |
|
|
601
|
-
| ----- | ----------------------------- | --------------------------------------- |
|
|
602
|
-
| 1 | `~/.robota/settings.json` | User global |
|
|
603
|
-
| 2 | `~/.claude/settings.json` | User global (Claude Code compatible) |
|
|
604
|
-
| 3 | `.robota/settings.json` | Project |
|
|
605
|
-
| 4 | `.robota/settings.local.json` | Project (local) |
|
|
606
|
-
| 5 | `.claude/settings.json` | Project (Claude Code compatible) |
|
|
607
|
-
| 6 | `.claude/settings.local.json` | Project (local, Claude Code compatible) |
|
|
608
|
-
|
|
609
|
-
`$ENV:VAR` substitution is applied after merge for provider API keys.
|
|
384
|
+
Settings are JSON files merged in the order the host gives them, later layers winning.
|
|
385
|
+
`$ENV:NAME` values in provider credentials are replaced with the environment variable. The Robota
|
|
386
|
+
CLI reads `~/.robota/settings.json`, `~/.claude/settings.json`, `.robota/settings.json`,
|
|
387
|
+
`.robota/settings.local.json`, `.claude/settings.json` and `.claude/settings.local.json`, in that
|
|
388
|
+
order; project files are read only in a trusted project.
|
|
610
389
|
|
|
611
390
|
```json
|
|
612
391
|
{
|
|
613
|
-
"
|
|
614
|
-
"currentProvider": "qwen",
|
|
392
|
+
"currentProvider": "anthropic",
|
|
615
393
|
"providers": {
|
|
616
|
-
"qwen": {
|
|
617
|
-
"type": "qwen",
|
|
618
|
-
"model": "qwen-plus",
|
|
619
|
-
"apiKey": "$ENV:DASHSCOPE_API_KEY",
|
|
620
|
-
"baseURL": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
621
|
-
},
|
|
622
|
-
"gemma": {
|
|
623
|
-
"type": "gemma",
|
|
624
|
-
"model": "supergemma4-26b-uncensored-v2",
|
|
625
|
-
"apiKey": "lm-studio",
|
|
626
|
-
"baseURL": "http://localhost:1234/v1"
|
|
627
|
-
},
|
|
628
|
-
"openai": {
|
|
629
|
-
"type": "openai",
|
|
630
|
-
"model": "<openai-compatible-model>",
|
|
631
|
-
"apiKey": "$ENV:OPENAI_API_KEY"
|
|
632
|
-
},
|
|
633
394
|
"anthropic": {
|
|
634
395
|
"type": "anthropic",
|
|
635
396
|
"model": "claude-sonnet-4-6",
|
|
@@ -643,37 +404,30 @@ Settings are merged from lowest to highest priority:
|
|
|
643
404
|
}
|
|
644
405
|
```
|
|
645
406
|
|
|
646
|
-
`currentProvider` selects the active entry
|
|
647
|
-
|
|
648
|
-
## Permission Modes
|
|
649
|
-
|
|
650
|
-
| Mode | Read/Glob/Grep | Write/Edit | Bash |
|
|
651
|
-
| ------------------- | :------------: | :--------: | :-----: |
|
|
652
|
-
| `plan` | auto | deny | deny |
|
|
653
|
-
| `default` | auto | approve | approve |
|
|
654
|
-
| `acceptEdits` | auto | auto | approve |
|
|
655
|
-
| `bypassPermissions` | auto | auto | auto |
|
|
407
|
+
`currentProvider` selects the active entry in `providers`. Other keys include `hooks`, `env`,
|
|
408
|
+
`language`, `preset`, `autoCompactThreshold`, `sandbox` and `enabledPlugins`.
|
|
656
409
|
|
|
657
410
|
## Dependencies
|
|
658
411
|
|
|
659
|
-
| Package
|
|
660
|
-
|
|
|
661
|
-
| `@robota-sdk/agent-core`
|
|
662
|
-
| `@robota-sdk/agent-session`
|
|
663
|
-
| `@robota-sdk/agent-tools`
|
|
664
|
-
| `@robota-sdk/agent-
|
|
665
|
-
| `
|
|
666
|
-
| `
|
|
412
|
+
| Package | Used for |
|
|
413
|
+
| ----------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
414
|
+
| `@robota-sdk/agent-core` | Agent engine, provider contract, permissions, hooks |
|
|
415
|
+
| `@robota-sdk/agent-session` | `Session`, logging and persistence ports |
|
|
416
|
+
| `@robota-sdk/agent-tools` | Tool factories and sandbox clients |
|
|
417
|
+
| `@robota-sdk/agent-tool-defaults` | The default tool set (loaded on first use) |
|
|
418
|
+
| `@robota-sdk/agent-executor` | Background tasks and subagent managers |
|
|
419
|
+
| `@robota-sdk/agent-file-authority` | Bounded, root-relative project file reads |
|
|
420
|
+
| `@robota-sdk/agent-interface-*` (analytics, command, execution, session, session-mobility, transport) | Shared contracts |
|
|
421
|
+
| `yaml`, `zod` | Frontmatter parsing and settings validation |
|
|
667
422
|
|
|
668
423
|
## Documentation
|
|
669
424
|
|
|
670
|
-
|
|
425
|
+
- [docs/SPEC.md](./docs/SPEC.md) — package contract, boundaries and design decisions
|
|
426
|
+
- [SDK guide](../../content/guide/sdk.md) and [Building agents](../../content/guide/building-agents.md)
|
|
427
|
+
- [Hook event catalog](../agent-core/docs/HOOK-CATALOG.md)
|
|
671
428
|
|
|
672
|
-
From `packages/agent-framework` in
|
|
673
|
-
|
|
674
|
-
testing SDK. The credentialed recorder now lives at `scripts/record-goal-cassette.mts`; it is
|
|
675
|
-
non-published development tooling, not a runtime provider dependency. Recording is an explicit
|
|
676
|
-
live-provider operation; replay needs no credentials and preserves the committed cassette.
|
|
429
|
+
From `packages/agent-framework` in the repository, `pnpm scenario:verify` runs the offline
|
|
430
|
+
examples (no provider credentials needed).
|
|
677
431
|
|
|
678
432
|
## License
|
|
679
433
|
|