fix(runner): retain bounded ACPX startup diagnosis
This commit is contained in:
parent
e9998cbfdb
commit
d60306420d
|
|
@ -618,6 +618,7 @@ fn response_error_classification(error: &ResponseError) -> &'static str {
|
|||
"SESSION_MODEL_REPLAY_FAILED" => return "session_model_replay_failed",
|
||||
"SESSION_CONFIG_OPTION_REPLAY_FAILED" => return "session_config_option_replay_failed",
|
||||
"CLAUDE_ACP_SESSION_CREATE_TIMEOUT" => return "claude_session_create_timeout",
|
||||
"ACPX_SESSION_HANDSHAKE_TIMEOUT" => return "session_handshake_timeout",
|
||||
_ => {}
|
||||
}
|
||||
match error.message.as_str() {
|
||||
|
|
@ -706,6 +707,13 @@ mod tests {
|
|||
)),
|
||||
"session_handshake_timeout"
|
||||
);
|
||||
assert_eq!(
|
||||
response_error_classification(&error(
|
||||
"ACPX_SESSION_HANDSHAKE_TIMEOUT",
|
||||
"bounded provider admission failed",
|
||||
)),
|
||||
"session_handshake_timeout"
|
||||
);
|
||||
assert_eq!(
|
||||
response_error_classification(&error("ACP_MODEL_UNSUPPORTED", "violet-circuit-4821",)),
|
||||
"requested_model_unsupported"
|
||||
|
|
|
|||
|
|
@ -130,6 +130,14 @@ describe("ACPX sidecar input sequencing", () => {
|
|||
code: "ACP_MODEL_UNSUPPORTED",
|
||||
detailCode: "AGENT_STARTUP_FAILED",
|
||||
});
|
||||
const nestedHandshake = new AggregateError(
|
||||
[
|
||||
Object.assign(new Error("admission deadline"), {
|
||||
name: "AcpxSessionHandshakeTimeoutError",
|
||||
}),
|
||||
],
|
||||
"runtime initialization cleanup failed",
|
||||
);
|
||||
|
||||
expect(acpxSidecarErrorCode(missingModule)).toBe(
|
||||
"AGENT_STARTUP_FAILED.MODULE_NOT_FOUND",
|
||||
|
|
@ -141,5 +149,8 @@ describe("ACPX sidecar input sequencing", () => {
|
|||
"violet-circuit-4821",
|
||||
);
|
||||
expect(acpxSidecarErrorCode(model)).toBe("ACP_MODEL_UNSUPPORTED");
|
||||
expect(acpxSidecarErrorCode(nestedHandshake)).toBe(
|
||||
"ACPX_SESSION_HANDSHAKE_TIMEOUT",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,13 +35,40 @@ export function acpxBootstrapBlockedError(
|
|||
* contributes a closed category and is never copied into the code itself.
|
||||
*/
|
||||
export function acpxSidecarErrorCode(error: Error): string {
|
||||
const pending: Error[] = [error];
|
||||
const observed = new Set<Error>();
|
||||
while (pending.length > 0 && observed.size < 16) {
|
||||
const current = pending.shift()!;
|
||||
if (observed.has(current)) continue;
|
||||
observed.add(current);
|
||||
const code = directAcpxSidecarErrorCode(current);
|
||||
if (code !== null) return code;
|
||||
|
||||
const details = current as Error & Record<string, unknown>;
|
||||
if (current instanceof AggregateError) {
|
||||
for (const nested of current.errors) {
|
||||
if (nested instanceof Error) pending.push(nested);
|
||||
}
|
||||
}
|
||||
if (details.cause instanceof Error) pending.push(details.cause);
|
||||
}
|
||||
return "acpx_sidecar_command_failed";
|
||||
}
|
||||
|
||||
function directAcpxSidecarErrorCode(error: Error): string | null {
|
||||
const details = error as Error & Record<string, unknown>;
|
||||
const code =
|
||||
typeof details.code === "string"
|
||||
? details.code
|
||||
: typeof details.detailCode === "string"
|
||||
? details.detailCode
|
||||
: "acpx_sidecar_command_failed";
|
||||
: null;
|
||||
if (code === null) {
|
||||
return error.name === "AcpxSessionHandshakeTimeoutError" ||
|
||||
error.message === "ACPX session handshake exceeded its admission deadline"
|
||||
? "ACPX_SESSION_HANDSHAKE_TIMEOUT"
|
||||
: null;
|
||||
}
|
||||
if (code !== "AGENT_STARTUP_FAILED") return code;
|
||||
|
||||
const stderr =
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ export const DEFAULT_CODEX_ACPX_RUNTIME_SHUTDOWN_BOUND_MS =
|
|||
// only after the exact attempt reaches a terminal outcome.
|
||||
const activeRuntimeCleanupOwners = new Set<Promise<unknown>>();
|
||||
const activeCodexRuntimeCleanupOwners = new Set<Promise<unknown>>();
|
||||
const SESSION_HANDSHAKE_TIMEOUT_MS = 8_000;
|
||||
// Provider initialization may include a cold native app-server start on a
|
||||
// minimally provisioned runner. Keep admission finite while allowing the
|
||||
// qualified runtime enough time to complete that local handshake.
|
||||
const SESSION_HANDSHAKE_TIMEOUT_MS = 30_000;
|
||||
|
||||
class AcpxRuntimeCloseTimeoutError extends Error {
|
||||
constructor() {
|
||||
|
|
@ -67,6 +70,8 @@ class AcpxRuntimeCloseFinalTimeoutError extends Error {
|
|||
}
|
||||
|
||||
class AcpxSessionHandshakeTimeoutError extends Error {
|
||||
readonly code = "ACPX_SESSION_HANDSHAKE_TIMEOUT";
|
||||
|
||||
constructor() {
|
||||
super("ACPX session handshake exceeded its admission deadline");
|
||||
this.name = "AcpxSessionHandshakeTimeoutError";
|
||||
|
|
@ -828,7 +833,7 @@ function runtimePort(
|
|||
}
|
||||
if (
|
||||
lateReconciliationAttempts >=
|
||||
MAX_LATE_RUNTIME_CLEANUP_RECONCILIATION_ATTEMPTS
|
||||
MAX_LATE_RUNTIME_CLEANUP_RECONCILIATION_ATTEMPTS
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1127,9 +1132,7 @@ function delay(timeoutMs: number): Promise<void> {
|
|||
return new Promise((resolve) => setTimeout(resolve, timeoutMs));
|
||||
}
|
||||
|
||||
type ProviderExitOutcome =
|
||||
| { exited: true }
|
||||
| { exited: false; error: unknown };
|
||||
type ProviderExitOutcome = { exited: true } | { exited: false; error: unknown };
|
||||
|
||||
class ProviderExitObservation {
|
||||
#outcome: ProviderExitOutcome | null = null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue