@mono-agent/agent-runtime 0.20.11 → 0.21.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.
Files changed (148) hide show
  1. package/ARCHITECTURE.md +50 -11
  2. package/MIGRATION.md +288 -26
  3. package/README.md +352 -477
  4. package/package.json +13 -44
  5. package/src/agent/tool-bloat.js +145 -9
  6. package/src/agent/tools/agent-tool.js +108 -9
  7. package/src/agent/tools/bash.js +11 -26
  8. package/src/agent/tools/codex-subscription-search.js +123 -29
  9. package/src/agent/tools/exec.js +10 -2
  10. package/src/agent/tools/index.js +7 -0
  11. package/src/agent/tools/monitor.js +149 -0
  12. package/src/agent/tools/pi-bridge.js +123 -19
  13. package/src/agent/tools/shared/bash-environment.js +31 -0
  14. package/src/agent/tools/shared/monitors.js +293 -0
  15. package/src/agent/tools/shared/path-resolver.js +25 -6
  16. package/src/agent/tools/shared/process-jobs.js +6 -1
  17. package/src/agent/tools/shared/process-runner.js +26 -6
  18. package/src/agent/tools/shared/tool-context.js +8 -0
  19. package/src/agent/tools/web-access-interstitial.js +70 -0
  20. package/src/agent/tools/web-browser-render.js +83 -58
  21. package/src/agent/tools/web-controller.js +112 -21
  22. package/src/agent/tools/web-document-extractor.js +379 -0
  23. package/src/agent/tools/web-fetch.js +271 -243
  24. package/src/agent/tools/web-request.js +65 -0
  25. package/src/agent/tools/web-search-output.js +165 -0
  26. package/src/agent/tools/web-search-state.js +75 -0
  27. package/src/agent/tools/web-search.js +532 -71
  28. package/src/ai/cost.js +13 -68
  29. package/src/ai/failure.js +3 -3
  30. package/src/ai/index.js +5 -17
  31. package/src/ai/observer.js +8 -0
  32. package/src/ai/pi-interop.js +221 -1
  33. package/src/ai/pi-oauth-compat.js +1 -1
  34. package/src/ai/provider-check.js +131 -0
  35. package/src/ai/providers/codex/app-server-client.js +592 -0
  36. package/src/ai/providers/pi-models.js +18 -10
  37. package/src/ai/providers/pi-native/compaction-driver.js +94 -42
  38. package/src/ai/providers/pi-native/compaction-summary.js +140 -0
  39. package/src/ai/providers/pi-native/harness-adapter.js +376 -0
  40. package/src/ai/providers/pi-native/prompt-cache-diagnostics.js +103 -0
  41. package/src/ai/providers/pi-native/provider-attribution.js +102 -0
  42. package/src/ai/providers/pi-native/result-builder.js +38 -14
  43. package/src/ai/providers/pi-native/session-lifecycle.js +253 -55
  44. package/src/ai/providers/pi-native/stream-subscriber.js +52 -6
  45. package/src/ai/providers/pi-native/terminal-recovery.js +40 -0
  46. package/src/ai/providers/pi-native/turn-runner.js +279 -28
  47. package/src/ai/providers/pi-native.js +206 -61
  48. package/src/ai/runtime/capabilities.js +11 -56
  49. package/src/ai/runtime/live-input-events.js +250 -54
  50. package/src/ai/runtime/model-refs.js +118 -153
  51. package/src/ai/runtime/registry.js +22 -56
  52. package/src/ai/runtime/router.js +76 -417
  53. package/src/ai/runtime/session-liveness.js +3 -4
  54. package/src/ai/runtime/sessions.js +4 -5
  55. package/src/ai/runtime/tool-policy.js +0 -2
  56. package/src/ai/tool-lifecycle.js +32 -18
  57. package/src/ai/types.js +37 -112
  58. package/src/index.js +0 -6
  59. package/src/runtime.js +29 -16
  60. package/types/agent/tool-bloat.d.ts +1 -1
  61. package/types/agent/tools/agent-tool.d.ts +4 -2
  62. package/types/agent/tools/bash.d.ts +5 -3
  63. package/types/agent/tools/codex-subscription-search.d.ts +7 -3
  64. package/types/agent/tools/exec.d.ts +5 -3
  65. package/types/agent/tools/index.d.ts +1 -0
  66. package/types/agent/tools/monitor.d.ts +47 -0
  67. package/types/agent/tools/pi-bridge.d.ts +7 -4
  68. package/types/agent/tools/shared/bash-environment.d.ts +4 -0
  69. package/types/agent/tools/shared/monitors.d.ts +98 -0
  70. package/types/agent/tools/shared/process-jobs.d.ts +5 -1
  71. package/types/agent/tools/shared/process-runner.d.ts +14 -4
  72. package/types/agent/tools/shared/tool-context.d.ts +2 -0
  73. package/types/agent/tools/web-access-interstitial.d.ts +23 -0
  74. package/types/agent/tools/web-browser-render.d.ts +4 -1
  75. package/types/agent/tools/web-controller.d.ts +4 -2
  76. package/types/agent/tools/web-document-extractor.d.ts +27 -0
  77. package/types/agent/tools/web-fetch.d.ts +19 -24
  78. package/types/agent/tools/web-request.d.ts +20 -0
  79. package/types/agent/tools/web-search-output.d.ts +31 -0
  80. package/types/agent/tools/web-search-state.d.ts +21 -0
  81. package/types/agent/tools/web-search.d.ts +10 -45
  82. package/types/ai/cost.d.ts +1 -2
  83. package/types/ai/index.d.ts +2 -4
  84. package/types/ai/observer.d.ts +6 -0
  85. package/types/ai/pi-interop.d.ts +81 -0
  86. package/types/ai/provider-check.d.ts +53 -0
  87. package/types/ai/providers/codex/app-server-client.d.ts +37 -0
  88. package/types/ai/providers/pi-native/compaction-driver.d.ts +2 -1
  89. package/types/ai/providers/pi-native/compaction-summary.d.ts +19 -0
  90. package/types/ai/providers/pi-native/harness-adapter.d.ts +58 -0
  91. package/types/ai/providers/pi-native/prompt-cache-diagnostics.d.ts +3 -0
  92. package/types/ai/providers/pi-native/provider-attribution.d.ts +26 -0
  93. package/types/ai/providers/pi-native/result-builder.d.ts +14 -4
  94. package/types/ai/providers/pi-native/session-lifecycle.d.ts +25 -6
  95. package/types/ai/providers/pi-native/stream-subscriber.d.ts +2 -2
  96. package/types/ai/providers/pi-native/terminal-recovery.d.ts +2 -0
  97. package/types/ai/providers/pi-native/turn-runner.d.ts +68 -10
  98. package/types/ai/providers/pi-native.d.ts +21 -4
  99. package/types/ai/runtime/capabilities.d.ts +21 -70
  100. package/types/ai/runtime/live-input-events.d.ts +32 -8
  101. package/types/ai/runtime/model-refs.d.ts +0 -24
  102. package/types/ai/runtime/router.d.ts +3 -10
  103. package/types/ai/runtime/tool-policy.d.ts +0 -2
  104. package/types/ai/tool-lifecycle.d.ts +4 -3
  105. package/types/ai/types.d.ts +162 -256
  106. package/types/index.d.ts +0 -1
  107. package/src/ai/providers/acp-client.js +0 -1149
  108. package/src/ai/providers/acp-privacy.js +0 -124
  109. package/src/ai/providers/acp-public.js +0 -21
  110. package/src/ai/providers/acp-session-tokens.js +0 -282
  111. package/src/ai/providers/acp-transport.js +0 -356
  112. package/src/ai/providers/acp.js +0 -543
  113. package/src/ai/providers/claude-cli.js +0 -883
  114. package/src/ai/providers/claude-sandbox.js +0 -71
  115. package/src/ai/providers/claude-sdk-discovery-worker.js +0 -53
  116. package/src/ai/providers/claude-sdk-discovery.js +0 -352
  117. package/src/ai/providers/claude-sdk.js +0 -1127
  118. package/src/ai/providers/claude-subagent-activity.js +0 -719
  119. package/src/ai/providers/claude-subagents.js +0 -88
  120. package/src/ai/providers/codex-app.js +0 -2946
  121. package/src/ai/providers/opencode-app.js +0 -1109
  122. package/src/ai/providers/opencode-discovery.js +0 -39
  123. package/src/ai/providers/opencode-server.js +0 -508
  124. package/src/ai/runtime/context-windows.js +0 -46
  125. package/src/ai/runtime/fast-mode.js +0 -8
  126. package/src/ai/streaming/codex-events.js +0 -146
  127. package/src/ai/streaming/opencode-events.js +0 -59
  128. package/types/ai/providers/acp-client.d.ts +0 -227
  129. package/types/ai/providers/acp-privacy.d.ts +0 -25
  130. package/types/ai/providers/acp-public.d.ts +0 -7
  131. package/types/ai/providers/acp-session-tokens.d.ts +0 -41
  132. package/types/ai/providers/acp-transport.d.ts +0 -45
  133. package/types/ai/providers/acp.d.ts +0 -93
  134. package/types/ai/providers/claude-cli.d.ts +0 -305
  135. package/types/ai/providers/claude-sandbox.d.ts +0 -79
  136. package/types/ai/providers/claude-sdk-discovery-worker.d.ts +0 -1
  137. package/types/ai/providers/claude-sdk-discovery.d.ts +0 -97
  138. package/types/ai/providers/claude-sdk.d.ts +0 -138
  139. package/types/ai/providers/claude-subagent-activity.d.ts +0 -53
  140. package/types/ai/providers/claude-subagents.d.ts +0 -18
  141. package/types/ai/providers/codex-app.d.ts +0 -151
  142. package/types/ai/providers/opencode-app.d.ts +0 -96
  143. package/types/ai/providers/opencode-discovery.d.ts +0 -4
  144. package/types/ai/providers/opencode-server.d.ts +0 -20
  145. package/types/ai/runtime/context-windows.d.ts +0 -9
  146. package/types/ai/runtime/fast-mode.d.ts +0 -2
  147. package/types/ai/streaming/codex-events.d.ts +0 -40
  148. package/types/ai/streaming/opencode-events.d.ts +0 -42
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @mono-agent/agent-runtime
2
2
 
3
3
  Use this package when you need direct, capability-aware access to mono-agent's
4
- six built-in model runtime bridges, including product-neutral ACP v1 agents.
4
+ Pi runtime and the ~39 providers it reaches.
5
5
 
6
6
  ## Category
7
7
 
@@ -10,13 +10,13 @@ six built-in model runtime bridges, including product-neutral ACP v1 agents.
10
10
 
11
11
  Category: `runtime`
12
12
  Tier: `core`
13
- Catalog responsibility: Provides six runtime bridges (ACP v1, Claude SDK, Claude Code CLI, Codex app-server, OpenCode app-server, Pi SDK); direct OpenCode requires stable CLI >=1.15.0 on PATH.
13
+ Catalog responsibility: Provides the Pi SDK runtime bridge with native tools, MCP, sessions, compaction, and provider-catalog integration.
14
14
 
15
15
  <!-- package-metadata:end -->
16
16
 
17
17
  ## Responsibility
18
18
 
