@lazyingart/agintiflow 0.20.245 → 0.20.246
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-context-budget-recovery.js +80 -0
- package/src/agent-runner.js +101 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.246",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -322,4 +322,84 @@ assert.ok(
|
|
|
322
322
|
"DeepSeek runtime compaction exceeded the bounded retry target"
|
|
323
323
|
);
|
|
324
324
|
|
|
325
|
+
function noisyFullReadPair(index, generation) {
|
|
326
|
+
const id = `validator-${generation}-${index}`;
|
|
327
|
+
const file = `tmp/validator-${generation}-${index}.py`;
|
|
328
|
+
return [
|
|
329
|
+
{
|
|
330
|
+
role: "assistant",
|
|
331
|
+
content: "",
|
|
332
|
+
reasoning_content: "Inspect the latest validator.",
|
|
333
|
+
tool_calls: [
|
|
334
|
+
{
|
|
335
|
+
id,
|
|
336
|
+
type: "function",
|
|
337
|
+
function: { name: "read_file", arguments: JSON.stringify({ path: file }) },
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
role: "tool",
|
|
343
|
+
tool_call_id: id,
|
|
344
|
+
content: JSON.stringify({
|
|
345
|
+
ok: true,
|
|
346
|
+
toolName: "read_file",
|
|
347
|
+
path: file,
|
|
348
|
+
bytes: 4096,
|
|
349
|
+
lineCount: 120,
|
|
350
|
+
sha256: `${generation}${String(index).padStart(2, "0")}`.repeat(24).slice(0, 64),
|
|
351
|
+
contentTruncated: false,
|
|
352
|
+
content: `VALIDATOR-${generation}-${index}\n${"broad validator content ".repeat(150)}`,
|
|
353
|
+
}),
|
|
354
|
+
},
|
|
355
|
+
];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const twiceCompactedState = {
|
|
359
|
+
...compactionState,
|
|
360
|
+
messages: [
|
|
361
|
+
...deepSeekRuntimeMessages,
|
|
362
|
+
...Array.from({ length: 14 }, (_, index) => noisyFullReadPair(index + 1, 2)).flat(),
|
|
363
|
+
],
|
|
364
|
+
};
|
|
365
|
+
const twiceCompacted = buildContextBudgetCompactionMessages(
|
|
366
|
+
twiceCompactedState,
|
|
367
|
+
{ ...config, provider: "deepseek", model: "deepseek-chat" },
|
|
368
|
+
{ title: "", url: "" },
|
|
369
|
+
8,
|
|
370
|
+
{ reason: "second DeepSeek compaction" }
|
|
371
|
+
);
|
|
372
|
+
const twiceCompactedText = twiceCompacted.map((message) => message.content || "").join("\n");
|
|
373
|
+
assert.ok(twiceCompactedText.includes("reliability-evidence-v14"));
|
|
374
|
+
assert.ok(twiceCompactedText.includes("EVIDENCE-CHUNK-ONE"));
|
|
375
|
+
assert.ok(twiceCompactedText.includes("EVIDENCE-CHUNK-TWO"));
|
|
376
|
+
assert.equal(
|
|
377
|
+
(twiceCompactedText.match(/Tool: deep_research/g) || []).length,
|
|
378
|
+
1,
|
|
379
|
+
"second DeepSeek compaction duplicated the completed research record"
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const thriceCompactedState = {
|
|
383
|
+
...compactionState,
|
|
384
|
+
messages: [
|
|
385
|
+
...twiceCompacted,
|
|
386
|
+
...Array.from({ length: 14 }, (_, index) => noisyFullReadPair(index + 1, 3)).flat(),
|
|
387
|
+
],
|
|
388
|
+
};
|
|
389
|
+
const thriceCompacted = buildContextBudgetCompactionMessages(
|
|
390
|
+
thriceCompactedState,
|
|
391
|
+
{ ...config, provider: "deepseek", model: "deepseek-chat" },
|
|
392
|
+
{ title: "", url: "" },
|
|
393
|
+
12,
|
|
394
|
+
{ reason: "third DeepSeek compaction" }
|
|
395
|
+
);
|
|
396
|
+
const thriceCompactedText = thriceCompacted.map((message) => message.content || "").join("\n");
|
|
397
|
+
assert.ok(thriceCompactedText.includes("reliability-evidence-v14"));
|
|
398
|
+
assert.ok(thriceCompactedText.includes("EVIDENCE-CHUNK-ONE"));
|
|
399
|
+
assert.ok(thriceCompactedText.includes("EVIDENCE-CHUNK-TWO"));
|
|
400
|
+
assert.ok(
|
|
401
|
+
estimateMessageTokens(thriceCompacted) <= 12288,
|
|
402
|
+
"cumulative DeepSeek compaction exceeded the bounded retry target"
|
|
403
|
+
);
|
|
404
|
+
|
|
325
405
|
console.log("context budget recovery smoke passed");
|
package/src/agent-runner.js
CHANGED
|
@@ -838,19 +838,38 @@ function retainedReadRange(payload = {}, args = {}) {
|
|
|
838
838
|
};
|
|
839
839
|
}
|
|
840
840
|
|
|
841
|
+
function parseRetainedToolEvidenceMessage(message = {}) {
|
|
842
|
+
if (message?.role !== "user") return null;
|
|
843
|
+
const content = String(message.content || "");
|
|
844
|
+
if (!/^Retained runtime tool evidence\./i.test(content.trim())) return null;
|
|
845
|
+
const match = content.match(
|
|
846
|
+
/^Retained runtime tool evidence\.[^\n]*\nTool:\s*([^\n]+)\nArguments:\s*([^\n]+)\nVerified result:\s*([\s\S]+)$/i
|
|
847
|
+
);
|
|
848
|
+
if (!match) return null;
|
|
849
|
+
const name = String(match[1] || "").trim();
|
|
850
|
+
const args = safeParseToolContent(match[2]) || {};
|
|
851
|
+
const payload = safeParseToolContent(match[3]);
|
|
852
|
+
if (!name || !COMPACTION_STATE_TOOL_NAMES.has(name) || !payload || typeof payload !== "object") {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
return { name, args, payload };
|
|
856
|
+
}
|
|
857
|
+
|
|
841
858
|
function summarizeRetainedSourceEvidence(messages = [], limit = 28) {
|
|
842
859
|
const bySource = new Map();
|
|
843
860
|
for (const message of messages) {
|
|
844
|
-
|
|
845
|
-
|
|
861
|
+
const retained = parseRetainedToolEvidenceMessage(message);
|
|
862
|
+
if (message?.role !== "tool" && !retained) continue;
|
|
863
|
+
const payload = retained?.payload || safeParseToolContent(message.content);
|
|
846
864
|
if (!payload || payload.ok === false || payload.blocked || payload.skipped) continue;
|
|
847
|
-
const toolName = String(payload.toolName || payload.name || "");
|
|
865
|
+
const toolName = String(retained?.name || payload.toolName || payload.name || "");
|
|
848
866
|
if (!["read_file", "list_files", "search_files", "inspect_project", "run_command"].includes(toolName)) {
|
|
849
867
|
continue;
|
|
850
868
|
}
|
|
851
|
-
const
|
|
852
|
-
const
|
|
853
|
-
const
|
|
869
|
+
const args = retained?.args || payload.args || {};
|
|
870
|
+
const sourcePath = String(payload.path || args.path || "").trim();
|
|
871
|
+
const command = String(args.command || "").trim();
|
|
872
|
+
const readRange = toolName === "read_file" ? retainedReadRange(payload, args) : null;
|
|
854
873
|
const key = `${toolName}:${sourcePath || command}${readRange ? `:${readRange.key}` : ""}`;
|
|
855
874
|
const parts = [`tool=${toolName}`];
|
|
856
875
|
if (sourcePath) parts.push(`path=${sourcePath}`);
|
|
@@ -986,6 +1005,7 @@ function compactRetainedToolPayload(toolName, payload = {}, args = {}) {
|
|
|
986
1005
|
result.results = compactPathItems(payload.results, 24, ["path", "file", "line", "match"]);
|
|
987
1006
|
} else if (toolName === "run_command") {
|
|
988
1007
|
result.args = { command: compactMultiline(args.command || payload.args?.command || "", 1200) };
|
|
1008
|
+
if (payload.changed !== undefined) result.changed = Boolean(payload.changed);
|
|
989
1009
|
if (Number.isFinite(Number(payload.exitCode))) result.exitCode = Number(payload.exitCode);
|
|
990
1010
|
if (payload.stdout) result.stdout = compactMultiline(payload.stdout, 1800);
|
|
991
1011
|
if (payload.stderr) result.stderr = compactMultiline(payload.stderr, 1200);
|
|
@@ -998,11 +1018,51 @@ function compactRetainedToolPayload(toolName, payload = {}, args = {}) {
|
|
|
998
1018
|
return redactValue(result);
|
|
999
1019
|
}
|
|
1000
1020
|
|
|
1021
|
+
function retainedToolRecordPriority(record = {}) {
|
|
1022
|
+
const name = String(record.name || "");
|
|
1023
|
+
const args = record.args || {};
|
|
1024
|
+
const payload = record.payload || {};
|
|
1025
|
+
let priority = 100;
|
|
1026
|
+
if (name === "deep_research") priority = 1000;
|
|
1027
|
+
else if (name === "inspect_project") priority = 900;
|
|
1028
|
+
else if (["apply_patch", "write_file"].includes(name)) priority = 850;
|
|
1029
|
+
else if (name === "run_command") {
|
|
1030
|
+
const exitCode = Number(payload.exitCode);
|
|
1031
|
+
priority = payload.changed === true || (Number.isFinite(exitCode) && exitCode !== 0) ? 800 : 500;
|
|
1032
|
+
} else if (name === "read_file") {
|
|
1033
|
+
const range = retainedReadRange(payload, args);
|
|
1034
|
+
priority = range.lineLimit > 0 ? 750 : 600;
|
|
1035
|
+
} else if (["search_files", "list_files"].includes(name)) priority = 400;
|
|
1036
|
+
return priority + Math.min(0.999, Math.max(0, Number(record.ordinal) || 0) / 100000);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1001
1039
|
function retainedToolStateMessages(messages = [], limit = 12) {
|
|
1002
1040
|
const callsById = new Map();
|
|
1003
1041
|
const recordsByKey = new Map();
|
|
1004
1042
|
let ordinal = 0;
|
|
1043
|
+
const retainRecord = (name, args = {}, payload = {}) => {
|
|
1044
|
+
if (!COMPACTION_STATE_TOOL_NAMES.has(name)) return;
|
|
1045
|
+
if (!payload || payload.ok === false || payload.blocked || payload.skipped) return;
|
|
1046
|
+
const sourcePath = String(payload.path || args?.path || "").trim();
|
|
1047
|
+
const command = String(args?.command || payload.args?.command || "").trim();
|
|
1048
|
+
const durableIdentity = String(
|
|
1049
|
+
sourcePath || command || payload.researchId || args?.researchId || args?.query || name
|
|
1050
|
+
).trim();
|
|
1051
|
+
const readRange = name === "read_file" ? retainedReadRange(payload, args) : null;
|
|
1052
|
+
const key = `${name}:${durableIdentity || ordinal}${readRange ? `:${readRange.key}` : ""}`;
|
|
1053
|
+
recordsByKey.set(key, {
|
|
1054
|
+
ordinal: ordinal += 1,
|
|
1055
|
+
name,
|
|
1056
|
+
args: redactValue(args),
|
|
1057
|
+
payload: compactRetainedToolPayload(name, payload, args),
|
|
1058
|
+
});
|
|
1059
|
+
};
|
|
1005
1060
|
for (const message of messages) {
|
|
1061
|
+
const retained = parseRetainedToolEvidenceMessage(message);
|
|
1062
|
+
if (retained) {
|
|
1063
|
+
retainRecord(retained.name, retained.args, retained.payload);
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1006
1066
|
if (message?.role === "assistant" && Array.isArray(message.tool_calls)) {
|
|
1007
1067
|
for (const call of message.tool_calls) {
|
|
1008
1068
|
const id = String(call?.id || "").trim();
|
|
@@ -1018,27 +1078,19 @@ function retainedToolStateMessages(messages = [], limit = 12) {
|
|
|
1018
1078
|
if (message?.role !== "tool") continue;
|
|
1019
1079
|
const call = callsById.get(String(message.tool_call_id || "").trim());
|
|
1020
1080
|
const payload = safeParseToolContent(message.content);
|
|
1021
|
-
if (!call || !payload
|
|
1022
|
-
|
|
1023
|
-
const command = String(call.args?.command || payload.args?.command || "").trim();
|
|
1024
|
-
const readRange = call.name === "read_file" ? retainedReadRange(payload, call.args) : null;
|
|
1025
|
-
const key = `${call.name}:${sourcePath || command || ordinal}${readRange ? `:${readRange.key}` : ""}`;
|
|
1026
|
-
recordsByKey.set(key, {
|
|
1027
|
-
ordinal: ordinal += 1,
|
|
1028
|
-
name: call.name,
|
|
1029
|
-
args: redactValue(call.args),
|
|
1030
|
-
payload: compactRetainedToolPayload(call.name, payload, call.args),
|
|
1031
|
-
});
|
|
1081
|
+
if (!call || !payload) continue;
|
|
1082
|
+
retainRecord(call.name, call.args, payload);
|
|
1032
1083
|
}
|
|
1033
1084
|
|
|
1034
1085
|
const records = [...recordsByKey.values()];
|
|
1035
|
-
const
|
|
1036
|
-
.
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1086
|
+
const selected = [...records]
|
|
1087
|
+
.sort(
|
|
1088
|
+
(left, right) =>
|
|
1089
|
+
retainedToolRecordPriority(right) - retainedToolRecordPriority(left) ||
|
|
1090
|
+
right.ordinal - left.ordinal
|
|
1091
|
+
)
|
|
1092
|
+
.slice(0, Math.max(1, Number(limit) || 12))
|
|
1093
|
+
.sort((left, right) => left.ordinal - right.ordinal);
|
|
1042
1094
|
|
|
1043
1095
|
return selected.flatMap((record, index) => {
|
|
1044
1096
|
const id = `aginti-compacted-tool-${index + 1}`;
|
|
@@ -1087,6 +1139,15 @@ function retainedToolStateTextMessages(messages = [], limit = 12) {
|
|
|
1087
1139
|
return retained;
|
|
1088
1140
|
}
|
|
1089
1141
|
|
|
1142
|
+
function retainedToolPairPriority(pair = [], order = 0) {
|
|
1143
|
+
const assistantCall = pair[0]?.tool_calls?.[0];
|
|
1144
|
+
const retained = pair.length === 1 ? parseRetainedToolEvidenceMessage(pair[0]) : null;
|
|
1145
|
+
const name = String(retained?.name || assistantCall?.function?.name || "");
|
|
1146
|
+
const args = retained?.args || safeParseToolContent(assistantCall?.function?.arguments) || {};
|
|
1147
|
+
const payload = retained?.payload || safeParseToolContent(pair[1]?.content) || {};
|
|
1148
|
+
return retainedToolRecordPriority({ name, args, payload, ordinal: order });
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1090
1151
|
function isRuntimeCompactionRequest(content = "") {
|
|
1091
1152
|
return /^(?:The runtime proactively compacted a long agent history|A previous agent-step model request timed out|Continue from this compacted, valid transcript)/i.test(
|
|
1092
1153
|
String(content || "").trim()
|
|
@@ -1107,6 +1168,7 @@ function summarizeOriginalRequests(messages = [], limit = 6) {
|
|
|
1107
1168
|
if (!content.trim()) continue;
|
|
1108
1169
|
if (/^Step \d+\/\d+ .*Latest runtime snapshot:/i.test(content)) continue;
|
|
1109
1170
|
if (/^Previous assistant response retained as compacted history/i.test(content)) continue;
|
|
1171
|
+
if (parseRetainedToolEvidenceMessage(message)) continue;
|
|
1110
1172
|
if (isRuntimeCompactionRequest(content)) continue;
|
|
1111
1173
|
if (isRuntimeRecoveryRequest(content)) continue;
|
|
1112
1174
|
requests.push(compactSingleLine(content, 1200));
|
|
@@ -1357,20 +1419,29 @@ function buildCompactedRuntimeMessages(state, config, snapshot, step, options =
|
|
|
1357
1419
|
];
|
|
1358
1420
|
const retainedPairs = [];
|
|
1359
1421
|
for (let index = 0; index < retainedToolMessages.length; index += deepSeekCompaction ? 1 : 2) {
|
|
1360
|
-
|
|
1422
|
+
const pair = retainedToolMessages.slice(index, index + (deepSeekCompaction ? 1 : 2));
|
|
1423
|
+
retainedPairs.push({
|
|
1424
|
+
pair,
|
|
1425
|
+
order: retainedPairs.length,
|
|
1426
|
+
priority: retainedToolPairPriority(pair, retainedPairs.length),
|
|
1427
|
+
});
|
|
1361
1428
|
}
|
|
1362
1429
|
const selectedPairs = [];
|
|
1363
|
-
|
|
1430
|
+
const prioritizedPairs = [...retainedPairs].sort(
|
|
1431
|
+
(left, right) => right.priority - left.priority || right.order - left.order
|
|
1432
|
+
);
|
|
1433
|
+
for (const candidatePair of prioritizedPairs) {
|
|
1364
1434
|
const candidate = [
|
|
1365
1435
|
...baseMessages,
|
|
1366
|
-
...pair,
|
|
1367
|
-
...selectedPairs.
|
|
1436
|
+
...candidatePair.pair,
|
|
1437
|
+
...selectedPairs.flatMap((item) => item.pair),
|
|
1368
1438
|
];
|
|
1369
|
-
if (estimateMessageTokens(candidate) <= targetTokens) selectedPairs.
|
|
1439
|
+
if (estimateMessageTokens(candidate) <= targetTokens) selectedPairs.push(candidatePair);
|
|
1370
1440
|
}
|
|
1441
|
+
selectedPairs.sort((left, right) => left.order - right.order);
|
|
1371
1442
|
const compactMessages = [
|
|
1372
1443
|
...baseMessages,
|
|
1373
|
-
...selectedPairs.
|
|
1444
|
+
...selectedPairs.flatMap((item) => item.pair),
|
|
1374
1445
|
];
|
|
1375
1446
|
|
|
1376
1447
|
if (!compactMessages.some((message) => message.role === "system")) {
|