diff --git a/CHANGELOG.md b/CHANGELOG.md index fcde2b446..6b3ebba1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - TUI: keep the last streamed response instead of replacing it with "(no output)". (#747 — thanks @thewilloftheshadow) - Slack: accept slash commands with or without leading `/` for custom command configs. (#798 — thanks @thewilloftheshadow) - Onboarding/Configure: refuse to proceed with invalid configs; run `clawdbot doctor` first to avoid wiping custom fields. (#764 — thanks @mukhtharcm) +- Onboarding: quote Windows browser URLs when launching via `cmd start` to preserve OAuth query params. (#794 — thanks @roshanasingh4) - Anthropic: merge consecutive user turns (preserve newest metadata) before validation to avoid “Incorrect role information” errors. (#804 — thanks @ThomsenDrake) - Discord/Slack: centralize reply-thread planning so auto-thread replies stay in the created thread without parent reply refs. - Telegram: respect account-scoped bindings when webhook mode is enabled. (#821 — thanks @gumadeiras) diff --git a/src/agents/pi-embedded-helpers.test.ts b/src/agents/pi-embedded-helpers.test.ts index dcb19ed83..b352bc319 100644 --- a/src/agents/pi-embedded-helpers.test.ts +++ b/src/agents/pi-embedded-helpers.test.ts @@ -10,8 +10,8 @@ import { isCloudCodeAssistFormatError, isCompactionFailureError, isContextOverflowError, - isMessagingToolDuplicate, isFailoverErrorMessage, + isMessagingToolDuplicate, normalizeTextForComparison, sanitizeGoogleTurnOrdering, sanitizeSessionMessagesImages, diff --git a/src/commands/onboard-helpers.test.ts b/src/commands/onboard-helpers.test.ts index 5d058f51d..334c6b51e 100644 --- a/src/commands/onboard-helpers.test.ts +++ b/src/commands/onboard-helpers.test.ts @@ -27,9 +27,13 @@ describe("openUrl", () => { expect(ok).toBe(true); expect(mocks.runCommandWithTimeout).toHaveBeenCalledTimes(1); - const argv = mocks.runCommandWithTimeout.mock.calls[0]?.[0]; - expect(argv?.slice(0, 4)).toEqual(["cmd", "/c", "start", ""]); + const [argv, options] = mocks.runCommandWithTimeout.mock.calls[0] ?? []; + expect(argv?.slice(0, 4)).toEqual(["cmd", "/c", "start", '""']); expect(argv?.at(-1)).toBe(`"${url}"`); + expect(options).toMatchObject({ + timeoutMs: 5_000, + windowsVerbatimArguments: true, + }); }); }); diff --git a/src/commands/onboard-helpers.ts b/src/commands/onboard-helpers.ts index 37f85e1f5..bd04da0aa 100644 --- a/src/commands/onboard-helpers.ts +++ b/src/commands/onboard-helpers.ts @@ -232,9 +232,22 @@ function resolveSshTargetHint(): string { export async function openUrl(url: string): Promise { const resolved = await resolveBrowserOpenCommand(); if (!resolved.argv) return false; - const command = [...resolved.argv, resolved.quoteUrl ? `"${url}"` : url]; + const quoteUrl = resolved.quoteUrl === true; + const command = [...resolved.argv]; + if (quoteUrl) { + if (command.at(-1) === "") { + // Preserve the empty title token for `start` when using verbatim args. + command[command.length - 1] = '""'; + } + command.push(`"${url}"`); + } else { + command.push(url); + } try { - await runCommandWithTimeout(command, { timeoutMs: 5_000 }); + await runCommandWithTimeout(command, { + timeoutMs: 5_000, + windowsVerbatimArguments: quoteUrl, + }); return true; } catch { // ignore; we still print the URL for manual open diff --git a/src/process/exec.ts b/src/process/exec.ts index 4a0d6f831..71b828bcd 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -48,6 +48,7 @@ export type CommandOptions = { cwd?: string; input?: string; env?: NodeJS.ProcessEnv; + windowsVerbatimArguments?: boolean; }; export async function runCommandWithTimeout( @@ -59,6 +60,7 @@ export async function runCommandWithTimeout( ? { timeoutMs: optionsOrTimeout } : optionsOrTimeout; const { timeoutMs, cwd, input, env } = options; + const { windowsVerbatimArguments } = options; const hasInput = input !== undefined; // Spawn with inherited stdin (TTY) so tools like `pi` stay interactive when needed. @@ -67,6 +69,7 @@ export async function runCommandWithTimeout( stdio: [hasInput ? "pipe" : "inherit", "pipe", "pipe"], cwd, env: env ? { ...process.env, ...env } : process.env, + windowsVerbatimArguments, }); let stdout = ""; let stderr = "";