fix(runner): classify ACPX admission failures
This commit is contained in:
parent
d49c011403
commit
9611da2b38
|
|
@ -7,6 +7,15 @@ export interface OpenedAcpxSidecarHost {
|
|||
const FAILED_ADMISSION_CLOSE_TIMEOUT_MS = 8_000;
|
||||
const ACTIVE_HOST_CLEANUP_ATTEMPTS = 4;
|
||||
|
||||
class AcpxSidecarStatusReadTimeoutError extends Error {
|
||||
readonly code = "ACPX_SIDECAR_STATUS_READ_TIMEOUT";
|
||||
|
||||
constructor() {
|
||||
super("ACPX session status read exceeded its timeout");
|
||||
this.name = "AcpxSidecarStatusReadTimeoutError";
|
||||
}
|
||||
}
|
||||
|
||||
export function hasSidecarSessionOwnership(
|
||||
host: unknown,
|
||||
activeHostCleanup: Promise<void> | null,
|
||||
|
|
@ -42,8 +51,7 @@ export async function readSidecarHostStatusWithin(
|
|||
host.status(),
|
||||
new Promise<never>((_resolve, reject) => {
|
||||
timer = setTimeout(
|
||||
() =>
|
||||
reject(new Error("ACPX session status read exceeded its timeout")),
|
||||
() => reject(new AcpxSidecarStatusReadTimeoutError()),
|
||||
timeoutMs,
|
||||
);
|
||||
timer.unref();
|
||||
|
|
@ -129,9 +137,7 @@ export function recoverAndCombineSidecarHostCleanup(
|
|||
prior: Promise<void> | null,
|
||||
): Promise<void> {
|
||||
const recovered = recoverSidecarHostCleanup(host, cleanup);
|
||||
return prior
|
||||
? combineSidecarHostCleanups([prior, recovered])
|
||||
: recovered;
|
||||
return prior ? combineSidecarHostCleanups([prior, recovered]) : recovered;
|
||||
}
|
||||
|
||||
export function reportAuthoritativeSidecarHostCleanupFailure(
|
||||
|
|
|
|||
|
|
@ -1043,8 +1043,9 @@ async function persistedRuntimeStatus(
|
|||
const recordId = handle.acpxRecordId ?? handle.sessionKey;
|
||||
const record = await sessionStore.load(recordId);
|
||||
if (!record) {
|
||||
throw new Error(
|
||||
"The pinned ACPX runtime omitted its persisted session record",
|
||||
throw Object.assign(
|
||||
new Error("The pinned ACPX runtime omitted its persisted session record"),
|
||||
{ code: "ACPX_PERSISTED_SESSION_MISSING" },
|
||||
);
|
||||
}
|
||||
if (
|
||||
|
|
@ -1052,8 +1053,9 @@ async function persistedRuntimeStatus(
|
|||
record.acpSessionId !== identity.backendSessionId ||
|
||||
record.agentSessionId !== identity.agentSessionId
|
||||
) {
|
||||
throw new Error(
|
||||
"The persisted ACPX session identity changed after admission",
|
||||
throw Object.assign(
|
||||
new Error("The persisted ACPX session identity changed after admission"),
|
||||
{ code: "ACPX_PERSISTED_SESSION_IDENTITY_MISMATCH" },
|
||||
);
|
||||
}
|
||||
const currentModelId = record.acpx?.current_model_id;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ export async function requireVerifiedAcpxModel(
|
|||
profile: QualifiedAcpxProfile,
|
||||
): Promise<AcpxModelStatus> {
|
||||
if (!control.getStatus) {
|
||||
throw new Error("ACPX agent cannot verify its effective model");
|
||||
throw acpxModelVerificationError(
|
||||
"ACPX_MODEL_STATUS_UNAVAILABLE",
|
||||
"ACPX agent cannot verify its effective model",
|
||||
);
|
||||
}
|
||||
const requestedModel = profile.qualificationModel;
|
||||
let status = await control.getStatus();
|
||||
|
|
@ -31,7 +34,8 @@ export async function requireVerifiedAcpxModel(
|
|||
status.models?.currentModelId !== requestedModel;
|
||||
if (mustSelectCanonical) {
|
||||
if (!control.setModel) {
|
||||
throw new Error(
|
||||
throw acpxModelVerificationError(
|
||||
"ACPX_MODEL_SELECTION_UNAVAILABLE",
|
||||
"ACPX agent cannot verify its canonical model through ACP config options",
|
||||
);
|
||||
}
|
||||
|
|
@ -39,13 +43,18 @@ export async function requireVerifiedAcpxModel(
|
|||
status = await control.getStatus();
|
||||
}
|
||||
if (status.models?.currentModelId !== profile.reportedModelId) {
|
||||
throw new Error(
|
||||
throw acpxModelVerificationError(
|
||||
"ACPX_EFFECTIVE_MODEL_MISMATCH",
|
||||
`ACPX effective model mismatch: requested ${requestedModel}, expected ACP selector ${profile.reportedModelId}, received ${status.models?.currentModelId ?? "unverified"}`,
|
||||
);
|
||||
}
|
||||
return normalizeQualifiedModelStatus(status, profile);
|
||||
}
|
||||
|
||||
function acpxModelVerificationError(code: string, message: string): Error {
|
||||
return Object.assign(new Error(message), { code });
|
||||
}
|
||||
|
||||
function normalizeQualifiedModelStatus(
|
||||
status: AcpxModelStatus,
|
||||
profile: QualifiedAcpxProfile,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ const RUNTIME_ADMISSION_VERIFICATION_TIMEOUT_MS = 8_000;
|
|||
const activeRuntimeHostCleanupOwners = new Set<Promise<unknown>>();
|
||||
|
||||
class AcpxRuntimeAdmissionTimeoutError extends Error {
|
||||
readonly code = "ACPX_RUNTIME_ADMISSION_VERIFICATION_TIMEOUT";
|
||||
|
||||
constructor() {
|
||||
super("ACPX runtime admission verification exceeded its deadline");
|
||||
this.name = "AcpxRuntimeAdmissionTimeoutError";
|
||||
|
|
@ -126,11 +128,7 @@ export type AcpxSemanticToolSession = Omit<RunnerToolBridgeOptions, "secret">;
|
|||
|
||||
export interface AcpxRetainedCleanupFailure {
|
||||
resource:
|
||||
| "credential"
|
||||
| "provider_lifetime"
|
||||
| "command"
|
||||
| "runtime"
|
||||
| "tool_bridge";
|
||||
"credential" | "provider_lifetime" | "command" | "runtime" | "tool_bridge";
|
||||
attempt: number;
|
||||
error: unknown;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue