@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 +1 -1
- package/scripts/smoke-cli-chat.js +17 -1
- package/scripts/smoke-model-roles.js +14 -1
- package/src/interactive-cli.js +16 -2
- package/src/model-client.js +55 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
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 {
|
|
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: [
|
|
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
|
package/src/interactive-cli.js
CHANGED
|
@@ -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);
|
package/src/model-client.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|