@mono-agent/agent-runtime 0.20.10 → 0.20.14

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 (101) hide show
  1. package/MIGRATION.md +264 -25
  2. package/README.md +139 -448
  3. package/package.json +7 -43
  4. package/src/agent/tools/agent-tool.js +5 -5
  5. package/src/agent/tools/bash.js +1 -24
  6. package/src/agent/tools/codex-subscription-search.js +1 -1
  7. package/src/agent/tools/index.js +7 -0
  8. package/src/agent/tools/monitor.js +140 -0
  9. package/src/agent/tools/pi-bridge.js +92 -7
  10. package/src/agent/tools/shared/bash-environment.js +31 -0
  11. package/src/agent/tools/shared/monitors.js +274 -0
  12. package/src/agent/tools/shared/process-runner.js +23 -5
  13. package/src/ai/cost.js +13 -68
  14. package/src/ai/index.js +4 -17
  15. package/src/ai/pi-interop.js +65 -1
  16. package/src/ai/pi-oauth-compat.js +1 -1
  17. package/src/ai/providers/codex/app-server-client.js +592 -0
  18. package/src/ai/providers/pi-models.js +18 -10
  19. package/src/ai/providers/pi-native/compaction-driver.js +49 -21
  20. package/src/ai/providers/pi-native/harness-adapter.js +338 -0
  21. package/src/ai/providers/pi-native/result-builder.js +10 -10
  22. package/src/ai/providers/pi-native/session-lifecycle.js +95 -40
  23. package/src/ai/providers/pi-native/stream-subscriber.js +23 -5
  24. package/src/ai/providers/pi-native/turn-runner.js +34 -15
  25. package/src/ai/providers/pi-native.js +48 -22
  26. package/src/ai/runtime/capabilities.js +11 -56
  27. package/src/ai/runtime/model-refs.js +118 -153
  28. package/src/ai/runtime/registry.js +22 -56
  29. package/src/ai/runtime/router.js +48 -408
  30. package/src/ai/runtime/session-liveness.js +3 -4
  31. package/src/ai/runtime/sessions.js +4 -5
  32. package/src/ai/runtime/tool-policy.js +0 -2
  33. package/src/ai/types.js +12 -108
  34. package/src/index.js +0 -6
  35. package/src/runtime.js +8 -14
  36. package/types/agent/tools/agent-tool.d.ts +1 -2
  37. package/types/agent/tools/codex-subscription-search.d.ts +1 -1
  38. package/types/agent/tools/index.d.ts +1 -0
  39. package/types/agent/tools/monitor.d.ts +44 -0
  40. package/types/agent/tools/pi-bridge.d.ts +3 -2
  41. package/types/agent/tools/shared/bash-environment.d.ts +4 -0
  42. package/types/agent/tools/shared/monitors.d.ts +83 -0
  43. package/types/agent/tools/shared/process-runner.d.ts +11 -2
  44. package/types/ai/cost.d.ts +1 -2
  45. package/types/ai/index.d.ts +1 -4
  46. package/types/ai/pi-interop.d.ts +20 -0
  47. package/types/ai/providers/codex/app-server-client.d.ts +37 -0
  48. package/types/ai/providers/pi-native/harness-adapter.d.ts +56 -0
  49. package/types/ai/providers/pi-native/result-builder.d.ts +3 -3
  50. package/types/ai/providers/pi-native/session-lifecycle.d.ts +2 -1
  51. package/types/ai/providers/pi-native/stream-subscriber.d.ts +2 -2
  52. package/types/ai/providers/pi-native/turn-runner.d.ts +33 -6
  53. package/types/ai/providers/pi-native.d.ts +21 -4
  54. package/types/ai/runtime/capabilities.d.ts +21 -70
  55. package/types/ai/runtime/model-refs.d.ts +0 -24
  56. package/types/ai/runtime/router.d.ts +3 -10
  57. package/types/ai/runtime/tool-policy.d.ts +0 -2
  58. package/types/ai/types.d.ts +28 -250
  59. package/types/index.d.ts +0 -1
  60. package/src/ai/providers/acp-client.js +0 -1149
  61. package/src/ai/providers/acp-privacy.js +0 -124
  62. package/src/ai/providers/acp-public.js +0 -21
  63. package/src/ai/providers/acp-session-tokens.js +0 -282
  64. package/src/ai/providers/acp-transport.js +0 -356
  65. package/src/ai/providers/acp.js +0 -543
  66. package/src/ai/providers/claude-cli.js +0 -883
  67. package/src/ai/providers/claude-sandbox.js +0 -71
  68. package/src/ai/providers/claude-sdk-discovery-worker.js +0 -53
  69. package/src/ai/providers/claude-sdk-discovery.js +0 -352
  70. package/src/ai/providers/claude-sdk.js +0 -1127
  71. package/src/ai/providers/claude-subagent-activity.js +0 -719
  72. package/src/ai/providers/claude-subagents.js +0 -88
  73. package/src/ai/providers/codex-app.js +0 -2946
  74. package/src/ai/providers/opencode-app.js +0 -1109
  75. package/src/ai/providers/opencode-discovery.js +0 -39
  76. package/src/ai/providers/opencode-server.js +0 -508
  77. package/src/ai/runtime/context-windows.js +0 -46
  78. package/src/ai/runtime/fast-mode.js +0 -8
  79. package/src/ai/streaming/codex-events.js +0 -146
  80. package/src/ai/streaming/opencode-events.js +0 -59
  81. package/types/ai/providers/acp-client.d.ts +0 -227
  82. package/types/ai/providers/acp-privacy.d.ts +0 -25
  83. package/types/ai/providers/acp-public.d.ts +0 -7
  84. package/types/ai/providers/acp-session-tokens.d.ts +0 -41
  85. package/types/ai/providers/acp-transport.d.ts +0 -45
  86. package/types/ai/providers/acp.d.ts +0 -93
  87. package/types/ai/providers/claude-cli.d.ts +0 -305
  88. package/types/ai/providers/claude-sandbox.d.ts +0 -79
  89. package/types/ai/providers/claude-sdk-discovery-worker.d.ts +0 -1
  90. package/types/ai/providers/claude-sdk-discovery.d.ts +0 -97
  91. package/types/ai/providers/claude-sdk.d.ts +0 -138
  92. package/types/ai/providers/claude-subagent-activity.d.ts +0 -53
  93. package/types/ai/providers/claude-subagents.d.ts +0 -18
  94. package/types/ai/providers/codex-app.d.ts +0 -151
  95. package/types/ai/providers/opencode-app.d.ts +0 -96
  96. package/types/ai/providers/opencode-discovery.d.ts +0 -4
  97. package/types/ai/providers/opencode-server.d.ts +0 -20
  98. package/types/ai/runtime/context-windows.d.ts +0 -9
  99. package/types/ai/runtime/fast-mode.d.ts +0 -2
  100. package/types/ai/streaming/codex-events.d.ts +0 -40
  101. 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,8 @@ 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`.
31
29
 
32
30
  Create one runtime for a host, parse a model reference, and run a turn:
33
31
 
@@ -39,8 +37,7 @@ import {
39
37
 
40
38
  const runtime = createRuntime({ workspace: process.cwd() });
41
39
  const result = await runtime.run("You are a concise repository assistant.", {
42
- model: parseRuntimeModelReference("claude:claude-sonnet-4-6"),
43
- executionMode: "sdk",
40
+ model: parseRuntimeModelReference("anthropic:claude-sonnet-4-6"),
44
41
  messages: [{ role: "user", content: "Summarize README.md." }],
45
42
  cwd: process.cwd(),
46
43
  allowedTools: ["Read"],
@@ -61,8 +58,7 @@ capability. The host then explicitly intersects the two reviewed protocol
61
58
  revisions, reads only the originating tool's declared `ui://` resource, and
