@link-assistant/agent 0.8.10 → 0.8.13
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/README.md +39 -1
- package/package.json +1 -1
- package/src/session/message-v2.ts +32 -0
- package/src/session/processor.ts +14 -4
- package/src/session/retry.ts +16 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ This is an MVP implementation of an OpenCode-compatible CLI agent, focused on ma
|
|
|
24
24
|
|
|
25
25
|
- ✅ **JSON Input/Output**: Compatible with `opencode run --format json --model opencode/grok-code`
|
|
26
26
|
- ✅ **Plain Text Input**: Also accepts plain text messages (auto-converted to JSON format)
|
|
27
|
-
- ✅ **Flexible Model Selection**: Defaults to free OpenCode Zen Grok Code Fast 1, supports [OpenCode Zen](https://opencode.ai/docs/zen/), [Claude OAuth](../docs/claude-oauth.md),
|
|
27
|
+
- ✅ **Flexible Model Selection**: Defaults to free OpenCode Zen Grok Code Fast 1, supports [OpenCode Zen](https://opencode.ai/docs/zen/), [Claude OAuth](../docs/claude-oauth.md), [Groq](../docs/groq.md), and [OpenRouter](../docs/openrouter.md) providers
|
|
28
28
|
- ✅ **No Restrictions**: Fully unrestricted file system and command execution access (no sandbox)
|
|
29
29
|
- ✅ **Minimal Footprint**: Built with Bun.sh for maximum efficiency
|
|
30
30
|
- ✅ **Full Tool Support**: 13 tools including websearch, codesearch, batch - all enabled by default
|
|
@@ -59,6 +59,38 @@ bun add @link-assistant/agent
|
|
|
59
59
|
|
|
60
60
|
After installation, the `agent` command will be available globally.
|
|
61
61
|
|
|
62
|
+
## Uninstallation
|
|
63
|
+
|
|
64
|
+
### Uninstalling the Agent
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Remove the globally installed package
|
|
68
|
+
bun remove -g @link-assistant/agent
|
|
69
|
+
|
|
70
|
+
# Or if installed locally in your project
|
|
71
|
+
bun remove @link-assistant/agent
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Uninstalling Bun
|
|
75
|
+
|
|
76
|
+
If you need to completely remove Bun from your system:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Remove the Bun binary and installation directory
|
|
80
|
+
rm -rf ~/.bun
|
|
81
|
+
|
|
82
|
+
# Remove the Bun cache (optional)
|
|
83
|
+
rm -rf ~/.bun/install/cache
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
After removing the `~/.bun` directory, you may also need to remove Bun from your shell configuration. Check and remove lines referencing `~/.bun/bin` from:
|
|
87
|
+
|
|
88
|
+
- `~/.bashrc`
|
|
89
|
+
- `~/.zshrc`
|
|
90
|
+
- `~/.config/fish/config.fish`
|
|
91
|
+
|
|
92
|
+
Or the corresponding configuration file for your shell.
|
|
93
|
+
|
|
62
94
|
## Usage
|
|
63
95
|
|
|
64
96
|
### Simplest Examples
|
|
@@ -116,6 +148,11 @@ echo "hi" | agent --model opencode/gemini-3-pro # Gemini 3 Pro
|
|
|
116
148
|
echo "hi" | agent --model groq/llama-3.3-70b-versatile # Llama 3.3 70B
|
|
117
149
|
echo "hi" | agent --model groq/llama-3.1-8b-instant # Llama 3.1 8B (fast)
|
|
118
150
|
|
|
151
|
+
# OpenRouter models (requires OPENROUTER_API_KEY)
|
|
152
|
+
echo "hi" | agent --model openrouter/anthropic/claude-sonnet-4 # Claude via OpenRouter
|
|
153
|
+
echo "hi" | agent --model openrouter/openai/gpt-4o # GPT-4o via OpenRouter
|
|
154
|
+
echo "hi" | agent --model openrouter/meta-llama/llama-3.3-70b # Llama via OpenRouter
|
|
155
|
+
|
|
119
156
|
# Anthropic direct (requires ANTHROPIC_API_KEY)
|
|
120
157
|
echo "hi" | agent --model anthropic/claude-sonnet-4-5 # Claude Sonnet 4.5
|
|
121
158
|
echo "hi" | agent --model anthropic/claude-opus-4-1 # Claude Opus 4.1
|
|
@@ -138,6 +175,7 @@ echo "hi" | agent --model github-copilot/gpt-4o # Uses Copilot
|
|
|
138
175
|
|
|
139
176
|
See [MODELS.md](../MODELS.md) for complete list of available models and pricing.
|
|
140
177
|
See [docs/groq.md](../docs/groq.md) for Groq provider documentation.
|
|
178
|
+
See [docs/openrouter.md](../docs/openrouter.md) for OpenRouter provider documentation.
|
|
141
179
|
See [docs/claude-oauth.md](../docs/claude-oauth.md) for Claude OAuth provider documentation.
|
|
142
180
|
|
|
143
181
|
### Direct Prompt Mode
|
package/package.json
CHANGED
|
@@ -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
|
}
|