diff --git a/apps/desktop/src/preview/BrowserImport/ChromiumCookies.ts b/apps/desktop/src/preview/BrowserImport/ChromiumCookies.ts index 36ec1700debe..a2b8ed1e5734 100644 --- a/apps/desktop/src/preview/BrowserImport/ChromiumCookies.ts +++ b/apps/desktop/src/preview/BrowserImport/ChromiumCookies.ts @@ -326,25 +326,26 @@ export const readChromiumCookies = Effect.fn("ChromiumCookies.readChromiumCookie ChromiumCookieReadError, FileSystem.FileSystem | Path.Path | Scope.Scope | ChildProcessSpawner.ChildProcessSpawner > { - const keys = yield* ( + const toCookieReadError = (cause: ChromiumKeyError) => + new ChromiumCookieReadError({ + reason: cause.reason, + cookieDatabasePath: source.cookieDatabasePath, + cause, + }); + // Two `yield*` paths — a ternary union of Effects is not iterable under + // `Effect.fn` (`never` / `Buffer` vs `Buffer`). + const keys: ChromiumKeyMaterial = source.platform === "win32" && source.windowsLocalStatePath - ? readWindowsKey(source.windowsLocalStatePath).pipe(Effect.map((gcmV10) => ({ gcmV10 }))) - : resolveChromiumKeys({ + ? yield* readWindowsKey(source.windowsLocalStatePath).pipe( + Effect.map((gcmV10): ChromiumKeyMaterial => ({ gcmV10 })), + Effect.mapError(toCookieReadError), + ) + : yield* resolveChromiumKeys({ platform: source.platform, keychainService: source.keychainService, keychainAccount: source.keychainAccount, linuxSecretApplication: source.linuxSecretApplication, - }) - ).pipe( - Effect.mapError( - (cause: ChromiumKeyError) => - new ChromiumCookieReadError({ - reason: cause.reason, - cookieDatabasePath: source.cookieDatabasePath, - cause, - }), - ), - ); + }).pipe(Effect.mapError(toCookieReadError)); const snapshotPath = yield* snapshotCookieDatabase(source.cookieDatabasePath).pipe( Effect.mapError( diff --git a/apps/desktop/src/updates/updatesTestHarness.ts b/apps/desktop/src/updates/updatesTestHarness.ts index 6064e568ea0a..38f051df37b6 100644 --- a/apps/desktop/src/updates/updatesTestHarness.ts +++ b/apps/desktop/src/updates/updatesTestHarness.ts @@ -41,7 +41,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { let downloadCount = 0; let allowDowngrade = false; let fullChangelog = false; - const feedUrls: ElectronUpdater.ElectronUpdaterFeedUrl[] = []; + const feedUrls: unknown[] = []; const listeners = new Map void>>(); const sentStates: DesktopUpdateState[] = []; const installSteps: string[] = []; diff --git a/apps/discord-bot/src/features/ResponseBridge.test.ts b/apps/discord-bot/src/features/ResponseBridge.test.ts index c137133e66b5..3960b5ba7ec8 100644 --- a/apps/discord-bot/src/features/ResponseBridge.test.ts +++ b/apps/discord-bot/src/features/ResponseBridge.test.ts @@ -1697,6 +1697,69 @@ Use get_command_or_subagent_output("call-caf23c75-09ca-4fc6-a98e-daa9bcaa8e80-41 expect(messages.map((message) => message.id)).toEqual(["user-real-1"]); }); + it("suppresses runtime_info / pull_request_linking scaffolding as external input", () => { + // Regression: Grok extra ACP prompt parts persist as user-role text and were + // mirrored as 💭 from **unknown@unknown** with the raw harness instructions. + const runtimeDump = `In case you're asked: you are running in T3 Code through the Grok harness, as grok-4.6. No need to mention this otherwise. You can embed images and videos in your response using Markdown with absolute file paths. + + +When the t3-code MCP server exposes link_pull_request, you must use it to register every pull request you create or work on for this thread. +`; + expect(isInternalAgentScaffoldingUserText(runtimeDump)).toBe(true); + expect(shouldSuppressExternalUserEcho(runtimeDump)).toBe(true); + expect(classifyUserMessageIngress(runtimeDump)).toBe("internal"); + expect( + shouldEchoUserMessageToDiscord({ + text: runtimeDump, + messageId: "user-runtime-1", + seenUserMessageIds: [], + sentDiscordUserMessageIds: [], + }), + ).toBe(false); + const messages = externalUserMessagesToEcho({ + messages: [ + { + id: MessageId.make("user-runtime-1"), + role: "user", + text: runtimeDump, + turnId: null, + streaming: false, + createdAt: "2026-07-18T00:00:00.000Z", + updatedAt: "2026-07-18T00:00:00.000Z", + }, + { + id: MessageId.make("user-real-1"), + role: "user", + text: "please also check PR 42", + turnId: null, + streaming: false, + createdAt: "2026-07-18T00:00:01.000Z", + updatedAt: "2026-07-18T00:00:01.000Z", + }, + ], + observedInitialUserSnapshot: true, + seenUserMessageIds: [], + sentDiscordUserMessageIds: [], + }); + expect(messages.map((message) => message.id)).toEqual(["user-real-1"]); + }); + + it("still echoes t3-client text when harness envelopes are mixed into a real message", () => { + const mixed = `please also check PR 42 +In case you're asked: you are running in T3 Code through the Grok harness, as grok-4.6.`; + expect(isInternalAgentScaffoldingUserText(mixed)).toBe(false); + expect(classifyUserMessageIngress(mixed)).toBe("t3-client"); + expect( + shouldEchoUserMessageToDiscord({ + text: mixed, + messageId: "user-mixed-1", + seenUserMessageIds: [], + sentDiscordUserMessageIds: [], + }), + ).toBe(true); + expect(summarizeExternalUserInput(mixed)).toBe("please also check PR 42"); + }); + it("whitelists github + t3-client only (never same-surface discord or internal)", () => { // Cross-surface policy: Discord echoes other surfaces, not its own ingress or harness. expect(DISCORD_EXTERNAL_ECHO_SURFACES).toEqual(new Set(["github", "t3-client"])); @@ -2002,6 +2065,18 @@ Repository: acme/widgets Background task "x" completed (exit code: 0). +suffix`), + ).toBe("prefix\n\nsuffix"); + }); + + it("strips runtime_info and pull_request_linking envelopes from echoed text", () => { + expect( + summarizeExternalUserInput(`prefix +In case you're asked: you are running in T3 Code through the Grok harness, as grok-4.6. + + +When the t3-code MCP server exposes link_pull_request, you must use it. + suffix`), ).toBe("prefix\n\nsuffix"); }); diff --git a/apps/discord-bot/src/features/ResponseBridge.ts b/apps/discord-bot/src/features/ResponseBridge.ts index cffd04f58994..1c7ddddc5ab3 100644 --- a/apps/discord-bot/src/features/ResponseBridge.ts +++ b/apps/discord-bot/src/features/ResponseBridge.ts @@ -1247,6 +1247,18 @@ export function isInternalAgentScaffoldingUserText(text: string): boolean { if (/^Background task\s+"/iu.test(body) && /completed\s*\(exit code:/iu.test(body)) { return true; } + // Extra ACP prompt parts (Grok/Cursor/Antigravity) can persist as user-role + // text. A message that is only those envelopes is not a human client. + // Mixed bodies still echo; summarizeExternalUserInput strips the tags. + if (/<\s*(?:runtime_info|pull_request_linking)\b/iu.test(body)) { + const remainder = body + .replace(/<\s*runtime_info\b[^>]*>[\s\S]*?<\/\s*runtime_info\s*>/giu, "") + .replace(/<\s*pull_request_linking\b[^>]*>[\s\S]*?<\/\s*pull_request_linking\s*>/giu, "") + .trim(); + if (remainder === "" || /<\s*(?:runtime_info|pull_request_linking)\b/iu.test(remainder)) { + return true; + } + } // Other harness / tool XML shells that sometimes land as user-role text. if (/<\s*system(?:-|\s)?(?:message|context|notification)\b/iu.test(body)) return true; if (/<\s*tool_(?:result|response|call)\b/iu.test(body)) return true; @@ -1305,11 +1317,13 @@ export function externalUserMessagesToEcho(input: { } export function summarizeExternalUserInput(text: string): string { - // Drop HTML comment blocks (GitHub PR context) and system-reminder envelopes so + // Drop HTML comment blocks (GitHub PR context) and harness envelopes so // anything that still slips through the echo filter is less noisy. return text .replace(/\s*/gu, "") .replace(/<\s*system-reminder\b[^>]*>[\s\S]*?<\/\s*system-reminder\s*>/giu, "") + .replace(/<\s*runtime_info\b[^>]*>[\s\S]*?<\/\s*runtime_info\s*>/giu, "") + .replace(/<\s*pull_request_linking\b[^>]*>[\s\S]*?<\/\s*pull_request_linking\s*>/giu, "") .replace(/\n{3,}/gu, "\n\n") .trim(); } diff --git a/apps/server/src/background/HostPowerMonitor.ts b/apps/server/src/background/HostPowerMonitor.ts index 3d8c8b35998a..a72e38e23a0a 100644 --- a/apps/server/src/background/HostPowerMonitor.ts +++ b/apps/server/src/background/HostPowerMonitor.ts @@ -68,7 +68,7 @@ export const make = Effect.fn("background.hostPower.make")(function* ( Effect.flatMap( Option.match({ onNone: () => Effect.void, - onSome: (next) => PubSub.publish(changes, next), + onSome: (next) => PubSub.publish(changes, next).pipe(Effect.asVoid), }), ), Effect.asVoid, diff --git a/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts b/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts index 40254bfe80aa..3951b9c695fe 100644 --- a/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts +++ b/apps/server/src/resourceTelemetry/NativeTelemetryClient.ts @@ -508,7 +508,7 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu Effect.flatMap( Option.match({ onNone: () => Effect.void, - onSome: (deferred) => Deferred.succeed(deferred, event.processes), + onSome: (deferred) => Deferred.succeed(deferred, event.processes).pipe(Effect.asVoid), }), ), Effect.asVoid,