@vedmalex/ai-connect 0.4.0 → 0.6.0

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
@@ -274,6 +274,7 @@ Known local presets now include:
274
274
  - `openai:codex-cli`
275
275
  - `anthropic:claude-cli`
276
276
  - `openclaude:openclaude-cli`
277
+ - `pi:pi-cli`
277
278
  - `anthropic:claude-code-acp`
278
279
  - `opencode:opencode-server`
279
280
  - `opencode:opencode-acp`
@@ -362,7 +363,7 @@ const client = createLocalClient(
362
363
  );
363
364
  ```
364
365
 
365
- Another print-mode coding-agent CLI example `pi` (invoked with `pi --print`; no ACP sidecar):
366
+ `pi` has a built-in CLI preset (`pi-cli`). The preset supplies the default command (`pi`), argsTemplate (`["--print","--model","{model}","{prompt}"]`), parser (`kind: "text"`), and discovery (`via: "none"`). The minimal config is therefore:
366
367
 
367
368
  ```ts
368
369
  import { createLocalClient, defineConfig } from "@vedmalex/ai-connect";
@@ -376,15 +377,9 @@ const client = createLocalClient(
376
377
  id: "local",
377
378
  transport: {
378
379
  kind: "cli",
379
- id: "pi-cli",
380
- command: "pi",
381
- cli: {
382
- argsTemplate: ["--print", "--model", "{model}", "{prompt}"],
383
- parser: { kind: "text" }, // raw stdout -> result.text
384
- discovery: { via: "none" }, // no ACP sidecar for pi
385
- },
380
+ id: "pi-cli", // selects the built-in pi-cli preset
386
381
  },
387
- models: ["gemini-3.1-pro-low", "gemini-3-flash"],
382
+ models: ["gemini-3.1-pro-low"],
388
383
  },
389
384
  ],
390
385
  },
@@ -437,6 +432,65 @@ Current local transport scope:
437
432
  - `cli`: text generation, plus model discovery via a list `command`, a `static` config catalog, or an ACP sidecar
438
433
  - `server`: text generation plus provider-native model discovery
439
434
 
435
+ ### CLI File and Image Input
436
+
437
+ CLI routes can stage local attachment files into a temp directory and pass them to the subprocess as argv tokens or inline prompt references.
438
+
439
+ **Client-level staging** is configured under `cli.staging`:
440
+
441
+ ```ts
442
+ const client = createLocalClient(config, {
443
+ cli: {
444
+ staging: {
445
+ dir: "/tmp/my-staging", // default: os.tmpdir()
446
+ prefix: "ai-connect-", // temp dir name prefix
447
+ keep: true, // retain per-invocation temp dir (debug only)
448
+ },
449
+ },
450
+ });
451
+ ```
452
+
453
+ Staged files are written under `<stagingDir>/attachments/` with sanitized basenames (path-traversal safe) and removed after the call completes (unless `keep: true`). Attachments that carry only a remote URI and no bytes degrade to the raw URI reference.
454
+
455
+ **Per-route file input** is declared under `transport.cli.fileInput`:
456
+
457
+ ```ts
458
+ transport: {
459
+ kind: "cli",
460
+ id: "my-route",
461
+ cli: {
462
+ fileInput: {
463
+ placement: "args", // "args" (default) | "prompt"
464
+ perFileArgs: ["@{path}"], // argv tokens per file; {path} and {name} placeholders
465
+ // mentionTemplate: "@{path}", // prompt placement: inserted per file (default "@{path}")
466
+ // separator: " ",
467
+ categories: ["image", "document", "text", "other"], // accepted categories (default: all)
468
+ stagingDir: "/custom/dir", // per-route override of cli.staging.dir
469
+ },
470
+ },
471
+ }
472
+ ```
473
+
474
+ A `{files}` placeholder in `argsTemplate` expands to the full per-file argv block; it records a single telemetry key, not the staged absolute paths.
475
+
476
+ **Capability gate.** A route advertises `supportsImageInput` only when its `fileInput` stages the `image` category, and `supportsFileUpload` only when it stages the `document` category. A route that does not accept a category rejects the attachment with `unsupported_capability` at routing time, before any subprocess is spawned.
477
+
478
+ **Built-in preset behavior:**
479
+
480
+ - **`pi` preset** — accepts attachments from all categories (`image`, `document`, `text`, `other`). Each file is passed as an `@{path}` argv token (`perFileArgs: ["@{path}"]`). Pass an image attachment and `pi` receives `@/tmp/.../attachments/photo.png` as an argument. PDFs are staged as-is with no document extraction; `pi` receives raw bytes — prefer images or text files for reliable results.
481
+
482
+ ```ts
483
+ const result = await client.generate({
484
+ messages: [{ role: "user", content: "Describe this image." }],
485
+ attachments: ["/path/to/photo.png"],
486
+ // route is pi:cli:local or routeHints selects pi-cli
487
+ });
488
+ ```
489
+
490
+ - **`codex` preset** — accepts images only (`categories: ["image"]`). Each image is passed as `--image {path}` (`perFileArgs: ["--image", "{path}"]`). A non-image attachment (e.g. a PDF) on a codex route is rejected with `unsupported_capability` before any spawn.
491
+
492
+ - **`claude` / `openclaude` presets** — do not accept CLI file input by default. File input is opt-in: add an explicit `transport.cli.fileInput` block to your route config.
493
+
440
494
  ## Mock Gateway
