@lazyingart/agintiflow 0.20.255 → 0.20.256

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.255",
3
+ "version": "0.20.256",
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",
@@ -1076,7 +1076,13 @@ try {
1076
1076
  }
1077
1077
  const reviewResult = await runChat("/review changed files only\n/exit\n");
1078
1078
  if (!reviewResult.stdout.includes("Review focus: changed files only") || !reviewResult.stdout.includes("Mock run complete")) {
1079
- throw new Error("interactive /review did not launch the bounded review workflow");
1079
+ throw new Error(
1080
+ [
1081
+ "interactive /review did not launch the bounded review workflow",
1082
+ `stdout tail:\n${reviewResult.stdout.slice(-6000)}`,
1083
+ `stderr tail:\n${reviewResult.stderr.slice(-3000)}`,
1084
+ ].join("\n")
1085
+ );
1080
1086
  }
1081
1087
  const scsDefaultStatusResult = await runChat("/scs status\n");
1082
1088
  if (!scsDefaultStatusResult.stdout.includes("SCS mode: auto")) {
@@ -1353,5 +1359,7 @@ try {
1353
1359
  )
1354
1360
  );
1355
1361
  } finally {
1356
- await fs.rm(tempRoot, { recursive: true, force: true });
1362
+ if (process.env.AGINTIFLOW_SMOKE_KEEP_TEMP !== "1") {
1363
+ await fs.rm(tempRoot, { recursive: true, force: true });
1364
+ }
1357
1365
  }
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import {
7
7
  buildModelTimeoutRetryMessages,
8
8
  genericArtifactFilenameBlock,
9
+ modelTimeoutExhaustionRoute,
9
10
  modelTimeoutRetryRoute,
10
11
  applyModelTimeoutRetryRoute,
11
12
  recoverFocusedTextRewriteWithWritingSpecialist,
@@ -734,6 +735,37 @@ try {
734
735
  exactReadOnlyMultiPatternSummaryPolicy.writesWorkspace === false,
735
736
  "a bounded grep/sort/uniq count summary incorrectly required destructive host permission"
736
737
  );
738
+ const exactPdfHeaderAudit =
739
+ 'cd /home/lachlan/ProjectsLFS/AgenticApp/output/wechat_worker/task-123 && file report.pdf && head -c 8 report.pdf | xxd | head -1';
740
+ const exactPdfHeaderAuditPolicy = evaluateCommandPolicy(
741
+ exactPdfHeaderAudit,
742
+ {
743
+ ...hostWorkspacePolicy,
744
+ commandCwd: "/home/lachlan/ProjectsLFS/AgenticApp",
745
+ }
746
+ );
747
+ assert(
748
+ exactPdfHeaderAuditPolicy.allowed &&
749
+ exactPdfHeaderAuditPolicy.category === "read-only" &&
750
+ exactPdfHeaderAuditPolicy.needsNetwork === false &&
751
+ exactPdfHeaderAuditPolicy.writesWorkspace === false,
752
+ "a bounded workspace-local PDF header audit incorrectly required destructive host permission"
753
+ );
754
+ for (const unsafeXxdCommand of [
755
+ "xxd -r - output.bin",
756
+ "xxd report.pdf output.hex",
757
+ ]) {
758
+ const unsafeXxdPolicy = evaluateCommandPolicy(
759
+ unsafeXxdCommand,
760
+ hostWorkspacePolicy
761
+ );
762
+ assert(
763
+ !unsafeXxdPolicy.allowed ||
764
+ unsafeXxdPolicy.category !== "read-only" ||
765
+ unsafeXxdPolicy.writesWorkspace === true,
766
+ `write-capable xxd command bypassed host policy: ${unsafeXxdCommand}`
767
+ );
768
+ }
737
769
  const exactReadOnlyGitIdentityProbe =
738
770
  'git status --short && echo "TOPLEVEL=$(git rev-parse --show-toplevel 2>&1)" && echo "BRANCH=$(git rev-parse --abbrev-ref HEAD 2>&1)"';
739
771
  const exactReadOnlyGitIdentityProbePolicy = evaluateCommandPolicy(
@@ -971,6 +1003,12 @@ try {
971
1003
  const pythonDemoPolicy = evaluateCommandPolicy("python3 demo.py 2>&1", dockerWorkspaceNoInstallsPolicy);
972
1004
  assert(pythonDemoPolicy.allowed, "workspace-local python demo script should be allowed without package installs");
973
1005
  assert(pythonDemoPolicy.category === "toolchain", "workspace-local python demo script should be classified as toolchain");
1006
+ const scopedPdfBuildPolicy = evaluateCommandPolicy(
1007
+ "cd output/task-1 && python3 build_report.py 2>&1 | tail -20 && ls -la *.pdf && sha256sum *.pdf",
1008
+ hostWorkspacePolicy
1009
+ );
1010
+ assert(scopedPdfBuildPolicy.allowed, "bounded PDF builder plus read-only verification was treated as broad host shell");
1011
+ assert(scopedPdfBuildPolicy.category === "toolchain", "bounded PDF builder pipeline should remain a toolchain");
974
1012
  const broadLocalProbePolicy = evaluateCommandPolicy(
975
1013
  'python3 --version; echo "---IMPORT TEST---"; python3 -c "import pathlib; print(pathlib.Path.cwd())" 2>&1; echo "---FILES---"; ls -la 2>&1 | head -20',
976
1014
  dockerWorkspaceNoInstallsPolicy
@@ -1232,6 +1270,27 @@ try {
1232
1270
  const localGitCommitPolicy = evaluateCommandPolicy('git commit -m "Initial local workflow commit"', dockerWorkspaceNoInstallsPolicy);
1233
1271
  assert(localGitCommitPolicy.allowed, "local git commit should be allowed without package installs");
1234
1272
  assert(localGitCommitPolicy.category === "git-workflow", "local git commit should be classified as git-workflow");
1273
+ const privateRuntimeStagePolicy = evaluateCommandPolicy("git add -- '.aginti/'", dockerWorkspaceNoInstallsPolicy);
1274
+ assert(!privateRuntimeStagePolicy.allowed, "private .aginti runtime state must never be stageable");
1275
+ assert(
1276
+ privateRuntimeStagePolicy.permissionAdvice?.autoRecover === true,
1277
+ "private runtime staging should recover automatically instead of pausing for user approval"
1278
+ );
1279
+ const chainedPrivateRuntimeStagePolicy = evaluateCommandPolicy(
1280
+ "git add -- 'service_ctl.py' && git add -- '.aginti/' && git commit -m 'Repair service'",
1281
+ dockerWorkspaceNoInstallsPolicy
1282
+ );
1283
+ assert(!chainedPrivateRuntimeStagePolicy.allowed, "a chained command must not stage private runtime state");
1284
+ const broadGitAddPolicy = evaluateCommandPolicy("git add -A", dockerWorkspaceNoInstallsPolicy);
1285
+ assert(!broadGitAddPolicy.allowed, "broad git staging must be rejected");
1286
+ assert(
1287
+ broadGitAddPolicy.permissionAdvice?.autoRecover === true,
1288
+ "broad staging should recover automatically to exact task-owned paths"
1289
+ );
1290
+ const commitAllPolicy = evaluateCommandPolicy("git commit -a -m 'Repair service'", dockerWorkspaceNoInstallsPolicy);
1291
+ assert(!commitAllPolicy.allowed, "git commit -a must not bypass bounded task-owned staging");
1292
+ const boundedGitAddPolicy = evaluateCommandPolicy("git add -- 'service_ctl.py'", dockerWorkspaceNoInstallsPolicy);
1293
+ assert(boundedGitAddPolicy.allowed, "bounded task-owned git staging should remain available");
1235
1294
  const localGitSwitchPolicy = evaluateCommandPolicy("git switch -c feature-a", dockerWorkspaceNoInstallsPolicy);
1236
1295
  assert(localGitSwitchPolicy.allowed, "local git switch -c should be allowed without package installs");
1237
1296
  const localGitCheckoutExistingPolicy = evaluateCommandPolicy("git checkout main", dockerWorkspaceNoInstallsPolicy);
@@ -1299,6 +1358,40 @@ try {
1299
1358
  }),
1300
1359
  "recoverable workspace cd correction still paused the session"
1301
1360
  );
1361
+ const outsideScratchAdvice = buildPermissionAdvice({
1362
+ toolName: "run_command",
1363
+ args: { command: "mkdir -p /tmp/test_service && cd /tmp/test_service" },
1364
+ guard: {
1365
+ category: "blocked",
1366
+ reason: "mkdir target must be a safe workspace-relative directory: /tmp/test_service",
1367
+ },
1368
+ config: {
1369
+ ...dockerWorkspacePolicy,
1370
+ commandCwd: "/home/example/project",
1371
+ },
1372
+ state: { sessionId: "coding-outside-scratch-smoke" },
1373
+ });
1374
+ assert(
1375
+ outsideScratchAdvice.autoRecover === true,
1376
+ "an outside scratch-directory request should be corrected in-turn instead of pausing"
1377
+ );
1378
+ assert(
1379
+ /\.aginti\/verification/i.test(outsideScratchAdvice.instruction),
1380
+ "scratch-directory correction did not provide a workspace-relative verification path"
1381
+ );
1382
+ assert(
1383
+ !/package-install|approve-package/i.test(
1384
+ [outsideScratchAdvice.summary, outsideScratchAdvice.instruction].join("\n")
1385
+ ),
1386
+ "scratch-directory correction incorrectly suggested package-install escalation"
1387
+ );
1388
+ assert(
1389
+ !shouldPauseForPermissionAdvice({
1390
+ blocked: true,
1391
+ permissionAdvice: outsideScratchAdvice,
1392
+ }),
1393
+ "recoverable scratch-directory correction still paused the session"
1394
+ );
1302
1395
  const destructiveAdvice = buildPermissionAdvice({
1303
1396
  toolName: "run_command",
1304
1397
  args: { command: "rm -rf reports && git reset --hard" },
@@ -1448,6 +1541,95 @@ try {
1448
1541
  !shouldPauseForPermissionAdvice({ blocked: true, permissionAdvice: dynamicEvidenceAdvice }),
1449
1542
  "dynamic evidence filename recovery still produced a permission pause"
1450
1543
  );
1544
+ const mixedToolchainAuditCommand = [
1545
+ "cd output/task-1 && python3 build_report.py",
1546
+ "python3 - <<'PY'",
1547
+ "from pathlib import Path",
1548
+ "print(Path('report.pdf').stat().st_size)",
1549
+ "PY",
1550
+ ].join(" && ");
1551
+ const mixedToolchainAuditAdvice = buildPermissionAdvice({
1552
+ toolName: "run_command",
1553
+ args: { command: mixedToolchainAuditCommand },
1554
+ guard: {
1555
+ category: "general-shell",
1556
+ reason: "General shell commands on the host require Allow destructive actions.",
1557
+ },
1558
+ config: hostWorkspacePolicy,
1559
+ state: { sessionId: "coding-mixed-toolchain-audit-smoke" },
1560
+ });
1561
+ assert(
1562
+ mixedToolchainAuditAdvice.autoRecover === true &&
1563
+ mixedToolchainAuditAdvice.recoveryCommand ===
1564
+ "cd output/task-1 && python3 build_report.py",
1565
+ "mixed toolchain and inline audit did not recover to the safe build prefix"
1566
+ );
1567
+ assert(
1568
+ !shouldPauseForPermissionAdvice({
1569
+ blocked: true,
1570
+ permissionAdvice: mixedToolchainAuditAdvice,
1571
+ }),
1572
+ "mixed toolchain and inline audit recovery still paused the session"
1573
+ );
1574
+ const unsafeMixedAuditAdvice = buildPermissionAdvice({
1575
+ toolName: "run_command",
1576
+ args: {
1577
+ command:
1578
+ "rm -rf output/task-1 && python3 build_report.py && python3 - <<'PY'\nprint('x')\nPY",
1579
+ },
1580
+ guard: {
1581
+ category: "general-shell",
1582
+ reason: "General shell commands on the host require Allow destructive actions.",
1583
+ },
1584
+ config: hostWorkspacePolicy,
1585
+ state: { sessionId: "coding-unsafe-mixed-audit-smoke" },
1586
+ });
1587
+ assert(
1588
+ unsafeMixedAuditAdvice.autoRecover !== true,
1589
+ "destructive mixed audit received an unsafe automatic recovery"
1590
+ );
1591
+ const inlinePdfAuditAdvice = buildPermissionAdvice({
1592
+ toolName: "run_command",
1593
+ args: {
1594
+ command:
1595
+ 'cd output/task-1 && python3 -c "import re;d=open(\'report.pdf\',\'rb\').read();print(len(re.findall(rb\'/Type\\\\s*/Page[^s]\',d)))"',
1596
+ },
1597
+ guard: {
1598
+ category: "general-shell",
1599
+ reason: "General shell commands on the host require Allow destructive actions.",
1600
+ },
1601
+ config: hostWorkspacePolicy,
1602
+ state: { sessionId: "coding-inline-pdf-audit-smoke" },
1603
+ });
1604
+ assert(
1605
+ inlinePdfAuditAdvice.autoRecover === true &&
1606
+ /deterministic artifact gate/i.test(inlinePdfAuditAdvice.instruction),
1607
+ "read-only inline PDF audit did not recover without stronger permission"
1608
+ );
1609
+ assert(
1610
+ !shouldPauseForPermissionAdvice({
1611
+ blocked: true,
1612
+ permissionAdvice: inlinePdfAuditAdvice,
1613
+ }),
1614
+ "read-only inline PDF audit recovery still paused the session"
1615
+ );
1616
+ const inlinePdfWriteAdvice = buildPermissionAdvice({
1617
+ toolName: "run_command",
1618
+ args: {
1619
+ command:
1620
+ 'cd output/task-1 && python3 -c "open(\'report.pdf\',\'wb\').write(b\'replacement\')"',
1621
+ },
1622
+ guard: {
1623
+ category: "general-shell",
1624
+ reason: "General shell commands on the host require Allow destructive actions.",
1625
+ },
1626
+ config: hostWorkspacePolicy,
1627
+ state: { sessionId: "coding-inline-pdf-write-smoke" },
1628
+ });
1629
+ assert(
1630
+ inlinePdfWriteAdvice.autoRecover !== true,
1631
+ "write-capable inline Python received read-only automatic recovery"
1632
+ );
1451
1633
  const malformedPatchGuard = {
1452
1634
  allowed: false,
1453
1635
  category: "workspace-patch",
@@ -1878,6 +2060,33 @@ try {
1878
2060
  !boundedList.entries.some((item) => item.path.startsWith(".runtime-generated")),
1879
2061
  "list_files traversed an unrecognized hidden runtime directory"
1880
2062
  );
2063
+ await fs.mkdir(path.join(workspace, "output", "task-scope"), { recursive: true });
2064
+ await fs.writeFile(path.join(workspace, "output", "task-scope", "report.md"), "scoped\n", "utf8");
2065
+ await fs.writeFile(path.join(workspace, "unrelated.md"), "unrelated\n", "utf8");
2066
+ const scopedRead = await executeWorkspaceTool(
2067
+ "read_file",
2068
+ { path: "output/task-scope/report.md" },
2069
+ {
2070
+ commandCwd: workspace,
2071
+ allowFileTools: true,
2072
+ workspacePathScopeRoots: ["output/task-scope"],
2073
+ }
2074
+ );
2075
+ assert(scopedRead.content === "scoped\n", "task-scoped artifact read failed inside its root");
2076
+ const outsideScopedRead = await executeWorkspaceTool(
2077
+ "read_file",
2078
+ { path: "unrelated.md" },
2079
+ {
2080
+ commandCwd: workspace,
2081
+ allowFileTools: true,
2082
+ workspacePathScopeRoots: ["output/task-scope"],
2083
+ }
2084
+ );
2085
+ assert(
2086
+ outsideScopedRead.blocked === true &&
2087
+ /outside the active task artifact scope/.test(String(outsideScopedRead.reason || "")),
2088
+ "task-scoped artifact tools could read an unrelated repository file"
2089
+ );
1881
2090
  const filenameSearch = await executeWorkspaceTool(
1882
2091
  "search_files",
1883
2092
  { path: ".", query: "routine_entry.py", maxResults: 5 },
@@ -2143,8 +2352,43 @@ try {
2143
2352
  model: "localllm-fast",
2144
2353
  });
2145
2354
  assert(
2146
- defaultLocalTimeoutRoute.timeoutMs === 300000 && defaultLocalTimeoutRoute.retryTimeoutMs === 600000,
2147
- "LocalLLM default timeout did not allow bounded slow local generation and one longer retry"
2355
+ defaultLocalTimeoutRoute.timeoutMs === 300000 && defaultLocalTimeoutRoute.retryTimeoutMs === 180000,
2356
+ "LocalLLM same-model timeout retry retained a doubled stall window after prompt compaction"
2357
+ );
2358
+ const exhaustedFastRoute = modelTimeoutExhaustionRoute(
2359
+ {
2360
+ provider: "localllm",
2361
+ model: "localllm-fast",
2362
+ mainProvider: "localllm",
2363
+ mainModel: "localllm-deep",
2364
+ spareProvider: "localllm",
2365
+ spareModel: "localllm-deep",
2366
+ localAvailableModels: ["localllm-fast", "localllm-deep"],
2367
+ },
2368
+ defaultLocalTimeoutRoute
2369
+ );
2370
+ assert(
2371
+ exhaustedFastRoute.switchedModel === true &&
2372
+ exhaustedFastRoute.model === "localllm-deep" &&
2373
+ exhaustedFastRoute.retryTimeoutMs === 240000,
2374
+ "two bounded fast-route timeouts did not select one stronger in-provider recovery hop"
2375
+ );
2376
+ const alreadySwitchedTimeoutRoute = modelTimeoutExhaustionRoute(
2377
+ {
2378
+ provider: "localllm",
2379
+ model: "localllm-deep",
2380
+ mainProvider: "localllm",
2381
+ mainModel: "localllm-deep",
2382
+ },
2383
+ modelTimeoutRetryRoute({
2384
+ provider: "localllm",
2385
+ model: "localllm-deep",
2386
+ modelTimeoutMs: 120000,
2387
+ })
2388
+ );
2389
+ assert(
2390
+ alreadySwitchedTimeoutRoute.switchedModel === false,
2391
+ "timeout exhaustion attempted an unbounded model bounce after an existing route switch"
2148
2392
  );
2149
2393
  const artifactTimeoutMessages = buildModelTimeoutRetryMessages(
2150
2394
  {
@@ -2310,14 +2554,33 @@ try {
2310
2554
  assert(patchRun.events.some((event) => event.type === "file.changed"), "patch run did not persist file.changed event");
2311
2555
 
2312
2556
  await fs.writeFile(path.join(workspace, "patch-target.txt"), "old\n", "utf8");
2313
- const multiPatchRun = await runMock("Apply multi-file Codex patch to replace old and add a note.", "coding-patch-multi");
2557
+ const multiPatchRun = await executeWorkspaceTool(
2558
+ "apply_patch",
2559
+ {
2560
+ patch: [
2561
+ "*** Begin Patch",
2562
+ "*** Update File: patch-target.txt",
2563
+ "@@",
2564
+ "-old",
2565
+ "+new",
2566
+ "*** Add File: notes/patch-note.md",
2567
+ "+Created by AgInTiFlow mock mode.",
2568
+ "+Goal: multi-file patch smoke.",
2569
+ "*** End Patch",
2570
+ ].join("\n"),
2571
+ },
2572
+ {
2573
+ commandCwd: workspace,
2574
+ allowFileTools: true,
2575
+ }
2576
+ );
2314
2577
  const multiPatched = await fs.readFile(path.join(workspace, "patch-target.txt"), "utf8");
2315
2578
  const patchNote = await fs.readFile(path.join(workspace, "notes/patch-note.md"), "utf8");
2316
2579
  assert(multiPatched === "new\n", "mock multi-file patch did not update expected file");
2317
2580
  assert(patchNote.includes("multi-file patch smoke"), "mock multi-file patch did not add expected file");
2318
2581
  assert(
2319
- multiPatchRun.events.filter((event) => event.type === "file.changed").length >= 2,
2320
- "multi-file patch did not persist per-file change events"
2582
+ multiPatchRun.ok && multiPatchRun.changes?.length >= 2,
2583
+ "multi-file patch did not report each file change"
2321
2584
  );
2322
2585
 
2323
2586
  await fs.writeFile(path.join(workspace, "unified-target.txt"), "alpha\nold\nomega\n", "utf8");
@@ -208,6 +208,7 @@ const compactionState = {
208
208
  content: JSON.stringify({
209
209
  ok: true,
210
210
  toolName: "read_file",
211
+ goalRevision: 11,
211
212
  path: "reports/reliability.md",
212
213
  startLine: 1,
213
214
  lineLimit: 50,
@@ -253,6 +254,7 @@ const compactionState = {
253
254
  content: JSON.stringify({
254
255
  ok: true,
255
256
  toolName: "read_file",
257
+ goalRevision: 11,
256
258
  path: "/evidence/Musia/SKILL.md",
257
259
  bytes: 2048,
258
260
  sha256: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
@@ -307,6 +309,21 @@ assert.ok(runtimeText.includes("/evidence/Musia/SKILL.md"));
307
309
  assert.ok(runtimeText.includes("Create and review songs through the established Musia production workflow"));
308
310
  assert.ok(runtimeText.includes("# Musia Music Production"));
309
311
  assert.ok(runtimeText.includes("sha256=1234567890abcdef"));
312
+ const retainedRevisionedReadIndex = runtimeMessages.findIndex((message) =>
313
+ Array.isArray(message.tool_calls) &&
314
+ message.tool_calls.some(
315
+ (call) =>
316
+ call?.function?.name === "read_file" &&
317
+ String(call?.function?.arguments || "").includes("reports/reliability.md") &&
318
+ String(call?.function?.arguments || "").includes('"startLine":1')
319
+ )
320
+ );
321
+ assert.ok(retainedRevisionedReadIndex >= 0, "native compaction lost the revisioned source read");
322
+ assert.equal(
323
+ JSON.parse(runtimeMessages[retainedRevisionedReadIndex + 1].content).goalRevision,
324
+ 11,
325
+ "runtime compaction dropped the source read's goal revision"
326
+ );
310
327
  assert.ok(runtimeText.includes("content=complete"));
311
328
  assert.ok(runtimeText.includes("node bin/musia.js doctor --json"));
312
329
  assert.ok(runtimeText.includes("scripts/xyq_cdp_browser.py"));