@link-assistant/agent 0.27.0 → 0.27.1

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.
Files changed (54) hide show
  1. package/README.md +47 -5
  2. package/package.json +41 -42
  3. package/src/cli/argv.ts +1 -1
  4. package/src/cli/cmd/run.ts +7 -1
  5. package/src/cli/continuous-mode.js +38 -12
  6. package/src/cli/event-handler.js +3 -1
  7. package/src/cli/model-config.js +20 -4
  8. package/src/cli/run-options.js +144 -78
  9. package/src/config/config.ts +306 -105
  10. package/src/config/defaults.ts +13 -5
  11. package/src/config/file-config.ts +16 -0
  12. package/src/file/ripgrep.ts +38 -5
  13. package/src/file/time.ts +86 -9
  14. package/src/index.js +39 -33
  15. package/src/mcp/index.ts +370 -106
  16. package/src/permission/index.ts +22 -0
  17. package/src/project/instance.ts +5 -1
  18. package/src/project/project.ts +35 -2
  19. package/src/provider/free-tier-error.ts +1 -1
  20. package/src/provider/kilo-deprecation.ts +30 -0
  21. package/src/provider/kilo.ts +25 -7
  22. package/src/provider/provider.ts +53 -22
  23. package/src/provider/retry-fetch.ts +5 -0
  24. package/src/provider/transform.ts +139 -17
  25. package/src/session/compaction.ts +18 -2
  26. package/src/session/index.ts +30 -3
  27. package/src/session/message-v2.ts +318 -32
  28. package/src/session/processor.ts +274 -140
  29. package/src/session/prompt.ts +154 -128
  30. package/src/session/retry.ts +26 -13
  31. package/src/session/summary.ts +4 -1
  32. package/src/tool/bash.ts +232 -45
  33. package/src/tool/bash.txt +1 -1
  34. package/src/tool/codesearch.ts +15 -78
  35. package/src/tool/edit.ts +111 -30
  36. package/src/tool/glob.ts +2 -1
  37. package/src/tool/grep.ts +175 -57
  38. package/src/tool/ls.ts +2 -1
  39. package/src/tool/multiedit.ts +3 -1
  40. package/src/tool/read.ts +169 -41
  41. package/src/tool/task.ts +26 -18
  42. package/src/tool/tool.ts +4 -1
  43. package/src/tool/webfetch.ts +68 -56
  44. package/src/tool/webfetch.txt +4 -4
  45. package/src/tool/websearch.ts +20 -75
  46. package/src/tool/websearch.txt +2 -0
  47. package/src/tool/write.ts +23 -6
  48. package/src/util/bom.ts +32 -0
  49. package/src/util/exa-mcp.ts +82 -0
  50. package/src/util/legacy-error-names.ts +39 -0
  51. package/src/util/read-bounded.ts +32 -0
  52. package/src/util/sse-usage-extractor.ts +67 -13
  53. package/src/util/timeout.ts +3 -4
  54. package/src/util/verbose-fetch.ts +40 -1
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`
26
26
  - ✅ **Plain Text Input**: Also accepts plain text messages (auto-converted to JSON format)
27
- - ✅ **Flexible Model Selection**: Defaults to free Kilo Nemotron 3 Super (Kilo authentication recommended), supports every [models.dev](https://models.dev) provider OpenCode supports ([full list](../docs/providers.md)), OpenCode's subscription logins, [gpt4free](../docs/gpt4free.md), Pollinations, local servers, [Formal AI](../docs/formal-ai.md) and [Claude OAuth](../docs/claude-oauth.md), with [live model discovery](../docs/model-discovery.md)
27
+ - ✅ **Flexible Model Selection**: Defaults to free Kilo Nemotron 3 Ultra (Kilo authentication recommended), supports every [models.dev](https://models.dev) provider OpenCode supports ([full list](../docs/providers.md)), OpenCode's subscription logins, [gpt4free](../docs/gpt4free.md), Pollinations, local servers, [Formal AI](../docs/formal-ai.md) and [Claude OAuth](../docs/claude-oauth.md), with [live model discovery](../docs/model-discovery.md)
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 kilo/nemotron-3-super-free
172
+ echo "hi" | agent --model kilo/nemotron-3-ultra-free
173
173
  ```
174
174
 
175
175
  ### More Examples
