Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions apps/desktop/src/preview/BrowserImport/ChromiumCookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ArrayBuffer>` vs `Buffer<ArrayBufferLike>`).
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(
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/updates/updatesTestHarness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Set<(...args: readonly unknown[]) => void>>();
const sentStates: DesktopUpdateState[] = [];
const installSteps: string[] = [];
Expand Down
75 changes: 75 additions & 0 deletions apps/discord-bot/src/features/ResponseBridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<runtime_info>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.</runtime_info>

<pull_request_linking>
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.
</pull_request_linking>`;
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
<runtime_info>In case you're asked: you are running in T3 Code through the Grok harness, as grok-4.6.</runtime_info>`;
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"]));
Expand Down Expand Up @@ -2002,6 +2065,18 @@ Repository: acme/widgets
<system-reminder>
Background task "x" completed (exit code: 0).
</system-reminder>
suffix`),
).toBe("prefix\n\nsuffix");
});

it("strips runtime_info and pull_request_linking envelopes from echoed text", () => {
expect(
summarizeExternalUserInput(`prefix
<runtime_info>In case you're asked: you are running in T3 Code through the Grok harness, as grok-4.6.</runtime_info>

<pull_request_linking>
When the t3-code MCP server exposes link_pull_request, you must use it.
</pull_request_linking>
suffix`),
).toBe("prefix\n\nsuffix");
});
Expand Down
16 changes: 15 additions & 1 deletion apps/discord-bot/src/features/ResponseBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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\S]*?-->\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();
}
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/background/HostPowerMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/resourceTelemetry/NativeTelemetryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading