@lazyingart/agintiflow 0.20.13 → 0.20.14

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.
@@ -48,7 +48,25 @@ Interactive commands:
48
48
 
49
49
  In the interactive CLI, `/provider`, `/route`, `/model`, `/spare`, and `/auxiliary model` without arguments open selectors. Use Up/Down/Left/Right to move through choices, Enter to confirm, and Esc to cancel. Slash-command hints use the same arrow selection behavior: type a prefix such as `/mo`, use arrows to choose `/model` or `/models`, then press Enter or Tab.
50
50
 
51
- `/route`, `/model`, and `/spare` intentionally share the same text-model selector so users do not need to learn three different catalogs. The shared list is grouped as DeepSeek, Venice Uncensored, Venice GPT/Claude/Gemma/Qwen, OpenAI, Qwen, and Mock. OpenAI entries show the recommended default reasoning effort in the description; `/spare` stores that reasoning value when selected.
51
+ `/route`, `/model`, and `/spare` intentionally share the same text-model selector so users do not need to learn three different catalogs. The selector is hierarchical:
52
+
53
+ 1. Choose a provider family.
54
+ 2. Choose the model in that family.
55
+ 3. For OpenAI models, choose reasoning effort: `low`, `medium`, `high`, or `xhigh`.
56
+
57
+ Provider families:
58
+
59
+ | Family | Models |
60
+ | --- | --- |
61
+ | DeepSeek | `deepseek-v4-flash`, `deepseek-v4-pro` |
62
+ | Venice | `venice-uncensored-1-2`, `venice-uncensored`, `gemma-4-uncensored` |
63
+ | OpenAI | `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex`, `gpt-5.3-codex-spark`, `gpt-5.2`; each has low/medium/high/xhigh reasoning |
64
+ | Venice GPT | GPT-family Venice routes such as `openai-gpt-55`, `openai-gpt-54`, `openai-gpt-54-mini`, `openai-gpt-53-codex`, `openai-gpt-52` |
65
+ | Venice Gemma | Gemma instruct routes such as `google-gemma-4-31b-it`, `google-gemma-4-26b-a4b-it`, `google-gemma-3-27b-it` |
66
+ | Venice Claude | Claude Sonnet/Opus routes such as `claude-sonnet-4-6`, `claude-opus-4-7`, `claude-opus-4-6` |
67
+ | Venice Qwen | Qwen routes such as `qwen3-6-27b`, `qwen-3-6-plus`, `qwen3-coder-480b-a35b-instruct-turbo` |
68
+ | Qwen | `qwen-plus`, `qwen-turbo`, `qwen-max` |
69
+ | Mock | `mock-agent` |
52
70
 
53
71
  `/venice` opens a two-step selector for the Venice route and main models. The current text choices are:
54
72
 
@@ -77,14 +95,21 @@ It keeps smart routing enabled. If the Venice key is missing, run `/auth venice`
77
95
  | `deepseek` | DeepSeek | Default route/main because V4 Flash and V4 Pro are cheap and strong. |
78
96
  | `openai` | OpenAI | Spare/frontier checks, Codex-family work, and explicit manual routes. |
79
97
  | `qwen` | Qwen | Chinese and general-purpose OpenAI-compatible tasks. |
80
- | `venice-uncensored` | Venice | Venice-native uncensored text models. |
98
+ | `venice` | Venice | Venice-native text shortcuts: Venice 1.2, Venice 1.1, and Gemma 4 Uncensored. |
81
99
  | `venice-gpt` | Venice | GPT-family models through Venice. |
82
100
  | `venice-claude` | Venice | Claude-family models through Venice. |
83
- | `venice-gemma` | Venice | Gemma-family models through Venice. |
101
+ | `venice-gemma` | Venice | Gemma instruct models through Venice, excluding the Gemma 4 Uncensored shortcut. |
84
102
  | `venice-qwen` | Venice | Qwen-family models through Venice. |
85
103
  | `venice-image` | Venice | Image generation/editing such as Nano Banana, GPT Image, Wan, Qwen Image. |
86
104
  | `grsai` | GRS AI | Auxiliary image generation only. |
87
105
 
106
+ `/auxiliary model` uses the same two-level pattern for image tools:
107
+
108
+ | Family | Models |
109
+ | --- | --- |
110
+ | GRS AI | `nano-banana-2`, `nano-banana-2-edit`, `gpt-image-2`, `gpt-image-2-edit` when the configured GRS AI-compatible endpoint supports them |
111
+ | Venice Image | `wan-2-7-pro-edit`, `nano-banana-2`, `gpt-image-2`, `grok-imagine-image`, `qwen-image-2-pro`, `bria-bg-remover`, `recraft-v4`, `flux-2-pro`, and related image/edit routes |
112
+
88
113
  ## Keys
89
114
 
90
115
  Keys are set per project and stored only in ignored `.aginti/.env`:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.13",
3
+ "version": "0.20.14",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a web-first coding agent and CLI with DeepSeek routing, sandboxed tools, model providers, canvas artifacts, and optional wrappers.",
6
6
  "license": "Apache-2.0",
package/public/app.js CHANGED
@@ -989,7 +989,7 @@ function fieldValue(field) {
989
989
  }
990
990
 
991
991
  function providerModelOptions(provider = providerField.value) {
992
- return modelCatalog[provider] || [];
992
+ return (modelCatalog[provider] || []).filter((item) => !item.hidden);
993
993
  }
994
994
 
