@mono-agent/agent-runtime 0.13.0 → 0.14.0

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/ARCHITECTURE.md CHANGED
@@ -24,6 +24,11 @@ a plain-data/plain-function seam the host wires up, not an import.
24
24
 
25
25
  ## Package Boundary
26
26
 
27
+ **Diagram summary:** A host constructs `createRuntime()` or `createRouterRuntime()`.
28
+ The runtime selects one of five provider bridges while sharing observability,
29
+ failure normalization, and capability-specific agent-kernel services. It returns
30
+ a provider-neutral result for the host to interpret.
31
+
27
32
  ```mermaid
28
33
  flowchart TB
29
34
  HostApp["Host app<br/>API / coordinator / worker / UI / DB"] --> CoreAI["host runtime composition"]
@@ -39,6 +44,7 @@ flowchart TB
39
44
  Registry --> ClaudeCLI["Claude Code CLI bridge"]
40
45
  Registry --> PiSDK["Pi SDK bridge<br/>OpenAI, Codex, Gemini, OpenRouter,<br/>Ollama, custom providers"]
41
46
  Registry --> CodexApp["Codex app-server CLI bridge"]
47
+ Registry --> OpenCodeApp["OpenCode app-server CLI bridge"]
42
48
 
43
49
  AgentKernel --> Builtins["Read / Write / Edit / Glob / Grep / Bash<br/>NodeRepl / WebFetch / WebSearch"]
44
50
  AgentKernel --> MCP["MCP stdio / SSE / HTTP tools"]
@@ -49,6 +55,7 @@ flowchart TB
49
55
  ClaudeCLI --> Providers
50
56
  PiSDK --> Providers
51
57
  CodexApp --> Providers
58
+ OpenCodeApp --> Providers
52
59
 
53
60
  Runtime --> Result["RuntimeResult<br/>text, structuredResult, events,<br/>usage, diagnostics, failureKind"]
54
61
  Result --> CoreAI
@@ -61,16 +68,24 @@ and pre-resolved settings into the runtime instead.
61
68
 
62
69
  ## Runtime Selection
63
70
 
71
+ **Diagram summary:** Hosts may use `parseRuntimeModelReference()` to turn a
72
+ canonical string into the object required by `run()`. The static registry then
73
+ matches that object plus execution mode and lazily imports Claude SDK, Claude
74
+ Code CLI, Pi SDK, Codex app-server, or OpenCode app-server code. Capability
75
+ descriptors are available without loading those provider implementations.
76
+
64
77
  ```mermaid
65
78
  flowchart LR
66
- ModelRef["options.model<br/>claude:* / pi:*:* / codex:*"] --> Parse["parseRuntimeModelReference()"]
67
- Parse --> Mode["options.executionMode<br/>sdk or cli"]
79
+ AuthoredRef["authored model string"] --> Parse["parseRuntimeModelReference()"]
80
+ Parse --> ModelRef["options.model<br/>parsed RuntimeModelRef"]
81
+ ModelRef --> Mode["options.executionMode<br/>sdk or cli"]
68
82
  Mode --> Resolve["resolveRuntimeBridge()"]
69
83
 
70
84
  Resolve -->|sdk=claude + sdk mode| ClaudeSDK["claude bridge<br/>@anthropic-ai/claude-agent-sdk"]
71
85
  Resolve -->|sdk=claude + cli mode| ClaudeCLI["claude-code bridge<br/>claude binary"]
72
86
  Resolve -->|sdk=pi| PiSDK["pi bridge<br/>@earendil-works/pi-agent-core"]
73
87
  Resolve -->|sdk=codex + cli mode| CodexApp["codex-app bridge<br/>codex app-server"]
88
+ Resolve -->|sdk=opencode + cli mode| OpenCodeApp["opencode-app bridge<br/>isolated OpenCode server"]
74
89
 
75
90
  Resolve --> Caps["runtimeCapabilities()<br/>static backend features"]
76
91
  Caps --> Used["capabilitiesUsed<br/>per-call observed features"]
@@ -82,6 +97,10 @@ Canonical active model references are:
82
97
  `executionMode`
83
98
  - `pi:<providerId>:<modelName>` for Pi SDK providers
84
99
  - `codex:<modelId>` for Codex app-server CLI
100
+ - `opencode:<providerId>:<modelName>` for the isolated OpenCode app-server CLI
101
+
102
+ `createRuntime().run()` expects this already-parsed object; it does not parse a
103
+ string implicitly.
85
104
 
86
105
  Legacy aliases are canonicalized at host ingress when needed. The strict parser
87
106
  keeps the package boundary honest by rejecting reserved runtime IDs such as
@@ -89,6 +108,11 @@ keeps the package boundary honest by rejecting reserved runtime IDs such as
89
108
 
90
109
  ## Run Lifecycle
91
110
 
111
+ **Diagram summary:** The host calls `run()`, the runtime lazily loads one bridge,
112
+ and that bridge talks to its SDK or subprocess. Supported tool calls pass through
113
+ the shared kernel, provider events are normalized, and the host receives a result
114
+ that it must validate for its domain.
115
+
92
116
  ```mermaid
93
117
  sequenceDiagram
94
118
  participant Host as Host app
@@ -105,15 +129,19 @@ sequenceDiagram
105
129
  Runtime->>Observer: create hub from host + call observers
106
130
  Runtime->>Bridge: execute(systemPrompt, normalized options)
107
131
 
108
- Bridge->>Kernel: prepare tools, MCP, approvals, limits
109
- Kernel-->>Bridge: provider-specific tool surface
132
+ opt bridge supports managed or MCP tool dispatch
133
+ Bridge->>Kernel: prepare tools, MCP, approvals, limits
134
+ Kernel-->>Bridge: provider-specific tool surface
135
+ end
110
136
  Bridge->>Provider: send prompt, messages, tools, schema, settings
111
137
 
112
138
  loop streaming events
113
139
  Provider-->>Bridge: assistant/tool/result/provider events
114
140
  Bridge->>Observer: normalized runtime events
115
- Bridge->>Kernel: execute built-in/MCP tools as needed
116
- Kernel-->>Bridge: tool results or tool errors
141
+ opt provider requests host-dispatched tools
142
+ Bridge->>Kernel: execute built-in/MCP tools
143
+ Kernel-->>Bridge: tool results or tool errors
144
+ end
117
145
  end
118
146
 
119
147
  Bridge-->>Runtime: RuntimeResult
@@ -122,12 +150,20 @@ sequenceDiagram
122
150
  Host->>Host: validate/parse host-specific contract
123
151
  ```
