Skip to content
Open
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
43 changes: 33 additions & 10 deletions sdk/typescript/src/worker-progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,8 @@ export function scanProgressUpdatesFromEvent(
: null;
if (typeof output !== "string") return [];
const updates: ScanProgress[] = [];
let codeFence = false;
for (const line of output.split(/\r?\n/u)) {
if (/^\s*```/u.test(line)) {
codeFence = !codeFence;
continue;
}
if (codeFence || !line.startsWith(SCAN_PROGRESS_PREFIX)) continue;
for (const line of linesOutsideCodeFences(output)) {
if (!line.startsWith(SCAN_PROGRESS_PREFIX)) continue;
const progress = scanProgressFromMarker(line);
if (progress !== null) updates.push(progress);
}
Expand Down Expand Up @@ -165,9 +160,9 @@ function dispatchStatus(
item: Readonly<Record<string, unknown>>,
): ScanWorkerStatus | null {
if (typeof item["text"] !== "string") return null;
const markers = item["text"]
.split(/\r?\n/u)
.filter((line) => line.startsWith(WORKER_STATUS_PREFIX));
const markers = linesOutsideCodeFences(item["text"]).filter((line) =>
line.startsWith(WORKER_STATUS_PREFIX),
);
const marker = markers[0];
if (markers.length !== 1 || marker === undefined) return null;
let payload: unknown;
Expand All @@ -194,6 +189,34 @@ function dispatchStatus(
};
}

function linesOutsideCodeFences(value: string): string[] {
const lines: string[] = [];
let fence: { marker: "`" | "~"; length: number } | null = null;
for (const line of value.split(/\r?\n/u)) {
if (fence !== null) {
const closing = /^[ \t]{0,3}(`{3,}|~{3,})[ \t]*$/u.exec(line)?.[1];
if (
closing !== undefined &&
closing[0] === fence.marker &&
closing.length >= fence.length
) {
fence = null;
}
continue;
}
const opening = /^[ \t]{0,3}(`{3,}|~{3,})/u.exec(line)?.[1];
if (opening !== undefined) {
fence = {
marker: opening[0] as "`" | "~",
length: opening.length,
};
continue;
}
lines.push(line);
}
return lines;
}

function isProgressCount(value: unknown): value is number {
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
}
Expand Down
58 changes: 58 additions & 0 deletions sdk/typescript/tests-ts/worker-progress-fences.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { describe, expect, test } from "bun:test";
import {
scanProgressUpdatesFromEvent,
workerStatusFromEvent,
} from "../src/worker-progress.js";

function messageEvent(text: string): Record<string, unknown> {
return {
type: "item.completed",
item: { id: "message-1", type: "agent_message", text },
};
}

describe("worker progress Markdown fences", () => {
test("does not close a longer outer fence with a shorter backtick run", () => {
const text = [
"````markdown",
"```text",
'CODEX_SECURITY_SCAN_PROGRESS {"phase":"discovery","filesCompleted":8,"filesTotal":8}',
"```",
"````",
].join("\n");

expect(scanProgressUpdatesFromEvent(messageEvent(text))).toEqual([]);
});

test("ignores worker-status markers inside fenced examples", () => {
const text = [
"Example:",
"```text",
'CODEX_SECURITY_WORKER_STATUS {"phase":"file_review","planned":6,"started":6}',
"```",
].join("\n");

expect(workerStatusFromEvent(messageEvent(text))).toBeNull();
});

test("supports tilde fences and still reads markers after a fence closes", () => {
const text = [
"~~~text",
'CODEX_SECURITY_SCAN_PROGRESS {"phase":"discovery","filesCompleted":7,"filesTotal":8}',
'CODEX_SECURITY_WORKER_STATUS {"phase":"file_review","planned":6,"started":5}',
"~~~",
'CODEX_SECURITY_SCAN_PROGRESS {"phase":"validation","filesCompleted":8,"filesTotal":8}',
'CODEX_SECURITY_WORKER_STATUS {"phase":"validation","planned":2,"started":2}',
].join("\n");

expect(scanProgressUpdatesFromEvent(messageEvent(text))).toEqual([
{ phase: "validation", filesCompleted: 8, filesTotal: 8 },
]);
expect(workerStatusFromEvent(messageEvent(text))).toEqual({
kind: "dispatch",
phase: "validation",
planned: 2,
started: 2,
});
});
});
Loading