test(live): accept yielded Codex bridge parent

This commit is contained in:
Vincent Koc
2026-07-06 09:50:55 -07:00
parent db334b3cf7
commit 8336a94ecb
3 changed files with 50 additions and 1 deletions
@@ -7,6 +7,7 @@ import {
EXPECTED_CODEX_STATUS_COMMAND_TEXT,
isExpectedCodexModelsCommandText,
isExpectedCodexStatusCommandText,
isExpectedYieldedAgentTimeout,
isRetryableCodexHarnessLiveError,
isStrictExpectedCodexModelsCommandText,
} from "./gateway-codex-harness.live-helpers.js";
@@ -41,6 +42,27 @@ describe("gateway codex harness live helpers", () => {
expect(isRetryableCodexHarnessLiveError(error)).toBe(false);
});
it("accepts only paused yielded agent timeouts for native subagent delivery", () => {
expect(
isExpectedYieldedAgentTimeout({
status: "timeout",
result: { meta: { livenessState: "paused", yielded: true } },
}),
).toBe(true);
expect(
isExpectedYieldedAgentTimeout({
status: "timeout",
result: { meta: { livenessState: "paused", yielded: false } },
}),
).toBe(false);
expect(
isExpectedYieldedAgentTimeout({
status: "ok",
result: { meta: { livenessState: "paused", yielded: true } },
}),
).toBe(false);
});
it("accepts the current codex status prose from the live harness", () => {
const text =
"OpenClaw is running on `openai/gpt-5.5` with low reasoning/text settings. Context is at `22k/272k` tokens, no compactions, and the current session is `agent:dev:live-codex-harness`.";
@@ -419,3 +419,24 @@ export function isRetryableCodexHarnessLiveError(error: unknown): boolean {
}
return error.message.includes("gateway request timeout for sessions.list");
}
/** Matches the terminal snapshot emitted when a native subagent parent yields for delivery. */
export function isExpectedYieldedAgentTimeout(payload: unknown): boolean {
if (!payload || typeof payload !== "object") {
return false;
}
const result = (payload as { result?: unknown; status?: unknown }).result;
if (!result || typeof result !== "object") {
return false;
}
const meta = (result as { meta?: unknown }).meta;
if (!meta || typeof meta !== "object") {
return false;
}
const record = meta as { livenessState?: unknown; yielded?: unknown };
return (
(payload as { status?: unknown }).status === "timeout" &&
record.yielded === true &&
record.livenessState === "paused"
);
}
@@ -27,6 +27,7 @@ import {
EXPECTED_CODEX_MODELS_COMMAND_TEXT,
EXPECTED_CODEX_STATUS_COMMAND_TEXT,
isExpectedCodexStatusCommandText,
isExpectedYieldedAgentTimeout,
isRetryableCodexHarnessLiveError,
isStrictExpectedCodexModelsCommandText,
} from "./gateway-codex-harness.live-helpers.js";
@@ -257,6 +258,7 @@ async function writeLiveGatewayConfig(params: {
}
async function requestAgentTextWithEvents(params: {
acceptYieldedTimeout?: boolean;
client: GatewayClient;
eventPrefix?: string;
includeAllSessions?: boolean;
@@ -293,7 +295,9 @@ async function requestAgentTextWithEvents(params: {
},
{ expectFinal: true, timeoutMs: CODEX_HARNESS_REQUEST_TIMEOUT_MS },
);
if (payload?.status !== "ok") {
const acceptedYieldedTimeout =
params.acceptYieldedTimeout === true && isExpectedYieldedAgentTimeout(payload);
if (payload?.status !== "ok" && !acceptedYieldedTimeout) {
throw new Error(`agent status=${String(payload?.status)} payload=${JSON.stringify(payload)}`);
}
return { text: extractPayloadText(payload.result), events };
@@ -980,6 +984,8 @@ async function verifyCodexNativeSubagentBridgeProbe(params: {
const parentToken = `CODEX-NATIVE-PARENT-${runId.slice(0, 6).toUpperCase()}`;
const { listTaskRecords } = await import("../tasks/runtime-internal.js");
const { text, events } = await requestAgentTextWithEvents({
// Native Codex waiting pauses this parent turn; task delivery resumes it separately.
acceptYieldedTimeout: true,
client: params.client,
eventPrefix: "codex_app_server.",
includeAllSessions: true,