62
59
  receives one exact connection capability. Successful registration retains that
63
60
  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.
61
+ eviction closes the client, transport, and sandbox cleanup.
66
62
  See [Reply files and MCP Apps](https://mono-agent-docs.vercel.app/tools/rich-replies/).
67
63
 
68
64
  ## Architecture
@@ -73,7 +69,7 @@ only after a run selects a matching model reference and execution mode:
73
69
  ### Data flow
74
70
 
75
71
  1. `createRuntime()` binds host callbacks and creates an isolated tool context.
76
- 2. `resolveRuntimeBridge()` checks the six static bridge descriptors in order.
72
+ 2. `resolveRuntimeBridge()` resolves the single static bridge descriptor.
77
73
  3. The selected descriptor lazily imports its provider implementation.
78
74
  4. The bridge prepares the runtime inputs it supports, including managed or MCP
79
75
  tools only where that bridge can represent them, and streams normalized
@@ -91,7 +87,7 @@ only after a run selects a matching model reference and execution mode:
91
87
  | --- | --- |
92
88
  | `src/runtime.js` | Host binding, per-instance tool context, bridge dispatch, and observer flushing |
93
89
  | `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 |
90
+ | `src/ai/providers/` | Pi provider integrations |
95
91
  | `src/agent/tools/` | Managed tools, MCP adaptation, output limits, and the injectable sandbox seam |
96
92
  | `src/agent/` | Approvals, allowlists, transcript snapshots, and compaction policy helpers |
97
93
 
@@ -114,11 +110,6 @@ actually supplies:
114
110
  | Bridge | Genuine tool-result distinctions | Conservative fallback |
115
111
  | --- | --- | --- |
116
112
  | 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
113
 
123
114
  The runtime never derives a state from result prose. Structured timeout, signal,
124
115
  non-zero exit, completed success, and a specific non-runtime, non-cancellation
@@ -157,10 +148,9 @@ provider-supplied known kind when available and otherwise `runtime_error`.
157
148
  | API | Use it for |
158
149
  | --- | --- |
159
150
  | `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
151
  | `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 |
152
+ | `parseRuntimeModelReference()` | Convert a canonical `<provider>:<model>` string into the object required by `run()` |
153
+ | `listRuntimeBridges()` / `runtimeCapabilities()` | Inspect the built-in bridge descriptor without loading provider implementations |
164
154
  | `createPiOAuthApiKeyResolver()` | Bind a host-owned Pi auth file with refresh-safe writes |
165
155
  | `listPiBuiltinModels()` / `getPiBuiltinModel()` | Read cloned snapshots from the runtime-owned, exact-pinned Pi model catalog without importing Pi directly |
166
156
  | `resolvePiOAuthApiKey()` / `loginPiOAuth()` | Use the runtime-owned Pi OAuth implementation without importing Pi's mutable provider registry |
@@ -178,25 +168,19 @@ Every symbol exported by each public code entrypoint is listed below.
178
168
  **`@mono-agent/agent-runtime`**
179
169
 
180
170
  ```text
181
- ACP_PROTOCOL_VERSION
182
- ACTIVE_RUNTIME_KINDS
183
171
  ALLOWLIST_MODE_ALL
184
172
  ALLOWLIST_MODE_CUSTOM
185
173
  APPROVAL_DECISIONS
186
- AcpCallbackContext
187
- AcpClientError
188
- AcpClientHostOptions
189
- AcpInteractionRequest
190
- AcpListedSession
191
- AcpProfileDescriptor
192
- AcpSessionListResult
193
174
  BINARY_BLOAT_TOOLS
194
175
  BridgeSpec
195
- CLAUDE_SDK_CATALOG_VERSION
196
176
  DEFAULT_RUNTIME_BRAND
197
177
  DEFAULT_TOOL_BLOAT_CONFIG
198
178
  MAX_TOOL_RESULT_BYTES
199
- RESERVED_RUNTIME_KINDS
179
+ PiBuiltinModelSnapshot
180
+ PiBuiltinProviderSnapshot
181
+ PiOAuthCredentialsSnapshot
182
+ PiOAuthLoginCallbacks
183
+ PiReasoningLevel
200
184
  RISK_TIERS
201
185
  RUNTIME_CAPABILITIES
202
186
  RuntimeBridge
@@ -204,47 +188,34 @@ RuntimeBridgeDescriptor
204
188
  RuntimeBridgeId
205
189
  RuntimeModelRef
206
190
  UNKNOWN_CAPABILITY
207
- acpRuntimeBridge
208
- authenticateAcpProfile
209
191
  buildCapabilitiesUsed
210
192
  buildTranscriptTailSnapshot
211
- canonicalizeLegacyModelReference
212
193
  configureToolRuntime
213
194
  createApprovalManager
214
- createClaudeSdkDiscoveryIsolation
215
195
  createMetricsObserver
216
196
  createObserverHub
217
197
  createPiOAuthApiKeyResolver
218
198
  createRouterRuntime
219
199
  createRuntime
220
200
  createSessionRegistry
221
- curatedClaudeSdkModels
222
- deleteAcpSession
223
- discoverClaudeSdkModels
201
+ describePiBuiltinProvider
224
202
  disposeAllProviderSessions
225
203
  disposeProviderSession
226
- executionModeIncompatibilityReason
227
- generateAcpResponse
228
204
  generatePiNativeResponse
229
205
  getPiBuiltinModel
230
206
  inferAllowlistMode
231
207
  invalidateProviderSession
232
208
  isLikelyContextTermination
233
- isModelCompatibleWithExecutionMode
234
- listAcpSessions
235
209
  listPiBuiltinModels
210
+ listPiBuiltinProviders
236
211
  listRuntimeBridges
237
212
  loginPiOAuth
238
- logoutAcpProfile
239
213
  normalizeAllowlistMode
240
- normalizeClaudeSdkCatalog
241
- normalizeClaudeSdkModelId
242
214
  normalizeList
243
215
  normalizeRuntimeModelReference
244
216
  parseRuntimeModelReference
245
217
  parseStoredAllowlist
246
218
  piNativeRuntimeBridge
247
- probeAcpProfile
248
219
  readRuntimeBrand
249
220
  readToolRuntime
250
221
  reasoningLevelsForPiModel
@@ -258,12 +229,9 @@ resolvePiOAuthApiKey
258
229
  resolveRuntimeBrand
259
230
  resolveRuntimeBridge
260
231
  runtimeCapabilities
261
- sdkFromModelReference
262
232
  storedAllowlistMode
263
233
  syncProviderSession
264
234
  toolCompactionAppliedFromWarnings
265
- validateAcpProfileId
266
- validateAcpProviderSessionId
267
235
  wrapToolsWithApprovalGate
268
236
  ```
269
237
 
@@ -332,6 +300,8 @@ inferSkillsRoot
332
300
 
333
301
  ```text
334
302
  DEFAULT_CODEX_SEARCH_MODEL
303
+ DEFAULT_MONITOR_TIMEOUT_MS
304
+ MIN_MONITOR_TIMEOUT_MS
335
305
  bashToolImpl
336
306
  bashToolRun
337
307
  createWebToolController
@@ -343,9 +313,12 @@ grepToolImpl
343
313
  inspectCodexSubscriptionSearch
344
314
  isPathAllowed
345
315
  isWorkdirAllowed
316
+ monitorStopToolRun
317
+ monitorToolRun
346
318
  normalizeBackgroundBashTimeoutMs
347
319
  normalizeBackgroundTimeoutMs
348
320
  normalizeBashTimeoutMs
321
+ normalizeMonitorTimeoutMs
349
322
  normalizeProcessTimeoutMs
350
323
  performWebFetch
351
324
  performWebSearch
@@ -393,64 +366,42 @@ renderResumeSnapshot
393
366
  **`@mono-agent/agent-runtime/ai`**
394
367
 
395
368
  ```text
396
- ACP_PROTOCOL_VERSION
397
- ACTIVE_RUNTIME_KINDS
398
- AcpCallbackContext
399
- AcpClientError
400
- AcpClientHostOptions
401
- AcpInteractionRequest
402
- AcpListedSession
403
- AcpProfileDescriptor
404
- AcpSessionListResult
405
369
  BridgeSpec
406
- CLAUDE_SDK_CATALOG_VERSION
407
- RESERVED_RUNTIME_KINDS
370
+ PiBuiltinModelSnapshot
371
+ PiBuiltinProviderSnapshot
372
+ PiOAuthCredentialsSnapshot
373
+ PiOAuthLoginCallbacks
374
+ PiReasoningLevel
408
375
  RUNTIME_CAPABILITIES
409
376
  RuntimeBridge
410
377
  RuntimeBridgeDescriptor
411
378
  RuntimeBridgeId
412
379
  RuntimeModelRef
413
380
  UNKNOWN_CAPABILITY
414
- acpRuntimeBridge
415
- authenticateAcpProfile
416
381
  buildCapabilitiesUsed
417
- canonicalizeLegacyModelReference
418
- createClaudeSdkDiscoveryIsolation
419
382
  createMetricsObserver
420
383
  createObserverHub
421
384
  createSessionRegistry
422
- curatedClaudeSdkModels
423
- deleteAcpSession
424
- discoverClaudeSdkModels
385
+ describePiBuiltinProvider
425
386
  disposeAllProviderSessions
426
387
  disposeProviderSession
427
- executionModeIncompatibilityReason
428
- generateAcpResponse
429
388
  generatePiNativeResponse
430
389
  getPiBuiltinModel
431
390
  invalidateProviderSession
432
- isModelCompatibleWithExecutionMode
433
- listAcpSessions
434
391
  listPiBuiltinModels
392
+ listPiBuiltinProviders
435
393
  listRuntimeBridges
436
394
  loginPiOAuth
437
- logoutAcpProfile
438
- normalizeClaudeSdkCatalog
439
- normalizeClaudeSdkModelId
440
395
  normalizeRuntimeModelReference
441
396
  parseRuntimeModelReference
442
397
  piNativeRuntimeBridge
443
- probeAcpProfile
444
398
  reasoningLevelsForPiModel
445
399
  refreshProviderSession
446
400
  resolvePiOAuthApiKey
447
401
  resolveRuntimeBridge
448
402
  runtimeCapabilities
449
- sdkFromModelReference
450
403
  syncProviderSession
451
404
  toolCompactionAppliedFromWarnings
452
- validateAcpProfileId
453
- validateAcpProviderSessionId
454
405
  ```
455
406
 
456
407
  **`@mono-agent/agent-runtime/ai/cost.js`**
@@ -498,93 +449,32 @@ statsForCompletedChange
498
449
  formatLiveInputGuidance
499
450
  ```
500
451
 
501
- **`@mono-agent/agent-runtime/ai/providers/acp.js`**
452
+ **`@mono-agent/agent-runtime/ai/providers/codex/app-server-client.js`**
502
453
 
503
454
  ```text
504
- acpRuntimeBridge
505
- generateAcpResponse
506
- ```
507
-
508
- **`@mono-agent/agent-runtime/ai/providers/claude-cli.js`**
509
-
510
- ```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
455
+ CODEX_APP_SERVER_ARGS
456
+ CODEX_APP_SERVER_ISOLATED_ARGS
457
+ addOpaqueSensitiveValue
458
+ codexErrorMessage
545
459
  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
460
+ isCodexRequestTimeout
461
+ isSensitivePayloadField
462
+ normalizedSensitiveName
463
+ redactCodexDiagnostic
464
+ redactCodexPayload
465
+ sanitizeCodexDiagnostic
466
+ sanitizeCodexNotification
467
+ sanitizeCodexResponseError
468
+ sensitiveEnvironmentValues
469
+ utf8Head
574
470
  ```
575
471
 
576
472
  **`@mono-agent/agent-runtime/ai/runtime/model-refs.js`**
577
473
 
578
474
  ```text
579
- ACTIVE_RUNTIME_KINDS
580
- RESERVED_RUNTIME_KINDS
581
475
  RuntimeModelRef
582
- canonicalizeLegacyModelReference
583
- executionModeIncompatibilityReason
584
- isModelCompatibleWithExecutionMode
585
476
  normalizeRuntimeModelReference
586
477
  parseRuntimeModelReference
587
- sdkFromModelReference
588
478
  ```
589
479
 
590
480
  **`@mono-agent/agent-runtime/ai/runtime/registry.js`**
@@ -600,33 +490,21 @@ resolveRuntimeBridge
600
490
  runtimeCapabilities
601
491
  ```
602
492
 
603
- **`@mono-agent/agent-runtime/ai/streaming/codex-events.js`**
604
-
605
- ```text
606
- normalizeCodexItemEvent
607
- normalizeCodexItemType
608
- ```
609
-
610
493
  <!-- public-api-inventory:end -->
611
494
 
612
495
  ### When to reach for this vs. other JS agent runtimes
613
496
 
614
497
  `@mono-agent/agent-runtime` is purpose-built for **autonomous, long-running agent work** with provider portability and operational resilience as first-class concerns. It is *not* a streaming-chat UI kit. Where each peer fits:
615
498
 
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.
499
+ - **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.
500
+ - **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
501
  - **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
502
  - **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
503
  - **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
504
 
622
505
  **What we natively bridge (no extra packages):**
623
506
 
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.
507
+ - 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
508
 
631
509
  **At-a-glance:**
632
510
 
@@ -646,81 +524,28 @@ normalizeCodexItemType
646
524
 
647
525
  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
526
 
649
- ### Picking a backend
527
+ ### Model references
650
528
 
651
- The runtime picks a backend from `options.model` + `options.executionMode`:
529
+ There is one runtime. A `model` is a parsed
530
+ `{ provider, model, reference }` object whose `reference` must be the canonical
531
+ `<provider>:<model>` spelling; convert reference strings with
532
+ `parseRuntimeModelReference()` before calling `run()` — `run()` does not parse
533
+ strings.
652
534
 
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.
535
+ #### ACP
536
+
537
+ The ACP *client* runtime backend was removed in 0.21.0. `mono-agent bridge acp`
538
+ serving ACP to clients is unaffected and lives in `@mono-agent/agent-app`.
710
539
 
711
540
  ### `createRuntime(host)`
712
541
 
713
- Pass host-level integration once at boot. Keys are optional unless the selected
714
- backend contract requires them.
542
+ Pass host-level integration once at boot. Every key is optional.
715
543
 
716
544
  ```js
717
545
  createRuntime({
718
546
  // -- host callbacks --
719
547
  resolveCustomPricing, // (parsed) => NormalizedPricing | null
720
548
  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
549
  persistArtifact, // ({ filename, buffer, toolName, toolUseId }) => path | null
725
550
  onCompactionRecorded, // (compactionRow) => void — fired when the pi bridge
726
551
  // runs an automatic compaction (proactive or reactive
@@ -744,9 +569,10 @@ createRuntime({
744
569
  observers: [],
745
570
 
746
571
  // -- 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.
572
+ // Optional. When set, the runtime asks the host before every BUILT-IN tool
573
+ // call whose risk tier is "medium" or "high" (and not session-allowlisted).
574
+ // MCP-backed tools are not gated. See the "Approval gates" section below
575
+ // for the request/response shape.
750
576
  onToolApprovalRequest, // async (req) => { decision, reason? }
751
577
  toolRiskTiers: { Bash: "high" }, // per-tool tier override (low|medium|high)
752
578
  approvalDefaultRiskTier: "medium",
@@ -761,9 +587,6 @@ createRuntime({
761
587
  tempdirPrefix: "agent-runtime-cli-", // mkdtemp prefix for CLI provider scratch dirs
762
588
  providerModelPrefix: "agent", // id prefix for custom Pi providers
763
589
  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
590
  },
768
591
  });
769
592
  ```
@@ -818,163 +641,51 @@ Per-call options (a non-exhaustive selection):
818
641
  | Option | Type | Notes |
819
642
  |---|---|---|
820
643
  | `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
644
  | `messages` | `Message[]` | Conversation history. |
823
645
  | `cwd` | `string` | Working directory for the agent's tools. |
824
646
  | `allowedTools` | `string[]` | Built-in tool allowlist. Default: all. |
825
647
  | `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. |
648
+ | `subagents` | `RuntimeSubagentsOptions` | In-process `Agent` delegation: profiles, caps, and the nested-run callback. This replaced the withdrawn caller-defined native teammate profiles. |
649
+ | `skills` / `skillsRoot` | `{name, description}[]` / `string` | Skills disclosed to the run and the directory holding `<name>/SKILL.md`. |
650
+ | `mcpServers` | `Record<string, McpServerConfig>` | Configured MCP servers (stdio / sse / http). |
831
651
  | `sandboxPolicy` | `SandboxPolicy` | Optional fail-closed sandbox policy for built-in tools and stdio MCP process startup. |
832
652
  | `webSearchConfig` | `{ backend?, endpoint?, codex?: { model? } }` | Run-scoped local SearXNG, ChatGPT-subscription Codex, and keyless WebSearch backend selection. |
833
653
  | `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. |
654
+ | `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
655
  | `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. |
656
+ | `outputSchema` | `JSONSchema` | Requests structured JSON; see “Structured output” below. |
837
657
  | `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. |
658
+ | `liveInput` | `AsyncIterable<{ body: string; id?: string; receivedAt?: string; acknowledge?: () => void; reject?: (error?: unknown) => void }>` | Stream of in-flight user messages for steering the active run. The 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. |
840
659
  | `onEvent` | `(event) => void` | Fired for every runtime event (assistant text, tool calls/results, applied live input, runtime warnings, structured output). |
841
660
  | `runId` | `string` | Tag this run for downstream callbacks (e.g. `onCompactionRecorded`). |
842
661
  | `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. |
662
+ | `runArtifactDir` | `string` | Used as the Playwright MCP filename target. |
847
663
 
848
664
  `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
665
+ `listRuntimeBridges()` expose `tool_policy`. The Pi bridge reports
666
+ `"projected"`, meaning it projects restrictive `allowedTools` /
667
+ `disallowedTools` as written, so named-only lists, `[]`, and denylists are all
668
+ supported. A wildcard dominates named entries, so `["*", "Read"]` is allow-all.
669
+ The `"allow_all_only"` value survives for custom structural bridges that accept
670
+ only an effective unrestricted policy; omission by such a bridge means the
671
+ capability is unknown.
672
+
673
+ Live input is native on the Pi bridge.
674
+ After the bridge invokes `acknowledge()`, the runtime emits exactly one
862
675
  `{ type: "live_input_applied", inputId, receivedAt? }` event for that logical
863
676
  run. It deliberately omits the guidance body. A fallback router reuses the same
864
677
  instrumented input stream, so replay or duplicate acknowledgement cannot emit a
865
678
  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.
679
+ undo the steer that already reached the harness; it surfaces as a bounded
680
+ `live_input_failed` runtime warning and ends that run's live-input consumer, so
681
+ later guidance for the same run is no longer steered.
920
682
 
921
- Returns:
683
+ ### Project instructions
922
684
 
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`.
685
+ The Pi runtime does not read another tool's filesystem settings, hooks, plugins,
686
+ or project documents. Subagents are the kernel's own in-process delegation
687
+ surface (the `Agent` tool), configured by the host rather than discovered from a
688
+ provider's on-disk profiles.
978
689
 
979
690
  ### Built-in tools
980
691
 
@@ -1021,51 +732,42 @@ truncation is explicitly desired; omitting it is not a separate hidden limit.
1021
732
  The standard 256 KiB tool-payload guard still applies to oversized tool results.
1022
733
 
1023
734
  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.
735
+ Declaring a server is the authorization boundary: it exposes that server's whole
736
+ tool surface to the run, and `sandboxPolicy` does not reach state the server
737
+ owns on the other side of the connection. Do not declare a server whose complete
738
+ tool surface is not authorized for the run: there is no per-call gate behind it.
739
+ `onToolApprovalRequest` wraps the built-in tool set only (`getPiBuiltinTools`);
740
+ `initPiMcpTools` is initialized without an approval manager, so an MCP call runs
741
+ without asking the host, whatever risk tier is configured.
1035
742
 
1036
743
  ### Structured output
1037
744
 
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.
745
+ Pass `options.outputSchema` (a JSON Schema). The Pi runtime surfaces captured
746
+ JSON as `result.structuredResult`.
1047
747
 
1048
748
  ### Provider fallback router
1049
749
 
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.
750
+ `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. 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.
1051
751
 
1052
752
  ```js
1053
- import { createRouterRuntime } from "@mono-agent/agent-runtime";
753
+ import { createRouterRuntime, parseRuntimeModelReference } from "@mono-agent/agent-runtime";
1054
754
 
755
+ // A RuntimeModelRef is { provider, model, reference }; `reference` is required
756
+ // and must be the canonical `<provider>:<model>` spelling, so build refs with
757
+ // parseRuntimeModelReference rather than writing the object literal by hand.
1055
758
  const router = createRouterRuntime({
1056
759
  host: { /* same shape as createRuntime */ },
1057
- routeSafety: "per-route-native",
1058
760
  // Backoff shape for same-model retries; per-route counts live on `attempts`.
1059
761
  retry: { backoffMs: 1000, maxBackoffMs: 15000 },
1060
762
  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 },
763
+ { model: parseRuntimeModelReference("anthropic:claude-sonnet-5"), effort: "high", attempts: 2 },
764
+ { model: parseRuntimeModelReference("openai-codex:gpt-5.6-sol"), effort: "xhigh" },
765
+ { model: parseRuntimeModelReference("ollama:gemma4:31b"), effort: null },
1064
766
  ],
1065
767
  });
1066
768
 
1067
769
  const result = await router.run("...", { /* same shape as runtime.run */ });
1068
- console.log(result.failoverHistory, result.routeSafetyHistory);
770
+ console.log(result.failoverHistory);
1069
771
  ```
1070
772
 
1071
773
  Behaviour:
@@ -1073,32 +775,30 @@ Behaviour:
1073
775
  - Successful run on entry N → returns the result with `failoverHistory` set to attempts 0..N-1.
1074
776
  - 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
777
  - 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.
778
+ - 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
779
  - 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
780
  - 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
781
  - Provider auth failure → retries the next chain entry and preserves `failureKind: "provider_auth"` in `failoverHistory` for the failed attempt.
1080
782
  - Malformed request/config/billing-type non-retryable failure → returns immediately with `failoverHistory` containing the one attempt.
1081
783
  - Cancellation → returns immediately.
1082
784
  - 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.
785
+ - Every route is Pi-native, so the whole chain shares one monotonic runtime
786
+ contract. There is no per-route safety negotiation and no
787
+ `provider_route_safety` event: with a single contract there is nothing to
788
+ reconcile between routes. Pi routes retain the sandbox policy, including
789
+ routes reached through `Agent` children.
1094
790
  - A resolver-supplied Pi runtime may own provider credentials and lifecycle,
1095
791
  but must expose `configureTools()`: before every attempt the router replaces
1096
792
  its mutable tool context with the router's effective host/configured safety
1097
793
  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`.
794
+ that cannot accept this projection fails the attempt before it runs.
795
+ - Attempt-resolver failures that missing `configureTools()` included — surface
796
+ as `failureKind: "provider_unavailable"`, with the error text fixed to
797
+ `The route attempt could not be resolved before execution.` (only a
798
+ `ResolverProtectedOptionError`, built from a repository-owned allowlist key,
799
+ reports its own message). Resolver credentials/options never enter result
800
+ telemetry, and such a failure advances to the next entry rather than consuming
801
+ the route's remaining `attempts`.
1102
802
  - `resolveAttempt` runs once per attempt — including every same-model retry — and
1103
803
  receives `{ attemptIndex, retryIndex }`, where `attemptIndex` stays the chain
1104
804
  index. Its `cleanup` runs after each attempt.
@@ -1116,12 +816,16 @@ The runtime emits structured events for everything that happens during a run —
1116
816
  A built-in aggregator covers the common metrics:
1117
817
 
1118
818
  ```js
1119
- import { createRuntime, createMetricsObserver } from "@mono-agent/agent-runtime";
819
+ import {
820
+ createMetricsObserver,
821
+ createRuntime,
822
+ parseRuntimeModelReference,
823
+ } from "@mono-agent/agent-runtime";
1120
824
 
1121
825
  const metrics = createMetricsObserver();
1122
826
  const runtime = createRuntime({ observers: [metrics] });
1123
827
 
1124
- await runtime.run("...", { model: { sdk: "claude", model: "claude-sonnet-4-6" } });
828
+ await runtime.run("...", { model: parseRuntimeModelReference("anthropic:claude-sonnet-4-6") });
1125
829
 
1126
830
  console.log(metrics.snapshot());
1127
831
  // {
@@ -1146,7 +850,7 @@ Notable new events emitted by the bridges:
1146
850
 
1147
851
  ### Approval gates (human-in-the-loop)
1148
852
 
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.
853
+ 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
854
 
1151
855
  ```js
1152
856
  const runtime = createRuntime({
@@ -1175,15 +879,10 @@ Responses:
1175
879
  - `{ decision: "deny", reason? }` — block; the agent receives a tool error.
1176
880
  - `{ decision: "always" }` — allow + session-allowlist for the run.
1177
881
 
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.
882
+ Coverage is the Pi runtime's built-in tool set, via tool dispatch wrapping.
883
+ MCP-backed tools are outside the gate — the MCP bridge is built without an
884
+ approval manager so for those, declaring the server is the authorization
885
+ boundary, not approving the call.
1187
886
 
1188
887
  ### Tool-result bloat handling
1189
888
 
@@ -1257,22 +956,14 @@ These are stable but treated as advanced API. Most consumers should reach for `c
1257
956
  ## Dependency Boundary
1258
957
 
1259
958
  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
959
+ dependencies are `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`,
960
+ `@modelcontextprotocol/sdk`, `@vscode/ripgrep`, `cross-spawn`, and `zod`.
961
+
962
+ The runtime owns and exact-pins its Pi dependencies; consumers use the runtime's
963
+ Pi façade rather than coordinating a second direct `@earendil-works/pi-ai`
964
+ dependency. If a downstream test suite still needs Pi's faux-provider helpers,
965
+ isolate that fixture or keep its development-only Pi dependency on the runtime's
966
+ exact version until the fixture is removed; a broad host range can otherwise
1276
967
  float Pi Agent Core's own upstream dependency independently of this façade.
1277
968
 
1278
969
  Sandbox enforcement is an injectable `RuntimeSandbox` seam.