@lazyingart/agintiflow 0.20.69 → 0.20.71
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 +46 -0
- package/scripts/smoke-coding-tools.js +21 -1
- package/src/agent-runner.js +49 -1
- package/src/interactive-cli.js +57 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.71",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Low-cost, project-aware Web and CLI agents with DeepSeek/Venice/OpenAI routing, visible tool calls, durable sessions, scouts, AAPS, SCS, and guarded local execution.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -24,6 +24,44 @@ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-")
|
|
|
24
24
|
const agintiflowHome = path.join(tempRoot, ".agintiflow-home");
|
|
25
25
|
const binPath = path.join(repoRoot, "bin/aginti-cli.js");
|
|
26
26
|
|
|
27
|
+
function charCellWidth(char = "") {
|
|
28
|
+
const code = char.codePointAt(0);
|
|
29
|
+
if (!code) return 0;
|
|
30
|
+
if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
|
|
31
|
+
if (
|
|
32
|
+
(code >= 0x0300 && code <= 0x036f) ||
|
|
33
|
+
(code >= 0x1ab0 && code <= 0x1aff) ||
|
|
34
|
+
(code >= 0x1dc0 && code <= 0x1dff) ||
|
|
35
|
+
(code >= 0x20d0 && code <= 0x20ff) ||
|
|
36
|
+
(code >= 0xfe20 && code <= 0xfe2f)
|
|
37
|
+
) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
code >= 0x1100 &&
|
|
42
|
+
(code <= 0x115f ||
|
|
43
|
+
code === 0x2329 ||
|
|
44
|
+
code === 0x232a ||
|
|
45
|
+
(code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f) ||
|
|
46
|
+
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
47
|
+
(code >= 0xf900 && code <= 0xfaff) ||
|
|
48
|
+
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
49
|
+
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
50
|
+
(code >= 0xff00 && code <= 0xff60) ||
|
|
51
|
+
(code >= 0xffe0 && code <= 0xffe6) ||
|
|
52
|
+
(code >= 0x1f300 && code <= 0x1f64f) ||
|
|
53
|
+
(code >= 0x1f900 && code <= 0x1f9ff) ||
|
|
54
|
+
(code >= 0x20000 && code <= 0x3fffd))
|
|
55
|
+
) {
|
|
56
|
+
return 2;
|
|
57
|
+
}
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function cellWidth(value = "") {
|
|
62
|
+
return [...String(value || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "")].reduce((sum, char) => sum + charCellWidth(char), 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
27
65
|
function runChat(inputText) {
|
|
28
66
|
return runCli(["chat", "--provider", "mock", "--routing", "manual", "--profile", "code"], inputText);
|
|
29
67
|
}
|
|
@@ -221,6 +259,14 @@ try {
|
|
|
221
259
|
if (!jaLaunchHeader.includes("低コストでプロジェクトを理解するエージェント")) {
|
|
222
260
|
throw new Error("launch header did not localize by language option");
|
|
223
261
|
}
|
|
262
|
+
const zhLaunchHeader = buildLaunchHeaderLines({ width: 80, packageVersion: "0.0.0", animated: false, language: "zh-Hans" });
|
|
263
|
+
if (zhLaunchHeader.some((line) => cellWidth(line) > 80)) {
|
|
264
|
+
throw new Error("launch header did not account for CJK terminal cell width");
|
|
265
|
+
}
|
|
266
|
+
const jaNarrowHeader = buildLaunchHeaderLines({ width: 80, packageVersion: "0.0.0", animated: false, language: "ja" });
|
|
267
|
+
if (jaNarrowHeader.some((line) => cellWidth(line) > 80)) {
|
|
268
|
+
throw new Error("Japanese launch header overflowed narrow terminal width");
|
|
269
|
+
}
|
|
224
270
|
const hugePromptLayout = buildPromptLayout(Array.from({ length: 30 }, (_unused, index) => `line ${index + 1}`).join("\n"), 120, 80, 20);
|
|
225
271
|
if (hugePromptLayout.renderedRows.length > 12 || !hugePromptLayout.renderedRows.some((line) => line.includes("earlier input row"))) {
|
|
226
272
|
throw new Error("terminal prompt layout did not bound redraw size for large prompts");
|
|
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { repairModelMessageHistory, runAgent } from "../src/agent-runner.js";
|
|
6
|
+
import { repairModelMessageHistory, runAgent, shouldShortCircuitToolBatch, skippedAfterBlockedToolResult } from "../src/agent-runner.js";
|
|
7
7
|
import { resolveRuntimeConfig } from "../src/config.js";
|
|
8
8
|
import { readCodebaseMap } from "../src/codebase-map.js";
|
|
9
9
|
import { evaluateCommandPolicy } from "../src/command-policy.js";
|
|
@@ -119,6 +119,25 @@ try {
|
|
|
119
119
|
interruptedDeepSeekState.messages.at(-1)?.content === "Continue with this new request: /review",
|
|
120
120
|
"interrupted repair dropped the new user request"
|
|
121
121
|
);
|
|
122
|
+
const blockedBatchResult = {
|
|
123
|
+
ok: false,
|
|
124
|
+
blocked: true,
|
|
125
|
+
toolName: "run_command",
|
|
126
|
+
category: "nested-aginti",
|
|
127
|
+
permissionAdvice: { category: "nested-aginti", suggestedCommand: "aginti doctor --json" },
|
|
128
|
+
};
|
|
129
|
+
assert(shouldShortCircuitToolBatch(blockedBatchResult), "permissionAdvice block did not trigger batch short-circuit");
|
|
130
|
+
const skippedBatchResult = skippedAfterBlockedToolResult(
|
|
131
|
+
{
|
|
132
|
+
id: "call-b",
|
|
133
|
+
type: "function",
|
|
134
|
+
function: { name: "run_command", arguments: "{\"command\":\"npx aginti capabilities --json\"}" },
|
|
135
|
+
},
|
|
136
|
+
blockedBatchResult
|
|
137
|
+
);
|
|
138
|
+
assert(skippedBatchResult.skipped, "skipped tool result did not mark skipped=true");
|
|
139
|
+
assert(skippedBatchResult.blocked, "skipped tool result did not remain blocked");
|
|
140
|
+
assert(skippedBatchResult.priorBlockedCategory === "nested-aginti", "skipped tool result did not preserve prior block category");
|
|
122
141
|
const completeToolState = {
|
|
123
142
|
messages: [
|
|
124
143
|
{ role: "system", content: "system" },
|
|
@@ -513,6 +532,7 @@ try {
|
|
|
513
532
|
workspace,
|
|
514
533
|
checks: [
|
|
515
534
|
"deepseek_history_repair",
|
|
535
|
+
"blocked_tool_batch_short_circuit",
|
|
516
536
|
"deepseek_pro_patch_route",
|
|
517
537
|
"large_profile_pro_route",
|
|
518
538
|
"auto_system_pro_route",
|
package/src/agent-runner.js
CHANGED
|
@@ -659,6 +659,35 @@ function sanitizeToolArgs(toolName, args) {
|
|
|
659
659
|
return safeArgs;
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
function safeParseToolArgs(toolCall) {
|
|
663
|
+
try {
|
|
664
|
+
return JSON.parse(toolCall?.function?.arguments || "{}");
|
|
665
|
+
} catch {
|
|
666
|
+
return {};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export function shouldShortCircuitToolBatch(toolResult) {
|
|
671
|
+
return Boolean(toolResult?.blocked && toolResult?.permissionAdvice);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export function skippedAfterBlockedToolResult(toolCall, blockedResult) {
|
|
675
|
+
const toolName = toolCall?.function?.name || "unknown";
|
|
676
|
+
const args = sanitizeToolArgs(toolName, safeParseToolArgs(toolCall));
|
|
677
|
+
return {
|
|
678
|
+
ok: false,
|
|
679
|
+
blocked: true,
|
|
680
|
+
skipped: true,
|
|
681
|
+
toolName,
|
|
682
|
+
args,
|
|
683
|
+
category: "blocked-batch",
|
|
684
|
+
reason:
|
|
685
|
+
"Skipped because an earlier tool call in the same assistant message returned permissionAdvice. The runtime stops the batch so the agent cannot retry variants before the user/model sees the blocker.",
|
|
686
|
+
priorBlockedTool: blockedResult?.toolName || "",
|
|
687
|
+
priorBlockedCategory: blockedResult?.category || "",
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
662
691
|
function goalClearlyAllowsOverwrite(goal = "") {
|
|
663
692
|
const text = String(goal || "").toLowerCase();
|
|
664
693
|
return (
|
|
@@ -1723,7 +1752,8 @@ export async function runAgent(config) {
|
|
|
1723
1752
|
}
|
|
1724
1753
|
|
|
1725
1754
|
let continueForQueuedInput = false;
|
|
1726
|
-
for (
|
|
1755
|
+
for (let toolIndex = 0; toolIndex < toolCalls.length; toolIndex += 1) {
|
|
1756
|
+
const toolCall = toolCalls[toolIndex];
|
|
1727
1757
|
throwIfAborted(config);
|
|
1728
1758
|
const toolResult = await executeTool(browserState, toolCall, snapshot, config, store, observers, state);
|
|
1729
1759
|
state.messages.push({
|
|
@@ -1797,6 +1827,24 @@ export async function runAgent(config) {
|
|
|
1797
1827
|
});
|
|
1798
1828
|
}
|
|
1799
1829
|
|
|
1830
|
+
if (shouldShortCircuitToolBatch(toolResult)) {
|
|
1831
|
+
for (const skippedToolCall of toolCalls.slice(toolIndex + 1)) {
|
|
1832
|
+
const skippedResult = skippedAfterBlockedToolResult(skippedToolCall, toolResult);
|
|
1833
|
+
state.messages.push({
|
|
1834
|
+
role: "tool",
|
|
1835
|
+
tool_call_id: skippedToolCall.id,
|
|
1836
|
+
content: JSON.stringify(skippedResult),
|
|
1837
|
+
});
|
|
1838
|
+
await store.appendEvent("tool.skipped", sanitizeToolResult(skippedResult));
|
|
1839
|
+
observers.event("tool.skipped", {
|
|
1840
|
+
toolName: skippedResult.toolName,
|
|
1841
|
+
reason: skippedResult.reason,
|
|
1842
|
+
priorBlockedTool: skippedResult.priorBlockedTool,
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
break;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1800
1848
|
if (config.provider === "mock" && toolResult.ok === false && !toolResult.blocked) {
|
|
1801
1849
|
throw new Error(
|
|
1802
1850
|
`Mock tool failed: ${toolResult.error || toolResult.reason || `${toolResult.toolName || "tool"} returned ok=false`}`
|
package/src/interactive-cli.js
CHANGED
|
@@ -175,8 +175,62 @@ function promptViewportRows(height = terminalHeight()) {
|
|
|
175
175
|
return Math.max(Math.min(Math.floor(Number(height) * 0.42), 10), 4);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
function stripAnsi(value) {
|
|
179
|
+
return String(value || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function charCellWidth(char = "") {
|
|
183
|
+
const code = char.codePointAt(0);
|
|
184
|
+
if (!code) return 0;
|
|
185
|
+
if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
|
|
186
|
+
if (
|
|
187
|
+
(code >= 0x0300 && code <= 0x036f) ||
|
|
188
|
+
(code >= 0x1ab0 && code <= 0x1aff) ||
|
|
189
|
+
(code >= 0x1dc0 && code <= 0x1dff) ||
|
|
190
|
+
(code >= 0x20d0 && code <= 0x20ff) ||
|
|
191
|
+
(code >= 0xfe20 && code <= 0xfe2f)
|
|
192
|
+
) {
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
if (
|
|
196
|
+
code >= 0x1100 &&
|
|
197
|
+
(code <= 0x115f ||
|
|
198
|
+
code === 0x2329 ||
|
|
199
|
+
code === 0x232a ||
|
|
200
|
+
(code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f) ||
|
|
201
|
+
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
202
|
+
(code >= 0xf900 && code <= 0xfaff) ||
|
|
203
|
+
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
204
|
+
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
205
|
+
(code >= 0xff00 && code <= 0xff60) ||
|
|
206
|
+
(code >= 0xffe0 && code <= 0xffe6) ||
|
|
207
|
+
(code >= 0x1f300 && code <= 0x1f64f) ||
|
|
208
|
+
(code >= 0x1f900 && code <= 0x1f9ff) ||
|
|
209
|
+
(code >= 0x20000 && code <= 0x3fffd))
|
|
210
|
+
) {
|
|
211
|
+
return 2;
|
|
212
|
+
}
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function stringCellWidth(value = "") {
|
|
217
|
+
return [...String(value || "")].reduce((sum, char) => sum + charCellWidth(char), 0);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function sliceToVisualWidth(value = "", width = 0) {
|
|
221
|
+
let used = 0;
|
|
222
|
+
let result = "";
|
|
223
|
+
for (const char of String(value || "")) {
|
|
224
|
+
const next = used + charCellWidth(char);
|
|
225
|
+
if (next > width) break;
|
|
226
|
+
result += char;
|
|
227
|
+
used = next;
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
178
232
|
function visualLength(value) {
|
|
179
|
-
return stripAnsi(value)
|
|
233
|
+
return stringCellWidth(stripAnsi(value));
|
|
180
234
|
}
|
|
181
235
|
|
|
182
236
|
function padVisible(value, width) {
|
|
@@ -186,7 +240,7 @@ function padVisible(value, width) {
|
|
|
186
240
|
|
|
187
241
|
function panelLine(content = "", bgCode = ansi.systemBg, width = editorWidth()) {
|
|
188
242
|
const raw = String(content || "");
|
|
189
|
-
const safeContent = visualLength(raw) > width ? stripAnsi(raw)
|
|
243
|
+
const safeContent = visualLength(raw) > width ? sliceToVisualWidth(stripAnsi(raw), width) : raw;
|
|
190
244
|
if (!useColor) return padVisible(safeContent, width);
|
|
191
245
|
const padded = padVisible(safeContent, width).replaceAll(ansi.reset, `${ansi.reset}${bgCode}`);
|
|
192
246
|
return `${bgCode}${padded}${ansi.reset}`;
|
|
@@ -203,10 +257,6 @@ function commandCompleter(line = "") {
|
|
|
203
257
|
return [hits.length > 0 ? hits : SLASH_COMMANDS, trimmed];
|
|
204
258
|
}
|
|
205
259
|
|
|
206
|
-
function stripAnsi(value) {
|
|
207
|
-
return String(value || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
260
|
function promptGutter() {
|
|
211
261
|
const visible = stripAnsi(userPrompt()).replace(/^\n/, "").length;
|
|
212
262
|
return " ".repeat(Math.max(visible - 2, 0)) + `${color("|", ansi.userBg)} `;
|
|
@@ -253,7 +303,7 @@ function clamp(value, min, max) {
|
|
|
253
303
|
|
|
254
304
|
function compactLine(value = "", limit = 96) {
|
|
255
305
|
const text = stripAnsi(String(value || "").replace(/\s+/g, " ").trim());
|
|
256
|
-
return text
|
|
306
|
+
return visualLength(text) <= limit ? text : `${sliceToVisualWidth(text, Math.max(limit - 1, 1))}…`;
|
|
257
307
|
}
|
|
258
308
|
|
|
259
309
|
export function formatElapsedDuration(ms = 0) {
|