@lazyingart/agintiflow 0.20.0 → 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.",
@@ -749,6 +749,7 @@ export function buildPromptLayout(buffer = "", cursor = 0, width = terminalWidth
749
749
  const visible = promptVisibleWindow(rows, cursorRow, height);
750
750
  const renderedRows = [];
751
751
  let renderedCursorRow = cursorRow - visible.start;
752
+ const useInputPadding = options.inputPadding !== false && height >= 10;
752
753
 
753
754
  if (options.statusLine) {
754
755
  renderedRows.push(panelLine(`${labelText("run", { prompt: true })}${compactLine(options.statusLine, lineWidth - 10)}`, ansi.statusBg, lineWidth));
@@ -771,11 +772,20 @@ export function buildPromptLayout(buffer = "", cursor = 0, width = terminalWidth
771
772
  renderedCursorRow += 1;
772
773
  }
773
774
 
775
+ if (useInputPadding) {
776
+ renderedRows.push(panelLine("", ansi.userBg, lineWidth));
777
+ renderedCursorRow += 1;
778
+ }
779
+
774
780
  for (const row of rows.slice(visible.start, visible.end)) {
775
781
  const content = safeBuffer ? `${row.prefix}${row.text}` : `${row.prefix}${emptyHint}`;
776
782
  renderedRows.push(panelLine(content, ansi.userBg, lineWidth));
777
783
  }
778
784
 
785
+ if (useInputPadding) {
786
+ renderedRows.push(panelLine("", ansi.userBg, lineWidth));
787
+ }
788
+
779
789
  if (visible.bottomHidden > 0) {
780
790
  renderedRows.push(panelLine(` ... ${visible.bottomHidden} later input row${visible.bottomHidden === 1 ? "" : "s"}`, ansi.systemBg, lineWidth));
781
791
  }
@@ -1258,6 +1268,11 @@ class LiveRunInput {
1258
1268
  async submitAsap() {
1259
1269
  const content = this.buffer.trim();
1260
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
+ }
1261
1276
  const item = {
1262
1277
  id: `cli-asap-${Date.now()}-${Math.random().toString(16).slice(2)}`,
1263
1278
  content,
@@ -1282,6 +1297,11 @@ class LiveRunInput {
1282
1297
  queueAfterFinish() {
1283
1298
  const content = this.buffer.trim();
1284
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
+ }
1285
1305
  this.pendingQueued.push({
1286
1306
  id: `cli-queued-${Date.now()}-${Math.random().toString(16).slice(2)}`,
1287
1307
  content,
@@ -1596,6 +1616,35 @@ function parseProviderModel(value, fallbackProvider = "") {
1596
1616
  return { provider: fallbackProvider, model: text };
1597
1617
  }
1598
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
+
1599
1648
  function printModelRoles(state) {
1600
1649
  const roles = getModelRoleDefaults({
1601
1650
  routeProvider: state.routeProvider,
@@ -1669,6 +1718,7 @@ function modelRoleChoices(role = "main") {
1669
1718
  label: "DeepSeek V4 Pro",
1670
1719
  description: "complex coding, debugging, writing, and long tasks",
1671
1720
  main: true,
1721
+ spare: true,
1672
1722
  },
1673
1723
  {
1674
1724
  provider: "venice",
@@ -1677,6 +1727,7 @@ function modelRoleChoices(role = "main") {
1677
1727
  description: "Venice text route; use /auth venice if missing",
1678
1728
  route: true,
1679
1729
  main: true,
1730
+ spare: true,
1680
1731
  },
1681
1732
  {
1682
1733
  provider: "openai",
@@ -1684,6 +1735,7 @@ function modelRoleChoices(role = "main") {
1684
1735
  label: "OpenAI GPT-5.5",
1685
1736
  description: "frontier spare/manual model",
1686
1737
  main: true,
1738
+ spare: true,
1687
1739
  },
1688
1740
  {
1689
1741
  provider: "openai",
@@ -1691,6 +1743,7 @@ function modelRoleChoices(role = "main") {
1691
1743
  label: "OpenAI GPT-5.4",
1692
1744
  description: "strong everyday coding spare",
1693
1745
  main: true,
1746
+ spare: true,
1694
1747
  },
1695
1748
  {
1696
1749
  provider: "openai",
@@ -1698,6 +1751,7 @@ function modelRoleChoices(role = "main") {
1698
1751
  label: "OpenAI GPT-5.4 Mini",
1699
1752
  description: "small fast spare route",
1700
1753
  route: true,
1754
+ spare: true,
1701
1755
  },
1702
1756
  {
1703
1757
  provider: "qwen",
@@ -1712,6 +1766,7 @@ function modelRoleChoices(role = "main") {
1712
1766
  label: "Qwen Max",
1713
1767
  description: "larger Qwen route",
1714
1768
  main: true,
1769
+ spare: true,
1715
1770
  },
1716
1771
  {
1717
1772
  provider: "mock",
@@ -1720,12 +1775,81 @@ function modelRoleChoices(role = "main") {
1720
1775
  description: "local deterministic test route",
1721
1776
  route: true,
1722
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",
1723
1851
  },
1724
1852
  ];
1725
- const filtered = common.filter((item) => (role === "route" ? item.route : item.main));
1726
- return role === "route"
1727
- ? filtered.sort((a, b) => Number(b.model === "deepseek-v4-flash") - Number(a.model === "deepseek-v4-flash"))
1728
- : filtered.sort((a, b) => Number(b.model === "deepseek-v4-pro") - Number(a.model === "deepseek-v4-pro"));
1729
1853
  }
1730
1854
 
1731
1855
  function modelChoiceLine(option) {
@@ -1763,6 +1887,7 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
1763
1887
  return new Promise((resolve) => {
1764
1888
  emitKeypressEvents(input);
1765
1889
  const wasRaw = Boolean(input.isRaw);
1890
+ const startedAt = Date.now();
1766
1891
  let selectedIndex = clamp(initialIndex, 0, Math.max(options.length - 1, 0));
1767
1892
  let lineCount = 0;
1768
1893
  const cleanup = () => {
@@ -1799,6 +1924,7 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
1799
1924
  return;
1800
1925
  }
1801
1926
  if (key.name === "return" || key.name === "enter" || key.sequence === "\r") {
1927
+ if (Date.now() - startedAt < 120) return;
1802
1928
  finish(options[selectedIndex]);
1803
1929
  }
1804
1930
  };
@@ -1812,14 +1938,35 @@ function selectModelChoice({ title, subtitle, options, initialIndex = 0 }) {
1812
1938
 
1813
1939
  async function pickModelRole(role, state) {
1814
1940
  const options = modelRoleChoices(role);
1815
- const currentProvider = role === "route" ? state.routeProvider || "deepseek" : state.mainProvider || state.provider || "deepseek";
1816
- 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";
1817
1957
  const initialIndex = Math.max(
1818
1958
  options.findIndex((item) => item.provider === currentProvider && item.model === currentModel),
1819
1959
  0
1820
1960
  );
1821
1961
  const selected = await selectModelChoice({
1822
- 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",
1823
1970
  subtitle: "Up/Down/Left/Right selects, Enter confirms, Esc cancels.",
1824
1971
  options,
1825
1972
  initialIndex,
@@ -1833,6 +1980,16 @@ async function pickModelRole(role, state) {
1833
1980
  state.routeProvider = selected.provider;
1834
1981
  state.routeModel = selected.model;
1835
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}`);
1836
1993
  } else {
1837
1994
  state.mainProvider = selected.provider;
1838
1995
  state.mainModel = selected.model;
@@ -1841,6 +1998,28 @@ async function pickModelRole(role, state) {
1841
1998
  return true;
1842
1999
  }
1843
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
+
1844
2023
  async function maybeOnboardDeepSeekKey(state) {
1845
2024
  if (!shouldPromptForDeepSeek(state, process.cwd())) return;
1846
2025
 
@@ -1966,7 +2145,13 @@ async function handleCommand(line, state, packageDir) {
1966
2145
  return true;
1967
2146
  }
1968
2147
  if (action.startsWith("model")) {
1969
- 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");
1970
2155
  state.auxiliaryProvider = selected.provider || "grsai";
1971
2156
  state.auxiliaryModel = selected.model || state.auxiliaryModel || "nano-banana-2";
1972
2157
  printSystemLine(`auxiliary=${state.auxiliaryProvider}/${state.auxiliaryModel}`);
@@ -2067,15 +2252,19 @@ async function handleCommand(line, state, packageDir) {
2067
2252
  return true;
2068
2253
  }
2069
2254
  if (command === "venice") {
2070
- state.routingMode = "smart";
2071
- state.provider = "deepseek";
2072
- state.model = "";
2073
- state.routeProvider = "venice";
2074
- state.routeModel = "venice-uncensored-1-2";
2075
- state.mainProvider = "venice";
2076
- 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);
2077
2266
  const keys = providerKeyStatus(process.cwd());
2078
- 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");
2079
2268
  if (!keys.venice) {
2080
2269
  printAgentMessage("Venice model roles are selected, but no Venice key is configured. Run `/auth venice` to save one.");
2081
2270
  }
@@ -2144,6 +2333,11 @@ async function handleCommand(line, state, packageDir) {
2144
2333
  }
2145
2334
  if (command === "spare") {
2146
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
+ }
2147
2341
  printAgentMessage(`Spare model: ${state.spareProvider}/${state.spareModel} reasoning=${state.spareReasoning}`);
2148
2342
  return true;
2149
2343
  }
@@ -2182,6 +2376,15 @@ async function handleCommand(line, state, packageDir) {
2182
2376
  return true;
2183
2377
  }
2184
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
+ }
2185
2388
  state.provider = value === "auto" ? "" : value;
2186
2389
  printSystemLine(`provider=${state.provider || "auto"}`);
2187
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,