@robota-sdk/agent-framework 3.0.0-beta.83 → 3.0.0-beta.85

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,166 +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:
86
-
87
- ```bash
88
- pnpm --filter @robota-sdk/agent-framework scenario:verify
89
- ```
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).
90
67
 
91
- ### Runtime host (`robota --serve`)
68
+ ## InteractiveSession
92
69
 
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 — a SessionSlot: the live session every presentation drives, stable across a switch
108
- // host.session.current — the session it currently forwards to
109
- // await host.shutdown() — bounded transport teardown + session shutdown
110
- ```
75
+ import { InteractiveSession } from '@robota-sdk/agent-framework';
76
+ import type { IAIProvider } from '@robota-sdk/agent-core';
111
77
 
112
- ## Features
113
-
114
- - **InteractiveSession** — Event-driven session wrapper (composition over Session). Central client-facing API for CLI, web, API server, or any other client
115
- - **SystemCommandExecutor + ISystemCommand** — SDK-level command execution infrastructure for product-composed command modules
116
- - **CommandRegistry, BuiltinCommandSource, SkillCommandSource** — Command registry and SDK common discovery APIs. User-visible built-ins are composed through `agent-command` packages.
117
- - **Model Command Common APIs** — Provider-neutral `/model` helpers that resolve active provider catalogs and optionally invoke provider-owned refresh hooks
118
- - **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
119
- - **createQuery()** — Provider-bound factory for one-shot AI agent interactions with streaming support
120
- - **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
121
- - **Session assembly** — Internal factory wires tools, provider, config, and context for `InteractiveSession`
122
- - **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
123
- - **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
124
- - **Sandbox Hydration** — Snapshot-capable sandbox clients persist `sandboxSnapshotId` on shutdown and restore it before saved message replay on non-fork resume
125
- - **Agent Tool** — Sub-agent session creation for multi-agent workflows
126
- - **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`
127
- - **Hooks** — `PreToolUse`, `PostToolUse`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `Stop` events with shell command execution
128
- - **Streaming** — Real-time text delta callbacks via `onTextDelta`
129
- - **Context Loading** — AGENTS.md / CLAUDE.md walk-up discovery and system prompt assembly
130
- - **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
131
- - **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
132
- - **Context Reference Inventory** — Manual `/context add` references are stored by `InteractiveSession`, included in future prompt model input, and exposed through SDK command common APIs
133
- - **Config Loading** — 6-file settings merge with provider profiles, legacy provider compatibility, and `$ENV:VAR` substitution for provider credentials
134
- - **Context Window Management** — Token tracking, configurable auto-compaction (default ~83.5%), manual `session.compact()`
135
- - **Background Jobs** — Runtime-managed subagent tasks with transcripts and task snapshots
136
- - **Agent Batch Jobs** — the model-facing Agent tool's `jobs` argument (`Agent` invoked with `{ jobs: [...] }`) starts explicit parallel subagent requests deterministically
137
- - **Edit Checkpoints** — Checkpoint/rewind support for safer edit workflows
138
- - **Project Memory** — Command-driven memory capture and retrieval surfaces
139
- - **Replay Events** — Session execution can forward provider/tool boundary events and provider-native raw payload events into append-only logs
140
- - **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
141
- - **Bundle Plugin System** — Install and manage reusable extensions packaged as bundle plugins
142
-
143
- ## Architecture
78
+ declare const provider: IAIProvider;
144
79
 
