wotann 0.5.96 → 0.5.98

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 (231) hide show
  1. package/README.md +18 -38
  2. package/dist/api/anthropic-gateway.d.ts +4 -5
  3. package/dist/api/anthropic-gateway.js +4 -5
  4. package/dist/api/server.js +4 -2
  5. package/dist/api/ws-hardening.d.ts +2 -2
  6. package/dist/api/ws-hardening.js +2 -2
  7. package/dist/auth/login.d.ts +1 -2
  8. package/dist/auth/login.js +3 -44
  9. package/dist/browser/agentic-browser.d.ts +14 -0
  10. package/dist/browser/agentic-browser.js +150 -3
  11. package/dist/browser/browser-tools.d.ts +6 -0
  12. package/dist/browser/browser-tools.js +57 -6
  13. package/dist/channels/gateway.d.ts +16 -1
  14. package/dist/channels/gateway.js +46 -0
  15. package/dist/channels/unified-dispatch.d.ts +5 -1
  16. package/dist/channels/unified-dispatch.js +21 -9
  17. package/dist/cli/cli-detection.js +0 -8
  18. package/dist/cli/commands/browse.d.ts +4 -4
  19. package/dist/cli/commands/browse.js +17 -5
  20. package/dist/cli/commands/computer-use.d.ts +1 -1
  21. package/dist/cli/commands/computer-use.js +7 -42
  22. package/dist/cli/commands.js +8 -38
  23. package/dist/cli/first-run-runner-factory.d.ts +2 -3
  24. package/dist/cli/first-run-runner-factory.js +6 -3
  25. package/dist/cli/onboarding-screens/done-screen.js +1 -1
  26. package/dist/cli/onboarding-screens.d.ts +4 -7
  27. package/dist/cli/onboarding-screens.js +29 -35
  28. package/dist/cli/orphan-wires/arena-cmd.d.ts +14 -3
  29. package/dist/cli/orphan-wires/arena-cmd.js +12 -10
  30. package/dist/cli/orphan-wires/harness-introspect-cmd.d.ts +1 -1
  31. package/dist/cli/orphan-wires/harness-introspect-cmd.js +2 -2
  32. package/dist/cli/orphan-wires/redteam-scan-cmd.d.ts +13 -5
  33. package/dist/cli/orphan-wires/redteam-scan-cmd.js +9 -12
  34. package/dist/cli/run-onboarding-wizard.js +9 -16
  35. package/dist/cli/runtime-query.d.ts +1 -1
  36. package/dist/cli/runtime-query.js +5 -1
  37. package/dist/cli/test-provider.js +1 -4
  38. package/dist/cli/voice-cmds.d.ts +6 -8
  39. package/dist/cli/voice-cmds.js +10 -24
  40. package/dist/computer-use/computer-agent.d.ts +14 -0
  41. package/dist/computer-use/computer-agent.js +59 -2
  42. package/dist/computer-use/perception-engine.d.ts +4 -1
  43. package/dist/computer-use/perception-engine.js +11 -3
  44. package/dist/context/limits.d.ts +2 -4
  45. package/dist/context/limits.js +17 -29
  46. package/dist/context/maximizer.d.ts +0 -1
  47. package/dist/context/maximizer.js +11 -22
  48. package/dist/context/window-intelligence.js +1 -1
  49. package/dist/core/agent-bridge.d.ts +7 -6
  50. package/dist/core/agent-bridge.js +31 -10
  51. package/dist/core/agent-tool-context.d.ts +4 -0
  52. package/dist/core/agent-tool-context.js +12 -1
  53. package/dist/core/config-discovery.d.ts +4 -4
  54. package/dist/core/config-discovery.js +11 -12
  55. package/dist/core/config.d.ts +1 -5
  56. package/dist/core/config.js +4 -16
  57. package/dist/core/default-provider.js +6 -3
  58. package/dist/core/effort-mode.d.ts +4 -5
  59. package/dist/core/effort-mode.js +31 -14
  60. package/dist/core/hardware-detect.d.ts +3 -11
  61. package/dist/core/hardware-detect.js +11 -22
  62. package/dist/core/prompt-override.js +17 -4
  63. package/dist/core/runtime.d.ts +31 -5
  64. package/dist/core/runtime.js +262 -37
  65. package/dist/core/schema-migration.js +9 -3
  66. package/dist/core/types.d.ts +14 -3
  67. package/dist/core/workspace.js +7 -26
  68. package/dist/daemon/auto-update.d.ts +6 -11
  69. package/dist/daemon/auto-update.js +12 -148
  70. package/dist/daemon/kairos-rpc.d.ts +4 -1
  71. package/dist/daemon/kairos-rpc.js +436 -268
  72. package/dist/daemon/kairos-tools.d.ts +3 -3
  73. package/dist/daemon/kairos-tools.js +3 -5
  74. package/dist/daemon/kairos.js +35 -3
  75. package/dist/daemon/rpc-handlers/sandbox-rpc.d.ts +12 -3
  76. package/dist/daemon/rpc-handlers/sandbox-rpc.js +64 -38
  77. package/dist/daemon/start.js +0 -9
  78. package/dist/desktop/companion-server.d.ts +1 -0
  79. package/dist/desktop/companion-server.js +136 -113
  80. package/dist/index.js +72 -104
  81. package/dist/intelligence/harness-introspect.d.ts +2 -2
  82. package/dist/intelligence/harness-introspect.js +5 -12
  83. package/dist/intelligence/provider-arbitrage.js +16 -26
  84. package/dist/intelligence/task-semantic-router.js +12 -11
  85. package/dist/lib.d.ts +0 -7
  86. package/dist/lib.js +0 -13
  87. package/dist/memory/cross-encoder.d.ts +3 -3
  88. package/dist/memory/cross-encoder.js +3 -3
  89. package/dist/memory/local-embedding.d.ts +2 -2
  90. package/dist/memory/local-embedding.js +4 -105
  91. package/dist/memory/onnx-cross-encoder.d.ts +17 -23
  92. package/dist/memory/onnx-cross-encoder.js +19 -49
  93. package/dist/memory/store.d.ts +3 -3
  94. package/dist/memory/store.js +9 -23
  95. package/dist/middleware/layers.d.ts +2 -2
  96. package/dist/middleware/layers.js +5 -4
  97. package/dist/mobile/ios-app.d.ts +3 -2
  98. package/dist/mobile/ios-app.js +11 -6
  99. package/dist/orchestration/agent-registry.d.ts +2 -2
  100. package/dist/orchestration/agent-registry.js +16 -14
  101. package/dist/orchestration/architect-editor.js +6 -10
  102. package/dist/orchestration/council.js +10 -1
  103. package/dist/orchestration/proof-bundles.d.ts +9 -0
  104. package/dist/orchestration/proof-bundles.js +2 -0
  105. package/dist/plugins/service-manifest.d.ts +1 -1
  106. package/dist/providers/adapter-registry.js +10 -11
  107. package/dist/providers/capability-equalizer.js +6 -32
  108. package/dist/providers/cli-registry.js +0 -21
  109. package/dist/providers/discovery.d.ts +0 -70
  110. package/dist/providers/discovery.js +3 -211
  111. package/dist/providers/dynamic-discovery.d.ts +0 -2
  112. package/dist/providers/dynamic-discovery.js +0 -45
  113. package/dist/providers/fallback-chain.d.ts +6 -11
  114. package/dist/providers/fallback-chain.js +30 -27
  115. package/dist/providers/model-defaults.js +4 -1
  116. package/dist/providers/model-discovery.d.ts +2 -1
  117. package/dist/providers/model-discovery.js +10 -29
  118. package/dist/providers/model-router.d.ts +1 -10
  119. package/dist/providers/model-router.js +19 -63
  120. package/dist/providers/model-switcher.js +18 -1
  121. package/dist/providers/onboarding-auth-choices.js +3 -0
  122. package/dist/providers/openai-compat-adapter.js +1 -1
  123. package/dist/providers/preset-library.d.ts +1 -1
  124. package/dist/providers/preset-library.js +12 -5
  125. package/dist/providers/provider-brain.js +5 -2
  126. package/dist/providers/provider-ladder.d.ts +10 -16
  127. package/dist/providers/provider-ladder.js +19 -16
  128. package/dist/providers/provider-service.js +0 -41
  129. package/dist/providers/public-tier/fallback-chain.js +4 -7
  130. package/dist/providers/rate-limiter.d.ts +5 -5
  131. package/dist/providers/rate-limiter.js +17 -8
  132. package/dist/providers/registry.d.ts +1 -1
  133. package/dist/providers/registry.js +7 -50
  134. package/dist/providers/smart-routing.js +6 -2
  135. package/dist/providers/state-aware-failover.js +6 -1
  136. package/dist/providers/usage-intelligence.d.ts +3 -3
  137. package/dist/providers/usage-intelligence.js +4 -20
  138. package/dist/security/approval-binding.d.ts +52 -0
  139. package/dist/security/approval-binding.js +57 -0
  140. package/dist/security/approval-queue-decision-broker.d.ts +29 -0
  141. package/dist/security/approval-queue-decision-broker.js +76 -0
  142. package/dist/security/file-freeze.js +3 -0
  143. package/dist/security/guardrails-off.d.ts +5 -11
  144. package/dist/security/guardrails-off.js +35 -116
  145. package/dist/security/hash-audit-chain.d.ts +5 -4
  146. package/dist/security/hash-audit-chain.js +8 -6
  147. package/dist/security/human-approval.d.ts +2 -0
  148. package/dist/security/human-approval.js +15 -24
  149. package/dist/security/pii-scrubber.d.ts +3 -2
  150. package/dist/security/pii-scrubber.js +5 -13
  151. package/dist/security/privacy-router.d.ts +2 -2
  152. package/dist/security/privacy-router.js +14 -10
  153. package/dist/security/private-mode.d.ts +5 -6
  154. package/dist/security/private-mode.js +11 -13
  155. package/dist/security/provenance-label.d.ts +11 -0
  156. package/dist/security/provenance-label.js +29 -0
  157. package/dist/security/provenance-tracker.d.ts +42 -0
  158. package/dist/security/provenance-tracker.js +187 -0
  159. package/dist/security/taint-approval-coordinator.d.ts +49 -0
  160. package/dist/security/taint-approval-coordinator.js +148 -0
  161. package/dist/security/taint-receipt.d.ts +37 -0
  162. package/dist/security/taint-receipt.js +77 -0
  163. package/dist/security/taint-sink-gate.d.ts +22 -0
  164. package/dist/security/taint-sink-gate.js +189 -0
  165. package/dist/security/visual-taint.d.ts +17 -0
  166. package/dist/security/visual-taint.js +64 -0
  167. package/dist/session/approval-queue.d.ts +23 -2
  168. package/dist/session/approval-queue.js +84 -6
  169. package/dist/telemetry/cost-oracle.js +17 -10
  170. package/dist/tools/agent-tools.d.ts +5 -0
  171. package/dist/tools/agent-tools.js +275 -170
  172. package/dist/training/pipeline.d.ts +2 -2
  173. package/dist/training/pipeline.js +5 -13
  174. package/dist/ui/components/CommandPaletteCommands.js +3 -3
  175. package/dist/ui/components/ModelPicker.d.ts +1 -1
  176. package/dist/ui/components/ModelPicker.js +5 -2
  177. package/dist/ui/components/OnboardingWizard.d.ts +3 -3
  178. package/dist/ui/components/OnboardingWizard.js +4 -10
  179. package/dist/ui/components/ProviderSetupOverlay.js +3 -4
  180. package/dist/ui/components/StartupScreen.js +1 -1
  181. package/dist/ui/components/v3/AppV3.js +3 -0
  182. package/dist/ui/components/v3/OnboardingTour.d.ts +4 -3
  183. package/dist/ui/components/v3/OnboardingTour.js +10 -5
  184. package/dist/utils/atomic-io.d.ts +1 -0
  185. package/dist/utils/atomic-io.js +23 -0
  186. package/dist/utils/atomic-write.d.ts +2 -0
  187. package/dist/utils/atomic-write.js +17 -0
  188. package/dist/utils/bounded-base64.d.ts +2 -0
  189. package/dist/utils/bounded-base64.js +17 -0
  190. package/dist/utils/path-realpath.d.ts +12 -0
  191. package/dist/utils/path-realpath.js +38 -2
  192. package/dist/utils/sidecar-downloader.d.ts +2 -2
  193. package/dist/utils/sidecar-downloader.js +2 -20
  194. package/dist/utils/sqlite-kv-store.d.ts +1 -1
  195. package/dist/utils/sqlite-kv-store.js +4 -3
  196. package/dist/verification/reproduction/autonomous-gate.d.ts +53 -0
  197. package/dist/verification/reproduction/autonomous-gate.js +72 -0
  198. package/dist/verification/reproduction/checkout-prep.d.ts +47 -0
  199. package/dist/verification/reproduction/checkout-prep.js +78 -0
  200. package/dist/verification/reproduction/diff-checker.d.ts +26 -0
  201. package/dist/verification/reproduction/diff-checker.js +33 -0
  202. package/dist/verification/reproduction/enforcement.d.ts +16 -0
  203. package/dist/verification/reproduction/enforcement.js +34 -0
  204. package/dist/verification/reproduction/exec-runner.d.ts +15 -0
  205. package/dist/verification/reproduction/exec-runner.js +47 -0
  206. package/dist/verification/reproduction/index.d.ts +10 -0
  207. package/dist/verification/reproduction/index.js +10 -0
  208. package/dist/verification/reproduction/mutation-gate.d.ts +42 -0
  209. package/dist/verification/reproduction/mutation-gate.js +43 -0
  210. package/dist/verification/reproduction/proof-artifact.d.ts +16 -0
  211. package/dist/verification/reproduction/proof-artifact.js +22 -0
  212. package/dist/verification/reproduction/replay-runner.d.ts +37 -0
  213. package/dist/verification/reproduction/replay-runner.js +28 -0
  214. package/dist/verification/reproduction/reproduce.d.ts +34 -0
  215. package/dist/verification/reproduction/reproduce.js +31 -0
  216. package/dist/verification/reproduction/verdict.d.ts +41 -0
  217. package/dist/verification/reproduction/verdict.js +40 -0
  218. package/dist/verification/verifier-engine.js +1 -1
  219. package/dist/verification/verifier-model-selector.js +8 -16
  220. package/dist/voice/edge-tts-backend.d.ts +3 -9
  221. package/dist/voice/edge-tts-backend.js +8 -9
  222. package/dist/voice/stt-detector.d.ts +6 -13
  223. package/dist/voice/stt-detector.js +17 -128
  224. package/dist/voice/tts-engine.d.ts +9 -19
  225. package/dist/voice/tts-engine.js +36 -113
  226. package/dist/voice/vibevoice-backend.js +17 -1
  227. package/dist/voice/voice-mode.d.ts +3 -8
  228. package/dist/voice/voice-mode.js +14 -152
  229. package/dist/voice/voice-pipeline.d.ts +10 -12
  230. package/dist/voice/voice-pipeline.js +47 -245
  231. package/package.json +4 -4
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **The All-Father of AI agent harnesses.**
6
6
 
