@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.74",
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",
@@ -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 && state.messages[cursor]?.role === "tool") {
262
- const toolMessage = state.messages[cursor];
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: scsPlan.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(scsPlan.plan);
1508
- await store.appendEvent("plan.created", { plan: scsPlan.plan, scs: true });
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: scsPlan.plan, scs: true });
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
- await applyToolLoopGuard(state, toolResult, store, observers);
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)) {
@@ -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) {