@lazyingart/agintiflow 0.20.2 → 0.20.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.2",
3
+ "version": "0.20.4",
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
@@ -1136,7 +1136,9 @@ function renderModelOptions() {
1136
1136
  modelRoutePillEl.textContent =
1137
1137
  mode === "manual"
1138
1138
  ? `${provider} · ${primary}`
1139
- : `${mode} · route ${routeLabel} · main ${mainLabel}`;
1139
+ : `${mode} · ${routeLabel} -> ${mainLabel}`;
1140
+ modelRoutePillEl.title =
1141
+ mode === "manual" ? `Manual route: ${provider}/${primary}` : `Route: ${routeLabel}. Main: ${mainLabel}.`;
1140
1142
  }
1141
1143
  if (!modelCatalogEl) return;
1142
1144
  if (options.length === 0) {
package/public/index.html CHANGED
@@ -114,14 +114,7 @@
114
114
 
115
115
  <p id="routing-hint" class="subtle route-hint"></p>
116
116
  <p id="model-route-status" class="subtle route-hint"></p>
117
- <section class="model-catalog-panel" aria-label="Model catalog">
118
- <div class="model-catalog-head">
119
- <span>Route</span>
120
- <strong id="model-route-pill">DeepSeek flash/pro</strong>
121
- </div>
122
- <div id="model-role-grid" class="model-role-grid"></div>
123
- <div id="model-catalog" class="model-catalog"></div>
124
- </section>
117
+ <p id="model-route-pill" class="route-pill">DeepSeek flash/pro</p>
125
118
 
126
119
  <label>
127
120
  <span data-i18n="taskProfileLabel">Task profile</span>
@@ -381,6 +374,15 @@
381
374
  </div>
382
375
  </section>
383
376
 
377
+ <section class="settings-section model-catalog-panel" aria-label="Model catalog">
378
+ <div class="model-catalog-head">
379
+ <span>Catalog</span>
380
+ <strong>Provider models</strong>
381
+ </div>
382
+ <div id="model-role-grid" class="model-role-grid"></div>
383
+ <div id="model-catalog" class="model-catalog"></div>
384
+ </section>
385
+
384
386
  <section class="settings-section">
385
387
  <div class="settings-section-head">
386
388
  <strong>Browser and target</strong>
package/public/styles.css CHANGED
@@ -139,6 +139,22 @@ h1 {
139
139
  #fffdfa;
140
140
  }
141
141
 
142
+ .route-pill {
143
+ max-width: 100%;
144
+ overflow: hidden;
145
+ margin: -2px 0 0;
146
+ padding: 8px 10px;
147
+ border: 1px solid rgba(15, 118, 110, 0.16);
148
+ border-radius: 999px;
149
+ background: rgba(204, 251, 241, 0.42);
150
+ color: #0f766e;
151
+ font-size: 0.78rem;
152
+ font-weight: 800;
153
+ line-height: 1.25;
154
+ text-overflow: ellipsis;
155
+ white-space: nowrap;
156
+ }
157
+
142
158
  .model-catalog-head {
143
159
  display: flex;
144
160
  align-items: center;
@@ -168,7 +184,7 @@ h1 {
168
184
  .model-role-grid {
169
185
  display: grid;
170
186
  gap: 8px;
171
- grid-template-columns: repeat(2, minmax(0, 1fr));
187
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
172
188
  }
173
189
 
174
190
  .model-role-card {
@@ -244,7 +260,7 @@ h1 {
244
260
  .model-chip {
245
261
  display: grid;
246
262
  gap: 2px;
247
- flex: 1 1 148px;
263
+ flex: 1 1 180px;
248
264
  min-width: 0;
249
265
  padding: 9px 11px;
250
266
  border: 1px solid rgba(15, 118, 110, 0.2);
@@ -4,7 +4,14 @@ import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { buildLaunchHeaderLines, buildPromptLayout, classifyEscapeAction, formatWorkspaceChange, stripMarkdown } from "../src/interactive-cli.js";
7
+ import {
8
+ buildLaunchHeaderLines,
9
+ buildPromptLayout,
10
+ canonicalSlashPromptBuffer,
11
+ classifyEscapeAction,
12
+ formatWorkspaceChange,
13
+ stripMarkdown,
14
+ } from "../src/interactive-cli.js";
8
15
 
9
16
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
17
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-"));
@@ -179,6 +186,14 @@ try {
179
186
  if (classifyEscapeAction({ active: true, pendingAsap: [] }) !== "abort") {
180
187
  throw new Error("active Esc should abort when no ASAP pipe messages are pending");
181
188
  }
189
+ if (
190
+ canonicalSlashPromptBuffer("/ve") !== "/venice" ||
191
+ canonicalSlashPromptBuffer("/v") !== "/venice" ||
192
+ canonicalSlashPromptBuffer("/not-a-command") !== "/not-a-command" ||
193
+ canonicalSlashPromptBuffer("/venice off") !== "/venice off"
194
+ ) {
195
+ throw new Error("slash command prompt canonicalization did not preserve final submitted command correctly");
196
+ }
182
197
 
183
198
  await runCli(["init"], "");
184
199
  const instructions = await fs.readFile(path.join(tempRoot, "AGINTI.md"), "utf8");
@@ -265,6 +280,7 @@ try {
265
280
  "auxiliary-command-spelling",
266
281
  "skills-command",
267
282
  "slash-prefix-autoselect",
283
+ "slash-prefix-canonical-history",
268
284
  "instructions-chat-edit",
269
285
  "interactive-chat",
270
286
  "mock-file-write",
@@ -9,6 +9,7 @@ import {
9
9
  modelsForProviderGroup,
10
10
  selectModelRoute,
11
11
  } from "../src/model-routing.js";
12
+ import { parseTextToolCalls } from "../src/model-client.js";
12
13
 
13
14
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
14
15
 
@@ -109,6 +110,10 @@ assert(fastRoute.model === "deepseek-v4-flash", "fast route did not use route mo
109
110
  assert(MODEL_PROVIDER_GROUPS["venice-gpt"].provider === "venice", "venice-gpt group missing");
110
111
  assert(modelsForProviderGroup("venice-gemma").some((item) => item.id === "gemma-4-uncensored"), "venice-gemma bucket missing Gemma");
111
112
  assert(AUXILIARY_MODEL_CATALOG["venice-image"].some((item) => item.id === "gpt-image-2"), "Venice image catalog missing GPT Image 2");
113
+ const parsedTextToolCalls = parseTextToolCalls('[TOOL_CALLS]list_files[ARGS]call_123[ARGS]{"path":".","maxDepth":1}');
114
+ assert(parsedTextToolCalls.length === 1, "Venice text tool-call parser did not detect encoded tool call");
115
+ assert(parsedTextToolCalls[0].function.name === "list_files", "Venice text tool-call parser returned wrong tool name");
116
+ assert(parsedTextToolCalls[0].function.arguments.includes('"maxDepth":1'), "Venice text tool-call parser returned wrong arguments");
112
117
 
113
118
  const output = await runCli(["models"]);
114
119
  assert(output.includes("/route") && output.includes("/spare") && output.includes("venice-gpt"), "aginti models output missing role details");
@@ -125,7 +130,15 @@ console.log(
125
130
  JSON.stringify(
126
131
  {
127
132
  ok: true,
128
- checks: ["role-defaults", "route-overrides", "provider-groups", "auxiliary-catalog", "cli-models-command", "venice-shortcut"],
133
+ checks: [
134
+ "role-defaults",
135
+ "route-overrides",
136
+ "provider-groups",
137
+ "auxiliary-catalog",
138
+ "venice-text-tool-parser",
139
+ "cli-models-command",
140
+ "venice-shortcut",
141
+ ],
129
142
  },
130
143
  null,
131
144
  2
@@ -194,6 +194,15 @@ function resolveSlashCommand(command = "") {
194
194
  return suggestion ? suggestion.slice(1) : raw;
195
195
  }
196
196
 
197
+ export function canonicalSlashPromptBuffer(value = "") {
198
+ const text = String(value || "");
199
+ const trimmed = text.trim();
200
+ if (!trimmed.startsWith("/") || /\s/.test(trimmed)) return text;
201
+ const raw = trimmed.slice(1);
202
+ const resolved = resolveSlashCommand(raw);
203
+ return resolved === raw ? text : `/${resolved}`;
204
+ }
205
+
197
206
  function clamp(value, min, max) {
198
207
  return Math.min(Math.max(value, min), max);
199
208
  }
@@ -944,6 +953,11 @@ function readTtyPrompt(options = {}) {
944
953
  };
945
954
 
946
955
  const submit = () => {
956
+ const canonical = canonicalSlashPromptBuffer(buffer);
957
+ if (canonical !== buffer) {
958
+ buffer = canonical;
959
+ cursor = buffer.length;
960
+ }
947
961
  renderNow();
948
962
  moveToPromptBottom(rendered);
949
963
  cleanup();
@@ -2607,7 +2621,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
2607
2621
  }
2608
2622
  throw error;
2609
2623
  }
2610
- const line = answer.trim();
2624
+ const line = canonicalSlashPromptBuffer(answer).trim();
2611
2625
  if (!line) continue;
2612
2626
  if (line.startsWith("/")) {
2613
2627
  const keepGoing = await handleCommand(line, state, packageDir);
@@ -2619,7 +2633,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
2619
2633
  const pendingPrompts = [{ content: line }];
2620
2634
  while (pendingPrompts.length > 0) {
2621
2635
  const nextPrompt = pendingPrompts.shift();
2622
- const content = String(nextPrompt.content || "").trim();
2636
+ const content = canonicalSlashPromptBuffer(String(nextPrompt.content || "")).trim();
2623
2637
  if (!content) continue;
2624
2638
  if (content.startsWith("/")) {
2625
2639
  const keepGoing = await handleCommand(content, state, packageDir);
@@ -73,6 +73,59 @@ function toolChoiceForProvider(config, messages = []) {
73
73
  return messages.some((message) => message.role === "tool") ? "auto" : "required";
74
74
  }
75
75
 
76
+ export function parseTextToolCalls(content = "") {
77
+ const text = String(content || "");
78
+ if (!text.includes("[TOOL_CALLS]")) return [];
79
+
80
+ const calls = [];
81
+ const pattern = /\[TOOL_CALLS\]([A-Za-z0-9_-]+)\[ARGS\]([A-Za-z0-9_.:-]+)\[ARGS\]([\s\S]*?)(?=\[TOOL_CALLS\]|$)/g;
82
+ for (const match of text.matchAll(pattern)) {
83
+ const name = match[1]?.trim();
84
+ const id = match[2]?.trim() || `text-tool-${calls.length + 1}`;
85
+ const rawArgs = match[3]?.trim() || "{}";
86
+ if (!name) continue;
87
+ try {
88
+ JSON.parse(rawArgs);
89
+ } catch {
90
+ continue;
91
+ }
92
+ calls.push({
93
+ id,
94
+ type: "function",
95
+ function: {
96
+ name,
97
+ arguments: rawArgs,
98
+ },
99
+ });
100
+ }
101
+ return calls;
102
+ }
103
+
104
+ function normalizeTextToolCallResponse(response) {
105
+ const message = response?.choices?.[0]?.message;
106
+ if (!message || Array.isArray(message.tool_calls) && message.tool_calls.length > 0) return response;
107
+
108
+ const calls = parseTextToolCalls(message.content || "");
109
+ if (calls.length === 0) return response;
110
+
111
+ const content = String(message.content || "").split("[TOOL_CALLS]")[0].trim();
112
+ return {
113
+ ...response,
114
+ choices: response.choices.map((choice, index) =>
115
+ index === 0
116
+ ? {
117
+ ...choice,
118
+ message: {
119
+ ...message,
120
+ content,
121
+ tool_calls: calls,
122
+ },
123
+ }
124
+ : choice
125
+ ),
126
+ };
127
+ }
128
+
76
129
  function mockCommandForGoal(goal = "") {
77
130
  const text = String(goal).toLowerCase();
78
131
  if (/\blist\b|folder contents|directory contents|files?/.test(text)) return "ls -la";
@@ -866,7 +919,7 @@ export async function requestNextStep(client, config, messages) {
866
919
  ]);
867
920
  }
868
921
 
869
- return client.chat.completions.create(
922
+ const response = await client.chat.completions.create(
870
923
  {
871
924
  model: config.model,
872
925
  temperature: 0,
@@ -877,4 +930,5 @@ export async function requestNextStep(client, config, messages) {
877
930
  },
878
931
  requestOptions(config)
879
932
  );
933
+ return normalizeTextToolCallResponse(response);
880
934
  }