7
- One install. Every model. Every channel. Full autonomy.
7
+ One install. Your providers. Every channel. Full autonomy.
8
8
 
9
9
  <p>
10
10
  <a href="https://github.com/gabrielvuksani/wotann/actions/workflows/ci.yml"><img src="https://github.com/gabrielvuksani/wotann/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
@@ -25,7 +25,7 @@ One install. Every model. Every channel. Full autonomy.
25
25
  <td align="center">
26
26
  <strong>WOTANN treats the LLM as just the inference call.</strong><br/>
27
27
  Memory, tools, sandbox, orchestration, learning — all come from the harness.<br/>
28
- A local Gemma gets the same intelligence scaffolding as Claude Opus.
28
+ Inference comes from a remote subscription or API-key provider you configure.
29
29
  </td>
30
30
  </tr>
31
31
  </table>
@@ -38,13 +38,13 @@ A local Gemma gets the same intelligence scaffolding as Claude Opus.
38
38
 
39
39
  ## Why WOTANN
40
40
 
41
- Most agent frameworks lock you to a vendor, degrade quietly when a model can't tool-call, and hand you a single UI. WOTANN inverts that. It lives at the layer above inference so your choice of model is always a drop-in. Switch from Claude Gemini Gemma mid-session and you keep the same memory, the same tools, the same capabilities.
41
+ Most agent frameworks lock you to a vendor, degrade quietly when a model can't tool-call, and hand you a single UI. WOTANN inverts that. It lives at the layer above inference so your configured lab subscription or API-key provider remains replaceable without moving agent execution off your machine.
42
42
 
