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.
- 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 +72 -104
- 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 +9 -0
- package/dist/orchestration/proof-bundles.js +2 -0
- 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-binding.d.ts +52 -0
- package/dist/security/approval-binding.js +57 -0
- 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/human-approval.d.ts +2 -0
- package/dist/security/human-approval.js +15 -24
- 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 +53 -0
- package/dist/verification/reproduction/autonomous-gate.js +72 -0
- package/dist/verification/reproduction/checkout-prep.d.ts +47 -0
- package/dist/verification/reproduction/checkout-prep.js +78 -0
- package/dist/verification/reproduction/diff-checker.d.ts +26 -0
- package/dist/verification/reproduction/diff-checker.js +33 -0
- package/dist/verification/reproduction/enforcement.d.ts +16 -0
- package/dist/verification/reproduction/enforcement.js +34 -0
- package/dist/verification/reproduction/exec-runner.d.ts +15 -0
- package/dist/verification/reproduction/exec-runner.js +47 -0
- package/dist/verification/reproduction/index.d.ts +10 -0
- package/dist/verification/reproduction/index.js +10 -0
- package/dist/verification/reproduction/mutation-gate.d.ts +42 -0
- package/dist/verification/reproduction/mutation-gate.js +43 -0
- package/dist/verification/reproduction/proof-artifact.d.ts +16 -0
- package/dist/verification/reproduction/proof-artifact.js +22 -0
- package/dist/verification/reproduction/replay-runner.d.ts +37 -0
- package/dist/verification/reproduction/replay-runner.js +28 -0
- package/dist/verification/reproduction/reproduce.d.ts +34 -0
- package/dist/verification/reproduction/reproduce.js +31 -0
- package/dist/verification/reproduction/verdict.d.ts +41 -0
- package/dist/verification/reproduction/verdict.js +40 -0
- 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
|
@@ -143,6 +143,34 @@ async function pickBackend(dep) {
|
|
|
143
143
|
function errEnv(error, detail) {
|
|
144
144
|
return { ok: false, error, ...(detail !== undefined ? { detail } : {}) };
|
|
145
145
|
}
|
|
146
|
+
function registerBrowserDomSource(dep, content) {
|
|
147
|
+
if (content.length === 0)
|
|
148
|
+
return;
|
|
149
|
+
dep.taint?.coordinator.registerSource({
|
|
150
|
+
kind: "browser-dom",
|
|
151
|
+
content,
|
|
152
|
+
integrity: "untrusted",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function authorizeBrowserMutation(dep, tool, args) {
|
|
156
|
+
if (!dep.taint)
|
|
157
|
+
return errEnv("not_configured", "browser mutation taint policy is unavailable");
|
|
158
|
+
const sessionId = dep.taint.sessionId.trim();
|
|
159
|
+
if (!sessionId)
|
|
160
|
+
return errEnv("not_configured", "browser mutation taint session is unavailable");
|
|
161
|
+
const action = Object.freeze({
|
|
162
|
+
tool,
|
|
163
|
+
args: Object.freeze(structuredClone(args)),
|
|
164
|
+
cwd: dep.taint.cwd?.trim() || ".",
|
|
165
|
+
});
|
|
166
|
+
const authorization = await dep.taint.coordinator.authorize({ sessionId, action });
|
|
167
|
+
if (!authorization.authorized)
|
|
168
|
+
return errEnv("upstream_error", authorization.reason);
|
|
169
|
+
const verification = dep.taint.coordinator.verifyExecution({ authorization, action });
|
|
170
|
+
return verification.ok
|
|
171
|
+
? null
|
|
172
|
+
: errEnv("upstream_error", `browser mutation execution verification failed: ${verification.reason}`);
|
|
173
|
+
}
|
|
146
174
|
// ── Dispatcher ──────────────────────────────────────────────
|
|
147
175
|
export async function dispatchBrowserTool(toolName, input, dep) {
|
|
148
176
|
// Agentic trio — these don't need a page-bound backend; they
|
|
@@ -150,7 +178,7 @@ export async function dispatchBrowserTool(toolName, input, dep) {
|
|
|
150
178
|
if (toolName === "browser.plan" ||
|
|
151
179
|
toolName === "browser.spawn_tab" ||
|
|
152
180
|
toolName === "browser.approve_action") {
|
|
153
|
-
return dispatchAgenticTool(toolName, input, dep
|
|
181
|
+
return dispatchAgenticTool(toolName, input, dep);
|
|
154
182
|
}
|
|
155
183
|
const backend = await pickBackend(dep);
|
|
156
184
|
if (!backend) {
|
|
@@ -163,6 +191,9 @@ export async function dispatchBrowserTool(toolName, input, dep) {
|
|
|
163
191
|
return errEnv("bad_input", "url required");
|
|
164
192
|
if (!isSafeUrl(url))
|
|
165
193
|
return errEnv("ssrf_blocked", url);
|
|
194
|
+
const taint = await authorizeBrowserMutation(dep, toolName, { url });
|
|
195
|
+
if (taint)
|
|
196
|
+
return taint;
|
|
166
197
|
if (backend === "chrome" && dep.chrome) {
|
|
167
198
|
const result = await dep.chrome.execute({ type: "navigate", url });
|
|
168
199
|
return result.success
|
|
@@ -181,6 +212,9 @@ export async function dispatchBrowserTool(toolName, input, dep) {
|
|
|
181
212
|
const selector = input["selector"];
|
|
182
213
|
if (typeof selector !== "string" || selector.trim().length === 0)
|
|
183
214
|
return errEnv("bad_input", "selector required");
|
|
215
|
+
const taint = await authorizeBrowserMutation(dep, toolName, { selector });
|
|
216
|
+
if (taint)
|
|
217
|
+
return taint;
|
|
184
218
|
if (backend === "chrome" && dep.chrome) {
|
|
185
219
|
const result = await dep.chrome.execute({ type: "click", selector });
|
|
186
220
|
return result.success
|
|
@@ -200,6 +234,9 @@ export async function dispatchBrowserTool(toolName, input, dep) {
|
|
|
200
234
|
const text = input["text"];
|
|
201
235
|
if (typeof selector !== "string" || typeof text !== "string")
|
|
202
236
|
return errEnv("bad_input", "selector + text required");
|
|
237
|
+
const taint = await authorizeBrowserMutation(dep, toolName, { selector, text });
|
|
238
|
+
if (taint)
|
|
239
|
+
return taint;
|
|
203
240
|
if (backend === "chrome" && dep.chrome) {
|
|
204
241
|
const result = await dep.chrome.execute({ type: "type", selector, value: text });
|
|
205
242
|
return result.success
|
|
@@ -234,14 +271,20 @@ export async function dispatchBrowserTool(toolName, input, dep) {
|
|
|
234
271
|
const result = await dep.chrome.execute({ type: "read_dom" });
|
|
235
272
|
if (!result.success)
|
|
236
273
|
return errEnv("upstream_error", result.error);
|
|
237
|
-
const text = result.domTree
|
|
274
|
+
const text = result.domTree
|
|
275
|
+
? dep.chrome.domToText(result.domTree)
|
|
276
|
+
: typeof result.data === "string"
|
|
277
|
+
? result.data
|
|
278
|
+
: "";
|
|
279
|
+
registerBrowserDomSource(dep, text);
|
|
238
280
|
return { ok: true, data: { text, backend } };
|
|
239
281
|
}
|
|
240
282
|
if (backend === "camoufox" && dep.camoufox) {
|
|
241
283
|
const result = await dep.camoufox.getText();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
284
|
+
if (!result.success)
|
|
285
|
+
return errEnv("upstream_error", result.error);
|
|
286
|
+
registerBrowserDomSource(dep, result.text);
|
|
287
|
+
return { ok: true, data: { text: result.text, backend } };
|
|
245
288
|
}
|
|
246
289
|
return errEnv("not_configured", "backend vanished");
|
|
247
290
|
}
|
|
@@ -251,7 +294,8 @@ export async function dispatchBrowserTool(toolName, input, dep) {
|
|
|
251
294
|
}
|
|
252
295
|
}
|
|
253
296
|
}
|
|
254
|
-
async function dispatchAgenticTool(toolName, input,
|
|
297
|
+
async function dispatchAgenticTool(toolName, input, dep) {
|
|
298
|
+
const agentic = dep.agentic;
|
|
255
299
|
switch (toolName) {
|
|
256
300
|
case "browser.plan": {
|
|
257
301
|
const task = input["task"];
|
|
@@ -289,6 +333,13 @@ async function dispatchAgenticTool(toolName, input, agentic) {
|
|
|
289
333
|
const taskId = typeof taskIdRaw === "string" && taskIdRaw.length > 0 ? taskIdRaw : "default";
|
|
290
334
|
if (!agentic || !agentic.tabRegistry)
|
|
291
335
|
return errEnv("not_configured", "tab-registry not wired");
|
|
336
|
+
const taint = await authorizeBrowserMutation(dep, toolName, {
|
|
337
|
+
ownership,
|
|
338
|
+
...(url !== undefined ? { url } : {}),
|
|
339
|
+
taskId,
|
|
340
|
+
});
|
|
341
|
+
if (taint)
|
|
342
|
+
return taint;
|
|
292
343
|
const tabId = agentic.spawnTabId?.(url) ?? `tab-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
293
344
|
const owner = ownership === "user" ? { kind: "user" } : { kind: "agent", taskId };
|
|
294
345
|
const result = agentic.tabRegistry.register(url !== undefined ? { tabId, owner, url } : { tabId, owner });
|
|
@@ -65,7 +65,7 @@ export type AgentOutboundMessageResult = {
|
|
|
65
65
|
readonly messageId: string;
|
|
66
66
|
} | {
|
|
67
67
|
readonly ok: false;
|
|
68
|
-
readonly reason: "channel_not_allowed" | "adapter_not_connected" | "missing_recipient_for_pairing" | "pairing_required";
|
|
68
|
+
readonly reason: "channel_not_allowed" | "adapter_not_connected" | "missing_recipient_for_pairing" | "pairing_required" | "taint_denied";
|
|
69
69
|
readonly pairingCode?: string;
|
|
70
70
|
};
|
|
71
71
|
export type AgentMessageToolSender = (request: {
|
|
@@ -102,10 +102,22 @@ export interface DeviceNode {
|
|
|
102
102
|
}
|
|
103
103
|
export interface GatewayConfig {
|
|
104
104
|
readonly requirePairing: boolean;
|
|
105
|
+
readonly requireOutboundAuthorization: boolean;
|
|
105
106
|
readonly pairingCodeTTL: number;
|
|
106
107
|
readonly maxQueueSize: number;
|
|
107
108
|
readonly allowedChannels: readonly ChannelType[];
|
|
108
109
|
}
|
|
110
|
+
export interface ChannelOutboundPayload {
|
|
111
|
+
readonly channelType: ChannelType;
|
|
112
|
+
readonly channelId: string;
|
|
113
|
+
readonly content: string;
|
|
114
|
+
readonly replyTo?: string;
|
|
115
|
+
readonly purpose: "send" | "agent-message" | "broadcast" | "response";
|
|
116
|
+
}
|
|
117
|
+
export type ChannelOutboundAuthorizer = (payload: ChannelOutboundPayload) => Promise<{
|
|
118
|
+
readonly ok: boolean;
|
|
119
|
+
readonly reason?: string;
|
|
120
|
+
}>;
|
|
109
121
|
export declare class ChannelGateway {
|
|
110
122
|
private readonly config;
|
|
111
123
|
private adapters;
|
|
@@ -114,6 +126,7 @@ export declare class ChannelGateway {
|
|
|
114
126
|
private devices;
|
|
115
127
|
private messageQueue;
|
|
116
128
|
private messageHandler;
|
|
129
|
+
private outboundAuthorizer;
|
|
117
130
|
private routePolicyEngine;
|
|
118
131
|
private pushInversionRegistry;
|
|
119
132
|
constructor(config?: Partial<GatewayConfig>);
|
|
@@ -163,6 +176,7 @@ export declare class ChannelGateway {
|
|
|
163
176
|
* This is typically the agent's query function.
|
|
164
177
|
*/
|
|
165
178
|
setMessageHandler(handler: (message: ChannelMessage) => Promise<string>): void;
|
|
179
|
+
setOutboundAuthorizer(authorizer: ChannelOutboundAuthorizer): void;
|
|
166
180
|
/**
|
|
167
181
|
* Connect all registered adapters.
|
|
168
182
|
*/
|
|
@@ -239,4 +253,5 @@ export declare class ChannelGateway {
|
|
|
239
253
|
getVerifiedSenderCount(): number;
|
|
240
254
|
private handleIncomingMessage;
|
|
241
255
|
private queueMessage;
|
|
256
|
+
private authorizeOutbound;
|
|
242
257
|
}
|
package/dist/channels/gateway.js
CHANGED
|
@@ -31,6 +31,7 @@ import { randomUUID } from "node:crypto";
|
|
|
31
31
|
import { createPushInversionRegistry, } from "./push-inversion.js";
|
|
32
32
|
const DEFAULT_CONFIG = {
|
|
33
33
|
requirePairing: true,
|
|
34
|
+
requireOutboundAuthorization: false,
|
|
34
35
|
pairingCodeTTL: 5 * 60 * 1000, // 5 minutes
|
|
35
36
|
maxQueueSize: 100,
|
|
36
37
|
allowedChannels: ["telegram", "slack", "discord", "webchat", "cli"],
|
|
@@ -43,6 +44,7 @@ export class ChannelGateway {
|
|
|
43
44
|
devices = new Map();
|
|
44
45
|
messageQueue = [];
|
|
45
46
|
messageHandler = null;
|
|
47
|
+
outboundAuthorizer = null;
|
|
46
48
|
// Route-policy engine (optional). When present, every inbound message
|
|
47
49
|
// is gated by the per-channel policy: pairing rules, rate limits, and
|
|
48
50
|
// response formatting. When absent the gateway falls back to its
|
|
@@ -129,6 +131,9 @@ export class ChannelGateway {
|
|
|
129
131
|
setMessageHandler(handler) {
|
|
130
132
|
this.messageHandler = handler;
|
|
131
133
|
}
|
|
134
|
+
setOutboundAuthorizer(authorizer) {
|
|
135
|
+
this.outboundAuthorizer = authorizer;
|
|
136
|
+
}
|
|
132
137
|
/**
|
|
133
138
|
* Connect all registered adapters.
|
|
134
139
|
*/
|
|
@@ -237,6 +242,9 @@ export class ChannelGateway {
|
|
|
237
242
|
const adapter = this.adapters.get(channelType);
|
|
238
243
|
if (!adapter?.connected)
|
|
239
244
|
return false;
|
|
245
|
+
if (!(await this.authorizeOutbound({ channelType, channelId, content, replyTo, purpose: "send" }))) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
240
248
|
return adapter.send(channelId, content, replyTo);
|
|
241
249
|
}
|
|
242
250
|
/**
|
|
@@ -261,6 +269,15 @@ export class ChannelGateway {
|
|
|
261
269
|
return { ok: false, reason: "pairing_required", pairingCode: pairing.code };
|
|
262
270
|
}
|
|
263
271
|
}
|
|
272
|
+
if (!(await this.authorizeOutbound({
|
|
273
|
+
channelType: request.channelType,
|
|
274
|
+
channelId: request.channelId,
|
|
275
|
+
content: request.content,
|
|
276
|
+
replyTo: request.replyTo,
|
|
277
|
+
purpose: "agent-message",
|
|
278
|
+
}))) {
|
|
279
|
+
return { ok: false, reason: "taint_denied" };
|
|
280
|
+
}
|
|
264
281
|
const sent = await adapter.send(request.channelId, request.content, request.replyTo);
|
|
265
282
|
if (!sent)
|
|
266
283
|
return { ok: false, reason: "adapter_not_connected" };
|
|
@@ -292,6 +309,14 @@ export class ChannelGateway {
|
|
|
292
309
|
const sent = [];
|
|
293
310
|
for (const [type, adapter] of this.adapters) {
|
|
294
311
|
if (adapter.connected) {
|
|
312
|
+
if (!(await this.authorizeOutbound({
|
|
313
|
+
channelType: type,
|
|
314
|
+
channelId: "broadcast",
|
|
315
|
+
content,
|
|
316
|
+
purpose: "broadcast",
|
|
317
|
+
}))) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
295
320
|
const ok = await adapter.send("broadcast", content);
|
|
296
321
|
if (ok)
|
|
297
322
|
sent.push(type);
|
|
@@ -402,6 +427,17 @@ export class ChannelGateway {
|
|
|
402
427
|
: rawResponse;
|
|
403
428
|
const adapter = this.adapters.get(message.channelType);
|
|
404
429
|
if (adapter?.connected) {
|
|
430
|
+
const authorized = await this.authorizeOutbound({
|
|
431
|
+
channelType: message.channelType,
|
|
432
|
+
channelId: message.channelId,
|
|
433
|
+
content: response,
|
|
434
|
+
replyTo: message.id,
|
|
435
|
+
purpose: "response",
|
|
436
|
+
});
|
|
437
|
+
if (!authorized) {
|
|
438
|
+
await adapter.send(message.channelId, "denied: outbound_authorization_required", message.id);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
405
441
|
await adapter.send(message.channelId, response, message.id);
|
|
406
442
|
}
|
|
407
443
|
else {
|
|
@@ -421,6 +457,16 @@ export class ChannelGateway {
|
|
|
421
457
|
}
|
|
422
458
|
this.messageQueue.push(message);
|
|
423
459
|
}
|
|
460
|
+
async authorizeOutbound(payload) {
|
|
461
|
+
if (!this.outboundAuthorizer)
|
|
462
|
+
return !this.config.requireOutboundAuthorization;
|
|
463
|
+
try {
|
|
464
|
+
return (await this.outboundAuthorizer(payload)).ok;
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
424
470
|
}
|
|
425
471
|
function normalizeChannelOverride(value) {
|
|
426
472
|
return typeof value === "string" ? value : undefined;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* A message received on Telegram can be responded to on Slack and emailed.
|
|
20
20
|
* The dispatch plane abstracts away the transport layer completely.
|
|
21
21
|
*/
|
|
22
|
-
import type { ChannelAdapter, ChannelMessage, ChannelType, DeviceNode } from "./gateway.js";
|
|
22
|
+
import type { ChannelAdapter, ChannelMessage, ChannelOutboundAuthorizer, ChannelType, DeviceNode } from "./gateway.js";
|
|
23
23
|
import type { DispatchRoutePolicy } from "./dispatch.js";
|
|
24
24
|
import { RoutePolicyEngine } from "./route-policies.js";
|
|
25
25
|
import type { ComputerSessionStore, SessionEvent } from "../session/computer-session-store.js";
|
|
@@ -57,6 +57,7 @@ export interface ChannelHealth {
|
|
|
57
57
|
}
|
|
58
58
|
export interface UnifiedDispatchConfig {
|
|
59
59
|
readonly requirePairing: boolean;
|
|
60
|
+
readonly requireOutboundAuthorization: boolean;
|
|
60
61
|
readonly pairingCodeTTL: number;
|
|
61
62
|
readonly maxQueueSize: number;
|
|
62
63
|
readonly maxInboxSize: number;
|
|
@@ -84,6 +85,7 @@ export declare class UnifiedDispatchPlane {
|
|
|
84
85
|
private readonly policies;
|
|
85
86
|
private readonly routePolicyEngine;
|
|
86
87
|
private messageHandler;
|
|
88
|
+
private outboundAuthorizer;
|
|
87
89
|
private computerSessionStore;
|
|
88
90
|
private computerSessionDispose;
|
|
89
91
|
private readonly computerSessionListeners;
|
|
@@ -91,6 +93,7 @@ export declare class UnifiedDispatchPlane {
|
|
|
91
93
|
constructor(config?: Partial<UnifiedDispatchConfig>);
|
|
92
94
|
registerAdapter(adapter: ChannelAdapter): void;
|
|
93
95
|
setMessageHandler(handler: (message: ChannelMessage) => Promise<string>): void;
|
|
96
|
+
setOutboundAuthorizer(authorizer: ChannelOutboundAuthorizer): void;
|
|
94
97
|
/**
|
|
95
98
|
* Attach a ComputerSessionStore so session events flow through this plane
|
|
96
99
|
* to every registered listener. Calling again with a different store
|
|
@@ -184,6 +187,7 @@ export declare class UnifiedDispatchPlane {
|
|
|
184
187
|
private handleIncomingMessage;
|
|
185
188
|
private processTask;
|
|
186
189
|
private updateHealth;
|
|
190
|
+
private authorizeOutbound;
|
|
187
191
|
private trimInbox;
|
|
188
192
|
}
|
|
189
193
|
export { analyzeComplexity, classifyPriority };
|
|
@@ -24,6 +24,7 @@ import { RoutePolicyEngine, createDefaultPolicy } from "./route-policies.js";
|
|
|
24
24
|
import { SurfaceRegistry, } from "./fan-out.js";
|
|
25
25
|
const DEFAULT_CONFIG = {
|
|
26
26
|
requirePairing: true,
|
|
27
|
+
requireOutboundAuthorization: false,
|
|
27
28
|
pairingCodeTTL: 5 * 60 * 1000,
|
|
28
29
|
maxQueueSize: 100,
|
|
29
30
|
maxInboxSize: 500,
|
|
@@ -96,6 +97,7 @@ export class UnifiedDispatchPlane {
|
|
|
96
97
|
policies = new Map();
|
|
97
98
|
routePolicyEngine;
|
|
98
99
|
messageHandler = null;
|
|
100
|
+
outboundAuthorizer = null;
|
|
99
101
|
// Computer-session bridge (Phase 3 P1-F1). When wired, incoming SessionEvents
|
|
100
102
|
// fan out to every listener registered via `onComputerSessionEvent`. The
|
|
101
103
|
// ComputerSessionStore remains the single source of truth for session state
|
|
@@ -138,6 +140,9 @@ export class UnifiedDispatchPlane {
|
|
|
138
140
|
setMessageHandler(handler) {
|
|
139
141
|
this.messageHandler = handler;
|
|
140
142
|
}
|
|
143
|
+
setOutboundAuthorizer(authorizer) {
|
|
144
|
+
this.outboundAuthorizer = authorizer;
|
|
145
|
+
}
|
|
141
146
|
// ── Computer-Session Bridge (Phase 3 P1-F1) ────────────
|
|
142
147
|
/**
|
|
143
148
|
* Attach a ComputerSessionStore so session events flow through this plane
|
|
@@ -317,7 +322,7 @@ export class UnifiedDispatchPlane {
|
|
|
317
322
|
for (const channelType of targetChannels) {
|
|
318
323
|
const adapter = this.adapters.get(channelType);
|
|
319
324
|
if (adapter?.connected) {
|
|
320
|
-
const ok = await
|
|
325
|
+
const ok = await this.sendToChannel(channelType, "forward", content);
|
|
321
326
|
if (ok)
|
|
322
327
|
forwarded.push(channelType);
|
|
323
328
|
}
|
|
@@ -353,6 +358,9 @@ export class UnifiedDispatchPlane {
|
|
|
353
358
|
const adapter = this.adapters.get(channelType);
|
|
354
359
|
if (!adapter?.connected)
|
|
355
360
|
return false;
|
|
361
|
+
if (!(await this.authorizeOutbound({ channelType, channelId, content, replyTo, purpose: "send" }))) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
356
364
|
const ok = await adapter.send(channelId, content, replyTo);
|
|
357
365
|
if (ok) {
|
|
358
366
|
this.updateHealth(channelType, {
|
|
@@ -365,12 +373,9 @@ export class UnifiedDispatchPlane {
|
|
|
365
373
|
const sent = [];
|
|
366
374
|
for (const [type, adapter] of this.adapters) {
|
|
367
375
|
if (adapter.connected) {
|
|
368
|
-
const ok = await
|
|
376
|
+
const ok = await this.sendToChannel(type, "broadcast", content);
|
|
369
377
|
if (ok) {
|
|
370
378
|
sent.push(type);
|
|
371
|
-
this.updateHealth(type, {
|
|
372
|
-
messagesSent: (this.channelHealth.get(type)?.messagesSent ?? 0) + 1,
|
|
373
|
-
});
|
|
374
379
|
}
|
|
375
380
|
}
|
|
376
381
|
}
|
|
@@ -503,10 +508,7 @@ export class UnifiedDispatchPlane {
|
|
|
503
508
|
// Respond on the original channel
|
|
504
509
|
const adapter = this.adapters.get(task.message.channelType);
|
|
505
510
|
if (adapter?.connected) {
|
|
506
|
-
await
|
|
507
|
-
this.updateHealth(task.message.channelType, {
|
|
508
|
-
messagesSent: (this.channelHealth.get(task.message.channelType)?.messagesSent ?? 0) + 1,
|
|
509
|
-
});
|
|
511
|
+
await this.sendToChannel(task.message.channelType, task.message.channelId, response, task.message.id);
|
|
510
512
|
}
|
|
511
513
|
// Cross-channel routing: also send to configured response channels
|
|
512
514
|
if (this.config.enableCrossChannelRouting) {
|
|
@@ -538,6 +540,16 @@ export class UnifiedDispatchPlane {
|
|
|
538
540
|
};
|
|
539
541
|
this.channelHealth.set(type, { ...current, ...updates });
|
|
540
542
|
}
|
|
543
|
+
async authorizeOutbound(payload) {
|
|
544
|
+
if (!this.outboundAuthorizer)
|
|
545
|
+
return !this.config.requireOutboundAuthorization;
|
|
546
|
+
try {
|
|
547
|
+
return (await this.outboundAuthorizer(payload)).ok;
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
541
553
|
trimInbox() {
|
|
542
554
|
if (this.inbox.size > this.config.maxInboxSize) {
|
|
543
555
|
const completed = [...this.inbox.entries()]
|
|
@@ -65,14 +65,6 @@ export const CLI_REGISTRY = Object.freeze([
|
|
|
65
65
|
versionRegex: /(\d+\.\d+\.\d+)/,
|
|
66
66
|
url: "https://github.com/google-gemini/gemini-cli",
|
|
67
67
|
},
|
|
68
|
-
{
|
|
69
|
-
id: "ollama",
|
|
70
|
-
displayName: "Ollama",
|
|
71
|
-
binaryNames: ["ollama"],
|
|
72
|
-
versionArgs: ["--version"],
|
|
73
|
-
versionRegex: /version is (\d+\.\d+\.\d+)/,
|
|
74
|
-
url: "https://ollama.ai",
|
|
75
|
-
},
|
|
76
68
|
{
|
|
77
69
|
id: "aider",
|
|
78
70
|
displayName: "Aider",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* - A planner stub (cheap heuristic — production wires to a real LLM)
|
|
16
16
|
* - A browser driver stub (production wires to chrome-bridge.ts)
|
|
17
17
|
*
|
|
18
|
-
* QB #6 (honest stubs):
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* QB #6 (honest stubs): the command runs in dry-run mode against a
|
|
19
|
+
* synthetic page. `--enable-driver` fails closed until this CLI owns a
|
|
20
|
+
* real driver wire. Production callers inject one via `runAgenticBrowse`.
|
|
21
21
|
*/
|
|
22
22
|
import type { BrowsePlan, BrowseSessionStatus } from "../../browser/agentic-browser.js";
|
|
23
23
|
export interface BrowseCommandOptions {
|
|
@@ -30,7 +30,7 @@ export interface BrowseCommandOptions {
|
|
|
30
30
|
readonly startUrl?: string;
|
|
31
31
|
/** When true, requires every browser action to pause for human approval. */
|
|
32
32
|
readonly alwaysAsk?: boolean;
|
|
33
|
-
/**
|
|
33
|
+
/** Reserved for a future real driver wire. Currently fails closed when true. */
|
|
34
34
|
readonly enableDriver?: boolean;
|
|
35
35
|
}
|
|
36
36
|
export interface BrowseCommandResult {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* - A planner stub (cheap heuristic — production wires to a real LLM)
|
|
16
16
|
* - A browser driver stub (production wires to chrome-bridge.ts)
|
|
17
17
|
*
|
|
18
|
-
* QB #6 (honest stubs):
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* QB #6 (honest stubs): the command runs in dry-run mode against a
|
|
19
|
+
* synthetic page. `--enable-driver` fails closed until this CLI owns a
|
|
20
|
+
* real driver wire. Production callers inject one via `runAgenticBrowse`.
|
|
21
21
|
*/
|
|
22
22
|
import { runAgenticBrowse } from "../../browser/agentic-browser.js";
|
|
23
23
|
import { inspectUrl } from "../../security/url-instruction-guard.js";
|
|
@@ -59,7 +59,7 @@ function deriveUrlFromTask(task) {
|
|
|
59
59
|
const q = encodeURIComponent(task.slice(0, 256));
|
|
60
60
|
return `https://duckduckgo.com/html/?q=${q}`;
|
|
61
61
|
}
|
|
62
|
-
// ── Driver stub
|
|
62
|
+
// ── Driver stub ─────────────────────────────────────────────────
|
|
63
63
|
/**
|
|
64
64
|
* Honest dry-run driver — never opens a browser. Returns a synthetic
|
|
65
65
|
* page snapshot so the orchestrator's security pipeline can exercise
|
|
@@ -151,7 +151,7 @@ function buildTrifectaWrapper(alwaysAsk) {
|
|
|
151
151
|
// ── Run command ────────────────────────────────────────────────
|
|
152
152
|
export async function runBrowseCommand(opts) {
|
|
153
153
|
const task = opts.task.trim();
|
|
154
|
-
const dryRun =
|
|
154
|
+
const dryRun = true;
|
|
155
155
|
const placeholderPlan = {
|
|
156
156
|
id: "plan-empty",
|
|
157
157
|
task: "",
|
|
@@ -171,6 +171,17 @@ export async function runBrowseCommand(opts) {
|
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
173
|
const plan = buildHeuristicPlan(task, opts.startUrl);
|
|
174
|
+
if (opts.enableDriver === true) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
task,
|
|
178
|
+
plan,
|
|
179
|
+
status: "failed",
|
|
180
|
+
stepsExecuted: 0,
|
|
181
|
+
dryRun,
|
|
182
|
+
error: "--enable-driver is not wired; no real browser action executed",
|
|
183
|
+
};
|
|
184
|
+
}
|
|
174
185
|
// QB #6 honest stubs: per-session HMAC secret + cheap heuristic
|
|
175
186
|
// classifier. Production callers (Claude Sub, OpenAI, etc.) wire a
|
|
176
187
|
// real LLM-backed classifier here.
|
|
@@ -201,6 +212,7 @@ export async function runBrowseCommand(opts) {
|
|
|
201
212
|
hiddenTextScan: buildHiddenTextScan(),
|
|
202
213
|
trifectaGuard: buildTrifectaWrapper(opts.alwaysAsk === true),
|
|
203
214
|
browserDriver: createDryRunDriver(),
|
|
215
|
+
executionMode: "dry-run",
|
|
204
216
|
...(opts.maxSteps !== undefined ? { maxStepsOverride: opts.maxSteps } : {}),
|
|
205
217
|
...(cursorEmit !== undefined ? { cursorEmit } : {}),
|
|
206
218
|
};
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
import { ComputerUseAgent, type ComputerAgentRepertoire } from "../../computer-use/computer-agent.js";
|
|
43
43
|
import { type ReplayReport } from "../../computer-use/action-replay.js";
|
|
44
44
|
import type { ComputerAction } from "../../computer-use/driver-contract.js";
|
|
45
|
-
import { CuaDriverComputerDriver } from "../../computer-use/cua-driver-backend.js";
|
|
45
|
+
import type { CuaDriverComputerDriver } from "../../computer-use/cua-driver-backend.js";
|
|
46
46
|
export type ComputerUseSubcommand = "click" | "type" | "key" | "screenshot" | "wait" | "scroll" | "repertoire" | "replay";
|
|
47
47
|
/** Subcommands that the CLI exposes. Source of truth for the
|
|
48
48
|
* `src/index.ts` dispatcher's `valid` allow-list. */
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
import { promises as fs } from "node:fs";
|
|
43
43
|
import { ComputerUseAgent, } from "../../computer-use/computer-agent.js";
|
|
44
44
|
import { deserializeSession, replaySession, } from "../../computer-use/action-replay.js";
|
|
45
|
-
import { CuaDriverComputerDriver, cuaDriverInstallHint, isCuaDriverAvailable, } from "../../computer-use/cua-driver-backend.js";
|
|
46
45
|
/** Subcommands that the CLI exposes. Source of truth for the
|
|
47
46
|
* `src/index.ts` dispatcher's `valid` allow-list. */
|
|
48
47
|
export const COMPUTER_USE_SUBCOMMANDS = Object.freeze([
|
|
@@ -165,47 +164,13 @@ export async function runComputerUseCommand(opts) {
|
|
|
165
164
|
dryRun: true,
|
|
166
165
|
};
|
|
167
166
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
error: `cua-driver unavailable — ${cuaDriverInstallHint()}`,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
// --execute path: explicit opt-in only. It uses the provider-agnostic
|
|
179
|
-
// CUA driver backend rather than Anthropic computer_use schemas; dry-run
|
|
180
|
-
// remains the default safety boundary.
|
|
181
|
-
const driver = opts.makeComputerDriver ? opts.makeComputerDriver() : new CuaDriverComputerDriver();
|
|
182
|
-
try {
|
|
183
|
-
const execution = await driver.execute(action);
|
|
184
|
-
return {
|
|
185
|
-
ok: execution.ok,
|
|
186
|
-
subcommand: opts.subcommand,
|
|
187
|
-
action,
|
|
188
|
-
dryRun: false,
|
|
189
|
-
...(execution.ok ? {} : { error: execution.error ?? "computer-use execute failed" }),
|
|
190
|
-
execution: {
|
|
191
|
-
ok: execution.ok,
|
|
192
|
-
...(execution.output !== undefined ? { output: execution.output } : {}),
|
|
193
|
-
...(execution.error !== undefined ? { error: execution.error } : {}),
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
catch (err) {
|
|
198
|
-
return {
|
|
199
|
-
ok: false,
|
|
200
|
-
subcommand: opts.subcommand,
|
|
201
|
-
action,
|
|
202
|
-
dryRun: false,
|
|
203
|
-
error: `execute failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
finally {
|
|
207
|
-
await driver.close();
|
|
208
|
-
}
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
subcommand: opts.subcommand,
|
|
170
|
+
action,
|
|
171
|
+
dryRun: false,
|
|
172
|
+
error: "computer-use --execute requires daemon — run `wotann engine start`, then retry",
|
|
173
|
+
};
|
|
209
174
|
}
|
|
210
175
|
// ── Phase-2 subcommand handlers ─────────────────────────────────
|
|
211
176
|
/**
|
package/dist/cli/commands.js
CHANGED
|
@@ -9,7 +9,7 @@ import { promisify } from "node:util";
|
|
|
9
9
|
import { createWorkspace } from "../core/workspace.js";
|
|
10
10
|
import { discoverProviders, formatFullStatus } from "../providers/discovery.js";
|
|
11
11
|
import { PROVIDER_DEFAULTS } from "../providers/model-defaults.js";
|
|
12
|
-
import {
|
|
12
|
+
import { isForbiddenTrustLayerProvider } from "../providers/fallback-chain.js";
|
|
13
13
|
import { buildPRDescription, parseConflictBlocks, parseDiffStat, renderCommitMessage, suggestCommitMessage, suggestConflictResolution, } from "../git/magic-git.js";
|
|
14
14
|
const execFileAsync = promisify(execFile);
|
|
15
15
|
export async function runInit(targetDir, options) {
|
|
@@ -54,20 +54,12 @@ export async function runInit(targetDir, options) {
|
|
|
54
54
|
console.log(chalk.dim(` ○ ${p.label}`) + chalk.dim(` — ${setupHint}`));
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
//
|
|
57
|
+
// Legacy --free compatibility guidance. Trust-layer inference stays BYO.
|
|
58
58
|
if (options.free) {
|
|
59
59
|
console.log();
|
|
60
|
-
console.log(chalk.bold("
|
|
61
|
-
console.log(chalk.dim("
|
|
62
|
-
console.log(chalk.dim("
|
|
63
|
-
console.log(chalk.dim(" KV cache: q8_0 (doubles Ollama context window)"));
|
|
64
|
-
const ollamaActive = active.some((p) => p.provider === "ollama");
|
|
65
|
-
if (!ollamaActive) {
|
|
66
|
-
console.log();
|
|
67
|
-
console.log(chalk.yellow(" ⚠ Ollama not detected."));
|
|
68
|
-
console.log(chalk.dim(" Install: https://ollama.ai"));
|
|
69
|
-
console.log(chalk.dim(" Then: ollama pull qwen3-coder-next"));
|
|
70
|
-
}
|
|
60
|
+
console.log(chalk.bold(" BYO provider required:"));
|
|
61
|
+
console.log(chalk.dim(" --free no longer configures local or anonymous model runtimes."));
|
|
62
|
+
console.log(chalk.dim(" Configure a remote subscription or API key with `wotann login`."));
|
|
71
63
|
}
|
|
72
64
|
// Extended context guidance
|
|
73
65
|
if (options.extendedContext) {
|
|
@@ -91,8 +83,6 @@ function getProviderSetupHint(provider) {
|
|
|
91
83
|
return 'npx @openai/codex --full-auto "hello"';
|
|
92
84
|
case "copilot":
|
|
93
85
|
return "export GH_TOKEN=ghp_... (GitHub PAT)";
|
|
94
|
-
case "ollama":
|
|
95
|
-
return "ollama serve (https://ollama.ai)";
|
|
96
86
|
case "gemini":
|
|
97
87
|
return "export GEMINI_API_KEY=... (free at ai.google.dev)";
|
|
98
88
|
case "huggingface":
|
|
@@ -176,7 +166,7 @@ export async function runDoctor(targetDir, options = {}) {
|
|
|
176
166
|
detail: `${nodeVersion} (requires ≥22.13.0)`,
|
|
177
167
|
});
|
|
178
168
|
// S4-9: Expanded doctor checks — daemon health, socket, DB integrity,
|
|
179
|
-
//
|
|
169
|
+
// port conflicts, and API key validity.
|
|
180
170
|
// 4. Daemon socket reachability
|
|
181
171
|
const { resolveDaemonSocketPath, isNamedPipe } = await import("../daemon/transport/socket-path.js");
|
|
182
172
|
const socketPath = process.platform === "win32"
|
|
@@ -241,27 +231,7 @@ export async function runDoctor(targetDir, options = {}) {
|
|
|
241
231
|
detail: "Not yet created (no-op)",
|
|
242
232
|
});
|
|
243
233
|
}
|
|
244
|
-
// 6.
|
|
245
|
-
const ollamaUrl = resolveOllamaHost();
|
|
246
|
-
try {
|
|
247
|
-
const controller = new AbortController();
|
|
248
|
-
const timer = setTimeout(() => controller.abort(), 1500);
|
|
249
|
-
const res = await fetch(`${ollamaUrl}/api/version`, { signal: controller.signal });
|
|
250
|
-
clearTimeout(timer);
|
|
251
|
-
checks.push({
|
|
252
|
-
name: "Ollama",
|
|
253
|
-
ok: res.ok,
|
|
254
|
-
detail: res.ok ? `Reachable at ${ollamaUrl}` : `HTTP ${res.status}`,
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
checks.push({
|
|
259
|
-
name: "Ollama",
|
|
260
|
-
ok: false,
|
|
261
|
-
detail: `Not reachable at ${ollamaUrl} (install ollama or set OLLAMA_URL)`,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
// 7. Session token perms (if present) — must be 0o600
|
|
234
|
+
// 6. Session token perms (if present) — must be 0o600
|
|
265
235
|
const tokenPath = resolveWotannHomeSubdir("session-token.json");
|
|
266
236
|
if (existsSync(tokenPath)) {
|
|
267
237
|
try {
|
|
@@ -489,7 +459,7 @@ export async function runMagicGit(options) {
|
|
|
489
459
|
}
|
|
490
460
|
export function buildModelCatalogRows(defaults, statuses) {
|
|
491
461
|
const statusByProvider = new Map(statuses.map((status) => [status.provider, status]));
|
|
492
|
-
return Object.entries(defaults).map(([provider, entry]) => {
|
|
462
|
+
return Object.entries(defaults).filter(([provider]) => !isForbiddenTrustLayerProvider(provider)).map(([provider, entry]) => {
|
|
493
463
|
const status = statusByProvider.get(provider);
|
|
494
464
|
const seen = new Set();
|
|
495
465
|
const models = [];
|