@mono-agent/agent-runtime 0.20.14 → 0.21.1

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 (85) hide show
  1. package/ARCHITECTURE.md +50 -11
  2. package/MIGRATION.md +30 -7
  3. package/README.md +219 -35
  4. package/package.json +9 -4
  5. package/src/agent/tool-bloat.js +145 -9
  6. package/src/agent/tools/agent-tool.js +104 -5
  7. package/src/agent/tools/bash.js +10 -2
  8. package/src/agent/tools/codex-subscription-search.js +122 -28
  9. package/src/agent/tools/exec.js +10 -2
  10. package/src/agent/tools/monitor.js +11 -2
  11. package/src/agent/tools/pi-bridge.js +70 -26
  12. package/src/agent/tools/read.js +3 -78
  13. package/src/agent/tools/shared/image.js +89 -0
  14. package/src/agent/tools/shared/monitors.js +22 -3
  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 +3 -1
  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/failure.js +3 -3
  29. package/src/ai/index.js +1 -0
  30. package/src/ai/observer.js +8 -0
  31. package/src/ai/pi-interop.js +156 -0
  32. package/src/ai/provider-check.js +131 -0
  33. package/src/ai/providers/pi-native/compaction-driver.js +45 -21
  34. package/src/ai/providers/pi-native/compaction-summary.js +140 -0
  35. package/src/ai/providers/pi-native/harness-adapter.js +40 -2
  36. package/src/ai/providers/pi-native/prompt-cache-diagnostics.js +103 -0
  37. package/src/ai/providers/pi-native/provider-attribution.js +102 -0
  38. package/src/ai/providers/pi-native/result-builder.js +28 -4
  39. package/src/ai/providers/pi-native/session-lifecycle.js +167 -24
  40. package/src/ai/providers/pi-native/stream-subscriber.js +30 -2
  41. package/src/ai/providers/pi-native/terminal-recovery.js +40 -0
  42. package/src/ai/providers/pi-native/turn-runner.js +245 -13
  43. package/src/ai/providers/pi-native.js +159 -40
  44. package/src/ai/runtime/live-input-events.js +250 -54
  45. package/src/ai/runtime/router.js +30 -11
  46. package/src/ai/tool-lifecycle.js +32 -18
  47. package/src/ai/types.js +26 -5
  48. package/src/runtime.js +24 -5
  49. package/types/agent/tool-bloat.d.ts +1 -1
  50. package/types/agent/tools/agent-tool.d.ts +4 -1
  51. package/types/agent/tools/bash.d.ts +5 -3
  52. package/types/agent/tools/codex-subscription-search.d.ts +6 -2
  53. package/types/agent/tools/exec.d.ts +5 -3
  54. package/types/agent/tools/monitor.d.ts +5 -2
  55. package/types/agent/tools/pi-bridge.d.ts +7 -5
  56. package/types/agent/tools/shared/image.d.ts +23 -0
  57. package/types/agent/tools/shared/monitors.d.ts +17 -2
  58. package/types/agent/tools/shared/process-jobs.d.ts +5 -1
  59. package/types/agent/tools/shared/process-runner.d.ts +3 -2
  60. package/types/agent/tools/shared/tool-context.d.ts +2 -0
  61. package/types/agent/tools/web-access-interstitial.d.ts +23 -0
  62. package/types/agent/tools/web-browser-render.d.ts +4 -1
  63. package/types/agent/tools/web-controller.d.ts +4 -2
  64. package/types/agent/tools/web-document-extractor.d.ts +27 -0
  65. package/types/agent/tools/web-fetch.d.ts +19 -24
  66. package/types/agent/tools/web-request.d.ts +20 -0
  67. package/types/agent/tools/web-search-output.d.ts +31 -0
  68. package/types/agent/tools/web-search-state.d.ts +21 -0
  69. package/types/agent/tools/web-search.d.ts +10 -45
  70. package/types/ai/index.d.ts +1 -0
  71. package/types/ai/observer.d.ts +6 -0
  72. package/types/ai/pi-interop.d.ts +61 -0
  73. package/types/ai/provider-check.d.ts +53 -0
  74. package/types/ai/providers/pi-native/compaction-driver.d.ts +2 -1
  75. package/types/ai/providers/pi-native/compaction-summary.d.ts +19 -0
  76. package/types/ai/providers/pi-native/harness-adapter.d.ts +3 -1
  77. package/types/ai/providers/pi-native/prompt-cache-diagnostics.d.ts +3 -0
  78. package/types/ai/providers/pi-native/provider-attribution.d.ts +26 -0
  79. package/types/ai/providers/pi-native/result-builder.d.ts +11 -1
  80. package/types/ai/providers/pi-native/session-lifecycle.d.ts +23 -5
  81. package/types/ai/providers/pi-native/terminal-recovery.d.ts +2 -0
  82. package/types/ai/providers/pi-native/turn-runner.d.ts +36 -5
  83. package/types/ai/runtime/live-input-events.d.ts +32 -8
  84. package/types/ai/tool-lifecycle.d.ts +4 -3
  85. package/types/ai/types.d.ts +140 -12
