@lazyingart/agintiflow 0.20.229 → 0.20.231

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.229",
3
+ "version": "0.20.231",
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",
@@ -23,6 +23,7 @@ import { formatBehaviorContractForPrompt } from "../src/behavior-contract.js";
23
23
  import { resolveRuntimeConfig } from "../src/config.js";
24
24
  import { readCodebaseMap } from "../src/codebase-map.js";
25
25
  import { classifyCommand, evaluateCommandPolicy } from "../src/command-policy.js";
26
+ import { checkToolUse } from "../src/guardrails.js";
26
27
  import { shouldReviewToolResult } from "../src/scs-controller.js";
27
28
  import {
28
29
  engineeringGuidanceForTask,
@@ -36,6 +37,7 @@ import {
36
37
  buildFailedCommandAdvice,
37
38
  buildPermissionAdvice,
38
39
  isOptionalGeneratedPreviewCleanup,
40
+ isUnrequestedCleanupCommand,
39
41
  } from "../src/permission-advice.js";
40
42
  import { runJsonSpecialist } from "../src/json-specialist.js";
41
43
  import { SessionStore } from "../src/session-store.js";
@@ -1230,7 +1232,7 @@ try {
1230
1232
  "optional generated-preview cleanup should retain evidence and recover without pausing"
1231
1233
  );
1232
1234
  assert(
1233
- /leave the ignored preview files in place/i.test(optionalPreviewCleanupAdvice.instruction),
1235
+ /leave every candidate file in place/i.test(optionalPreviewCleanupAdvice.instruction),
1234
1236
  "optional generated-preview cleanup advice did not tell the agent to retain evidence"
1235
1237
  );
1236
1238
  assert(
@@ -1239,6 +1241,65 @@ try {
1239
1241
  }),
1240
1242
  "a requested final artifact was misclassified as optional preview cleanup"
1241
1243
  );
