@lazyingart/agintiflow 0.20.50 → 0.20.51
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 +46 -0
- package/src/agent-runner.js +55 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.51",
|
|
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",
|
|
@@ -85,6 +85,52 @@ try {
|
|
|
85
85
|
!staleDeepSeekState.messages.some((message) => message.role === "tool" && message.tool_call_id === "stale-call"),
|
|
86
86
|
"repaired DeepSeek history retained an orphan stale tool message"
|
|
87
87
|
);
|
|
88
|
+
const interruptedDeepSeekState = {
|
|
89
|
+
messages: [
|
|
90
|
+
{ role: "system", content: "system" },
|
|
91
|
+
{ role: "user", content: "old request" },
|
|
92
|
+
{
|
|
93
|
+
role: "assistant",
|
|
94
|
+
content: "Running checks.",
|
|
95
|
+
reasoning_content: "Need shell evidence.",
|
|
96
|
+
tool_calls: [
|
|
97
|
+
{ id: "call-a", type: "function", function: { name: "run_command", arguments: "{}" } },
|
|
98
|
+
{ id: "call-b", type: "function", function: { name: "run_command", arguments: "{}" } },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{ role: "tool", tool_call_id: "call-a", content: "{\"ok\":true}" },
|
|
102
|
+
{ role: "user", content: "Continue with this new request: /review" },
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
const interruptedRepair = repairModelMessageHistory(interruptedDeepSeekState, { provider: "deepseek" });
|
|
106
|
+
assert(interruptedRepair.changed, "interrupted tool-call history was not repaired");
|
|
107
|
+
assert(interruptedRepair.incompleteToolCallMessages === 1, "interrupted repair did not count the incomplete tool call");
|
|
108
|
+
assert(
|
|
109
|
+
!interruptedDeepSeekState.messages.some((message) => Array.isArray(message.tool_calls) && message.tool_calls.length > 0),
|
|
110
|
+
"interrupted repair retained incomplete assistant tool calls"
|
|
111
|
+
);
|
|
112
|
+
assert(
|
|
113
|
+
!interruptedDeepSeekState.messages.some((message) => message.role === "tool"),
|
|
114
|
+
"interrupted repair retained orphan partial tool result"
|
|
115
|
+
);
|
|
116
|
+
assert(
|
|
117
|
+
interruptedDeepSeekState.messages.at(-1)?.content === "Continue with this new request: /review",
|
|
118
|
+
"interrupted repair dropped the new user request"
|
|
119
|
+
);
|
|
120
|
+
const completeToolState = {
|
|
121
|
+
messages: [
|
|
122
|
+
{ role: "system", content: "system" },
|
|
123
|
+
{
|
|
124
|
+
role: "assistant",
|
|
125
|
+
content: "Using a tool.",
|
|
126
|
+
tool_calls: [{ id: "call-ok", type: "function", function: { name: "list_files", arguments: "{}" } }],
|
|
127
|
+
},
|
|
128
|
+
{ role: "tool", tool_call_id: "call-ok", content: "{\"ok\":true}" },
|
|
129
|
+
{ role: "user", content: "next" },
|
|
130
|
+
],
|
|
131
|
+
};
|
|
132
|
+
const completeRepair = repairModelMessageHistory(completeToolState, { provider: "openai" });
|
|
133
|
+
assert(!completeRepair.changed, "complete OpenAI-format tool history should not be modified");
|
|
88
134
|
const patchRoute = selectModelRoute({
|
|
89
135
|
routingMode: "smart",
|
|
90
136
|
provider: "deepseek",
|
package/src/agent-runner.js
CHANGED
|
@@ -190,32 +190,68 @@ function preserveAssistantMessage(message) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
export function repairModelMessageHistory(state, config = {}) {
|
|
193
|
-
if (
|
|
194
|
-
return {
|
|
193
|
+
if (!Array.isArray(state?.messages)) {
|
|
194
|
+
return {
|
|
195
|
+
changed: false,
|
|
196
|
+
droppedAssistantMessages: 0,
|
|
197
|
+
convertedAssistantMessages: 0,
|
|
198
|
+
droppedToolMessages: 0,
|
|
199
|
+
incompleteToolCallMessages: 0,
|
|
200
|
+
};
|
|
195
201
|
}
|
|
196
202
|
|
|
197
203
|
const repaired = [];
|
|
198
|
-
const droppedToolCallIds = new Set();
|
|
199
204
|
let droppedAssistantMessages = 0;
|
|
200
205
|
let convertedAssistantMessages = 0;
|
|
201
206
|
let droppedToolMessages = 0;
|
|
207
|
+
let incompleteToolCallMessages = 0;
|
|
202
208
|
|
|
203
|
-
for (
|
|
209
|
+
for (let index = 0; index < state.messages.length; index += 1) {
|
|
210
|
+
const message = state.messages[index];
|
|
204
211
|
if (message.role === "assistant") {
|
|
205
212
|
const hasReasoningContent = Boolean(message.reasoning_content || message.reasoningContent);
|
|
206
213
|
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
207
214
|
const content = String(message.content || "");
|
|
215
|
+
const requiresDeepSeekReasoning = config.provider === "deepseek" && !hasReasoningContent;
|
|
216
|
+
|
|
217
|
+
if (toolCalls.length > 0) {
|
|
218
|
+
const expectedIds = toolCalls.map((call) => String(call?.id || "")).filter(Boolean);
|
|
219
|
+
const expected = new Set(expectedIds);
|
|
220
|
+
const followingToolMessages = [];
|
|
221
|
+
const duplicateOrUnexpectedToolMessages = [];
|
|
222
|
+
const seen = new Set();
|
|
223
|
+
let cursor = index + 1;
|
|
224
|
+
|
|
225
|
+
while (cursor < state.messages.length && state.messages[cursor]?.role === "tool") {
|
|
226
|
+
const toolMessage = state.messages[cursor];
|
|
227
|
+
const toolCallId = String(toolMessage.tool_call_id || "");
|
|
228
|
+
if (expected.has(toolCallId) && !seen.has(toolCallId)) {
|
|
229
|
+
followingToolMessages.push(toolMessage);
|
|
230
|
+
seen.add(toolCallId);
|
|
231
|
+
} else {
|
|
232
|
+
duplicateOrUnexpectedToolMessages.push(toolMessage);
|
|
233
|
+
}
|
|
234
|
+
cursor += 1;
|
|
235
|
+
}
|
|
208
236
|
|
|
209
|
-
|
|
210
|
-
if (
|
|
237
|
+
const completeToolResults = expectedIds.length > 0 && expectedIds.every((id) => seen.has(id));
|
|
238
|
+
if (requiresDeepSeekReasoning || !completeToolResults) {
|
|
211
239
|
droppedAssistantMessages += 1;
|
|
240
|
+
droppedToolMessages += followingToolMessages.length + duplicateOrUnexpectedToolMessages.length;
|
|
241
|
+
if (!completeToolResults) incompleteToolCallMessages += 1;
|
|
242
|
+
index = cursor - 1;
|
|
212
243
|
continue;
|
|
213
244
|
}
|
|
214
245
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
246
|
+
repaired.push(preserveAssistantMessage(message));
|
|
247
|
+
repaired.push(...followingToolMessages);
|
|
248
|
+
droppedToolMessages += duplicateOrUnexpectedToolMessages.length;
|
|
249
|
+
index = cursor - 1;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (requiresDeepSeekReasoning) {
|
|
254
|
+
if (content.startsWith("Execution plan:")) {
|
|
219
255
|
droppedAssistantMessages += 1;
|
|
220
256
|
continue;
|
|
221
257
|
}
|
|
@@ -236,7 +272,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
236
272
|
continue;
|
|
237
273
|
}
|
|
238
274
|
|
|
239
|
-
if (message.role === "tool"
|
|
275
|
+
if (message.role === "tool") {
|
|
240
276
|
droppedToolMessages += 1;
|
|
241
277
|
continue;
|
|
242
278
|
}
|
|
@@ -259,6 +295,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
259
295
|
droppedAssistantMessages,
|
|
260
296
|
convertedAssistantMessages,
|
|
261
297
|
droppedToolMessages,
|
|
298
|
+
incompleteToolCallMessages,
|
|
262
299
|
};
|
|
263
300
|
}
|
|
264
301
|
|
|
@@ -1314,6 +1351,13 @@ export async function runAgent(config) {
|
|
|
1314
1351
|
|
|
1315
1352
|
ensureChatState(state);
|
|
1316
1353
|
|
|
1354
|
+
const initialRepair = repairModelMessageHistory(state, config);
|
|
1355
|
+
if (initialRepair.changed) {
|
|
1356
|
+
await store.appendEvent("history.repaired", initialRepair);
|
|
1357
|
+
observers.event("history.repaired", initialRepair);
|
|
1358
|
+
await store.saveState(state);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1317
1361
|
observers.log("session.ready", {
|
|
1318
1362
|
sessionId,
|
|
1319
1363
|
provider: config.provider,
|