package/ARCHITECTURE.md CHANGED
@@ -106,6 +106,34 @@ Legacy aliases are canonicalized at host ingress when needed. The strict parser
106
106
  keeps the package boundary honest by rejecting reserved runtime IDs such as
107
107
  `openai:*`, `vercel:*`, and `claude-code:*`.
108
108
 
109
+ ## Host prompt assembly and replay
110
+
111
+ The harness's complete `prompt` and typed `sections` describe inspection context.
112
+ Dispatch uses its separate `systemPrompt`: core/SOUL → identity → stable skill
113
+ index/guidance → selected skill bodies → fixed host-envelope instruction. The
114
+ runtime adds its structured-output instruction without moving it into host
115
+ history. Session facts and the conditional warm-skill paragraph live in the
116
+ leading `<host_turn_context>` envelope on every current user message. That
117
+ latest envelope supersedes older copies; quoted surface labels, user/history,
118
+ memory and tool output remain untrusted. Tool enforcement and delivery routes
119
+ do not derive authority from envelope text.
120
+
121
+ Every harness-prepared cold run and stale-session retry supplies chronological canonical
122
+ messages with deterministic speaker/timestamp labels, then the bounded untrusted
123
+ tool-history projection, then one current user message (envelope, existing
124
+ speaker/preceding-message/user/attachment text, recall suffix). Legacy system/tool
125
+ history is labeled untrusted text, not system authority or native tool calls.
126
+ Inspection, canonical history and provider transcripts remain distinct: the host
127
+ never persists the envelope into canonical user text or memory capture and does
128
+ not use it as a recall query. Configured last-64 retention is unchanged.
129
+
130
+ Only the primary's first router attempt may retain a provider session. A retry or
131
+ backup after a warm primary failure has the current message and bounded
132
+ failed-attempt snapshot, without the earlier conversation. Its answer retires the
133
+ coordinated durable epoch; the next turn reseeds from canonical history. Fresh
134
+ stateless Pi calls use a private in-memory repository so their stable attribution
135
+ id cannot collide with or delete the primary's transcript.
136
+
109
137
  ## Run Lifecycle
110
138
 
111
139
  **Diagram summary:** The host calls `run()`, the runtime lazily loads one bridge,
@@ -298,25 +326,36 @@ provider exposes queue-after-turn), not durability/cost:
298
326
 
299
327
  The pi runtime is built on pi-agent-core's native `AgentHarness` (the hand-rolled
300
328
  bridge was removed once native reached parity); it owns the session and
301
- pi-ai-managed retry. `AgentHarness` itself has **no** automatic compaction, so
302
- the pi bridge drives it through a one-shot `session_before_compact` hook: before
329
+ pi-ai-managed retry. `AgentHarness` supports native checkpoint and overflow compaction; mono-agent
330
+ disables that path and drives its own guarded policy through a one-shot `session_before_compact` hook: before
303
331
  each turn it compares the full request estimate with an adaptive trigger, and if
304
332
  a turn still overflows it retries exactly once only after a preview verifies a
305
333
  positive reduction. Runs report `context_compaction_applied` as `true` (a
306
334
  compaction fired), `false` (enabled but not needed), or `null` (disabled via
307
335
  `runtime.compaction.enabled: false`).
308
336
 
309
- | Provider | Warm session | Resume across turns | Survives process restart |
337
+ `ai/providers/pi-native/compaction-summary.js` prepares copies for Pi's public
338
+ `compact()`: bounded tool-result heads/tails, confirmed built-in file operations,
339
+ and supplemental summary focus. Its model facade changes only summary context;
340
+ model, options, request context and other model methods are forwarded. Each
341
+ completion is accounted once at return or rejection. The driver attaches these
342
+ rows to terminal compaction events, preserving spend even when a preview rejects
343
+ persistence. File metadata and generated prose are measured separately. Native
344
+ cut rules and reserve math remain Pi-owned. Payload diagnostics correlate
345
+ assistant usage independently of these operation-scoped summary requests.
346
+
347
+ | Active bridge | Warm session | Resume across turns | Survives process restart |
310
348
  |---|---|---|---|
311
349
  | **pi** | Yes (pi `AgentHarness` + JSONL session repo) | session repo | Yes only with `piSessionsRoot` and the durable history/session transaction contract |
312
- | **claude-sdk** | No persistent process (stream closes at turn end) | `queryOptions.resume` | No (Anthropic-side id) |
313
- | **claude-cli** | No respawns `claude --resume` per turn (re-inits MCP) | `--resume` replay | No |
314
- | **codex-app** | Live subprocess thread (dies with the subprocess) | next turn on the thread, else replay | No |
315
- | **opencode-app** | No every run uses an isolated server and private database | Unsupported | No |
316
-
317
- Claude CLI and Codex only *approximate* a warm session (resume/replay), so do
318
- not assume warm-session latency wins there. Direct OpenCode is intentionally
319
- stateless across runs.
350
+
351
+ The current registry contains only Pi. A confirmed warm session omits host replay.
352
+ An unconfirmed durable reopen refreshes the handle and supplies canonical history:
353
+ true native resume skips seeding; missing JSONL creates an empty session and seeds
354
+ once. Native resume preserves assistant blocks, tool ids/arguments/results and
355
+ reasoning signatures. Cold reconstruction has only canonical text, so byte identity
356
+ with the original native transcript is not promised. Compaction replaces an older
357
+ prefix and keeps a tail; it may remove loaded skill bodies. Fallback routes remain
358
+ isolated and stateless, with the same cancellation/reset/failed-commit barriers.
320
359
 
321
360
  ## Essential Takeaway
322
361
 
package/MIGRATION.md CHANGED
@@ -25,7 +25,7 @@ before restarting one.
25
25
 
26
26
  ### Pi 0.85 dependency migration
27
27
 
28
- The runtime exact-pins Pi AI and Pi Agent Core at `0.85.0`; the TUI pins Pi TUI
28
+ The runtime exact-pins Pi AI and Pi Agent Core at `0.85.1`; the TUI pins Pi TUI
29
29
  at the same version. Pi's harness is now created asynchronously and exposes
30
30
  prompt, navigation, compaction, abort, event, and transcript operations through
31
31
  its `main` lane with an explicit operation context. mono-agent absorbs that API
@@ -60,6 +60,29 @@ use any of them, nothing changes:
60
60
  Note also that `openai-codex` and `opencode-go` are **Pi provider ids**, not
61
61
  references to the deleted bridges. Routes naming them keep working.
62
62
 
63
+ ### Web research configuration
64
+
65
+ SearXNG remains supported in strict mode and as the first configured `auto`
66
+ route. Its canonical JSON setting is now provider-scoped:
67
+
68
+ ```json
69
+ { "tools": { "web": { "search": {
70
+ "backend": "searxng",
71
+ "searxng": { "endpoint": "http://127.0.0.1:8088" }
72
+ } } } }
73
+ ```
74
+
75
+ The former `tools.web.search.endpoint` and
76
+ `MONO_AGENT_WEB_SEARCH_ENDPOINT` spellings remain compatibility aliases; no
77
+ immediate migration is required. Prefer `tools.web.search.searxng.endpoint` or
78
+ `MONO_AGENT_WEB_SEARCH_SEARXNG_ENDPOINT` when editing config. If both spellings
79
+ are present they must normalize to the same URL.
80
+
81
+ Ollama Web Search is additive and explicit-only: select `backend: "ollama"`.
82
+ It never joins `auto`. Local mode defaults to `http://127.0.0.1:11434`; hosted
83
+ `https://ollama.com` additionally requires `ollama.apiKeyEnv`. A hosted key is
84
+ never sent to a local or custom origin.
85
+
63
86
  ### Model reference grammar
64
87
 
65
88
  Old: `<sdk>:[<provider>:]<model>`. New: **`<provider>:<model>`**, split at the
@@ -671,14 +694,14 @@ falls back to its own env vars, exactly as returning `undefined` from the old ho
671
694
  did). **No host action needed** — `resolvePiApiKey` behaves as before.
672
695
 
673
696
  Current dependency pins: **`@earendil-works/pi-ai` and
