@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.
- package/docs/supervision-campaign-ledger.md +49 -0
- package/package.json +1 -1
- package/scripts/smoke-coding-tools.js +131 -1
- package/scripts/smoke-dynamic-step-budget.js +3954 -542
- package/scripts/smoke-math-rendering.js +22 -1
- package/scripts/smoke-runtime-compat.js +4 -1
- package/scripts/smoke-tmux-tools.js +22 -4
- package/src/agent-runner.js +1098 -82
- package/src/command-policy.js +749 -48
- package/src/scs-evidence.js +416 -75
- package/src/shell-syntax.js +679 -0
- package/src/workspace-tools.js +6 -6
|
@@ -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.
|
|
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",
|