441
495
 
442
496
  For API-level debugging you can run a local mock backend that behaves like a small OpenAI/Anthropic/Gemini proxy and captures the real finalized wire payloads:
@@ -1,3 +1,125 @@
1
+ // src/cli-presets.ts
2
+ var AI_CONNECT_DEFAULT_CLI_PRESETS = {
3
+ pi: {
4
+ id: "pi",
5
+ label: "pi (coding agent)",
6
+ command: "pi",
7
+ transportId: "pi-cli",
8
+ options: {
9
+ preset: "pi",
10
+ // pi print mode emits the answer as plain text on stdout (no JSON wrapper),
11
+ // and has no ACP mode — so it uses the raw `text` parser and no discovery sidecar.
12
+ // pi takes file/image attachments as separate `@<path>` argv tokens (REQ-024);
13
+ // {files} expands one `@<staged-path>` token per attachment. pi stages all
14
+ // categories (PDFs as-is, no extraction guarantee).
15
+ argsTemplate: ["--print", "--model", "{model}", "{files}", "{prompt}"],
16
+ discovery: {
17
+ via: "none"
18
+ },
19
+ parser: {
20
+ kind: "text"
21
+ },
22
+ fileInput: {
23
+ placement: "args",
24
+ perFileArgs: ["@{path}"]
25
+ }
26
+ }
27
+ },
28
+ claude: {
29
+ id: "claude",
30
+ label: "Claude CLI",
31
+ command: "claude",
32
+ transportId: "claude-cli",
33
+ options: {
34
+ preset: "claude",
35
+ argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
36
+ discovery: {
37
+ via: "acp",
38
+ acp: {
39
+ transportId: "claude-code-acp"
40
+ }
41
+ },
42
+ parser: {
43
+ kind: "json",
44
+ textPath: "__preset__:claude.result",
45
+ usagePath: "usage",
46
+ errorPath: "__preset__:claude.error"
47
+ }
48
+ }
49
+ },
50
+ openclaude: {
51
+ id: "openclaude",
52
+ label: "OpenClaude CLI",
53
+ command: "openclaude",
54
+ transportId: "openclaude-cli",
55
+ options: {
56
+ preset: "openclaude",
57
+ argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
58
+ parser: {
59
+ kind: "json",
60
+ textPath: "__preset__:claude.result",
61
+ usagePath: "usage",
62
+ errorPath: "__preset__:claude.error"
63
+ }
64
+ }
65
+ },
66
+ codex: {
67
+ id: "codex",
68
+ label: "Codex CLI",
69
+ command: "codex",
70
+ transportId: "codex-cli",
71
+ options: {
72
+ preset: "codex",
73
+ // codex exec takes image attachments via `-i/--image <FILE>` flags
74
+ // (REQ-024); {files} expands a `--image <staged-path>` pair per image.
75
+ // Images only — a non-image attachment cleanly rejects pre-spawn.
76
+ argsTemplate: [
77
+ "exec",
78
+ "--json",
79
+ "--output-last-message",
80
+ "{output_file}",
81
+ "--model",
82
+ "{model}",
83
+ "{files}",
84
+ "{prompt}"
85
+ ],
86
+ discovery: {
87
+ via: "acp",
88
+ acp: {
89
+ transportId: "codex-acp"
90
+ }
91
+ },
92
+ fileInput: {
93
+ placement: "args",
94
+ perFileArgs: ["--image", "{path}"],
95
+ categories: ["image"]
96
+ },
97
+ parser: {
98
+ kind: "jsonl",
99
+ text: {
100
+ path: "__preset__:codex.text"
101
+ },
102
+ usage: {
103
+ path: "__preset__:codex.usage"
104
+ },
105
+ error: {
106
+ path: "__preset__:codex.error"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ };
112
+ var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
113
+ "anthropic:claude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.claude.command,
114
+ "anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
115
+ "openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
116
+ "openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
117
+ "pi:pi-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.pi.command
118
+ };
119
+ function getCliTransportPreset(presetId) {
120
+ return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
121
+ }
122
+
1
123
  // src/errors.ts
2
124
  var AiConnectError = class extends Error {
3
125
  code;
@@ -345,9 +467,38 @@ function normalizeTransport(providerId, input) {
345
467
  ...fallback !== void 0 ? { fallback } : {}
346
468
  };
347
469
  })();
470
+ const fileInput = (() => {
471
+ const fi = descriptor.cli?.fileInput;
472
+ if (!fi) {
473
+ return void 0;
474
+ }
475
+ const validCategories = ["image", "document", "text", "other"];
476
+ if (fi.categories) {
477
+ for (const c of fi.categories) {
478
+ assert(
479
+ validCategories.includes(c),
480
+ `Unsupported CLI fileInput category "${String(c)}" for provider "${providerId}".`
481
+ );
482
+ }
483
+ }
484
+ const placement = fi.placement ?? "args";
485
+ assert(
486
+ placement === "args" || placement === "prompt",
487
+ `Unsupported CLI fileInput placement "${String(placement)}" for provider "${providerId}".`
488
+ );
489
+ return {
490
+ placement,
491
+ ...fi.perFileArgs ? { perFileArgs: fi.perFileArgs.map((part) => String(part)) } : {},
492
+ ...fi.mentionTemplate?.trim() ? { mentionTemplate: fi.mentionTemplate.trim() } : {},
493
+ ...fi.separator !== void 0 ? { separator: fi.separator } : {},
494
+ ...fi.categories ? { categories: [...fi.categories] } : {},
495
+ ...fi.stagingDir?.trim() ? { stagingDir: fi.stagingDir.trim() } : {}
496
+ };
497
+ })();
348
498
  return {
349
499
  ...normalized,
350
- ...discovery ? { discovery } : {}
500
+ ...discovery ? { discovery } : {},
501
+ ...fileInput ? { fileInput } : {}
351
502
  };
352
503
  })();
