wotann 0.5.97 → 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.
- package/README.md +18 -38
- package/dist/api/anthropic-gateway.d.ts +4 -5
- package/dist/api/anthropic-gateway.js +4 -5
- package/dist/api/server.js +4 -2
- package/dist/api/ws-hardening.d.ts +2 -2
- package/dist/api/ws-hardening.js +2 -2
- package/dist/auth/login.d.ts +1 -2
- package/dist/auth/login.js +3 -44
- package/dist/browser/agentic-browser.d.ts +14 -0
- package/dist/browser/agentic-browser.js +150 -3
- package/dist/browser/browser-tools.d.ts +6 -0
- package/dist/browser/browser-tools.js +57 -6
- package/dist/channels/gateway.d.ts +16 -1
- package/dist/channels/gateway.js +46 -0
- package/dist/channels/unified-dispatch.d.ts +5 -1
- package/dist/channels/unified-dispatch.js +21 -9
- package/dist/cli/cli-detection.js +0 -8
- package/dist/cli/commands/browse.d.ts +4 -4
- package/dist/cli/commands/browse.js +17 -5
- package/dist/cli/commands/computer-use.d.ts +1 -1
- package/dist/cli/commands/computer-use.js +7 -42
- package/dist/cli/commands.js +8 -38
- package/dist/cli/first-run-runner-factory.d.ts +2 -3
- package/dist/cli/first-run-runner-factory.js +6 -3
- package/dist/cli/onboarding-screens/done-screen.js +1 -1
- package/dist/cli/onboarding-screens.d.ts +4 -7
- package/dist/cli/onboarding-screens.js +29 -35
- package/dist/cli/orphan-wires/arena-cmd.d.ts +14 -3
- package/dist/cli/orphan-wires/arena-cmd.js +12 -10
- package/dist/cli/orphan-wires/harness-introspect-cmd.d.ts +1 -1
- package/dist/cli/orphan-wires/harness-introspect-cmd.js +2 -2
- package/dist/cli/orphan-wires/redteam-scan-cmd.d.ts +13 -5
- package/dist/cli/orphan-wires/redteam-scan-cmd.js +9 -12
- package/dist/cli/run-onboarding-wizard.js +9 -16
- package/dist/cli/runtime-query.d.ts +1 -1
- package/dist/cli/runtime-query.js +5 -1
- package/dist/cli/test-provider.js +1 -4
- package/dist/cli/voice-cmds.d.ts +6 -8
- package/dist/cli/voice-cmds.js +10 -24
- package/dist/computer-use/computer-agent.d.ts +14 -0
- package/dist/computer-use/computer-agent.js +59 -2
- package/dist/computer-use/perception-engine.d.ts +4 -1
- package/dist/computer-use/perception-engine.js +11 -3
- package/dist/context/limits.d.ts +2 -4
- package/dist/context/limits.js +17 -29
- package/dist/context/maximizer.d.ts +0 -1
- package/dist/context/maximizer.js +11 -22
- package/dist/context/window-intelligence.js +1 -1
- package/dist/core/agent-bridge.d.ts +7 -6
- package/dist/core/agent-bridge.js +31 -10
- package/dist/core/agent-tool-context.d.ts +4 -0
- package/dist/core/agent-tool-context.js +12 -1
- package/dist/core/config-discovery.d.ts +4 -4
- package/dist/core/config-discovery.js +11 -12
- package/dist/core/config.d.ts +1 -5
- package/dist/core/config.js +4 -16
- package/dist/core/default-provider.js +6 -3
- package/dist/core/effort-mode.d.ts +4 -5
- package/dist/core/effort-mode.js +31 -14
- package/dist/core/hardware-detect.d.ts +3 -11
- package/dist/core/hardware-detect.js +11 -22
- package/dist/core/prompt-override.js +17 -4
- package/dist/core/runtime.d.ts +31 -5
- package/dist/core/runtime.js +262 -37
- package/dist/core/schema-migration.js +9 -3
- package/dist/core/types.d.ts +14 -3
- package/dist/core/workspace.js +7 -26
- package/dist/daemon/auto-update.d.ts +6 -11
- package/dist/daemon/auto-update.js +12 -148
- package/dist/daemon/kairos-rpc.d.ts +4 -1
- package/dist/daemon/kairos-rpc.js +436 -268
- package/dist/daemon/kairos-tools.d.ts +3 -3
- package/dist/daemon/kairos-tools.js +3 -5
- package/dist/daemon/kairos.js +35 -3
- package/dist/daemon/rpc-handlers/sandbox-rpc.d.ts +12 -3
- package/dist/daemon/rpc-handlers/sandbox-rpc.js +64 -38
- package/dist/daemon/start.js +0 -9
- package/dist/desktop/companion-server.d.ts +1 -0
- package/dist/desktop/companion-server.js +136 -113
- package/dist/index.js +70 -144
- package/dist/intelligence/harness-introspect.d.ts +2 -2
- package/dist/intelligence/harness-introspect.js +5 -12
- package/dist/intelligence/provider-arbitrage.js +16 -26
- package/dist/intelligence/task-semantic-router.js +12 -11
- package/dist/lib.d.ts +0 -7
- package/dist/lib.js +0 -13
- package/dist/memory/cross-encoder.d.ts +3 -3
- package/dist/memory/cross-encoder.js +3 -3
- package/dist/memory/local-embedding.d.ts +2 -2
- package/dist/memory/local-embedding.js +4 -105
- package/dist/memory/onnx-cross-encoder.d.ts +17 -23
- package/dist/memory/onnx-cross-encoder.js +19 -49
- package/dist/memory/store.d.ts +3 -3
- package/dist/memory/store.js +9 -23
- package/dist/middleware/layers.d.ts +2 -2
- package/dist/middleware/layers.js +5 -4
- package/dist/mobile/ios-app.d.ts +3 -2
- package/dist/mobile/ios-app.js +11 -6
- package/dist/orchestration/agent-registry.d.ts +2 -2
- package/dist/orchestration/agent-registry.js +16 -14
- package/dist/orchestration/architect-editor.js +6 -10
- package/dist/orchestration/council.js +10 -1
- package/dist/orchestration/proof-bundles.d.ts +4 -3
- package/dist/plugins/service-manifest.d.ts +1 -1
- package/dist/providers/adapter-registry.js +10 -11
- package/dist/providers/capability-equalizer.js +6 -32
- package/dist/providers/cli-registry.js +0 -21
- package/dist/providers/discovery.d.ts +0 -70
- package/dist/providers/discovery.js +3 -211
- package/dist/providers/dynamic-discovery.d.ts +0 -2
- package/dist/providers/dynamic-discovery.js +0 -45
- package/dist/providers/fallback-chain.d.ts +6 -11
- package/dist/providers/fallback-chain.js +30 -27
- package/dist/providers/model-defaults.js +4 -1
- package/dist/providers/model-discovery.d.ts +2 -1
- package/dist/providers/model-discovery.js +10 -29
- package/dist/providers/model-router.d.ts +1 -10
- package/dist/providers/model-router.js +19 -63
- package/dist/providers/model-switcher.js +18 -1
- package/dist/providers/onboarding-auth-choices.js +3 -0
- package/dist/providers/openai-compat-adapter.js +1 -1
- package/dist/providers/preset-library.d.ts +1 -1
- package/dist/providers/preset-library.js +12 -5
- package/dist/providers/provider-brain.js +5 -2
- package/dist/providers/provider-ladder.d.ts +10 -16
- package/dist/providers/provider-ladder.js +19 -16
- package/dist/providers/provider-service.js +0 -41
- package/dist/providers/public-tier/fallback-chain.js +4 -7
- package/dist/providers/rate-limiter.d.ts +5 -5
- package/dist/providers/rate-limiter.js +17 -8
- package/dist/providers/registry.d.ts +1 -1
- package/dist/providers/registry.js +7 -50
- package/dist/providers/smart-routing.js +6 -2
- package/dist/providers/state-aware-failover.js +6 -1
- package/dist/providers/usage-intelligence.d.ts +3 -3
- package/dist/providers/usage-intelligence.js +4 -20
- package/dist/security/approval-queue-decision-broker.d.ts +29 -0
- package/dist/security/approval-queue-decision-broker.js +76 -0
- package/dist/security/file-freeze.js +3 -0
- package/dist/security/guardrails-off.d.ts +5 -11
- package/dist/security/guardrails-off.js +35 -116
- package/dist/security/hash-audit-chain.d.ts +5 -4
- package/dist/security/hash-audit-chain.js +8 -6
- package/dist/security/pii-scrubber.d.ts +3 -2
- package/dist/security/pii-scrubber.js +5 -13
- package/dist/security/privacy-router.d.ts +2 -2
- package/dist/security/privacy-router.js +14 -10
- package/dist/security/private-mode.d.ts +5 -6
- package/dist/security/private-mode.js +11 -13
- package/dist/security/provenance-label.d.ts +11 -0
- package/dist/security/provenance-label.js +29 -0
- package/dist/security/provenance-tracker.d.ts +42 -0
- package/dist/security/provenance-tracker.js +187 -0
- package/dist/security/taint-approval-coordinator.d.ts +49 -0
- package/dist/security/taint-approval-coordinator.js +148 -0
- package/dist/security/taint-receipt.d.ts +37 -0
- package/dist/security/taint-receipt.js +77 -0
- package/dist/security/taint-sink-gate.d.ts +22 -0
- package/dist/security/taint-sink-gate.js +189 -0
- package/dist/security/visual-taint.d.ts +17 -0
- package/dist/security/visual-taint.js +64 -0
- package/dist/session/approval-queue.d.ts +23 -2
- package/dist/session/approval-queue.js +84 -6
- package/dist/telemetry/cost-oracle.js +17 -10
- package/dist/tools/agent-tools.d.ts +5 -0
- package/dist/tools/agent-tools.js +275 -170
- package/dist/training/pipeline.d.ts +2 -2
- package/dist/training/pipeline.js +5 -13
- package/dist/ui/components/CommandPaletteCommands.js +3 -3
- package/dist/ui/components/ModelPicker.d.ts +1 -1
- package/dist/ui/components/ModelPicker.js +5 -2
- package/dist/ui/components/OnboardingWizard.d.ts +3 -3
- package/dist/ui/components/OnboardingWizard.js +4 -10
- package/dist/ui/components/ProviderSetupOverlay.js +3 -4
- package/dist/ui/components/StartupScreen.js +1 -1
- package/dist/ui/components/v3/AppV3.js +3 -0
- package/dist/ui/components/v3/OnboardingTour.d.ts +4 -3
- package/dist/ui/components/v3/OnboardingTour.js +10 -5
- package/dist/utils/atomic-io.d.ts +1 -0
- package/dist/utils/atomic-io.js +23 -0
- package/dist/utils/atomic-write.d.ts +2 -0
- package/dist/utils/atomic-write.js +17 -0
- package/dist/utils/bounded-base64.d.ts +2 -0
- package/dist/utils/bounded-base64.js +17 -0
- package/dist/utils/path-realpath.d.ts +12 -0
- package/dist/utils/path-realpath.js +38 -2
- package/dist/utils/sidecar-downloader.d.ts +2 -2
- package/dist/utils/sidecar-downloader.js +2 -20
- package/dist/utils/sqlite-kv-store.d.ts +1 -1
- package/dist/utils/sqlite-kv-store.js +4 -3
- package/dist/verification/reproduction/autonomous-gate.d.ts +5 -4
- package/dist/verification/reproduction/autonomous-gate.js +5 -4
- package/dist/verification/reproduction/checkout-prep.d.ts +6 -7
- package/dist/verification/reproduction/enforcement.d.ts +2 -0
- package/dist/verification/reproduction/enforcement.js +5 -1
- package/dist/verification/reproduction/index.d.ts +1 -1
- package/dist/verification/reproduction/index.js +1 -1
- package/dist/verification/reproduction/replay-runner.d.ts +4 -4
- package/dist/verification/reproduction/reproduce.d.ts +1 -1
- package/dist/verification/reproduction/verdict.d.ts +4 -2
- package/dist/verification/verifier-engine.js +1 -1
- package/dist/verification/verifier-model-selector.js +8 -16
- package/dist/voice/edge-tts-backend.d.ts +3 -9
- package/dist/voice/edge-tts-backend.js +8 -9
- package/dist/voice/stt-detector.d.ts +6 -13
- package/dist/voice/stt-detector.js +17 -128
- package/dist/voice/tts-engine.d.ts +9 -19
- package/dist/voice/tts-engine.js +36 -113
- package/dist/voice/vibevoice-backend.js +17 -1
- package/dist/voice/voice-mode.d.ts +3 -8
- package/dist/voice/voice-mode.js +14 -152
- package/dist/voice/voice-pipeline.d.ts +10 -12
- package/dist/voice/voice-pipeline.js +47 -245
- 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.
|
|
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
|
-
|
|
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
|
|
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
|
|
47
|
-
| **
|
|
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
|
-
|
|
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
|
-
#
|
|
106
|
-
|
|
107
|
-
wotann # auto-detects Ollama
|
|
105
|
+
# ChatGPT/Codex and Claude subscription paths are supported too
|
|
106
|
+
wotann onboard
|
|
108
107
|
```
|
|
109
108
|
|
|
110
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
155
|
+
### The harness amplifies provider capabilities
|
|
176
156
|
|
|
177
|
-
`capability-augmenter` injects tool-calling, vision (via OCR + a11y tree), and extended thinking
|
|
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
|
|
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 ·
|
|
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
|
|
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**:
|
|
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
|
-
|
|
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
|
|
12
|
-
*
|
|
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.
|
|
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
|
|
12
|
-
*
|
|
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.
|
|
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
|
*
|
package/dist/api/server.js
CHANGED
|
@@ -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, ...
|
|
263
|
-
:
|
|
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
|
|
18
|
-
*
|
|
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.
|
package/dist/api/ws-hardening.js
CHANGED
|
@@ -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
|
|
18
|
-
*
|
|
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.
|
package/dist/auth/login.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
package/dist/auth/login.js
CHANGED
|
@@ -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.
|
|
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: "
|
|
385
|
-
method: "
|
|
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
|
-
|
|
219
|
-
|
|
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.
|
|
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
|
/**
|