995
995
  function mergeModelOptions(...groups) {
@@ -109,9 +109,14 @@ const fastRoute = selectModelRoute({
109
109
  assert(fastRoute.model === "deepseek-v4-flash", "fast route did not use route model override");
110
110
 
111
111
  assert(MODEL_PROVIDER_GROUPS["venice-gpt"].provider === "venice", "venice-gpt group missing");
112
- assert(modelsForProviderGroup("venice-gemma").some((item) => item.id === "gemma-4-uncensored"), "venice-gemma bucket missing Gemma");
113
- assert(modelsForProviderGroup("venice-uncensored").some((item) => item.id === "e2ee-venice-uncensored-24b-p"), "venice-uncensored bucket missing Venice 1.1");
112
+ assert(modelsForProviderGroup("venice").some((item) => item.id === "venice-uncensored-1-2"), "venice group missing Venice 1.2");
113
+ assert(modelsForProviderGroup("venice-gemma").some((item) => item.id === "google-gemma-4-31b-it"), "venice-gemma bucket missing Gemma 4 instruct");
114
+ assert(!modelsForProviderGroup("venice-gemma").some((item) => item.id === "gemma-4-uncensored"), "venice-gemma bucket should not include Gemma 4 Uncensored shortcut");
115
+ assert(modelsForProviderGroup("venice-gpt").some((item) => item.id === "openai-gpt-54-mini"), "venice-gpt bucket missing GPT-5.4 Mini");
116
+ assert(modelsForProviderGroup("venice-claude").some((item) => item.id === "claude-opus-4-7"), "venice-claude bucket missing Claude Opus 4.7");
117
+ assert(modelsForProviderGroup("venice-qwen").some((item) => item.id === "qwen3-coder-480b-a35b-instruct-turbo"), "venice-qwen bucket missing Qwen Coder");
114
118
  assert(AUXILIARY_MODEL_CATALOG["venice-image"].some((item) => item.id === "gpt-image-2"), "Venice image catalog missing GPT Image 2");
119
+ assert(AUXILIARY_MODEL_CATALOG["venice-image"].some((item) => item.id === "wan-2-7-pro-edit"), "Venice image catalog missing Wan edit");
115
120
  const routeChoices = modelRoleChoices("route").map((item) => `${item.provider}/${item.model}`);
116
121
  const mainChoices = modelRoleChoices("main").map((item) => `${item.provider}/${item.model}`);
117
122
  const spareChoices = modelRoleChoices("spare").map((item) => `${item.provider}/${item.model}`);
@@ -123,9 +128,14 @@ for (const expected of [
123
128
  "venice/venice-uncensored-1-2",
124
129
  "venice/venice-uncensored",
125
130
  "venice/gemma-4-uncensored",
131
+ "venice/google-gemma-4-31b-it",
132
+ "venice/google-gemma-4-26b-a4b-it",
126
133
  "venice/openai-gpt-55",
134
+ "venice/openai-gpt-54-mini",
127
135
  "venice/claude-sonnet-4-6",
136
+ "venice/claude-opus-4-7",
128
137
  "venice/qwen3-6-27b",
138
+ "venice/qwen3-coder-480b-a35b-instruct-turbo",
129
139
  "openai/gpt-5.5",
130
140
  "openai/gpt-5.4",
131
141
  "openai/gpt-5.4-mini",
@@ -137,7 +147,9 @@ for (const expected of [
137
147
  assert(routeChoices.includes(expected), `shared model selector missing ${expected}`);
138
148
  }
139
149
  assert(!routeChoices.includes("venice/e2ee-venice-uncensored-24b-p"), "shared model selector should hide unstable E2EE Venice 1.1");
150
+ assert(modelRoleChoices("route").some((item) => item.provider === "openai" && item.model === "gpt-5.5" && item.reasoningOptions.includes("xhigh")), "OpenAI selector missing reasoning levels");
140
151
  assert(modelRoleChoices("auxiliary").some((item) => item.provider === "grsai"), "auxiliary selector missing GRS AI");
152
+ assert(modelRoleChoices("auxiliary").some((item) => item.provider === "venice" && item.model === "wan-2-7-pro-edit"), "auxiliary selector missing Venice Wan edit");
141
153
  const parsedTextToolCalls = parseTextToolCalls('[TOOL_CALLS]list_files[ARGS]call_123[ARGS]{"path":".","maxDepth":1}');
142
154
  assert(parsedTextToolCalls.length === 1, "Venice text tool-call parser did not detect encoded tool call");
143
155
  assert(parsedTextToolCalls[0].function.name === "list_files", "Venice text tool-call parser returned wrong tool name");
@@ -1651,8 +1651,10 @@ function createState(args = {}) {
1651
1651
  preferredWrapper: args.preferredWrapper || "codex",
1652
1652
  routeProvider: args.routeProvider || "",
1653
1653
  routeModel: args.routeModel || "",
1654
+ routeReasoning: args.routeReasoning || "",
1654
1655
  mainProvider: args.mainProvider || "",
1655
1656
  mainModel: args.mainModel || "",
1657
+ mainReasoning: args.mainReasoning || "",
1656
1658
  spareProvider: args.spareProvider || "openai",
1657
1659
  spareModel: args.spareModel || "gpt-5.4",
1658
1660
  spareReasoning: args.spareReasoning || "medium",
@@ -1694,8 +1696,10 @@ function useDeepSeekDefaults(state) {
1694
1696
  state.model = "";
1695
1697
  state.routeProvider = "deepseek";
1696
1698
  state.routeModel = "deepseek-v4-flash";
1699
+ state.routeReasoning = "";
1697
1700
  state.mainProvider = "deepseek";
1698
1701
  state.mainModel = "deepseek-v4-pro";
1702
+ state.mainReasoning = "";
1699
1703
  }
1700
1704
 
1701
1705
  function veniceTextModelChoices() {
@@ -1766,8 +1770,10 @@ function printModelRoles(state) {
1766
1770
  const roles = getModelRoleDefaults({
1767
1771
  routeProvider: state.routeProvider,
1768
1772
  routeModel: state.routeModel,
1773
+ routeReasoning: state.routeReasoning,
1769
1774
  mainProvider: state.mainProvider,
1770
1775
  mainModel: state.mainModel,
1776
+ mainReasoning: state.mainReasoning,
1771
1777
  spareProvider: state.spareProvider,
1772
1778
  spareModel: state.spareModel,
1773
1779
  spareReasoning: state.spareReasoning,
@@ -1827,13 +1833,17 @@ function compactReasoning(model = {}) {
1827
1833
  return `${preferred} reasoning`;
1828
1834
  }
1829
1835
 
1836
+ function isReasoningLevel(value = "") {
1837
+ return ["low", "medium", "high", "xhigh"].includes(String(value || "").toLowerCase());
1838
+ }
1839
+
1830
1840
  function modelSelectorGroup(provider, model = {}) {
1831
1841
  if (provider === "deepseek") return "DeepSeek";
1832
1842
  if (provider === "openai") return "OpenAI";
1833
1843
  if (provider === "qwen") return "Qwen";
1834
1844
  if (provider === "mock") return "Mock";
1835
1845
  if (provider === "venice") {
1836
- if (model.bucket === "venice-uncensored") return "Venice Uncensored";
1846
+ if (model.bucket === "venice") return "Venice";
1837
1847
  if (model.bucket === "venice-gpt") return "Venice GPT";
1838
1848
  if (model.bucket === "venice-claude") return "Venice Claude";
1839
1849
  if (model.bucket === "venice-gemma") return "Venice Gemma";
@@ -1843,25 +1853,96 @@ function modelSelectorGroup(provider, model = {}) {
1843
1853
  return provider;
1844
1854
  }
1845
1855
 
1846
- function textModelRoleChoices() {
1847
- const providerOrder = ["deepseek", "venice", "openai", "qwen", "mock"];
1848
- const veniceOrder = [
1849
- "venice-uncensored-1-2",
1850
- "venice-uncensored",
1851
- "gemma-4-uncensored",
1852
- "openai-gpt-55",
1853
- "claude-sonnet-4-6",
1854
- "qwen3-6-27b",
1855
- ];
1856
+ const TEXT_MODEL_GROUPS = [
1857
+ {
1858
+ id: "deepseek",
1859
+ label: "DeepSeek",
1860
+ description: "DeepSeek V4 Flash and Pro defaults.",
1861
+ },
1862
+ {
1863
+ id: "venice",
1864
+ label: "Venice",
1865
+ description: "Venice 1.2, Venice 1.1, and Gemma 4 Uncensored.",
1866
+ },
1867
+ {
1868
+ id: "openai",
1869
+ label: "OpenAI",
1870
+ description: "GPT and Codex models with selectable reasoning.",
1871
+ },
1872
+ {
1873
+ id: "venice-gpt",
1874
+ label: "Venice GPT",
1875
+ description: "OpenAI/GPT-family text models through Venice.",
1876
+ },
1877
+ {
1878
+ id: "venice-gemma",
1879
+ label: "Venice Gemma",
1880
+ description: "Gemma instruct models through Venice, excluding Gemma 4 Uncensored.",
1881
+ },
1882
+ {
1883
+ id: "venice-claude",
1884
+ label: "Venice Claude",
1885
+ description: "Claude Sonnet and Opus models through Venice.",
1886
+ },
1887
+ {
1888
+ id: "venice-qwen",
1889
+ label: "Venice Qwen",
1890
+ description: "Qwen text and coder models through Venice.",
1891
+ },
1892
+ {
1893
+ id: "qwen",
1894
+ label: "Qwen",
1895
+ description: "DashScope Qwen Plus/Turbo/Max.",
1896
+ },
1897
+ {
1898
+ id: "mock",
1899
+ label: "Mock",
1900
+ description: "Deterministic local test model.",
1901
+ },
1902
+ ];
1903
+
1904
+ function modelGroupForSelection(provider, model = {}) {
1905
+ if (provider === "venice") return model.bucket || "venice";
1906
+ return provider;
1907
+ }
1908
+
1909
+ function modelGroupChoices() {
1910
+ return TEXT_MODEL_GROUPS.map((group) => ({
1911
+ action: "group",
1912
+ groupId: group.id,
1913
+ label: group.label,
1914
+ description: group.description,
1915
+ }));
1916
+ }
1917
+
1918
+ function modelsForSelectorGroup(groupId = "") {
1919
+ const group = String(groupId || "");
1920
+ if (group === "deepseek") return (PROVIDER_MODEL_CATALOG.deepseek || []).filter((model) => !model.hidden);
1921
+ if (group === "openai") return (PROVIDER_MODEL_CATALOG.openai || []).filter((model) => !model.hidden);
1922
+ if (group === "qwen") return (PROVIDER_MODEL_CATALOG.qwen || []).filter((model) => !model.hidden);
1923
+ if (group === "mock") return (PROVIDER_MODEL_CATALOG.mock || []).filter((model) => !model.hidden);
1924
+ if (group === "venice") {
1925
+ const order = ["venice-uncensored-1-2", "venice-uncensored", "gemma-4-uncensored"];
1926
+ const byId = new Map((PROVIDER_MODEL_CATALOG.venice || []).filter((model) => !model.hidden).map((model) => [model.id, model]));
1927
+ return order.map((id) => byId.get(id)).filter(Boolean);
1928
+ }
1929
+ if (group.startsWith("venice-")) {
1930
+ return (PROVIDER_MODEL_CATALOG.venice || []).filter((model) => !model.hidden && model.bucket === group);
1931
+ }
1932
+ return [];
1933
+ }
1934
+
1935
+ function providerForSelectorGroup(groupId = "") {
1936
+ if (String(groupId).startsWith("venice")) return "venice";
1937
+ return groupId;
1938
+ }
1939
+
1940
+ function textModelRoleChoices(groupId = "") {
1941
+ const groups = groupId ? [groupId] : TEXT_MODEL_GROUPS.map((group) => group.id);
1856
1942
  const choices = [];
1857
- for (const provider of providerOrder) {
1858
- let models = PROVIDER_MODEL_CATALOG[provider] || [];
1859
- if (provider === "venice") {
1860
- const byId = new Map(models.filter((model) => !model.hidden).map((model) => [model.id, model]));
1861
- models = veniceOrder.map((id) => byId.get(id)).filter(Boolean);
1862
- } else {
1863
- models = models.filter((model) => !model.hidden);
1864
- }
1943
+ for (const groupIdItem of groups) {
1944
+ const provider = providerForSelectorGroup(groupIdItem);
1945
+ const models = modelsForSelectorGroup(groupIdItem);
1865
1946
  for (const model of models) {
1866
1947
  const group = modelSelectorGroup(provider, model);
1867
1948
  const reasoning = compactReasoning(model);
@@ -1874,6 +1955,7 @@ function textModelRoleChoices() {
1874
1955
  group,
1875
1956
  description: details,
1876
1957
  reasoningDefault: reasoning ? reasoning.split(" ")[0] : "",
1958
+ reasoningOptions: Array.isArray(model.reasoning) ? model.reasoning : [],
1877
1959
  });
1878
1960
  }
1879
1961
  }
@@ -1882,30 +1964,47 @@ function textModelRoleChoices() {
1882
1964
 
1883
1965
  export function modelRoleChoices(role = "main") {
1884
1966
  if (role !== "auxiliary") return textModelRoleChoices();
1885
- const common = [
1886
- {
1887
- provider: "grsai",
1888
- model: "nano-banana-2",
1889
- label: "GRS AI Nano Banana",
1890
- description: "default auxiliary image model",
1891
- auxiliary: true,
1892
- },
1967
+ return auxiliaryModelRoleChoices();
1968
+ }
1969
+
1970
+ function auxiliaryGroupChoices() {
1971
+ return [
1893
1972
  {
1894
- provider: "venice",
1895
- model: "gpt-image-2",
1896
- label: "Venice GPT Image 2",
1897
- description: "high-quality Venice image generation",
1898
- auxiliary: true,
1973
+ action: "group",
1974
+ groupId: "grsai",
1975
+ label: "GRS AI",
1976
+ description: "Nano Banana and GPT image models through GRS AI-compatible keys.",
1899
1977
  },
1900
1978
  {
1901
- provider: "venice",
1902
- model: "nano-banana-2",
1903
- label: "Venice Nano Banana 2",
1904
- description: "Venice image generation route",
1905
- auxiliary: true,
1979
+ action: "group",
1980
+ groupId: "venice-image",
1981
+ label: "Venice Image",
1982
+ description: "Venice image, edit, background-removal, Qwen, Wan, Grok, Recraft, Flux routes.",
1906
1983
  },
1907
1984
  ];
1908
- return common.filter((item) => item.auxiliary).sort((a, b) => Number(b.provider === "grsai") - Number(a.provider === "grsai"));
1985
+ }
1986
+
1987
+ function auxiliaryProviderForGroup(groupId = "") {
1988
+ return groupId === "venice-image" ? "venice" : "grsai";
1989
+ }
1990
+
1991
+ function auxiliaryModelRoleChoices(groupId = "") {
1992
+ const groups = groupId ? [groupId] : ["grsai", "venice-image"];
1993
+ const choices = [];
1994
+ for (const group of groups) {
1995
+ const provider = auxiliaryProviderForGroup(group);
1996
+ for (const model of AUXILIARY_MODEL_CATALOG[group] || []) {
1997
+ choices.push({
1998
+ provider,
1999
+ model: model.id,
2000
+ label: `${group === "venice-image" ? "Venice Image" : "GRS AI"} · ${model.label || model.id}`,
2001
+ group,
2002
+ description: [model.type, model.price, model.description].filter(Boolean).join("; "),
2003
+ auxiliary: true,
2004
+ });
2005
+ }
2006
+ }
2007
+ return choices;
1909
2008
  }
1910
2009
 
1911
2010
  function providerChoices() {
@@ -1945,6 +2044,7 @@ function providerChoices() {
1945
2044
 
1946
2045
  function modelChoiceLine(option) {
1947
2046
  if (option.action === "disable") return `${option.label} ${option.description}`;
2047
+ if (option.action === "group" || option.action === "reasoning") return `${option.label} ${option.description}`;
1948
2048
  return `${option.label} ${option.provider}/${option.model} ${option.description}`;
1949
2049
  }
1950
2050
 
@@ -2034,7 +2134,6 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
2034
2134
  }
2035
2135
 
2036
2136
  async function pickModelRole(role, state) {
2037
- const options = modelRoleChoices(role);
2038
2137
  const currentProvider =
2039
2138
  role === "route"
2040
2139
  ? state.routeProvider || "deepseek"
@@ -2051,6 +2150,32 @@ async function pickModelRole(role, state) {
2051
2150
  : role === "auxiliary"
2052
2151
  ? state.auxiliaryModel || "nano-banana-2"
2053
2152
  : state.mainModel || "deepseek-v4-pro";
2153
+
2154
+ const groupOptions = role === "auxiliary" ? auxiliaryGroupChoices() : modelGroupChoices();
2155
+ const currentModelEntry =
2156
+ role === "auxiliary"
2157
+ ? modelRoleChoices("auxiliary").find((item) => item.provider === currentProvider && item.model === currentModel)
2158
+ : modelRoleChoices(role).find((item) => item.provider === currentProvider && item.model === currentModel);
2159
+ const currentGroup = currentModelEntry?.group || currentProvider;
2160
+ const group = await selectModelChoice({
2161
+ title:
2162
+ role === "route"
2163
+ ? "Select route model family"
2164
+ : role === "spare"
2165
+ ? "Select spare model family"
2166
+ : role === "auxiliary"
2167
+ ? "Select auxiliary provider"
2168
+ : "Select main model family",
2169
+ subtitle: "First choose a provider family. Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
2170
+ options: groupOptions,
2171
+ initialIndex: Math.max(
2172
+ groupOptions.findIndex((item) => item.groupId === currentGroup),
2173
+ 0
2174
+ ),
2175
+ });
2176
+ if (!group) return false;
2177
+
2178
+ const options = role === "auxiliary" ? auxiliaryModelRoleChoices(group.groupId) : textModelRoleChoices(group.groupId);
2054
2179
  const initialIndex = Math.max(
2055
2180
  options.findIndex((item) => item.provider === currentProvider && item.model === currentModel),
2056
2181
  0
@@ -2058,29 +2183,61 @@ async function pickModelRole(role, state) {
2058
2183
  const selected = await selectModelChoice({
2059
2184
  title:
2060
2185
  role === "route"
2061
- ? "Select route model"
2186
+ ? `Select route model: ${group.label}`
2062
2187
  : role === "spare"
2063
- ? "Select spare model"
2188
+ ? `Select spare model: ${group.label}`
2064
2189
  : role === "auxiliary"
2065
- ? "Select auxiliary model"
2066
- : "Select main model",
2067
- subtitle: "Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
2190
+ ? `Select auxiliary model: ${group.label}`
2191
+ : `Select main model: ${group.label}`,
2192
+ subtitle:
2193
+ role === "auxiliary"
2194
+ ? "Choose the image/edit auxiliary model. Enter confirms, Esc cancels."
2195
+ : "Choose the text model. OpenAI choices ask for reasoning next.",
2068
2196
  options,
2069
2197
  initialIndex,
2070
2198
  });
2071
2199
  if (!selected) return false;
2072
2200
 
2201
+ let selectedReasoning = selected.reasoningDefault || "";
2202
+ if (Array.isArray(selected.reasoningOptions) && selected.reasoningOptions.length > 0) {
2203
+ const currentReasoning =
2204
+ role === "route"
2205
+ ? state.routeReasoning || selectedReasoning || "medium"
2206
+ : role === "spare"
2207
+ ? state.spareReasoning || selectedReasoning || "medium"
2208
+ : state.mainReasoning || selectedReasoning || "medium";
2209
+ const reasoningOptions = selected.reasoningOptions.map((level) => ({
2210
+ action: "reasoning",
2211
+ label: level,
2212
+ value: level,
2213
+ description: `${selected.label} with ${level} reasoning`,
2214
+ }));
2215
+ const reasoning = await selectModelChoice({
2216
+ title: `Select reasoning: ${selected.label}`,
2217
+ subtitle: "Up/Down selects reasoning effort. Enter confirms, Esc cancels.",
2218
+ options: reasoningOptions,
2219
+ initialIndex: Math.max(
2220
+ reasoningOptions.findIndex((item) => item.value === currentReasoning),
2221
+ reasoningOptions.findIndex((item) => item.value === selectedReasoning),
2222
+ 0
2223
+ ),
2224
+ });
2225
+ if (!reasoning) return false;
2226
+ selectedReasoning = reasoning.value;
2227
+ }
2228
+
2073
2229
  state.routingMode = "smart";
2074
2230
  state.provider = "deepseek";
2075
2231
  state.model = "";
2076
2232
  if (role === "route") {
2077
2233
  state.routeProvider = selected.provider;
2078
2234
  state.routeModel = selected.model;
2079
- printSystemLine(`route=${state.routeProvider}/${state.routeModel}`);
2235
+ state.routeReasoning = selectedReasoning || state.routeReasoning || "";
2236
+ printSystemLine(`route=${state.routeProvider}/${state.routeModel}${state.routeReasoning ? ` reasoning=${state.routeReasoning}` : ""}`);
2080
2237
  } else if (role === "spare") {
2081
2238
  state.spareProvider = selected.provider;
2082
2239
  state.spareModel = selected.model;
2083
- state.spareReasoning = selected.reasoningDefault || state.spareReasoning || "medium";
2240
+ state.spareReasoning = selectedReasoning || state.spareReasoning || "medium";
2084
2241
  printSystemLine(`spare=${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
2085
2242
  } else if (role === "auxiliary") {
2086
2243
  state.auxiliaryProvider = selected.provider;
@@ -2090,7 +2247,8 @@ async function pickModelRole(role, state) {
2090
2247
  } else {
2091
2248
  state.mainProvider = selected.provider;
2092
2249
  state.mainModel = selected.model;
2093
- printSystemLine(`main=${state.mainProvider}/${state.mainModel}`);
2250
+ state.mainReasoning = selectedReasoning || state.mainReasoning || "";
2251
+ printSystemLine(`main=${state.mainProvider}/${state.mainModel}${state.mainReasoning ? ` reasoning=${state.mainReasoning}` : ""}`);
2094
2252
  }
2095
2253
  return true;
2096
2254
  }
@@ -2274,11 +2432,16 @@ async function handleCommand(line, state, packageDir) {
2274
2432
  `Image generation: ${keys.grsai ? "GRSAI key available" : "missing GRSAI key"}`,
2275
2433
  `Venice image: ${keys.venice ? "Venice key available" : "missing Venice key"}`,
2276
2434
  `Selected auxiliary: ${state.auxiliaryProvider || "grsai"}/${state.auxiliaryModel || "nano-banana-2"}`,
2277
- "Use `/auxiliary grsai` or `/auxiliary venice` to paste a key, `/auxiliary model grsai/nano-banana-2`, `/auxiliary image`, or `/auxiliary off`.",
2435
+ "Use `/auxiliary model` for the two-level selector, `/auxiliary grsai` or `/auxiliary venice` to paste a key, `/auxiliary image`, or `/auxiliary off`.",
2278
2436
  ].join("\n")
2279
2437
  );
2280
2438
  return true;
2281
2439
  }
2440
+ if (action === "select") {
2441
+ const changed = await pickModelRole("auxiliary", state);
2442
+ if (!changed) printSystemLine(`auxiliary=${state.auxiliaryProvider || "grsai"}/${state.auxiliaryModel || "nano-banana-2"}`);
2443
+ return true;
2444
+ }
2282
2445
  if (action === "on") {
2283
2446
  state.allowAuxiliaryTools = true;
2284
2447
  printSystemLine("auxiliary=on");
@@ -2303,7 +2466,7 @@ async function handleCommand(line, state, packageDir) {
2303
2466
  return true;
2304
2467
  }
2305
2468
  const selected = parseProviderModel(modelValue, state.auxiliaryProvider || "grsai");
2306
- state.auxiliaryProvider = selected.provider || "grsai";
2469
+ state.auxiliaryProvider = selected.provider === "venice-image" ? "venice" : selected.provider || "grsai";
2307
2470
  state.auxiliaryModel = selected.model || state.auxiliaryModel || "nano-banana-2";
2308
2471
  printSystemLine(`auxiliary=${state.auxiliaryProvider}/${state.auxiliaryModel}`);
2309
2472
  return true;
@@ -2312,7 +2475,7 @@ async function handleCommand(line, state, packageDir) {
2312
2475
  await promptAndSaveProviderKey(action === "venice" ? "venice" : "grsai", state);
2313
2476
  return true;
2314
2477
  }
2315
- printAgentMessage("Usage: /auxiliary [status|grsai|venice|model grsai/nano-banana-2|on|off|image]");
2478
+ printAgentMessage("Usage: /auxiliary [status|select|grsai|venice|model [grsai/nano-banana-2]|on|off|image]");
2316
2479
  return true;
2317
2480
  }
2318
2481
  if (command === "new") {
@@ -2464,10 +2627,12 @@ async function handleCommand(line, state, packageDir) {
2464
2627
  printSystemLine(`routing=${state.routingMode}`);
2465
2628
  return true;
2466
2629
  }
2467
- const selected = parseProviderModel(value, state.routeProvider || "deepseek");
2630
+ const parts = value.split(/\s+/).filter(Boolean);
2631
+ const selected = parseProviderModel(parts[0] || value, state.routeProvider || "deepseek");
2468
2632
  state.routeProvider = selected.provider || "deepseek";
2469
2633
  state.routeModel = selected.model || state.routeModel || "deepseek-v4-flash";
2470
- printSystemLine(`route=${state.routeProvider}/${state.routeModel}`);
2634
+ if (isReasoningLevel(parts[1])) state.routeReasoning = parts[1].toLowerCase();
2635
+ printSystemLine(`route=${state.routeProvider}/${state.routeModel}${state.routeReasoning ? ` reasoning=${state.routeReasoning}` : ""}`);
2471
2636
  return true;
2472
2637
  }
2473
2638
  if (command === "main" || command === "model") {
@@ -2486,6 +2651,7 @@ async function handleCommand(line, state, packageDir) {
2486
2651
  if (command === "main") {
2487
2652
  state.mainProvider = "";
2488
2653
  state.mainModel = "";
2654
+ state.mainReasoning = "";
2489
2655
  printSystemLine("main=auto");
2490
2656
  } else {
2491
2657
  state.model = "";
@@ -2493,15 +2659,17 @@ async function handleCommand(line, state, packageDir) {
2493
2659
  }
2494
2660
  return true;
2495
2661
  }
2496
- const selected = parseProviderModel(value, state.mainProvider || state.provider || "deepseek");
2662
+ const parts = value.split(/\s+/).filter(Boolean);
2663
+ const selected = parseProviderModel(parts[0] || value, state.mainProvider || state.provider || "deepseek");
2497
2664
  if (command === "main" || selected.provider) {
2498
2665
  state.mainProvider = selected.provider || "deepseek";
2499
2666
  state.mainModel = selected.model || "deepseek-v4-pro";
2667
+ if (isReasoningLevel(parts[1])) state.mainReasoning = parts[1].toLowerCase();
2500
2668
  if (command === "model") {
2501
2669
  state.provider = state.mainProvider;
2502
2670
  state.model = state.mainModel;
2503
2671
  }
2504
- printSystemLine(`main=${state.mainProvider}/${state.mainModel}`);
2672
+ printSystemLine(`main=${state.mainProvider}/${state.mainModel}${state.mainReasoning ? ` reasoning=${state.mainReasoning}` : ""}`);
2505
2673
  return true;
2506
2674
  }
2507
2675
  state.model = value;
@@ -88,11 +88,11 @@ export const MODEL_PROVIDER_GROUPS = {
88
88
  role: "regional/general",
89
89
  description: "DashScope/OpenAI-compatible Qwen route for Chinese and general work.",
90
90
  },
91
- "venice-uncensored": {
92
- label: "Venice Uncensored",
91
+ venice: {
92
+ label: "Venice",
93
93
  provider: "venice",
94
94
  role: "alternate text",
95
- description: "Venice uncensored text models for optional manual routes.",
95
+ description: "Venice 1.2/1.1 and Gemma 4 Uncensored for optional manual routes.",
96
96
  },
97
97
  "venice-gpt": {
98
98
  label: "Venice GPT",
@@ -128,21 +128,46 @@ export const AUXILIARY_MODEL_CATALOG = {
128
128
  type: "image",
129
129
  description: "Default auxiliary image-generation route through GRS AI-compatible APIs.",
130
130
  },
131
+ {
132
+ id: "nano-banana-2-edit",
133
+ label: "Nano Banana 2 Edit",
134
+ type: "inpaint",
135
+ description: "Image edit route through GRS AI-compatible APIs when available.",
136
+ },
131
137
  {
132
138
  id: "gpt-image-2",
133
139
  label: "GPT Image 2",
134
140
  type: "image",
135
141
  description: "High-quality image generation when available through the configured auxiliary endpoint.",
136
142
  },
143
+ {
144
+ id: "gpt-image-2-edit",
145
+ label: "GPT Image 2 Edit",
146
+ type: "inpaint",
147
+ description: "High-quality image editing when available through the configured auxiliary endpoint.",
148
+ },
137
149
  ],
138
150
  "venice-image": [
151
+ { id: "wan-2-7-pro-edit", label: "Wan 2.7 Pro Edit", type: "inpaint", price: "$0.09/edit" },
139
152
  { id: "nano-banana-2", label: "Nano Banana 2", type: "image", price: "$0.10/image" },
153
+ { id: "nano-banana-2-edit", label: "Nano Banana 2 Edit", type: "inpaint", price: "$0.10/edit" },
140
154
  { id: "gpt-image-2", label: "GPT Image 2", type: "image", price: "$0.27/image" },
141
155
  { id: "gpt-image-2-edit", label: "GPT Image 2 Edit", type: "inpaint", price: "$0.36/edit" },
142
- { id: "wan-2-7-pro-edit", label: "Wan 2.7 Pro Edit", type: "inpaint", price: "$0.09/edit" },
156
+ { id: "grok-imagine-image-pro", label: "Grok Imagine Pro", type: "image", price: "$0.09/image" },
143
157
  { id: "grok-imagine-image", label: "Grok Imagine", type: "image", price: "$0.03/image" },
158
+ { id: "wan-2-7-text-to-image", label: "Wan 2.7", type: "image", price: "$0.04/image" },
159
+ { id: "wan-2-7-pro-text-to-image", label: "Wan 2.7 Pro", type: "image", price: "$0.09/image" },
160
+ { id: "qwen-image-2", label: "Qwen Image 2", type: "image", price: "$0.05/image" },
144
161
  { id: "qwen-image-2-pro", label: "Qwen Image 2 Pro", type: "image", price: "$0.10/image" },
162
+ { id: "qwen-image-2-edit", label: "Qwen Image 2 Edit", type: "inpaint", price: "$0.05/edit" },
163
+ { id: "qwen-image-2-pro-edit", label: "Qwen Image 2 Pro Edit", type: "inpaint", price: "$0.10/edit" },
145
164
  { id: "bria-bg-remover", label: "Background Remover", type: "image", price: "$0.03/image" },
165
+ { id: "recraft-v4", label: "Recraft V4", type: "image", price: "$0.05/image" },
166
+ { id: "recraft-v4-pro", label: "Recraft V4 Pro", type: "image", price: "$0.29/image" },
167
+ { id: "flux-2-pro", label: "Flux 2 Pro", type: "image", price: "$0.04/image" },
168
+ { id: "flux-2-max", label: "Flux 2 Max", type: "image", price: "$0.09/image" },
169
+ { id: "nano-banana-pro", label: "Nano Banana Pro", type: "image", price: "$0.18/image" },
170
+ { id: "nano-banana-pro-edit", label: "Nano Banana Pro Edit", type: "inpaint", price: "$0.18/edit" },
146
171
  ],
147
172
  };
148
173
 
@@ -211,21 +236,21 @@ export const PROVIDER_MODEL_CATALOG = {
211
236
  {
212
237
  id: "venice-uncensored-1-2",
213
238
  label: "Venice Uncensored 1.2",
214
- bucket: "venice-uncensored",
239
+ bucket: "venice",
215
240
  context: "128K",
216
241
  description: "Current Venice uncensored text route.",
217
242
  },
218
243
  {
219
244
  id: "venice-uncensored",
220
245
  label: "Venice Uncensored 1.1",
221
- bucket: "venice-uncensored",
246
+ bucket: "venice",
222
247
  context: "32K",
223
248
  description: "Working Venice uncensored 1.1 text route.",
224
249
  },
225
250
  {
226
251
  id: "e2ee-venice-uncensored-24b-p",
227
252
  label: "Venice Uncensored 1.1 E2EE",
228
- bucket: "venice-uncensored",
253
+ bucket: "venice",
229
254
  context: "32K",
230
255
  hidden: true,
231
256
  description: "Documented E2EE Venice 1.1 route; hidden from selectors until upstream stabilizes.",
@@ -233,7 +258,7 @@ export const PROVIDER_MODEL_CATALOG = {
233
258
  {
234
259
  id: "venice-uncensored-role-play",
235
260
  label: "Venice Role Play Uncensored",
236
- bucket: "venice-uncensored",
261
+ bucket: "venice",
237
262
  context: "128K",
238
263
  hidden: true,
239
264
  description: "Role-play oriented Venice route.",
@@ -241,10 +266,38 @@ export const PROVIDER_MODEL_CATALOG = {
241
266
  {
242
267
  id: "gemma-4-uncensored",
243
268
  label: "Gemma 4 Uncensored",
244
- bucket: "venice-gemma",
269
+ bucket: "venice",
245
270
  context: "256K",
246
271
  description: "Gemma-family uncensored Venice route.",
247
272
  },
273
+ {
274
+ id: "google-gemma-4-31b-it",
275
+ label: "Google Gemma 4 31B Instruct",
276
+ bucket: "venice-gemma",
277
+ context: "256K",
278
+ description: "Gemma 4 instruct model through Venice.",
279
+ },
280
+ {
281
+ id: "google-gemma-4-26b-a4b-it",
282
+ label: "Google Gemma 4 26B A4B Instruct",
283
+ bucket: "venice-gemma",
284
+ context: "256K",
285
+ description: "Smaller Gemma 4 instruct model through Venice.",
286
+ },
287
+ {
288
+ id: "google-gemma-3-27b-it",
289
+ label: "Google Gemma 3 27B Instruct",
290
+ bucket: "venice-gemma",
291
+ context: "198K",
292
+ description: "Gemma 3 instruct model through Venice.",
293
+ },
294
+ {
295
+ id: "e2ee-gemma-3-27b-p",
296
+ label: "Gemma 3 27B E2EE",
297
+ bucket: "venice-gemma",
298
+ context: "40K",
299
+ description: "Private E2EE Gemma 3 route through Venice.",
300
+ },
248
301
  {
249
302
  id: "qwen3-6-27b",
250
303
  label: "Qwen 3.6 27B",
@@ -252,6 +305,77 @@ export const PROVIDER_MODEL_CATALOG = {
252
305
  context: "256K",
253
306
  description: "Qwen-family Venice route.",
254
307
  },
308
+ {
309
+ id: "qwen-3-6-plus",
310
+ label: "Qwen 3.6 Plus Uncensored",
311
+ bucket: "venice-qwen",
312
+ context: "1.0M",
313
+ description: "Long-context Qwen 3.6 Plus route through Venice.",
314
+ },
315
+ {
316
+ id: "qwen3-5-9b",
317
+ label: "Qwen 3.5 9B",
318
+ bucket: "venice-qwen",
319
+ context: "256K",
320
+ description: "Small fast Qwen-family Venice route.",
321
+ },
322
+ {
323
+ id: "qwen3-5-35b-a3b",
324
+ label: "Qwen 3.5 35B A3B",
325
+ bucket: "venice-qwen",
326
+ context: "256K",
327
+ description: "Mid-size Qwen-family Venice route.",
328
+ },
329
+ {
330
+ id: "qwen3-5-397b-a17b",
331
+ label: "Qwen 3.5 397B A17B",
332
+ bucket: "venice-qwen",
333
+ context: "128K",
334
+ description: "Large Qwen-family Venice route.",
335
+ },
336
+ {
337
+ id: "qwen3-coder-480b-a35b-instruct-turbo",
338
+ label: "Qwen 3 Coder 480B Turbo",
339
+ bucket: "venice-qwen",
340
+ context: "256K",
341
+ description: "Coding-oriented Qwen route through Venice.",
342
+ },
343
+ {
344
+ id: "qwen3-vl-235b-a22b",
345
+ label: "Qwen3 VL 235B",
346
+ bucket: "venice-qwen",
347
+ context: "256K",
348
+ description: "Vision-language Qwen route through Venice.",
349
+ },
350
+ {
351
+ id: "qwen3-235b-a22b-thinking-2507",
352
+ label: "Qwen 3 235B A22B Thinking",
353
+ bucket: "venice-qwen",
354
+ context: "128K",
355
+ description: "Qwen thinking route through Venice.",
356
+ },
357
+ {
358
+ id: "qwen3-235b-a22b-instruct-2507",
359
+ label: "Qwen 3 235B A22B Instruct",
360
+ bucket: "venice-qwen",
361
+ context: "128K",
362
+ description: "Qwen instruct route through Venice.",
363
+ },
364
+ {
365
+ id: "qwen3-next-80b",
366
+ label: "Qwen 3 Next 80B",
367
+ bucket: "venice-qwen",
368
+ context: "256K",
369
+ description: "Qwen Next route through Venice.",
370
+ },
371
+ {
372
+ id: "qwen3-coder-480b-a35b-instruct",
373
+ label: "Qwen 3 Coder 480B",
374
+ bucket: "venice-qwen",
375
+ context: "256K",
376
+ hidden: true,
377
+ description: "Deprecated Qwen Coder route; hidden by default.",
378
+ },
255
379
  {
256
380
  id: "openai-gpt-55",
257
381
  label: "GPT-5.5 via Venice",
@@ -259,6 +383,69 @@ export const PROVIDER_MODEL_CATALOG = {
259
383
  context: "1.0M",
260
384
  description: "OpenAI-family Venice-routed model.",
261
385
  },
386
+ {
387
+ id: "openai-gpt-55-pro",
388
+ label: "GPT-5.5 Pro via Venice",
389
+ bucket: "venice-gpt",
390
+ context: "1.0M",
391
+ description: "High-capacity GPT-5.5 Pro route through Venice.",
392
+ },
393
+ {
394
+ id: "openai-gpt-54",
395
+ label: "GPT-5.4 via Venice",
396
+ bucket: "venice-gpt",
397
+ context: "1.0M",
398
+ description: "GPT-5.4 route through Venice.",
399
+ },
400
+ {
401
+ id: "openai-gpt-54-pro",
402
+ label: "GPT-5.4 Pro via Venice",
403
+ bucket: "venice-gpt",
404
+ context: "1.0M",
405
+ description: "High-capacity GPT-5.4 Pro route through Venice.",
406
+ },
407
+ {
408
+ id: "openai-gpt-54-mini",
409
+ label: "GPT-5.4 Mini via Venice",
410
+ bucket: "venice-gpt",
411
+ context: "400K",
412
+ description: "Small fast GPT route through Venice.",
413
+ },
414
+ {
415
+ id: "openai-gpt-53-codex",
416
+ label: "GPT-5.3 Codex via Venice",
417
+ bucket: "venice-gpt",
418
+ context: "400K",
419
+ description: "Coding-optimized GPT route through Venice.",
420
+ },
421
+ {
422
+ id: "openai-gpt-52",
423
+ label: "GPT-5.2 via Venice",
424
+ bucket: "venice-gpt",
425
+ context: "256K",
426
+ description: "Professional-work GPT route through Venice.",
427
+ },
428
+ {
429
+ id: "openai-gpt-52-codex",
430
+ label: "GPT-5.2 Codex via Venice",
431
+ bucket: "venice-gpt",
432
+ context: "256K",
433
+ description: "Codex-flavored GPT-5.2 route through Venice.",
434
+ },
435
+ {
436
+ id: "openai-gpt-4o-2024-11-20",
437
+ label: "GPT-4o via Venice",
438
+ bucket: "venice-gpt",
439
+ context: "128K",
440
+ description: "GPT-4o route through Venice.",
441
+ },
442
+ {
443
+ id: "openai-gpt-4o-mini-2024-07-18",
444
+ label: "GPT-4o Mini via Venice",
445
+ bucket: "venice-gpt",
446
+ context: "128K",
447
+ description: "Small GPT-4o route through Venice.",
448
+ },
262
449
  {
263
450
  id: "claude-sonnet-4-6",
264
451
  label: "Claude Sonnet 4.6 via Venice",
@@ -266,6 +453,41 @@ export const PROVIDER_MODEL_CATALOG = {
266
453
  context: "1.0M",
267
454
  description: "Claude-family Venice-routed model.",
268
455
  },
456
+ {
457
+ id: "claude-opus-4-7",
458
+ label: "Claude Opus 4.7 via Venice",
459
+ bucket: "venice-claude",
460
+ context: "1.0M",
461
+ description: "High-capacity Claude Opus route through Venice.",
462
+ },
463
+ {
464
+ id: "claude-opus-4-6-fast",
465
+ label: "Claude Opus 4.6 Fast via Venice",
466
+ bucket: "venice-claude",
467
+ context: "1.0M",
468
+ description: "Fast Claude Opus route through Venice.",
469
+ },
470
+ {
471
+ id: "claude-opus-4-6",
472
+ label: "Claude Opus 4.6 via Venice",
473
+ bucket: "venice-claude",
474
+ context: "1.0M",
475
+ description: "Claude Opus route through Venice.",
476
+ },
477
+ {
478
+ id: "claude-opus-4-5",
479
+ label: "Claude Opus 4.5 via Venice",
480
+ bucket: "venice-claude",
481
+ context: "198K",
482
+ description: "Claude Opus 4.5 route through Venice.",
483
+ },
484
+ {
485
+ id: "claude-sonnet-4-5",
486
+ label: "Claude Sonnet 4.5 via Venice",
487
+ bucket: "venice-claude",
488
+ context: "198K",
489
+ description: "Claude Sonnet 4.5 route through Venice.",
490
+ },
269
491
  ],
270
492
  qwen: [
271
493
  {
@@ -274,6 +496,12 @@ export const PROVIDER_MODEL_CATALOG = {
274
496
  role: "default",
275
497
  description: "DashScope OpenAI-compatible route.",
276
498
  },
499
+ {
500
+ id: "qwen-turbo",
501
+ label: "Qwen Turbo",
502
+ role: "fast",
503
+ description: "Fast DashScope OpenAI-compatible Qwen route.",
504
+ },
277
505
  {
278
506
  id: "qwen-max",
279
507
  label: "Qwen Max",
@@ -497,6 +725,7 @@ export function getModelRoleDefaults(overrides = {}) {
497
725
  command: "/route",
498
726
  provider: presets.fast.provider,
499
727
  model: presets.fast.model,
728
+ reasoning: overrides.routeReasoning || process.env.AGINTI_ROUTE_REASONING || "",
500
729
  description: "Fast planner and triage model. Default: DeepSeek V4 Flash.",
501
730
  },
502
731
  main: {
@@ -505,6 +734,7 @@ export function getModelRoleDefaults(overrides = {}) {
505
734
  command: "/model",
506
735
  provider: presets.complex.provider,
507
736
  model: presets.complex.model,
737
+ reasoning: overrides.mainReasoning || process.env.AGINTI_MAIN_REASONING || "",
508
738
  description: "Complex executor for coding, debugging, writing, and long tasks. Default: DeepSeek V4 Pro.",
509
739
  },
510
740
  spare: {