@lazyingart/agintiflow 0.20.318 → 0.20.320

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.318",
3
+ "version": "0.20.320",
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",
@@ -4556,6 +4556,105 @@ try {
4556
4556
  failedSpacedExitProbeResult.projectTest?.explicitExitStatus === 1,
4557
4557
  "a spaced nonzero test-status wrapper was recorded as successful generic evidence"
4558
4558
  );
4559
+ const unsafePipedTestCommand =
4560
+ "cd . && PYTHONPATH=src python -m pytest tests/test_wechat_document_reader.py -q 2>&1 | tail -5 && echo '---artifacts---' && ls -la output/task";
4561
+ const unsafePipedTestState = { meta: { goalContract: { revision: 1 } } };
4562
+ const unsafePipedTestResult = {
4563
+ toolName: "run_command",
4564
+ ok: true,
4565
+ exitCode: 0,
4566
+ args: { command: unsafePipedTestCommand },
4567
+ stdout: "....... [100%]\n7 passed in 0.04s\n---artifacts---\n",
4568
+ stderr: "",
4569
+ projectMutationPaths: [],
4570
+ commandPolicy: classifyCommand(unsafePipedTestCommand),
4571
+ };
4572
+ recordProjectVerificationOutcome(
4573
+ unsafePipedTestState,
4574
+ unsafePipedTestResult,
4575
+ { commandCwd: workspace, taskProfile: "qa", allowShellTool: true, sandboxMode: "host" }
4576
+ );
4577
+ assert(
4578
+ unsafePipedTestState.meta.projectVerification?.mutationRevision === 0 &&
4579
+ unsafePipedTestResult.nonAuthoritativeTestOutputPipeline === true &&
4580
+ unsafePipedTestResult.projectTest === undefined,
4581
+ `an exit-status-hiding test pipeline fabricated source mutation or passing test evidence: ${JSON.stringify({
4582
+ mutationRevision:
4583
+ unsafePipedTestState.meta.projectVerification?.mutationRevision,
4584
+ nonAuthoritativeTestOutputPipeline:
4585
+ unsafePipedTestResult.nonAuthoritativeTestOutputPipeline,
4586
+ projectTest: unsafePipedTestResult.projectTest,
4587
+ policy: unsafePipedTestResult.commandPolicy,
4588
+ })}`
4589
+ );
4590
+ const mutatingPipedTestCommand = `${unsafePipedTestCommand} && touch src/agent-runner.js`;
4591
+ const mutatingPipedTestState = { meta: { goalContract: { revision: 1 } } };
4592
+ const mutatingPipedTestResult = {
4593
+ toolName: "run_command",
4594
+ ok: true,
4595
+ exitCode: 0,
4596
+ args: { command: mutatingPipedTestCommand },
4597
+ stdout: "7 passed in 0.04s\n",
4598
+ stderr: "",
4599
+ projectMutationPaths: [],
4600
+ commandPolicy: classifyCommand(mutatingPipedTestCommand),
4601
+ };
4602
+ recordProjectVerificationOutcome(
4603
+ mutatingPipedTestState,
4604
+ mutatingPipedTestResult,
4605
+ {
4606
+ commandCwd: workspace,
4607
+ taskProfile: "qa",
4608
+ allowShellTool: true,
4609
+ allowDestructive: true,
4610
+ sandboxMode: "host",
4611
+ }
4612
+ );
4613
+ assert(
4614
+ mutatingPipedTestState.meta.projectVerification?.mutationRevision === 1 &&
4615
+ mutatingPipedTestResult.nonAuthoritativeTestOutputPipeline !== true,
4616
+ "a source mutation appended to an unsafe test pipeline escaped revision tracking"
4617
+ );
4618
+ const workspaceCdWrappedTestCommand =
4619
+ `cd ${workspace} && PYTHONPATH=src python -m pytest tests/test_wechat_document_reader.py -v; echo "EXIT=$?"`;
4620
+ assert(
4621
+ isSubstantiveTestCommand(workspaceCdWrappedTestCommand, {
4622
+ commandCwd: workspace,
4623
+ }),
4624
+ "a safe explicit-status test wrapper lost test identity behind a workspace cd"
4625
+ );
4626
+ const workspaceCdWrappedTestState = {
4627
+ meta: { goalContract: { revision: 1 } },
4628
+ };
4629
+ const workspaceCdWrappedTestResult = {
4630
+ toolName: "run_command",
4631
+ ok: true,
4632
+ exitCode: 0,
4633
+ args: { command: workspaceCdWrappedTestCommand },
4634
+ stdout: "7 passed in 0.06s\nEXIT=0\n",
4635
+ stderr: "",
4636
+ projectMutationPaths: [],
4637
+ commandPolicy: classifyCommand(workspaceCdWrappedTestCommand),
4638
+ };
4639
+ recordProjectVerificationOutcome(
4640
+ workspaceCdWrappedTestState,
4641
+ workspaceCdWrappedTestResult,
4642
+ {
4643
+ commandCwd: workspace,
4644
+ taskProfile: "qa",
4645
+ allowShellTool: true,
4646
+ sandboxMode: "host",
4647
+ }
4648
+ );
4649
+ assert(
4650
+ workspaceCdWrappedTestResult.projectTest?.passed === true &&
4651
+ workspaceCdWrappedTestState.meta.projectVerification?.testRuns?.length === 1,
4652
+ `a successful workspace-cd status wrapper did not produce one passing test run: ${JSON.stringify({
4653
+ projectTest: workspaceCdWrappedTestResult.projectTest,
4654
+ testRuns:
4655
+ workspaceCdWrappedTestState.meta.projectVerification?.testRuns,
4656
+ })}`
4657
+ );
4559
4658
  const pipedUnittestCommand =
