@lazyingart/agintiflow 0.20.3 → 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.3",
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",
@@ -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
  }