@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/README.md CHANGED
@@ -1,23 +1,35 @@
1
1
  # @robota-sdk/agent-framework
2
2
 
3
- Runtime hosts are owned by this package: import `createHeadlessTransport`,
4
- `HeadlessInteractionChannel`, `createProgrammaticAgent`, `ProgrammaticInteractionChannel`,
5
- `TransportRegistry`, and its file/memory settings repositories from the root. Their implementation
6
- lives in `src/transport-host/`; terminal `PrintTerminal` and `promptInput` are local to the CLI.
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
- Programmatic SDK for building AI agents with Robota. Provides `InteractiveSession` as the central client-facing API, `createQuery()` for one-shot use, `createAgentRuntime()` as a composition factory for headless and multi-session consumers, session management, SDK-owned command/common APIs, permissions, hooks, streaming, context loading, bounded prompt file references, and context reference inventory.
8
+ It has three main entry points:
9
9
 
10
- This is the **assembly layer** of the Robota ecosystem — it composes lower-level packages (`agent-core`, `agent-tools`, `agent-session`, `agent-provider-*`) into a cohesive SDK.
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
- ## Quick Start
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
- // Simple one-shot query
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 queryWithOptions = createQuery({
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
- const detailedResponse = await queryWithOptions('Analyze the code');
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
- Without `projectAccess`, the runtime exposes an observable Restricted decision and does not construct
66
- project context, settings, memory, session, or log adapters. A host that has inspected or granted a
67
- workspace through `WorkspaceTrustService` passes that exact `TWorkspaceProjectAccess` decision.
68
- Project persistence is then composed explicitly from its named state facets; a bare `cwd` never
69
- creates a project store. A per-session `sessionStore` replaces the runtime store, while an explicit
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
- Replay-only project recovery validates the versioned JSONL before reconstructing a session. Loads and
81
- listings report malformed logs as `corrupt` and unsupported versions as `unsupported`, rather than
82
- hiding them as missing sessions. Snapshot encoding is unchanged; unversioned legacy logs are not replayed.
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
- The maintained offline examples verify explicit persist→resume composition and the workspace
85
- authority boundary with no provider credentials:
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
- ```bash
88
- pnpm --filter @robota-sdk/agent-framework scenario:verify
89
- ```
68
+ ## InteractiveSession
90
69
 
91
- ### Runtime host (`robota --serve`)
92
-
93
- `buildRuntimeSession()` is the single session-construction seam: every presentation — the TUI channel
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 { startRuntimeHost } from '@robota-sdk/agent-framework';
102
- import type { IRuntimeHostOptions, IRuntimeHostHandle } from '@robota-sdk/agent-framework';
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
- ## Features
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
- agent-framework (assembly layer)
146
- ├── InteractiveSession ← central client-facing API (event-driven)
147
- │ └── Session ← generic session (agent-session)
148
- ├── SystemCommandExecutor ← SDK-level command execution
149
- ├── CommandRegistry / BuiltinCommandSource / SkillCommandSource
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
- The SDK is **pure TypeScript with no React dependency**. The CLI is a thin presentation layer (TUI, plus the `--serve` runtime host) that consumes `InteractiveSession` events and maps them to React state. Any other client (web app, API server, worker) can do the same.
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
- ## API
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
- ### InteractiveSession — Central Client-Facing API
103
+ await session.shutdown();
104
+ ```
170
105
 
171
- `InteractiveSession` wraps `Session` (composition over inheritance) to provide event-driven interaction for any client. It manages streaming text accumulation, tool execution state tracking, prompt queuing, abort orchestration, and message history. Logic that was previously embedded in CLI React hooks now lives here.
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
- `WorkspaceTrustService` accepts host-owned identity and trust-store adapters. Its authority is opaque,
280
- runtime-registered, root-bound, and cannot be reconstructed from a path or serialized marker. See the
281
- maintained offline `verify-workspace-project-authority.ts` example for a complete inspect/grant/revoke
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
- ### SystemCommandExecutor — SDK-Level Commands
213
+ ## Headless and multi-session runtime
296
214
 
297
- `SystemCommandExecutor` executes named system commands against an `InteractiveSession`. Commands are pure TypeScript — no React, no TUI dependency. The CLI wraps them as slash commands with UI chrome.
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 { SystemCommandExecutor, createSystemCommands } from '@robota-sdk/agent-framework';
301
- import type { ICommandHostContext } from '@robota-sdk/agent-framework';
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
- // Register a custom command
314
- executor.register({
315
- name: 'status',
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
- // List all commands
321
- executor.listCommands(); // ISystemCommand[]
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
- SDK core does not own user-visible built-in commands. Product built-ins are supplied as `agent-command` modules. SDK command identity is slash-free (`skills`, `help`, `compact`); UI shells render and parse those commands as slash syntax such as `/skills`, `/help`, and `/compact`.
326
-
327
- Command modules may use SDK common APIs for shared provider-neutral behavior. For `/model`, the SDK
328
- resolves the active provider from settings, reads provider-owned fallback metadata from injected
329
- `IProviderDefinition` records, and can invoke provider-owned catalog refresh hooks. The CLI/TUI must
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
- For provider setup, the SDK projects provider-owned setup help links from `IProviderDefinition`
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
- For `/permissions`, the SDK owns permission-mode constants, subcommand descriptors, validation,
337
- state formatting, and command-host adapter access. The command module owns user-visible behavior and
338
- keeps permission-mode changes under `/permissions [mode]`.
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
- For `/validate-session`, the session command module calls SDK session command APIs to locate and
341
- validate the current JSONL session log. Hosts may override `validateCurrentSessionReplayLog()` in
342
- `ICommandHostContext` when they own a non-file-backed session log store.
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
- ### CommandRegistry, BuiltinCommandSource, SkillCommandSource
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
- These classes provide slash command discovery and aggregation for clients that expose a command palette or autocomplete UI.
259
+ ## Hooks
347
260
 
348
- ```typescript
349
- import { CommandRegistry, createNodeHostContributionSource } from '@robota-sdk/agent-framework';
350
- import { createSkillsCommandModule } from '@robota-sdk/agent-command';
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
- const registry = new CommandRegistry();
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
- // Get all commands (returns ICommand[])
360
- const commands = registry.getCommands();
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
- // Filter by prefix (for autocomplete)
363
- const filtered = registry.getCommands('mod'); // matches "mode", "model"
273
+ ## Commands
364
274
 
365
- // Resolve short plugin name to fully qualified form
366
- registry.resolveQualifiedName('audit'); // "my-plugin:audit"
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
- `SkillCommandSource` is the SDK common API used by the skills command module. The host supplies both
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 { createQuery } from '@robota-sdk/agent-framework';
395
- import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
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 = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
398
- const query = createQuery({ provider });
288
+ declare const provider: IAIProvider;
399
289
 
400
- const response = await query('Show me the file list');
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 queryWithOptions = createQuery({
301
+ const session = new InteractiveSession({
302
+ cwd: process.cwd(),
403
303
  provider,
404
- cwd: '/path/to/project',
405
- permissionMode: 'acceptEdits',
406
- maxTurns: 10,
407
- onTextDelta: (delta) => process.stdout.write(delta),
304
+ commandModules: [statusModule],
408
305
  });
409
-
410
- const detailedResponse = await queryWithOptions('Analyze the code');
306
+ const result = await session.executeCommand('status', '');
307
+ console.log(result?.message);
411
308
  ```
412
309
 
413
- ### Session Assembly
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
- `createSession()`, `loadConfig()`, `loadContext()`, and `detectProject()` are internal SDK assembly
416
- details. Use `InteractiveSession` for event-driven sessions or `createQuery()` for prompt-only
417
- one-shot calls.
314
+ ## Subagents
418
315
 
419
- ### Built-in Tools
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
- `@robota-sdk/agent-framework` assembles built-in tools for SDK sessions, but direct tool usage imports
422
- from the owner package:
329
+ ## Sandbox execution
423
330
 
424
- Each file tool is a FACTORY that requires the containment root it operates in (ARCH-010) — there is
425
- no ready-made instance, because one bound at import time can carry no root, and a file tool without a
426
- root has no boundary:
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 { IWorkspaceManifest } from '@robota-sdk/agent-tools';
468
- import { Sandbox } from 'e2b';
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
- `E2BSandboxClient` is a structural adapter owned by `agent-tools`, and it does not make `e2b` a dependency of `agent-framework`. Install and create the concrete provider SDK in the application layer, then pass the adapter into `InteractiveSession`. `workspaceManifest` also uses the `agent-tools` contract; SDK applies it once before constructing the underlying `Session`.
490
-
491
- When `sessionStore` and a snapshot-capable `sandboxClient` are both provided, `InteractiveSession.shutdown()` stores `sandboxSnapshotId` in the session record. A later non-fork `resumeSessionId` restore calls `sandboxClient.restore(snapshotId)` before saved messages are injected back into the `Session`. Forked sessions intentionally do not hydrate the previous sandbox reference because provider pause/resume references can be one-to-one.
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
- `createSubagentSession()` creates an isolated child session for delegating subtasks. The subagent receives pre-resolved config and context from the parent — it does not load config files or context from disk. Callers may provide a stable `sessionId`, `sessionLogger`, and `sessionStore` so the child session writes durable state.
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
- ### Agent Definitions
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
- `IAgentDefinition` describes a reusable agent configuration (system prompt, allowed tools, permission mode). Custom agents are discovered from `.robota/agents/` (project), `.claude/agents/` (Claude Code compatible), and `~/.robota/agents/` (user). `AgentDefinitionLoader` is an internal class — it is not part of the public API.
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
- Built-in agents: `general-purpose` (full tool access), `Explore` (read-only, Haiku model), `Plan` (read-only planning).
382
+ ## Settings
512
383
 
513
- ### createAgentTool()
514
-
515
- `createAgentTool()` wraps subagent creation into a tool the AI can invoke directly. The parent session's hooks, permissions, and context are forwarded to the child.
516
-
517
- When a background job resumes a forked record, the runner passes the record's `resumeSessionId` as
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
- "defaultMode": "default",
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 from `providers`. Qwen Model Studio profiles use `type: "qwen"` with the documented DashScope OpenAI-compatible `baseURL`. DeepSeek API profiles use `type: "deepseek"` with the documented DeepSeek OpenAI-compatible `baseURL`. Gemma-family local models should use a `type: "gemma"` profile so provider-specific stream projection is applied. The resolved SDK config normalizes the active profile into `provider.name`, `provider.model`, `provider.apiKey`, optional `provider.baseURL`, and optional `provider.timeout`. The legacy `provider` object remains supported when `currentProvider` is not configured.
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 | Purpose |
660
- | -------------------------------------- | ------------------------------------------------------------- |
661
- | `@robota-sdk/agent-core` | Engine, providers, permissions, hooks |
662
- | `@robota-sdk/agent-session` | Session, neutral log/store ports, explicit Node host adapters |
663
- | `@robota-sdk/agent-tools` | Tool infrastructure + built-in tools |
664
- | `@robota-sdk/agent-provider-anthropic` | Anthropic LLM provider |
665
- | `chalk` | Terminal colors (permission prompt) |
666
- | `zod` | Settings schema validation |
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
- See [docs/SPEC.md](./docs/SPEC.md) for the full specification, architecture details, and design decisions.
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 a built repository, run
673
- `pnpm exec tsx examples/verify-goal-cassette-replay.mts` for offline goal replay through the public
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