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
207 changes: 193 additions & 14 deletions apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7006,6 +7006,37 @@ describe("CodexAdapterV2 post-settle continuation", () => {
notification: false,
expectedClass: "provider_error",
},
...([false, true] as const).map(
(notification) =>
({
name: `disconnected-${notification ? "notification" : "completion"}`,
code: "responseStreamDisconnected",
errorInfo: { responseStreamDisconnected: { httpStatusCode: null } },
notification,
expectedClass: "transport_error",
}) as const,
),
{
name: "connection-failed",
code: "responseStreamConnectionFailed",
errorInfo: { responseStreamConnectionFailed: { httpStatusCode: null } },
notification: false,
expectedClass: "transport_error",
},
{
name: "http-connection-failed",
code: "httpConnectionFailed",
errorInfo: { httpConnectionFailed: { httpStatusCode: null } },
notification: false,
expectedClass: "transport_error",
},
{
name: "http-provider-failure",
code: "httpConnectionFailed",
errorInfo: { httpConnectionFailed: { httpStatusCode: 503 } },
notification: false,
expectedClass: "provider_error",
},
{
name: "chatgpt-workspace-limit",
code: "other",
Expand Down Expand Up @@ -7040,6 +7071,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {
expectedCode: "subscription_sharing_usage_limit_exceeded",
expectedMessage: "Usage settings",
},
{
name: "terminal-disconnection",
code: "responseStreamDisconnected",
notification: false,
terminalMessage: "Connection lost.",
terminalErrorInfo: { responseStreamDisconnected: { httpStatusCode: null } },
expectedClass: "transport_error",
},
{
name: "sharing-code-mention-retry",
code: "responseStreamDisconnected",
Expand All @@ -7049,6 +7088,71 @@ describe("CodexAdapterV2 post-settle continuation", () => {
willRetry: true,
expectedClass: "transport_error",
},
...(
[
{
name: "generic-disconnection",
status: null,
terminalMessage: "Connection lost.",
expectedClass: "transport_error",
},
{
name: "generic-replacement",
status: null,
terminalMessage: "A different failure.",
expectedClass: "provider_error",
},
{
name: "generic-http-status",
status: 503,
terminalMessage: "Connection lost.",
expectedClass: "provider_error",
},
{
name: "generic-truncated-replacement",
status: null,
details: `${"Connection lost. ".repeat(300)}Network cause.`,
terminalMessage: `${"Connection lost. ".repeat(300)}Different cause.`,
expectedClass: "provider_error",
},
{
name: "generic-terminal-status",
status: null,
terminalMessage: "Connection lost.",
terminalErrorInfo: { responseStreamDisconnected: { httpStatusCode: 503 } },
expectedClass: "provider_error",
},
{
name: "generic-exhaustion",
status: null,
terminalMessage: "Connection lost.",
terminalErrorInfo: { responseTooManyFailedAttempts: { httpStatusCode: null } },
completionErrorInfo: { responseTooManyFailedAttempts: { httpStatusCode: null } },
expectedClass: "transport_error",
},
{
name: "generic-blank-details",
status: null,
details: " ",
terminalMessage: "Provider stopped this request.",
expectedClass: "transport_error",
},
{
name: "generic-exhaustion-status",
status: null,
terminalMessage: "Connection lost.",
terminalErrorInfo: { responseTooManyFailedAttempts: { httpStatusCode: 503 } },
completionErrorInfo: { responseTooManyFailedAttempts: { httpStatusCode: null } },
expectedClass: "provider_error",
},
] as const
).map((scenario) => ({
...scenario,
code: "responseStreamDisconnected" as const,
errorInfo: { responseStreamDisconnected: { httpStatusCode: scenario.status } },
notification: true,
details: "details" in scenario ? scenario.details : "Connection lost.",
})),
] as const)("classifies Codex terminal failures from $name evidence", (scenario) =>
Effect.gen(function* () {
const nativeThreadId = `native-limit-${scenario.name}`;
Expand Down Expand Up @@ -7172,7 +7276,10 @@ describe("CodexAdapterV2 post-settle continuation", () => {
params: {
threadId: nativeThreadId,
turnId: nativeTurnId,
willRetry: scenario.name === "retry" || "willRetry" in scenario,
willRetry:
scenario.name === "retry" ||
"willRetry" in scenario ||
"terminalMessage" in scenario,
error: {
message,
codexErrorInfo: errorInfo,
Expand All @@ -7188,6 +7295,27 @@ describe("CodexAdapterV2 post-settle continuation", () => {
},
]
: []),
...("terminalMessage" in scenario
? [
{
type: "emit_inbound" as const,
label: "error/terminal",
frame: {
method: "error",
params: {
threadId: nativeThreadId,
turnId: nativeTurnId,
willRetry: false,
error: {
message: scenario.terminalMessage,
codexErrorInfo:
"terminalErrorInfo" in scenario ? scenario.terminalErrorInfo : "other",
},
},
},
},
]
: []),
{
type: "emit_inbound",
label: "turn/completed",
Expand All @@ -7198,12 +7326,24 @@ describe("CodexAdapterV2 post-settle continuation", () => {
turn: {
...makeCodexReplayTurn({ id: nativeTurnId, status: "failed" }),
error: {
message: scenario.name === "replacement" ? "A different failure." : message,
...(scenario.notification &&
scenario.name !== "matching-details" &&
!("details" in scenario)
? {}
: { codexErrorInfo: errorInfo }),
message:
"terminalMessage" in scenario
? scenario.terminalMessage
: scenario.name === "replacement"
? "A different failure."
: message,
...("terminalMessage" in scenario
? {
codexErrorInfo:
"completionErrorInfo" in scenario
? scenario.completionErrorInfo
: "other",
}
: scenario.notification &&
scenario.name !== "matching-details" &&
!("details" in scenario)
? {}
: { codexErrorInfo: errorInfo }),
},
},
},
Expand Down Expand Up @@ -7361,34 +7501,66 @@ describe("CodexAdapterV2 post-settle continuation", () => {
// regression: #17501 Codex 429 retry exhaustion classifies as a usage limit
it.effect.each([
{
code: "responseTooManyFailedAttempts",
notificationStatus: 500,
completionStatus: 429,
willRetry: false,
expectedClass: "usage_limit",
},
{
code: "responseTooManyFailedAttempts",
notificationStatus: 429,
completionStatus: 500,
willRetry: true,
expectedClass: "provider_error",
},
{
code: "responseTooManyFailedAttempts",
notificationStatus: 429,
completionStatus: null,
willRetry: false,
expectedClass: "usage_limit",
},
{
code: "responseTooManyFailedAttempts",
notificationStatus: 429,
completionStatus: null,
willRetry: true,
expectedClass: "usage_limit",
},
{
code: "responseStreamDisconnected",
notificationStatus: null,
completionStatus: 503,
willRetry: false,
expectedClass: "provider_error",
},
{
code: "responseStreamDisconnected",
notificationStatus: 503,
completionStatus: null,
willRetry: false,
expectedClass: "transport_error",
},
{
code: "responseStreamDisconnected",
notificationStatus: null,
completionStatus: 503,
willRetry: true,
expectedClass: "provider_error",
},
{
code: "responseStreamDisconnected",
notificationStatus: null,
completionStatus: null,
willRetry: false,
expectedClass: "transport_error",
},
] as const)(
"honors completion HTTP status from $notificationStatus to $completionStatus (retry: $willRetry)",
({ notificationStatus, completionStatus, expectedClass, willRetry }) =>
"honors $code completion HTTP status from $notificationStatus to $completionStatus (retry: $willRetry)",
({ code, notificationStatus, completionStatus, expectedClass, willRetry }) =>
Effect.gen(function* () {
const name = `${notificationStatus}-${completionStatus}-${willRetry}`;
const name = `${code}-${notificationStatus}-${completionStatus}-${willRetry}`;
const nativeThreadId = `native-exhaustion-status-${name}`;
const nativeTurnId = `turn-exhaustion-status-${name}`;
const message = "Provider stopped this request.";
Expand All @@ -7409,7 +7581,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
error: {
message,
codexErrorInfo: {
responseTooManyFailedAttempts: { httpStatusCode: notificationStatus },
[code]: { httpStatusCode: notificationStatus },
},
additionalDetails,
},
Expand All @@ -7428,7 +7600,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
error: {
message,
codexErrorInfo: {
responseTooManyFailedAttempts: { httpStatusCode: completionStatus },
[code]: { httpStatusCode: completionStatus },
},
},
},
Expand All @@ -7452,9 +7624,16 @@ describe("CodexAdapterV2 post-settle continuation", () => {
assert.equal(terminal?.status, "failed");
if (terminal?.status !== "failed") return;
assert.equal(terminal.failure.class, expectedClass);
assert.equal(terminal.failure.code, "responseTooManyFailedAttempts");
assert.equal(terminal.failure.code, code);
assert.equal(terminal.threadDisposition, "reusable");
const notificationClass = notificationStatus === 429 ? "usage_limit" : "provider_error";
const notificationClass =
code === "responseTooManyFailedAttempts"
? notificationStatus === 429
? "usage_limit"
: "provider_error"
: notificationStatus === null
? "transport_error"
: "provider_error";
assert.equal(
terminal.failure.message,
expectedClass === notificationClass ? additionalDetails : message,
Expand Down
Loading
Loading