@lazyingart/agintiflow 0.20.74 → 0.20.75
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-coding-tools.js +26 -0
- package/src/agent-runner.js +88 -55
- package/src/model-client.js +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.75",
|
|
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",
|
|
@@ -78,6 +78,31 @@ try {
|
|
|
78
78
|
};
|
|
79
79
|
const repair = repairModelMessageHistory(staleDeepSeekState, { provider: "deepseek" });
|
|
80
80
|
assert(repair.changed, "stale DeepSeek history was not repaired");
|
|
81
|
+
|
|
82
|
+
const interleavedToolState = {
|
|
83
|
+
messages: [
|
|
84
|
+
{ role: "system", content: "system" },
|
|
85
|
+
{ role: "user", content: "do guarded writes" },
|
|
86
|
+
{
|
|
87
|
+
role: "assistant",
|
|
88
|
+
content: "",
|
|
89
|
+
tool_calls: [
|
|
90
|
+
{ id: "call-a", type: "function", function: { name: "write_file", arguments: "{\"path\":\".env\",\"content\":\"TOKEN=blocked\"}" } },
|
|
91
|
+
{ id: "call-b", type: "function", function: { name: "write_file", arguments: "{\"path\":\"notes/ok.md\",\"content\":\"ok\"}" } },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{ role: "tool", tool_call_id: "call-a", content: "{\"ok\":false,\"blocked\":true}" },
|
|
95
|
+
{ role: "user", content: "Loop guard: do not repeat the blocked call." },
|
|
96
|
+
{ role: "tool", tool_call_id: "call-b", content: "{\"ok\":false,\"skipped\":true}" },
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
const interleavedRepair = repairModelMessageHistory(interleavedToolState, { provider: "openai" });
|
|
100
|
+
assert(interleavedRepair.changed, "interleaved tool-call history repair did not report a change");
|
|
101
|
+
const roles = interleavedToolState.messages.map((message) => `${message.role}:${message.tool_call_id || ""}`);
|
|
102
|
+
assert(
|
|
103
|
+
roles.join("|") === "system:|user:|assistant:|tool:call-a|tool:call-b|user:",
|
|
104
|
+
`interleaved tool-call history was not repaired into provider-valid order: ${roles.join("|")}`
|
|
105
|
+
);
|
|
81
106
|
assert(
|
|
82
107
|
staleDeepSeekState.messages.every(
|
|
83
108
|
(message) => message.role !== "assistant" || message.reasoning_content || message.reasoningContent
|
|
@@ -596,6 +621,7 @@ try {
|
|
|
596
621
|
workspace,
|
|
597
622
|
checks: [
|
|
598
623
|
"deepseek_history_repair",
|
|
624
|
+
"interleaved_tool_history_repair",
|
|
599
625
|
"blocked_tool_batch_short_circuit",
|
|
600
626
|
"deepseek_pro_patch_route",
|
|
601
627
|
"runtime_time_context",
|
package/src/agent-runner.js
CHANGED
|
@@ -213,14 +213,23 @@ function normalizeUrlPath(relativePath) {
|
|
|
213
213
|
function preserveAssistantMessage(message) {
|
|
214
214
|
const preserved = {
|
|
215
215
|
role: "assistant",
|
|
216
|
-
content: message.content || "",
|
|
217
|
-
tool_calls: message.tool_calls
|
|
216
|
+
content: redactSensitiveText(message.content || ""),
|
|
217
|
+
tool_calls: Array.isArray(message.tool_calls)
|
|
218
|
+
? message.tool_calls.map((call) => ({
|
|
219
|
+
...call,
|
|
220
|
+
function: {
|
|
221
|
+
...(call.function || {}),
|
|
222
|
+
arguments:
|
|
223
|
+
typeof call.function?.arguments === "string"
|
|
224
|
+
? redactSensitiveText(call.function.arguments)
|
|
225
|
+
: call.function?.arguments,
|
|
226
|
+
},
|
|
227
|
+
}))
|
|
228
|
+
: message.tool_calls,
|
|
218
229
|
};
|
|
219
230
|
|
|
220
231
|
const reasoningContent = message.reasoning_content || message.reasoningContent;
|
|
221
|
-
if (reasoningContent)
|
|
222
|
-
preserved.reasoning_content = reasoningContent;
|
|
223
|
-
}
|
|
232
|
+
if (reasoningContent) preserved.reasoning_content = redactSensitiveText(reasoningContent);
|
|
224
233
|
|
|
225
234
|
return preserved;
|
|
226
235
|
}
|
|
@@ -233,6 +242,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
233
242
|
convertedAssistantMessages: 0,
|
|
234
243
|
droppedToolMessages: 0,
|
|
235
244
|
incompleteToolCallMessages: 0,
|
|
245
|
+
reorderedInterleavedMessages: 0,
|
|
236
246
|
};
|
|
237
247
|
}
|
|
238
248
|
|
|
@@ -241,6 +251,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
241
251
|
let convertedAssistantMessages = 0;
|
|
242
252
|
let droppedToolMessages = 0;
|
|
243
253
|
let incompleteToolCallMessages = 0;
|
|
254
|
+
let reorderedInterleavedMessages = 0;
|
|
244
255
|
|
|
245
256
|
for (let index = 0; index < state.messages.length; index += 1) {
|
|
246
257
|
const message = state.messages[index];
|
|
@@ -254,12 +265,21 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
254
265
|
const expectedIds = toolCalls.map((call) => String(call?.id || "")).filter(Boolean);
|
|
255
266
|
const expected = new Set(expectedIds);
|
|
256
267
|
const followingToolMessages = [];
|
|
268
|
+
const deferredInterleavedMessages = [];
|
|
257
269
|
const duplicateOrUnexpectedToolMessages = [];
|
|
258
270
|
const seen = new Set();
|
|
259
271
|
let cursor = index + 1;
|
|
260
272
|
|
|
261
|
-
while (cursor < state.messages.length
|
|
262
|
-
const
|
|
273
|
+
while (cursor < state.messages.length) {
|
|
274
|
+
const nextMessage = state.messages[cursor];
|
|
275
|
+
if (nextMessage?.role !== "tool") {
|
|
276
|
+
const complete = expectedIds.length > 0 && expectedIds.every((id) => seen.has(id));
|
|
277
|
+
if (complete || nextMessage?.role === "assistant") break;
|
|
278
|
+
deferredInterleavedMessages.push(nextMessage);
|
|
279
|
+
cursor += 1;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const toolMessage = nextMessage;
|
|
263
283
|
const toolCallId = String(toolMessage.tool_call_id || "");
|
|
264
284
|
if (expected.has(toolCallId) && !seen.has(toolCallId)) {
|
|
265
285
|
followingToolMessages.push(toolMessage);
|
|
@@ -275,12 +295,18 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
275
295
|
droppedAssistantMessages += 1;
|
|
276
296
|
droppedToolMessages += followingToolMessages.length + duplicateOrUnexpectedToolMessages.length;
|
|
277
297
|
if (!completeToolResults) incompleteToolCallMessages += 1;
|
|
298
|
+
if (deferredInterleavedMessages.length) {
|
|
299
|
+
repaired.push(...deferredInterleavedMessages);
|
|
300
|
+
reorderedInterleavedMessages += deferredInterleavedMessages.length;
|
|
301
|
+
}
|
|
278
302
|
index = cursor - 1;
|
|
279
303
|
continue;
|
|
280
304
|
}
|
|
281
305
|
|
|
282
306
|
repaired.push(preserveAssistantMessage(message));
|
|
283
307
|
repaired.push(...followingToolMessages);
|
|
308
|
+
repaired.push(...deferredInterleavedMessages);
|
|
309
|
+
reorderedInterleavedMessages += deferredInterleavedMessages.length;
|
|
284
310
|
droppedToolMessages += duplicateOrUnexpectedToolMessages.length;
|
|
285
311
|
index = cursor - 1;
|
|
286
312
|
continue;
|
|
@@ -320,6 +346,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
320
346
|
droppedAssistantMessages > 0 ||
|
|
321
347
|
convertedAssistantMessages > 0 ||
|
|
322
348
|
droppedToolMessages > 0 ||
|
|
349
|
+
reorderedInterleavedMessages > 0 ||
|
|
323
350
|
repaired.length !== state.messages.length;
|
|
324
351
|
|
|
325
352
|
if (changed) {
|
|
@@ -332,6 +359,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
332
359
|
convertedAssistantMessages,
|
|
333
360
|
droppedToolMessages,
|
|
334
361
|
incompleteToolCallMessages,
|
|
362
|
+
reorderedInterleavedMessages,
|
|
335
363
|
};
|
|
336
364
|
}
|
|
337
365
|
|
|
@@ -1372,7 +1400,7 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
1372
1400
|
return result;
|
|
1373
1401
|
}
|
|
1374
1402
|
case "finish":
|
|
1375
|
-
return { ok: true, done: true, result: String(args.result || ""), toolName: "finish" };
|
|
1403
|
+
return { ok: true, done: true, result: redactSensitiveText(String(args.result || "")), toolName: "finish" };
|
|
1376
1404
|
default:
|
|
1377
1405
|
throw new Error(`Unknown tool: ${toolCall.function.name}`);
|
|
1378
1406
|
}
|
|
@@ -1481,7 +1509,7 @@ export async function runAgent(config) {
|
|
|
1481
1509
|
taskProfile: config.taskProfile,
|
|
1482
1510
|
goal: config.goal,
|
|
1483
1511
|
});
|
|
1484
|
-
state.plan = scsPlan.plan;
|
|
1512
|
+
state.plan = redactSensitiveText(scsPlan.plan);
|
|
1485
1513
|
state.meta.scs = scsPlan.scs;
|
|
1486
1514
|
state.messages.push({
|
|
1487
1515
|
role: "user",
|
|
@@ -1496,7 +1524,7 @@ export async function runAgent(config) {
|
|
|
1496
1524
|
await store.appendEvent("scs.committee.plan_drafted", {
|
|
1497
1525
|
phase: scsPlan.scs.phase,
|
|
1498
1526
|
phaseGoal: scsPlan.scs.phaseGoal,
|
|
1499
|
-
plan:
|
|
1527
|
+
plan: state.plan,
|
|
1500
1528
|
acceptanceCriteria: scsPlan.scs.acceptanceCriteria,
|
|
1501
1529
|
});
|
|
1502
1530
|
await store.appendEvent(`scs.student.${scsPlan.scs.student.decision}`, scsPlan.scs.student);
|
|
@@ -1504,16 +1532,16 @@ export async function runAgent(config) {
|
|
|
1504
1532
|
phase: scsPlan.scs.phase,
|
|
1505
1533
|
phaseGoal: scsPlan.scs.phaseGoal,
|
|
1506
1534
|
});
|
|
1507
|
-
await store.savePlan(
|
|
1508
|
-
await store.appendEvent("plan.created", { plan:
|
|
1535
|
+
await store.savePlan(state.plan);
|
|
1536
|
+
await store.appendEvent("plan.created", { plan: state.plan, scs: true });
|
|
1509
1537
|
await store.saveState(state);
|
|
1510
|
-
observers.event("plan.created", { plan:
|
|
1538
|
+
observers.event("plan.created", { plan: state.plan, scs: true });
|
|
1511
1539
|
observers.event("scs.student.approve_plan", scsPlan.scs.student);
|
|
1512
1540
|
emitConsole(config, `SCS: student approved phase plan (${Math.round((scsPlan.scs.student.confidence || 0) * 100)}%).`, {
|
|
1513
1541
|
kind: "meta",
|
|
1514
1542
|
});
|
|
1515
1543
|
} else {
|
|
1516
|
-
const plan = await createPlan(client, config, state);
|
|
1544
|
+
const plan = redactSensitiveText(await createPlan(client, config, state));
|
|
1517
1545
|
state.plan = plan;
|
|
1518
1546
|
await store.savePlan(plan);
|
|
1519
1547
|
await store.appendEvent("plan.created", { plan });
|
|
@@ -1718,7 +1746,7 @@ export async function runAgent(config) {
|
|
|
1718
1746
|
|
|
1719
1747
|
await store.appendEvent("model.responded", {
|
|
1720
1748
|
step,
|
|
1721
|
-
content: assistantMessage.content || "",
|
|
1749
|
+
content: redactSensitiveText(assistantMessage.content || ""),
|
|
1722
1750
|
toolCalls: (assistantMessage.tool_calls || []).map((call) => ({
|
|
1723
1751
|
id: call.id,
|
|
1724
1752
|
name: call.function.name,
|
|
@@ -1727,7 +1755,7 @@ export async function runAgent(config) {
|
|
|
1727
1755
|
});
|
|
1728
1756
|
observers.event("model.responded", {
|
|
1729
1757
|
step,
|
|
1730
|
-
content: assistantMessage.content || "",
|
|
1758
|
+
content: redactSensitiveText(assistantMessage.content || ""),
|
|
1731
1759
|
});
|
|
1732
1760
|
|
|
1733
1761
|
const toolCalls = assistantMessage.tool_calls || [];
|
|
@@ -1740,7 +1768,7 @@ export async function runAgent(config) {
|
|
|
1740
1768
|
await store.saveState(state);
|
|
1741
1769
|
continue;
|
|
1742
1770
|
}
|
|
1743
|
-
const fallback = assistantMessage.content?.trim() || "No tool call returned.";
|
|
1771
|
+
const fallback = redactSensitiveText(assistantMessage.content?.trim() || "No tool call returned.");
|
|
1744
1772
|
if (config.scsActive) {
|
|
1745
1773
|
const decision = await reviewScsFinish(client, config, state, fallback, {
|
|
1746
1774
|
events: await store.loadEvents(),
|
|
@@ -1792,6 +1820,7 @@ export async function runAgent(config) {
|
|
|
1792
1820
|
}
|
|
1793
1821
|
|
|
1794
1822
|
let continueForQueuedInput = false;
|
|
1823
|
+
const postBatchToolResults = [];
|
|
1795
1824
|
for (let toolIndex = 0; toolIndex < toolCalls.length; toolIndex += 1) {
|
|
1796
1825
|
const toolCall = toolCalls[toolIndex];
|
|
1797
1826
|
throwIfAborted(config);
|
|
@@ -1801,44 +1830,7 @@ export async function runAgent(config) {
|
|
|
1801
1830
|
tool_call_id: toolCall.id,
|
|
1802
1831
|
content: JSON.stringify(toolResult),
|
|
1803
1832
|
});
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
if (config.scsActive && shouldReviewToolResult(toolResult, state)) {
|
|
1807
|
-
const decision = await reviewScsToolResult(client, config, state, toolResult, {
|
|
1808
|
-
events: await store.loadEvents(),
|
|
1809
|
-
taskProfile: config.taskProfile,
|
|
1810
|
-
goal: config.goal,
|
|
1811
|
-
});
|
|
1812
|
-
state.meta.scs = state.meta.scs || { enabled: true, mode: config.enableScs || "on", active: true };
|
|
1813
|
-
state.meta.scs.monitorReviews = (state.meta.scs.monitorReviews || 0) + 1;
|
|
1814
|
-
state.meta.scs.lastStudentDecision = decision;
|
|
1815
|
-
await store.appendEvent(`scs.student.${decision.decision}`, {
|
|
1816
|
-
...decision,
|
|
1817
|
-
toolName: toolResult.toolName,
|
|
1818
|
-
});
|
|
1819
|
-
observers.event(`scs.student.${decision.decision}`, {
|
|
1820
|
-
decision: decision.decision,
|
|
1821
|
-
reason: decision.reason,
|
|
1822
|
-
toolName: toolResult.toolName,
|
|
1823
|
-
});
|
|
1824
|
-
if (decision.decision === "rethink_plan" || decision.decision === "reject_phase") {
|
|
1825
|
-
state.messages.push({
|
|
1826
|
-
role: "user",
|
|
1827
|
-
content: [
|
|
1828
|
-
"SCS student monitor requested a rethink based on tool evidence.",
|
|
1829
|
-
`Decision: ${decision.decision}`,
|
|
1830
|
-
`Reason: ${decision.reason || "No reason provided."}`,
|
|
1831
|
-
decision.nextRequiredAction ? `Next required action: ${decision.nextRequiredAction}` : "",
|
|
1832
|
-
"Supervisor: do not repeat the same failed call. Adjust within the approved phase or finish with a concrete blocker if the phase is invalidated.",
|
|
1833
|
-
]
|
|
1834
|
-
.filter(Boolean)
|
|
1835
|
-
.join("\n"),
|
|
1836
|
-
});
|
|
1837
|
-
emitConsole(config, `SCS: ${decision.decision} after ${toolResult.toolName}: ${decision.reason || "reviewed"}`, {
|
|
1838
|
-
kind: "meta",
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1833
|
+
postBatchToolResults.push(toolResult);
|
|
1842
1834
|
|
|
1843
1835
|
if (toolResult.toolName === "run_command") {
|
|
1844
1836
|
observers.log("command.output", {
|
|
@@ -1956,6 +1948,47 @@ export async function runAgent(config) {
|
|
|
1956
1948
|
}
|
|
1957
1949
|
}
|
|
1958
1950
|
|
|
1951
|
+
for (const toolResult of postBatchToolResults) {
|
|
1952
|
+
await applyToolLoopGuard(state, toolResult, store, observers);
|
|
1953
|
+
|
|
1954
|
+
if (config.scsActive && shouldReviewToolResult(toolResult, state)) {
|
|
1955
|
+
const decision = await reviewScsToolResult(client, config, state, toolResult, {
|
|
1956
|
+
events: await store.loadEvents(),
|
|
1957
|
+
taskProfile: config.taskProfile,
|
|
1958
|
+
goal: config.goal,
|
|
1959
|
+
});
|
|
1960
|
+
state.meta.scs = state.meta.scs || { enabled: true, mode: config.enableScs || "on", active: true };
|
|
1961
|
+
state.meta.scs.monitorReviews = (state.meta.scs.monitorReviews || 0) + 1;
|
|
1962
|
+
state.meta.scs.lastStudentDecision = decision;
|
|
1963
|
+
await store.appendEvent(`scs.student.${decision.decision}`, {
|
|
1964
|
+
...decision,
|
|
1965
|
+
toolName: toolResult.toolName,
|
|
1966
|
+
});
|
|
1967
|
+
observers.event(`scs.student.${decision.decision}`, {
|
|
1968
|
+
decision: decision.decision,
|
|
1969
|
+
reason: decision.reason,
|
|
1970
|
+
toolName: toolResult.toolName,
|
|
1971
|
+
});
|
|
1972
|
+
if (decision.decision === "rethink_plan" || decision.decision === "reject_phase") {
|
|
1973
|
+
state.messages.push({
|
|
1974
|
+
role: "user",
|
|
1975
|
+
content: [
|
|
1976
|
+
"SCS student monitor requested a rethink based on tool evidence.",
|
|
1977
|
+
`Decision: ${decision.decision}`,
|
|
1978
|
+
`Reason: ${decision.reason || "No reason provided."}`,
|
|
1979
|
+
decision.nextRequiredAction ? `Next required action: ${decision.nextRequiredAction}` : "",
|
|
1980
|
+
"Supervisor: do not repeat the same failed call. Adjust within the approved phase or finish with a concrete blocker if the phase is invalidated.",
|
|
1981
|
+
]
|
|
1982
|
+
.filter(Boolean)
|
|
1983
|
+
.join("\n"),
|
|
1984
|
+
});
|
|
1985
|
+
emitConsole(config, `SCS: ${decision.decision} after ${toolResult.toolName}: ${decision.reason || "reviewed"}`, {
|
|
1986
|
+
kind: "meta",
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1959
1992
|
if (continueForQueuedInput) continue;
|
|
1960
1993
|
|
|
1961
1994
|
if (config.scsActive && shouldReviewScsProgress(step, state)) {
|
package/src/model-client.js
CHANGED
|
@@ -6,6 +6,7 @@ import { engineeringGuidanceForTask } from "./engineering-guidance.js";
|
|
|
6
6
|
import { formatSkillsForPrompt, selectSkillsForGoal } from "./skill-library.js";
|
|
7
7
|
import { platformInfo, platformLabel } from "./platform.js";
|
|
8
8
|
import { formatBehaviorContractForPrompt } from "./behavior-contract.js";
|
|
9
|
+
import { redactSensitiveText } from "./redaction.js";
|
|
9
10
|
|
|
10
11
|
export function createClient(config) {
|
|
11
12
|
if (config.provider === "mock") {
|
|
@@ -558,7 +559,7 @@ export async function createPlan(client, config, state) {
|
|
|
558
559
|
requestOptions(config)
|
|
559
560
|
);
|
|
560
561
|
|
|
561
|
-
return response.choices[0]?.message?.content?.trim() || "1. Inspect the page.\n2. Use the smallest safe action.\n3. Finish with a concise answer.";
|
|
562
|
+
return redactSensitiveText(response.choices[0]?.message?.content?.trim() || "1. Inspect the page.\n2. Use the smallest safe action.\n3. Finish with a concise answer.");
|
|
562
563
|
}
|
|
563
564
|
|
|
564
565
|
export async function requestNextStep(client, config, messages) {
|