@@ -190,11 +190,10 @@ echo '{"message":"run command","tools":[{"name":"bash","params":{"command":"ls -
190
190
  **Using different models:**
191
191
 
192
192
  ```bash
193
- # Default model (free Nemotron 3 Super)
193
+ # Default model (free Nemotron 3 Ultra)
194
194
  echo "hi" | agent
195
195
 
196
196
  # Built-in free models (authenticate with agent auth login or KILO_API_KEY)
197
- echo "hi" | agent --model kilo/nemotron-3-super-free
198
197
  echo "hi" | agent --model kilo/nemotron-3-ultra-free
199
198
  echo "hi" | agent --model kilo/nemotron-3.5-lightning-free
200
199
 
@@ -285,7 +284,7 @@ agent [options]
285
284
 
286
285
  Options:
287
286
  --model Model to use in format providerID/modelID
288
- Default: kilo/nemotron-3-super-free
287
+ Default: kilo/nemotron-3-ultra-free
289
288
  --json-standard JSON output format standard
290
289
  Choices: "opencode" (default), "claude" (experimental)
291
290
  --output-format Alias for JSON output format
@@ -311,6 +310,42 @@ Permission Options:
311
310
  invoked via batch.
312
311
  Env: LINK_ASSISTANT_AGENT_DISABLE_TOOLS
313
312
 
313
+ Compatibility Options (issue #335):
314
+ Each option restores behaviour from before the OpenCode stability sync, or
315
+ tunes a new limit. Every option also has a LINK_ASSISTANT_AGENT_* env var.
316
+ --bash-shell <auto|legacy|path> Shell for the bash tool. "auto" (default):
317
+ $SHELL if POSIX, else bash, else /bin/sh;
318
+ "legacy": /bin/sh as before.
319
+ Env: LINK_ASSISTANT_AGENT_BASH_SHELL
320
+ --legacy-tool-output Empty edit/write output, head-truncated bash
321
+ output, empty read past EOF (as before).
322
+ Env: LINK_ASSISTANT_AGENT_LEGACY_TOOL_OUTPUT
323
+ --legacy-edit-matching Loose fuzzy edit matching; an empty oldString
324
+ overwrites an existing file (as before).
325
+ Env: LINK_ASSISTANT_AGENT_LEGACY_EDIT_MATCHING
326
+ --glob-follow-symlinks Follow symlinked directories in glob/ls.
327
+ Env: LINK_ASSISTANT_AGENT_GLOB_FOLLOW_SYMLINKS
328
+ --no-grep-hidden Do not search hidden files in grep.
329
+ Env: LINK_ASSISTANT_AGENT_GREP_HIDDEN=false
330
+ --grep-timeout-ms <ms> Timeout of one grep search (default 30000,
331
+ 0 = none). Env: LINK_ASSISTANT_AGENT_GREP_TIMEOUT_MS
332
+ --read-max-lines <n> Max lines per read call (default 2000,
333
+ 0 = no cap). Env: LINK_ASSISTANT_AGENT_READ_MAX_LINES
334
+ --read-max-bytes <n> Max bytes per read call (default 51200,
335
+ 0 = no cap). Env: LINK_ASSISTANT_AGENT_READ_MAX_BYTES
336
+ --legacy-error-names Report ContextOverflowError as a
337
+ non-retryable APIError in JSON output.
338
+ Env: LINK_ASSISTANT_AGENT_LEGACY_ERROR_NAMES
339
+ --legacy-token-usage tokens.output includes reasoning tokens.
340
+ Env: LINK_ASSISTANT_AGENT_LEGACY_TOKEN_USAGE
341
+ --legacy-loop-exit End the turn on a "stop" finish even when
342
+ tools ran in that step.
343
+ Env: LINK_ASSISTANT_AGENT_LEGACY_LOOP_EXIT
344
+ --mcp-connect-timeout <ms> Default MCP connect/initialize timeout
345
+ (default 30000, 0 = none). Per server:
346
+ "connect_timeout" in the mcp config.
347
+ Env: LINK_ASSISTANT_AGENT_MCP_CONNECT_TIMEOUT
348
+
314
349
  Stdin Mode Options:
315
350
  -p, --prompt Direct prompt (bypasses stdin reading)
316
351
  --disable-stdin Disable stdin streaming (requires --prompt)
@@ -509,6 +544,13 @@ This will create a configuration file at `~/.config/link-assistant-agent/opencod
509
544
  }
