@robota-sdk/agent-framework 3.0.0-beta.100

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