Skip to content

Commit 7f6a93e

Browse files
committed
refactor(agents): share embedded runner error normalization
1 parent aa79ab1 commit 7f6a93e

8 files changed

Lines changed: 24 additions & 136 deletions

File tree

src/agents/embedded-agent-runner/run.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
} from "../../infra/agent-events.js";
3131
import { sleepWithAbort } from "../../infra/backoff.js";
3232
import { freezeDiagnosticTraceContext } from "../../infra/diagnostic-trace-context.js";
33-
import { formatErrorMessage } from "../../infra/errors.js";
33+
import { formatErrorMessage, toErrorObject } from "../../infra/errors.js";
3434
import { buildAgentHookContextChannelFields } from "../../plugins/hook-agent-context.js";
3535
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
3636
import { resolveProviderAuthProfileId } from "../../plugins/provider-runtime.js";
@@ -2965,7 +2965,7 @@ async function runEmbeddedAgentInternal(
29652965
!hasRecoverableCodexAppServerTimeoutOutcome &&
29662966
!shouldSurfaceCodexCompletionTimeout
29672967
) {
2968-
throw toLintErrorObject(promptError, "Prompt failed");
2968+
throw toErrorObject(promptError, "Prompt failed");
29692969
}
29702970
}
29712971

@@ -3235,7 +3235,7 @@ async function runEmbeddedAgentInternal(
32353235
});
32363236
logPromptFailoverDecision("surface_error");
32373237
}
3238-
throw toLintErrorObject(promptError, "Prompt failed");
3238+
throw toErrorObject(promptError, "Prompt failed");
32393239
}
32403240

32413241
const assistantForFailover = currentAttemptAssistant ?? sessionAssistantForCandidate;
@@ -4186,17 +4186,3 @@ function resolveAuthProfileStateProvider(
41864186
const idProvider = profileId.split(":", 1)[0]?.trim();
41874187
return idProvider || fallbackProvider;
41884188
}
4189-
4190-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
4191-
if (value instanceof Error) {
4192-
return value;
4193-
}
4194-
if (typeof value === "string") {
4195-
return new Error(value);
4196-
}
4197-
const error = new Error(fallbackMessage, { cause: value });
4198-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
4199-
Object.assign(error, value);
4200-
}
4201-
return error;
4202-
}

src/agents/embedded-agent-runner/run/abortable.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/**
22
* AbortSignal-aware promise racing helper for embedded-agent attempts.
33
*/
4+
import { toErrorObject } from "../../../infra/errors.js";
5+
46
function getAbortReason(signal: AbortSignal): unknown {
57
return "reason" in signal ? (signal as { reason?: unknown }).reason : undefined;
68
}
@@ -39,23 +41,8 @@ export function abortable<T>(signal: AbortSignal, promise: Promise<T>): Promise<
3941
},
4042
(err: unknown) => {
4143
signal.removeEventListener("abort", onAbort);
42-
reject(toLintErrorObject(err, "Non-Error rejection"));
44+
reject(toErrorObject(err, "Non-Error rejection"));
4345
},
4446
);
4547
});
4648
}
47-
48-
/** Converts non-Error promise rejections into Error instances without dropping object fields. */
49-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
50-
if (value instanceof Error) {
51-
return value;
52-
}
53-
if (typeof value === "string") {
54-
return new Error(value);
55-
}
56-
const error = new Error(fallbackMessage, { cause: value });
57-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
58-
Object.assign(error, value);
59-
}
60-
return error;
61-
}

src/agents/embedded-agent-runner/run/attempt.async-tasks.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Waits for completion-required async tasks before finalizing an attempt.
33
*/
4+
import { toErrorObject } from "../../../infra/errors.js";
45
import { isCronRunSessionKey } from "../../../sessions/session-key-utils.js";
56
import { isTerminalTaskStatus } from "../../../tasks/task-executor-policy.js";
67
import type { TaskRecord } from "../../../tasks/task-registry.types.js";
@@ -77,7 +78,7 @@ async function sleepWithAbort(
7778
},
7879
(err: unknown) => {
7980
signal.removeEventListener("abort", onAbort);
80-
reject(toLintErrorObject(err, "Non-Error rejection"));
81+
reject(toErrorObject(err, "Non-Error rejection"));
8182
},
8283
);
8384
});
@@ -245,17 +246,3 @@ export async function waitForCompletionRequiredAsyncTasks(params: {
245246
}
246247
}
247248
}
248-
249-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
250-
if (value instanceof Error) {
251-
return value;
252-
}
253-
if (typeof value === "string") {
254-
return new Error(value);
255-
}
256-
const error = new Error(fallbackMessage, { cause: value });
257-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
258-
Object.assign(error, value);
259-
}
260-
return error;
261-
}

