From b8db227991396a57d5872d95582e69a98e686bba Mon Sep 17 00:00:00 2001 From: Sentinel-Bluebuilder Date: Thu, 21 May 2026 14:27:48 -0700 Subject: [PATCH] fix(ai-path): connect() onProgress no longer fires 'log' duplicates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously connect({ onProgress }) received both the structured stage events (wallet, node-check, validate, session, handshake, tunnel, verify) AND a sibling 'log' event for the same line, fired from the SDK's internal raw log forwarder. Consumers had to filter `stage === 'log'` to avoid triple-printing every line. The forwarder now routes raw logs to a separate `onLog` callback. The documented contract for `onProgress` — "one event per stage transition" — is now what consumers actually observe. - ai-path/connect.js: split raw `log:` forwarder; route to opts.onLog, do NOT forward to opts.onProgress. JSDoc updated. - ai-path/README.md: options table now lists onProgress + onLog separately and documents `silent: true` for suppressing the SDK's built-in step output. - ai-path/FAILURES.md: documented the API-CONTRACT change under Pending Integration with a migration note. Consumer-app context: the x402 fresh-test agent harness saw triple-logged progress lines on every connect(). After filtering 'log' on the consumer side the output became correct — but only because both sides knew the quirk. This fixes the SDK so the documented contract is the actual contract. No breaking change for consumers that ignore 'log' or set silent: true. --- ai-path/FAILURES.md | 6 ++++++ ai-path/README.md | 4 +++- ai-path/connect.js | 10 ++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ai-path/FAILURES.md b/ai-path/FAILURES.md index 7593588..6f98ce4 100644 --- a/ai-path/FAILURES.md +++ b/ai-path/FAILURES.md @@ -396,6 +396,12 @@ Every finding traces back to a specific project. This section documents the sour ## Pending Integration +### connect() `onProgress` no longer receives `'log'` events +**Category:** API-CONTRACT +**Summary:** Prior to this change, `connect({ onProgress })` received both the structured stage events (`'wallet'`, `'session'`, `'tunnel'`, etc.) AND a raw `'log'` event for every internal SDK log line. Consumers ended up logging every line twice — once from `[log]` and once from the structured stage. +**Fix:** `onProgress` now only fires for the documented structured stages. Raw logs go to a new optional `onLog(message)` callback. To suppress the SDK's own built-in `[STEP X/Y]` lines, pass `silent: true`. +**Migration:** If you were filtering `stage === 'log'` in your callback, you can drop the check. If you depended on log lines, add `onLog: (msg) => …` to your `connect()` opts. + ### [PENDING] fix-registry-backup.md **Category:** BUG-FIX **Summary:** `setSystemProxy()` overwrites Windows proxy settings with `/f` (force), no backup/restore of previous state. If user had corporate proxy, `clearSystemProxy()` sets "no proxy" instead of restoring their previous configuration. diff --git a/ai-path/README.md b/ai-path/README.md index bc87eaa..fccd234 100644 --- a/ai-path/README.md +++ b/ai-path/README.md @@ -187,7 +187,9 @@ const vpn = await connect({ | `killSwitch` | `boolean` | `false` | Block all non-tunnel traffic while connected. **UNTESTED — code exists but never verified on mainnet. WireGuard only.** | | `maxAttempts` | `number` | `3` | Max nodes to try on auto-connect before failing | | `timeout` | `number` | `120000` | Connection timeout in milliseconds (2 minutes) | -| `onProgress` | `function` | `null` | `(step: string, detail: string) => void` | +| `onProgress` | `function` | `null` | `(stage: string, detail: string) => void` — structured stages only (`wallet`/`node-check`/`session`/`handshake`/`tunnel`/`verify`). Fires once per stage. | +| `onLog` | `function` | `null` | `(message: string) => void` — raw SDK log lines. Use for verbose tracing; otherwise prefer `onProgress`. | +| `silent` | `boolean` | `false` | Suppress the SDK's built-in `[STEP X/Y]` console output (consumer drives its own log via `onProgress`). | | `signal` | `AbortSignal` | `null` | AbortController signal for cancellation | | `v2rayExePath` | `string` | `auto` | Path to V2Ray binary. Auto-detected from `bin/` | diff --git a/ai-path/connect.js b/ai-path/connect.js index 24cba34..71a60b1 100644 --- a/ai-path/connect.js +++ b/ai-path/connect.js @@ -283,7 +283,11 @@ async function preValidateBalance(mnemonic) { * @param {string|number} [opts.subscriptionId] - Connect via existing subscription (operator-provisioned) * @param {string|number} [opts.planId] - Connect via plan (subscribes + starts session) * @param {string} [opts.feeGranter] - Operator address that pays gas (sent1...). Skips balance check. - * @param {function} [opts.onProgress] - Progress callback: (stage, message) => void + * @param {function} [opts.onProgress] - Structured stage callback: (stage, message) => void. + * Stages: 'wallet'|'node-check'|'validate'|'session'|'handshake'|'tunnel'|'verify'|'dry-run'. + * Each stage fires exactly once per transition — no 'log' duplicates. + * @param {function} [opts.onLog] - Raw SDK log callback: (message) => void. Receives every + * internal log line. Use this for verbose tracing; otherwise prefer onProgress. * @param {number} [opts.timeout] - Connection timeout in ms (default: 120000 — 2 minutes) * @param {boolean} [opts.silent] - If true, suppress step-by-step console output * @returns {Promise<{ @@ -507,7 +511,9 @@ export async function connect(opts = {}) { } }, log: (msg) => { - if (opts.onProgress) opts.onProgress('log', msg); + // Raw SDK logs go to opts.onLog only. Do NOT forward to onProgress — + // that duplicates every structured stage event with a 'log' sibling. + if (opts.onLog) opts.onLog(msg); }, };