145
- ```
146
- agent-framework (assembly layer)
147
- ├── InteractiveSession ← central client-facing API (event-driven)
148
- │ └── Session ← generic session (agent-session)
149
- ├── SystemCommandExecutor ← SDK-level command execution
150
- ├── CommandRegistry / BuiltinCommandSource / SkillCommandSource
151
- ├── Agent runtime dependencies and background orchestration
152
- ├── Edit checkpoints and command-driven memory
153
- ├── createQuery() ← one-shot entry point factory
154
- ├── createSession() ← internal assembly factory
155
- └── deps:
156
- agent-session (Session, neutral session ports, explicit Node host adapters)
157
- agent-file-authority (stable bounded project byte reads over retained native handles)
158
- agent-tools (tool infrastructure + 9 built-in tools)
159
- agent-provider-* (per-vendor AI providers: -anthropic, -openai, -gemini, …)
160
- agent-core (Robota engine, providers, permissions, hooks)
161
-
162
- agent-cli (TUI layer — bridges InteractiveSession events to React/Ink state)
163
- → agent-framework
164
- ```
80
+ const session = new InteractiveSession({
81
+ cwd: process.cwd(),
82
+ provider,
83
+ permissionMode: 'default',
84
+ maxTurns: 10,
85
+ });
165
86
 
166
- 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));
167
96
 
168
- ## 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');
169
102
 
170
- ### InteractiveSession — Central Client-Facing API
103
+ await session.shutdown();
104
+ ```
171
105
 
172
- `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.
173
171
 
174
172
  ```typescript
173
+ import { homedir } from 'node:os';
174
+ import { join } from 'node:path';
175
175
  import {
176
176
  InteractiveSession,
177
+ createNodeWorkspaceTrustService,
177
178
  createProjectSessionStore,
178
179
  getWorkspaceProjectStateStorage,
179
180
  } from '@robota-sdk/agent-framework';
180
181
  import type { IAIProvider } from '@robota-sdk/agent-core';
181
- import type { TWorkspaceProjectAccess } from '@robota-sdk/agent-framework';
182
182
 
183
183
  declare const provider: IAIProvider;
184
- // Supplied by a host-owned WorkspaceTrustService configured with projectStateDirectories
185
- // after identity/trust validation.
186
- declare const projectAccess: TWorkspaceProjectAccess;
187
184
  const cwd = process.cwd();
188
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
+
189
198
  const sessionStore =
190
199
  projectAccess.status === 'trusted'
191
200
  ? createProjectSessionStore(
@@ -194,282 +203,146 @@ const sessionStore =
194
203
  )
195
204
  : undefined;
196
205
 
197
- const session = new InteractiveSession({
198
- cwd,
199
- provider,
200
- projectAccess,
201
- sessionStore,
202
- resumeSessionId: 'sess_123', // Session ID to restore, incl. sandbox snapshot (optional)
203
- forkSession: false, // Fork the resumed session into a new one (optional)
204
- permissionMode: 'default',
205
- maxTurns: 10,
206
- });
207
-
208
- // Subscribe to events
209
- session.on('text_delta', (delta: string) => {
210
- process.stdout.write(delta); // streaming text chunk
211
- });
212
- session.on('tool_start', (state) => {
213
- console.log(`Running: ${state.toolName}`);
214
- });
215
- session.on('tool_end', (state) => {
216
- console.log(`Done: ${state.toolName} — ${state.result}`);
217
- });
218
- session.on('thinking', (isThinking: boolean) => {
219
- // show/hide spinner
220
- });
221
- session.on('complete', (result) => {
222
- console.log(result.response);
223
- });
224
- session.on('error', (error: Error) => {
225
- console.error(error);
226
- });
227
- session.on('context_update', (state) => {
228
- // token usage updated
229
- });
230
- session.on('interrupted', (result) => {
231
- // abort completed
232
- });
233
-
234
- // Submit a prompt. The handle identifies this accepted turn even if it waits in the queue.
235
- const handle = await session.submit('Explain this code', undefined, undefined, {
236
- driverId: 'owner',
237
- });
238
- await handle.completed;
239
-
240
- // Path-like @file references are expanded into model-only prompt context by the SDK.
241
- // The user-visible history keeps the original prompt plus a structured file-reference event.
242
- await session.submit('Explain @AGENTS.md and @docs/SPEC.md');
243
-
244
- // Submit with display override (shown in UI) and raw input (for hook matching)
245
- await session.submit('full expanded prompt…', '/audit', '/rulebased-harness:audit');
246
-
247
- // Execute slash commands through the command layer. With the skills command module composed,
248
- // `/audit src/index.ts` is normalized by SDK to command "skills" with args "audit src/index.ts".
249
- await session.executeCommand('audit', 'src/index.ts');
250
-
251
- // Abort current execution and clear queue
252
- session.abort();
253
-
254
- // Cancel queued prompt without aborting current execution
255
- session.cancelQueue();
256
-
257
- // Execute system commands
258
- const result = await session.executeCommand('context', '');
259
- // result.message, result.success, result.data
260
-
261
- // List all registered system commands
262
- session.listCommands(); // Array<{ name, description }>
263
-
264
- // State queries
265
- session.isExecuting(); // boolean
266
- session.getPendingPrompt(); // string | null
267
- session.getMessages(); // TUniversalMessage[]
268
- session.getContextState(); // IContextWindowState
269
- session.getStreamingText(); // string (accumulated so far)
270
- session.getActiveTools(); // IToolState[]
271
-
272
- // Session naming
273
- session.getName(); // string | undefined
274
- session.setName('my-task'); // sets the session name
275
-
276
- // Access underlying Session for advanced use
277
- session.getSession(); // Session
206
+ const session = new InteractiveSession({ cwd, provider, projectAccess, sessionStore });
278
207
  ```
