@lazyingart/agintiflow 0.20.215 → 0.20.216

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.
@@ -186,3 +186,52 @@ DeepSeek-to-LocalLLM handoff when local perception is enabled, continue to block
186
186
  hosted vision without explicit authorization, and block all automatic vision
187
187
  when both local and hosted routes are disabled. A focused regression covers the
188
188
  guard and the actual LocalLLM client route.
189
+
190
+ ### SQLite migration and literal-query safety
191
+
192
+ `database-migration-safety-015` passed on AgInTiFlow `0.20.215` from a normal,
193
+ imperfect maintenance prompt. The DeepSeek-backed database profile diagnosed a
194
+ destructive version-1 migration, `INSERT OR REPLACE` identity loss, and unsafe
195
+ `LIKE` semantics. In one retained session it replaced the migration with an
196
+ in-place transaction, preserved item IDs, tags, and relationships, used an
197
+ identity-preserving UPSERT, escaped `%`, `_`, and backslashes as literal search
198
+ text, added regression tests, and committed target repair `fb97fbd`.
199
+
200
+ The run also exercised recovery behavior without a supervisor rescue prompt.
201
+ DeepSeek initially requested too many tools in one turn; the contract guard
202
+ rejected that batch and the next turn continued with allowed calls. Later, a
203
+ noncanonical `TEST_EXIT:0` shell suffix confused project-test evidence despite
204
+ five passing tests. The agent recognized the discrepancy, reran the canonical
205
+ README command, obtained `passed:true`, cleaned transient Python artifacts, and
206
+ finished normally. No AgInTiFlow product patch was required for this scenario.
207
+
208
+ Independent acceptance used
209
+ `supervision/acceptance/database_migration_safety_contract.py` to verify legacy
210
+ IDs `7`, `12`, and `19`, tag relationships, schema columns, foreign keys,
211
+ idempotent reopening, stable URL updates, literal punctuation, archive
212
+ filtering, absence of destructive SQL, intentional commit history, and a clean
213
+ worktree. The hidden contract passed.
214
+
215
+ ### Shell grammar, mutation revision, and output provenance hardening
216
+
217
+ The next campaign phase exercised completion evidence under realistic compound
218
+ commands, multiline acceptance criteria, delegated test runners, Git workflows,
219
+ and pre-existing output files. The reusable repair centralizes shell
220
+ canonicalization, command classification, Git-action intent, and evidence
221
+ tracking instead of adding project- or prompt-specific branches. In particular,
222
+ escaped line continuations and heredocs are parsed structurally; read-only test
223
+ evidence is separated from write capability; later mutations invalidate stale
224
+ validation; commit, pull-request, tag, and push evidence must occur in the
225
+ requested order; and exact output files count only when the current required
226
+ generator created or changed them.
227
+
228
+ Five independent review rounds found and drove regressions for inline mutation
229
+ batches, stale opaque validators, Git grammar and ordering, zero-test runners,
230
+ external executable paths, arithmetic shifts mistaken for heredocs, multiline
231
+ command substitutions, stale exact outputs, and ambiguous Git nouns such as
232
+ "commit message" or "branch diagram". A final fresh hosted Codex review was
233
+ blocked by its rolling quota and the DeepSeek review route was blocked by
234
+ provider balance. A separate read-only `localllm-deep` review completed with no
235
+ actionable findings. The full `npm test` suite, focused dynamic-step-budget,
236
+ coding-tools, SCS-evidence, and syntax checks all pass before packaging
237
+ AgInTiFlow `0.20.216`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.215",
3
+ "version": "0.20.216",
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",
@@ -19,7 +19,7 @@ import {
19
19
  import { formatBehaviorContractForPrompt } from "../src/behavior-contract.js";
20
20
  import { resolveRuntimeConfig } from "../src/config.js";
21
21
  import { readCodebaseMap } from "../src/codebase-map.js";
22
- import { evaluateCommandPolicy } from "../src/command-policy.js";
22
+ import { classifyCommand, evaluateCommandPolicy } from "../src/command-policy.js";
23
23
  import { shouldReviewToolResult } from "../src/scs-controller.js";
24
24
  import {
25
25
  engineeringGuidanceForTask,
@@ -328,6 +328,39 @@ try {
328
328
  packageInstallPolicy: "block",
329
329
  commandCwd: workspace,
330
330
  });
331
+ const opaqueInPlaceEditPolicy = evaluateCommandPolicy("sed -i 's/old/new/' report.md", {
332
+ allowShellTool: true,
333
+ sandboxMode: "host",
334
+ packageInstallPolicy: "block",
335
+ commandCwd: workspace,
336
+ allowDestructive: true,
337
+ });
338
+ assert(
339
+ opaqueInPlaceEditPolicy.writesWorkspace === true,
340
+ "an unknown host shell edit was incorrectly classified as read-only"
341
+ );
342
+ const destructiveGitPolicy = evaluateCommandPolicy("git reset --hard HEAD~1", {
343
+ allowShellTool: true,
344
+ sandboxMode: "host",
345
+ packageInstallPolicy: "block",
346
+ commandCwd: workspace,
347
+ allowDestructive: true,
348
+ });
349
+ assert(
350
+ destructiveGitPolicy.category === "destructive" &&
351
+ destructiveGitPolicy.writesWorkspace === true,
352
+ "a destructive git command was not classified as a workspace mutation"
353
+ );
354
+ const safeTagPolicy = evaluateCommandPolicy("git tag v0.20.216", {
355
+ allowShellTool: true,
356
+ sandboxMode: "host",
357
+ packageInstallPolicy: "block",
358
+ commandCwd: workspace,
359
+ });
360
+ assert(
361
+ safeTagPolicy.allowed && safeTagPolicy.category === "git-workflow" && safeTagPolicy.writesWorkspace,
362
+ "a bounded local git tag was not classified as a git workflow action"
363
+ );
331
364
  assert(boundedRgPolicy.allowed, "targeted bounded rg should remain allowed");
332
365
  const boundedAdvice = buildPermissionAdvice({
333
366
  toolName: "run_command",
@@ -692,6 +725,53 @@ try {
692
725
  const actualDangerAfterQuotePolicy = evaluateCommandPolicy('echo "rm -rf is text" && rm -rf reports', dockerWorkspacePolicy);
693
726
  assert(!actualDangerAfterQuotePolicy.allowed, "actual destructive command after quoted text should still be blocked");
694
727
  assert(actualDangerAfterQuotePolicy.category === "destructive", "actual destructive command after quoted text was not classified as destructive");
728
+ for (const command of [
729
+ "npm test &",
730
+ "npm test & sed -i 's/old/new/' report.md",
731
+ ]) {
732
+ const classification = classifyCommand(command);
733
+ const policy = evaluateCommandPolicy(command, hostWorkspacePolicy);
734
+ assert(
735
+ !policy.allowed &&
736
+ classification.category !== "test" &&
737
+ classification.substantiveTest !== true &&
738
+ classification.writesWorkspace === true,
739
+ `background execution bypassed bounded test policy: ${command}`
740
+ );
741
+ }
742
+ const backgroundPublishPolicy = evaluateCommandPolicy(
743
+ "npm test & npm publish",
744
+ { ...hostWorkspacePolicy, allowDestructive: true, allowPasswords: true }
745
+ );
746
+ assert(
747
+ !backgroundPublishPolicy.allowed && backgroundPublishPolicy.category === "blocked",
748
+ "a background test bypassed the hard package-publication guard"
749
+ );
750
+ for (const command of [
751
+ `node -e 'require("fs").writeFileSync("report.md", "changed")' --test`,
752
+ "node test/unit.test.js --test",
753
+ ]) {
754
+ const classification = classifyCommand(command);
755
+ assert(
756
+ classification.category !== "test" &&
757
+ classification.substantiveTest !== true &&
758
+ classification.writesWorkspace === true,
759
+ `a Node entrypoint fabricated test-runner identity: ${command}`
760
+ );
761
+ }
762
+ const redirectedTestPolicy = evaluateCommandPolicy("npm test 2>&1", hostWorkspacePolicy);
763
+ assert(
764
+ redirectedTestPolicy.allowed && redirectedTestPolicy.substantiveTest === true,
765
+ "descriptor redirection was mistaken for background test execution"
766
+ );
767
+ const singleQuoteBackslashPolicy = evaluateCommandPolicy(
768
+ "git status 'x\\\\'; touch report.md",
769
+ dockerWorkspacePolicy
770
+ );
771
+ assert(
772
+ !singleQuoteBackslashPolicy.allowed || singleQuoteBackslashPolicy.category !== "read-only",
773
+ "a literal backslash inside single quotes hid a following workspace mutation"
774
+ );
695
775
  const safeChmodAndRunPolicy = evaluateCommandPolicy(
696
776
  'chmod +x /workspace/reports/run_bounded_02079_v2.sh && bash /workspace/reports/run_bounded_02079.sh 2>&1; echo "RUN_COMMAND_EXIT: $?"',
697
777
  dockerWorkspacePolicy
@@ -996,6 +1076,18 @@ try {
996
1076
  assert(sanitizedSmallRead.content === longSmallFile, "small read_file result did not keep full content for the model");
997
1077
  assert(sanitizedSmallRead.contentTruncated === false, "small read_file result should not be marked truncated");
998
1078
  assert(!("contentPreview" in sanitizedSmallRead), "small read_file result should not replace full content with preview");
1079
+ const sanitizedCommandResult = sanitizeToolResult({
1080
+ ok: true,
1081
+ toolName: "run_command",
1082
+ args: { command: "echo password=private-value" },
1083
+ commandPolicy: { normalizedCommand: "echo password=private-value" },
1084
+ stdout: "password=private-value",
1085
+ });
1086
+ const serializedCommandResult = JSON.stringify(sanitizedCommandResult);
1087
+ assert(
1088
+ !serializedCommandResult.includes("private-value") && serializedCommandResult.includes("[REDACTED]"),
1089
+ "a nested normalized command bypassed tool-event redaction"
1090
+ );
999
1091
  const largeModelRead = toolResultForModel({
1000
1092
  ok: true,
1001
1093
  toolName: "read_file",
@@ -1444,6 +1536,44 @@ try {
1444
1536
  "hybrid wrapped unified apply_patch did not update expected file"
1445
1537
  );
1446
1538
 
1539
+ await fs.mkdir(path.join(workspace, "a"), { recursive: true });
1540
+ await fs.writeFile(path.join(workspace, "report.md"), "root report\n", "utf8");
1541
+ await fs.writeFile(path.join(workspace, "a", "report.md"), "nested old\n", "utf8");
1542
+ const nestedCustomPatch = await executeWorkspaceTool(
1543
+ "apply_patch",
1544
+ {
1545
+ patch: [
1546
+ "*** Begin Patch",
1547
+ "*** Update File: a/report.md",
1548
+ "@@",
1549
+ "-nested old",
1550
+ "+nested new",
1551
+ "*** End Patch",
1552
+ ].join("\n"),
1553
+ },
1554
+ { commandCwd: workspace, allowFileTools: true }
1555
+ );
1556
+ assert(
1557
+ nestedCustomPatch.ok &&
1558
+ (await fs.readFile(path.join(workspace, "a", "report.md"), "utf8")) === "nested new\n" &&
1559
+ (await fs.readFile(path.join(workspace, "report.md"), "utf8")) === "root report\n",
1560
+ "custom patch path canonicalization confused a real a/ directory with a unified-diff prefix"
1561
+ );
1562
+ const nestedCustomDelete = await executeWorkspaceTool(
1563
+ "apply_patch",
1564
+ { patch: ["*** Begin Patch", "*** Delete File: a/report.md", "*** End Patch"].join("\n") },
1565
+ { commandCwd: workspace, allowFileTools: true }
1566
+ );
1567
+ assert(nestedCustomDelete.ok, "custom patch could not delete its exact nested target");
1568
+ const nestedStillExists = await fs
1569
+ .stat(path.join(workspace, "a", "report.md"))
1570
+ .then(() => true)
1571
+ .catch(() => false);
1572
+ assert(
1573
+ !nestedStillExists && (await fs.readFile(path.join(workspace, "report.md"), "utf8")) === "root report\n",
1574
+ "custom delete stripped a real a/ directory and deleted the wrong root file"
1575
+ );
1576
+
1447
1577
  await fs.writeFile(path.join(workspace, "repair-report.md"), "old report\n", "utf8");
1448
1578
  const ordinaryAddExistingError = await executeWorkspaceTool(
1449
1579
  "apply_patch",