19
- Provides six runtime bridges (ACP v1, 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 and ACP-owned stdio children enforce optional mono-agent sandbox policy 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.
19
+ Implements the Pi runtime. This is the runtime layer that `@mono-agent/runtime-adapter` wraps behind runtime contracts. Pi-owned stdio children enforce optional mono-agent sandbox policy through an injectable `RuntimeSandbox` seam (a fail-closed passthrough by default; `@mono-agent/runtime-adapter` injects the real implementation). Every route in a fallback chain is Pi-native and shares one contract; no route silently drops required capabilities.
20
20
 
21
21
  ## Install / Usage
22
22
 
@@ -24,10 +24,14 @@ Provides six runtime bridges (ACP v1, Claude SDK, Claude Code CLI, Codex app-ser
24
24
  pnpm add @mono-agent/agent-runtime
25
25
  ```
26
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.
27
+ Node.js 22.19 or newer is required. Pi is the only runtime, and it talks to
28
+ providers over their SDKs, so no provider CLI has to be on `PATH`.
29
+
30
+ When a host enables background Exec/Bash, `wake_on_completion` defaults to true;
31
+ explicit false retains terminal lifecycle updates without a completion turn.
32
+ Using that field without `background: true` is invalid. Host-provided
33
+ `processJobsAvailability` exposes lineage and exhaustion diagnostics even
34
+ when a request has no start controller.
31
35
 
32
36
  Create one runtime for a host, parse a model reference, and run a turn:
33
37
 
@@ -39,8 +43,7 @@ import {
39
43
 
40
44
  const runtime = createRuntime({ workspace: process.cwd() });
41
45
  const result = await runtime.run("You are a concise repository assistant.", {
42
- model: parseRuntimeModelReference("claude:claude-sonnet-4-6"),
43
- executionMode: "sdk",
46
+ model: parseRuntimeModelReference("anthropic:claude-sonnet-4-6"),
44
47
  messages: [{ role: "user", content: "Summarize README.md." }],
45
48
  cwd: process.cwd(),
46
49
  allowedTools: ["Read"],
@@ -50,6 +53,16 @@ if (result.error) throw new Error(result.error);
50
53
  console.log(result.text);
51
54
  ```
52
55
 
56
+ Requests to Pi providers `opencode` and `opencode-go`, and to custom models whose
57
+ exact URL hostname is `opencode.ai`, carry `x-opencode-client: mono-agent` and an
58
+ `x-opencode-session` continuity value. The value is the raw internal provider
59
+ session id, not a channel conversation id. It remains stable for a continuous
60
+ conversation epoch, rotates on reset or invalidation, and is fresh per call when
61
+ a direct caller supplies no session or attribution identity. Existing
62
+ case-insensitive auth/model/request headers—including explicit `null`
63
+ suppression—and caller header transforms take precedence. Other providers receive
64
+ no `x-opencode-*` headers.
65
+
53
66
  `Glob` and `Grep` prefer the packaged `@vscode/ripgrep` binary on supported
54
67
  platforms. An explicit `ripgrepPath` wins, with `PATH` as the final fallback.
55
68
 
@@ -61,19 +74,24 @@ capability. The host then explicitly intersects the two reviewed protocol
61
74
  revisions, reads only the originating tool's declared `ui://` resource, and
62
75
  receives one exact connection capability. Successful registration retains that
63
76
  existing MCP client instead of creating a client per UI call; host LRU/idle
64
- eviction closes the client, transport, and sandbox cleanup. Other runtime
65
- backends do not advertise or receive this extension.
77
+ eviction closes the client, transport, and sandbox cleanup.
66
78
  See [Reply files and MCP Apps](https://mono-agent-docs.vercel.app/tools/rich-replies/).
67
79
 
68
80
  ## Architecture
69
81
 
82
+ The injected Pi-native `Monitor` tool supports `wake_on: "batch" | "exit"`,
83
+ `dedupe: "none" | "batch"`, and `min_wake_interval_ms` (defaults batch/none/0).
84
+ The start receipt reports the host's effective policy, including interval
85
+ clamping. Terminal delivery bypasses batch suppression and timing. A cancelled
86
+ watch is intentionally stopped and must not be automatically recreated.
87
+
70
88
  The package uses a fixed registry of bridge descriptors and loads provider code
71
89
  only after a run selects a matching model reference and execution mode:
72
90
 
73
91
  ### Data flow
74
92
 
75
93
  1. `createRuntime()` binds host callbacks and creates an isolated tool context.
76
- 2. `resolveRuntimeBridge()` checks the six static bridge descriptors in order.
94
+ 2. `resolveRuntimeBridge()` resolves the single static bridge descriptor.
77
95
  3. The selected descriptor lazily imports its provider implementation.
78
96
  4. The bridge prepares the runtime inputs it supports, including managed or MCP
79
97
  tools only where that bridge can represent them, and streams normalized
@@ -91,7 +109,7 @@ only after a run selects a matching model reference and execution mode:
91
109
  | --- | --- |
92
110
  | `src/runtime.js` | Host binding, per-instance tool context, bridge dispatch, and observer flushing |
93
111
  | `src/ai/runtime/` | Model-reference parsing, the lazy bridge registry, capabilities, sessions, and fallback routing |
94
- | `src/ai/providers/` | ACP v1, Claude SDK/CLI, Codex app-server, OpenCode app-server, and Pi SDK integrations |
112
+ | `src/ai/providers/` | Pi provider integrations |
95
113
  | `src/agent/tools/` | Managed tools, MCP adaptation, output limits, and the injectable sandbox seam |
96
114
  | `src/agent/` | Approvals, allowlists, transcript snapshots, and compaction policy helpers |
97
115
 
@@ -100,13 +118,23 @@ the [architecture guide](https://github.com/robertsreberski/mono-agent/blob/main
100
118
 
101
119
  ### Managed-tool lifecycle fidelity
102
120
 
121
+ Native event admission precedes queued storage: observers receive `recordEvent`
122
+ and optional `recordToolLifecycle` callbacks synchronously. The latter receives
123
+ the normalized lifecycle event before persistence begins. This lets the harness
124
+ retain work emitted before cancellation while its sidecar is busy. Persistence
125
+ and client delivery remain serialized; later cancellation cannot reclassify an
126
+ already admitted tool result.
127
+
103
128
  `RuntimeRunOptions.toolLifecycleSink` is an awaited host-owned boundary. The
104
129
  runtime sends redaction-eligible raw arguments/content plus stable provider call
105
130
  id and name; the host returns only record/sequence, persistence/truncation byte
106
131
  metadata, and opaque artifact references. That returned metadata is attached to
107
- the exact normalized `tool_use` / `tool_result` event rendered by clients. Sink
108
- failure is fail-soft and explicit (`persistence: "failed"` plus an error code),
109
- never fake success. A callback exception cannot duplicate a client event.
132
+ the exact normalized `tool_use` / `tool_result` event rendered by clients.
133
+ `persistence: "deferred"` preserves a host-accepted write whose foreground
134
+ confirmation window elapsed; `failed` plus an error code is reserved for a
135
+ definitive sink rejection. Both remain fail-soft for the provider outcome and
136
+ neither is converted into fake success. A callback exception cannot duplicate a
137
+ client event.
110
138
 
111
139
  Provider bridges only claim terminal distinctions their structured protocol
112
140
  actually supplies:
@@ -114,11 +142,6 @@ actually supplies:
114
142
  | Bridge | Genuine tool-result distinctions | Conservative fallback |
115
143
  | --- | --- | --- |
116
144
  | Pi native | `success`, `error`, numeric `exit_nonzero`, structured `timeout`, structured `signal`, and abort-backed `cancelled`; host approval denial/expiry adds `rejected`/`timeout` | Unknown failed outcome → `error` |
117
- | Codex app-server | `success`, `error`, and numeric command `exit_nonzero`; shared host abort rules still apply | Other failed item → `error`; no result before run end remains dangling for host closure/recovery |
118
- | Claude SDK | `success` / `error`; shared host approval events can add `rejected`/`timeout` and an aborted error result can add `cancelled` | Undistinguished failed result → `error` |
119
- | Claude Code CLI | `success` / `error`; a Codex-shaped command item with an explicit non-zero code is `exit_nonzero`; shared approval/abort rules still apply | Undistinguished failed result → `error` |
120
- | OpenCode app-server | `success` / `error`; shared approval/abort rules still apply | Undistinguished failed result → `error` |
121
- | ACP v1 | `completed` → `success`, `failed` → `error`; shared approval/abort rules still apply | ACP exposes no tool-level signal/exit/timeout distinction, so failed → `error` |
122
145
 
123
146
  The runtime never derives a state from result prose. Structured timeout, signal,
124
147
  non-zero exit, completed success, and a specific non-runtime, non-cancellation
@@ -157,13 +180,14 @@ provider-supplied known kind when available and otherwise `runtime_error`.
157
180
  | API | Use it for |
158
181
  | --- | --- |
159
182
  | `createRuntime()` | Run one model bridge with host-owned credentials, observers, tools, and lifecycle callbacks |
160
- | `probeAcpProfile()` / ACP management helpers | Probe, authenticate, log out, list sessions, validate opaque handles, or delete an ACP provider session |
161
183
  | `createRouterRuntime()` | Retry an ordered model chain while preserving explicit route-safety contracts |
162
- | `parseRuntimeModelReference()` | Convert a canonical `acp:`, `claude:`, `codex:`, `opencode:`, or `pi:` string into the object required by `run()` |
163
- | `listRuntimeBridges()` / `runtimeCapabilities()` | Inspect the six built-in bridge descriptors without loading provider implementations |
184
+ | `parseRuntimeModelReference()` | Convert a canonical `<provider>:<model>` string into the object required by `run()` |
185
+ | `listRuntimeBridges()` / `runtimeCapabilities()` | Inspect the built-in bridge descriptor without loading provider implementations |
164
186
  | `createPiOAuthApiKeyResolver()` | Bind a host-owned Pi auth file with refresh-safe writes |
165
187
  | `listPiBuiltinModels()` / `getPiBuiltinModel()` | Read cloned snapshots from the runtime-owned, exact-pinned Pi model catalog without importing Pi directly |
166
188
  | `resolvePiOAuthApiKey()` / `loginPiOAuth()` | Use the runtime-owned Pi OAuth implementation without importing Pi's mutable provider registry |
189
+ | `describePiProviderAuth()` / `checkPiProviderAuth()` / `loginPiProviderAuth()` | Bridge Pi's provider-owned auth descriptions, detection, prompts, device events, and returned credential without exposing its mutable registry |
190
+ | `runPiProviderCheck()` | Execute one isolated, bounded Pi request for an explicitly selected provider/model and return only a fixed sanitized outcome; only provider-construction options are admitted, never session/history/tool/hook state |
167
191
  | `createMetricsObserver()` | Aggregate normalized event, token, cache, cost, tool, error, turn, and approval metrics |
168
192
 
169
193
  Most hosts should use `@mono-agent/runtime-adapter` instead of importing deep
@@ -178,25 +202,26 @@ Every symbol exported by each public code entrypoint is listed below.
178
202
  **`@mono-agent/agent-runtime`**
179
203
 
180
204
  ```text
181
- ACP_PROTOCOL_VERSION
182
- ACTIVE_RUNTIME_KINDS
183
205
  ALLOWLIST_MODE_ALL
184
206
  ALLOWLIST_MODE_CUSTOM
185
207
  APPROVAL_DECISIONS
186
- AcpCallbackContext
187
- AcpClientError
188
- AcpClientHostOptions
189
- AcpInteractionRequest
190
- AcpListedSession
191
- AcpProfileDescriptor
192
- AcpSessionListResult
193
208
  BINARY_BLOAT_TOOLS
194
209
  BridgeSpec
195
- CLAUDE_SDK_CATALOG_VERSION
196
210
  DEFAULT_RUNTIME_BRAND
197
211
  DEFAULT_TOOL_BLOAT_CONFIG
198
212
  MAX_TOOL_RESULT_BYTES
199
- RESERVED_RUNTIME_KINDS
213
+ PROVIDER_CHECK_PROMPT
214
+ PiBuiltinModelSnapshot
215
+ PiBuiltinProviderSnapshot
216
+ PiOAuthCredentialsSnapshot
217
+ PiOAuthLoginCallbacks
218
+ PiProviderAuthCheck
219
+ PiProviderAuthDescription
220
+ PiProviderAuthInteraction
221
+ PiProviderAuthPrompt
222
+ PiReasoningLevel
223
+ ProviderCheckCode
224
+ ProviderCheckOutcome
200
225
  RISK_TIERS
201
226
  RUNTIME_CAPABILITIES
202
227
  RuntimeBridge
@@ -204,47 +229,38 @@ RuntimeBridgeDescriptor
204
229
  RuntimeBridgeId
205
230
  RuntimeModelRef
206
231
  UNKNOWN_CAPABILITY
207
- acpRuntimeBridge
208
- authenticateAcpProfile
209
232
  buildCapabilitiesUsed
210
233
  buildTranscriptTailSnapshot
211
- canonicalizeLegacyModelReference
234
+ checkPiProviderAuth
235
+ classifyProviderCheckFailure
212
236
  configureToolRuntime
213
237
  createApprovalManager
214
- createClaudeSdkDiscoveryIsolation
215
238
  createMetricsObserver
216
239
  createObserverHub
217
240
  createPiOAuthApiKeyResolver
218
241
  createRouterRuntime
219
242
  createRuntime
220
243
  createSessionRegistry
221
- curatedClaudeSdkModels
222
- deleteAcpSession
223
- discoverClaudeSdkModels
244
+ describePiBuiltinProvider
245
+ describePiProviderAuth
224
246
  disposeAllProviderSessions
225
247
  disposeProviderSession
226
- executionModeIncompatibilityReason
227
- generateAcpResponse
228
248
  generatePiNativeResponse
229
249
  getPiBuiltinModel
230
250
  inferAllowlistMode
231
251
  invalidateProviderSession
232
252
  isLikelyContextTermination
233
- isModelCompatibleWithExecutionMode
234
- listAcpSessions
235
253
  listPiBuiltinModels
254
+ listPiBuiltinProviders
236
255
  listRuntimeBridges
237
256
  loginPiOAuth
238
- logoutAcpProfile
257
+ loginPiProviderAuth
239
258
  normalizeAllowlistMode
240
- normalizeClaudeSdkCatalog
241
- normalizeClaudeSdkModelId
242
259
  normalizeList
243
260
  normalizeRuntimeModelReference
244
261
  parseRuntimeModelReference
245
262
  parseStoredAllowlist
246
263
  piNativeRuntimeBridge
247
- probeAcpProfile
248
264
  readRuntimeBrand
249
265
  readToolRuntime
250
266
  reasoningLevelsForPiModel
@@ -257,13 +273,11 @@ resolveAllowlistMap
257
273
  resolvePiOAuthApiKey
258
274
  resolveRuntimeBrand
259
275
  resolveRuntimeBridge
276
+ runPiProviderCheck
260
277
  runtimeCapabilities
261
- sdkFromModelReference
262
278
  storedAllowlistMode
263
279
  syncProviderSession
264
280
  toolCompactionAppliedFromWarnings
265
- validateAcpProfileId
266
- validateAcpProviderSessionId
267
281
  wrapToolsWithApprovalGate
268
282
  ```
269
283
 
@@ -332,6 +346,8 @@ inferSkillsRoot
332
346
 
333
347
  ```text
334
348
  DEFAULT_CODEX_SEARCH_MODEL
349
+ DEFAULT_MONITOR_TIMEOUT_MS
350
+ MIN_MONITOR_TIMEOUT_MS
335
351
  bashToolImpl
336
352
  bashToolRun
337
353
  createWebToolController
@@ -343,9 +359,12 @@ grepToolImpl
343
359
  inspectCodexSubscriptionSearch
344
360
  isPathAllowed
345
361
  isWorkdirAllowed
362
+ monitorStopToolRun
363
+ monitorToolRun
346
364
  normalizeBackgroundBashTimeoutMs
347
365
  normalizeBackgroundTimeoutMs
348
366
  normalizeBashTimeoutMs
367
+ normalizeMonitorTimeoutMs
349
368
  normalizeProcessTimeoutMs
350
369
  performWebFetch
351
370
  performWebSearch
@@ -393,64 +412,54 @@ renderResumeSnapshot
393
412
  **`@mono-agent/agent-runtime/ai`**
394
413
 
395
414
  ```text
396
- ACP_PROTOCOL_VERSION
397
- ACTIVE_RUNTIME_KINDS
398
- AcpCallbackContext
399
- AcpClientError
400
- AcpClientHostOptions
401
- AcpInteractionRequest
402
- AcpListedSession
403
- AcpProfileDescriptor
404
- AcpSessionListResult
405
415
  BridgeSpec
406
- CLAUDE_SDK_CATALOG_VERSION
407
- RESERVED_RUNTIME_KINDS
416
+ PROVIDER_CHECK_PROMPT
417
+ PiBuiltinModelSnapshot
418
+ PiBuiltinProviderSnapshot
419
+ PiOAuthCredentialsSnapshot
420
+ PiOAuthLoginCallbacks
421
+ PiProviderAuthCheck
422
+ PiProviderAuthDescription
423
+ PiProviderAuthInteraction
424
+ PiProviderAuthPrompt
425
+ PiReasoningLevel
426
+ ProviderCheckCode
427
+ ProviderCheckOutcome
408
428
  RUNTIME_CAPABILITIES
409
429
  RuntimeBridge
410
430
  RuntimeBridgeDescriptor
411
431
  RuntimeBridgeId
412
432
  RuntimeModelRef
413
433
  UNKNOWN_CAPABILITY
414
- acpRuntimeBridge
415
- authenticateAcpProfile
416
434
  buildCapabilitiesUsed
417
- canonicalizeLegacyModelReference
418
- createClaudeSdkDiscoveryIsolation
435
+ checkPiProviderAuth
436
+ classifyProviderCheckFailure
419
437
  createMetricsObserver
420
438
  createObserverHub
421
439
  createSessionRegistry
422
- curatedClaudeSdkModels
423
- deleteAcpSession
424
- discoverClaudeSdkModels
440
+ describePiBuiltinProvider
441
+ describePiProviderAuth
425
442
  disposeAllProviderSessions
426
443
  disposeProviderSession
427
- executionModeIncompatibilityReason
428
- generateAcpResponse
429
444
  generatePiNativeResponse
430
445
  getPiBuiltinModel
431
446
  invalidateProviderSession
432
- isModelCompatibleWithExecutionMode
433
- listAcpSessions
434
447
  listPiBuiltinModels
448
+ listPiBuiltinProviders
435
449
  listRuntimeBridges
436
450
  loginPiOAuth
437
- logoutAcpProfile
438
- normalizeClaudeSdkCatalog
439
- normalizeClaudeSdkModelId
451
+ loginPiProviderAuth
440
452
  normalizeRuntimeModelReference
441
453
  parseRuntimeModelReference
442
454
  piNativeRuntimeBridge
443
- probeAcpProfile
444
455
  reasoningLevelsForPiModel
445
456
  refreshProviderSession
446
457
  resolvePiOAuthApiKey
447
458
  resolveRuntimeBridge
459
+ runPiProviderCheck
448
460
  runtimeCapabilities
449
- sdkFromModelReference
450
461
  syncProviderSession
451
462
  toolCompactionAppliedFromWarnings
452
- validateAcpProfileId
453
- validateAcpProviderSessionId
454
463
  ```
455
464
 
456
465
  **`@mono-agent/agent-runtime/ai/cost.js`**
@@ -498,93 +507,32 @@ statsForCompletedChange
498
507
  formatLiveInputGuidance
499
508
  ```
500
509
 
501
- **`@mono-agent/agent-runtime/ai/providers/acp.js`**
502
-
503
- ```text
504
- acpRuntimeBridge
505
- generateAcpResponse
506
- ```
507
-
508
- **`@mono-agent/agent-runtime/ai/providers/claude-cli.js`**
510
+ **`@mono-agent/agent-runtime/ai/providers/codex/app-server-client.js`**
509
511
 
510
512
  ```text
511
- buildCliCommand
512
- claudeCodeRuntimeBridge
513
- createThinkingBuffer
514
- generateCliResponse
515
- normalizeCliEvent
516
- ```
517
-
518
- **`@mono-agent/agent-runtime/ai/providers/claude-sdk-discovery.js`**
519
-
520
- ```text
521
- CLAUDE_SDK_CATALOG_VERSION
522
- ClaudeSdkCatalogModel
523
- ClaudeSdkEffort
524
- createClaudeSdkDiscoveryIsolation
525
- curatedClaudeSdkModels
526
- discoverClaudeSdkModels
527
- normalizeClaudeSdkCatalog
528
- normalizeClaudeSdkModelId
529
- ```
530
-
531
- **`@mono-agent/agent-runtime/ai/providers/claude-sdk.js`**
532
-
533
- ```text
534
- claudeEffortOptions
535
- claudeRuntimeBridge
536
- claudeSdkModelForQuery
537
- generateClaudeResponse
538
- toolPayloadLimit
539
- ```
540
-
541
- **`@mono-agent/agent-runtime/ai/providers/codex-app.js`**
542
-
543
- ```text
544
- codexAppRuntimeBridge
513
+ CODEX_APP_SERVER_ARGS
514
+ CODEX_APP_SERVER_ISOLATED_ARGS
515
+ addOpaqueSensitiveValue
516
+ codexErrorMessage
545
517
  createCodexAppServerClient
546
- generateCodexAppResponse
547
- ```
548
-
549
- **`@mono-agent/agent-runtime/ai/providers/opencode-discovery.js`**
550
-
551
- ```text
552
- discoverOpencodeProviders
553
- ```
554
-
555
- **`@mono-agent/agent-runtime/ai/runtime/context-windows.js`**
556
-
557
- ```text
558
- CLAUDE_ONE_MILLION_CONTEXT_MODELS
559
- DEFAULT_CONTEXT_WINDOW
560
- ONE_MILLION_CONTEXT_WINDOW
561
- claudeModelSupportsContextWindow
562
- claudeModelSupportsOneMillionContext
563
- hasExplicitOneMillionContextWindow
564
- modelWithContextWindow
565
- normalizeContextWindow
566
- stripContextWindowSuffix
567
- ```
568
-
569
- **`@mono-agent/agent-runtime/ai/runtime/fast-mode.js`**
570
-
571
- ```text
572
- codexModelSupportsFastMode
573
- normalizeFastMode
518
+ isCodexRequestTimeout
519
+ isSensitivePayloadField
520
+ normalizedSensitiveName
521
+ redactCodexDiagnostic
522
+ redactCodexPayload
523
+ sanitizeCodexDiagnostic
524
+ sanitizeCodexNotification
525
+ sanitizeCodexResponseError
526
+ sensitiveEnvironmentValues
527
+ utf8Head
574
528
  ```
575
529
 
576
530
  **`@mono-agent/agent-runtime/ai/runtime/model-refs.js`**
577
531
 
578
532
  ```text
579
- ACTIVE_RUNTIME_KINDS
580
- RESERVED_RUNTIME_KINDS
581
533
  RuntimeModelRef
582
- canonicalizeLegacyModelReference
583
- executionModeIncompatibilityReason
584
- isModelCompatibleWithExecutionMode
585
534
  normalizeRuntimeModelReference
586
535
  parseRuntimeModelReference
587
- sdkFromModelReference
588
536
  ```
589
537
 
590
538
  **`@mono-agent/agent-runtime/ai/runtime/registry.js`**
@@ -600,33 +548,21 @@ resolveRuntimeBridge
600
548
  runtimeCapabilities
601
549
  ```
602
550
 
603
- **`@mono-agent/agent-runtime/ai/streaming/codex-events.js`**
604
-
605
- ```text
606
- normalizeCodexItemEvent
607
- normalizeCodexItemType
608
- ```
609
-
610
551
  <!-- public-api-inventory:end -->
611
552
 
612
553
  ### When to reach for this vs. other JS agent runtimes
613
554
 
614
555
  `@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:
615
556
 
616
- - **Vercel AI SDK** — best when you're building a chat / generative-UI experience inside a React or Next.js app. `useChat`, `useCompletion`, streaming server components, and edge-runtime compatibility are their strengths. Their provider list is curated (Anthropic, OpenAI, Google, etc., via `@ai-sdk/*` packages); there's no Pi gateway, no Claude Code CLI, no Codex CLI app-server, and no per-call provider fallback. If you're rendering a streaming chat into a browser, use them. If you're orchestrating multi-turn autonomous work that must survive a rate-limited primary provider, use us.
617
- - **Claude Agent SDK** (`@anthropic-ai/claude-agent-sdk`) — first-party Anthropic SDK. Tight integration with Claude features (canUseTool, sub-agents, hooks, MCP). We *wrap* it as one of our six bridges and add transcript-resume across provider drops, a structured failure taxonomy, a tool-bloat guard with artifact persistence, and a provider fallback router. Context/window handling remains bridge-specific; the pi-native bridge drives its own compaction recovery. Reach for the bare Anthropic SDK when you only ever talk to Claude and don't need cross-provider portability or resume.
557
+ - **Vercel AI SDK** — best when you're building a chat / generative-UI experience inside a React or Next.js app. `useChat`, `useCompletion`, streaming server components, and edge-runtime compatibility are their strengths. Their provider list is curated (Anthropic, OpenAI, Google, etc., via `@ai-sdk/*` packages); there's no Pi gateway and no per-call provider fallback. If you're rendering a streaming chat into a browser, use them. If you're orchestrating multi-turn autonomous work that must survive a rate-limited primary provider, use us.
558
+ - **Claude Agent SDK** (`@anthropic-ai/claude-agent-sdk`) — first-party Anthropic SDK. Tight integration with Claude features (canUseTool, sub-agents, hooks, MCP). We reach Anthropic models through the Pi gateway instead, and add transcript-resume across provider drops, a structured failure taxonomy, a tool-bloat guard with artifact persistence, and a provider fallback router. Reach for the bare Anthropic SDK when you only ever talk to Claude and don't need cross-provider portability or resume.
618
559
  - **Mastra** — a workflow engine + memory + RAG stack. Different category: it's the layer *above* a runtime. You can layer Mastra workflows on top of `@mono-agent/agent-runtime` if you want both.
619
560
  - **OpenAI Agents SDK** — first-party OpenAI SDK. Same trade-off as the Claude Agent SDK: tight integration with OpenAI, no other providers. Pi providers in our runtime cover OpenAI plus a dozen others through a single API.
620
561
  - **LangChain.js** — kitchen sink with deep abstraction stacks. We're deliberately lean; if you want chains, agents, vector stores, and parsers under one umbrella, LangChain is built for that. If you want a focused runtime kernel, use us.
621
562
 
622
563
  **What we natively bridge (no extra packages):**
623
564
 
624
- - Anthropic Claude via the Claude Agent SDK (`claude` SDK).
625
- - Anthropic Claude via the `claude` Code CLI binary.
626
- - OpenAI's Codex via the `codex` app-server CLI.
627
- - OpenCode providers via an isolated, password-authenticated `opencode` app-server.
628
- - Any ACP v1 stdio agent resolved by the host from an `acp:<profile-id>` reference.
629
- - OpenAI, Google Gemini, AWS Bedrock, OpenRouter, xAI, Groq, Mistral, Perplexity, DeepSeek, Ollama, LlamaCPP, GLM, Vercel AI Gateway, GitHub Copilot, Gemini CLI — all through the Pi (`@earendil-works/pi-ai`) provider gateway, which our SDK adapter speaks directly.
565
+ - Anthropic, OpenAI-Codex, OpenAI, Google Gemini, AWS Bedrock, OpenRouter, xAI, Groq, Mistral, Perplexity, DeepSeek, Ollama, LlamaCPP, GLM, Vercel AI Gateway, GitHub Copilot, Gemini CLI — all through the Pi (`@earendil-works/pi-ai`) provider gateway, which our SDK adapter speaks directly.
630
566
 
631
567
  **At-a-glance:**
632
568
 
@@ -646,81 +582,28 @@ normalizeCodexItemType
646
582
 
647
583
  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.
648
584
 
649
- ### Picking a backend
585
+ ### Model references
650
586
 
651
- The runtime picks a backend from `options.model` + `options.executionMode`:
587
+ There is one runtime. A `model` is a parsed
588
+ `{ provider, model, reference }` object whose `reference` must be the canonical
589
+ `<provider>:<model>` spelling; convert reference strings with
590
+ `parseRuntimeModelReference()` before calling `run()` — `run()` does not parse
591
+ strings.
652
592
 
653
- | `model.sdk` | `executionMode` | Backend |
654
- |---|---|---|
655
- | `"claude"` | `"sdk"` (or omitted) | Claude SDK |
656
- | `"claude"` | `"cli"` | `claude` CLI |
657
- | `"pi"` | `"sdk"` (or omitted) | Pi SDK |
658
- | `"codex"` | `"cli"` | Codex app-server CLI |
659
- | `"opencode"` | `"cli"` | Isolated OpenCode app-server CLI |
660
- | `"acp"` | `"acp"` | ACP v1 stdio client |
661
-
662
- A `model` is a parsed `{ sdk, model, provider? }` object. Convert canonical
663
- strings such as `"pi:openai:gpt-5.5"` with
664
- `parseRuntimeModelReference()` before calling `run()`.
665
-
666
- #### ACP v1 host contract
667
-
668
- ACP references are canonical `acp:<profile-id>` strings and always use the
669
- dedicated `executionMode: "acp"`. The host supplies
670
- `resolveAcpProfile(profileId, context)` either to `createRuntime()` or per run;
671
- per-run callbacks win. A profile contains an absolute executable command,
672
- literal arguments, an exact child environment, ownership declarations for
673
- configuration/workspace/MCP, explicit capability policy, and bounded process
674
- limits. The runtime never invokes a shell or inherits `process.env`.
675
-
676
- Client-owned filesystem, terminal, permission, and elicitation behavior must be
677
- provided as callbacks and is advertised only when enabled. The runtime-adapter
678
- facade injects mono-agent's real sandbox implementation; direct kernel callers
679
- must provide their own when policy requires it. Every owned stdio bridge is
680
- closed after the operation with stdin close, TERM, then bounded KILL escalation.
681
- Callback payloads retain their typed operation fields but omit raw protocol
682
- session ids, extension metadata, and copied raw-id strings. Session-scoped
683
- callbacks receive the corresponding opaque handle as
684
- `AcpCallbackContext.providerSessionId`; request ids are opaque host correlation
685
- tokens as well.
686
- Session-update dispatch reads only validated own protocol fields. If a valid
687
- transport frame is too structurally complex for the bounded host sanitizer,
688
- the turn fails explicitly as `provider_protocol` instead of emitting a partial
689
- tool, plan, or message event.
690
-
691
- ACP provider-session ids and list cursors are confidential, authenticated v2
692
- handles bound to their token kind and profile. The host must supply an exact
693
- 32-byte binary `acpSessionTokenKey` for every task run, list, validation, and
694
- delete operation. Call
695
- `validateAcpProviderSessionId(handle, expectedProfileId, key)` at untrusted
696
- ingress. Keep the key stable and secret across host restarts; changing it
697
- invalidates every outstanding handle. Legacy `acp:v1:` and `acp-cursor:v1:`
698
- values are rejected.
699
-
700
- Preserve each returned handle byte-for-byte and pass it back only to the
701
- matching high-level resume, list, validation, or delete operation. Encryption
702
- uses a fresh nonce, so two handles for the same remote id are not equality
703
- keys. Raw protocol session ids, cursors, token keys, and transport connections
704
- remain private runtime state and are omitted from profile resolver context,
705
- callbacks, and diagnostics. Under the default `auto` recovery policy, the
706
- client prefers `session/resume`, then `session/load`, and finally a fresh
707
- session when neither capability is advertised. Explicit `resume` or `load`
708
- policies fail closed if missing. Stable usage comes from the latest typed
709
- `usage_update` notification; unstable `PromptResponse.usage` is ignored.
593
+ #### ACP
594
+
595
+ The ACP *client* runtime backend was removed in 0.21.0. `mono-agent bridge acp`
596
+ serving ACP to clients is unaffected and lives in `@mono-agent/agent-app`.
710
597
 
711
598
  ### `createRuntime(host)`
712
599
 
713
- Pass host-level integration once at boot. Keys are optional unless the selected
714
- backend contract requires them.
600
+ Pass host-level integration once at boot. Every key is optional.
715
601
 
716
602
  ```js
717
603
  createRuntime({
718
604
  // -- host callbacks --
719
605
  resolveCustomPricing, // (parsed) => NormalizedPricing | null
720
606
  resolvePiApiKey, // async (provider) => string | undefined
721
- resolveAcpProfile, // async (profileId, context) => AcpProfileDescriptor
722
- onAcpInteractionRequest, // async permission/elicitation fallback callback
723
- acpSessionTokenKey, // Uint8Array(32), required for ACP task/session-handle operations
724
607
  persistArtifact, // ({ filename, buffer, toolName, toolUseId }) => path | null
725
608
  onCompactionRecorded, // (compactionRow) => void — fired when the pi bridge
726
609
  // runs an automatic compaction (proactive or reactive
@@ -729,6 +612,8 @@ createRuntime({
729
612
  // -- tool runtime context (process-level config for the tool kernel) --
730
613
  workspace, // primary allowed root for path-based tools
731
614
  repoRoot, // secondary allowed root
615
+ additionalReadRoots, // extra realpath-contained file-tool read roots
616
+ additionalWriteRoots, // extra realpath-contained file-tool write roots
732
617
  ripgrepPath, // explicit path to `rg`; falls back to packaged binary, then PATH
733
618
  qaOutputDir, // fallback dir for Playwright MCP filename routing
734
619
  sandboxPolicy, // optional SandboxPolicy for tools and stdio MCP (enforced
@@ -744,9 +629,10 @@ createRuntime({
744
629
  observers: [],
745
630
 
746
631
  // -- approval gates (HITL) --
747
- // Optional. When set, the runtime asks the host before every tool call
748
- // whose risk tier is "medium" or "high" (and not session-allowlisted).
749
- // See the "Approval gates" section below for the request/response shape.
632
+ // Optional. When set, the runtime asks the host before every BUILT-IN tool
633
+ // call whose risk tier is "medium" or "high" (and not session-allowlisted).
634
+ // MCP-backed tools are not gated. See the "Approval gates" section below
635
+ // for the request/response shape.
750
636
  onToolApprovalRequest, // async (req) => { decision, reason? }
751
637
  toolRiskTiers: { Bash: "high" }, // per-tool tier override (low|medium|high)
752
638
  approvalDefaultRiskTier: "medium",
@@ -761,9 +647,6 @@ createRuntime({
761
647
  tempdirPrefix: "agent-runtime-cli-", // mkdtemp prefix for CLI provider scratch dirs
762
648
  providerModelPrefix: "agent", // id prefix for custom Pi providers
763
649
  doctorCommand: "agent-runtime doctor", // command suggested in tool error messages
764
- serviceName: "agent-runtime", // Codex app-server serviceName
765
- clientInfoName: "agent-runtime", // Codex app-server clientInfo.name
766
- clientInfoTitle: "Agent Runtime", // Codex app-server clientInfo.title
767
650
  },
768
651
  });
769
652
  ```
@@ -793,6 +676,10 @@ defensively cloned model snapshots; `getPiBuiltinModel(providerId, modelId)`
793
676
  returns one cloned snapshot or `undefined`; and
794
677
  `reasoningLevelsForPiModel(model)` translates a Pi model into mono-agent's
795
678
  reasoning vocabulary, including `none` rather than Pi's `off`.
679
+ Pi 0.85.1 exposes GPT-6 Astra through these same catalog APIs as
680
+ `openai:gpt-6-astra` for OpenAI API keys and
681
+ `openai-codex:gpt-6-astra` for Codex subscriptions; no separate model allowlist
682
+ is maintained by mono-agent.
796
683
  `resolvePiOAuthApiKey(providerId, credentials)` refreshes a caller-owned
797
684
  credential snapshot and returns `{ apiKey, newCredentials }` or `null`, while
798
685
  `loginPiOAuth(providerId, callbacks)` runs the selected supported login flow.
@@ -802,13 +689,25 @@ manual-code, and abort callbacks pass through unchanged.
802
689
  These functions deliberately do not expose Pi's mutable model collections or
803
690
  OAuth-provider registry.
804
691
 
692
+ Headless hosts can instead use `describePiProviderAuth()` to enumerate a
693
+ provider's OAuth/API-key methods, `checkPiProviderAuth()` to distinguish stored
694
+ or ambient credential detection without refreshing or making a model request,
695
+ and `loginPiProviderAuth()` to relay Pi's typed prompts and events. The last
696
+ function returns a credential to its caller but does not persist it; the owning
697
+ application must apply its own safe-store transaction and must never serialize
698
+ prompt answers into an operator projection. Abort can close supported Pi flows
699
+ and discard an uncooperative provider's late result, but it cannot undo a
700
+ provider-side grant or a filesystem mutation that already began; the owning app
701
+ must fence pre-mutation persistence and safely drain atomic promotion/cleanup.
702
+
805
703
  Returns:
806
704
 
807
705
  - `run(systemPrompt, options)` — async, runs one agent turn against the chosen backend.
808
706
  - `configureTools(next)` — update the tool runtime context after construction.
809
707
  - `syncSession(id)` — fsync provider-owned durable state before canonical history commits.
810
708
  - `refreshSession(id)` — guarantee the next resume cannot reuse process-local state; absence succeeds and cleanup uncertainty rejects.
811
- - `retireDurableSession(id, sessionsRoot)` — delete and verify every exact-id durable Pi transcript, including cold duplicates.
709
+ - `recoverSession(receipt, { appliedInputIds })` — validate and fsync an opted-in, settled durable Pi tail without executing the provider or appending a message. A host-owned `sessionRecovery: { runId, revision }` run option enables receipts; retries/backups strip that option and every returned receipt. Pending recovery blocks native resume. Failed/aborted assistant messages stay on disk and are filtered by Pi; completed tool evidence retains its native bytes. False or uncertain recovery requires host retirement. See [session recovery](../../docs/runtime/sessions-concurrency.md).
710
+ - `retireDurableSession(id, sessionsRoot)` — delete and verify every exact-id durable Pi transcript, including cold duplicates. If the matching live session is still unwinding after cancellation or failure, refresh it out of the registry and unlink and fsync its current JSONL; a post-runtime retry also removes any headerless exact-name file recreated by a late append. Uncoordinated calls retain the legacy rollback behavior; opted-in admitted durable calls can retain a provisional tail for host recovery.
812
711
  - `disposeSession(id)` / `invalidateSession(id)` / `disposeAllSessions()` — ordinary best-effort eviction, destructive live invalidation, and shutdown cleanup.
813
712
 
814
713
  #### `runtime.run(systemPrompt, options)`
@@ -818,163 +717,58 @@ Per-call options (a non-exhaustive selection):
818
717
  | Option | Type | Notes |
819
718
  |---|---|---|
820
719
  | `model` | `RuntimeModelRef` | **Required.** Pass the object returned by `parseRuntimeModelReference()`; `run()` does not parse strings. |
821
- | `executionMode` | `"sdk" \| "cli" \| "acp"` | Default `"sdk"`; ACP references require `"acp"`. |
822
720
  | `messages` | `Message[]` | Conversation history. |
823
721
  | `cwd` | `string` | Working directory for the agent's tools. |
824
722
  | `allowedTools` | `string[]` | Built-in tool allowlist. Default: all. |
825
723
  | `disallowedTools` | `string[]` | Block list. |
826
- | `nativeSubagents` | `object` | Caller-defined Claude native `Task` profiles. Direct Codex rejects configured teammate definitions because Codex owns its collaboration agents. |
827
- | `settingSources` | `("user" \| "project" \| "local")[]` | Claude Agent SDK filesystem settings opt-in. Omitted/empty disables those three sources; Anthropic managed settings still apply. |
828
- | `codexLoadProjectDocs` | `boolean` | Codex app-server repository-instruction opt-in. Omitted/false sets `project_doc_max_bytes=0`; true restores Codex defaults. Explicit `codexAppServerArgs` wins. |
829
- | `codexSandboxNetworkAccess` | `boolean` | Code-only Codex app-server per-turn network control. Only strict `true` enables it for plan/default/acceptEdits; omitted or any other value disables it. |
830
- | `mcpServers` | `Record<string, McpServerConfig>` | Configured MCP servers (stdio / sse / http); on direct Codex, each forwarded server authorizes its own tool calls. |
724
+ | `subagents` | `RuntimeSubagentsOptions` | In-process `Agent` delegation: profiles, caps, and the nested-run callback. This replaced the withdrawn caller-defined native teammate profiles. |
725
+ | `skills` / `skillsRoot` | `{name, description}[]` / `string` | Skills disclosed to the run and the directory holding `<name>/SKILL.md`. |
726
+ | `mcpServers` | `Record<string, McpServerConfig>` | Configured MCP servers (stdio / sse / http). |
831
727
  | `sandboxPolicy` | `SandboxPolicy` | Optional fail-closed sandbox policy for built-in tools and stdio MCP process startup. |
832
- | `webSearchConfig` | `{ backend?, endpoint?, codex?: { model? } }` | Run-scoped local SearXNG, ChatGPT-subscription Codex, and keyless WebSearch backend selection. |
728
+ | `webSearchConfig` | `{ backend?, maxRequestsPerRun?, searxng?: { endpoint? }, ollama?: { baseUrl?, apiKey?, apiKeyEnv?, trustPublicUrl? }, codex?: { model? } }` | Run-scoped ordered WebSearch selection and a 1–20 actual-provider-request budget (default 4). `auto` uses explicitly configured Ollama, configured SearXNG, Codex, then keyless; named modes are strict. The deprecated top-level `endpoint` remains a SearXNG compatibility alias. |
833
729
  | `webFetchConfig` | `{ render?, browserCommand? }` | Run-scoped static extraction and optional isolated browser-render policy. |
834
- | `piToolExecutionMode` | `"safe-parallel" \| "sequential"` | Pi built-in scheduling. Safe parallelism is the default; stateful/mutating and MCP tools stay sequential. |
730
+ | `piToolExecutionMode` | `"safe-parallel" \| "sequential"` | Pi built-in scheduling. Safe parallelism is the default; read-only tools may overlap only when the offered tool set contains no stateful/mutating or MCP tool. Otherwise Pi 0.85 serializes the whole batch. |
835
731
  | `maxTurns` | `number` | Hard cap on agent turns. |
836
- | `outputSchema` | `JSONSchema` | Requests structured JSON on capable bridges; see “Structured output” below for bridge-specific return behavior. |
732
+ | `outputSchema` | `JSONSchema` | Requests structured JSON; see “Structured output” below. |
837
733
  | `abortSignal` | `AbortSignal` | Cancel the run. |
838
- | `liveInput` | `AsyncIterable<{ body: string; id?: string; receivedAt?: string; acknowledge?: () => void; reject?: (error?: unknown) => void }>` | Stream of in-flight user messages for steering on capable bridges. A bridge acknowledges only after its native steering boundary accepts the message; per-attempt rejection permits router replay. Acknowledgement emits metadata-only `live_input_applied` telemetry. |
839
- | `claudeAgentQuery` | `typeof query` | Advanced programmatic/test seam for the Claude SDK bridge. When omitted, the bridge uses the runtime's pinned Claude Agent SDK. This is not a config field or telemetry value. |
734
+ | `liveInput` | `AsyncIterable<RuntimeLiveInputMessage>` | Stream of in-flight user messages. `accepted` reports native queue acceptance; `acknowledge` reports exact owned-operation transcript consumption; `uncertain` fences ambiguous delivery; proved-safe `reject` permits identified router replay. Stable nonblank IDs are required for cross-attempt replay. |
840
735
  | `onEvent` | `(event) => void` | Fired for every runtime event (assistant text, tool calls/results, applied live input, runtime warnings, structured output). |
736
+ | `persistArtifact` | `({ filename, buffer, toolName, toolUseId }) => path \| null` | Synchronous artifact sink for this run. A run value overrides the host default and route-attempt resolvers cannot replace it. |
841
737
  | `runId` | `string` | Tag this run for downstream callbacks (e.g. `onCompactionRecorded`). |
842
738
  | `providerSessionId` | `string` | Resume a prior provider session. |
843
- | `acpSessionTokenKey` | `Uint8Array(32)` | Required for ACP task runs when not bound at `createRuntime()`; keep it secret and stable across restarts. |
844
- | `runArtifactDir` | `string` | Used by some providers as the Playwright MCP filename target. |
845
- | `codexAppServerCommand` | `string` | Override the Codex CLI binary. |
846
- | `codexAppServerArgs` | `string[]` | Override the Codex CLI arguments. |
739
+ | `runArtifactDir` | `string` | Used as the Playwright MCP filename target. |
847
740
 
848
741
  `runtimeCapabilities()` and each descriptor returned by
849
- `listRuntimeBridges()` expose `tool_policy`. `"projected"` means the bridge can
850
- project restrictive `allowedTools` / `disallowedTools`; `"allow_all_only"`
851
- means it accepts only an effective unrestricted policy—`allowedTools` omitted
852
- or containing `"*"`, with no denied tools. A wildcard dominates named entries,
853
- so `["*", "Read"]` is allow-all. Named-only lists, `[]`, and any denylist remain
854
- unsupported on the direct Codex and direct OpenCode bridges and fail before
855
- provider startup. Built-in bridges always report this field; omission by a
856
- custom structural bridge means the capability is unknown.
857
-
858
- Live input is native on the Claude SDK, Codex app-server, and Pi bridges. The
859
- one-shot Claude CLI and direct OpenCode bridges advertise it as unsupported so
860
- routers skip them when a direct runtime call requires steering.
861
- After a native bridge invokes `acknowledge()`, the runtime emits exactly one
862
- `{ type: "live_input_applied", inputId, receivedAt? }` event for that logical
863
- run. It deliberately omits the guidance body. A fallback router reuses the same
864
- instrumented input stream, so replay or duplicate acknowledgement cannot emit a
865
- second applied event. A throwing host `acknowledge` or `reject` callback cannot
866
- change the native steering outcome; the Codex bridge reports it as a bounded
867
- `live_input_callback_failed` runtime warning.
868
-
869
- ### Provider-native subagents and project instructions
870
-
871
- Claude SDK runs are filesystem-isolated by default: mono-agent passes
872
- `settingSources: []`, which disables user, project, and local settings sources,
873
- including their `CLAUDE.md`, hooks, plugins, and `.claude/agents` profiles.
874
- Anthropic managed settings remain in force and may still configure hooks or
875
- plugins; `settingSources` is not a managed-policy bypass. Opt into only the
876
- needed sources, for example `settingSources: ["project"]`. User, project, and
877
- local settings may execute configured hooks and plugins, so enable only trusted
878
- settings and avoid opting in while running in an untrusted checkout. This
879
- option is SDK only. The Claude Code CLI performs its own settings discovery,
880
- and mono-agent does not pass it a `--setting-sources` value.
881
-
882
- Codex app-server owns its native collaboration agents and their profiles. The
883
- bridge observes and normalizes their lifecycle, but it does not synthesize a
884
- `collaborationMode` payload or inject caller-defined `nativeSubagents`
885
- teammates. A non-empty configured teammate list fails before app-server startup
886
- with `skipped_capability_mismatch`, allowing a fallback router to continue to a
887
- Claude route.
888
-
889
- Codex app-server runs disable automatic repository-instruction discovery by
890
- default with `project_doc_max_bytes=0`. Set `codexLoadProjectDocs: true` when
891
- Codex and its own collaboration agents should load repository instructions. If
892
- `codexAppServerArgs` is supplied, that explicit argument vector is authoritative
893
- and `codexLoadProjectDocs` does not alter it.
894
-
895
- `codexSandboxNetworkAccess` is a separate code-only, provider-native control.
896
- It is unrelated to `RuntimeRunOptions.sandboxPolicy`, which controls
897
- mono-agent's own sandbox and is not consumed by Codex's provider-owned tool
898
- loop. Only strict `true` enables network access for plan/read-only and
899
- default/acceptEdits/workspace-write turns; the no-tools probe remains offline
900
- and bypass remains danger-full-access. Combining workspace-write with network
901
- access grants repository read and network egress in the same turn. Prefer
902
- `permissionMode: "plan"` when only read-only browsing is needed.
903
-
904
- Provider-native and in-process delegation share `subagent_activity` telemetry.
905
- `subagent.id` is the canonical parent attachment key: the initiating parent
906
- tool-use id whenever the provider exposes it, or a stable synthetic key for an
907
- orphan lifecycle record. `nativeId` is an optional provider task/thread id for
908
- correlation only and never replaces that key.
909
- The normalized phases are `agent_started`, `started`, `completed`, `message`,
910
- and `agent_completed`. A `message` belongs to the child and must not be treated
911
- as parent answer text or as a completed tool call.
912
-
913
- Restrictive allowlists must still authorize the delegation surface. Include
914
- `Agent` for the in-process built-in. Claude-native teammate definitions add
915
- `Task` to an explicit allowed list automatically; filesystem profiles enabled
916
- only through `settingSources` require callers to include `Task` themselves.
917
- An explicit deny still wins. Direct Codex remains an allow-all-only bridge, so
918
- a named restrictive allowlist fails before provider startup rather than being
919
- silently widened.
920
-
921
- Returns:
922
-
923
- ```ts
924
- {
925
- text: string, // raw assistant text
926
- structuredResult?: any, // captured JSON on supported bridges
927
- structuredResultSource?: string, // where structuredResult came from
928
- events: RuntimeEvent[], // full event stream (for host-side parsing)
929
- usage: {
930
- input_tokens, output_tokens,
931
- cache_read_tokens, cache_creation_tokens,
932
- cost_usd,
933
- },
934
- durationMs: number,
935
- numTurns: number,
936
- model: string,
937
- effort: string,
938
- sdk: "claude" | "pi" | "codex" | "opencode",
939
- cancelled: boolean,
940
- error: string | null,
941
- errorDetails: object | null,
942
- failureKind: string | null,
943
- providerSessionId: string | null,
944
- runtimeWarnings: RuntimeWarning[],
945
- diagnostics: object,
946
- capabilitiesUsed: { // what the backend actually did this call
947
- prompt_cache_active: true|false|null,
948
- thinking_enabled: true|false|null,
949
- structured_output_enforced: boolean,
950
- subagent_invoked: true|false|null,
951
- mcp_servers_used: string[],
952
- native_subagents_used: string[],
953
- tool_compaction_applied: boolean,
954
- context_compaction_applied: true|false|null,
955
- },
956
- }
957
- ```
958
-
959
- `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.
960
-
961
- Successful provider requests may also emit exact context telemetry through
962
- `onEvent` and `result.events`:
963
-
964
- - `context_usage` is one provider-counted request snapshot, never the run's
965
- aggregate processed-token total. Pi emits it at each successful assistant
966
- `message_end`; Codex uses `thread/tokenUsage/updated.tokenUsage.last`; direct
967
- OpenCode requires a completed assistant message with native `tokens.total`;
968
- ACP normalizes the agent's exact `usage_update` `used`/`size` pair.
969
- Each event identifies the measured model and includes `contextWindow` only
970
- when the provider's own model metadata supplied it. The Claude bridges do not
971
- currently emit this event.
972
- - `context_compaction` is a lifecycle event with a stable `operationId`,
973
- `status` (`running`, `succeeded`, `skipped`, or `failed`), `sdk`, `trigger`,
974
- `timestamp`, and optional safe reason/model/count fields. Pi drives and emits
975
- its own lifecycle; Codex and OpenCode normalize their native notifications and
976
- suppress deprecated duplicate notifications. Pi's before/after counts are
977
- estimates and explicitly set `tokenCountsExact: false`.
742
+ `listRuntimeBridges()` expose `tool_policy`. The Pi bridge reports
743
+ `"projected"`, meaning it projects restrictive `allowedTools` /
744
+ `disallowedTools` as written, so named-only lists, `[]`, and denylists are all
745
+ supported. A wildcard dominates named entries, so `["*", "Read"]` is allow-all.
746
+ The `"allow_all_only"` value survives for custom structural bridges that accept
747
+ only an effective unrestricted policy; omission by such a bridge means the
748
+ capability is unknown.
749
+
750
+ Live input is native on the Pi bridge. Native acceptance is not consumption.
751
+ Consumption requires the exact Pi entry's user `message_end` in the one
752
+ main-lane prompt operation owned by the Mono run, plus the prompt result's
753
+ matching operation ID. This proves transcript incorporation only—not provider
754
+ receipt, answer use, or adherence. `live_input_consumed` records native
755
+ evidence; legacy `live_input_applied` follows only when host acknowledgement
756
+ returns exactly `recorded`. Void, `ignored`, throwing, or thenable callbacks
757
+ produce settlement-unconfirmed diagnostics instead of false success.
758
+
759
+ The logical-run fence keys stable IDs across retry and failover. The first
760
+ occurrence owns its body and callbacks; later same-ID occurrences are suppressed
761
+ as invalid duplicates. A proved pre-acceptance rejection or native queue removal
762
+ can replay the original owner. Accepted, consumed, and uncertain IDs never
763
+ replay. Missing IDs remain compatible but are exposed only in the first iterator
764
+ generation. Diagnostics contain metadata only, never guidance bodies.
765
+
766
+ ### Project instructions
767
+
768
+ The Pi runtime does not read another tool's filesystem settings, hooks, plugins,
769
+ or project documents. Subagents are the kernel's own in-process delegation
770
+ surface (the `Agent` tool), configured by the host rather than discovered from a
771
+ provider's on-disk profiles.
978
772
 
979
773
  ### Built-in tools
980
774
 
@@ -988,12 +782,13 @@ by one lazily started Node.js REPL child per run. You select them via
988
782
  `allowedTools`. Tool implementations honor:
989
783
 
990
784
  - `cwd` (required for path-based tools)
991
- - The runtime context's `workspace` / `repoRoot` allow-list (paths outside both, plus `/tmp` and `process.cwd()`, are rejected)
992
- - Output truncation with optional artifact persistence (`{toolArtifactDir}/tool-output/{runId}/...` when `toolArtifactDir` is configured)
785
+ - The runtime context's `workspace` / `repoRoot` allow-list (paths outside both, plus `/tmp` and `process.cwd()`, are rejected), with optional `additionalReadRoots` / `additionalWriteRoots` for narrowly scoped managed file-tool access. Additional roots require both the requested path and its realpath to stay inside the configured roots, so a symlink cannot escape them.
786
+ - Output truncation with optional host-provided artifact persistence. The configured app binds this per run under `artifacts.dir/tool-output/<runId>/`.
993
787
 
994
788
  The Pi-native tool context may structurally receive a host process-job
995
- controller. Only then do Exec and Bash add optional `background`; with no
996
- controller their schemas and foreground path are unchanged. A background call
789
+ controller. Only then do Exec and Bash add optional `background` and
790
+ `wake_on_completion`. A host can disclose lineage diagnostics independently
791
+ of that controller; a background request without one is rejected. A background call
997
792
  hands the exact prepared command to the controller and stops awaiting it. The
998
793
  kernel first creates a command-agnostic detached POSIX group leader; only after
999
794
  the host durably records its PID, equal PGID, and process incarnation does the
@@ -1004,14 +799,34 @@ contract package for this boundary.
1004
799
 
1005
800
  `NodeRepl` uses Node's default `node:repl` evaluator, so variables, `_`, `_error`, and loaded modules persist across calls in the same run. It supports multiline input and top-level `await`, resolves workspace-installed packages, and is closed with the run. Its child is prepared through the same sandbox seam as `Exec`/`Bash` and communicates through token-authenticated, length-prefixed JSON frames on ordinary stdin/stdout; abort, the fixed 120-second timeout, child exit, or hard output overflow resets the session. It deliberately has no session ids, persistent history, terminal commands, or package-install surface.
1006
801
 
1007
- `WebSearch` uses a configured loopback SearXNG endpoint and/or deterministic
1008
- public fallbacks that require no credentials. It canonicalizes and deduplicates
1009
- results, then fuses multiple query rankings. `WebFetch` extracts HTML, JSON,
1010
- feeds, PDFs, and text locally with
1011
- bounded redirects, bodies, headers, and retries. Config can opt into isolated
1012
- `agent-browser` rendering for sparse client-rendered HTML. One ephemeral
1013
- controller per run deduplicates identical calls and closes every browser
1014
- namespace at run end.
802
+ `WebSearch` uses explicit Ollama Web Search, a configured loopback SearXNG
803
+ endpoint, and deterministic public fallbacks. Strict backends do not fall
804
+ through, and `auto` tries explicitly configured Ollama configured SearXNG →
805
+ Codex keyless. Hosted Ollama bearer credentials are accepted only for the
806
+ exact official origin. Search canonicalizes and deduplicates results, trying
807
+ supplied alternate queries only if the primary has no relevant results. Codex
808
+ subscription search preserves a 10% allowance reserve. An optional host-injected
809
+ coordinator shares admission and cooldowns across processes. `WebFetch`
810
+ deterministically decodes and extracts HTML, JSON, feeds, PDFs, and text locally
811
+ with bounded redirects, bodies, headers, retries, and structured parser
812
+ failures. Config can opt into isolated `agent-browser` rendering for sparse
813
+ client-rendered HTML; an explicit `render: "always"` call is browser-first.
814
+ One ephemeral controller per run deduplicates identical calls and closes every
815
+ browser namespace at run end. WebFetch `start_line` and `max_lines` reuse a
816
+ bounded run-scoped document cache. Search and fetch use total deadlines of 60
817
+ and 45 seconds respectively, with bounded transport cleanup after cancellation.
818
+ WebSearch also shares a default hard budget of four actual provider requests
819
+ across route retries in one logical run. Cache hits, in-flight followers,
820
+ cooldown skips, and quota skips do not spend it. Rate-limited providers are
821
+ deferred for that run, with retry timing and an explicit next action returned
822
+ to the model; children and later runs receive fresh budgets.
823
+
824
+ Each normalized WebSearch result caps its title at 500 characters and its
825
+ snippet at 4,000 characters, including a visible truncation marker directing
826
+ the model to `WebFetch`. The ranked result body is capped at 64 KiB UTF-8;
827
+ lower-ranked snippets shrink before whole results are omitted, while the
828
+ control, metadata, filter, and balanced untrusted-result framing always remain.
829
+ Ollama receives the caller's effective 1–10 result limit as `max_results`.
1015
830
 
1016
831
  Pi runs with selected skills also expose `ReadSkill`. It returns the complete
1017
832
  skill instructions by default, including content beyond the former
@@ -1021,51 +836,59 @@ truncation is explicitly desired; omitting it is not a separate hidden limit.
1021
836
  The standard 256 KiB tool-payload guard still applies to oversized tool results.
1022
837
 
1023
838
  Override or extend the tool surface by passing `mcpServers` for MCP-backed tools.
1024
- On direct Codex normal runs, each valid server that survives translation into
1025
- the app-server config is the authorization boundary for the tools it exposes.
1026
- The bridge accepts Codex's synthesized `mcp_tool_call` elicitation for that exact
1027
- server without persisting an approval. Inherited or otherwise unconfigured
1028
- server names, genuine downstream MCP elicitations, and other app-server requests
1029
- remain fail-closed.
1030
-
1031
- This also applies under direct Codex `permissionMode: "plan"`: the read-only
1032
- sandbox constrains Codex-owned filesystem and command execution, but a declared
1033
- MCP tool can still change state managed by its server. Do not declare a server
1034
- whose complete tool surface is not authorized for the run.
839
+ Declaring a server is the authorization boundary: it exposes that server's whole
840
+ tool surface to the run, and `sandboxPolicy` does not reach state the server
841
+ owns on the other side of the connection. Do not declare a server whose complete
842
+ tool surface is not authorized for the run: there is no per-call gate behind it.
843
+ `onToolApprovalRequest` wraps the built-in tool set only (`getPiBuiltinTools`);
844
+ `initPiMcpTools` is initialized without an approval manager, so an MCP call runs
845
+ without asking the host, whatever risk tier is configured.
1035
846
 
1036
847
  ### Structured output
1037
848
 
1038
- Pass `options.outputSchema` (a JSON Schema). Claude SDK, Claude CLI, and Pi SDK
1039
- surface captured JSON as `result.structuredResult`. Codex app-server receives
1040
- the schema and reports that structured output was enforced, but its bridge
1041
- returns provider text rather than parsing `structuredResult`; hosts must parse
1042
- and validate `result.text`. Direct OpenCode rejects `outputSchema` with a typed
1043
- capability mismatch.
1044
-
1045
- The package does **not** validate captured output against your schema. Hosts run
1046
- their own validation (Zod, AJV, and similar) before applying domain effects.
849
+ Pass `options.outputSchema` (a JSON Schema). The Pi runtime surfaces captured
850
+ JSON as `result.structuredResult`.
1047
851
 
1048
852
  ### Provider fallback router
1049
853
 
1050
- `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.
854
+ `createRouterRuntime({ host, chain, 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. Only the primary's first attempt can keep a provider session; retries and backups are stateless and their successful results withhold `providerSessionId`. Entry `effort` is tri-state: a string fixes that route, `null` asks for provider default, and omission inherits the legacy per-run effort.
855
+
856
+ The primary's first attempt owns the provider session. Retries and failovers run
857
+ stateless with bounded transcript-tail replay. With coordinated durable Pi history,
858
+ any answer from a retry or backup retires the primary epoch. The next turn
859
+ cold-reseeds from canonical history; after a primary first-attempt success,
860
+ subsequent turns resume the new session and are eligible for provider caching.
861
+
862
+ On a warm turn whose primary attempt fails, the retry or backup attempt runs
863
+ stateless with the current message and a bounded snapshot of the failed attempt,
864
+ without the earlier conversation; the next turn reseeds from canonical history.
865
+
866
+ Provider attribution remains stable across attempts even when the router withholds
867
+ the resumable result id. Fresh stateless Pi calls use a private in-memory repository
868
+ to avoid colliding with, or deleting, a primary transcript sharing that attribution.
869
+ Lifecycle methods forward to the router's original inner runtime; a custom
870
+ `resolveAttempt().runtime` must not assume those methods target its own independent
871
+ session store.
1051
872
 
1052
873
  ```js
1053
- import { createRouterRuntime } from "@mono-agent/agent-runtime";
874
+ import { createRouterRuntime, parseRuntimeModelReference } from "@mono-agent/agent-runtime";
1054
875
 
876
+ // A RuntimeModelRef is { provider, model, reference }; `reference` is required
877
+ // and must be the canonical `<provider>:<model>` spelling, so build refs with
878
+ // parseRuntimeModelReference rather than writing the object literal by hand.
1055
879
  const router = createRouterRuntime({
1056
880
  host: { /* same shape as createRuntime */ },
1057
- routeSafety: "per-route-native",
1058
881
  // Backoff shape for same-model retries; per-route counts live on `attempts`.
1059
882
  retry: { backoffMs: 1000, maxBackoffMs: 15000 },
1060
883
  chain: [
1061
- { model: { sdk: "claude", model: "claude-sonnet-5" }, effort: "high", attempts: 2 },
1062
- { model: { sdk: "codex", model: "gpt-5.6-sol" }, effort: "xhigh" },
1063
- { model: { sdk: "pi", provider: "ollama", model: "gemma4:31b" }, effort: null },
884
+ { model: parseRuntimeModelReference("anthropic:claude-sonnet-5"), effort: "high", attempts: 2 },
885
+ { model: parseRuntimeModelReference("openai-codex:gpt-5.6-sol"), effort: "xhigh" },
886
+ { model: parseRuntimeModelReference("ollama:gemma4:31b"), effort: null },
1064
887
  ],
1065
888
  });
1066
889
 
1067
890
  const result = await router.run("...", { /* same shape as runtime.run */ });
1068
- console.log(result.failoverHistory, result.routeSafetyHistory);
891
+ console.log(result.failoverHistory);
1069
892
  ```
1070
893
 
1071
894
  Behaviour:
@@ -1073,32 +896,30 @@ Behaviour:
1073
896
  - Successful run on entry N → returns the result with `failoverHistory` set to attempts 0..N-1.
1074
897
  - Retryable provider failure → retries the SAME entry while it has `attempts` left (emitting `provider_retry_started` after a doubling backoff), then emits `provider_failover_started`, builds a transcript snapshot, and advances to the next entry. `attempts` defaults to `1` per entry, so the kernel is single-shot unless a host opts in — `@mono-agent/config` supplies the product default of 2 on the primary.
1075
898
  - Same-model retries fire only for transient subkinds (`overloaded`, `rate_limited`, `timeout`, `network`, `server_error`, `retryable_request`, terminated streams). A retry drops the route's provider session, since the failed attempt already appended to it, and appends its own `failoverHistory` entry carrying `retryIndex`.
1076
- - A retry is *not* a failover: `provider_route_safety` and `provider_failover_started` are emitted once per entry, and `provider_failover_completed` only fires when a genuinely different model answered.
899
+ - A retry is *not* a failover: `provider_failover_started` is emitted once per entry, and `provider_failover_completed` only fires when a genuinely different model answered.
1077
900
  - This is a whole-logical-turn retry sitting strictly outside the provider bridges' own transport retries. On a `pi` route, `attempts: 2` combined with pi's default `maxRetries: 2` means up to six provider stream starts.
1078
901
  - Context-window failure after bridge compaction recovery → never retries the same entry (a second identical request against the same window is a guaranteed second failure); preserves `failureKind: "context_limit"` in `failoverHistory` and tries the next entry; quota/output/max-turn `usage_limit` remains terminal.
1079
902
  - Provider auth failure → retries the next chain entry and preserves `failureKind: "provider_auth"` in `failoverHistory` for the failed attempt.
1080
903
  - Malformed request/config/billing-type non-retryable failure → returns immediately with `failoverHistory` containing the one attempt.
1081
904
  - Cancellation → returns immediately.
1082
905
  - Chain exhausted → `failureKind: "provider_unavailable_exhausted"`, `failoverHistory` lists every attempt.
1083
- - `uniform` safety keeps the shared monotonic runtime; `per-route-native` isolates
1084
- route runtimes and records each bounded safety contract/status.
1085
- - A `per-route-native` non-Pi route cannot project non-empty internal
1086
- `sandboxPolicy.protectedRoots`; the router records `safety_unavailable` and
1087
- advances before route resolution or provider invocation. Empty protected-root
1088
- sets preserve ordinary provider-native behavior, while Pi routes retain the
1089
- policy. The same invariant covers model routes reached through `Agent`
1090
- children.
1091
- - Pi route telemetry distinguishes `disabled`, fail-closed `mono-agent-srt`,
1092
- and `mono-agent-srt-unsafe-host-fallback`; the last describes a configured
1093
- policy that prefers SRT but permits host execution, not which branch ran.
906
+ - Every route is Pi-native, so the whole chain shares one monotonic runtime
907
+ contract. There is no per-route safety negotiation and no
908
+ `provider_route_safety` event: with a single contract there is nothing to
909
+ reconcile between routes. Pi routes retain the sandbox policy, including
910
+ routes reached through `Agent` children.
1094
911
  - A resolver-supplied Pi runtime may own provider credentials and lifecycle,
1095
912
  but must expose `configureTools()`: before every attempt the router replaces
1096
913
  its mutable tool context with the router's effective host/configured safety
1097
914
  inputs, while request-scoped overrides remain on that exact run. A runtime
1098
- that cannot accept this projection fails closed as `safety_unavailable`.
1099
- - Attempt-resolver failures are sanitized to `safety_unavailable`; resolver
1100
- credentials/options never enter result telemetry, and they advance to the next
1101
- entry rather than consuming the route's remaining `attempts`.
915
+ that cannot accept this projection fails the attempt before it runs.
916
+ - Attempt-resolver failures that missing `configureTools()` included — surface
917
+ as `failureKind: "provider_unavailable"`, with the error text fixed to
918
+ `The route attempt could not be resolved before execution.` (only a
919
+ `ResolverProtectedOptionError`, built from a repository-owned allowlist key,
920
+ reports its own message). Resolver credentials/options never enter result
921
+ telemetry, and such a failure advances to the next entry rather than consuming
922
+ the route's remaining `attempts`.
1102
923
  - `resolveAttempt` runs once per attempt — including every same-model retry — and
1103
924
  receives `{ attemptIndex, retryIndex }`, where `attemptIndex` stays the chain
1104
925
  index. Its `cleanup` runs after each attempt.
@@ -1116,12 +937,16 @@ The runtime emits structured events for everything that happens during a run —
1116
937
  A built-in aggregator covers the common metrics:
1117
938
 
1118
939
  ```js
1119
- import { createRuntime, createMetricsObserver } from "@mono-agent/agent-runtime";
940
+ import {
941
+ createMetricsObserver,
942
+ createRuntime,
943
+ parseRuntimeModelReference,
944
+ } from "@mono-agent/agent-runtime";
1120
945
 
1121
946
  const metrics = createMetricsObserver();
1122
947
  const runtime = createRuntime({ observers: [metrics] });
1123
948
 
1124
- await runtime.run("...", { model: { sdk: "claude", model: "claude-sonnet-4-6" } });
949
+ await runtime.run("...", { model: parseRuntimeModelReference("anthropic:claude-sonnet-4-6") });
1125
950
 
1126
951
  console.log(metrics.snapshot());
1127
952
  // {
@@ -1146,7 +971,7 @@ Notable new events emitted by the bridges:
1146
971
 
1147
972
  ### Approval gates (human-in-the-loop)
1148
973
 
1149
- 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.
974
+ Pass `onToolApprovalRequest` to gate built-in tool calls behind a runtime approval. The runtime calls your callback once per built-in tool invocation whose risk tier requires it, and pauses the agent until you respond.
1150
975
 
1151
976
  ```js
1152
977
  const runtime = createRuntime({
@@ -1175,32 +1000,86 @@ Responses:
1175
1000
  - `{ decision: "deny", reason? }` — block; the agent receives a tool error.
1176
1001
  - `{ decision: "always" }` — allow + session-allowlist for the run.
1177
1002
 
1178
- Backend coverage: Claude SDK (via `canUseTool`) and Pi SDK (via tool dispatch wrapping). Direct OpenCode projects `permissionMode` into its SDK rules and forwards native permission events through the callback; `default`/`acceptEdits` ask for reads, dynamic/custom permission names require explicit host approval in attended modes, and unsupported live-question/subagent permissions are always denied. OpenCode `plan` is read-only but not a secret boundary because path rules follow symlinks; use Pi plus native `srt` for filesystem confinement. Claude CLI and Codex app-server use their backend-native `permissionMode` / `approvalPolicy` instead of the per-call gate.
1179
-
1180
- Direct OpenCode uses a password-authenticated ephemeral loopback server and a unique private database for every run; that database is deleted after the server closes, so user sessions and saved approvals are never imported. Session resume and MCP injection are intentionally unsupported. Repo/global config and external plugins/skills are disabled, and the provider shell inherits only a narrow non-secret environment; built-in providers use the normal OpenCode auth store so token rotation persists. `OPENCODE_AUTH_CONTENT` is rejected, stable OpenCode CLI >=1.15.0 is required, and the user's native DB migration marker must pre-exist. Provider replies are always one-shot—even a host `always` decision stays only in the current mono-agent run. Positive `maxTurns`, explicit effort, structured output, live input, fast mode, native subagents, and runtime skill metadata fail with typed capability mismatches before startup rather than being silently ignored.
1181
-
1182
- Approval lifecycle is observable via `onEvent`:
1183
-
1184
- - `tool_approval_pending` — emitted before calling the host.
1185
- - `tool_approval_granted` — host approved.
1186
- - `tool_approval_denied` — host denied, timed out, threw, or no callback for a high-risk tool.
1003
+ Coverage is the Pi runtime's built-in tool set, via tool dispatch wrapping.
1004
+ MCP-backed tools are outside the gate — the MCP bridge is built without an
1005
+ approval manager so for those, declaring the server is the authorization
1006
+ boundary, not approving the call.
1187
1007
 
1188
1008
  ### Tool-result bloat handling
1189
1009
 
1190
1010
  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:
1191
1011
 
1192
1012
  1. Calls your `persistArtifact({ filename, buffer, toolName, toolUseId })` callback (if you supplied one).
1193
- 2. Substitutes a compact text reference in the agent's transcript.
1013
+ 2. For text-only overflow, substitutes a compact summary plus a UTF-8-safe
1014
+ 60/40 head/tail sample inside a new balanced untrusted frame. The explicit
1015
+ notice says the omitted middle may contain content and the retained tail is
1016
+ not the source ending. Image, binary, and mixed payloads stay summary-only.
1194
1017
  3. Emits a `runtime_warning` with `warning_kind: "tool_payload_truncated"` and the saved-paths array.
1195
1018
 
1196
- Hosts that don't supply `persistArtifact` get the truncation summary but no on-disk capture.
1019
+ Hosts that don't supply `persistArtifact`, or whose sink fails, get honest
1020
+ `persistence unavailable` text and no on-disk capture; the tool call still
1021
+ completes. The configured app writes owner-private raw, untrusted files under
1022
+ `artifacts.dir/tool-output/<runId>/`. Neither run-artifact retention nor
1023
+ tool-history retention cleans them up automatically.
1197
1024
 
1198
1025
  Before that byte cap runs, the builtin `Read` tool normalizes raster images with an edge longer than 8,000 px to fit within an 8,000 × 8,000 px box. Resizing preserves aspect ratio and the source format (resized BMP input becomes PNG), retains GIF/WebP animation, and never modifies the source file. Images already within the limit are embedded byte-for-byte unchanged.
1199
1026
 
1027
+ ### System instructions and transcript assembly
1028
+
1029
+ The host supplies stable system instructions separately from ordered messages.
1030
+ `@mono-agent/agent-harness` projects core/SOUL, identity and skill instructions
1031
+ by typed section ids; its full inspection prompt is not dispatched as system
1032
+ text. The runtime still appends its structured-output instruction here.
1033
+ The current user message starts with the host's latest Session/warm-skill
1034
+ envelope, followed by user/attachment text and recall. Tool authorization and
1035
+ delivery routing remain host-enforced.
1036
+
1037
+ On every cold/stateless/retry path, canonical historical text precedes the cold
1038
+ untrusted tool-history projection and current turn. Canonical replay preserves
1039
+ speaker and stored timestamp labels, but cannot reconstruct native reasoning
1040
+ signatures or tool calls. A true Pi resume skips all supplied prior messages and
1041
+ keeps its native transcript; a missing durable JSONL seeds prior messages once.
1042
+ History retention remains last-64 for the configured host. Compaction can replace
1043
+ an older native prefix and remove previously loaded skill bodies, so warm-skill
1044
+ guidance only says complete instructions may remain visible.
1045
+
1200
1046
  ### Context compaction
1201
1047
 
1202
- The sole pi bridge runs on pi-agent-core's native `AgentHarness`. pi performs **no**
1203
- automatic in-loop compaction, so the bridge drives it: before each turn it estimates the
1048
+ Summary preparation preserves labelled heads and tails of text tool results within
1049
+ Pi's 2,000-character serializer allowance. Copies retain tool identities and
1050
+ arguments; live results remain unchanged. Confirmed `Read`/`Write`/`Edit` results
1051
+ augment file metadata using `file_path`; failed or unmatched writes remain
1052
+ unresolved evidence. File metadata and supplemental file-operation evidence each
1053
+ have a 4 KiB bound, with omission counts. Record references remain unavailable
1054
+ without a proven host resolver.
1055
+
1056
+ A versioned focus supplements both Pi summary requests, including split turns:
1057
+ intent, approval constraints, open work, decisions, exact symbols, errors, and next
1058
+ action, distinguishing current instructions from superseded ones. Pi's prompts,
1059
+ cut rules, recent tail and output budgets remain unchanged. Empty, malformed,
1060
+ aborted and output-truncated summaries are rejected before persistence.
1061
+
1062
+ Each `context_compaction` event includes metadata-only `accounting` (version 1).
1063
+ Terminal events retain separately identified summary requests and their status,
1064
+ duration, provider tokens and cost, including rejected requests. Missing usage or
1065
+ cost stays `null`. Transcript and full-request before/after estimates are comparable
1066
+ and explicitly inexact; `afterSource` distinguishes a candidate preview from the
1067
+ persisted context. Summary text estimates, appended metadata bytes, tail
1068
+ estimate, policy and preparation omission counts are separate. Summary content,
1069
+ paths, tool arguments and raw cache keys are excluded from accounting.
1070
+
1071
+ With prompt-cache diagnostics enabled, assistant payload diagnostics and usage
1072
+ share a request ID and phase. `context_usage.providerCostUsd` preserves unknown
1073
+ provider cost as `null` alongside the existing compatibility cost field. Summary requests use operation-scoped IDs in
1074
+ compaction accounting; their usage never flows into assistant request totals.
1075
+ `scripts/summarize-prompt-cache.mjs` reports boundaries, separate assistant/summary
1076
+ cost, and first differing observed message fingerprints for full inputs. Differences
1077
+ are evidence of payload changes, not proof of cache misses; delta/unsupported
1078
+ prefix comparisons remain unknown. Pi summary requests retain their existing
1079
+ `cacheRetention: "none"` behavior.
1080
+
1081
+ The sole pi bridge runs on pi-agent-core's native `AgentHarness`. Pi supports native checkpoint and overflow compaction; mono-agent disables that path
1082
+ and drives guarded compaction itself: before each turn it estimates the
1204
1083
  running model's context usage and calls `AgentHarness.compact()` when near the window
1205
1084
  (proactive), and if a turn still overflows it compacts once and re-prompts exactly once
1206
1085
  only after a rebuilt-context preview proves positive reduction (reactive recovery).
@@ -1256,23 +1135,18 @@ These are stable but treated as advanced API. Most consumers should reach for `c
1256
1135
 
1257
1136
  ## Dependency Boundary
1258
1137
 
1259
- This package has zero `@mono-agent/*` workspace dependencies. Its runtime
1260
- dependencies are `@agentclientprotocol/sdk`, `@anthropic-ai/claude-agent-sdk`, `@anthropic-ai/sdk`,
1261
- `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`,
1262
- `@modelcontextprotocol/sdk`, `@opencode-ai/sdk`, `@vscode/ripgrep`,
1263
- `cross-spawn`, and `zod`.
1264
-
1265
- The runtime owns and exact-pins the compatible Pi pair at `0.80.6`; consumers
1266
- use the runtime's Pi façade rather than coordinating a second direct
1267
- `@earendil-works/pi-ai` dependency. Do not attempt to flatten the resulting
1268
- Anthropic dependency tree: Pi AI pins `@anthropic-ai/sdk@0.91.1`, while the
1269
- Claude Agent SDK requires `@anthropic-ai/sdk>=0.93.0` and the runtime supplies
1270
- its compatible newer SDK. Two isolated Anthropic SDK versions are therefore
1271
- expected. `RuntimeRunOptions.claudeAgentQuery` provides deterministic Claude
1272
- tests without mocking package resolution or sending real SDK traffic.
1273
- If a downstream test suite still needs Pi's faux-provider helpers, isolate that
1274
- fixture or keep its development-only Pi dependency on the runtime's exact
1275
- `0.80.6` version until the fixture is removed; a broad host range can otherwise
1138
+ This package has zero `@mono-agent/*` workspace dependencies. Its runtime core
1139
+ uses `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`,
1140
+ `@modelcontextprotocol/sdk`, `@vscode/ripgrep`, `cross-spawn`, and `zod`.
1141
+ Managed web extraction owns its direct parser dependencies: Defuddle,
1142
+ Readability, linkedom, Turndown, fast-xml-parser, and unpdf. Image inspection
1143
+ uses bmp-ts and sharp.
1144
+
1145
+ The runtime owns and exact-pins its Pi dependencies; consumers use the runtime's
1146
+ Pi façade rather than coordinating a second direct `@earendil-works/pi-ai`
1147
+ dependency. If a downstream test suite still needs Pi's faux-provider helpers,
1148
+ isolate that fixture or keep its development-only Pi dependency on the runtime's
1149
+ exact version until the fixture is removed; a broad host range can otherwise
1276
1150
  float Pi Agent Core's own upstream dependency independently of this façade.
1277
1151
 
1278
1152
  Sandbox enforcement is an injectable `RuntimeSandbox` seam.
@@ -1298,7 +1172,8 @@ runtime fails closed.
1298
1172
  - [Programmatic approvals and structured output](https://mono-agent-docs.vercel.app/programmatic/approval-and-structured-output/)
1299
1173
  shows the code-only host hooks.
1300
1174
  - [Local-first web research](https://mono-agent-docs.vercel.app/tools/web-research/)
1301
- documents SearXNG, extraction, retry, browser isolation, and sandbox policy.
1175
+ documents Ollama/SearXNG selection, extraction, retry, browser isolation, and
1176
+ sandbox policy.
1302
1177
  - [Reply files and MCP Apps](https://mono-agent-docs.vercel.app/tools/rich-replies/)
1303
1178
  documents the host bridge, browser sandbox, and lifecycle limits.
1304
1179
  - [Architecture](https://github.com/robertsreberski/mono-agent/blob/main/packages/agent-runtime/ARCHITECTURE.md)