@link-assistant/agent 0.16.12 → 0.16.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 CHANGED
@@ -20,11 +20,11 @@
20
20
 
21
21
  > This is the JavaScript/Bun implementation. See also the [Rust implementation](../rust/README.md).
22
22
 
23
- This is an MVP implementation of an OpenCode-compatible CLI agent, focused on maximum efficiency and unrestricted execution. We reproduce OpenCode's `run --format json --model opencode/kimi-k2.5-free` mode with:
23
+ This is an MVP implementation of an OpenCode-compatible CLI agent, focused on maximum efficiency and unrestricted execution. We reproduce OpenCode's `run --format json --model opencode/minimax-m2.5-free` mode with:
24
24
 
25
- - ✅ **JSON Input/Output**: Compatible with `opencode run --format json --model opencode/kimi-k2.5-free`
25
+ - ✅ **JSON Input/Output**: Compatible with `opencode run --format json --model opencode/minimax-m2.5-free`
26
26
  - ✅ **Plain Text Input**: Also accepts plain text messages (auto-converted to JSON format)
27
- - ✅ **Flexible Model Selection**: Defaults to free OpenCode Zen Kimi K2.5, supports [OpenCode Zen](https://opencode.ai/docs/zen/), [Claude OAuth](../docs/claude-oauth.md), [Groq](../docs/groq.md), and [OpenRouter](../docs/openrouter.md) providers
27
+ - ✅ **Flexible Model Selection**: Defaults to free OpenCode Zen MiniMax M2.5 Free, 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
@@ -169,7 +169,7 @@ echo '{"message":"hi"}' | agent
169
169
  **With custom model:**
170
170
 
171
171
  ```bash
172
- echo "hi" | agent --model opencode/kimi-k2.5-free
172
+ echo "hi" | agent --model opencode/minimax-m2.5-free
173
173
  ```
174
174
 
175
175
  ### More Examples
@@ -190,7 +190,7 @@ echo '{"message":"run command","tools":[{"name":"bash","params":{"command":"ls -
190
190
  **Using different models:**
191
191
 
192
192
  ```bash
193
- # Default model (free Kimi K2.5)
193
+ # Default model (free MiniMax M2.5)
194
194
  echo "hi" | agent
195
195
 
196
196
  # Other free models (in order of recommendation)
@@ -280,7 +280,7 @@ agent [options]
280
280
 
281
281
  Options:
282
282
  --model Model to use in format providerID/modelID
283
- Default: opencode/kimi-k2.5-free
283
+ Default: opencode/minimax-m2.5-free
284
284
  --json-standard JSON output format standard
285
285
  Choices: "opencode" (default), "claude" (experimental)
286
286
  --use-existing-claude-oauth Use existing Claude OAuth credentials
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/agent",
3
- "version": "0.16.12",
3
+ "version": "0.16.13",
4
4
  "description": "A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1651,12 +1651,7 @@ export namespace Provider {
1651
1651
  priority = priority.filter((m) => m !== 'claude-haiku-4.5');
1652
1652
  }
1653
1653
  if (providerID === 'opencode' || providerID === 'local') {
1654
- priority = [
1655
- 'kimi-k2.5-free',
1656
- 'minimax-m2.5-free',
1657
- 'gpt-5-nano',
1658
- 'big-pickle',
1659
- ];
1654
+ priority = ['minimax-m2.5-free', 'gpt-5-nano', 'big-pickle'];
1660
1655
  }
1661
1656
  if (providerID === 'kilo') {
1662
1657
  priority = [
@@ -1684,7 +1679,6 @@ export namespace Provider {
1684
1679
 
1685
1680
  const priority = [
1686
1681
  'glm-5-free',
1687
- 'kimi-k2.5-free',
1688
1682
  'minimax-m2.5-free',
1689
1683
  'gpt-5-nano',
1690
1684
  'big-pickle',
@@ -1856,7 +1850,7 @@ export namespace Provider {
1856
1850
  * Examples:
1857
1851
  * - "kilo/glm-5-free" -> { providerID: "kilo", modelID: "glm-5-free" }
1858
1852
  * - "glm-5-free" -> { providerID: "kilo", modelID: "glm-5-free" } (resolved)
1859
- * - "kimi-k2.5-free" -> { providerID: "opencode", modelID: "kimi-k2.5-free" } (resolved)
1853
+ * - "big-pickle" -> { providerID: "opencode", modelID: "big-pickle" } (resolved)
1860
1854
  * - "nonexistent-model" -> throws ModelNotFoundError
1861
1855
  *
1862
1856
  * @param model - Model string with or without provider prefix
@@ -1916,7 +1910,7 @@ export namespace Provider {
1916
1910
  * When one provider hits rate limits, the system can try an alternative.
1917
1911
  *
1918
1912
  * Note: This is only used for models without explicit provider specification.
1919
- * If user specifies "kilo/kimi-k2.5-free", no fallback will occur.
1913
+ * If user specifies "kilo/deepseek-r1-free", no fallback will occur.
1920
1914
  */
1921
1915
  const SHARED_FREE_MODELS: Record<string, string[]> = {
1922
1916
  // Currently no shared models between OpenCode and Kilo providers.
@@ -1928,7 +1922,7 @@ export namespace Provider {
1928
1922
  * This function returns a list of alternative providers that offer the same model.
1929
1923
  *
1930
1924
  * Note: This only returns alternatives for models without explicit provider specification.
1931
- * If the original request had an explicit provider (like "kilo/kimi-k2.5-free"), this returns empty array.
1925
+ * If the original request had an explicit provider (like "kilo/deepseek-r1-free"), this returns empty array.
1932
1926
  *
1933
1927
  * @param modelID - The model ID to find alternatives for
1934
1928
  * @param failedProviderID - The provider that failed
@@ -22,6 +22,34 @@ export namespace SessionProcessor {
22
22
  const DOOM_LOOP_THRESHOLD = 3;
23
23
  const log = Log.create({ service: 'session.processor' });
24
24
 
25
+ /**
26
+ * Detect model-not-supported errors from an API response body.
27
+ *
28
+ * Some providers (e.g., OpenCode, OpenRouter) return HTTP 401 with a body
29
+ * like {"type":"ModelError","message":"Model X not supported"} instead of
30
+ * the semantically correct 400/404. Without this check the error looks like
31
+ * an authentication failure, making diagnostics confusing.
32
+ *
33
+ * See: https://github.com/link-assistant/agent/issues/208
34
+ */
35
+ export function isModelNotSupportedError(responseBody: string): boolean {
36
+ try {
37
+ const parsed = JSON.parse(responseBody);
38
+ // OpenCode/OpenRouter format: {"type":"error","error":{"type":"ModelError","message":"..."}}
39
+ if (parsed?.error?.type === 'ModelError') return true;
40
+ // Flat format: {"type":"ModelError","message":"..."}
41
+ if (parsed?.type === 'ModelError') return true;
42
+ return false;
43
+ } catch {
44
+ // If response body is not JSON, check for common text patterns
45
+ return (
46
+ responseBody.includes('ModelError') ||
47
+ responseBody.toLowerCase().includes('model not supported') ||
48
+ responseBody.toLowerCase().includes('model not found')
49
+ );
50
+ }
51
+ }
52
+
25
53
  export type Info = Awaited<ReturnType<typeof create>>;
26
54
  export type Result = Awaited<ReturnType<Info['process']>>;
27
55
 
@@ -551,6 +579,37 @@ export namespace SessionProcessor {
551
579
 
552
580
  // Clear retry state on non-retryable error
553
581
  SessionRetry.clearRetryState(input.sessionID);
582
+
583
+ // Detect model-not-supported errors from provider response body.
584
+ // OpenCode and similar proxies return HTTP 401 with
585
+ // {"type":"ModelError","message":"Model X not supported"}.
586
+ // Without this check the error looks like an auth failure (401),
587
+ // but it's actually a model-availability issue.
588
+ // See: https://github.com/link-assistant/agent/issues/208
589
+ if (
590
+ error?.name === 'APIError' &&
591
+ error.data.statusCode === 401 &&
592
+ error.data.responseBody
593
+ ) {
594
+ const isModelError = isModelNotSupportedError(
595
+ error.data.responseBody
596
+ );
597
+ if (isModelError) {
598
+ log.error(() => ({
599
+ message:
600
+ 'model not supported by provider — this is NOT an auth error',
601
+ hint: 'The model was found in the local cache but the provider rejected it. The model may have been removed or is temporarily unavailable.',
602
+ providerID: input.providerID,
603
+ modelID: input.model.id,
604
+ statusCode: error.data.statusCode,
605
+ responseBody: error.data.responseBody,
606
+ suggestion:
607
+ 'Try a different model or check the provider status. Use --model <provider>/<model-id> to specify an alternative.',
608
+ issue: 'https://github.com/link-assistant/agent/issues/208',
609
+ }));
610
+ }
611
+ }
612
+
554
613
  input.assistantMessage.error = error;
555
614
  Bus.publish(Session.Event.Error, {
556
615
  sessionID: input.assistantMessage.sessionID,
package/src/tool/task.ts CHANGED
@@ -99,7 +99,7 @@ export const TaskTool = Tool.define('task', async () => {
99
99
 
100
100
  const model = agent.model ??
101
101
  parentModel ?? {
102
- modelID: 'kimi-k2.5-free',
102
+ modelID: 'minimax-m2.5-free',
103
103
  providerID: 'opencode',
104
104
  };
105
105