279
208
 
280
- `WorkspaceTrustService` accepts host-owned identity and trust-store adapters. Its authority is opaque,
281
- runtime-registered, root-bound, and cannot be reconstructed from a path or serialized marker. See the
282
- maintained offline `verify-workspace-project-authority.ts` example for a complete inspect/grant/revoke
283
- composition and Restricted-versus-trusted observable.
284
-
285
- The Node host supplies the production lifecycle through
286
- `createNodeWorkspaceTrustService(trustStorePath, projectStateDirectories)`. The caller chooses the
287
- user-owned store path and all four project-state directories; the Robota CLI uses
288
- `~/.robota/workspace-trust.json` for the trust store. Omitting project-state directories still permits
289
- trust inspection, but deriving project-state storage then fails closed.
290
- The service binds grants to the canonical Git worktree and repository common-directory identity,
291
- stores only owner-readable generation records, and treats non-Git paths, repository replacement,
292
- revocation, and trust-store errors as Restricted. A later provider settings layer that changes `baseURL`
293
- without its own credential also clears inherited `apiKey` and `apiKeyEnv` fields; endpoint provenance
294
- 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.
295
212
 
296
- ### SystemCommandExecutor — SDK-Level Commands
213
+ ## Headless and multi-session runtime
297
214
 
298
- `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:
299
217
 
300
218
  ```typescript
301
- import { SystemCommandExecutor, createSystemCommands } from '@robota-sdk/agent-framework';
302
- import type { ICommandHostContext } from '@robota-sdk/agent-framework';
303
- import type { ICommandResult } from '@robota-sdk/agent-interface-command';
304
-
305
- declare const session: ICommandHostContext;
306
- const executor = new SystemCommandExecutor(); // starts empty unless commands are supplied
307
-
308
- // Execute a command
309
- const result: ICommandResult | null = await executor.execute('status', session, '');
310
- if (result) {
311
- console.log(result.message); // "OK"
312
- }
219
+ import { createAgentRuntime } from '@robota-sdk/agent-framework';
220
+ import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
313
221
 
314
- // Register a custom command
315
- executor.register({
316
- name: 'status',
317
- description: 'Show agent status',
318
- 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 }),
319
225
  });
320
226
 
321
- // List all commands
322
- executor.listCommands(); // ISystemCommand[]
323
- executor.hasCommand('permissions'); // boolean
227
+ // Each call creates a new InteractiveSession that inherits the runtime configuration.
228
+ const session = runtime.createSession({ permissionMode: 'plan' });
324
229
  ```
325
230
 
326
- 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`.
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.
327
236
 
328
- Command modules may use SDK common APIs for shared provider-neutral behavior. For `/model`, the SDK
329
- resolves the active provider from settings, reads provider-owned fallback metadata from injected
330
- `IProviderDefinition` records, and can invoke provider-owned catalog refresh hooks. The CLI/TUI must
331
- only render command results and must not own provider model lists.
237
+ ## Permissions
332
238
 
