@lazyingart/agintiflow 0.20.1 → 0.20.2

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.
@@ -584,15 +584,15 @@ function printHelp() {
584
584
  " /instructions Show AGINTI.md project instructions status.",
585
585
  " /memory Alias for /instructions.",
586
586
  " /models Show route/main/spare/wrapper/auxiliary model roles.",
587
- " /venice Shortcut: use Venice Uncensored 1.2 for route and main roles.",
587
+ " /venice [on|off] Toggle Venice Uncensored 1.2 for route and main roles.",
588
588
  " /route [mode|provider/model]",
589
589
  " Open route selector, or set routing/fast route model.",
590
590
  " /model [provider/model] Open main-model selector, or set the active/main model.",
591
- " /spare <provider/model> [reasoning]",
592
- " Set spare model, e.g. /spare openai/gpt-5.4 medium.",
591
+ " /spare [provider/model] [reasoning]",
592
+ " Open spare selector, or set e.g. /spare openai/gpt-5.4 medium.",
593
593
  " /wrapper [on|off|codex model reasoning]",
594
594
  " Configure optional external wrapper.",
595
- " /auxiliary [status|grsai|venice|model grsai/nano-banana-2|on|off|image]",
595
+ " /auxiliary [status|grsai|venice|model [provider/model]|on|off|image]",
596
596
  " Manage optional auxiliary skills, including image generation.",
597
597
  " /new Start a fresh session on the next message.",
598
598
  " /resume <session-id> Continue a saved session.",
@@ -602,7 +602,7 @@ function printHelp() {
602
602
  " /web-search on|off Enable or disable the web_search tool.",
603
603
  " /scouts on|off|<1-10> Enable parallel DeepSeek scouts and set scout count.",
604
604
  " /routing <mode> Set routing: smart, fast, complex, manual.",
605
- " /provider <name> Set provider: deepseek, openai, qwen, venice, mock.",
605
+ " /provider [name] Open provider selector, or set deepseek/openai/qwen/venice/mock.",
606
606
  " /docker on Use docker-workspace with approved package installs.",
607
607
  " /docker off Use host shell policy.",
608
608
  " /latex on Use the LaTeX/PDF profile in Docker with a larger step budget.",
@@ -1268,6 +1268,11 @@ class LiveRunInput {
1268
1268
  async submitAsap() {
1269
1269
  const content = this.buffer.trim();
1270
1270
  if (!content) return;
1271
+ if (content.startsWith("/")) {
1272
+ this.printLine(`${label("warn", ansi.red)} Slash commands are disabled while a run is active. Press Esc/Ctrl+C to stop, or wait until idle.`);
1273
+ this.setStatus(`running · command ${compactLine(content, 24)} not accepted during active run`);
1274
+ return;
1275
+ }
1271
1276
  const item = {
1272
1277
  id: `cli-asap-${Date.now()}-${Math.random().toString(16).slice(2)}`,
1273
1278
  content,
@@ -1292,6 +1297,11 @@ class LiveRunInput {
1292
1297
  queueAfterFinish() {
1293
1298
  const content = this.buffer.trim();
1294
1299
  if (!content) return;
1300
+ if (content.startsWith("/")) {
1301
+ this.printLine(`${label("warn", ansi.red)} Slash commands cannot be queued during a run. Wait until idle, then run ${content}.`);
1302
+ this.setStatus(`running · command ${compactLine(content, 24)} not queued`);
1303
+ return;
1304
+ }
1295
1305
  this.pendingQueued.push({
1296
1306
  id: `cli-queued-${Date.now()}-${Math.random().toString(16).slice(2)}`,
1297
1307
  content,
@@ -1606,6 +1616,35 @@ function parseProviderModel(value, fallbackProvider = "") {
1606
1616
  return { provider: fallbackProvider, model: text };
1607
1617
  }
1608
1618
 
1619
+ function useDeepSeekDefaults(state) {
1620
+ state.routingMode = "smart";
1621
+ state.provider = "deepseek";
1622
+ state.model = "";
1623
+ state.routeProvider = "deepseek";
1624
+ state.routeModel = "deepseek-v4-flash";
1625
+ state.mainProvider = "deepseek";
1626
+ state.mainModel = "deepseek-v4-pro";
1627
+ }
1628
+
1629
+ function useVeniceDefaults(state) {
1630
+ state.routingMode = "smart";
1631
+ state.provider = "deepseek";
1632
+ state.model = "";
1633
+ state.routeProvider = "venice";
1634
+ state.routeModel = "venice-uncensored-1-2";
1635
+ state.mainProvider = "venice";
1636
+ state.mainModel = "venice-uncensored-1-2";
1637
+ }
1638
+
1639
+ function isVeniceDefaultActive(state) {
1640
+ return (
1641
+ state.routeProvider === "venice" &&
1642
+ state.routeModel === "venice-uncensored-1-2" &&
1643
+ state.mainProvider === "venice" &&
1644
+ state.mainModel === "venice-uncensored-1-2"
1645
+ );
1646
+ }
1647
+
1609
1648
  function printModelRoles(state) {
1610
1649
  const roles = getModelRoleDefaults({
1611
1650
  routeProvider: state.routeProvider,
@@ -1679,6 +1718,7 @@ function modelRoleChoices(role = "main") {
1679
1718
  label: "DeepSeek V4 Pro",
1680
1719
  description: "complex coding, debugging, writing, and long tasks",
1681
1720
  main: true,
1721
+ spare: true,
1682
1722
  },
1683
1723
  {
1684
1724
  provider: "venice",
@@ -1687,6 +1727,7 @@ function modelRoleChoices(role = "main") {
1687
1727
  description: "Venice text route; use /auth venice if missing",
1688
1728
  route: true,
1689
1729
  main: true,
1730
+ spare: true,
1690
1731
  },
1691
1732
  {
1692
1733
  provider: "openai",
@@ -1694,6 +1735,7 @@ function modelRoleChoices(role = "main") {
1694
1735
  label: "OpenAI GPT-5.5",
1695
1736
  description: "frontier spare/manual model",
1696
1737
  main: true,
1738
+ spare: true,
1697
1739
  },
1698
1740
  {
1699
1741
  provider: "openai",
@@ -1701,6 +1743,7 @@ function modelRoleChoices(role = "main") {
1701
1743
  label: "OpenAI GPT-5.4",
1702
1744
  description: "strong everyday coding spare",
1703
1745
  main: true,
1746
+ spare: true,
1704
1747
  },
1705
1748
  {
1706
1749
  provider: "openai",
@@ -1708,6 +1751,7 @@ function modelRoleChoices(role = "main") {
1708
1751
  label: "OpenAI GPT-5.4 Mini",
1709
1752
  description: "small fast spare route",
1710
1753
  route: true,
1754
+ spare: true,
1711
1755
  },
1712
1756
  {
1713
1757
  provider: "qwen",
@@ -1722,6 +1766,7 @@ function modelRoleChoices(role = "main") {
1722
1766
  label: "Qwen Max",
1723
1767
  description: "larger Qwen route",
1724
1768
  main: true,
1769
+ spare: true,
1725
1770
  },
1726
1771
  {
1727
1772
  provider: "mock",
@@ -1730,12 +1775,81 @@ function modelRoleChoices(role = "main") {
1730
1775
  description: "local deterministic test route",
1731
1776
  route: true,
1732
1777
  main: true,
1778
+ spare: true,
1779
+ },
1780
+ {
1781
+ provider: "grsai",
1782
+ model: "nano-banana-2",
1783
+ label: "GRS AI Nano Banana",
1784
+ description: "default auxiliary image model",
1785
+ auxiliary: true,
1786
+ },
1787
+ {
1788
+ provider: "venice",
1789
+ model: "gpt-image-2",
1790
+ label: "Venice GPT Image 2",
1791
+ description: "high-quality Venice image generation",
1792
+ auxiliary: true,
1793
+ },
1794
+ {
1795
+ provider: "venice",
1796
+ model: "nano-banana-2",
1797
+ label: "Venice Nano Banana 2",
1798
+ description: "Venice image generation route",
1799
+ auxiliary: true,
1800
+ },
1801
+ ];
1802
+ const filtered = common.filter((item) => {
1803
+ if (role === "route") return item.route;
1804
+ if (role === "spare") return item.spare;
1805
+ if (role === "auxiliary") return item.auxiliary;
1806
+ return item.main;
1807
+ });
1808
+ if (role === "route") {
1809
+ return filtered.sort((a, b) => Number(b.model === "deepseek-v4-flash") - Number(a.model === "deepseek-v4-flash"));
1810
+ }
1811
+ if (role === "spare") {
1812
+ return filtered.sort((a, b) => Number(b.model === "gpt-5.4") - Number(a.model === "gpt-5.4"));
1813
+ }
1814
+ if (role === "auxiliary") {
1815
+ return filtered.sort((a, b) => Number(b.provider === "grsai") - Number(a.provider === "grsai"));
1816
+ }
1817
+ return filtered.sort((a, b) => Number(b.model === "deepseek-v4-pro") - Number(a.model === "deepseek-v4-pro"));
1818
+ }
1819
+
1820
+ function providerChoices() {
1821
+ return [
1822
+ {
1823
+ provider: "deepseek",
1824
+ model: "deepseek-v4-flash",
1825
+ label: "DeepSeek",
1826
+ description: "default smart route with flash/pro roles",
1827
+ },
1828
+ {
1829
+ provider: "openai",
1830
+ model: "gpt-5.4",
1831
+ label: "OpenAI",
1832
+ description: "manual GPT route; use /model for exact model",
1833
+ },
1834
+ {
1835
+ provider: "venice",
1836
+ model: "venice-uncensored-1-2",
1837
+ label: "Venice",
1838
+ description: "manual Venice route; /venice toggles route+main roles",
1839
+ },
1840
+ {
1841
+ provider: "qwen",
1842
+ model: "qwen-plus",
1843
+ label: "Qwen",
1844
+ description: "manual Qwen route",
1845
+ },
1846
+ {
1847
+ provider: "mock",
1848
+ model: "mock-agent",
1849
+ label: "Mock local",
1850
+ description: "deterministic offline smoke-test provider",
1733
1851
  },
1734
1852
  ];
1735
- const filtered = common.filter((item) => (role === "route" ? item.route : item.main));
1736
- return role === "route"
1737
- ? filtered.sort((a, b) => Number(b.model === "deepseek-v4-flash") - Number(a.model === "deepseek-v4-flash"))
1738
- : filtered.sort((a, b) => Number(b.model === "deepseek-v4-pro") - Number(a.model === "deepseek-v4-pro"));
1739
1853
  }
1740
1854
 
1741
1855
  function modelChoiceLine(option) {
@@ -1773,6 +1887,7 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
1773
1887
  return new Promise((resolve) => {
1774
1888
  emitKeypressEvents(input);
1775
1889
  const wasRaw = Boolean(input.isRaw);
1890
+ const startedAt = Date.now();
1776
1891
  let selectedIndex = clamp(initialIndex, 0, Math.max(options.length - 1, 0));
1777
1892
  let lineCount = 0;
1778
1893
  const cleanup = () => {
@@ -1809,6 +1924,7 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
1809
1924
  return;
1810
1925
  }
1811
1926
  if (key.name === "return" || key.name === "enter" || key.sequence === "\r") {
1927
+ if (Date.now() - startedAt < 120) return;
1812
1928
  finish(options[selectedIndex]);
1813
1929
  }
1814
1930
  };
@@ -1822,14 +1938,35 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
1822
1938
 
1823
1939
  async function pickModelRole(role, state) {
1824
1940
  const options = modelRoleChoices(role);
1825
- const currentProvider = role === "route" ? state.routeProvider || "deepseek" : state.mainProvider || state.provider || "deepseek";
1826
- const currentModel = role === "route" ? state.routeModel || "deepseek-v4-flash" : state.mainModel || "deepseek-v4-pro";
1941
+ const currentProvider =
1942
+ role === "route"
1943
+ ? state.routeProvider || "deepseek"
1944
+ : role === "spare"
1945
+ ? state.spareProvider || "openai"
1946
+ : role === "auxiliary"
1947
+ ? state.auxiliaryProvider || "grsai"
1948
+ : state.mainProvider || state.provider || "deepseek";
1949
+ const currentModel =
1950
+ role === "route"
1951
+ ? state.routeModel || "deepseek-v4-flash"
1952
+ : role === "spare"
1953
+ ? state.spareModel || "gpt-5.4"
1954
+ : role === "auxiliary"
1955
+ ? state.auxiliaryModel || "nano-banana-2"
1956
+ : state.mainModel || "deepseek-v4-pro";
1827
1957
  const initialIndex = Math.max(
1828
1958
  options.findIndex((item) => item.provider === currentProvider && item.model === currentModel),
1829
1959
  0
1830
1960
  );
1831
1961
  const selected = await selectModelChoice({
1832
- title: role === "route" ? "Select route model" : "Select main model",
1962
+ title:
1963
+ role === "route"
1964
+ ? "Select route model"
1965
+ : role === "spare"
1966
+ ? "Select spare model"
1967
+ : role === "auxiliary"
1968
+ ? "Select auxiliary model"
1969
+ : "Select main model",
1833
1970
  subtitle: "Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
1834
1971
  options,
1835
1972
  initialIndex,
@@ -1843,6 +1980,16 @@ async function pickModelRole(role, state) {
1843
1980
  state.routeProvider = selected.provider;
1844
1981
  state.routeModel = selected.model;
1845
1982
  printSystemLine(`route=${state.routeProvider}/${state.routeModel}`);
1983
+ } else if (role === "spare") {
1984
+ state.spareProvider = selected.provider;
1985
+ state.spareModel = selected.model;
1986
+ state.spareReasoning = state.spareReasoning || "medium";
1987
+ printSystemLine(`spare=${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
1988
+ } else if (role === "auxiliary") {
1989
+ state.auxiliaryProvider = selected.provider;
1990
+ state.auxiliaryModel = selected.model;
1991
+ state.allowAuxiliaryTools = true;
1992
+ printSystemLine(`auxiliary=${state.auxiliaryProvider}/${state.auxiliaryModel}`);
1846
1993
  } else {
1847
1994
  state.mainProvider = selected.provider;
1848
1995
  state.mainModel = selected.model;
@@ -1851,6 +1998,28 @@ async function pickModelRole(role, state) {
1851
1998
  return true;
1852
1999
  }
1853
2000
 
2001
+ async function pickProvider(state) {
2002
+ const options = providerChoices();
2003
+ const currentProvider = state.provider || "deepseek";
2004
+ const initialIndex = Math.max(
2005
+ options.findIndex((item) => item.provider === currentProvider),
2006
+ 0
2007
+ );
2008
+ const selected = await selectModelChoice({
2009
+ title: "Select provider",
2010
+ subtitle: "Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
2011
+ options,
2012
+ initialIndex,
2013
+ });
2014
+ if (!selected) return false;
2015
+
2016
+ state.provider = selected.provider;
2017
+ state.model = selected.model;
2018
+ state.routingMode = selected.provider === "deepseek" ? "smart" : "manual";
2019
+ printSystemLine(`provider=${state.provider} model=${state.model} routing=${state.routingMode}`);
2020
+ return true;
2021
+ }
2022
+
1854
2023
  async function maybeOnboardDeepSeekKey(state) {
1855
2024
  if (!shouldPromptForDeepSeek(state, process.cwd())) return;
1856
2025
 
@@ -1976,7 +2145,13 @@ async function handleCommand(line, state, packageDir) {
1976
2145
  return true;
1977
2146
  }
1978
2147
  if (action.startsWith("model")) {
1979
- const selected = parseProviderModel(action.replace(/^model\s*/, ""), state.auxiliaryProvider || "grsai");
2148
+ const modelValue = action.replace(/^model\s*/, "");
2149
+ if (!modelValue.trim() && input.isTTY && output.isTTY && typeof input.setRawMode === "function") {
2150
+ const changed = await pickModelRole("auxiliary", state);
2151
+ if (!changed) printSystemLine(`auxiliary=${state.auxiliaryProvider || "grsai"}/${state.auxiliaryModel || "nano-banana-2"}`);
2152
+ return true;
2153
+ }
2154
+ const selected = parseProviderModel(modelValue, state.auxiliaryProvider || "grsai");
1980
2155
  state.auxiliaryProvider = selected.provider || "grsai";
1981
2156
  state.auxiliaryModel = selected.model || state.auxiliaryModel || "nano-banana-2";
1982
2157
  printSystemLine(`auxiliary=${state.auxiliaryProvider}/${state.auxiliaryModel}`);
@@ -2077,15 +2252,19 @@ async function handleCommand(line, state, packageDir) {
2077
2252
  return true;
2078
2253
  }
2079
2254
  if (command === "venice") {
2080
- state.routingMode = "smart";
2081
- state.provider = "deepseek";
2082
- state.model = "";
2083
- state.routeProvider = "venice";
2084
- state.routeModel = "venice-uncensored-1-2";
2085
- state.mainProvider = "venice";
2086
- state.mainModel = "venice-uncensored-1-2";
2255
+ const action = value || (isVeniceDefaultActive(state) ? "off" : "on");
2256
+ if (action === "off" || action === "deepseek" || action === "default") {
2257
+ useDeepSeekDefaults(state);
2258
+ printSystemLine("venice=off routing=smart route=deepseek/deepseek-v4-flash main=deepseek/deepseek-v4-pro");
2259
+ return true;
2260
+ }
2261
+ if (action !== "on" && action !== "venice") {
2262
+ printAgentMessage("Usage: /venice [on|off]. No argument toggles between Venice and DeepSeek defaults.");
2263
+ return true;
2264
+ }
2265
+ useVeniceDefaults(state);
2087
2266
  const keys = providerKeyStatus(process.cwd());
2088
- printSystemLine("routing=smart route=venice/venice-uncensored-1-2 main=venice/venice-uncensored-1-2");
2267
+ printSystemLine("venice=on routing=smart route=venice/venice-uncensored-1-2 main=venice/venice-uncensored-1-2");
2089
2268
  if (!keys.venice) {
2090
2269
  printAgentMessage("Venice model roles are selected, but no Venice key is configured. Run `/auth venice` to save one.");
2091
2270
  }
@@ -2154,6 +2333,11 @@ async function handleCommand(line, state, packageDir) {
2154
2333
  }
2155
2334
  if (command === "spare") {
2156
2335
  if (!value) {
2336
+ if (input.isTTY && output.isTTY && typeof input.setRawMode === "function") {
2337
+ const changed = await pickModelRole("spare", state);
2338
+ if (!changed) printSystemLine(`spare=${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
2339
+ return true;
2340
+ }
2157
2341
  printAgentMessage(`Spare model: ${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
2158
2342
  return true;
2159
2343
  }
@@ -2192,6 +2376,15 @@ async function handleCommand(line, state, packageDir) {
2192
2376
  return true;
2193
2377
  }
2194
2378
  if (command === "provider") {
2379
+ if (!value) {
2380
+ if (input.isTTY && output.isTTY && typeof input.setRawMode === "function") {
2381
+ const changed = await pickProvider(state);
2382
+ if (!changed) printSystemLine(`provider=${state.provider || "auto"} model=${state.model || "auto"}`);
2383
+ return true;
2384
+ }
2385
+ printAgentMessage(`Provider: ${state.provider || "auto"}\nUse /provider deepseek|openai|qwen|venice|mock.`);
2386
+ return true;
2387
+ }
2195
2388
  state.provider = value === "auto" ? "" : value;
2196
2389
  printSystemLine(`provider=${state.provider || "auto"}`);
2197
2390
  return true;
@@ -58,7 +58,19 @@ function prepareMessages(config, messages) {
58
58
  }
59
59
 
60
60
  function requestOptions(config) {
61
- return config.abortSignal ? { signal: config.abortSignal } : undefined;
61
+ const timeout = Number(config.modelTimeoutMs || process.env.AGINTI_MODEL_TIMEOUT_MS || 90000);
62
+ return {
63
+ ...(config.abortSignal ? { signal: config.abortSignal } : {}),
64
+ ...(Number.isFinite(timeout) && timeout > 0 ? { timeout } : {}),
65
+ };
66
+ }
67
+
68
+ function toolChoiceForProvider(config, messages = []) {
69
+ if (config.provider !== "venice") return "auto";
70
+
71
+ // Venice accepts OpenAI tool calls but often treats the first "auto" call as plain chat.
72
+ // Require one tool call to enter the agent loop, then allow normal answer/finish behavior.
73
+ return messages.some((message) => message.role === "tool") ? "auto" : "required";
62
74
  }
63
75
 
64
76
  function mockCommandForGoal(goal = "") {
@@ -858,7 +870,7 @@ export async function requestNextStep(client, config, messages) {
858
870
  {
859
871
  model: config.model,
860
872
  temperature: 0,
861
- tool_choice: "auto",
873
+ tool_choice: toolChoiceForProvider(config, messages),
862
874
  parallel_tool_calls: false,
863
875
  messages: prepareMessages(config, messages),
864
876
  tools,