1244
+ const mixedValidationCleanupArgs = {
1245
+ command:
1246
+ "python3 validate.py; rm -f output/page-*.png scratch-notes.md; git status --short",
1247
+ };
1248
+ assert(
1249
+ isUnrequestedCleanupCommand(
1250
+ "run_command",
1251
+ mixedValidationCleanupArgs,
1252
+ { goal: "Create and verify a clean document." },
1253
+ {}
1254
+ ),
1255
+ "unrequested cleanup embedded after validation was not recognized as recoverable"
1256
+ );
1257
+ const mixedValidationCleanupAdvice = buildPermissionAdvice({
1258
+ toolName: "run_command",
1259
+ args: mixedValidationCleanupArgs,
1260
+ guard: {
1261
+ category: "destructive",
1262
+ reason: "Destructive shell commands require Allow destructive actions.",
1263
+ },
1264
+ config: { ...dockerWorkspacePolicy, goal: "Create and verify a clean document." },
1265
+ state: { sessionId: "coding-unrequested-cleanup-smoke" },
1266
+ });
1267
+ assert(
1268
+ mixedValidationCleanupAdvice.autoRecover === true,
1269
+ "unrequested cleanup should be skipped without pausing substantive work"
1270
+ );
1271
+ assert(
1272
+ !isUnrequestedCleanupCommand(
1273
+ "run_command",
1274
+ { command: "rm -f output/obsolete.pdf" },
1275
+ { goal: "Delete the obsolete PDF." },
1276
+ {}
1277
+ ),
1278
+ "an explicitly requested deletion was incorrectly treated as optional housekeeping"
1279
+ );
1280
+ const documentPageBatchGuard = checkToolUse({
1281
+ toolName: "read_image",
1282
+ args: { imagePaths: ["build/verification/page-1.png", "build/verification/page-2.png"] },
1283
+ snapshot: {},
1284
+ config: { ...dockerWorkspacePolicy, allowFileTools: true, taskProfile: "word" },
1285
+ });
1286
+ assert(
1287
+ documentPageBatchGuard.allowed === false &&
1288
+ documentPageBatchGuard.category === "document-page-visual-batch",
1289
+ "Word document review did not require one visual call per rendered page"
1290
+ );
1291
+ const documentPageBatchAdvice = buildPermissionAdvice({
1292
+ toolName: "read_image",
1293
+ args: { imagePaths: ["build/verification/page-1.png", "build/verification/page-2.png"] },
1294
+ guard: documentPageBatchGuard,
1295
+ config: { ...dockerWorkspacePolicy, taskProfile: "word" },
1296
+ state: { sessionId: "coding-document-page-visual-batch-smoke" },
1297
+ });
1298
+ assert(
1299
+ documentPageBatchAdvice.autoRecover === true &&
1300
+ /once for each rendered page/i.test(documentPageBatchAdvice.instruction),
1301
+ "Word document page batching did not recover into separate visual checks"
1302
+ );
1242
1303
  const failedNetworkAdvice = buildFailedCommandAdvice({
1243
1304
  args: { command: "git clone https://github.com/lazyingart/AgInTiFlow.git" },
1244
1305
  commandPolicy: clonePolicy,
@@ -48,6 +48,21 @@ async function main() {
48
48
  assert(!excessiveResearch.allowed, "deep_research accepted an excessive query/source budget");
49
49
 
50
50
  assert(hasExplicitDeepResearchIntent("literature review with primary papers"), "explicit research intent was not detected");
51
+ assert(
52
+ !hasExplicitDeepResearchIntent("Create a phone-friendly document from this folder.", [
53
+ {
54
+ role: "user",
55
+ content:
56
+ 'Step 2/30. Latest runtime snapshot:\n{"pageText":"Web search and resumable deep research are available when current evidence is required."}',
57
+ },
58
+ {
59
+ role: "user",
60
+ content:
61
+ "The previous tool-call batch was rejected before dispatch. Tools offered in that turn: deep_research, finish.",
62
+ },
63
+ ]),
64
+ "runtime control prose was misclassified as genuine deep-research intent"
65
+ );
51
66
  assert(
52
67
  toolChoiceForProvider({ provider: "deepseek" }, []) === "auto",
53
68
  "provider-neutral research routing added an unsupported named tool_choice"
@@ -1475,6 +1475,49 @@ const explicitDeepResearchFollowup = selectProgressiveTools(allTools, {
1475
1475
  });
1476
1476
  assert(names(explicitDeepResearchFollowup).includes("web_search"), "deep-research follow-up did not restore targeted recovery tools");
1477
1477
 
1478
+ const documentRuntimeSnapshot =
1479
+ 'Step 1/30. Latest runtime snapshot:\n{"pageText":"Workspace file tools are ready. Web search and resumable deep research are available when current evidence is required."}';
1480
+ const localDocumentStarter = selectProgressiveTools(allTools, {
1481
+ config: { provider: "deepseek" },
1482
+ goal: "Turn the files in this folder into an editable, phone-friendly handoff and finish it properly.",
1483
+ profile: "word",
1484
+ messages: [
1485
+ { role: "user", content: "Turn the files in this folder into an editable, phone-friendly handoff and finish it properly." },
1486
+ { role: "user", content: documentRuntimeSnapshot },
1487
+ ],
1488
+ });
1489
+ assert(names(localDocumentStarter).includes("read_file"), "local document start omitted workspace reading tools");
1490
+ assert(names(localDocumentStarter).includes("write_file"), "local document start omitted workspace writing tools");
1491
+ assert(
1492
+ !(names(localDocumentStarter).length === 2 && names(localDocumentStarter)[0] === "deep_research"),
1493
+ "runtime snapshot prose forced a local document task into deep research"
1494
+ );
1495
+
1496
+ const documentRepairAfterCompaction = selectProgressiveTools(allTools, {
1497
+ config: { provider: "deepseek" },
1498
+ goal: "Turn the files in this folder into an editable, phone-friendly handoff and finish it properly.",
1499
+ profile: "word",
1500
+ messages: [
1501
+ {
1502
+ role: "user",
1503
+ content:
1504
+ "The runtime proactively compacted a long agent history before the provider context became inefficient or unstable. Authoritative current goal: create the document.",
1505
+ },
1506
+ {
1507
+ role: "user",
1508
+ content:
1509
+ "The previous tool-call batch was rejected before dispatch. Reason code: TOOL_NOT_OFFERED. Rejected request: apply_patch. Tools offered in that turn: deep_research, finish.",
1510
+ },
1511
+ { role: "user", content: documentRuntimeSnapshot },
1512
+ ],
1513
+ });
1514
+ assert(names(documentRepairAfterCompaction).includes("apply_patch"), "document repair after compaction omitted apply_patch");
1515
+ assert(names(documentRepairAfterCompaction).includes("run_command"), "document repair after compaction omitted build verification");
1516
+ assert(
1517
+ !(names(documentRepairAfterCompaction).length === 2 && names(documentRepairAfterCompaction)[0] === "deep_research"),
1518
+ "post-failure runtime scaffolding regressed a local document repair into deep research"
1519
+ );
1520
+
1478
1521
  const scopedArtifactPrompt = `You are a persistent workspace agent. The surrounding policy mentions an evidence review.
1479
1522
  AGINTI_EVIDENCE_SCOPE_JSON: {"mode":"task","request":"Create one plain-text artifact and verify it."}
1480
1523
  Repository evidence to consult as relevant: literature review, evidence review, research report.`;
package/src/guardrails.js CHANGED
@@ -245,6 +245,14 @@ export function checkToolUse({ toolName, args, snapshot, config }) {
245
245
  .map((item) => String(item || "").trim())
246
246
  .filter(Boolean);
247
247
  if (values.length === 0) return { allowed: false, reason: "At least one image path or URL is required.", category: "perception-tools" };
248
+ if (String(config.taskProfile || "").toLowerCase() === "word" && values.length > 1) {
249
+ return {
250
+ allowed: false,
251
+ reason:
252
+ "Word-document page review requires one rendered page per image call so clipping, orphaned headings, sparse spill pages, and other page-specific defects cannot be averaged away.",
253
+ category: "document-page-visual-batch",
254
+ };
255
+ }
248
256
  if (values.length > 4) return { allowed: false, reason: "Too many images. Maximum is 4.", category: "perception-tools" };
249
257
  for (const value of values) {
250
258
  if (/^https?:\/\//i.test(value)) {
@@ -67,6 +67,23 @@ export function isOptionalGeneratedPreviewCleanup(toolName = "", args = {}) {
67
67
  );
68
68
  }
69
69
 
70
+ function goalRequestsDeletion(config = {}, state = {}) {
71
+ const goal = String(config.goal || state.goal || state.meta?.goalContract?.current || "");
72
+ return /\b(?:delete|remove|clean\s+up|cleanup|purge|erase|discard|drop)\b|删除|刪除|移除|清理|清除|删掉|刪掉|削除|消去/i.test(
73
+ goal
74
+ );
75
+ }
76
+
77
+ export function isUnrequestedCleanupCommand(toolName = "", args = {}, config = {}, state = {}) {
78
+ const goal = String(config.goal || state.goal || state.meta?.goalContract?.current || "").trim();
79
+ if (!goal || toolName !== "run_command" || goalRequestsDeletion(config, state)) return false;
80
+ const command = String(args.command || args.text || "");
81
+ return command
82
+ .split(/(?:&&|;|\n)/)
83
+ .map((segment) => segment.trim())
84
+ .some((segment) => /^(?:command\s+)?rm\s+(?:-[A-Za-z]*[fr][A-Za-z]*\s+|--force\s+)/.test(segment));
85
+ }
86
+
70
87
  function quoteShell(value = "") {
71
88
  const text = String(value || "");
72
89
  return `'${text.replace(/'/g, `'\\''`)}'`;
@@ -180,6 +197,22 @@ function adviceForCategory(category = "", { toolName = "", args = {}, config = {
180
197
  };
181
198
  }
182
199
 
200
+ if (category === "document-page-visual-batch") {
201
+ return {
202
+ ...base,
203
+ autoRecover: true,
204
+ summary:
205
+ "The document review batched multiple rendered pages, so page-specific visual defects could be missed.",
206
+ instruction:
207
+ "Continue automatically by calling read_image once for each rendered page. Evaluate clipping, overflow, orphaned headings, sparse spill pages, table splits, margins, and hierarchy on that page before moving to the next one.",
208
+ options: [
209
+ "Review page 1 alone, repair any defect, and rebuild before reviewing later pages.",
210
+ "Review each remaining page in a separate read_image call.",
211
+ "Finish only after every page has its own accepted visual evidence.",
212
+ ],
213
+ };
214
+ }
215
+
183
216
  if (category === "workspace-write") {
184
217
  return {
185
218
  ...base,
@@ -258,14 +291,17 @@ function adviceForCategory(category = "", { toolName = "", args = {}, config = {
258
291
  }
259
292
 
260
293
  if (category === "destructive") {
261
- if (isOptionalGeneratedPreviewCleanup(toolName, args)) {
294
+ if (
295
+ isOptionalGeneratedPreviewCleanup(toolName, args) ||
296
+ isUnrequestedCleanupCommand(toolName, args, config, state)
297
+ ) {
262
298
  return {
263
299
  ...base,
264
300
  autoRecover: true,
265
301
  summary:
266
- "Optional generated-preview cleanup was blocked safely; the verification evidence can remain ignored and the task should continue.",
302
+ "Unrequested cleanup was blocked safely; generated or verification files can remain ignored and the substantive task should continue.",
267
303
  instruction:
268
- "Do not retry, rename, or seek approval for this cleanup. Leave the ignored preview files in place, run any remaining read-only checks in a separate call, and finish the requested task when its substantive evidence passes.",
304
+ "Do not retry, rename, or seek approval for this cleanup. Leave every candidate file in place, run any remaining read-only checks in a separate call, and finish the requested task when its substantive evidence passes.",
269
305
  options: [
270
306
  "Retain the generated previews as private verification evidence.",
271
307
  "Run source hashes, artifact checks, and git status separately without a delete command.",
@@ -6,11 +6,36 @@ function messageText(content) {
6
6
  return content.map((part) => part?.text || part?.content || "").filter(Boolean).join("\n");
7
7
  }
8
8
 
9
+ const RUNTIME_USER_MESSAGE_PATTERNS = Object.freeze([
10
+ /^Step \d+\/\d+\b.*Latest runtime snapshot:/i,
11
+ /^Retained runtime tool evidence\./i,
12
+ /^The runtime proactively compacted a long agent history/i,
13
+ /^A previous agent-step model request timed out/i,
14
+ /^Continue from this compacted, valid transcript/i,
15
+ /^Previous assistant response retained as compacted history/i,
16
+ /^Highest-priority retained state:/i,
17
+ /^Bounded failed-test evidence packet(?: v\d+)?\./i,
18
+ /^Verification is still failing,/i,
19
+ /^The previous tool-call batch was rejected before dispatch\./i,
20
+ /^Loop guard:/i,
21
+ /^Runtime phase transition:/i,
22
+ ]);
23
+
24
+ function isRuntimeUserMessage(content = "") {
25
+ const text = String(content || "").trim();
26
+ return RUNTIME_USER_MESSAGE_PATTERNS.some((pattern) => pattern.test(text));
27
+ }
28
+
29
+ function genuineUserMessages(messages = []) {
30
+ return messages.filter(
31
+ (message) => message?.role === "user" && !isRuntimeUserMessage(messageText(message.content))
32
+ );
33
+ }
34
+
9
35
  export function hasExplicitDeepResearchIntent(goal = "", messages = []) {
10
36
  const recent = hasAgintiEvidenceScope(goal)
11
37
  ? ""
12
- : messages
13
- .filter((message) => message?.role === "user")
38
+ : genuineUserMessages(messages)
14
39
  .slice(-4)
15
40
  .map((message) => scopedChatopsEvidenceGoal(messageText(message.content)))
16
41
  .join("\n");
@@ -24,12 +49,19 @@ export function hasExplicitDeepResearchIntent(goal = "", messages = []) {
24
49
  }
25
50
 
26
51
  export function toolWasRequested(messages = [], toolName = "") {
27
- return messages.some(
28
- (message) =>
52
+ return messages.some((message) => {
53
+ if (
29
54
  message?.role === "assistant" &&
30
55
  Array.isArray(message.tool_calls) &&
31
56
  message.tool_calls.some((call) => call?.function?.name === toolName)
32
- );
57
+ ) {
58
+ return true;
59
+ }
60
+ if (message?.role !== "user") return false;
61
+ const content = messageText(message.content);
62
+ if (!/^Retained runtime tool evidence\./i.test(content.trim())) return false;
63
+ return new RegExp(`^Tool:\\s*${String(toolName).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "im").test(content);
64
+ });
33
65
  }
34
66
 
35
67
  export function shouldStartWithDeepResearch(goal = "", messages = []) {
@@ -276,7 +276,7 @@ export const TASK_PROFILES = {
276
276
  id: "word",
277
277
  label: "Word documents",
278
278
  prompt:
279
- "Bias toward Word/docx/document workflows while still using writing, conversion, LaTeX, or scripts when useful. Preserve source material byte-for-byte and synthesize a reader-facing document from the authoritative current facts instead of concatenating notes, logs, schemas, task IDs, private paths, or delivery instructions. Reconcile conflicting or superseded source facts before drafting; publish only the current state unless history is explicitly requested or necessary to explain a live decision. Prefer mature editable-document tooling already available in the workspace, such as python-docx, pandoc, or LibreOffice, over hand-written OOXML; when direct OOXML is genuinely necessary, validate its package parts and openability. Keep one maintainable source of truth and a reproducible project-local build command. Verify the DOCX is structurally editable, compile the PDF, run pdftotext or an equivalent extraction check that rejects replacement characters and unexpected control glyphs, and render every PDF page to a separate image under an ignored build/verification directory. Inspect every rendered page individually and repair orphaned headings, near-empty spill pages, awkward table or paragraph breaks, overlaps, clipping, excessive whitespace, weak hierarchy, and inconsistent number formatting before finishing. Retain ignored verification renders as evidence; optional cleanup must never block completion. Use clear descriptive filenames, exclude caches and generated debris from commits, inspect git status/diff before committing, and report success only after the editable source, reader-facing current-state content, visual layout, searchable text, and requested artifacts all pass.",
279
+ "Bias toward Word/docx/document workflows while still using writing, conversion, LaTeX, or scripts when useful. Preserve source material byte-for-byte and synthesize a reader-facing document from the authoritative current facts instead of concatenating notes, logs, schemas, task IDs, private paths, or delivery instructions. Reconcile conflicting or superseded source facts before drafting: values labeled corrected, replaced, earlier, old, prior, cancelled, or no longer selected identify what to discard, and those discarded literals must not appear in the final document unless the user explicitly requests history or the history is necessary to explain a live decision. Before finishing, extract the final text and search for every superseded literal found in the sources. Prefer mature editable-document tooling already available in the workspace, such as python-docx, pandoc, or LibreOffice, over hand-written OOXML; when direct OOXML is genuinely necessary, validate its package parts and openability. Keep one maintainable source of truth and a reproducible project-local build command. Verify the DOCX is structurally editable, compile the PDF, run pdftotext or an equivalent extraction check that rejects replacement characters and unexpected control glyphs, and verify PDF text bounding boxes remain inside a readable page margin. Render every PDF page to a separate image under an ignored build/verification directory. Inspect one rendered page per read_image call, never batch pages into one vision call, and repair orphaned headings, near-empty spill pages, awkward table or paragraph breaks, overlaps, clipping, excessive whitespace, weak hierarchy, and inconsistent number formatting before finishing. Retain ignored verification renders as evidence; optional cleanup must never block completion. Use clear descriptive filenames, exclude caches and generated debris from commits, inspect git status/diff before committing, and report success only after the editable source, reader-facing current-state content, visual layout, searchable text, and requested artifacts all pass.",
280
280
  tools: ["files", "shell", "canvas", "sandbox"],
281
281
  },
282
282
  latex: {