333
- For provider setup, the SDK projects provider-owned setup help links from `IProviderDefinition`
334
- records into generic prompt descriptions. The CLI/TUI renders those descriptions without owning
335
- provider-specific API key or console URLs.
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:
336
243
 
337
- For `/permissions`, the SDK owns permission-mode constants, subcommand descriptors, validation,
338
- state formatting, and command-host adapter access. The command module owns user-visible behavior and
339
- keeps permission-mode changes under `/permissions [mode]`.
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 |
340
251
 
341
- For `/validate-session`, the session command module calls SDK session command APIs to locate and
342
- validate the current JSONL session log. Hosts may override `validateCurrentSessionReplayLog()` in
343
- `ICommandHostContext` when they own a non-file-backed session log store.
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).
344
258
 
345
- ### CommandRegistry, BuiltinCommandSource, SkillCommandSource
259
+ ## Hooks
346
260
 
347
- These classes provide slash command discovery and aggregation for clients that expose a command palette or autocomplete UI.
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).
348
265
 
349
- ```typescript
350
- import { CommandRegistry, createNodeHostContributionSource } from '@robota-sdk/agent-framework';
351
- import { createSkillsCommandModule } from '@robota-sdk/agent-command';
266
+ This package adds two hook executors to the `command` and `http` executors from `agent-core`:
352
267
 
353
- const registry = new CommandRegistry();
354
- registry.addModule(
355
- createSkillsCommandModule({
356
- contributionSources: [createNodeHostContributionSource(process.cwd())],
357
- }),
358
- );
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 |
359
272
 
360
- // Get all commands (returns ICommand[])
361
- const commands = registry.getCommands();
273
+ ## Commands
362
274
 
363
- // Filter by prefix (for autocomplete)
364
- const filtered = registry.getCommands('mod'); // matches "mode", "model"
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`.
365
280
 
366
- // Resolve short plugin name to fully qualified form
367
- registry.resolveQualifiedName('audit'); // "my-plugin:audit"
368
- ```
369
-
370
- `SkillCommandSource` is the SDK common API used by the skills command module. The host supplies both
371
- the contribution sources and ordered root descriptors; absent sources or roots means no skill file
372
- discovery. This keeps product directory choices out of the framework and lets inspection use the same
373
- roots as activation.
374
-
375
- Model-invocable skills are exposed to the model as metadata only when the session has a composed
376
- model-invocable `skills` command descriptor. `@robota-sdk/agent-command` owns `skills` and
377
- activates skills through the SDK host API. Models use the SDK-projected `command_skills`
378
- tool by default, with skill arguments in `args`. A product host can set
379
- `modelCommandToolPrefix` on its session options to choose another prefix; Robota sets
380
- `robota_command_`. Mentioning a skill in ordinary prose,
381
- recommending a skill in assistant text, or matching a natural-language phrase in SDK/TUI code does
382
- not activate the skill.
383
-
384
- The framework's default prompt enclosure for attached `@file` content is
385
- `<file_references>`. Product hosts can set `promptFileReferenceTag` on session options to
386
- choose a different enclosure; Robota sets `robota_file_references`. SDK consumers that
387
- need the previous identifiers should pass `modelCommandToolPrefix: 'robota_command_'`
388
- and `promptFileReferenceTag: 'robota_file_references'` explicitly. Direct calls to
389
- `createProviderSafeModelCommandToolName`, `createModelCommandToolProjection`, and
390
- `buildPromptWithFileReferences` can pass the same values as their optional arguments.
391
-
392
- ### createQuery()
281
+ A command module is a plain object:
393
282
 
394
283
  ```typescript
395
- import { createQuery } from '@robota-sdk/agent-framework';
396
- 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';
397
287
 
398
- const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
399
- const query = createQuery({ provider });
288
+ declare const provider: IAIProvider;
400
289
 
401
- 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
+ };
402
300
 