43
43
  | | Most agents | **WOTANN** |
44
44
  |---|---|---|
45
45
  | **Provider** | Locked to one vendor | **25 providers** via openai-compat router; Anthropic + OpenAI subscriptions OR API keys |
46
- | **Capabilities** | Tool-calling, vision, thinking gated by model support | **Capability augmentation injects all three** for any model — even a 3B local Gemma |
47
- | **Free tier** | An afterthought | **First-class**: Ollama + Groq + Gemini 2.5 Flash free tier give the full experience |
46
+ | **Capabilities** | Tool-calling, vision, thinking gated by model support | **Capability augmentation** normalizes provider capabilities |
47
+ | **Inference economics** | Resold tokens or bundled inference | **BYO subscription or API key**: the user pays the lab |
48
48
  | **Memory** | Short-lived prompt context | **8-layer persistent**: SQLite + FTS5 + vector + graph-RAG + episodic + temporal + provenance |
49
49
  | **Surfaces** | One UI | **CLI + TUI + Desktop + iOS + Watch + CarPlay + 25 messaging channels** |
50
50
  | **Autonomy** | Vibes | **Proof bundles** (tests + typecheck + diff + screenshots) on every completion |
@@ -94,7 +94,7 @@ npx wotann@latest
94
94
 
95
95
  Node.js prerequisite on Windows: `winget install OpenJS.NodeJS.LTS` (Node 22.13+ required). The PowerShell installer enforces this; the npm path surfaces a friendly preflight error on older Node.
96
96
 
97
- That's the whole setup. WOTANN runs **entirely locally by default** (Ollama + free tiers). Drop an API key to upgrade selectively no lock-in, no telemetry unless you opt in.
97
+ Agent execution and trust enforcement run on your machine. Inference uses a provider subscription or API key that you configure; WOTANN does not run local models or silently route prompts to anonymous public endpoints.
98
98
 
99
99
  ```bash
100
100
  # Use frontier models when you want them
@@ -102,31 +102,11 @@ export ANTHROPIC_API_KEY=sk-ant-...
102
102
  export OPENAI_API_KEY=sk-...
103
103
  export GEMINI_API_KEY=AIza...
104
104
 
105
- # Or stay free
106
- ollama pull qwen2.5-coder:7b
107
- wotann # auto-detects Ollama
105
+ # ChatGPT/Codex and Claude subscription paths are supported too
106
+ wotann onboard
108
107
  ```
