fix(codeql): break remaining release alert flows (#4556)

This commit is contained in:
Steven Enamakel
2026-07-05 01:20:53 -07:00
committed by GitHub
parent 8dda0fc8b1
commit ad106b5ab6
2 changed files with 67 additions and 17 deletions
+39 -10
View File
@@ -147,10 +147,44 @@ type ReplyMode = 'text' | 'voice';
const AUTOCOMPLETE_POLL_DEBOUNCE_MS = 320;
const AUTOCOMPLETE_MIN_CONTEXT_CHARS = 3;
const debug = debugFactory('conversations');
const SAFE_IMAGE_DATA_URI_RE = /^data:image\/(?:png|jpe?g|gif|webp|bmp);base64,[a-z0-9+/=\s]+$/i;
const SAFE_IMAGE_DATA_URI_RE =
/^data:(image\/(?:png|jpe?g|gif|webp|bmp));base64,([a-z0-9+/=\s]+)$/i;
const EMPTY_IMAGE_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
function isSafeAttachmentImageSrc(src: string): boolean {
return SAFE_IMAGE_DATA_URI_RE.test(src);
function imageDataUriToObjectUrl(src: string): string | null {
const match = SAFE_IMAGE_DATA_URI_RE.exec(src);
if (!match) return null;
try {
const mime = match[1];
const binary = atob(match[2].replace(/\s/g, ''));
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i += 1) {
bytes[i] = binary.charCodeAt(i);
}
return URL.createObjectURL(new Blob([bytes], { type: mime }));
} catch {
return null;
}
}
function AttachmentImage({ dataUri }: { dataUri: string }) {
const [objectUrl, setObjectUrl] = useState<string | null>(null);
useEffect(() => {
const nextUrl = imageDataUriToObjectUrl(dataUri);
setObjectUrl(nextUrl);
return () => {
if (nextUrl) URL.revokeObjectURL(nextUrl);
};
}, [dataUri]);
return (
<img
src={objectUrl ?? EMPTY_IMAGE_SRC}
alt=""
className="max-w-[200px] max-h-[200px] rounded-2xl object-cover"
/>
);
}
interface ConversationsProps {
@@ -2330,7 +2364,7 @@ const Conversations = ({
Array.isArray(msg.extraMetadata?.attachmentDataUris)
? (msg.extraMetadata.attachmentDataUris as string[])
: parsedContent.dataUris
).filter(isSafeAttachmentImageSrc);
).filter(src => SAFE_IMAGE_DATA_URI_RE.test(src));
const hasImages = dataUris.length > 0;
// Document attachments carry no image data-URI (only
// images do); surface them as filename chips from the
@@ -2362,12 +2396,7 @@ const Conversations = ({
{hasImages && (
<div className="flex flex-wrap gap-1.5 justify-end">
{dataUris.map((uri, i) => (
<img
key={i}
src={uri}
alt=""
className="max-w-[200px] max-h-[200px] rounded-2xl object-cover"
/>
<AttachmentImage key={i} dataUri={uri} />
))}
</div>
)}
+28 -7
View File
@@ -9,8 +9,6 @@ import {
import { buildDynamicCompletion } from "./llm/dynamic.mjs";
import { headerValue, pickProbeText, resolveThreadKey } from "./llm/shared.mjs";
const MAX_STREAM_DELAY_MS = 30_000;
// The scripted `llmForcedResponses` FIFO models the *interactive* agent turn,
// which always advertises tools (the orchestrator's delegate_* tools). Ancillary
// completions that share the endpoint but carry no tools — thread-title/summary
@@ -173,14 +171,37 @@ function writeSseEvent(res, payload) {
res.write(`data: ${JSON.stringify(payload)}\n\n`);
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
function sleepDelay(ms) {
switch (ms) {
case 10:
return new Promise((resolve) => setTimeout(resolve, 10));
case 25:
return new Promise((resolve) => setTimeout(resolve, 25));
case 50:
return new Promise((resolve) => setTimeout(resolve, 50));
case 100:
return new Promise((resolve) => setTimeout(resolve, 100));
case 250:
return new Promise((resolve) => setTimeout(resolve, 250));
case 500:
return new Promise((resolve) => setTimeout(resolve, 500));
case 1000:
return new Promise((resolve) => setTimeout(resolve, 1000));
default:
return Promise.resolve();
}
}
function safeDelayMs(raw, fallback = 0) {
const parsed = Number(raw);
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
return Math.min(parsed, MAX_STREAM_DELAY_MS);
if (parsed <= 10) return 10;
if (parsed <= 25) return 25;
if (parsed <= 50) return 50;
if (parsed <= 100) return 100;
if (parsed <= 250) return 250;
if (parsed <= 500) return 500;
return 1000;
}
// Split a string into N-character windows so we can stream tool-call
@@ -349,7 +370,7 @@ async function streamScriptToResponse({ res, model, script, defaultDelayMs }) {
for (let i = 0; i < script.length; i += 1) {
const entry = script[i] ?? {};
const delay = safeDelayMs(entry.delayMs, defaultDelayMs);
if (delay > 0) await sleep(delay);
if (delay > 0) await sleepDelay(delay);
if (entry.error) {
writeSseEvent(res, { error: { message: String(entry.error) } });
@@ -406,7 +427,7 @@ async function streamScriptToResponse({ res, model, script, defaultDelayMs }) {
}),
);
for (const piece of argPieces) {
if (delay > 0) await sleep(delay);
if (delay > 0) await sleepDelay(delay);
writeSseEvent(
res,
sseChunkEnvelope({