403
- const queryWithOptions = createQuery({
301
+ const session = new InteractiveSession({
302
+ cwd: process.cwd(),
404
303
  provider,
405
- cwd: '/path/to/project',
406
- permissionMode: 'acceptEdits',
407
- maxTurns: 10,
408
- onTextDelta: (delta) => process.stdout.write(delta),
304
+ commandModules: [statusModule],
409
305
  });
410
-
411
- const detailedResponse = await queryWithOptions('Analyze the code');
412
- ```
413
-
414
- ### Session Assembly
415
-
416
- `createSession()`, `loadConfig()`, `loadContext()`, and `detectProject()` are internal SDK assembly
417
- details. Use `InteractiveSession` for event-driven sessions or `createQuery()` for prompt-only
418
- one-shot calls.
419
-
420
- ### Built-in Tools
421
-
422
- `@robota-sdk/agent-framework` assembles built-in tools for SDK sessions, but direct tool usage imports
423
- from the owner package:
424
-
425
- Each file tool is a FACTORY that requires the containment root it operates in (ARCH-010) — there is
426
- no ready-made instance, because one bound at import time can carry no root, and a file tool without a
427
- root has no boundary:
428
-
429
- ```typescript
430
- import {
431
- createShellTool,
432
- createBashTool,
433
- createEditTool,
434
- createGlobTool,
435
- createGrepTool,
436
- createReadTool,
437
- createWriteTool,
438
- webFetchTool,
439
- webSearchTool,
440
- askUserQuestionTool,
441
- } from '@robota-sdk/agent-tools';
442
-
443
- const cwd = process.cwd();
444
- const fileTools = [
445
- createShellTool({ cwd }),
446
- createBashTool({ cwd }),
447
- createEditTool({ cwd }),
448
- createGlobTool({ cwd }),
449
- createGrepTool({ cwd }),
450
- createReadTool({ cwd }),
451
- createWriteTool({ cwd }),
452
- ];
306
+ const result = await session.executeCommand('status', '');
307
+ console.log(result?.message);
453
308
  ```
454
309
 
455
- `webFetchTool`, `webSearchTool` and `askUserQuestionTool` remain instances: they touch no filesystem,
456
- so there is no root for them to be contained by.
457
-
458
- ### Sandbox Execution
459
-
460
- 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:
461
-
462
- <!-- doc-example-skip: requires the optional e2b dependency -->
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.
313
+
314
+ ## Subagents
315
+
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`. `runGroupChat` is a facade over
328
+ `@robota-sdk/agent-roundtable`'s `Roundtable`: the core runs the only turn loop, one participant per
329
+ step id, while the neutral contract (`IGroupChatOrchestrationSpec`, `SelectNextStep`) is unchanged.
330
+
331
+ ## Sandbox execution
332
+
333
+ A session can run its commands and file tools in a sandbox through a provider-neutral
334
+ `ISandboxClient` from `@robota-sdk/agent-tools`. `E2BSandboxClient` adapts an E2B sandbox that your
335
+ application creates; `@robota-sdk/agent-framework` does not depend on E2B.
463
336
 
464
337
  ```typescript
465
338
  import { InteractiveSession } from '@robota-sdk/agent-framework';
466
- import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
467
339
  import { E2BSandboxClient } from '@robota-sdk/agent-tools';
468
- import type { IWorkspaceManifest } from '@robota-sdk/agent-tools';
469
- import { Sandbox } from 'e2b';
340
+ import type { IAIProvider } from '@robota-sdk/agent-core';
341
+ import type { IE2BSandboxAdapter, IWorkspaceManifest } from '@robota-sdk/agent-tools';
342
+
343
+ declare const provider: IAIProvider;
344
+ declare const sandbox: IE2BSandboxAdapter; // e.g. `await Sandbox.create()` from the `e2b` package
470
345
 
471
- const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
472
- const sandbox = await Sandbox.create();
473
346
  const workspaceManifest: IWorkspaceManifest = {
474
347
  entries: {
475
348
  'task.md': { type: 'file', content: 'Review this repository.\n' },
@@ -483,154 +356,43 @@ const session = new InteractiveSession({
483
356
  provider,
484
357
  sandboxClient: new E2BSandboxClient({ sandbox }),
485
358
  workspaceManifest,
486
- reversibleExecution: { mode: 'local-first' },
487
359
  });
488
360
  ```
489
361
 
490
- `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`.
362
+ The manifest is applied once before the session starts. With a `sessionStore` and a sandbox client
363
+ that supports snapshots, `shutdown()` stores the sandbox snapshot ID, and resuming the session
364
+ (without forking) restores the sandbox before the saved messages are replayed.
491
365
 
492
- 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.
366
+ ## More capabilities
493
367
 
494
- ## Subagent Sessions
495
-
496
- `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.
497
-
498
- ```typescript
499
- import { createSubagentSession } from '@robota-sdk/agent-framework';
500
- import type { ISubagentOptions } from '@robota-sdk/agent-framework';
501
-
502
- // agentDefinition, parentConfig/parentContext/parentTools, provider, terminal, …
503
- declare const options: ISubagentOptions;
504
- const subSession = createSubagentSession(options);
505
- const result = await subSession.run('Analyze the test coverage gaps');
506
- ```
368
+ | Capability | Exports |
369
+ | --------------------------- | ----------------------------------------------------------------------------------------------------- |
370
+ | Model fallback | `FallbackProvider`, `resolveModelFallbackChain`, `applyModelFallback` |
371
+ | Advisor (second-model tool) | `AdvisorController`, `createAdvisorTool` |
372
+ | Edit checkpoints and rewind | `EditCheckpointStore`, `wrapEditCheckpointTools` |
373
+ | Reversible execution | `evaluateReversibleToolSafety`, `wrapReversibleExecutionTools` (session option `reversibleExecution`) |
374
+ | Project and user memory | `ProjectMemoryStore`, `WorkspaceMemoryStore`, `SemanticMemoryStore`, `listUserLocalMemoryItems` |
375
+ | Evals | `defineEval`, `runEval`, `createSessionRunFn`, matchers such as `exactMatch` and `usedTool` |
376
+ | Bundle plugins | `BundlePluginLoader`, `BundlePluginInstaller`, `MarketplaceClient` |
377
+ | Settings | `readSettings`, `writeSettings`, `inspectSettingsLayers`, `createNodeHostSettingsSource` |
378
+ | Self-hosting verification | `planSelfHostingVerification`, `transitionSelfHostingLoop` |
507
379
 
508
- ### Agent Definitions
380
+ Bundle plugins use the Claude Code plugin layout: a directory with `.claude-plugin/plugin.json`
381
+ (`name`, `version`, `description`, `features`) that can contribute skills, commands, hooks, MCP
382
+ server configuration and agent definitions.
509
383
 
510
- `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.
384
+ ## Settings
511
385
 
512
- Built-in agents: `general-purpose` (full tool access), `Explore` (read-only, Haiku model), `Plan` (read-only planning).
513
-
514
- ### createAgentTool()
515
-
516
- `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.
517
-
518
- When a background job resumes a forked record, the runner passes the record's `resumeSessionId` as
519
- both the child session ID and the persistence key, together with the same session store that holds
520
- the copied record. Each completed child turn then updates that copied record, so `attach` sees the
521
- conversation after the fork as well as the conversation copied at fork time. Ordinary subagent jobs
522
- remain transient when they do not carry `resumeSessionId`; attaching is still a view switch, never a
523
- merge with the parent record.
524
-
525
- 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.
526
-
527
- ## Replay-Grade Session Events
528
-
529
- `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.
530
-
531
- 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.
532
-
533
- ## Interactive Prompt Settlement
534
-
535
- `InteractiveSession` emits transport-neutral `permission_request` and `ask_request` events. Attached
536
- surfaces answer through `resolvePermission()` and `resolveAsk()`; the first answer wins and produces one
537
- `prompt_resolved` event. Session construction does not expose parallel `permissionHandler` or `askHandler`
538
- options, so local and remote surfaces share the same request/settlement path.
539
-
540
- Persisted checkpoint operations emit `branch_event` only after the transition and session save succeed.
541
- Transport-owned listeners must isolate their own delivery failures without changing arbitrary SDK listener
542
- exception behavior.
543
-
544
- ## Hook Executors (SDK-Specific)
545
-
546
- `agent-framework` provides two `IHookTypeExecutor` implementations beyond the `command` and `http` executors in `agent-core`:
547
-
548
- | Executor | Hook Type | Description |
549
- | ---------------- | --------- | ------------------------------------------------------------------------- |
550
- | `PromptExecutor` | `prompt` | Injects the hook's prompt text into the session as a system instruction |
551
- | `AgentExecutor` | `agent` | Creates a sub-agent session to process the hook input and return a result |
552
-
553
- ## Bundle Plugin System
554
-
555
- Bundle plugins package reusable extensions (tools, hooks, permissions, system prompt additions) into installable units.
556
-
557
- ### Types
558
-
559
- | Type | Description |
560
- | ----------------------- | --------------------------------------------------------------- |
561
- | `IBundlePluginManifest` | Plugin metadata: name, version, description, author, keywords |
562
- | `ILoadedBundlePlugin` | Full bundle: manifest + tools, hooks, permissions, systemPrompt |
563
-
564
- ### BundlePluginLoader
565
-
566
- Loads a bundle plugin from a directory path. Reads the manifest, resolves tool/hook definitions, and validates the bundle structure.
567
-
568
- ### BundlePluginInstaller
569
-
570
- Manages plugin installation and uninstallation:
571
-
572
- - Installs bundles to `~/.robota/plugins/` (user) or `.robota/plugins/` (project)
573
- - Tracks installed plugins in a registry file
574
- - Handles enable/disable state per plugin
575
-
576
- ## Plugins
577
-
578
- `@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.
579
-
580
- ```typescript
581
- import { Robota } from '@robota-sdk/agent-core';
582
- import { ConversationHistoryPlugin, LoggingPlugin } from '@robota-sdk/agent-plugin';
583
- import type { IAgentConfig } from '@robota-sdk/agent-core';
584
-
585
- declare const base: IAgentConfig;
586
- const agent = new Robota({
587
- ...base,
588
- plugins: [
589
- new ConversationHistoryPlugin({ storage: 'memory' }),
590
- new LoggingPlugin({ strategy: 'console', level: 'info' }),
591
- ],
592
- });
593
- ```
594
-
595
- 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`.
596
-
597
- ## Configuration
598
-
599
- Settings are merged from lowest to highest priority:
600
-
601
- | Layer | Path | Scope |
602
- | ----- | ----------------------------- | --------------------------------------- |
603
- | 1 | `~/.robota/settings.json` | User global |
604
- | 2 | `~/.claude/settings.json` | User global (Claude Code compatible) |
605
- | 3 | `.robota/settings.json` | Project |
606
- | 4 | `.robota/settings.local.json` | Project (local) |
607
- | 5 | `.claude/settings.json` | Project (Claude Code compatible) |
608
- | 6 | `.claude/settings.local.json` | Project (local, Claude Code compatible) |
609
-
610
- `$ENV:VAR` substitution is applied after merge for provider API keys.
386
+ Settings are JSON files merged in the order the host gives them, later layers winning.
387
+ `$ENV:NAME` values in provider credentials are replaced with the environment variable. The Robota
388
+ CLI reads `~/.robota/settings.json`, `~/.claude/settings.json`, `.robota/settings.json`,
389
+ `.robota/settings.local.json`, `.claude/settings.json` and `.claude/settings.local.json`, in that
390
+ order; project files are read only in a trusted project.
611
391
 
612
392
  ```json
613
393
  {
614
- "defaultMode": "default",
615
- "currentProvider": "qwen",
394
+ "currentProvider": "anthropic",
616
395
  "providers": {
617
- "qwen": {
618
- "type": "qwen",
619
- "model": "qwen-plus",
620
- "apiKey": "$ENV:DASHSCOPE_API_KEY",
621
- "baseURL": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
622
- },
623
- "gemma": {
624
- "type": "gemma",
625
- "model": "supergemma4-26b-uncensored-v2",
626
- "apiKey": "lm-studio",
627
- "baseURL": "http://localhost:1234/v1"
628
- },
629
- "openai": {
630
- "type": "openai",
631
- "model": "<openai-compatible-model>",
632
- "apiKey": "$ENV:OPENAI_API_KEY"
633
- },
634
396
  "anthropic": {
635
397
  "type": "anthropic",
636
398
  "model": "claude-sonnet-4-6",
@@ -644,37 +406,30 @@ Settings are merged from lowest to highest priority:
644
406
  }
645
407
  ```
646
408
 
647
- `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.
648
-
649
- ## Permission Modes
650
-
651
- | Mode | Read/Glob/Grep | Write/Edit | Bash |
652
- | ------------------- | :------------: | :--------: | :-----: |
653
- | `plan` | auto | deny | deny |
654
- | `default` | auto | approve | approve |
655
- | `acceptEdits` | auto | auto | approve |
656
- | `bypassPermissions` | auto | auto | auto |
409
+ `currentProvider` selects the active entry in `providers`. Other keys include `hooks`, `env`,
410
+ `language`, `preset`, `autoCompactThreshold`, `sandbox` and `enabledPlugins`.
657
411
 
658
412
  ## Dependencies
659
413
 
660
- | Package | Purpose |
661
- | -------------------------------------- | ------------------------------------------------------------- |
662
- | `@robota-sdk/agent-core` | Engine, providers, permissions, hooks |
663
- | `@robota-sdk/agent-session` | Session, neutral log/store ports, explicit Node host adapters |
664
- | `@robota-sdk/agent-tools` | Tool infrastructure + built-in tools |
665
- | `@robota-sdk/agent-provider-anthropic` | Anthropic LLM provider |
666
- | `chalk` | Terminal colors (permission prompt) |
667
- | `zod` | Settings schema validation |
414
+ | Package | Used for |
415
+ | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
416
+ | `@robota-sdk/agent-core` | Agent engine, provider contract, permissions, hooks |
417
+ | `@robota-sdk/agent-session` | `Session`, logging and persistence ports |
418
+ | `@robota-sdk/agent-tools` | Tool factories and sandbox clients |
419
+ | `@robota-sdk/agent-tool-defaults` | The default tool set (loaded on first use) |
420
+ | `@robota-sdk/agent-executor` | Background tasks and subagent managers |
421
+ | `@robota-sdk/agent-file-authority` | Bounded, root-relative project file reads |
422
+ | `@robota-sdk/agent-interface-*` (analytics, command, execution, session, session-mobility, transport) | Shared contracts |
423
+ | `yaml`, `zod` | Frontmatter parsing and settings validation |
668
424
 
669
425
  ## Documentation
670
426
 
671
- See [docs/SPEC.md](./docs/SPEC.md) for the full specification, architecture details, and design decisions.
427
+ - [docs/SPEC.md](./docs/SPEC.md) — package contract, boundaries and design decisions
428
+ - [SDK guide](../../content/guide/sdk.md) and [Building agents](../../content/guide/building-agents.md)
429
+ - [Hook event catalog](../agent-core/docs/HOOK-CATALOG.md)
672
430
 
673
- From `packages/agent-framework` in a built repository, run
674
- `pnpm exec tsx examples/verify-goal-cassette-replay.mts` for offline goal replay through the public
675
- testing SDK. The credentialed recorder now lives at `scripts/record-goal-cassette.mts`; it is
676
- non-published development tooling, not a runtime provider dependency. Recording is an explicit
677
- live-provider operation; replay needs no credentials and preserves the committed cassette.
431
+ From `packages/agent-framework` in the repository, `pnpm scenario:verify` runs the offline
432
+ examples (no provider credentials needed).
678
433
 
679
434
  ## License
680
435