@link-assistant/agent 0.8.11 → 0.8.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +5 -0
- package/src/session/message-v2.ts +32 -0
- package/src/session/processor.ts +14 -4
- package/src/session/retry.ts +16 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
+
// Set process title to 'agent' so it appears correctly in process monitoring tools like top/ps
|
|
4
|
+
// Both process.title and process.argv0 need to be set for maximum compatibility
|
|
5
|
+
process.title = 'agent';
|
|
6
|
+
process.argv0 = 'agent';
|
|
7
|
+
|
|
3
8
|
import { Server } from './server/server.ts';
|
|
4
9
|
import { Instance } from './project/instance.ts';
|
|
5
10
|
import { Log } from './util/log.ts';
|
|
@@ -58,6 +58,21 @@ export namespace MessageV2 {
|
|
|
58
58
|
typeof SocketConnectionError.Schema
|
|
59
59
|
>;
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Timeout error - caused by AbortSignal.timeout() firing when an API request
|
|
63
|
+
* takes too long. These are DOMException with name === 'TimeoutError'.
|
|
64
|
+
* These errors are transient and should be retried with increasing intervals.
|
|
65
|
+
* See: https://github.com/link-assistant/agent/issues/142
|
|
66
|
+
*/
|
|
67
|
+
export const TimeoutError = NamedError.create(
|
|
68
|
+
'TimeoutError',
|
|
69
|
+
z.object({
|
|
70
|
+
message: z.string(),
|
|
71
|
+
isRetryable: z.literal(true),
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
export type TimeoutError = z.infer<typeof TimeoutError.Schema>;
|
|
75
|
+
|
|
61
76
|
const PartBase = z.object({
|
|
62
77
|
id: z.string(),
|
|
63
78
|
sessionID: z.string(),
|
|
@@ -782,6 +797,11 @@ export namespace MessageV2 {
|
|
|
782
797
|
cause: e,
|
|
783
798
|
}
|
|
784
799
|
).toObject();
|
|
800
|
+
case e instanceof DOMException && e.name === 'TimeoutError':
|
|
801
|
+
return new MessageV2.TimeoutError(
|
|
802
|
+
{ message: e.message, isRetryable: true },
|
|
803
|
+
{ cause: e }
|
|
804
|
+
).toObject();
|
|
785
805
|
case MessageV2.OutputLengthError.isInstance(e):
|
|
786
806
|
return e;
|
|
787
807
|
case LoadAPIKeyError.isInstance(e):
|
|
@@ -816,6 +836,18 @@ export namespace MessageV2 {
|
|
|
816
836
|
{ cause: e }
|
|
817
837
|
).toObject();
|
|
818
838
|
}
|
|
839
|
+
// Detect timeout errors from various sources
|
|
840
|
+
// See: https://github.com/link-assistant/agent/issues/142
|
|
841
|
+
const isTimeoutError =
|
|
842
|
+
message.includes('The operation timed out') ||
|
|
843
|
+
message.includes('timed out') ||
|
|
844
|
+
e.name === 'TimeoutError';
|
|
845
|
+
if (isTimeoutError) {
|
|
846
|
+
return new MessageV2.TimeoutError(
|
|
847
|
+
{ message, isRetryable: true },
|
|
848
|
+
{ cause: e }
|
|
849
|
+
).toObject();
|
|
850
|
+
}
|
|
819
851
|
return new NamedError.Unknown(
|
|
820
852
|
{ message: e.toString() },
|
|
821
853
|
{ cause: e }
|
package/src/session/processor.ts
CHANGED
|
@@ -326,21 +326,31 @@ export namespace SessionProcessor {
|
|
|
326
326
|
providerID: input.providerID,
|
|
327
327
|
});
|
|
328
328
|
|
|
329
|
-
// Check if error is retryable (APIError or
|
|
329
|
+
// Check if error is retryable (APIError, SocketConnectionError, or TimeoutError)
|
|
330
330
|
const isRetryableAPIError =
|
|
331
331
|
error?.name === 'APIError' && error.data.isRetryable;
|
|
332
332
|
const isRetryableSocketError =
|
|
333
333
|
error?.name === 'SocketConnectionError' &&
|
|
334
334
|
error.data.isRetryable &&
|
|
335
335
|
attempt < SessionRetry.SOCKET_ERROR_MAX_RETRIES;
|
|
336
|
+
const isRetryableTimeoutError =
|
|
337
|
+
error?.name === 'TimeoutError' &&
|
|
338
|
+
error.data.isRetryable &&
|
|
339
|
+
attempt < SessionRetry.TIMEOUT_MAX_RETRIES;
|
|
336
340
|
|
|
337
|
-
if (
|
|
341
|
+
if (
|
|
342
|
+
isRetryableAPIError ||
|
|
343
|
+
isRetryableSocketError ||
|
|
344
|
+
isRetryableTimeoutError
|
|
345
|
+
) {
|
|
338
346
|
attempt++;
|
|
339
|
-
// Use
|
|
347
|
+
// Use error-specific delay calculation
|
|
340
348
|
const delay =
|
|
341
349
|
error?.name === 'SocketConnectionError'
|
|
342
350
|
? SessionRetry.socketErrorDelay(attempt)
|
|
343
|
-
:
|
|
351
|
+
: error?.name === 'TimeoutError'
|
|
352
|
+
? SessionRetry.timeoutDelay(attempt)
|
|
353
|
+
: SessionRetry.delay(error, attempt);
|
|
344
354
|
log.info(() => ({
|
|
345
355
|
message: 'retrying',
|
|
346
356
|
errorType: error?.name,
|
package/src/session/retry.ts
CHANGED
|
@@ -13,6 +13,12 @@ export namespace SessionRetry {
|
|
|
13
13
|
export const SOCKET_ERROR_INITIAL_DELAY = 1000; // 1 second
|
|
14
14
|
export const SOCKET_ERROR_BACKOFF_FACTOR = 2;
|
|
15
15
|
|
|
16
|
+
// Timeout error retry configuration
|
|
17
|
+
// When API requests time out (AbortSignal.timeout), retry with increasing intervals
|
|
18
|
+
// See: https://github.com/link-assistant/agent/issues/142
|
|
19
|
+
export const TIMEOUT_MAX_RETRIES = 3;
|
|
20
|
+
export const TIMEOUT_DELAYS = [30_000, 60_000, 120_000]; // 30s, 60s, 120s
|
|
21
|
+
|
|
16
22
|
export async function sleep(ms: number, signal: AbortSignal): Promise<void> {
|
|
17
23
|
return new Promise((resolve, reject) => {
|
|
18
24
|
const timeout = setTimeout(resolve, ms);
|
|
@@ -71,4 +77,14 @@ export namespace SessionRetry {
|
|
|
71
77
|
Math.pow(SOCKET_ERROR_BACKOFF_FACTOR, attempt - 1)
|
|
72
78
|
);
|
|
73
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Calculate delay for timeout error retries.
|
|
83
|
+
* Uses fixed intervals: 30s, 60s, 120s.
|
|
84
|
+
* See: https://github.com/link-assistant/agent/issues/142
|
|
85
|
+
*/
|
|
86
|
+
export function timeoutDelay(attempt: number): number {
|
|
87
|
+
const index = Math.min(attempt - 1, TIMEOUT_DELAYS.length - 1);
|
|
88
|
+
return TIMEOUT_DELAYS[index];
|
|
89
|
+
}
|
|
74
90
|
}
|