353
504
  const normalizedAuth = descriptor.auth?.methodId?.trim() ? {
@@ -383,7 +534,7 @@ function normalizeTransport(providerId, input) {
383
534
  };
384
535
  }
385
536
  if (descriptor.kind === "cli") {
386
- const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "gemini" ? "gemini-cli" : providerId === "qwen" ? "qwen-cli" : "cli";
537
+ const inferredId2 = providerId === "anthropic" ? "claude-cli" : providerId === "openclaude" ? "openclaude-cli" : providerId === "openai" ? "codex-cli" : providerId === "pi" ? "pi-cli" : "cli";
387
538
  return {
388
539
  kind: "cli",
389
540
  id: descriptor.id?.trim() || inferredId2,
@@ -400,7 +551,7 @@ function normalizeTransport(providerId, input) {
400
551
  ...descriptor.baseUrl?.trim() ? { baseUrl: descriptor.baseUrl.trim() } : {}
401
552
  };
402
553
  }
403
- const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" : providerId === "gemini" ? "gemini-acp" : "acp";
554
+ const inferredId = providerId === "anthropic" ? "claude-code-acp" : providerId === "openai" ? "codex-acp" : "acp";
404
555
  const normalizedLaunch = descriptor.launch ? (() => {
405
556
  const contextMode = descriptor.launch?.contextMode ?? "workspace";
406
557
  const skillsMode = descriptor.launch?.skillsMode ?? "default";
@@ -435,7 +586,40 @@ function normalizeRuntime(transport, runtime) {
435
586
  }
436
587
  return "universal";
437
588
  }
589
+ function cliPresetIdForTransportId(transportId) {
590
+ switch (transportId) {
591
+ case "pi-cli":
592
+ return "pi";
593
+ case "claude-cli":
594
+ return "claude";
595
+ case "openclaude-cli":
596
+ return "openclaude";
597
+ case "codex-cli":
598
+ return "codex";
599
+ default:
600
+ return void 0;
601
+ }
602
+ }
603
+ function cliFileInputCategories(transport) {
604
+ if (transport.kind !== "cli") {
605
+ return /* @__PURE__ */ new Set();
606
+ }
607
+ const presetId = transport.cli?.preset ?? cliPresetIdForTransportId(transport.id);
608
+ const presetFileInput = presetId ? AI_CONNECT_DEFAULT_CLI_PRESETS[presetId]?.options.fileInput : void 0;
609
+ const routeFileInput = transport.cli?.fileInput;
610
+ if (!presetFileInput && !routeFileInput) {
611
+ return /* @__PURE__ */ new Set();
612
+ }
613
+ const merged = {
614
+ ...presetFileInput ?? {},
615
+ ...routeFileInput ?? {}
616
+ };
617
+ return new Set(
618
+ merged.categories ?? ["image", "document", "text", "other"]
619
+ );
620
+ }
438
621
  function defaultCapabilities(transport, runtime) {
622
+ const cliFileCategories = cliFileInputCategories(transport);
439
623
  const localOnly = runtime === "local" || transport.kind === "acp" || transport.kind === "cli" || transport.kind === "server";
440
624
  const browserSafe = !localOnly;
441
625
  return {
@@ -450,10 +634,12 @@ function defaultCapabilities(transport, runtime) {
450
634
  supportsClientToolExecution: transport.kind === "api",
451
635
  // C6 / F-P-4: api routes now advertise document + image input parity with acp.
452
636
  // The per-account `capabilities` override remains the escape hatch for
453
- // text-only api proxies that must opt out.
454
- supportsFileUpload: transport.kind === "acp" || transport.kind === "api",
637
+ // text-only api proxies that must opt out. REQ-024: a cli route advertises
638
+ // document/image input ONLY for the categories its fileInput actually stages
639
+ // (category-aware so e.g. an image-only cli cleanly rejects a PDF pre-spawn).
640
+ supportsFileUpload: transport.kind === "acp" || transport.kind === "api" || cliFileCategories.has("document"),
455
641
  supportsFileOutput: transport.kind === "acp",
456
- supportsImageInput: transport.kind === "acp" || transport.kind === "api",
642
+ supportsImageInput: transport.kind === "acp" || transport.kind === "api" || cliFileCategories.has("image"),
457
643
  supportsImageOutput: transport.kind === "acp",
458
644
  supportsProfileVerification: true,
459
645
  supportsCredentialRotation: false,
@@ -1638,18 +1824,14 @@ var MODEL_REFERENCE = {
1638
1824
  key: "gemini-3.1-flash-lite",
1639
1825
  contextLength: 1048576
1640
1826
  },
1827
+ "gemini-3.1-pro": { key: "gemini-3.1-pro", contextLength: 1048576 },
1641
1828
  "gemini-3-pro": { key: "gemini-3-pro", contextLength: 2097152 },
1642
1829
  "auto-gemini-3": { key: "auto-gemini-3", contextLength: 1048576 },
1643
1830
  "gemini-2.5-flash": { key: "gemini-2.5-flash", contextLength: 1048576 },
1644
1831
  "gemini-2.5-pro": { key: "gemini-2.5-pro", contextLength: 2097152 },
1645
1832
  "gemini-2.0-flash": { key: "gemini-2.0-flash", contextLength: 1048576 },
1646
1833
  "gemini-1.5-flash": { key: "gemini-1.5-flash", contextLength: 1048576 },
1647
- "gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 },
1648
- // --- Qwen (catalog: qwen3-coder-plus) ---
1649
- "qwen3-coder-plus": { key: "qwen3-coder-plus", contextLength: 1048576 },
1650
- "qwen3-coder": { key: "qwen3-coder", contextLength: 262144 },
1651
- "qwen-max": { key: "qwen-max", contextLength: 32768 },
1652
- "qwen-plus": { key: "qwen-plus", contextLength: 131072 }
1834
+ "gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
1653
1835
  };
1654
1836
  function normalizeModelKey(model) {
1655
1837
  let key = model.trim().toLowerCase();
@@ -6340,144 +6522,9 @@ function createDefaultRouteHandlers(options = {}) {
6340
6522
  var AI_CONNECT_DEFAULT_ACP_COMMANDS = {
6341
6523
  "anthropic:claude-code-acp": "npx -y @agentclientprotocol/claude-agent-acp@^0.25.0",
6342
6524
  "openai:codex-acp": "npx @zed-industries/codex-acp@^0.11.1",
6343
- "gemini:gemini-acp": "gemini --acp",
6344
- "qwen:qwen-acp": "qwen --acp",
6345
6525
  "opencode:opencode-acp": "opencode acp"
6346
6526
  };
6347
6527
 
6348
- // src/cli-presets.ts
6349
- var AI_CONNECT_DEFAULT_CLI_PRESETS = {
6350
- gemini: {
6351
- id: "gemini",
6352
- label: "Gemini CLI",
6353
- command: "gemini",
6354
- transportId: "gemini-cli",
6355
- options: {
6356
- preset: "gemini",
6357
- argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
6358
- discovery: {
6359
- via: "acp",
6360
- acp: {
6361
- transportId: "gemini-acp"
6362
- }
6363
- },
6364
- parser: {
6365
- kind: "json",
6366
- textPath: "response",
6367
- usagePath: "stats",
6368
- errorPath: "error"
6369
- }
6370
- }
6371
- },
6372
- qwen: {
6373
- id: "qwen",
6374
- label: "Qwen CLI",
6375
- command: "qwen",
6376
- transportId: "qwen-cli",
6377
- options: {
6378
- preset: "qwen",
6379
- argsTemplate: ["-p", "{prompt}", "--output-format", "json", "--model", "{model}"],
6380
- discovery: {
6381
- via: "acp",
6382
- acp: {
6383
- transportId: "qwen-acp"
6384
- }
6385
- },
6386
- parser: {
6387
- kind: "json",
6388
- textPath: "__preset__:qwen.result",
6389
- usagePath: "__preset__:qwen.stats",
6390
- errorPath: "__preset__:qwen.error"
6391
- }
6392
- }
6393
- },
6394
- claude: {
6395
- id: "claude",
6396
- label: "Claude CLI",
6397
- command: "claude",
6398
- transportId: "claude-cli",
6399
- options: {
6400
- preset: "claude",
6401
- argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
6402
- discovery: {
6403
- via: "acp",
6404
- acp: {
6405
- transportId: "claude-code-acp"
6406
- }
6407
- },
6408
- parser: {
6409
- kind: "json",
6410
- textPath: "__preset__:claude.result",
6411
- usagePath: "usage",
6412
- errorPath: "__preset__:claude.error"
6413
- }
6414
- }
6415
- },
6416
- openclaude: {
6417
- id: "openclaude",
6418
- label: "OpenClaude CLI",
6419
- command: "openclaude",
6420
- transportId: "openclaude-cli",
6421
- options: {
6422
- preset: "openclaude",
6423
- argsTemplate: ["--print", "--output-format", "json", "--model", "{model}", "{prompt}"],
6424
- parser: {
6425
- kind: "json",
6426
- textPath: "__preset__:claude.result",
6427
- usagePath: "usage",
6428
- errorPath: "__preset__:claude.error"
6429
- }
6430
- }
6431
- },
6432
- codex: {
6433
- id: "codex",
6434
- label: "Codex CLI",
6435
- command: "codex",
6436
- transportId: "codex-cli",
6437
- options: {
6438
- preset: "codex",
6439
- argsTemplate: [
6440
- "exec",
6441
- "--json",
6442
- "--output-last-message",
6443
- "{output_file}",
6444
- "--model",
6445
- "{model}",
6446
- "{prompt}"
6447
- ],
6448
- discovery: {
6449
- via: "acp",
6450
- acp: {
6451
- transportId: "codex-acp"
6452
- }
6453
- },
6454
- parser: {
6455
- kind: "jsonl",
6456
- text: {
6457
- path: "__preset__:codex.text"
6458
- },
6459
- usage: {
6460
- path: "__preset__:codex.usage"
6461
- },
6462
- error: {
6463
- path: "__preset__:codex.error"
6464
- }
6465
- }
6466
- }
6467
- }
6468
- };
6469
- var AI_CONNECT_DEFAULT_CLI_COMMANDS = {
6470
- "anthropic:claude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.claude.command,
6471
- "anthropic:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
6472
- "openclaude:openclaude-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.openclaude.command,
6473
- "openai:codex-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.codex.command,
6474
- "gemini:gemini-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.gemini.command,
6475
- "qwen:qwen-cli": AI_CONNECT_DEFAULT_CLI_PRESETS.qwen.command
6476
- };
6477
- function getCliTransportPreset(presetId) {
6478
- return AI_CONNECT_DEFAULT_CLI_PRESETS[presetId];
6479
- }
6480
-
6481
6528
  // src/server-presets.ts
6482
6529
  var AI_CONNECT_DEFAULT_SERVER_PRESETS = {
6483
6530
  opencode: {
@@ -6594,52 +6641,22 @@ var TEXT_PROVIDER_CATALOG = [
6594
6641
  runtime: "universal",
6595
6642
  defaultModel: "gemini-3.1-flash-lite",
6596
6643
  defaultBaseUrl: ""
6597
- },
6598
- {
6599
- providerId: "gemini",
6600
- providerLabel: "Gemini",
6601
- transportKind: "cli",
6602
- transportId: "gemini-cli",
6603
- transportLabel: "Gemini CLI",
6604
- runtime: "local",
6605
- defaultModel: "gemini-2.5-flash",
6606
- defaultCommand: AI_CONNECT_DEFAULT_CLI_COMMANDS["gemini:gemini-cli"]
6607
- },
6608
- {
6609
- providerId: "gemini",
6610
- providerLabel: "Gemini",
6611
- transportKind: "acp",
6612
- transportId: "gemini-acp",
6613
- transportLabel: "Gemini ACP",
6614
- runtime: "local",
6615
- defaultModel: "auto-gemini-3",
6616
- defaultCommand: AI_CONNECT_DEFAULT_ACP_COMMANDS["gemini:gemini-acp"]
6617
6644
  }
6618
6645
  ]
6619
6646
  },
6620
6647
  {
6621
- providerId: "qwen",
6622
- label: "Qwen",
6648
+ providerId: "pi",
6649
+ label: "pi",
6623
6650
  transports: [
6624
6651
  {
6625
- providerId: "qwen",
6626
- providerLabel: "Qwen",
6652
+ providerId: "pi",
6653
+ providerLabel: "pi",
6627
6654
  transportKind: "cli",
6628
- transportId: "qwen-cli",
6629
- transportLabel: "Qwen CLI",
6655
+ transportId: "pi-cli",
6656
+ transportLabel: "pi CLI",
6630
6657
  runtime: "local",
6631
- defaultModel: "qwen3-coder-plus",
6632
- defaultCommand: AI_CONNECT_DEFAULT_CLI_COMMANDS["qwen:qwen-cli"]
6633
- },
6634
- {
6635
- providerId: "qwen",
6636
- providerLabel: "Qwen",
6637
- transportKind: "acp",
6638
- transportId: "qwen-acp",
6639
- transportLabel: "Qwen ACP",
6640
- runtime: "local",
6641
- defaultModel: "default",
6642
- defaultCommand: AI_CONNECT_DEFAULT_ACP_COMMANDS["qwen:qwen-acp"]
6658
+ defaultModel: "gemini-3.1-pro-low",
6659
+ defaultCommand: AI_CONNECT_DEFAULT_CLI_COMMANDS["pi:pi-cli"]
6643
6660
  }
6644
6661
  ]
6645
6662
  },