src/agents/embedded-agent-runner/run/attempt.queue-message.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Steers active embedded sessions and waits for transcript commits when needed.
33
*/
4+
import { toErrorObject } from "../../../infra/errors.js";
45
import { log } from "../logger.js";
56

67
/**
@@ -142,7 +143,7 @@ export async function steerAndWaitForTranscriptCommit(
142143
}
143144
unsubscribe?.();
144145
if (err) {
145-
reject(toLintErrorObject(err, "Non-Error rejection"));
146+
reject(toErrorObject(err, "Non-Error rejection"));
146147
return;
147148
}
148149
resolve();
@@ -230,17 +231,3 @@ export async function steerActiveSessionWithOptionalDeliveryWait(
230231
options.deliveryTimeoutMs ?? DEFAULT_QUEUE_TRANSCRIPT_COMMIT_TIMEOUT_MS,
231232
);
232233
}
233-
234-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
235-
if (value instanceof Error) {
236-
return value;
237-
}
238-
if (typeof value === "string") {
239-
return new Error(value);
240-
}
241-
const error = new Error(fallbackMessage, { cause: value });
242-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
243-
Object.assign(error, value);
244-
}
245-
return error;
246-
}

src/agents/embedded-agent-runner/run/attempt.session-lock.ts

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type OwnedSessionTranscriptCacheSnapshot,
1515
withOwnedSessionTranscriptWrites,
1616
} from "../../../config/sessions/transcript-write-context.js";
17+
import { toErrorObject } from "../../../infra/errors.js";
1718
import { resolveGlobalSingleton } from "../../../shared/global-singleton.js";
1819
import { isTranscriptOnlyOpenClawAssistantMessage } from "../../../shared/transcript-only-openclaw-assistant.js";
1920
import { isSessionWriteLockAcquireError } from "../../session-write-lock-error.js";
@@ -891,7 +892,7 @@ function waitForSessionFileOwnerRelease(params: {
891892
}): Promise<void> {
892893
if (params.signal?.aborted) {
893894
return Promise.reject(
894-
toLintErrorObject(abortOwnerWaitReason(params.signal), "Non-Error rejection"),
895+
toErrorObject(abortOwnerWaitReason(params.signal), "Non-Error rejection"),
895896
);
896897
}
897898
return new Promise<void>((resolve, reject) => {
@@ -915,21 +916,15 @@ function waitForSessionFileOwnerRelease(params: {
915916
};
916917
waiter.reject = (error) => {
917918
cleanup();
918-
reject(toLintErrorObject(error, "Non-Error rejection"));
919+
reject(toErrorObject(error, "Non-Error rejection"));
919920
};
920921
const timeoutMs = resolveSessionFileOwnerWaitTimeoutMs(params.timeoutMs);
921922
if (timeoutMs !== undefined) {
922-
waiter.timer = setTimeout(
923-
() => {
924-
waiter.reject(
925-
new EmbeddedAttemptSessionFileOwnerTimeoutError(
926-
params.sessionFile,
927-
timeoutMs,
928-
),
929-
);
930-
},
931-
timeoutMs,
932-
);
923+
waiter.timer = setTimeout(() => {
924+
waiter.reject(
925+
new EmbeddedAttemptSessionFileOwnerTimeoutError(params.sessionFile, timeoutMs),
926+
);
927+
}, timeoutMs);
933928
waiter.timer.unref?.();
934929
}
935930
if (params.signal) {
@@ -2084,17 +2079,3 @@ export function installPromptSubmissionLockRelease(params: {
20842079
wrappedStreamFn["__openclawSessionLockPromptReleaseInstalled"] = true;
20852080
agent.streamFn = wrappedStreamFn;
20862081
}
2087-
2088-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
2089-
if (value instanceof Error) {
2090-
return value;
2091-
}
2092-
if (typeof value === "string") {
2093-
return new Error(value);
2094-
}
2095-
const error = new Error(fallbackMessage, { cause: value });
2096-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
2097-
Object.assign(error, value);
2098-
}
2099-
return error;
2100-
}

src/agents/embedded-agent-runner/run/attempt.subscription-cleanup.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Builds subscription params and cleans up embedded attempt resources.
33
*/
4+
import { toErrorObject } from "../../../infra/errors.js";
45
import type { SubscribeEmbeddedAgentSessionParams } from "../../embedded-agent-subscribe.types.js";
56
import { log } from "../logger.js";
67
import { resolveEmbeddedAbortSettleTimeoutMs } from "./attempt.abort-settle-timeout.js";
@@ -142,20 +143,6 @@ export async function cleanupEmbeddedAttemptResources(params: {
142143
}
143144

144145
if (sessionLockReleaseError) {
145-
throw toLintErrorObject(sessionLockReleaseError, "Non-Error thrown");
146+
throw toErrorObject(sessionLockReleaseError, "Non-Error thrown");
146147
}
147148
}
148-
149-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
150-
if (value instanceof Error) {
151-
return value;
152-
}
153-
if (typeof value === "string") {
154-
return new Error(value);
155-
}
156-
const error = new Error(fallbackMessage, { cause: value });
157-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
158-
Object.assign(error, value);
159-
}
160-
return error;
161-
}

src/agents/embedded-agent-runner/run/attempt.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
freezeDiagnosticTraceContext,
4343
} from "../../../infra/diagnostic-trace-context.js";
4444
import { isEmbeddedMode } from "../../../infra/embedded-mode.js";
45-
import { formatErrorMessage } from "../../../infra/errors.js";
45+
import { formatErrorMessage, toErrorObject } from "../../../infra/errors.js";
4646
import { resolveHeartbeatSummaryForAgent } from "../../../infra/heartbeat-summary.js";
4747
import { getMachineDisplayName } from "../../../infra/machine-name.js";
4848
import { createCodexNativeWebSearchWrapper } from "../../../llm/providers/stream-wrappers/openai.js";
@@ -5737,7 +5737,7 @@ export async function runEmbeddedAttempt(
57375737
}),
57385738
);
57395739
} else {
5740-
await Promise.reject(toLintErrorObject(cleanupFailure, "Non-Error rejection"));
5740+
await Promise.reject(toErrorObject(cleanupFailure, "Non-Error rejection"));
57415741
}
57425742
}
57435743
}
@@ -5767,17 +5767,3 @@ export async function runEmbeddedAttempt(
57675767
restoreSkillEnv?.();
57685768
}
57695769
}
5770-
5771-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
5772-
if (value instanceof Error) {
5773-
return value;
5774-
}
5775-
if (typeof value === "string") {
5776-
return new Error(value);
5777-
}
5778-
const error = new Error(fallbackMessage, { cause: value });
5779-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
5780-
Object.assign(error, value);
5781-
}
5782-
return error;
5783-
}

src/agents/embedded-agent-runner/run/llm-idle-timeout.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
MAX_TIMER_TIMEOUT_MS,
88
} from "@openclaw/normalization-core/number-coercion";
99
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
10+
import { toErrorObject } from "../../../infra/errors.js";
1011
import { onLlmRequestActivity } from "../../../shared/llm-request-activity.js";
1112
import type { StreamFn } from "../../runtime/index.js";
1213
import type { MutableAssistantMessageEventStream } from "../../stream-compat.js";
@@ -332,7 +333,7 @@ export function streamWithIdleTimeout(
332333
cleanupIterator();
333334
return (
334335
streamIterator.throw?.(error) ??
335-
Promise.reject(toLintErrorObject(error, "Non-Error rejection"))
336+
Promise.reject(toErrorObject(error, "Non-Error rejection"))
336337
);
337338
},
338339
});
@@ -372,17 +373,3 @@ export function streamWithIdleTimeout(
372373
return wrapStream(maybeStream);
373374
};
374375
}
375-
376-
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
377-
if (value instanceof Error) {
378-
return value;
379-
}
380-
if (typeof value === "string") {
381-
return new Error(value);
382-
}
383-
const error = new Error(fallbackMessage, { cause: value });
384-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
385-
Object.assign(error, value);
386-
}
387-
return error;
388-
}

0 commit comments

Comments
 (0)