674
- `@earendil-works/pi-agent-core` are both `0.85.0`** (the initial Pi 0.80
697
+ `@earendil-works/pi-agent-core` are both `0.85.1`** (the initial Pi 0.80
675
698
  migration landed at `0.80.5`, from `^0.79.1`). Pi 0.85's durable lane harness is
676
699
  adapted behind the runtime's existing public API. Compaction remains owned by
677
700
  mono-agent policy, and model-native `max` reasoning plus Pi's request-wide
678
701
  pricing tiers are preserved.
679
702
 
680
- Packed npm consumers resolve the runtime-owned exact Pi AI 0.85.0 copy for both
681
- the runtime and Agent Core's `^0.85.0` dependency. The release guard verifies
703
+ Packed npm consumers resolve the runtime-owned exact Pi AI 0.85.1 copy for both
704
+ the runtime and Agent Core's `^0.85.1` dependency. The release guard verifies
682
705
  both resolution paths independently.
683
706
 
684
707
  The 0.83 upgrade carries two upstream removals, both absorbed inside the runtime
@@ -735,7 +758,7 @@ a compatibility subpath.
735
758
 
736
759
  ## Version
737
760
 
738
- This guide describes the published `0.20.x` package contract. Keep
761
+ This guide describes the published `0.21.x` package contract. Keep
739
762
  `@mono-agent/agent-runtime`, `@mono-agent/runtime-adapter`, and other
740
763
  `@mono-agent/*` packages on the same lockstep version when upgrading. The paired
741
764
  runtime adapter no longer exposes `piReasoningSummary` in its run-options type.
@@ -756,7 +779,7 @@ Worklab's runtime fork:
756
779
  `@earendil-works/pi-ai`, its separate Pi version constraint, and local copies
757
780
  of provider bridge code. Move tests off Pi's faux-provider helpers too; until
758
781
  that is complete, isolate the fixture or pin its development-only dependencies
759
- to the exact Pi AI `0.85.0` and Pi Agent Core `0.85.0` compatibility pins
782
+ to the exact Pi AI `0.85.1` and Pi Agent Core `0.85.1` compatibility pins
760
783
  rather than floating ranges. Do not restore the
761
784
  removed `pi-sdk.js` subpath.
762
785
  3. **Use the public Pi surfaces.** Run models through
@@ -764,7 +787,7 @@ Worklab's runtime fork:
764
787
  `listPiBuiltinModels`, `getPiBuiltinModel`,
765
788
  `reasoningLevelsForPiModel`, `resolvePiOAuthApiKey`, and `loginPiOAuth` for
766
789
  catalog and OAuth integration. Those façades keep Pi provider objects and the
767
- exact Pi AI `0.85.0` and Pi Agent Core `0.85.0` compatibility pins inside the runtime. OAuth login adapters
790
+ exact Pi AI `0.85.1` and Pi Agent Core `0.85.1` compatibility pins inside the runtime. OAuth login adapters
768
791
  must supply `onAuth`, `onDeviceCode`, `onPrompt`, and `onSelect`; the façade
769
792
  rejects an incomplete callback contract before starting provider login.
770
793
  4. **Inject Claude tests.** Replace package-level mocks of
package/README.md CHANGED
@@ -27,6 +27,12 @@ pnpm add @mono-agent/agent-runtime
27
27
  Node.js 22.19 or newer is required. Pi is the only runtime, and it talks to
28
28
  providers over their SDKs, so no provider CLI has to be on `PATH`.
29
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.
35
+
30
36
  Create one runtime for a host, parse a model reference, and run a turn:
31
37
 
32
38
  ```js
@@ -47,6 +53,16 @@ if (result.error) throw new Error(result.error);
47
53
  console.log(result.text);
48
54
  ```
49
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
+
50
66
  `Glob` and `Grep` prefer the packaged `@vscode/ripgrep` binary on supported
51
67
  platforms. An explicit `ripgrepPath` wins, with `PATH` as the final fallback.
52
68
 
@@ -63,6 +79,12 @@ See [Reply files and MCP Apps](https://mono-agent-docs.vercel.app/tools/rich-rep
63
79
 
64
80
  ## Architecture
65
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
+
66
88
  The package uses a fixed registry of bridge descriptors and loads provider code
67
89
  only after a run selects a matching model reference and execution mode:
68
90
 
@@ -96,13 +118,23 @@ the [architecture guide](https://github.com/robertsreberski/mono-agent/blob/main
96
118
 
97
119
  ### Managed-tool lifecycle fidelity
98
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
+
99
128
  `RuntimeRunOptions.toolLifecycleSink` is an awaited host-owned boundary. The
100
129
  runtime sends redaction-eligible raw arguments/content plus stable provider call
101
130
  id and name; the host returns only record/sequence, persistence/truncation byte
102
131
  metadata, and opaque artifact references. That returned metadata is attached to
103
- the exact normalized `tool_use` / `tool_result` event rendered by clients. Sink
104
- failure is fail-soft and explicit (`persistence: "failed"` plus an error code),
105
- 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.
106
138
 
107
139
  Provider bridges only claim terminal distinctions their structured protocol
108
140
  actually supplies:
@@ -154,6 +186,8 @@ provider-supplied known kind when available and otherwise `runtime_error`.
154
186
  | `createPiOAuthApiKeyResolver()` | Bind a host-owned Pi auth file with refresh-safe writes |
155
187
  | `listPiBuiltinModels()` / `getPiBuiltinModel()` | Read cloned snapshots from the runtime-owned, exact-pinned Pi model catalog without importing Pi directly |
156
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 |
157
191
  | `createMetricsObserver()` | Aggregate normalized event, token, cache, cost, tool, error, turn, and approval metrics |
158
192
 
159
193
  Most hosts should use `@mono-agent/runtime-adapter` instead of importing deep
@@ -176,11 +210,18 @@ BridgeSpec
176
210
  DEFAULT_RUNTIME_BRAND
177
211
  DEFAULT_TOOL_BLOAT_CONFIG
178
212
  MAX_TOOL_RESULT_BYTES
213
+ PROVIDER_CHECK_PROMPT
179
214
  PiBuiltinModelSnapshot
180
215
  PiBuiltinProviderSnapshot
181
216
  PiOAuthCredentialsSnapshot
182
217
  PiOAuthLoginCallbacks
218
+ PiProviderAuthCheck
219
+ PiProviderAuthDescription
220
+ PiProviderAuthInteraction
221
+ PiProviderAuthPrompt
183
222
  PiReasoningLevel
223
+ ProviderCheckCode
224
+ ProviderCheckOutcome
184
225
  RISK_TIERS
185
226
  RUNTIME_CAPABILITIES
186
227
  RuntimeBridge
@@ -190,6 +231,8 @@ RuntimeModelRef
190
231
  UNKNOWN_CAPABILITY
191
232
  buildCapabilitiesUsed
192
233
  buildTranscriptTailSnapshot
234
+ checkPiProviderAuth
235
+ classifyProviderCheckFailure
193
236
  configureToolRuntime
194
237
  createApprovalManager
195
238
  createMetricsObserver
@@ -199,6 +242,7 @@ createRouterRuntime
199
242
  createRuntime
200
243
  createSessionRegistry
201
244
  describePiBuiltinProvider
245
+ describePiProviderAuth
202
246
  disposeAllProviderSessions
203
247
  disposeProviderSession
204
248
  generatePiNativeResponse
@@ -210,6 +254,7 @@ listPiBuiltinModels
210
254
  listPiBuiltinProviders
211
255
  listRuntimeBridges
212
256
  loginPiOAuth
257
+ loginPiProviderAuth
213
258
  normalizeAllowlistMode
214
259
  normalizeList
215
260
  normalizeRuntimeModelReference
@@ -228,6 +273,7 @@ resolveAllowlistMap
228
273
  resolvePiOAuthApiKey
229
274
  resolveRuntimeBrand
230
275
  resolveRuntimeBridge
276
+ runPiProviderCheck
231
277
  runtimeCapabilities
232
278
  storedAllowlistMode
233
279
  syncProviderSession
@@ -367,11 +413,18 @@ renderResumeSnapshot
367
413
 
368
414
  ```text
369
415
  BridgeSpec
416
+ PROVIDER_CHECK_PROMPT
370
417
  PiBuiltinModelSnapshot
371
418
  PiBuiltinProviderSnapshot
372
419
  PiOAuthCredentialsSnapshot
373
420
  PiOAuthLoginCallbacks
421
+ PiProviderAuthCheck
422
+ PiProviderAuthDescription
423
+ PiProviderAuthInteraction
424
+ PiProviderAuthPrompt
374
425
  PiReasoningLevel
426
+ ProviderCheckCode
427
+ ProviderCheckOutcome
375
428
  RUNTIME_CAPABILITIES
376
429
  RuntimeBridge
377
430
  RuntimeBridgeDescriptor
@@ -379,10 +432,13 @@ RuntimeBridgeId
379
432
  RuntimeModelRef
380
433
  UNKNOWN_CAPABILITY
381
434
  buildCapabilitiesUsed
435
+ checkPiProviderAuth
436
+ classifyProviderCheckFailure
382
437
  createMetricsObserver
383
438
  createObserverHub
384
439
  createSessionRegistry
385
440
  describePiBuiltinProvider
441
+ describePiProviderAuth
386
442
  disposeAllProviderSessions
387
443
  disposeProviderSession
388
444
  generatePiNativeResponse
@@ -392,6 +448,7 @@ listPiBuiltinModels
392
448
  listPiBuiltinProviders
393
449
  listRuntimeBridges
394
450
  loginPiOAuth
451
+ loginPiProviderAuth
395
452
  normalizeRuntimeModelReference
396
453
  parseRuntimeModelReference
397
454
  piNativeRuntimeBridge
@@ -399,6 +456,7 @@ reasoningLevelsForPiModel
399
456
  refreshProviderSession
400
457
  resolvePiOAuthApiKey
401
458
  resolveRuntimeBridge
459
+ runPiProviderCheck
402
460
  runtimeCapabilities
403
461
  syncProviderSession
404
462
  toolCompactionAppliedFromWarnings
@@ -554,6 +612,8 @@ createRuntime({
554
612
  // -- tool runtime context (process-level config for the tool kernel) --
555
613
  workspace, // primary allowed root for path-based tools
556
614
  repoRoot, // secondary allowed root
615
+ additionalReadRoots, // extra realpath-contained file-tool read roots
616
+ additionalWriteRoots, // extra realpath-contained file-tool write roots
557
617
  ripgrepPath, // explicit path to `rg`; falls back to packaged binary, then PATH
558
618
  qaOutputDir, // fallback dir for Playwright MCP filename routing
559
619
  sandboxPolicy, // optional SandboxPolicy for tools and stdio MCP (enforced
@@ -616,6 +676,10 @@ defensively cloned model snapshots; `getPiBuiltinModel(providerId, modelId)`
616
676
  returns one cloned snapshot or `undefined`; and
617
677
  `reasoningLevelsForPiModel(model)` translates a Pi model into mono-agent's
618
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.
619
683
  `resolvePiOAuthApiKey(providerId, credentials)` refreshes a caller-owned
620
684
  credential snapshot and returns `{ apiKey, newCredentials }` or `null`, while
621
685
  `loginPiOAuth(providerId, callbacks)` runs the selected supported login flow.
@@ -625,13 +689,25 @@ manual-code, and abort callbacks pass through unchanged.
625
689
  These functions deliberately do not expose Pi's mutable model collections or
626
690
  OAuth-provider registry.
627
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
+
628
703
  Returns:
629
704
 
630
705
  - `run(systemPrompt, options)` — async, runs one agent turn against the chosen backend.
631
706
  - `configureTools(next)` — update the tool runtime context after construction.
632
707
  - `syncSession(id)` — fsync provider-owned durable state before canonical history commits.
633
708
  - `refreshSession(id)` — guarantee the next resume cannot reuse process-local state; absence succeeds and cleanup uncertainty rejects.
634
- - `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.
635
711
  - `disposeSession(id)` / `invalidateSession(id)` / `disposeAllSessions()` — ordinary best-effort eviction, destructive live invalidation, and shutdown cleanup.
636
712
 
637
713
  #### `runtime.run(systemPrompt, options)`
@@ -649,14 +725,15 @@ Per-call options (a non-exhaustive selection):
649
725
  | `skills` / `skillsRoot` | `{name, description}[]` / `string` | Skills disclosed to the run and the directory holding `<name>/SKILL.md`. |
650
726
  | `mcpServers` | `Record<string, McpServerConfig>` | Configured MCP servers (stdio / sse / http). |
651
727
  | `sandboxPolicy` | `SandboxPolicy` | Optional fail-closed sandbox policy for built-in tools and stdio MCP process startup. |
652
- | `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. |
653
729
  | `webFetchConfig` | `{ render?, browserCommand? }` | Run-scoped static extraction and optional isolated browser-render policy. |
654
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. |
655
731
  | `maxTurns` | `number` | Hard cap on agent turns. |
656
732
  | `outputSchema` | `JSONSchema` | Requests structured JSON; see “Structured output” below. |
657
733
  | `abortSignal` | `AbortSignal` | Cancel the run. |
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. |
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. |
659
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. |
660
737
  | `runId` | `string` | Tag this run for downstream callbacks (e.g. `onCompactionRecorded`). |
661
738
  | `providerSessionId` | `string` | Resume a prior provider session. |
662
739
  | `runArtifactDir` | `string` | Used as the Playwright MCP filename target. |
@@ -670,15 +747,21 @@ The `"allow_all_only"` value survives for custom structural bridges that accept
670
747
  only an effective unrestricted policy; omission by such a bridge means the
671
748
  capability is unknown.
672
749
 
673
- Live input is native on the Pi bridge.
674
- After the bridge invokes `acknowledge()`, the runtime emits exactly one
675
- `{ type: "live_input_applied", inputId, receivedAt? }` event for that logical
676
- run. It deliberately omits the guidance body. A fallback router reuses the same
677
- instrumented input stream, so replay or duplicate acknowledgement cannot emit a
678
- second applied event. A throwing host `acknowledge` or `reject` callback cannot
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.
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.
682
765
 
683
766
  ### Project instructions
684
767
 
@@ -699,12 +782,13 @@ by one lazily started Node.js REPL child per run. You select them via
699
782
  `allowedTools`. Tool implementations honor:
700
783
 
701
784
  - `cwd` (required for path-based tools)
702
- - The runtime context's `workspace` / `repoRoot` allow-list (paths outside both, plus `/tmp` and `process.cwd()`, are rejected)
703
- - 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>/`.
704
787
 
705
788
  The Pi-native tool context may structurally receive a host process-job
706
- controller. Only then do Exec and Bash add optional `background`; with no
707
- 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
708
792
  hands the exact prepared command to the controller and stops awaiting it. The
709
793
  kernel first creates a command-agnostic detached POSIX group leader; only after
710
794
  the host durably records its PID, equal PGID, and process incarnation does the
@@ -715,14 +799,34 @@ contract package for this boundary.
715
799
 
716
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.
717
801
 
718
- `WebSearch` uses a configured loopback SearXNG endpoint and/or deterministic
719
- public fallbacks that require no credentials. It canonicalizes and deduplicates
720
- results, then fuses multiple query rankings. `WebFetch` extracts HTML, JSON,
721
- feeds, PDFs, and text locally with
722
- bounded redirects, bodies, headers, and retries. Config can opt into isolated
723
- `agent-browser` rendering for sparse client-rendered HTML. One ephemeral
724
- controller per run deduplicates identical calls and closes every browser
725
- 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`.
726
830
 
727
831
  Pi runs with selected skills also expose `ReadSkill`. It returns the complete
728
832
  skill instructions by default, including content beyond the former
@@ -747,7 +851,24 @@ JSON as `result.structuredResult`.
747
851
 
748
852
  ### Provider fallback router
749
853
 
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.
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.
751
872
 
752
873
  ```js
753
874
  import { createRouterRuntime, parseRuntimeModelReference } from "@mono-agent/agent-runtime";
@@ -889,17 +1010,76 @@ boundary, not approving the call.
889
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:
890
1011
 
891
1012
  1. Calls your `persistArtifact({ filename, buffer, toolName, toolUseId })` callback (if you supplied one).
892
- 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.
893
1017
  3. Emits a `runtime_warning` with `warning_kind: "tool_payload_truncated"` and the saved-paths array.
894
1018
 
895
- 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.
896
1024
 
897
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.
898
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
+
899
1046
  ### Context compaction
900
1047
 
901
- The sole pi bridge runs on pi-agent-core's native `AgentHarness`. pi performs **no**
902
- 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
903
1083
  running model's context usage and calls `AgentHarness.compact()` when near the window
904
1084
  (proactive), and if a turn still overflows it compacts once and re-prompts exactly once
905
1085
  only after a rebuilt-context preview proves positive reduction (reactive recovery).
@@ -955,9 +1135,12 @@ These are stable but treated as advanced API. Most consumers should reach for `c
955
1135
 
956
1136
  ## Dependency Boundary
957
1137
 
958
- This package has zero `@mono-agent/*` workspace dependencies. Its runtime
959
- dependencies are `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`,
1138
+ This package has zero `@mono-agent/*` workspace dependencies. Its runtime core
1139
+ uses `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`,
960
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.
961
1144
 
962
1145
  The runtime owns and exact-pins its Pi dependencies; consumers use the runtime's
963
1146
  Pi façade rather than coordinating a second direct `@earendil-works/pi-ai`
@@ -989,7 +1172,8 @@ runtime fails closed.
989
1172
  - [Programmatic approvals and structured output](https://mono-agent-docs.vercel.app/programmatic/approval-and-structured-output/)
990
1173
  shows the code-only host hooks.
991
1174
  - [Local-first web research](https://mono-agent-docs.vercel.app/tools/web-research/)
992
- documents SearXNG, extraction, retry, browser isolation, and sandbox policy.
1175
+ documents Ollama/SearXNG selection, extraction, retry, browser isolation, and
1176
+ sandbox policy.
993
1177
  - [Reply files and MCP Apps](https://mono-agent-docs.vercel.app/tools/rich-replies/)
994
1178
  documents the host bridge, browser sandbox, and lifecycle limits.
995
1179
  - [Architecture](https://github.com/robertsreberski/mono-agent/blob/main/packages/agent-runtime/ARCHITECTURE.md)