4560
4659
  "python3 -m unittest discover -s tests -p 'test_wechat_document_reader.py' -v 2>&1 | tail -14; echo \"EXIT=${PIPESTATUS[0]}\"";
4561
4660
  assert(
@@ -5189,6 +5189,13 @@ function boundedReadOnlyTestPrefixCommand(value = "") {
5189
5189
  const command = normalizeProjectCommand(value);
5190
5190
  if (!command) return false;
5191
5191
  const tokens = tokenizeShellWords(command);
5192
+ if (
5193
+ tokens[0] === "cd" &&
5194
+ tokens.length === 2 &&
5195
+ !/(?:`|\$\(|[|<>])/u.test(command)
5196
+ ) {
5197
+ return true;
5198
+ }
5192
5199
  if (
5193
5200
  ["date", "echo", "printf"].includes(String(tokens[0] || "")) &&
5194
5201
  !/(?:`|\$\()/u.test(command)
@@ -5361,6 +5368,55 @@ function projectTestCommandWrapper(command = "") {
5361
5368
  );
5362
5369
  }
5363
5370
 
5371
+ function commandIsNonMutatingTestOutputPipeline(command = "") {
5372
+ const normalized = normalizeProjectCommand(command);
5373
+ if (!normalized || /\b(?:PIPESTATUS|pipefail)\b/u.test(normalized)) return false;
5374
+ const sequence = parseTopLevelShellSequence(normalized);
5375
+ if (
5376
+ sequence.openQuote ||
5377
+ sequence.trailingEscape ||
5378
+ sequence.trailingSeparator ||
5379
+ !sequence.commands.length
5380
+ ) {
5381
+ return false;
5382
+ }
5383
+ let sawTestPipeline = false;
5384
+ for (let index = 0; index < sequence.commands.length;) {
5385
+ if (sequence.separators[index] === "|") {
5386
+ let pipelineEnd = index + 1;
5387
+ while (sequence.separators[pipelineEnd] === "|") pipelineEnd += 1;
5388
+ const test = boundedTestCommandClassification(sequence.commands[index]);
5389
+ if (test.policy.substantiveTest !== true) return false;
5390
+ const filtersAreReadOnly = sequence.commands
5391
+ .slice(index + 1, pipelineEnd + 1)
5392
+ .every((candidate) => {
5393
+ const tokens = tokenizeShellWords(candidate);
5394
+ if (
5395
+ !["cat", "grep", "head", "sed", "sort", "tail", "tr", "uniq", "wc"].includes(
5396
+ String(tokens[0] || "")
5397
+ ) ||
5398
+ /(?:`|\$\(|[<>])/u.test(candidate)
5399
+ ) {
5400
+ return false;
5401
+ }
5402
+ const policy = classifyCommand(candidate);
5403
+ return policy.writesWorkspace !== true && policy.mayMutateProject !== true;
5404
+ });
5405
+ if (!filtersAreReadOnly) return false;
5406
+ sawTestPipeline = true;
5407
+ index = pipelineEnd + 1;
5408
+ continue;
5409
+ }
5410
+ const candidate = String(sequence.commands[index] || "")
5411
+ .trim()
5412
+ .replace(/^(?:[A-Za-z_]\w*=(?:"[^"\n]*"|'[^'\n]*'|[^\s;&|]+)\s*)+/u, "")
5413
+ .trim();
5414
+ if (candidate && !boundedReadOnlyTestPrefixCommand(candidate)) return false;
5415
+ index += 1;
5416
+ }
5417
+ return sawTestPipeline;
5418
+ }
5419
+
5364
5420
  function projectExitStatusWrapper(command = "") {
5365
5421
  return (
5366
5422
  parseNonMutatingExitStatusWrapper(command) ||
@@ -5439,10 +5495,12 @@ export function isSubstantiveTestCommand(command = "", config = {}) {
5439
5495
  if (isRetainedExactVerificationCommand(command, config)) return true;
5440
5496
  const normalized = normalizeProjectCommand(normalizeCommandForPolicy(command, config));
5441
5497
  if (projectTestCommandWrapper(normalized)) return true;
5442
- const text = projectExitStatusWrapper(normalized)?.command || normalized;
5498
+ const wrappedTestCommand =
5499
+ projectExitStatusWrapper(normalized)?.command || normalized;
5500
+ const text = normalizeLeadingWorkspaceCd(wrappedTestCommand, config);
5443
5501
  if (!text) return false;
5444
- const classification = classifyCommand(text);
5445
- if (classification.substantiveTest !== true) return false;
5502
+ const classification = boundedTestCommandClassification(text);
5503
+ if (classification.policy.substantiveTest !== true) return false;
5446
5504
  const sequence = parseTopLevelShellSequence(text);
5447
5505
  if (sequence.commands.length <= 1) return true;
5448
5506
  if (!sequence.separators.every((separator) => separator === "&&")) return false;
@@ -5450,9 +5508,17 @@ export function isSubstantiveTestCommand(command = "", config = {}) {
5450
5508
  let lastTestIndex = -1;
5451
5509
  let lastMutationIndex = -1;
5452
5510
  sequence.commands.forEach((segment, index) => {
5453
- const segmentPolicy = classifyCommand(segment);
5511
+ const segmentClassification = boundedTestCommandClassification(segment);
5512
+ const segmentPolicy = segmentClassification.policy;
5454
5513
  if (segmentPolicy.substantiveTest === true) lastTestIndex = index;
5455
- if (commandCanMutateProjectContent(segment, segmentPolicy)) lastMutationIndex = index;
5514
+ if (
5515
+ commandCanMutateProjectContent(
5516
+ segmentClassification.executableCommand,
5517
+ segmentPolicy
5518
+ )
5519
+ ) {
5520
+ lastMutationIndex = index;
5521
+ }
5456
5522
  });
5457
5523
  return lastTestIndex >= 0 && lastTestIndex >= lastMutationIndex;
5458
5524
  }
@@ -7248,6 +7314,10 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
7248
7314
  commandMutationPaths.length === 0 &&
7249
7315
  commandIsBoundedReadOnlyArtifactValidation(mutationCommand)
7250
7316
  );
7317
+ const nonMutatingTestOutputPipeline = Boolean(
7318
+ commandMutationPaths.length === 0 &&
7319
+ commandIsNonMutatingTestOutputPipeline(mutationCommand)
7320
+ );
7251
7321
  const projectContentMutation = Boolean(
7252
7322
  command &&
7253
7323
  toolResult.blocked !== true &&
@@ -7257,6 +7327,7 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
7257
7327
  !disposableGeneratedVerificationSideEffects &&
7258
7328
  !scopedTaskArtifactWrite &&
7259
7329
  !readOnlyArtifactValidation &&
7330
+ !nonMutatingTestOutputPipeline &&
7260
7331
  (commandSucceeded || commandMutationPaths.length > 0) &&
7261
7332
  (
7262
7333
  commandCanMutateProjectContent(mutationCommand, commandPolicy) ||
@@ -7277,6 +7348,9 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
7277
7348
  if (readOnlyArtifactValidation) {
7278
7349
  toolResult.readOnlyArtifactValidation = true;
7279
7350
  }
7351
+ if (nonMutatingTestOutputPipeline) {
7352
+ toolResult.nonAuthoritativeTestOutputPipeline = true;
7353
+ }
7280
7354
  if (testCommandWrapper?.semanticallyReadOnly === true) {
7281
7355
  toolResult.boundedReadOnlyTestWrapper = true;
7282
7356
  }
@@ -22632,6 +22706,9 @@ async function completionEvidenceDecision({ config, state, store, observers, ste
22632
22706
  detail.suggestedTestCommands.length
22633
22707
  ? `Use the established test command now: ${detail.suggestedTestCommands.join("; ")}.`
22634
22708
  : "",
22709
+ detail.missingEvidence.includes("test")
22710
+ ? "Run the focused test directly without piping it through tail, head, tee, grep, or another output filter that can replace the test exit status. If bounded output is essential, enable shell pipefail or capture the test's PIPESTATUS immediately and emit one EXIT=<status> marker. Keep artifact inspection in a separate later command."
22711
+ : "",
22635
22712
  detail.failedProjectTestSummary
22636
22713
  ? `The latest current test run failed: ${detail.failedProjectTestSummary}`
22637
22714
  : "",