109
108
 
110
- ### Zero-config public tier (no signup, no key)
111
-
112
- If you have no API keys and no local Ollama, WOTANN still works out of the box. The runtime auto-falls through to an anonymous public-tier chain — no account, no credit card, no environment variable required.
113
-
114
- ```bash
115
- # Cold-start with nothing configured. The TUI mounts and the first
116
- # message routes through the public tier automatically.
117
- npx wotann@latest
118
- ```
119
-
120
- The chain is ordered by latency + rate-limit headroom (you can introspect with `wotann providers list`):
121
-
122
- | Order | Provider | Model surface | Why |
123
- |---|---|---|---|
124
- | 1 | Pollinations.ai anonymous | `openai-fast` (GPT-OSS 20B reasoning, OVH backend) | Fast, ~1 req queued/IP |
125
- | 2 | LLM7.io anonymous | GLM-4.6V (vision) + Codestral (code) | 30 RPM, vision-capable |
126
- | 3 | OVHcloud anonymous | Llama 3.3 70B, GPT-OSS 120B, Qwen2.5-VL 72B | 2 RPM/IP, vision-capable |
127
- | 4 | AI Horde | Community-pooled models | Uncensored fallback; queue-dependent |
128
-
129
- Rate-limit errors fall through to the next adapter without surfacing to the user. When all four are saturated WOTANN reports honestly — no silent retries, no fake success. Disable the public tier entirely with `WOTANN_DISABLE_PUBLIC=1` for sandboxed/offline runs.
109
+ There is no anonymous zero-config inference tier. If no authenticated remote processor is configured, WOTANN reports that honestly.
130
110
 
131
111
  ---
132
112
 
@@ -160,21 +140,21 @@ $ wotann link
160
140
 
161
141
  ### Provider freedom (the moat)
162
142
 
163
- 25 providers via one router with automatic fallback chaining: `preferred → other paid Gemini Ollama free`. **The model is never silently degraded** if your chosen Opus is rate-limited, WOTANN tries Opus via another provider first; only when all paid paths exhaust does it fall back to Gemini/local.
143
+ Remote providers share one router with authenticated fallback chaining: `preferred → other configured remote processors`. Local runtimes and anonymous endpoints are excluded from executable fallback. If configured processors exhaust, WOTANN reports that honestly.
164
144
 
165
145
  Runtime providers can be added without editing source: drop a strict `provider.json` under `.wotann/providers/<name>/` with an OpenAI-compatible `baseUrl`, model list, capabilities, and an `auth.envKey`. WOTANN validates the manifest, refuses token-bearing manifests, and routes it through the same provider registry as built-ins.
166
146
 
167
147
  ```
168
148
  Anthropic API Anthropic Subscription OpenAI Codex (ChatGPT OAuth)
169
- GitHub Copilot Ollama Gemini HuggingFace
149
+ GitHub Copilot OpenRouter Gemini HuggingFace
170
150
  Azure Bedrock Vertex Mistral
171
151
  DeepSeek Perplexity xAI Together
172
152
  Fireworks SambaNova Groq Cerebras
173
153
  ```
174
154
 
175
- ### The harness amplifies even on a 3B local model
155
+ ### The harness amplifies provider capabilities
176
156
 
177
- `capability-augmenter` injects tool-calling, vision (via OCR + a11y tree), and extended thinking ("step by step" prompts) into providers that lack native support. Combined with hash-anchored editing, line-bucketed loop detection, and 41 forced-verification middleware layers in the PIPELINE, a **Qwen2.5-Coder:7B running locally gets the same forced verification, frustration detection, doom-loop guards, and memory system as Claude Opus**.
157
+ `capability-augmenter` injects tool-calling, vision (via OCR + a11y tree), and extended thinking prompts into configured providers that lack native support. The trust layer stays independent of any one model family.
178
158
 
179
159
  ### Multi-surface, one agent, one memory
180
160
 
@@ -204,7 +184,7 @@ Runs **Ralph mode** (verify-fix loop) + **self-healing** (provider fallback) + *
204
184
  - **Guardian** post-response LLM-as-judge review (`WOTANN_GUARDIAN=1`)
205
185
  - **Self-consistency voting** — N parallel samples with confidence score
206
186
  - **Council deliberation** — 3-stage multi-provider peer review + chairman synthesis
207
- - **Unified knowledge fabric** — single search API across memory / context-tree / graph-RAG / vector / FTS5, with `wotann memory embeddings warmup` for local BGE embeddings that keep semantic memory usable offline after the model is cached
187
+ - **Unified knowledge fabric** — single search API across memory / context-tree / graph-RAG / vector / FTS5, with deterministic retrieval fallbacks when no authenticated remote embedding processor is configured
208
188
  - **Context replay** — priority-weighted reassembly under an explicit token budget
209
189
  - **Dream pipeline** — nightly consolidation 02:00-04:00, extracts skills from successful runs
210
190
 
@@ -272,12 +252,12 @@ src/
272
252
  ├── intelligence/ 85 modules that meaningfully affect model behavior
273
253
  ├── orchestration/ 79 modules — coordinator · waves · PWR · Ralph · council · squads · canary · ...
274
254
  ├── memory/ 80 modules · SQLite + FTS5 · 8 layers · vector · graph-RAG · episodic
275
- ├── context/ 5 compaction strategies · Ollama KV cache compression
255
+ ├── context/ 5 compaction strategies · legacy local-runtime code slated for subtraction
276
256
  ├── prompt/ Engine · 18 prompt modules · instruction provenance
277
257
  ├── hooks/ 31 events (28 producer-wired · 3 advisory) · 23 guards · DoomLoop
278
258
  ├── computer-use/ 4-layer perception + action stack (text-mediated for any model)
279
259
  ├── channels/ 25+ adapters + DM pairing + node registry
280
- ├── voice/ Push-to-talk · STT/TTS · WhisperKit · Edge TTS · faster-whisper
260
+ ├── voice/ Push-to-talk · authenticated remote STT/TTS adapters
281
261
  ├── learning/ autoDream · instincts · skill-forge · pattern-crystallizer
282
262
  ├── identity/ Persona system · soul/identity loading · Norse mythology
283
263
  ├── security/ Anti-distillation · PII redaction · sandbox · guardrails · SSRF guard