124
152
 
125
- The package forwards provider structured output as `structuredResult`, but it
126
- does not validate that output against a host domain schema. Hosts own that
127
- validation and any state-machine side effects.
153
+ Claude SDK, Claude CLI, and Pi SDK can return provider-captured output as
154
+ `structuredResult`. Codex app-server receives the schema but returns its output
155
+ as text for the host to parse; direct OpenCode rejects the option. The package
156
+ does not validate any captured output against a host domain schema. Hosts own
157
+ that validation and all state-machine side effects.
128
158
 
129
159
  ## Main Subsystems
130
160
 
161
+ **Diagram summary:** The public barrels lead to the runtime factory, fallback
162
+ router, AI registry, and agent-kernel helpers. The registry owns five lazy
163
+ provider loaders. Shared agent modules own tools, context, approvals,
164
+ compaction, transcript snapshots, and result-size guards; shared AI modules own
165
+ failure, cost, observation, and capability metadata.
166
+
131
167
  ```mermaid
132
168
  flowchart TB
133
169
  Public["Public API<br/>src/index.js"] --> RuntimeFactory["runtime.js<br/>createRuntime()"]
@@ -142,6 +178,7 @@ flowchart TB
142
178
  Providers --> ClaudeCode["claude-cli.js"]
143
179
  Providers --> Pi["pi-native.js<br/>pi-models/messages/events"]
144
180
  Providers --> Codex["codex-app.js"]
181
+ Providers --> OpenCode["opencode-app.js<br/>opencode-server.js"]
145
182
 
146
183
  AgentExports --> Tools["agent/tools/*"]
147
184
  Tools --> ToolContext["shared/tool-context.js<br/>per-instance ToolContext<br/>workspace, repoRoot, rg, sandbox, brand"]
@@ -164,8 +201,9 @@ Key responsibilities by subsystem:
164
201
  - `runtime.js`: binds host callbacks once, builds a per-instance `ToolContext`
165
202
  (`agent/tools/shared/tool-context.js`) threaded to every bridge call via
166
203
  `options.toolContext`, and routes each call to the resolved bridge.
167
- - `ai/runtime/registry.js`: maps model reference plus execution mode to one of
168
- the built-in provider bridges.
204
+ - `ai/runtime/registry.js`: keeps the five static bridge descriptors, exposes
205
+ their metadata for introspection, and lazily imports the one whose model
206
+ reference plus execution mode matches a run.
169
207
  - `ai/runtime/router.js`: retries across an ordered fallback chain on retryable
170
208
  provider failures, carrying a transcript-tail resume snapshot forward.
171
209
  - `ai/providers/*`: owns provider-specific request shapes, event conversion,
@@ -199,6 +237,11 @@ Key responsibilities by subsystem:
199
237
 
200
238
  ## Host Responsibilities
201
239
 
240
+ **Diagram summary:** The host injects pricing, Pi credentials, artifact and
241
+ compaction persistence, tool-approval decisions, runtime branding, and allowed
242
+ filesystem roots. The runtime returns raw normalized data; the host owns domain
243
+ validation, durable state, and UI effects.
244
+
202
245
  ```mermaid
203
246
  flowchart LR
204
247
  Host["Host app"] --> Pricing["resolveCustomPricing"]
@@ -265,22 +308,21 @@ compaction fired), `false` (enabled but not needed), or `null` (disabled via
265
308
 
266
309
  | Provider | Warm session | Resume across turns | Survives process restart |
267
310
  |---|---|---|---|
268
- | **pi** | Yes (pi `AgentHarness` + JSONL session repo) | session repo | **Yes** (only one) |
311
+ | **pi** | Yes (pi `AgentHarness` + JSONL session repo) | session repo | Yes only with `piSessionsRoot` and the durable history/session transaction contract |
269
312
  | **claude-sdk** | No persistent process (stream closes at turn end) | `queryOptions.resume` | No (Anthropic-side id) |
270
313
  | **claude-cli** | No — respawns `claude --resume` per turn (re-inits MCP) | `--resume` replay | No |
271
314
  | **codex-app** | Live subprocess thread (dies with the subprocess) | next turn on the thread, else replay | No |
315
+ | **opencode-app** | No — every run uses an isolated server and private database | Unsupported | No |
272
316
 
273
- claude-cli and codex only *approximate* a warm session (resume/replay), so do
274
- not assume warm-session latency wins there. Recall (memory embeddings) is bounded
275
- by a timeout + circuit breaker and degrades to empty (with a `memory_degraded`
276
- warning) rather than blocking or failing a turn; selected skills are mtime-cached
277
- across turns.
317
+ Claude CLI and Codex only *approximate* a warm session (resume/replay), so do
318
+ not assume warm-session latency wins there. Direct OpenCode is intentionally
319
+ stateless across runs.
278
320
 
279
321
  ## Essential Takeaway
280
322
 
281
323
  Think of `@mono-agent/agent-runtime` as the portable agent process engine
282
324
  underneath a host app. The host decides what a task means, which agent should
283
325
  run, how state changes, and how results are persisted. The runtime decides how
284
- to talk to Claude, Pi, and Codex execution surfaces; how tools are exposed; how
285
- provider failures are normalized; and how enough telemetry is returned for a
286
- host to make reliable orchestration decisions.
326
+ to talk to Claude, Pi, Codex, and OpenCode execution surfaces; how tools are
327
+ exposed; how provider failures are normalized; and how enough telemetry is
328
+ returned for a host to make reliable orchestration decisions.
package/MIGRATION.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # `@mono-agent/agent-runtime` — Migration Guide
2
2
 
3
- Breaking and behavioral changes for consumers upgrading **from `0.3.x`** (the
4
- `feat/runtime-live-sessions` line). The public entry points are unchanged —
5
- `createRuntime` / `createMonoRuntime`, the run-options contract, and provider
6
- session support (`sessionId` in, `provider_session_id` out, `disposeSession` /
7
- `disposeAllSessions`) all keep their shapes. The changes below affect the **Pi
8
- runtime bridge, a few run options, durable-session semantics, the fallback
9
- router, and some diagnostics**.
3
+ Breaking and behavioral changes for consumers upgrading from `0.3.x` to the
4
+ current `0.13.x` contract. `createRuntime()` remains the package entry point;
5
+ `createMonoRuntime()` remains the typed facade in
6
+ `@mono-agent/runtime-adapter`. Provider-session input/output uses
7
+ `providerSessionId`, with `disposeSession()` and `disposeAllSessions()` retained.
10
8
 
11
- If you only use the Claude SDK / Claude CLI / Codex backends and do not touch Pi
12
- or durable sessions, this is a no-op upgrade.
9
+ Review every section that matches your usage. The package now has an explicit
10
+ exports map, a five-bridge lazy registry, typed policy objects, stricter sandbox
11
+ behavior, and revised provider-session semantics even when Pi is not your
12
+ primary route.
13
13
 
14
14
  ---
15
15
 
@@ -23,13 +23,14 @@ registry resolves `pi` → the native bridge unconditionally; there is no
23
23
  - **Public runtime API** (`createRuntime`, model reference `"pi:<provider>:<model>"`)
24
24
  is unchanged — `pi:openai:gpt-5.5` etc. still work.
25
25
  - **Deep imports** of `@mono-agent/agent-runtime/ai/providers/pi-sdk.js` **no
26
- longer resolve**: the deprecated compatibility shim was removed and, with the
27
- Phase-6 explicit `exports` map (no `./ai/*` / `./agent/*` wildcards), that
28
- subpath is not exported. **Action:** import `generatePiNativeResponse` /
29
- `piNativeRuntimeBridge` from `./ai`, and
30
- `isContextLimitError` / `normalizePiErrorMessage` from `./ai/providers/pi-errors.js`
31
- (or reach for the public runtime registry). The `pi*Backend` aliases are gone
32
- all Pi routes through the one native bridge.
26
+ longer resolve**: the compatibility shim was removed and the explicit exports
27
+ map has no provider wildcard. **Action:** import
28
+ `generatePiNativeResponse` / `piNativeRuntimeBridge` from
29
+ `@mono-agent/agent-runtime/ai`, or select Pi through the public runtime
30
+ registry. `pi-errors.js` is internal and is not an exported replacement; use
31
+ the normalized `RuntimeResult.failureKind` or the public failure helpers at
32
+ `@mono-agent/agent-runtime/ai/failure.js`. The `pi*Backend` aliases are gone
33
+ all Pi routes through the native bridge.
33
34
 
34
35
  ## 2. Removed run options: `piReasoningSummary`, `piCodexTransport`
35
36
 
@@ -37,10 +38,9 @@ These were Pi-bridge knobs the native path does not consume.
37
38
 
38
39
  - `piReasoningSummary` is **no longer read** and was removed from the run-options
39
40
  type. Pi-native derives reasoning from `effort` (`thinkingLevel`); the
40
- codex/claude CLIs emit reasoning summaries on their own. **Action:** stop
41
- passing `piReasoningSummary` it was already a no-op on the native path; remove
42
- it from your call sites. (Host config `runtime.reasoningSummary` still validates
43
- for back-compat but is not wired to a runtime option.)
41
+ Codex and Claude CLI bridges emit their own reasoning events. **Action:**
42
+ remove `piReasoningSummary` from call sites. The former
43
+ `runtime.reasoningSummary` config field has also been removed.
44
44
  - `piCodexTransport` was doc-only and is removed. No replacement is needed.
45
45
 
46
46
  ## 3. Pi context compaction: bridge-driven via AgentHarness.compact()
@@ -67,15 +67,15 @@ These were Pi-bridge knobs the native path does not consume.
67
67
 
68
68
  ## 4. Durable Pi session resume: create-on-miss semantics
69
69
 
70
- When a run supplies a `sessionId` **and** durable storage is configured
71
- (`piSessionsRoot`), Pi-native now **creates the session with that id if no
72
- on-disk JSONL exists** (create-on-miss), instead of returning
70
+ When a run supplies a `providerSessionId` (or the legacy `sessionId` alias) **and**
71
+ durable storage is configured (`piSessionsRoot`), Pi-native now **creates the
72
+ session with that id if no on-disk JSONL exists** (create-on-miss), instead of returning
73
73
  `session_not_found`. An existing JSONL is reopened and resumed as before.
74
74
 
75
75
  This makes a **stable, conversation-derived session id resume across process
76
76
  restarts** (the on-disk transcript is the durable history; the in-memory
77
77
  conversation→session map is no longer required to resume). **Action:** if you
78
- passed an arbitrary `sessionId` to a durable run expecting a hard
78
+ passed an arbitrary `providerSessionId` to a durable run expecting a hard
79
79
  `session_not_found` on first use, note it now succeeds by creating that session.
80
80
  The in-memory (non-durable) resume path still fast-fails `session_not_found` on a
81
81
  miss.
@@ -155,9 +155,7 @@ and that group's legacy `settings` keys are ignored; an absent typed object lets
155
155
  its group's `settings` keys through as a fallback. Consuming **any** legacy
156
156
  `settings` key emits exactly one `runtime_warning` with
157
157
  **`warning_kind: "deprecated_settings_option"`** per run (listing the consumed
158
- keys). Passing no `settings` — or an empty/irrelevant bag — never warns, so a host
159
- that never passed `settings` is byte-for-byte unchanged (mono-agent hosts do not
160
- pass it, so this is a no-op there).
158
+ keys). Passing no `settings` — or an empty/irrelevant bag — never warns.
161
159
 
162
160
  `resolveAgentCompactionPolicy(settings, model)` stays exported (the canonical
163
161
  clamp/mapper both paths route through), and `@mono-agent/runtime-adapter` exposes
@@ -241,18 +239,18 @@ The package exposes **22 named deep `.js` subpaths**:
241
239
 
242
240
  **Action:** if you deep-import a subpath not in this list, switch to the closest
243
241
  supported one, a barrel (`./ai` / `./agent`), or the public runtime registry.
244
- `pi-sdk.js` is gone and remains intentionally unexported (section 1). Worklab
245
- ports should import `generatePiNativeResponse` from `@mono-agent/agent-runtime/ai`
246
- instead of adding a `pi-sdk.js` compatibility subpath.
242
+ `pi-sdk.js` is gone and remains intentionally unexported (section 1). Import
243
+ `generatePiNativeResponse` from `@mono-agent/agent-runtime/ai` instead of adding
244
+ a compatibility subpath.
247
245
 
248
246
  ---
249
247
 
250
248
  ## Version
251
249
 
252
- These changes ship in the first `agent-runtime` release after `0.3.0` on the
253
- `feat/runtime-live-sessions` line (a minor/major bump; see the release tag). The
254
- paired `@mono-agent/runtime-adapter` drops the `piReasoningSummary` field from its
255
- run-options type in lockstep.
250
+ This guide describes the published `0.13.x` package contract. Keep
251
+ `@mono-agent/agent-runtime`, `@mono-agent/runtime-adapter`, and other
252
+ `@mono-agent/*` packages on the same lockstep version when upgrading. The paired
253
+ runtime adapter no longer exposes `piReasoningSummary` in its run-options type.
256
254
 
257
255
  ---
258
256
 
package/README.md CHANGED
@@ -1,15 +1,104 @@
1
1
  # @mono-agent/agent-runtime
2
2
 
3
+ Use this package when you need direct, capability-aware access to mono-agent's
4
+ five built-in model runtime bridges.
5
+
3
6
  ## Category
4
7
 
8
+ <!-- package-metadata:start -->
9
+ <!-- Generated by scripts/generate-package-docs.mjs. Do not edit by hand. -->
10
+
5
11
  Category: `runtime`
12
+ Tier: `core`
13
+ Catalog responsibility: Provides five runtime bridges (Claude SDK, Claude Code CLI, Codex app-server, OpenCode app-server, Pi SDK); direct OpenCode requires stable CLI >=1.15.0 on PATH.
14
+
15
+ <!-- package-metadata:end -->
6
16
 
7
17
  ## Responsibility
8
18
 
9
19
  Provides five runtime bridges (Claude SDK, Claude Code CLI, Codex app-server, OpenCode app-server, Pi SDK), with capabilities declared per bridge. This is the runtime layer that `@mono-agent/runtime-adapter` wraps behind runtime contracts. Pi enforces optional mono-agent sandbox policy for runtime-owned tools through an injectable `RuntimeSandbox` seam (a fail-closed passthrough by default; `@mono-agent/runtime-adapter` injects the real implementation). The router supports a compatibility-preserving uniform contract or explicit isolated per-route-native contracts; no provider route silently drops required capabilities.
10
20
 
21
+ ## Install / Usage
22
+
23
+ ```bash
24
+ pnpm add @mono-agent/agent-runtime
25
+ ```
26
+
27
+ Node.js 22.19 or newer is required. The Claude Code, Codex, and direct
28
+ OpenCode bridges also require their matching CLI on `PATH`; direct OpenCode
29
+ requires stable OpenCode 1.15.0 or newer. SDK-only Claude and Pi runs do not
30
+ spawn those CLIs.
31
+
32
+ Create one runtime for a host, parse a model reference, and run a turn:
33
+
34
+ ```js
35
+ import {
36
+ createRuntime,
37
+ parseRuntimeModelReference,
38
+ } from "@mono-agent/agent-runtime";
39
+
40
+ const runtime = createRuntime({ workspace: process.cwd() });
41
+ const result = await runtime.run("You are a concise repository assistant.", {
42
+ model: parseRuntimeModelReference("claude:claude-sonnet-4-6"),
43
+ executionMode: "sdk",
44
+ messages: [{ role: "user", content: "Summarize README.md." }],
45
+ cwd: process.cwd(),
46
+ allowedTools: ["Read"],
47
+ });
48
+
49
+ if (result.error) throw new Error(result.error);
50
+ console.log(result.text);
51
+ ```
52
+
53
+ `Glob` and `Grep` prefer the packaged `@vscode/ripgrep` binary on supported
54
+ platforms. An explicit `ripgrepPath` wins, with `PATH` as the final fallback.
55
+
56
+ ## Architecture
57
+
58
+ The package uses a fixed registry of bridge descriptors and loads provider code
59
+ only after a run selects a matching model reference and execution mode:
60
+
61
+ ### Data flow
62
+
63
+ 1. `createRuntime()` binds host callbacks and creates an isolated tool context.
64
+ 2. `resolveRuntimeBridge()` checks the five static bridge descriptors in order.
65
+ 3. The selected descriptor lazily imports its provider implementation.
66
+ 4. The bridge prepares the runtime inputs it supports, including managed or MCP
67
+ tools only where that bridge can represent them, streams normalized events,
68
+ and returns a provider-neutral `RuntimeResult`.
69
+ 5. The host validates any domain-specific result and owns persistence or UI
70
+ effects.
71
+
72
+ ### Package structure
73
+
74
+ | Source area | Responsibility |
75
+ | --- | --- |
76
+ | `src/runtime.js` | Host binding, per-instance tool context, bridge dispatch, and observer flushing |
77
+ | `src/ai/runtime/` | Model-reference parsing, the lazy bridge registry, capabilities, sessions, and fallback routing |
78
+ | `src/ai/providers/` | Claude SDK/CLI, Codex app-server, OpenCode app-server, and Pi SDK integrations |
79
+ | `src/agent/tools/` | Managed tools, MCP adaptation, output limits, and the injectable sandbox seam |
80
+ | `src/agent/` | Approvals, allowlists, transcript snapshots, and compaction policy helpers |
81
+
82
+ The detailed lifecycle, provider-session differences, and host boundary are in
83
+ the [architecture guide](https://github.com/robertsreberski/mono-agent/blob/main/packages/agent-runtime/ARCHITECTURE.md).
84
+
11
85
  ## Public API
12
86
 
87
+ ### Start here
88
+
89
+ | API | Use it for |
90
+ | --- | --- |
91
+ | `createRuntime()` | Run one model bridge with host-owned credentials, observers, tools, and lifecycle callbacks |
92
+ | `createRouterRuntime()` | Retry an ordered model chain while preserving explicit route-safety contracts |
93
+ | `parseRuntimeModelReference()` | Convert a canonical `claude:`, `codex:`, `opencode:`, or `pi:` string into the object required by `run()` |
94
+ | `listRuntimeBridges()` / `runtimeCapabilities()` | Inspect the five built-in bridge descriptors without loading provider implementations |
95
+ | `createPiOAuthApiKeyResolver()` | Bind a host-owned Pi auth file with refresh-safe writes |
96
+ | `createMetricsObserver()` | Aggregate normalized event, token, cache, cost, tool, error, turn, and approval metrics |
97
+
98
+ Most hosts should use `@mono-agent/runtime-adapter` instead of importing deep
99
+ runtime surfaces. The exhaustive inventory below is generated from the package
100
+ export map.
101
+
13
102
  <!-- public-api-inventory:start -->
14
103
  <!-- Generated by scripts/generate-public-api-docs.mjs. Do not edit by hand. -->
15
104
 
@@ -403,78 +492,7 @@ normalizeCodexItemType
403
492
 
404
493
  <!-- public-api-inventory:end -->
405
494
 
406
- ## Dependency Boundary
407
-
408
- Depends on external provider SDKs only (`@anthropic-ai/claude-agent-sdk`, `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, `@modelcontextprotocol/sdk`, `@opencode-ai/sdk`, `zod`) — **zero `@mono-agent/*` workspace-package dependencies**. Sandbox enforcement for runtime-owned command preparation and network/path policy checks is an injectable `RuntimeSandbox` seam; `@mono-agent/runtime-adapter` wires in the real sandbox implementation automatically for mono-agent hosts.
409
-
410
- ## What This Package Does Not Own
411
-
412
- - runtime contracts and backend descriptors (`@mono-agent/runtime-adapter`)
413
- - Conversation history, context building, or host-side session TTL policy (`@mono-agent/agent-harness`)
414
- - Host configuration (`@mono-agent/config`, `@mono-agent/agent-app`)
415
-
416
- ## Verification
417
-
418
- ```bash
419
- pnpm --filter @mono-agent/agent-runtime run test
420
- ```
421
-
422
- ## Overview
423
-
424
- Generic agent runtime that supports five bridges out of the box:
425
-
426
- - **Claude SDK** (`@anthropic-ai/claude-agent-sdk` 0.3.206)
427
- - **Claude Code CLI** (the `claude` binary)
428
- - **Pi SDK** (`@earendil-works/pi-agent-core`, used for OpenAI / Codex / Gemini / OpenRouter / Ollama / etc. via Pi providers)
429
- - **Codex CLI** (the `codex` app-server)
430
- - **OpenCode CLI** (an isolated `opencode` app-server driven through `@opencode-ai/sdk/v2`)
431
-
432
- Hosts wire in their own pricing, persistence, and credential callbacks (plus an `onCompactionRecorded` hook that fires on every automatic compaction — proactive or reactive — the pi bridge drives; see "Context compaction"). The runtime returns raw text + raw structured output; hosts that want a domain-specific contract parse it on their end.
433
-
434
- See [ARCHITECTURE.md](./ARCHITECTURE.md) for the package boundary, runtime
435
- selection flow, lifecycle diagrams, and host responsibilities. Upgrading from
436
- `0.3.x`? See [MIGRATION.md](./MIGRATION.md) for the Pi-native bridge, removed run
437
- options, durable-session resume semantics, and fallback-router changes.
438
-
439
- ## Install / Usage
440
-
441
- ```bash
442
- npm install @mono-agent/agent-runtime
443
- ```
444
-
445
- Peer requirements:
446
-
447
- - Node.js ≥ 22.19.0
448
- - `claude` CLI on PATH (only for `executionMode: "cli"` with `claude` SDK)
449
- - `codex` CLI on PATH (only for `executionMode: "cli"` with `codex` SDK; override via the `codexAppServerCommand` option)
450
- - stable `opencode` CLI >= 1.15.0 on PATH (only for direct `opencode:<provider>:<model>` refs)
451
- - `Glob` and `Grep` use the packaged `@vscode/ripgrep` binary on supported platforms. An explicit `ripgrepPath` is authoritative and PATH remains a fallback; provide one of those when optional dependencies are omitted or the platform is unsupported.
452
-
453
- ## Quick start
454
-
455
- ```js
456
- import { createRuntime } from "@mono-agent/agent-runtime";
457
-
458
- const runtime = createRuntime({
459
- // Host integration (all optional)
460
- workspace: "/path/to/repo",
461
- ripgrepPath: "/usr/bin/rg",
462
- });
463
-
464
- const result = await runtime.run("You are a helpful assistant.", {
465
- model: { sdk: "claude", model: "claude-sonnet-4-6" },
466
- executionMode: "sdk",
467
- messages: [{ role: "user", content: "Read README.md and summarize it." }],
468
- cwd: "/path/to/repo",
469
- allowedTools: ["Read", "Bash"],
470
- maxTurns: 10,
471
- onEvent: (event) => console.log(event.type),
472
- });
473
-
474
- console.log(result.text);
475
- ```
476
-
477
- ## When to reach for this vs. other JS agent runtimes
495
+ ### When to reach for this vs. other JS agent runtimes
478
496
 
479
497
  `@mono-agent/agent-runtime` is purpose-built for **autonomous, long-running agent work** with provider portability and operational resilience as first-class concerns. It is *not* a streaming-chat UI kit. Where each peer fits:
480
498
 
@@ -510,7 +528,7 @@ console.log(result.text);
510
528
 
511
529
  Honest summary: if the agent runs **without a human watching the screen** for minutes-to-hours and **must survive provider blips**, this is the right tool. If a human is watching a streaming chat, Vercel's SDK is the right tool. Both can coexist in the same app.
512
530
 
513
- ## Picking a backend
531
+ ### Picking a backend
514
532
 
515
533
  The runtime picks a backend from `options.model` + `options.executionMode`:
516
534
 
@@ -518,13 +536,15 @@ The runtime picks a backend from `options.model` + `options.executionMode`:
518
536
  |---|---|---|
519
537
  | `"claude"` | `"sdk"` (or omitted) | Claude SDK |
520
538
  | `"claude"` | `"cli"` | `claude` CLI |
521
- | `"pi"` | any | Pi SDK |
539
+ | `"pi"` | `"sdk"` (or omitted) | Pi SDK |
522
540
  | `"codex"` | `"cli"` | Codex app-server CLI |
523
541
  | `"opencode"` | `"cli"` | Isolated OpenCode app-server CLI |
524
542
 
525
- A `model` reference can be the parsed shape `{ sdk, model, provider? }` or a string (`"pi:openai:gpt-5.5"`, `"claude:claude-sonnet-4-6"`, etc.) that you parse with the package's `parseRuntimeModelReference` helper.
543
+ A `model` is a parsed `{ sdk, model, provider? }` object. Convert canonical
544
+ strings such as `"pi:openai:gpt-5.5"` with
545
+ `parseRuntimeModelReference()` before calling `run()`.
526
546
 
527
- ## `createRuntime(host)`
547
+ ### `createRuntime(host)`
528
548
 
529
549
  Pass host-level integration once at boot. All keys are optional.
530
550
 
@@ -608,13 +628,13 @@ Returns:
608
628
  - `retireDurableSession(id, sessionsRoot)` — delete and verify every exact-id durable Pi transcript, including cold duplicates.
609
629
  - `disposeSession(id)` / `invalidateSession(id)` / `disposeAllSessions()` — ordinary best-effort eviction, destructive live invalidation, and shutdown cleanup.
610
630
 
611
- ### `runtime.run(systemPrompt, options)`
631
+ #### `runtime.run(systemPrompt, options)`
612
632
 
613
633
  Per-call options (a non-exhaustive selection):
614
634
 
615
635
  | Option | Type | Notes |
616
636
  |---|---|---|
617
- | `model` | `object \| string` | **Required.** See "Picking a backend". |
637
+ | `model` | `RuntimeModelRef` | **Required.** Pass the object returned by `parseRuntimeModelReference()`; `run()` does not parse strings. |
618
638
  | `executionMode` | `"sdk" \| "cli"` | Default `"sdk"`. |
619
639
  | `messages` | `Message[]` | Conversation history. |
620
640
  | `cwd` | `string` | Working directory for the agent's tools. |
@@ -623,9 +643,9 @@ Per-call options (a non-exhaustive selection):
623
643
  | `mcpServers` | `Record<string, McpServerConfig>` | Configured MCP servers (stdio / sse / http). |
624
644
  | `sandboxPolicy` | `SandboxPolicy` | Optional fail-closed sandbox policy for built-in tools and stdio MCP process startup. |
625
645
  | `maxTurns` | `number` | Hard cap on agent turns. |
626
- | `outputSchema` | `JSONSchema` | If set, the agent is asked to produce structured JSON matching this schema. The result lands in `result.structuredResult`. |
646
+ | `outputSchema` | `JSONSchema` | Requests structured JSON on capable bridges; see “Structured output” below for bridge-specific return behavior. |
627
647
  | `abortSignal` | `AbortSignal` | Cancel the run. |
628
- | `liveInput` | `LiveInputQueue` | Stream of in-flight user messages (for human-in-the-loop steering). |
648
+ | `liveInput` | `AsyncIterable<{ body: string; id?: string }>` | Stream of in-flight user messages for steering on capable bridges. |
629
649
  | `onEvent` | `(event) => void` | Fired for every event the provider emits (assistant text, tool calls/results, runtime warnings, structured output). |
630
650
  | `runId` | `string` | Tag this run for downstream callbacks (e.g. `onCompactionRecorded`). |
631
651
  | `providerSessionId` | `string` | Resume a prior provider session. |
@@ -638,7 +658,7 @@ Returns:
638
658
  ```ts
639
659
  {
640
660
  text: string, // raw assistant text
641
- structuredResult?: any, // JSON returned via outputSchema (if any)
661
+ structuredResult?: any, // captured JSON on supported bridges
642
662
  structuredResultSource?: string, // where structuredResult came from
643
663
  events: RuntimeEvent[], // full event stream (for host-side parsing)
644
664
  usage: {
@@ -650,7 +670,7 @@ Returns:
650
670
  numTurns: number,
651
671
  model: string,
652
672
  effort: string,
653
- sdk: "claude" | "pi" | "codex",
673
+ sdk: "claude" | "pi" | "codex" | "opencode",
654
674
  cancelled: boolean,
655
675
  error: string | null,
656
676
  errorDetails: object | null,
@@ -673,7 +693,7 @@ Returns:
673
693
 
674
694
  `capabilitiesUsed` is the per-call complement to `runtimeCapabilities()`. Tristate fields use `null` to mean "this provider can't tell" — distinct from `false` ("definitely off"). It's also emitted as a `capabilities_resolved` event near the end of the run, so observers can capture it without inspecting the result object.
675
695
 
676
- ## Built-in tools
696
+ ### Built-in tools
677
697
 
678
698
  The agent kernel's managed tools are `Read`, `Write`, `Edit`, `Glob`, `Grep`, `Bash`, `NodeRepl`, `WebFetch`, and `WebSearch`. `NodeRepl({ code })` is backed by one lazily started Node.js REPL child per run. You select them via `allowedTools`. Tool implementations honor:
679
699
 
@@ -685,13 +705,19 @@ The agent kernel's managed tools are `Read`, `Write`, `Edit`, `Glob`, `Grep`, `B
685
705
 
686
706
  Override or extend the tool surface by passing `mcpServers` for MCP-backed tools.
687
707
 
688
- ## Structured output
708
+ ### Structured output
689
709
 
690
- Pass `options.outputSchema` (a JSON Schema). On Claude SDK / Codex app-server / Pi SDK, the runtime wires the schema into the provider's structured-output API. The matched JSON lands in `result.structuredResult`.
710
+ Pass `options.outputSchema` (a JSON Schema). Claude SDK, Claude CLI, and Pi SDK
711
+ surface captured JSON as `result.structuredResult`. Codex app-server receives
712
+ the schema and reports that structured output was enforced, but its bridge
713
+ returns provider text rather than parsing `structuredResult`; hosts must parse
714
+ and validate `result.text`. Direct OpenCode rejects `outputSchema` with a typed
715
+ capability mismatch.
691
716
 
692
- The package does **not** validate `structuredResult` against your schema — it only forwards what the provider produced. Hosts run their own validation (Zod, AJV, etc.).
717
+ The package does **not** validate captured output against your schema. Hosts run
718
+ their own validation (Zod, AJV, and similar) before applying domain effects.
693
719
 
694
- ## Provider fallback router
720
+ ### Provider fallback router
695
721
 
696
722
  `createRouterRuntime({ host, chain, routeSafety, resolveAttempt })` wraps the standard runtime with an ordered chain of model references. On a retryable provider/auth failure it retries the logical run against the next entry with one bounded transcript-tail snapshot. A chain is stateless across provider sessions. Entry `effort` is tri-state: a string fixes that route, `null` asks for provider default, and omission inherits the legacy per-run effort.
697
723
 
@@ -736,7 +762,7 @@ Behaviour:
736
762
 
737
763
  Chain entries can require backend capabilities via `requires: { structured_output: true, supports_mcp: true, ... }`; entries that don't satisfy the requirements are skipped (logged in `failoverHistory` as `failureKind: "skipped_capability_mismatch"`).
738
764
 
739
- ## Observers & metrics
765
+ ### Observers & metrics
740
766
 
741
767
  The runtime emits structured events for everything that happens during a run — assistant messages, tool calls, runtime warnings, cache hits/misses, cost updates, provider request start/end, approval lifecycle. Hosts can subscribe via `host.observers[]` (any number) or the simpler `options.onEvent` callback (one subscriber). Both work simultaneously.
742
768
 
@@ -771,7 +797,7 @@ Notable new events emitted by the bridges:
771
797
  - `cache_hit` / `cache_miss` — when the provider reports cached / cache-creation input tokens.
772
798
  - `cost_accumulated` — running cost in USD with cumulative token breakdown.
773
799
 
774
- ## Approval gates (human-in-the-loop)
800
+ ### Approval gates (human-in-the-loop)
775
801
 
776
802
  Pass `onToolApprovalRequest` to gate tool calls behind a runtime approval. The runtime calls your callback once per tool invocation whose risk tier requires it, and pauses the agent until you respond.
777
803
 
@@ -812,7 +838,7 @@ Approval lifecycle is observable via `onEvent`:
812
838
  - `tool_approval_granted` — host approved.
813
839
  - `tool_approval_denied` — host denied, timed out, threw, or no callback for a high-risk tool.
814
840
 
815
- ## Tool-result bloat handling
841
+ ### Tool-result bloat handling
816
842
 
817
843
  The kernel's tool-bloat guard (`agent/tool-bloat.js`, internal) enforces a 256 KB default cap per `tool_result`. When a payload exceeds the cap, the kernel:
818
844
 
@@ -822,7 +848,7 @@ The kernel's tool-bloat guard (`agent/tool-bloat.js`, internal) enforces a 256 K
822
848
 
823
849
  Hosts that don't supply `persistArtifact` get the truncation summary but no on-disk capture.
824
850
 
825
- ## Context compaction
851
+ ### Context compaction
826
852
 
827
853
  The sole pi bridge runs on pi-agent-core's native `AgentHarness`. pi performs **no**
828
854
  automatic in-loop compaction, so the bridge drives it: before each turn it estimates the
@@ -856,7 +882,7 @@ summary output `clamp(floor(W × 0.04), 2000, 12000)`. Explicit values retain th
856
882
  scalar validation bounds. `onCompactionRecorded(record)` fires only for accepted,
857
883
  persisted automatic compactions.
858
884
 
859
- ## Advanced exports
885
+ ### Advanced exports
860
886
 
861
887
  The package exposes a fixed set of inner pieces via subpath imports. The
862
888
  `exports` map is explicit (no `./ai/*` / `./agent/*` wildcards): only the mapped
@@ -875,10 +901,43 @@ import { configureToolRuntime, readToolRuntime } from "@mono-agent/agent-runtime
875
901
 
876
902
  These are stable but treated as advanced API. Most consumers should reach for `createRuntime` first.
877
903
 
878
- ## Example consumer
904
+ ## Dependency Boundary
905
+
906
+ This package has zero `@mono-agent/*` workspace dependencies. Its runtime
907
+ dependencies are `@anthropic-ai/claude-agent-sdk`, `@anthropic-ai/sdk`,
908
+ `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`,
909
+ `@modelcontextprotocol/sdk`, `@opencode-ai/sdk`, `@vscode/ripgrep`,
910
+ `cross-spawn`, and `zod`.
879
911
 
880
- See [`examples/echo-agent/`](../../examples/echo-agent/) for a runnable consumer that imports `@mono-agent/agent-runtime`, runs a single Claude SDK turn with the Bash tool, and prints the result.
912
+ Sandbox enforcement is an injectable `RuntimeSandbox` seam.
913
+ `@mono-agent/runtime-adapter` supplies the mono-agent implementation; a direct
914
+ consumer that configures a sandbox policy must inject an implementation or the
915
+ runtime fails closed.
881
916
 
882
- ## License
917
+ ## What This Package Does Not Own
883
918
 
884
- GPL-3.0-only.
919
+ - Typed host-facing runtime contracts and SRT process wrapping, owned by
920
+ `@mono-agent/runtime-adapter`.
921
+ - Conversation history, context assembly, memory coordination, or host-side
922
+ session policy, owned by `@mono-agent/agent-harness`.
923
+ - Configuration loading, communication channels, domain result validation, UI,
924
+ and host persistence.
925
+
926
+ ## Related Documentation
927
+
928
+ - [Runtime and providers](https://mono-agent-docs.vercel.app/runtime/) explains the
929
+ config-first model and backend choices.
930
+ - [Backends and model references](https://mono-agent-docs.vercel.app/runtime/backends/)
931
+ documents all five bridges and their execution modes.
932
+ - [Programmatic approvals and structured output](https://mono-agent-docs.vercel.app/programmatic/approval-and-structured-output/)
933
+ shows the code-only host hooks.
934
+ - [Architecture](https://github.com/robertsreberski/mono-agent/blob/main/packages/agent-runtime/ARCHITECTURE.md)
935
+ and [migration guide](https://github.com/robertsreberski/mono-agent/blob/main/packages/agent-runtime/MIGRATION.md)
936
+ cover internal flow and upgrades from `0.3.x`.
937
+
938
+ ## Verification
939
+
940
+ ```bash
941
+ pnpm --filter @mono-agent/agent-runtime run build
942
+ pnpm --filter @mono-agent/agent-runtime run test
943
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mono-agent/agent-runtime",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Agent runtime supporting Claude SDK/CLI, Codex, OpenCode, and Pi SDK bridges out of the box",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-only",
@@ -2,8 +2,10 @@
2
2
  // callback that can approve, deny, or "always approve" (session-scoped
3
3
  // allowlist). Adapted from zeroclaw's ApprovalManager pattern.
4
4
  //
5
- // Hosts opt in by passing `onToolApprovalRequest` to createRuntime. When the
6
- // callback is not supplied, the gate falls back to per-tier defaults:
5
+ // Runtime bridges opt in by passing `onToolApprovalRequest`; the Claude SDK and
6
+ // Pi bridges do not install this manager when that callback is absent. A host
7
+ // that calls `createApprovalManager` directly without a callback gets these
8
+ // low-level per-tier defaults:
7
9
  // low risk → auto-approve
8
10
  // medium risk → auto-approve (no host means "don't pause")
9
11
  // high risk → deny (fail closed)
@@ -123,7 +123,7 @@ export const RESERVED_RUNTIME_KINDS = [...RESERVED_RUNTIME_IDS];
123
123
  // sdk='claude' → CLI (claude binary) or SDK (Anthropic)
124
124
  // sdk='codex' → CLI only (codex app-server)
125
125
  // sdk='opencode' → CLI only (opencode server via @opencode-ai/sdk)
126
- // sdk='pi' → SDK only (pi-sdk handles openai-codex and other providers)
126
+ // sdk='pi' → SDK only (the pi-native bridge handles openai-codex and other providers)
127
127
 
128
128
  // Returns null when the combo is fine; otherwise a short reason string the
129
129
  // UI / API can show.
package/src/ai/types.js CHANGED
@@ -133,7 +133,7 @@
133
133
  * @property {string} [providerSessionId] Provider-owned resume id for resumable bridges.
134
134
  * @property {boolean} [sessionKeepAlive] Keep resumable provider state alive after the turn.
135
135
  * @property {number} [sessionIdleTimeoutMs] Idle TTL for resumable provider state.
136
- * @property {boolean} [liveInput] Whether this run expects a live/streaming input channel.
136
+ * @property {AsyncIterable<{body: string, id?: string}>} [liveInput] Stream of in-flight user messages for steering an active run.
137
137
  * @property {ReadonlyArray<*>} [observers] Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
138
138
  * @property {(event: RuntimeEvent) => void} [onEvent]
139
139
  * @property {ReadonlyArray<Object>} [messages]
package/src/runtime.js CHANGED
@@ -8,10 +8,10 @@
8
8
  // method that resolves the right provider bridge based on `options.model` +
9
9
  // `options.executionMode`.
10
10
  //
11
- // The built-in bridges (claude-sdk, claude-cli, pi-native, codex-app,
12
- // opencode-app) register themselves on import via the runtime registry. Hosts
13
- // that need
14
- // finer control can keep using the named exports (resolveRuntimeBridge,
11
+ // The runtime registry contains a static table for the five built-in bridges
12
+ // (claude-sdk, claude-cli, pi-native, codex-app, opencode-app) and lazily imports
13
+ // the matching implementation only when a run selects it. Hosts that need finer
14
+ // control can keep using the named exports (resolveRuntimeBridge,
15
15
  // generateClaudeResponse, etc.) directly.
16
16
  //
17
17
  // Return shape from `.run()`:
@@ -104,7 +104,7 @@
104
104
  * @property {string} [providerSessionId] Provider-owned resume id for resumable bridges.
105
105
  * @property {boolean} [sessionKeepAlive] Keep resumable provider state alive after the turn.
106
106
  * @property {number} [sessionIdleTimeoutMs] Idle TTL for resumable provider state.
107
- * @property {boolean} [liveInput] Whether this run expects a live/streaming input channel.
107
+ * @property {AsyncIterable<{body: string, id?: string}>} [liveInput] Stream of in-flight user messages for steering an active run.
108
108
  * @property {ReadonlyArray<*>} [observers] Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
109
109
  * @property {(event: RuntimeEvent) => void} [onEvent]
110
110
  * @property {ReadonlyArray<Object>} [messages]
@@ -490,9 +490,12 @@ export type RuntimeRunOptions = {
490
490
  */
491
491
  sessionIdleTimeoutMs?: number;
492
492
  /**
493
- * Whether this run expects a live/streaming input channel.
493
+ * Stream of in-flight user messages for steering an active run.
494
494
  */
495
- liveInput?: boolean;
495
+ liveInput?: AsyncIterable<{
496
+ body: string;
497
+ id?: string;
498
+ }>;
496
499
  /**
497
500
  * Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
498
501
  */