@lazyingart/agintiflow 0.18.0 → 0.19.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/src/cli.js CHANGED
@@ -1,7 +1,14 @@
1
1
  import { runAgent } from "./agent-runner.js";
2
2
  import { loadConfig } from "./config.js";
3
3
  import { listAgentWrappers } from "./tool-wrappers.js";
4
- import { getModelPresets } from "./model-routing.js";
4
+ import {
5
+ AUXILIARY_MODEL_CATALOG,
6
+ MODEL_PROVIDER_GROUPS,
7
+ PROVIDER_MODEL_CATALOG,
8
+ getModelPresets,
9
+ getModelRoleDefaults,
10
+ modelsForProviderGroup,
11
+ } from "./model-routing.js";
5
12
  import { getDockerSandboxStatus, runDockerPreflight } from "./docker-sandbox.js";
6
13
  import { buildCapabilityReport, printCapabilityReport } from "./capabilities.js";
7
14
  import { startInteractiveCli } from "./interactive-cli.js";
@@ -39,6 +46,17 @@ export function parseArgs(argv) {
39
46
  sessionId: "",
40
47
  provider: "",
41
48
  model: "",
49
+ routeProvider: "",
50
+ routeModel: "",
51
+ mainProvider: "",
52
+ mainModel: "",
53
+ spareProvider: "",
54
+ spareModel: "",
55
+ spareReasoning: "",
56
+ wrapperModel: "",
57
+ wrapperReasoning: "",
58
+ auxiliaryProvider: "",
59
+ auxiliaryModel: "",
42
60
  routingMode: "",
43
61
  commandCwd: "",
44
62
  sandboxMode: "",
@@ -57,6 +75,7 @@ export function parseArgs(argv) {
57
75
  headless: undefined,
58
76
  maxSteps: undefined,
59
77
  listRoutes: false,
78
+ listModels: false,
60
79
  listWrappers: false,
61
80
  sandboxStatus: false,
62
81
  sandboxPreflight: false,
@@ -116,6 +135,61 @@ export function parseArgs(argv) {
116
135
  i += 1;
117
136
  continue;
118
137
  }
138
+ if (arg === "--route-provider") {
139
+ result.routeProvider = readOption(argv, i);
140
+ i += 1;
141
+ continue;
142
+ }
143
+ if (arg === "--route-model") {
144
+ result.routeModel = readOption(argv, i);
145
+ i += 1;
146
+ continue;
147
+ }
148
+ if (arg === "--main-provider") {
149
+ result.mainProvider = readOption(argv, i);
150
+ i += 1;
151
+ continue;
152
+ }
153
+ if (arg === "--main-model") {
154
+ result.mainModel = readOption(argv, i);
155
+ i += 1;
156
+ continue;
157
+ }
158
+ if (arg === "--spare-provider") {
159
+ result.spareProvider = readOption(argv, i);
160
+ i += 1;
161
+ continue;
162
+ }
163
+ if (arg === "--spare-model") {
164
+ result.spareModel = readOption(argv, i);
165
+ i += 1;
166
+ continue;
167
+ }
168
+ if (arg === "--spare-reasoning") {
169
+ result.spareReasoning = readOption(argv, i);
170
+ i += 1;
171
+ continue;
172
+ }
173
+ if (arg === "--wrapper-model") {
174
+ result.wrapperModel = readOption(argv, i);
175
+ i += 1;
176
+ continue;
177
+ }
178
+ if (arg === "--wrapper-reasoning") {
179
+ result.wrapperReasoning = readOption(argv, i);
180
+ i += 1;
181
+ continue;
182
+ }
183
+ if (arg === "--aux-provider" || arg === "--auxiliary-provider") {
184
+ result.auxiliaryProvider = readOption(argv, i);
185
+ i += 1;
186
+ continue;
187
+ }
188
+ if (arg === "--aux-model" || arg === "--auxiliary-model") {
189
+ result.auxiliaryModel = readOption(argv, i);
190
+ i += 1;
191
+ continue;
192
+ }
119
193
  if (arg === "--routing") {
120
194
  result.routingMode = readOption(argv, i);
121
195
  i += 1;
@@ -236,6 +310,10 @@ export function parseArgs(argv) {
236
310
  result.listRoutes = true;
237
311
  continue;
238
312
  }
313
+ if (arg === "--list-models" || arg === "--models") {
314
+ result.listModels = true;
315
+ continue;
316
+ }
239
317
  if (arg === "--list-wrappers") {
240
318
  result.listWrappers = true;
241
319
  continue;
@@ -267,7 +345,7 @@ export function parseArgs(argv) {
267
345
 
268
346
  function printUsage() {
269
347
  console.log(
270
- 'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti skills [query] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti login [deepseek|openai|qwen|venice|grsai] OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex] [--list-skills] [--sandbox-status|--sandbox-preflight] "your task"'
348
+ 'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti models OR aginti skills [query] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
271
349
  );
272
350
  }
273
351
 
@@ -316,6 +394,33 @@ function printRoutes() {
316
394
  }
317
395
  }
318
396
 
397
+ function printModels() {
398
+ const roles = getModelRoleDefaults();
399
+ console.log("Model roles:");
400
+ for (const role of Object.values(roles)) {
401
+ const reasoning = role.reasoning ? ` reasoning=${role.reasoning}` : "";
402
+ console.log(` ${role.command}: ${role.provider}/${role.model}${reasoning} - ${role.description}`);
403
+ }
404
+ console.log("");
405
+ console.log("Provider groups:");
406
+ for (const [id, group] of Object.entries(MODEL_PROVIDER_GROUPS)) {
407
+ const models = modelsForProviderGroup(id)
408
+ .map((item) => item.id)
409
+ .join(", ");
410
+ console.log(` ${id}: provider=${group.provider} role=${group.role} models=${models || "(none)"}`);
411
+ }
412
+ console.log("");
413
+ console.log("Auxiliary image groups:");
414
+ for (const [id, models] of Object.entries(AUXILIARY_MODEL_CATALOG)) {
415
+ console.log(` ${id}: ${models.map((item) => item.id).join(", ")}`);
416
+ }
417
+ console.log("");
418
+ console.log("Direct provider models:");
419
+ for (const [provider, models] of Object.entries(PROVIDER_MODEL_CATALOG)) {
420
+ console.log(` ${provider}: ${models.map((item) => item.id).join(", ")}`);
421
+ }
422
+ }
423
+
319
424
  function printWrappers() {
320
425
  for (const wrapper of listAgentWrappers()) {
321
426
  console.log(`${wrapper.name}: ${wrapper.available ? "available" : "missing"} - ${wrapper.role}`);
@@ -580,6 +685,11 @@ export async function main(argv = process.argv.slice(2)) {
580
685
  return;
581
686
  }
582
687
 
688
+ if (argv[0] === "models" || argv[0] === "model") {
689
+ printModels();
690
+ return;
691
+ }
692
+
583
693
  if (argv[0] === "skills" || argv[0] === "skill") {
584
694
  printSkills(argv.slice(1).join(" ").trim());
585
695
  return;
@@ -633,6 +743,11 @@ export async function main(argv = process.argv.slice(2)) {
633
743
  return;
634
744
  }
635
745
 
746
+ if (args.listModels) {
747
+ printModels();
748
+ return;
749
+ }
750
+
636
751
  if (args.listWrappers) {
637
752
  printWrappers();
638
753
  return;
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import crypto from "node:crypto";
3
- import { getProviderDefaults, normalizeRoutingMode, selectModelRoute } from "./model-routing.js";
3
+ import { getModelRoleDefaults, getProviderDefaults, normalizeRoutingMode, selectModelRoute } from "./model-routing.js";
4
4
  import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./command-policy.js";
5
5
  import { normalizeWrapperName } from "./tool-wrappers.js";
6
6
  import { loadProjectEnv, resolveProjectRoot } from "./project.js";
@@ -53,6 +53,23 @@ export function resolveRuntimeConfig(args, overrides = {}) {
53
53
  model: overrides.model || args.model || process.env.LLM_MODEL || "",
54
54
  goal: args.goal || "",
55
55
  taskProfile,
56
+ routeProvider: overrides.routeProvider || args.routeProvider || process.env.AGINTI_ROUTE_PROVIDER || "",
57
+ routeModel: overrides.routeModel || args.routeModel || process.env.AGINTI_ROUTE_MODEL || "",
58
+ mainProvider: overrides.mainProvider || args.mainProvider || process.env.AGINTI_MAIN_PROVIDER || "",
59
+ mainModel: overrides.mainModel || args.mainModel || process.env.AGINTI_MAIN_MODEL || "",
60
+ });
61
+ const modelRoles = getModelRoleDefaults({
62
+ routeProvider: overrides.routeProvider || args.routeProvider || process.env.AGINTI_ROUTE_PROVIDER || "",
63
+ routeModel: overrides.routeModel || args.routeModel || process.env.AGINTI_ROUTE_MODEL || "",
64
+ mainProvider: overrides.mainProvider || args.mainProvider || process.env.AGINTI_MAIN_PROVIDER || "",
65
+ mainModel: overrides.mainModel || args.mainModel || process.env.AGINTI_MAIN_MODEL || "",
66
+ spareProvider: overrides.spareProvider || args.spareProvider || process.env.AGINTI_SPARE_PROVIDER || "",
67
+ spareModel: overrides.spareModel || args.spareModel || process.env.AGINTI_SPARE_MODEL || "",
68
+ spareReasoning: overrides.spareReasoning || args.spareReasoning || process.env.AGINTI_SPARE_REASONING || "",
69
+ wrapperModel: overrides.wrapperModel || args.wrapperModel || process.env.AGINTI_WRAPPER_MODEL || "",
70
+ wrapperReasoning: overrides.wrapperReasoning || args.wrapperReasoning || process.env.AGINTI_WRAPPER_REASONING || "",
71
+ auxiliaryProvider: overrides.auxiliaryProvider || args.auxiliaryProvider || process.env.AGINTI_AUX_PROVIDER || "",
72
+ auxiliaryModel: overrides.auxiliaryModel || args.auxiliaryModel || process.env.AGINTI_AUX_MODEL || "",
56
73
  });
57
74
 
58
75
  const defaults = getProviderDefaults(route.provider);
@@ -79,6 +96,18 @@ export function resolveRuntimeConfig(args, overrides = {}) {
79
96
  taskProfile,
80
97
  routeReason: route.reason,
81
98
  routeComplexityScore: route.complexityScore,
99
+ modelRoles,
100
+ routeProvider: modelRoles.route.provider,
101
+ routeModel: modelRoles.route.model,
102
+ mainProvider: modelRoles.main.provider,
103
+ mainModel: modelRoles.main.model,
104
+ spareProvider: modelRoles.spare.provider,
105
+ spareModel: modelRoles.spare.model,
106
+ spareReasoning: modelRoles.spare.reasoning,
107
+ wrapperModel: modelRoles.wrapper.model,
108
+ wrapperReasoning: modelRoles.wrapper.reasoning,
109
+ auxiliaryProvider: modelRoles.auxiliary.provider,
110
+ auxiliaryModel: modelRoles.auxiliary.model,
82
111
  requestedProvider,
83
112
  requestedModel: overrides.model || args.model || process.env.LLM_MODEL || "",
84
113
  provider: route.provider,
@@ -10,6 +10,13 @@ import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
10
10
  import { normalizeAuthProvider, runAuthWizard, shouldPromptForDeepSeek } from "./auth-onboarding.js";
11
11
  import { SessionStore } from "./session-store.js";
12
12
  import { listSkills, selectSkillsForGoal } from "./skill-library.js";
13
+ import {
14
+ AUXILIARY_MODEL_CATALOG,
15
+ MODEL_PROVIDER_GROUPS,
16
+ PROVIDER_MODEL_CATALOG,
17
+ getModelRoleDefaults,
18
+ modelsForProviderGroup,
19
+ } from "./model-routing.js";
13
20
 
14
21
  const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
15
22
  const ansi = {
@@ -59,6 +66,11 @@ const SLASH_COMMANDS = [
59
66
  "/profile",
60
67
  "/web-search",
61
68
  "/scouts",
69
+ "/models",
70
+ "/route",
71
+ "/main",
72
+ "/spare",
73
+ "/wrapper",
62
74
  "/routing",
63
75
  "/provider",
64
76
  "/model",
@@ -560,8 +572,16 @@ function printHelp() {
560
572
  " /auth [deepseek|openai|qwen|venice|grsai] Alias for /login.",
561
573
  " /instructions Show AGINTI.md project instructions status.",
562
574
  " /memory Alias for /instructions.",
563
- " /auxilliary [status|grsai|on|off|image]",
564
- " Manage optional auxiliary skills, including GRS AI image generation.",
575
+ " /models Show route/main/spare/wrapper/auxiliary model roles.",
576
+ " /route <mode|provider/model>",
577
+ " Set routing mode or fast route model, e.g. /route deepseek/deepseek-v4-flash.",
578
+ " /model <provider/model> Set the active/main model, e.g. /model deepseek/deepseek-v4-pro.",
579
+ " /spare <provider/model> [reasoning]",
580
+ " Set spare model, e.g. /spare openai/gpt-5.4 medium.",
581
+ " /wrapper [on|off|codex model reasoning]",
582
+ " Configure optional external wrapper.",
583
+ " /auxilliary [status|grsai|venice|model grsai/nano-banana-2|on|off|image]",
584
+ " Manage optional auxiliary skills, including image generation.",
565
585
  " /new Start a fresh session on the next message.",
566
586
  " /resume <session-id> Continue a saved session.",
567
587
  " /sessions List recent sessions in this project.",
@@ -571,7 +591,6 @@ function printHelp() {
571
591
  " /scouts on|off|<1-10> Enable parallel DeepSeek scouts and set scout count.",
572
592
  " /routing <mode> Set routing: smart, fast, complex, manual.",
573
593
  " /provider <name> Set provider: deepseek, openai, qwen, venice, mock.",
574
- " /model <name> Set an explicit model, or /model auto.",
575
594
  " /docker on Use docker-workspace with approved package installs.",
576
595
  " /docker off Use host shell policy.",
577
596
  " /latex on Use the LaTeX/PDF profile in Docker with a larger step budget.",
@@ -1357,6 +1376,11 @@ function printStatus(state) {
1357
1376
  printSystemLine(`status=${state.status || "idle"}${state.activeGoal ? ` workingOn=${state.activeGoal}` : ""}`);
1358
1377
  if (state.lastEvent) printSystemLine(`last=${state.lastEvent}`);
1359
1378
  printSystemLine(`provider=${state.provider || "auto"} routing=${state.routingMode} model=${state.model || "auto"}`);
1379
+ printSystemLine(
1380
+ `roles route=${state.routeProvider || "deepseek"}/${state.routeModel || "deepseek-v4-flash"} main=${
1381
+ state.mainProvider || "deepseek"
1382
+ }/${state.mainModel || "deepseek-v4-pro"} spare=${state.spareProvider || "openai"}/${state.spareModel || "gpt-5.4"}`
1383
+ );
1360
1384
  printSystemLine(`profile=${state.taskProfile} maxSteps=${state.maxSteps}`);
1361
1385
  printSystemLine(
1362
1386
  `shell=${state.allowShellTool} files=${state.allowFileTools} webSearch=${state.allowWebSearch} scouts=${state.allowParallelScouts}:${state.parallelScoutCount} auxiliary=${state.allowAuxiliaryTools} sandbox=${state.sandboxMode} installs=${state.packageInstallPolicy}`
@@ -1480,6 +1504,17 @@ function createState(args = {}) {
1480
1504
  allowWrapperTools: args.allowWrapperTools ?? false,
1481
1505
  allowDestructive: args.allowDestructive ?? false,
1482
1506
  preferredWrapper: args.preferredWrapper || "codex",
1507
+ routeProvider: args.routeProvider || "",
1508
+ routeModel: args.routeModel || "",
1509
+ mainProvider: args.mainProvider || "",
1510
+ mainModel: args.mainModel || "",
1511
+ spareProvider: args.spareProvider || "openai",
1512
+ spareModel: args.spareModel || "gpt-5.4",
1513
+ spareReasoning: args.spareReasoning || "medium",
1514
+ wrapperModel: args.wrapperModel || "gpt-5.5",
1515
+ wrapperReasoning: args.wrapperReasoning || "medium",
1516
+ auxiliaryProvider: args.auxiliaryProvider || "grsai",
1517
+ auxiliaryModel: args.auxiliaryModel || "nano-banana-2",
1483
1518
  taskProfile: normalizeTaskProfile(args.taskProfile || "auto"),
1484
1519
  headless: args.headless ?? false,
1485
1520
  maxSteps:
@@ -1490,6 +1525,81 @@ function createState(args = {}) {
1490
1525
  };
1491
1526
  }
1492
1527
 
1528
+ function parseProviderModel(value, fallbackProvider = "") {
1529
+ const text = String(value || "").trim();
1530
+ if (!text) return { provider: fallbackProvider, model: "" };
1531
+ if (text.includes("/")) {
1532
+ const [provider, ...modelParts] = text.split("/");
1533
+ return {
1534
+ provider: provider.trim() || fallbackProvider,
1535
+ model: modelParts.join("/").trim(),
1536
+ };
1537
+ }
1538
+ const parts = text.split(/\s+/).filter(Boolean);
1539
+ if (parts.length >= 2 && ["deepseek", "openai", "qwen", "venice", "mock", "grsai", "venice-image"].includes(parts[0])) {
1540
+ return { provider: parts[0], model: parts.slice(1).join(" ") };
1541
+ }
1542
+ return { provider: fallbackProvider, model: text };
1543
+ }
1544
+
1545
+ function printModelRoles(state) {
1546
+ const roles = getModelRoleDefaults({
1547
+ routeProvider: state.routeProvider,
1548
+ routeModel: state.routeModel,
1549
+ mainProvider: state.mainProvider,
1550
+ mainModel: state.mainModel,
1551
+ spareProvider: state.spareProvider,
1552
+ spareModel: state.spareModel,
1553
+ spareReasoning: state.spareReasoning,
1554
+ wrapperModel: state.wrapperModel,
1555
+ wrapperReasoning: state.wrapperReasoning,
1556
+ auxiliaryProvider: state.auxiliaryProvider,
1557
+ auxiliaryModel: state.auxiliaryModel,
1558
+ });
1559
+ const roleLines = Object.values(roles).map((role) => {
1560
+ const reasoning = role.reasoning ? ` reasoning=${role.reasoning}` : "";
1561
+ return `${role.command.padEnd(12)} ${role.provider}/${role.model}${reasoning}\n ${role.description}`;
1562
+ });
1563
+ const groups = Object.entries(MODEL_PROVIDER_GROUPS).map(([id, group]) => {
1564
+ const models = modelsForProviderGroup(id)
1565
+ .map((item) => item.id)
1566
+ .join(", ");
1567
+ return `${id}: ${group.role}${models ? ` (${models})` : ""}`;
1568
+ });
1569
+ const auxiliary = Object.entries(AUXILIARY_MODEL_CATALOG).map(
1570
+ ([id, models]) => `${id}: ${models.map((item) => item.id).join(", ")}`
1571
+ );
1572
+ const directProviders = Object.entries(PROVIDER_MODEL_CATALOG).map(([provider, models]) => {
1573
+ const compact = models
1574
+ .slice(0, 6)
1575
+ .map((item) => item.id)
1576
+ .join(", ");
1577
+ return `${provider}: ${compact}${models.length > 6 ? ", ..." : ""}`;
1578
+ });
1579
+ printAgentMessage(
1580
+ [
1581
+ "Model roles",
1582
+ ...roleLines,
1583
+ "",
1584
+ "Set roles",
1585
+ "/route deepseek/deepseek-v4-flash",
1586
+ "/model deepseek/deepseek-v4-pro",
1587
+ "/spare openai/gpt-5.4 medium",
1588
+ "/wrapper codex gpt-5.5 medium",
1589
+ "/auxilliary model grsai/nano-banana-2",
1590
+ "",
1591
+ "Provider groups",
1592
+ ...groups,
1593
+ "",
1594
+ "Auxiliary image groups",
1595
+ ...auxiliary,
1596
+ "",
1597
+ "Direct provider models",
1598
+ ...directProviders,
1599
+ ].join("\n")
1600
+ );
1601
+ }
1602
+
1493
1603
  async function maybeOnboardDeepSeekKey(state) {
1494
1604
  if (!shouldPromptForDeepSeek(state, process.cwd())) return;
1495
1605
 
@@ -1591,7 +1701,9 @@ async function handleCommand(line, state, packageDir) {
1591
1701
  [
1592
1702
  `Auxiliary tools: ${state.allowAuxiliaryTools ? "on" : "off"}`,
1593
1703
  `Image generation: ${keys.grsai ? "GRSAI key available" : "missing GRSAI key"}`,
1594
- "Use `/auxilliary grsai` to paste the image key, `/auxilliary image` to switch to the image profile, or `/auxilliary off` to hide auxiliary tools.",
1704
+ `Venice image: ${keys.venice ? "Venice key available" : "missing Venice key"}`,
1705
+ `Selected auxiliary: ${state.auxiliaryProvider || "grsai"}/${state.auxiliaryModel || "nano-banana-2"}`,
1706
+ "Use `/auxilliary grsai` or `/auxilliary venice` to paste a key, `/auxilliary model grsai/nano-banana-2`, `/auxilliary image`, or `/auxilliary off`.",
1595
1707
  ].join("\n")
1596
1708
  );
1597
1709
  return true;
@@ -1612,11 +1724,18 @@ async function handleCommand(line, state, packageDir) {
1612
1724
  printSystemLine("auxiliary=on profile=image");
1613
1725
  return true;
1614
1726
  }
1615
- if (["grsai", "login", "key", "token"].includes(action)) {
1616
- await promptAndSaveProviderKey("grsai", state);
1727
+ if (action.startsWith("model")) {
1728
+ const selected = parseProviderModel(action.replace(/^model\s*/, ""), state.auxiliaryProvider || "grsai");
1729
+ state.auxiliaryProvider = selected.provider || "grsai";
1730
+ state.auxiliaryModel = selected.model || state.auxiliaryModel || "nano-banana-2";
1731
+ printSystemLine(`auxiliary=${state.auxiliaryProvider}/${state.auxiliaryModel}`);
1732
+ return true;
1733
+ }
1734
+ if (["grsai", "venice", "login", "key", "token"].includes(action)) {
1735
+ await promptAndSaveProviderKey(action === "venice" ? "venice" : "grsai", state);
1617
1736
  return true;
1618
1737
  }
1619
- printAgentMessage("Usage: /auxilliary [status|grsai|on|off|image]");
1738
+ printAgentMessage("Usage: /auxilliary [status|grsai|venice|model grsai/nano-banana-2|on|off|image]");
1620
1739
  return true;
1621
1740
  }
1622
1741
  if (command === "new") {
@@ -1702,6 +1821,89 @@ async function handleCommand(line, state, packageDir) {
1702
1821
  printSystemLine(`parallelScouts=${state.allowParallelScouts ? "on" : "off"} count=${state.parallelScoutCount}`);
1703
1822
  return true;
1704
1823
  }
1824
+ if (command === "models") {
1825
+ printModelRoles(state);
1826
+ return true;
1827
+ }
1828
+ if (command === "route") {
1829
+ if (!value) {
1830
+ printAgentMessage(
1831
+ `Route model: ${state.routeProvider || "deepseek"}/${state.routeModel || "deepseek-v4-flash"}\nUse /route deepseek/deepseek-v4-flash or /route fast|smart|complex.`
1832
+ );
1833
+ return true;
1834
+ }
1835
+ if (["smart", "fast", "complex", "manual"].includes(value)) {
1836
+ state.routingMode = value;
1837
+ printSystemLine(`routing=${state.routingMode}`);
1838
+ return true;
1839
+ }
1840
+ const selected = parseProviderModel(value, state.routeProvider || "deepseek");
1841
+ state.routeProvider = selected.provider || "deepseek";
1842
+ state.routeModel = selected.model || state.routeModel || "deepseek-v4-flash";
1843
+ printSystemLine(`route=${state.routeProvider}/${state.routeModel}`);
1844
+ return true;
1845
+ }
1846
+ if (command === "main" || command === "model") {
1847
+ if (!value || value === "auto") {
1848
+ if (command === "main") {
1849
+ state.mainProvider = "";
1850
+ state.mainModel = "";
1851
+ printSystemLine("main=auto");
1852
+ } else {
1853
+ state.model = "";
1854
+ printSystemLine("model=auto");
1855
+ }
1856
+ return true;
1857
+ }
1858
+ const selected = parseProviderModel(value, state.mainProvider || state.provider || "deepseek");
1859
+ if (command === "main" || selected.provider) {
1860
+ state.mainProvider = selected.provider || "deepseek";
1861
+ state.mainModel = selected.model || "deepseek-v4-pro";
1862
+ if (command === "model") {
1863
+ state.provider = state.mainProvider;
1864
+ state.model = state.mainModel;
1865
+ }
1866
+ printSystemLine(`main=${state.mainProvider}/${state.mainModel}`);
1867
+ return true;
1868
+ }
1869
+ state.model = value;
1870
+ printSystemLine(`model=${state.model || "auto"}`);
1871
+ return true;
1872
+ }
1873
+ if (command === "spare") {
1874
+ if (!value) {
1875
+ printAgentMessage(`Spare model: ${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
1876
+ return true;
1877
+ }
1878
+ const parts = value.split(/\s+/).filter(Boolean);
1879
+ const selected = parseProviderModel(parts[0] || "", state.spareProvider || "openai");
1880
+ state.spareProvider = selected.provider || "openai";
1881
+ state.spareModel = selected.model || state.spareModel || "gpt-5.4";
1882
+ if (parts[1]) state.spareReasoning = parts[1];
1883
+ printSystemLine(`spare=${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
1884
+ return true;
1885
+ }
1886
+ if (command === "wrapper") {
1887
+ if (!value) {
1888
+ printAgentMessage(
1889
+ `Wrapper: ${state.preferredWrapper || "codex"} model=${state.wrapperModel || "gpt-5.5"} reasoning=${
1890
+ state.wrapperReasoning || "medium"
1891
+ } tools=${state.allowWrapperTools ? "on" : "off"}`
1892
+ );
1893
+ return true;
1894
+ }
1895
+ const parts = value.split(/\s+/).filter(Boolean);
1896
+ if (parts[0] === "on" || parts[0] === "off") {
1897
+ state.allowWrapperTools = parts[0] === "on";
1898
+ printSystemLine(`wrappers=${state.allowWrapperTools ? "on" : "off"}`);
1899
+ return true;
1900
+ }
1901
+ state.preferredWrapper = parts[0] || state.preferredWrapper || "codex";
1902
+ if (parts[1]) state.wrapperModel = parts[1];
1903
+ if (parts[2]) state.wrapperReasoning = parts[2];
1904
+ printSystemLine(`wrapper=${state.preferredWrapper} model=${state.wrapperModel} reasoning=${state.wrapperReasoning}`);
1905
+ return true;
1906
+ }
1705
1907
  if (command === "routing") {
1706
1908
  state.routingMode = value || "smart";
1707
1909
  printSystemLine(`routing=${state.routingMode}`);
@@ -1712,11 +1914,6 @@ async function handleCommand(line, state, packageDir) {
1712
1914
  printSystemLine(`provider=${state.provider || "auto"}`);
1713
1915
  return true;
1714
1916
  }
1715
- if (command === "model") {
1716
- state.model = value === "auto" ? "" : value;
1717
- printSystemLine(`model=${state.model || "auto"}`);
1718
- return true;
1719
- }
1720
1917
  if (command === "installs") {
1721
1918
  state.packageInstallPolicy = normalizePackageInstallPolicy(value || "prompt");
1722
1919
  printSystemLine(`installs=${state.packageInstallPolicy}`);
@@ -1803,6 +2000,17 @@ async function runPrompt(prompt, state, packageDir) {
1803
2000
  allowWrapperTools: state.allowWrapperTools,
1804
2001
  allowDestructive: state.allowDestructive,
1805
2002
  preferredWrapper: state.preferredWrapper,
2003
+ routeProvider: state.routeProvider,
2004
+ routeModel: state.routeModel,
2005
+ mainProvider: state.mainProvider,
2006
+ mainModel: state.mainModel,
2007
+ spareProvider: state.spareProvider,
2008
+ spareModel: state.spareModel,
2009
+ spareReasoning: state.spareReasoning,
2010
+ wrapperModel: state.wrapperModel,
2011
+ wrapperReasoning: state.wrapperReasoning,
2012
+ auxiliaryProvider: state.auxiliaryProvider,
2013
+ auxiliaryModel: state.auxiliaryModel,
1806
2014
  taskProfile: state.taskProfile,
1807
2015
  maxSteps: runMaxSteps,
1808
2016
  headless: state.headless,