@@ -332,13 +312,13 @@ Run `wotann --help` for the full 78+ command surface.
332
312
 
333
313
  - **Node ≥ 22.13** (current LTS; install via `winget install OpenJS.NodeJS.LTS` on Windows or `brew install node@22` on macOS)
334
314
  - **macOS 13+ / Linux / Windows 11**
335
- - **Optional**: Ollama (local models), Python 3.11+ (for camoufox stealth browser backend), Xcode 16 (iOS builds)
315
+ - **Optional**: Python 3.11+ (for camoufox stealth browser backend), Xcode 16 (iOS builds)
336
316
 
337
317
  ---
338
318
 
339
319
  ## Privacy
340
320
 
341
- Runs entirely locally by default. Telemetry is opt-out and triple-gated — industry-standard `DO_NOT_TRACK=1` is honored, plus WOTANN-specific `WOTANN_NO_TELEMETRY=1`, plus a sentinel file at `~/.wotann/no-telemetry`.
321
+ Agent execution and trust enforcement run locally. Inference payloads go only to the configured lab provider. Telemetry is opt-out and triple-gated — industry-standard `DO_NOT_TRACK=1` is honored, plus WOTANN-specific `WOTANN_NO_TELEMETRY=1`, plus a sentinel file at `~/.wotann/no-telemetry`.
342
322
 
343
323
  ```bash
344
324
  export WOTANN_NO_TELEMETRY=1
@@ -8,14 +8,13 @@
8
8
  * Messages protocol natively. Today they hit api.anthropic.com
9
9
  * directly, paying per-token to Anthropic. With this gateway, they
10
10
  * can hit `http://localhost:8420/v1/messages` and WOTANN routes the
11
- * call to whichever provider is cheapest/free (Ollama, OpenRouter
12
- * free tier, Groq, Gemini free tier, etc.) while still returning
13
- * the response in Anthropic's expected format.
11
+ * call to a configured authenticated remote provider while still
12
+ * returning the response in Anthropic's expected format.
14
13
  *
15
14
  * The harness's middleware (memory injection, hooks, sandbox guards,
16
15
  * cost tracking) runs every call regardless of which provider serves
17
- * the response. Free-tier first-class becomes consumable by every
18
- * tool in the ecosystem.
16
+ * the response. Authenticated remote provider routing becomes
17
+ * consumable by every tool in the ecosystem.
19
18
  *
20
19
  * SUPPORTED
21
20
  *
@@ -8,14 +8,13 @@
8
8
  * Messages protocol natively. Today they hit api.anthropic.com
9
9
  * directly, paying per-token to Anthropic. With this gateway, they
10
10
  * can hit `http://localhost:8420/v1/messages` and WOTANN routes the
11
- * call to whichever provider is cheapest/free (Ollama, OpenRouter
12
- * free tier, Groq, Gemini free tier, etc.) while still returning
13
- * the response in Anthropic's expected format.
11
+ * call to a configured authenticated remote provider while still
12
+ * returning the response in Anthropic's expected format.
14
13
  *
15
14
  * The harness's middleware (memory injection, hooks, sandbox guards,
16
15
  * cost tracking) runs every call regardless of which provider serves
17
- * the response. Free-tier first-class becomes consumable by every
18
- * tool in the ecosystem.
16
+ * the response. Authenticated remote provider routing becomes
17
+ * consumable by every tool in the ecosystem.
19
18
  *
20
19
  * SUPPORTED
21
20
  *
@@ -30,6 +30,7 @@ import { handleComputerSessionSseRequest } from "./sse-computer-session.js";
30
30
  // the canonical provider table instead of a hardcoded Anthropic+OpenAI
31
31
  // pair. Adding a provider in model-defaults.ts surfaces it here.
32
32
  import { PROVIDER_DEFAULTS, detectProviderFromEnv } from "../providers/model-defaults.js";
33
+ import { isForbiddenTrustLayerProvider } from "../providers/fallback-chain.js";
33
34
  // Round 12 — Anthropic Messages API gateway. Lets Claude Code/Cursor
34
35
  // hit our daemon instead of api.anthropic.com so they get free-tier
35
36
  // orchestration + harness middleware (memory, hooks, sandbox) on
