@mono-agent/agent-runtime 0.12.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 +62 -20
- package/MIGRATION.md +32 -34
- package/README.md +155 -96
- package/package.json +1 -1
- package/src/agent/approval.js +4 -2
- package/src/agent/tools/pi-bridge.js +1 -1
- package/src/ai/providers/pi-native/result-builder.js +45 -2
- package/src/ai/providers/pi-native.js +14 -1
- package/src/ai/runtime/model-refs.js +1 -1
- package/src/ai/types.js +1 -1
- package/src/runtime.js +4 -4
- package/types/ai/providers/pi-native/result-builder.d.ts +25 -2
- package/types/ai/types.d.ts +6 -3
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
|
-
|
|
67
|
-
Parse -->
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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`:
|
|
168
|
-
the
|
|
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 |
|
|
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
|
-
|
|
274
|
-
not assume warm-session latency wins there.
|
|
275
|
-
|
|
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
|
|
285
|
-
provider failures are normalized; and how enough telemetry is
|
|
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
|
|
4
|
-
`
|
|
5
|
-
`
|
|
6
|
-
session
|
|
7
|
-
`
|
|
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
|
-
|
|
12
|
-
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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**
|
|
71
|
-
(`piSessionsRoot`), Pi-native now **creates the
|
|
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 `
|
|
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
|
|
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).
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
run-options type
|
|
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
|
-
|
|
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
|
-
|
|
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"` |
|
|
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`
|
|
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
|
-
|
|
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
|
-
|
|
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` | `
|
|
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` |
|
|
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` | `
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
708
|
+
### Structured output
|
|
689
709
|
|
|
690
|
-
Pass `options.outputSchema` (a JSON Schema).
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
917
|
+
## What This Package Does Not Own
|
|
883
918
|
|
|
884
|
-
|
|
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
package/src/agent/approval.js
CHANGED
|
@@ -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
|
-
//
|
|
6
|
-
//
|
|
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)
|
|
@@ -334,7 +334,7 @@ function readSkillTool(skillNames = [], { skillsRoot, dataDir, skills = [] } = {
|
|
|
334
334
|
return {
|
|
335
335
|
name: "ReadSkill",
|
|
336
336
|
label: "Read Skill",
|
|
337
|
-
description: "Load the
|
|
337
|
+
description: "Load the complete instructions for a named skill. Use ReadSkill instead of Read for SKILL.md files.",
|
|
338
338
|
parameters: objectSchema({ name: { type: "string", enum: enumNames } }, ["name"]),
|
|
339
339
|
async execute(_toolCallId, { name }) {
|
|
340
340
|
if (sharedRoot) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// takes its inputs explicitly and returns the verbatim runtime-result contract
|
|
8
8
|
// (diagnostics key spellings, error fields, and shape unchanged — I9).
|
|
9
9
|
|
|
10
|
+
import { calculateContextTokens } from "@earendil-works/pi-agent-core";
|
|
10
11
|
import { isContextLimitError } from "../pi-errors.js";
|
|
11
12
|
import { isLikelyContextTermination } from "../../../agent/compaction.js";
|
|
12
13
|
import { isProviderAuthFailureText } from "../../failure.js";
|
|
@@ -31,6 +32,28 @@ export function usageFromMessages(messages = []) {
|
|
|
31
32
|
return usage;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Normalize the final provider request's usage into an exact context snapshot.
|
|
37
|
+
* Unlike usageFromMessages(), this deliberately does not aggregate earlier
|
|
38
|
+
* requests in the run: the last assistant usage is the same provider-counted
|
|
39
|
+
* value Pi's compaction logic trusts, so it can decrease after compaction.
|
|
40
|
+
* @param {any} assistantMessage
|
|
41
|
+
* @returns {{input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null}
|
|
42
|
+
*/
|
|
43
|
+
export function contextUsageFromAssistantMessage(assistantMessage) {
|
|
44
|
+
if (assistantMessage?.role !== "assistant" || !assistantMessage.usage) return null;
|
|
45
|
+
if (assistantMessage.stopReason === "error" || assistantMessage.stopReason === "aborted") return null;
|
|
46
|
+
const total = Number(calculateContextTokens(assistantMessage.usage)) || 0;
|
|
47
|
+
if (total <= 0) return null;
|
|
48
|
+
return {
|
|
49
|
+
input: Number(assistantMessage.usage.input) || 0,
|
|
50
|
+
output: Number(assistantMessage.usage.output) || 0,
|
|
51
|
+
cacheRead: Number(assistantMessage.usage.cacheRead) || 0,
|
|
52
|
+
cacheCreation: Number(assistantMessage.usage.cacheWrite) || 0,
|
|
53
|
+
total,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
/**
|
|
35
58
|
* Classify a pi error message into a runtime failure kind. Context-window
|
|
36
59
|
* overflows map to context_limit so the router can try the configured fallback;
|
|
@@ -51,9 +74,19 @@ export function failureKindForPiError(message, diagnostics, { maxTurnsHit = fals
|
|
|
51
74
|
|
|
52
75
|
/**
|
|
53
76
|
* Emit the per-run cache / cost / provider-completed events.
|
|
54
|
-
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, estimatedCost: number, start: number, externalAbort: boolean}} params
|
|
77
|
+
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, contextUsage?: {input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null, contextWindow?: number, estimatedCost: number, start: number, externalAbort: boolean}} params
|
|
55
78
|
*/
|
|
56
|
-
export function emitUsageCostEvents({
|
|
79
|
+
export function emitUsageCostEvents({
|
|
80
|
+
onEvent,
|
|
81
|
+
resolved,
|
|
82
|
+
reference,
|
|
83
|
+
usage,
|
|
84
|
+
contextUsage,
|
|
85
|
+
contextWindow,
|
|
86
|
+
estimatedCost,
|
|
87
|
+
start,
|
|
88
|
+
externalAbort,
|
|
89
|
+
}) {
|
|
57
90
|
if (usage.cacheRead > 0) {
|
|
58
91
|
onEvent({ type: "cache_hit", sdk: resolved.sdk, model: reference, tokens: usage.cacheRead, source: "prompt_cache" });
|
|
59
92
|
}
|
|
@@ -72,6 +105,16 @@ export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estim
|
|
|
72
105
|
cacheCreationTokens: Number(usage.cacheWrite) || 0,
|
|
73
106
|
},
|
|
74
107
|
});
|
|
108
|
+
if (contextUsage) {
|
|
109
|
+
const effectiveContextWindow = Number(contextWindow) || 0;
|
|
110
|
+
onEvent({
|
|
111
|
+
type: "context_usage",
|
|
112
|
+
sdk: resolved.sdk,
|
|
113
|
+
model: reference,
|
|
114
|
+
...(effectiveContextWindow > 0 ? { contextWindow: effectiveContextWindow } : {}),
|
|
115
|
+
tokens: contextUsage,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
75
118
|
onEvent({
|
|
76
119
|
type: "provider_request_completed",
|
|
77
120
|
sdk: resolved.sdk,
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
buildErrorDetails,
|
|
53
53
|
buildErrorResult,
|
|
54
54
|
buildSuccessResult,
|
|
55
|
+
contextUsageFromAssistantMessage,
|
|
55
56
|
emitCapabilitiesResolved,
|
|
56
57
|
emitUsageCostEvents,
|
|
57
58
|
usageFromMessages,
|
|
@@ -608,7 +609,19 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
608
609
|
cachedTokens: usage.cacheRead,
|
|
609
610
|
cacheWriteTokens: usage.cacheWrite,
|
|
610
611
|
});
|
|
611
|
-
emitUsageCostEvents({
|
|
612
|
+
emitUsageCostEvents({
|
|
613
|
+
onEvent,
|
|
614
|
+
resolved,
|
|
615
|
+
reference,
|
|
616
|
+
usage,
|
|
617
|
+
contextUsage: runState.externalAbort || runState.maxTurnsHit || runError
|
|
618
|
+
? null
|
|
619
|
+
: contextUsageFromAssistantMessage(lastAssistant),
|
|
620
|
+
contextWindow: runState.compaction.policy?.contextWindow,
|
|
621
|
+
estimatedCost,
|
|
622
|
+
start,
|
|
623
|
+
externalAbort: runState.externalAbort,
|
|
624
|
+
});
|
|
612
625
|
|
|
613
626
|
const rawErrorMessage = runState.externalAbort
|
|
614
627
|
? null
|
|
@@ -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-
|
|
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 {
|
|
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
|
|
12
|
-
//
|
|
13
|
-
// that need
|
|
14
|
-
//
|
|
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()`:
|
|
@@ -10,6 +10,21 @@ export function usageFromMessages(messages?: Array<any>): {
|
|
|
10
10
|
cacheWrite: number;
|
|
11
11
|
cost: number;
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Normalize the final provider request's usage into an exact context snapshot.
|
|
15
|
+
* Unlike usageFromMessages(), this deliberately does not aggregate earlier
|
|
16
|
+
* requests in the run: the last assistant usage is the same provider-counted
|
|
17
|
+
* value Pi's compaction logic trusts, so it can decrease after compaction.
|
|
18
|
+
* @param {any} assistantMessage
|
|
19
|
+
* @returns {{input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null}
|
|
20
|
+
*/
|
|
21
|
+
export function contextUsageFromAssistantMessage(assistantMessage: any): {
|
|
22
|
+
input: number;
|
|
23
|
+
output: number;
|
|
24
|
+
cacheRead: number;
|
|
25
|
+
cacheCreation: number;
|
|
26
|
+
total: number;
|
|
27
|
+
} | null;
|
|
13
28
|
/**
|
|
14
29
|
* Classify a pi error message into a runtime failure kind. Context-window
|
|
15
30
|
* overflows map to context_limit so the router can try the configured fallback;
|
|
@@ -25,9 +40,9 @@ export function failureKindForPiError(message: string | null, diagnostics: Recor
|
|
|
25
40
|
}): string | null;
|
|
26
41
|
/**
|
|
27
42
|
* Emit the per-run cache / cost / provider-completed events.
|
|
28
|
-
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, estimatedCost: number, start: number, externalAbort: boolean}} params
|
|
43
|
+
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, contextUsage?: {input: number, output: number, cacheRead: number, cacheCreation: number, total: number}|null, contextWindow?: number, estimatedCost: number, start: number, externalAbort: boolean}} params
|
|
29
44
|
*/
|
|
30
|
-
export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estimatedCost, start, externalAbort }: {
|
|
45
|
+
export function emitUsageCostEvents({ onEvent, resolved, reference, usage, contextUsage, contextWindow, estimatedCost, start, externalAbort, }: {
|
|
31
46
|
onEvent: (event: any) => void;
|
|
32
47
|
resolved: any;
|
|
33
48
|
reference: string;
|
|
@@ -38,6 +53,14 @@ export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estim
|
|
|
38
53
|
cacheWrite: number;
|
|
39
54
|
cost: number;
|
|
40
55
|
};
|
|
56
|
+
contextUsage?: {
|
|
57
|
+
input: number;
|
|
58
|
+
output: number;
|
|
59
|
+
cacheRead: number;
|
|
60
|
+
cacheCreation: number;
|
|
61
|
+
total: number;
|
|
62
|
+
} | null;
|
|
63
|
+
contextWindow?: number;
|
|
41
64
|
estimatedCost: number;
|
|
42
65
|
start: number;
|
|
43
66
|
externalAbort: boolean;
|
package/types/ai/types.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
*
|
|
493
|
+
* Stream of in-flight user messages for steering an active run.
|
|
494
494
|
*/
|
|
495
|
-
liveInput?:
|
|
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
|
*/
|