510
545
  ```
511
546
 
547
+ **MCP timeouts:** each server entry accepts `timeout` (ms for listing tools,
548
+ default 5000; unchanged meaning) and `connect_timeout` (ms for starting the
549
+ server and finishing the `initialize` handshake, default
550
+ `--mcp-connect-timeout`, 30000, or `timeout` if larger; `0` disables the
551
+ bound). A server that does not connect in time is reported as `failed`
552
+ instead of blocking the session.
553
+
512
554
  **Available Playwright Tools:**
513
555
 
514
556
  Once configured, the agent gains access to 22+ browser automation tools:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/agent",
3
- "version": "0.27.0",
3
+ "version": "0.27.1",
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",
@@ -51,65 +51,65 @@
51
51
  "LICENSE"
52
52
  ],
53
53
  "dependencies": {
54
- "@agentclientprotocol/sdk": "^2.0.1",
55
- "@ai-sdk/alibaba": "^2.0.74",
56
- "@ai-sdk/amazon-bedrock": "^5.0.122",
57
- "@ai-sdk/anthropic": "^4.0.86",
58
- "@ai-sdk/azure": "^4.0.108",
59
- "@ai-sdk/cerebras": "^3.0.76",
60
- "@ai-sdk/cohere": "^4.0.67",
61
- "@ai-sdk/deepinfra": "^3.0.76",
62
- "@ai-sdk/deepseek": "^3.0.71",
63
- "@ai-sdk/fireworks": "^3.0.79",
64
- "@ai-sdk/gateway": "^4.0.122",
65
- "@ai-sdk/google": "^4.0.103",
66
- "@ai-sdk/google-vertex": "^5.0.120",
67
- "@ai-sdk/groq": "^4.0.68",
68
- "@ai-sdk/mcp": "^2.0.82",
69
- "@ai-sdk/mistral": "^4.0.70",
70
- "@ai-sdk/openai": "^4.0.100",
71
- "@ai-sdk/openai-compatible": "^3.0.76",
72
- "@ai-sdk/perplexity": "^5.0.18",
73
- "@ai-sdk/provider": "^4.0.32",
74
- "@ai-sdk/togetherai": "^3.0.77",
75
- "@ai-sdk/xai": "^5.0.28",
54
+ "@agentclientprotocol/sdk": "^2.4.0",
55
+ "@ai-sdk/alibaba": "^2.0.81",
56
+ "@ai-sdk/amazon-bedrock": "^5.0.131",
57
+ "@ai-sdk/anthropic": "^4.0.95",
58
+ "@ai-sdk/azure": "^4.0.115",
59
+ "@ai-sdk/cerebras": "^3.0.83",
60
+ "@ai-sdk/cohere": "^4.0.74",
61
+ "@ai-sdk/deepinfra": "^3.0.83",
62
+ "@ai-sdk/deepseek": "^3.0.78",
63
+ "@ai-sdk/fireworks": "^3.0.86",
64
+ "@ai-sdk/gateway": "^4.0.134",
65
+ "@ai-sdk/google": "^4.0.110",
66
+ "@ai-sdk/google-vertex": "^5.0.130",
67
+ "@ai-sdk/groq": "^4.0.75",
68
+ "@ai-sdk/mcp": "^2.0.89",
69
+ "@ai-sdk/mistral": "^4.0.78",
70
+ "@ai-sdk/openai": "^4.0.107",
71
+ "@ai-sdk/openai-compatible": "^3.0.83",
72
+ "@ai-sdk/perplexity": "^5.0.25",
73
+ "@ai-sdk/provider": "^4.0.35",
74
+ "@ai-sdk/togetherai": "^3.0.84",
75
+ "@ai-sdk/xai": "^5.0.37",
76
76
  "@clack/prompts": "^1.8.1",
77
- "@hono/standard-validator": "^0.4.0",
77
+ "@hono/standard-validator": "^0.4.1",
78
78
  "@hono/zod-validator": "^0.9.1",
79
- "@modelcontextprotocol/sdk": "^1.32.1",
79
+ "@modelcontextprotocol/sdk": "^1.32.2",
80
80
  "@octokit/graphql": "^9.0.5",
81
81
  "@octokit/rest": "^22.0.1",
82
82
  "@openauthjs/openauth": "^0.4.3",
83
- "@opencode-ai/sdk": "^1.19.4",
83
+ "@opencode-ai/sdk": "^1.19.5",
84
84
  "@openrouter/ai-sdk-provider": "^3.1.0",
85
- "@opentui/core": "^0.6.6",
86
- "@opentui/solid": "^0.6.6",
85
+ "@opentui/core": "^0.8.1",
86
+ "@opentui/solid": "^0.8.1",
87
87
  "@parcel/watcher": "^2.6.0",
88
88
  "@solid-primitives/event-bus": "^1.1.4",
89
- "@standard-community/standard-json": "^0.3.7",
89
+ "@standard-community/standard-json": "^0.4.0",
90
90
  "@standard-community/standard-openapi": "^0.2.11",
91
91
  "@zip.js/zip.js": "^2.24.0",
92
- "ai": "^7.0.149",
92
+ "ai": "^7.0.161",
93
93
  "chokidar": "^6.0.0",
94
94
  "clipboardy": "^5.3.2",
95
- "command-stream": "^2.4.0",
95
+ "command-stream": "^2.6.0",
96
96
  "decimal.js": "^10.6.0",
97
97
  "diff": "^9.0.0",
98
98
  "fuzzysort": "^4.0.2",
99
- "glob": "^13.0.6",
100
- "google-auth-library": "^11.3.1",
99
+ "glob": "^14.0.3",
100
+ "google-auth-library": "^11.3.2",
101
101
  "gpt-tokenizer": "^4.1.0",
102
102
  "gray-matter": "^4.0.3",
103
- "hono": "^4.13.13",
103
+ "hono": "^4.13.16",
104
104
  "hono-openapi": "^1.5.0",
105
- "ignore": "^7.0.13",
105
+ "ignore": "^7.0.14",
106
106
  "jsonc-parser": "^3.3.1",
107
107
  "lino-arguments": "^0.3.0",
108
- "lino-objects-codec": "^0.9.2",
108
+ "lino-objects-codec": "^0.9.5",
109
109
  "log-lazy": "^1.0.4",
110
- "minimatch": "^10.2.6",
111
- "remeda": "^2.53.0",
112
- "solid-js": "1.9.12",
110
+ "minimatch": "^11.0.4",
111
+ "remeda": "^2.58.0",
112
+ "solid-js": "^1.9.18",
113
113
  "strip-ansi": "^7.2.0",
114
114
  "test-anywhere": "^0.9.1",
115
115
  "tree-sitter-bash": "^0.25.1",
@@ -125,7 +125,7 @@
125
125
  "devDependencies": {
126
126
  "@changesets/cli": "^3.0.3",
127
127
  "@eslint/js": "^10.0.1",
128
- "eslint": "^10.13.0",
128
+ "eslint": "^10.14.0",
129
129
  "eslint-config-prettier": "^10.1.8",
130
130
  "eslint-plugin-prettier": "^5.5.7",
131
131
  "eslint-plugin-promise": "^7.3.0",
@@ -151,11 +151,10 @@
151
151
  "node-pty@1.1.0": true
152
152
  },
153
153
  "overrides": {
154
- "seroval": "^1.6.9",
155
154
  "undici": "^7.16.0"
156
155
  },
157
156
  "dependencyPins": {
158
- "solid-js": "https://github.com/anomalyco/opentui/issues/689",
157
+ "lino-arguments": "https://github.com/link-foundation/lino-arguments/issues/50",
159
158
  "web-tree-sitter": "https://github.com/anomalyco/opentui/issues/1550"
160
159
  }
161
160
  }
package/src/cli/argv.ts CHANGED
@@ -105,7 +105,7 @@ export function getCompactionSafetyMarginFromProcessArgv(): string | null {
105
105
  /**
106
106
  * Extract --compaction-models argument directly from process.argv
107
107
  * The value is a links notation references sequence, e.g.:
108
- * "(kilo/nemotron-3-super-free kilo/nemotron-3-ultra-free kilo/nemotron-3.5-lightning-free same)"
108
+ * "(kilo/nemotron-3-ultra-free kilo/nemotron-3.5-lightning-free same)"
109
109
  * @returns The compaction models argument from CLI or null if not found
110
110
  * @see https://github.com/link-assistant/agent/issues/232
111
111
  */
@@ -9,6 +9,7 @@ import { EOL } from 'os';
9
9
  import { select } from '@clack/prompts';
10
10
  import { createOpencodeClient, type OpencodeClient } from '@opencode-ai/sdk';
11
11
  import { Provider } from '../../provider/provider';
12
+ import { legacyErrorObject } from '../../util/legacy-error-names';
12
13
 
13
14
  const TOOL: Record<string, [string, string]> = {
14
15
  todowrite: ['Todo', UI.Style.TEXT_WARNING_BOLD],
@@ -267,7 +268,12 @@ export const RunCommand = cmd({
267
268
  // Always include the human-readable string alongside the
268
269
  // machine-readable object so consumers never print
269
270
  // "[object Object]" (#289).
270
- if (outputJsonEvent('error', { message: err, error: props.error }))
271
+ if (
272
+ outputJsonEvent('error', {
273
+ message: err,
274
+ error: legacyErrorObject(props.error),
275
+ })
276
+ )
271
277
  continue;
272
278
  UI.error(err);
273
279
  }
@@ -356,6 +356,10 @@ export async function runContinuousServerMode(
356
356
  const parts = [{ type: 'text', text: messageText }];
357
357
 
358
358
  // Create a promise to wait for this message to complete
359
+ // `stopIdleWait` ends the wait when the prompt settles without ever
360
+ // going idle (it can throw before the loop starts); otherwise the
361
+ // subscription leaked and the input queue hung (issue #335).
362
+ let stopIdleWait = () => {};
359
363
  const messagePromise = new Promise((resolve) => {
360
364
  const checkIdle = Bus.subscribeAll((event) => {
361
365
  if (
@@ -366,6 +370,10 @@ export async function runContinuousServerMode(
366
370
  resolve();
367
371
  }
368
372
  });
373
+ stopIdleWait = () => {
374
+ checkIdle();
375
+ resolve();
376
+ };
369
377
  });
370
378
 
371
379
  // Send message
@@ -383,21 +391,28 @@ export async function runContinuousServerMode(
383
391
  temperature,
384
392
  }),
385
393
  }
386
- ).catch((error) => {
387
- hasError = true;
388
- eventHandler.output({
389
- type: 'error',
390
- timestamp: Date.now(),
391
- sessionID,
392
- message: errorText(error),
393
- error: error instanceof Error ? error.message : String(error),
394
+ )
395
+ // Read the reply to the end: it completes only after the prompt has
396
+ // returned (and released the connection), successful or not.
397
+ .then((response) => response.text())
398
+ .catch((error) => {
399
+ hasError = true;
400
+ eventHandler.output({
401
+ type: 'error',
402
+ timestamp: Date.now(),
403
+ sessionID,
404
+ message: errorText(error),
405
+ error: error instanceof Error ? error.message : String(error),
406
+ });
394
407
  });
395
- });
396
408
 
397
409
  // See the direct-mode comment below: the request rejection must be
398
- // observed before the process can exit (#290).
399
- await messagePromise;
410
+ // observed before the process can exit (#290). `session.idle` is
411
+ // published before the reply is written, so once the reply is complete
412
+ // the idle wait can stop.
400
413
  await requestPromise;
414
+ stopIdleWait();
415
+ await messagePromise;
401
416
  isProcessing = false;
402
417
 
403
418
  // Process next pending message if any
@@ -582,6 +597,10 @@ export async function runContinuousDirectMode(
582
597
  const parts = [{ type: 'text', text: messageText }];
583
598
 
584
599
  // Create a promise to wait for this message to complete
600
+ // `stopIdleWait` ends the wait when the prompt settles without ever
601
+ // going idle (it can throw before the loop starts); otherwise the
602
+ // subscription leaked and the input queue hung (issue #335).
603
+ let stopIdleWait = () => {};
585
604
  const messagePromise = new Promise((resolve) => {
586
605
  const checkIdle = Bus.subscribeAll((event) => {
587
606
  if (
@@ -592,6 +611,10 @@ export async function runContinuousDirectMode(
592
611
  resolve();
593
612
  }
594
613
  });
614
+ stopIdleWait = () => {
615
+ checkIdle();
616
+ resolve();
617
+ };
595
618
  });
596
619
 
597
620
  // Send message directly
@@ -618,8 +641,11 @@ export async function runContinuousDirectMode(
618
641
  // publishes `session.idle` while unwinding, so waiting only on the idle
619
642
  // event let the process exit before the rejection handler above ran —
620
643
  // no error record, exit code 0 (#290).
621
- await messagePromise;
644
+ // `session.idle` is published synchronously before `prompt` settles,
645
+ // so after it settles the idle wait can stop.
622
646
  await promptPromise;
647
+ stopIdleWait();
648
+ await messagePromise;
623
649
  isProcessing = false;
624
650
 
625
651
  // Process next pending message if any
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { Bus } from '../bus/index.ts';
7
7
  import { errorText } from '../util/error-text.ts';
8
+ import { legacyErrorObject } from '../util/legacy-error-names.ts';
8
9
 
9
10
  /**
10
11
  * Create a subscription for session bus events that outputs events in the selected JSON format.
@@ -142,7 +143,8 @@ export function outputBusEvent({
142
143
  timestamp: Date.now(),
143
144
  sessionID,
144
145
  message: errorText(props.error),
145
- error: props.error,
146
+ // --legacy-error-names maps new error names to the previous ones.
147
+ error: legacyErrorObject(props.error),
146
148
  });
147
149
  }
148
150
  }
@@ -6,6 +6,7 @@ import {
6
6
  } from './argv.ts';
7
7
  import { Log } from '../util/log.ts';
8
8
  import { ExtraProviders } from '../provider/extra-providers.ts';
9
+ import { resolveKiloDeprecatedAlias } from '../provider/kilo-deprecation.ts';
9
10
  import {
10
11
  DEFAULT_PROVIDER_ID,
11
12
  DEFAULT_MODEL_ID,
@@ -31,6 +32,17 @@ export class ModelResolutionError extends Error {
31
32
  }
32
33
  }
33
34
 
35
+ /**
36
+ * Map a withdrawn built-in Kilo alias to its replacement, with a warning (#335).
37
+ * Other providers and model IDs are returned unchanged.
38
+ * @param {string} providerID
39
+ * @param {string} modelID
40
+ * @returns {string}
41
+ */
42
+ function replaceDeprecatedModel(providerID, modelID) {
43
+ return providerID === 'kilo' ? resolveKiloDeprecatedAlias(modelID) : modelID;
44
+ }
45
+
34
46
  /**
35
47
  * Parse model config from argv. Supports "provider/model" or short "model" format.
36
48
  * @param {object} argv - Parsed command line arguments
@@ -127,7 +139,7 @@ export async function parseModelConfig(
127
139
  const modelParts = modelArg.split('/');
128
140
  // Accept alternative spellings such as `g4f/...` for `gpt4free/...` (#334)
129
141
  providerID = ExtraProviders.canonicalID(modelParts[0]);
130
- modelID = modelParts.slice(1).join('/');
142
+ modelID = replaceDeprecatedModel(providerID, modelParts.slice(1).join('/'));
131
143
 
132
144
  // Validate that providerID and modelID are not empty
133
145
  // Do NOT fall back to defaults - if the user provided an invalid format, fail clearly (#196)
@@ -347,7 +359,7 @@ async function resolveCompactionModelEntry(
347
359
  const parts = modelArg.split('/');
348
360
  return {
349
361
  providerID: parts[0],
350
- modelID: parts.slice(1).join('/'),
362
+ modelID: replaceDeprecatedModel(parts[0], parts.slice(1).join('/')),
351
363
  useSameModel: false,
352
364
  };
353
365
  }
@@ -399,7 +411,7 @@ async function resolveEntryWithinProvider(
399
411
  }
400
412
  return {
401
413
  providerID: parts[0],
402
- modelID: parts.slice(1).join('/'),
414
+ modelID: replaceDeprecatedModel(parts[0], parts.slice(1).join('/')),
403
415
  useSameModel: false,
404
416
  };
405
417
  }
@@ -408,7 +420,11 @@ async function resolveEntryWithinProvider(
408
420
  if (!(await Provider.hasModel(baseProviderID, modelArg))) {
409
421
  return null;
410
422
  }
411
- return { providerID: baseProviderID, modelID: modelArg, useSameModel: false };
423
+ return {
424
+ providerID: baseProviderID,
425
+ modelID: replaceDeprecatedModel(baseProviderID, modelArg),
426
+ useSameModel: false,
427
+ };
412
428
  }
413
429
 
414
430
  /**