@@ -258,9 +259,10 @@ export class WotannAPIServer {
258
259
  // Dedupe model ids across the (default/worker/oracle) tiers because
259
260
  // many providers point all three at the same model.
260
261
  const seen = new Set();
262
+ const remoteProviderNames = Object.keys(PROVIDER_DEFAULTS).filter((name) => !isForbiddenTrustLayerProvider(name));
261
263
  const orderedNames = activeProvider
262
- ? [activeProvider, ...Object.keys(PROVIDER_DEFAULTS).filter((n) => n !== activeProvider)]
263
- : Object.keys(PROVIDER_DEFAULTS);
264
+ ? [activeProvider, ...remoteProviderNames.filter((n) => n !== activeProvider)]
265
+ : remoteProviderNames;
264
266
  for (const providerName of orderedNames) {
265
267
  const defaults = PROVIDER_DEFAULTS[providerName];
266
268
  if (!defaults)
@@ -14,8 +14,8 @@
14
14
  *
15
15
  * Design notes:
16
16
  * - `withInsecureWsAllowance` reads the env var ONCE per call. Centralizing
17
- * this here avoids the ?? "ollama" class bug (Session 1 quality bar #1)
18
- * where vendor-biased fallbacks scatter across the codebase.
17
+ * this here avoids vendor-biased fallback bugs where provider
18
+ * defaults scatter across the codebase.
19
19
  * - `wsBackoff` returns a number (next sleep ms), never sleeps itself.
20
20
  * Caller composes it with their preferred timer/scheduler. This keeps
21
21
  * the helper testable without fake timers.
@@ -14,8 +14,8 @@
14
14
  *
15
15
  * Design notes:
16
16
  * - `withInsecureWsAllowance` reads the env var ONCE per call. Centralizing
17
- * this here avoids the ?? "ollama" class bug (Session 1 quality bar #1)
18
- * where vendor-biased fallbacks scatter across the codebase.
17
+ * this here avoids vendor-biased fallback bugs where provider
18
+ * defaults scatter across the codebase.
19
19
  * - `wsBackoff` returns a number (next sleep ms), never sleeps itself.
20
20
  * Caller composes it with their preferred timer/scheduler. This keeps
21
21
  * the helper testable without fake timers.
@@ -6,8 +6,7 @@
6
6
  * 2. Codex/ChatGPT: PKCE browser flow via auth.openai.com, stores to ~/.codex/auth.json
7
7
  * 3. GitHub Copilot: device code flow (shows code, user enters in browser)
8
8
  * 4. Gemini: opens browser to AI Studio API key page (user copies key)
9
- * 5. Ollama: no login needed verifies server and suggests models
10
- * 6. OpenAI-compatible API providers: env var check + instructions
9
+ * 5. OpenAI-compatible API providers: env var check + instructions
11
10
  *
12
11
  * PORT FALLBACK: If the OAuth callback port is taken (18920-18930),
13
12
  * the browser flow falls back to device code flow automatically.
@@ -6,8 +6,7 @@
6
6
  * 2. Codex/ChatGPT: PKCE browser flow via auth.openai.com, stores to ~/.codex/auth.json
7
7
  * 3. GitHub Copilot: device code flow (shows code, user enters in browser)
8
8
  * 4. Gemini: opens browser to AI Studio API key page (user copies key)
9
- * 5. Ollama: no login needed verifies server and suggests models
10
- * 6. OpenAI-compatible API providers: env var check + instructions
9
+ * 5. OpenAI-compatible API providers: env var check + instructions
11
10
  *
12
11
  * PORT FALLBACK: If the OAuth callback port is taken (18920-18930),
13
12
  * the browser flow falls back to device code flow automatically.
@@ -339,50 +338,11 @@ async function loginApiKeyProvider(provider) {
339
338
  }
340
339
  // ── Ollama Check ───────────────────────────────────────────
341
340
  async function loginOllama() {
342
- console.log(chalk.dim(" Checking Ollama status...\n"));
343
- try {
344
- const controller = new AbortController();
345
- const timeout = setTimeout(() => controller.abort(), 3000);
346
- const { ollamaUrl } = await import("../providers/ollama-host.js");
347
- const response = await fetch(ollamaUrl("/api/tags"), { signal: controller.signal });
348
- clearTimeout(timeout);
349
- if (response.ok) {
350
- const data = (await response.json());
351
- const models = data.models ?? [];
352
- const modelCount = models.length;
353
- console.log(chalk.green(` Ollama is running with ${modelCount} model(s):`));
354
- for (const m of models.slice(0, 10)) {
355
- const sizeGB = (m.size / 1e9).toFixed(1);
356
- console.log(chalk.dim(` - ${m.name} (${sizeGB}GB)`));
357
- }
358
- if (modelCount === 0) {
359
- console.log(chalk.yellow("\n No models installed. Recommended for coding:"));
360
- console.log(chalk.dim(" ollama pull qwen3-coder-next # 80B MoE, best coding (18GB VRAM)"));
361
- console.log(chalk.dim(" ollama pull qwen3.5:27b # multimodal, 256K context (20GB)"));
362
- console.log(chalk.dim(" ollama pull devstral:24b # fast coding agent (16GB)"));
363
- console.log(chalk.dim(" ollama pull qwen3-coder:7b # entry level (5GB)"));
364
- }
365
- return {
366
- provider: "ollama",
367
- success: true,
368
- message: `Ollama is running with ${modelCount} model(s). No login needed.`,
369
- method: "local-check",
370
- };
371
- }
372
- }
373
- catch {
374
- // Not running
375
- }
376
- console.log(chalk.yellow(" Ollama is not running."));
377
- console.log(chalk.dim("\n Install: https://ollama.ai"));
378
- console.log(chalk.dim(" Start: ollama serve"));
379
- console.log(chalk.dim(" Pull: ollama pull qwen3-coder-next"));
380
- console.log(chalk.dim(" Verify: ollama list\n"));
381
341
  return {
382
342
  provider: "ollama",
383
343
  success: false,
384
- message: "Ollama is not running. Install from https://ollama.ai, then: ollama serve && ollama pull qwen3-coder-next",
385
- method: "local-check",
344
+ message: "Local models are disabled in the WOTANN trust layer. Configure a remote subscription or API key.",
345
+ method: "disabled",
386
346
  };
387
347
  }
388
348
  // ── OpenAI API Key ─────────────────────────────────────────
@@ -464,7 +424,6 @@ export async function runLogin(provider, options = {}) {
464
424
  console.log(chalk.dim(" wotann login codex"));
465
425
  console.log(chalk.dim(" wotann login copilot"));
466
426
  console.log(chalk.dim(" wotann login gemini"));
467
- console.log(chalk.dim(" wotann login ollama"));
468
427
  console.log(chalk.dim(" wotann login nvidia"));
469
428
  console.log(chalk.dim(" wotann login huggingface\n"));
470
429
  }
@@ -53,6 +53,8 @@
53
53
  * - QB #14 claim verification: tests in tests/browser/agentic-browser
54
54
  * .test.ts cover all halt paths + the happy-path to closure.
55
55
  */
56
+ import type { TaintApprovalCoordinator } from "../security/taint-approval-coordinator.js";
57
+ import { type VisualTaintAdvisory } from "../security/visual-taint.js";
56
58
  /**
57
59
  * The kind of atomic step the planner can emit. Kept narrow on purpose
58
60
  * — expanding this union is a breaking change because every step kind
@@ -108,6 +110,8 @@ export interface BrowseTurnRecord {
108
110
  readonly hiddenTextReport?: unknown;
109
111
  /** From src/middleware/trifecta-guard.ts */
110
112
  readonly trifectaVerdict?: unknown;
113
+ /** Advisory-only cross-modal DOM/AX disagreement evidence. */
114
+ readonly visualTaintAdvisories?: readonly VisualTaintAdvisory[];
111
115
  readonly approved?: boolean;
112
116
  readonly timestamp: number;
113
117
  readonly haltReason?: string;
@@ -147,6 +151,8 @@ export interface BrowserDriverNavigateResult {
147
151
  readonly pageText: string;
148
152
  readonly elements: readonly BrowseElement[];
149
153
  readonly finalUrl?: string;
154
+ /** Optional accessibility-tree text when the driver extracts it separately. */
155
+ readonly accessibilityText?: string;
150
156
  }
151
157
  export interface BrowserDriver {
152
158
  readonly navigate: (url: string) => Promise<BrowserDriverNavigateResult>;
@@ -219,6 +225,14 @@ export interface BrowseOrchestratorOptions {
219
225
  readonly x: number;
220
226
  readonly y: number;
221
227
  };
228
+ /** Optional session-scoped deterministic provenance gate. */
229
+ readonly taintCoordinator?: TaintApprovalCoordinator;
230
+ /** Required whenever `taintCoordinator` is installed. */
231
+ readonly taintSessionId?: string;
232
+ /** Working directory recorded in taint receipts. Defaults to ".". */
233
+ readonly taintCwd?: string;
234
+ /** Explicit simulation mode. Live execution is the fail-closed default. */
235
+ readonly executionMode?: "live" | "dry-run";
222
236
  }
223
237
  /**
224
238
  * Remove hidden-text substrings from the page text before quarantine
@@ -54,8 +54,54 @@
54
54
  * .test.ts cover all halt paths + the happy-path to closure.
55
55
  */
56
56
  import { randomUUID } from "node:crypto";
57
+ import { compareVisualLanes } from "../security/visual-taint.js";
57
58
  // ═══ Internal helpers ════════════════════════════════════════════════════
58
59
  const DEFAULT_PREVIEW_MAX = 2048;
60
+ function snapshotBrowserActionArgs(args) {
61
+ return freezeBrowserSnapshot(structuredClone(args));
62
+ }
63
+ function freezeBrowserSnapshot(value, seen = new Set()) {
64
+ if (value === null || typeof value !== "object" || seen.has(value))
65
+ return value;
66
+ seen.add(value);
67
+ for (const nested of Object.values(value))
68
+ freezeBrowserSnapshot(nested, seen);
69
+ return Object.freeze(value);
70
+ }
71
+ async function authorizeBrowserAction(options, tool, args) {
72
+ if (!options.taintCoordinator) {
73
+ return options.executionMode === "dry-run"
74
+ ? { authorized: true }
75
+ : { authorized: false, reason: "runtime taint substrate is unavailable" };
76
+ }
77
+ const sessionId = options.taintSessionId?.trim();
78
+ if (!sessionId)
79
+ return { authorized: false, reason: "taint policy session is unavailable" };
80
+ const action = Object.freeze({
81
+ tool,
82
+ args: snapshotBrowserActionArgs(args),
83
+ cwd: options.taintCwd?.trim() || ".",
84
+ });
85
+ const authorization = await options.taintCoordinator.authorize({ sessionId, action });
86
+ if (!authorization.authorized) {
87
+ return { authorized: false, reason: authorization.reason };
88
+ }
89
+ const verification = options.taintCoordinator.verifyExecution({ authorization, action });
90
+ return verification.ok
91
+ ? { authorized: true }
92
+ : { authorized: false, reason: `execution re-pin failed: ${verification.reason}` };
93
+ }
94
+ function registerBrowserSource(options, kind, content) {
95
+ if (!options.taintCoordinator || content.length === 0)
96
+ return null;
97
+ try {
98
+ options.taintCoordinator.registerSource({ kind, content, integrity: "untrusted" });
99
+ return null;
100
+ }
101
+ catch (error) {
102
+ return error instanceof Error ? error.message : String(error);
103
+ }
104
+ }
59
105
  /**
60
106
  * Pull `hiddenText` off the hidden-text report shape without coupling
61
107
  * to the concrete type. Reports without the field are treated as
@@ -215,8 +261,21 @@ async function executeNavigate(step, options, now) {
215
261
  halted: true,
216
262
  };
217
263
  }
218
- // Emit a cursor frame for the overlay BEFORE we navigate so the UI
219
- // animation starts in sync with the agent's action.
264
+ const taintGate = await authorizeBrowserAction(options, "browser.navigate", { url });
265
+ if (!taintGate.authorized) {
266
+ return {
267
+ record: {
268
+ step,
269
+ url,
270
+ urlVerdict,
271
+ timestamp: now(),
272
+ haltReason: `taint-policy:${taintGate.reason}`,
273
+ },
274
+ halted: true,
275
+ };
276
+ }
277
+ // Emit a cursor frame for the overlay immediately before navigation
278
+ // so a denied taint gate never animates an action that did not happen.
220
279
  if (options.cursorEmit) {
221
280
  const xy = options.defaultCursorXY ?? { x: 0, y: 0 };
222
281
  options.cursorEmit({
@@ -252,6 +311,38 @@ async function executeNavigate(step, options, now) {
252
311
  const hiddenTextReport = await options.hiddenTextScan(driverResult.elements);
253
312
  const hiddenText = extractHiddenText(hiddenTextReport);
254
313
  const visibleText = subtractHiddenText(driverResult.pageText, hiddenText);
314
+ const sourceRegistrationError = registerBrowserSource(options, "browser-dom", visibleText) ??
315
+ registerBrowserSource(options, "browser-dom", hiddenText) ??
316
+ (driverResult.accessibilityText !== undefined
317
+ ? registerBrowserSource(options, "browser-ax", driverResult.accessibilityText)
318
+ : null);
319
+ if (sourceRegistrationError) {
320
+ return {
321
+ record: {
322
+ step,
323
+ url: driverResult.finalUrl ?? url,
324
+ urlVerdict,
325
+ hiddenTextReport,
326
+ timestamp: now(),
327
+ haltReason: `taint-source-registration-failed:${sourceRegistrationError.slice(0, 200)}`,
328
+ error: sourceRegistrationError,
329
+ },
330
+ halted: true,
331
+ };
332
+ }
333
+ const visualTaintAdvisories = compareVisualLanes({
334
+ dom: visibleText,
335
+ ...(driverResult.accessibilityText !== undefined
336
+ ? { ax: driverResult.accessibilityText }
337
+ : {}),
338
+ now,
339
+ });
340
+ const taintSessionId = options.taintSessionId?.trim();
341
+ if (options.taintCoordinator && taintSessionId) {
342
+ for (const advisory of visualTaintAdvisories) {
343
+ options.taintCoordinator.recordVisualAdvisory(taintSessionId, advisory);
344
+ }
345
+ }
255
346
  // Gate D — prompt-injection quarantine on the VISIBLE page text.
256
347
  const injectionVerdict = await options.contentQuarantine(visibleText);
257
348
  if (extractQuarantineHalted(injectionVerdict)) {
@@ -263,6 +354,7 @@ async function executeNavigate(step, options, now) {
263
354
  urlVerdict,
264
355
  hiddenTextReport,
265
356
  injectionVerdict,
357
+ visualTaintAdvisories,
266
358
  pageContentPreview: buildPageContentPreview(visibleText, options.pageContentPreviewMax ?? DEFAULT_PREVIEW_MAX),
267
359
  timestamp: now(),
268
360
  haltReason: `quarantine:${reason}`,
@@ -288,6 +380,7 @@ async function executeNavigate(step, options, now) {
288
380
  hiddenTextReport,
289
381
  injectionVerdict,
290
382
  trifectaVerdict,
383
+ visualTaintAdvisories,
291
384
  pageContentPreview: buildPageContentPreview(visibleText, options.pageContentPreviewMax ?? DEFAULT_PREVIEW_MAX),
292
385
  timestamp: now(),
293
386
  haltReason: `trifecta:BLOCK`,
@@ -305,6 +398,7 @@ async function executeNavigate(step, options, now) {
305
398
  hiddenTextReport,
306
399
  injectionVerdict,
307
400
  trifectaVerdict,
401
+ visualTaintAdvisories,
308
402
  approved: false,
309
403
  pageContentPreview: buildPageContentPreview(visibleText, options.pageContentPreviewMax ?? DEFAULT_PREVIEW_MAX),
310
404
  timestamp: now(),
@@ -323,6 +417,7 @@ async function executeNavigate(step, options, now) {
323
417
  hiddenTextReport,
324
418
  injectionVerdict,
325
419
  trifectaVerdict,
420
+ visualTaintAdvisories,
326
421
  approved: trifectaVerdict.verdict === "REQUIRE_APPROVAL" ? true : undefined,
327
422
  pageContentPreview: buildPageContentPreview(visibleText, options.pageContentPreviewMax ?? DEFAULT_PREVIEW_MAX),
328
423
  timestamp: now(),
@@ -394,6 +489,11 @@ async function executeInteraction(step, options, now) {
394
489
  halted: true,
395
490
  };
396
491
  }
492
+ const taintGate = await authorizeBrowserAction(options, "browser.click", {
493
+ selector: step.target,
494
+ });
495
+ if (!taintGate.authorized)
496
+ return taintPolicyHalt(step, trifectaVerdict, taintGate.reason, now);
397
497
  await options.browserDriver.click(step.target);
398
498
  }
399
499
  else if (step.kind === "type") {
@@ -411,6 +511,12 @@ async function executeInteraction(step, options, now) {
411
511
  }
412
512
  const textRaw = step.args?.["text"];
413
513
  const text = typeof textRaw === "string" ? textRaw : "";
514
+ const taintGate = await authorizeBrowserAction(options, "browser.type", {
515
+ selector: step.target,
516
+ text,
517
+ });
518
+ if (!taintGate.authorized)
519
+ return taintPolicyHalt(step, trifectaVerdict, taintGate.reason, now);
414
520
  await options.browserDriver.type(step.target, text);
415
521
  }
416
522
  else if (step.kind === "extract" && options.browserDriver.extract) {
@@ -426,7 +532,37 @@ async function executeInteraction(step, options, now) {
426
532
  halted: true,
427
533
  };
428
534
  }
429
- await options.browserDriver.extract(step.target);
535
+ const taintGate = await authorizeBrowserAction(options, "browser.read_page", {
536
+ selector: step.target,
537
+ });
538
+ if (!taintGate.authorized)
539
+ return taintPolicyHalt(step, trifectaVerdict, taintGate.reason, now);
540
+ const extracted = await options.browserDriver.extract(step.target);
541
+ const sourceRegistrationError = registerBrowserSource(options, "browser-dom", extracted);
542
+ if (sourceRegistrationError) {
543
+ return {
544
+ record: {
545
+ step,
546
+ trifectaVerdict,
547
+ timestamp: now(),
548
+ haltReason: `taint-source-registration-failed:${sourceRegistrationError.slice(0, 200)}`,
549
+ error: sourceRegistrationError,
550
+ },
551
+ halted: true,
552
+ };
553
+ }
554
+ }
555
+ else if (step.kind === "read") {
556
+ const taintGate = await authorizeBrowserAction(options, "browser.read_page", {});
557
+ if (!taintGate.authorized)
558
+ return taintPolicyHalt(step, trifectaVerdict, taintGate.reason, now);
559
+ }
560
+ else if (step.kind === "approve") {
561
+ const taintGate = await authorizeBrowserAction(options, "browser.approve_action", {
562
+ ...(step.args ?? {}),
563
+ });
564
+ if (!taintGate.authorized)
565
+ return taintPolicyHalt(step, trifectaVerdict, taintGate.reason, now);
430
566
  }
431
567
  // `read` and `approve` have no driver-side side-effect; the record
432
568
  // is purely audit.
@@ -454,6 +590,17 @@ async function executeInteraction(step, options, now) {
454
590
  halted: false,
455
591
  };
456
592
  }
593
+ function taintPolicyHalt(step, trifectaVerdict, reason, now) {
594
+ return {
595
+ record: {
596
+ step,
597
+ trifectaVerdict,
598
+ timestamp: now(),
599
+ haltReason: `taint-policy:${reason}`,
600
+ },
601
+ halted: true,
602
+ };
603
+ }
457
604
  // ═══ Main API ════════════════════════════════════════════════════════════
458
605
  /**
459
606
  * Run an agentic browse session from natural-language task to terminal
@@ -15,6 +15,7 @@
15
15
  import type { ToolDefinition } from "../core/types.js";
16
16
  import { ChromeBridge } from "./chrome-bridge.js";
17
17
  import { CamoufoxBrowser } from "./camoufox-backend.js";
18
+ import type { TaintApprovalCoordinator } from "../security/taint-approval-coordinator.js";
18
19
  import type { BrowsePlan, BrowsePlanStep } from "./agentic-browser.js";
19
20
  import type { TabRegistry } from "./tab-registry.js";
20
21
  export type BrowserToolOk<T> = {
@@ -64,6 +65,11 @@ export interface BrowserDep {
64
65
  chrome?: ChromeBridge | null;
65
66
  camoufox?: CamoufoxBrowser | null;
66
67
  agentic?: BrowserAgenticDep;
68
+ taint?: {
69
+ readonly coordinator: TaintApprovalCoordinator;
70
+ readonly sessionId: string;
71
+ readonly cwd?: string;
72
+ };
67
73
  }
68
74
  export declare function dispatchBrowserTool(toolName: BrowserToolName, input: Record<string, unknown>, dep: BrowserDep): Promise<BrowserToolResult<unknown>>;
69
75
  /**