@lazyingart/agintiflow 0.20.324 → 0.20.325

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.324",
3
+ "version": "0.20.325",
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",
@@ -80,6 +80,7 @@ import {
80
80
  recordAlreadyCommittedRepositoryRepair,
81
81
  recordDurableEvidenceCategories,
82
82
  recordProjectVerificationOutcome,
83
+ recordFailedCommandAttempt,
83
84
  repositoryStateInspectionCommand,
84
85
  recordExactOutputProgress,
85
86
  recordStaticDiscoveryProgress,
@@ -94,6 +95,7 @@ import {
94
95
  forbiddenCurrentTestRerunBlock,
95
96
  unchangedFailedTestRerunBlock,
96
97
  repeatedNoProgressToolBlock,
98
+ runCommandResultHasDurableProgress,
97
99
  redundantCadValidationAliasPatchBlock,
98
100
  regressiveInversePatchBlock,
99
101
  repeatedSuccessfulMutationBlock,
@@ -11159,6 +11161,73 @@ try {
11159
11161
  )?.category === "repeated-no-progress-call",
11160
11162
  "dynamic failure output allowed an unchanged failing command to loop"
11161
11163
  );
11164
+ assertStrict.equal(
11165
+ runCommandResultHasDurableProgress({
11166
+ toolName: "run_command",
11167
+ ok: true,
11168
+ commandPolicy: classifyCommand(
11169
+ 'find /aginti-env -type f -name "pdflatex" | xargs ls -la | grep -v "not found"'
11170
+ ),
11171
+ }),
11172
+ false,
11173
+ "a bounded find/xargs/grep probe fabricated durable workspace progress"
11174
+ );
11175
+ assertStrict.equal(
11176
+ runCommandResultHasDurableProgress({
11177
+ toolName: "run_command",
11178
+ ok: true,
11179
+ commandPolicy: {
11180
+ category: "general-shell",
11181
+ writesWorkspace: true,
11182
+ },
11183
+ projectMutationPaths: ["src/example.js"],
11184
+ }),
11185
+ true,
11186
+ "an observed shell mutation did not count as durable workspace progress"
11187
+ );
11188
+ const compactedFailureLoop = {
11189
+ stagnationEpoch: 4,
11190
+ recent: [],
11191
+ };
11192
+ for (let attempt = 0; attempt < 2; attempt += 1) {
11193
+ recordFailedCommandAttempt(compactedFailureLoop, {
11194
+ signature: repeatedProbeState.meta.toolLoop.recent[0].signature,
11195
+ toolName: "run_command",
11196
+ ok: false,
11197
+ blocked: false,
11198
+ noProgressProbe: true,
11199
+ outcomeFingerprint: `failure-${attempt}`,
11200
+ stagnationEpoch: 4,
11201
+ goalRevision: 1,
11202
+ mutationRevision: 0,
11203
+ });
11204
+ }
11205
+ const compactedRepeatedFailureState = {
11206
+ meta: {
11207
+ toolLoop: compactedFailureLoop,
11208
+ },
11209
+ };
11210
+ assert(
11211
+ repeatedNoProgressToolBlock(
11212
+ compactedRepeatedFailureState,
11213
+ "run_command",
11214
+ repeatedProbeArgs,
11215
+ { commandCwd: workspace }
11216
+ )?.category === "repeated-no-progress-call",
11217
+ "bounded failed-command history was lost after recent tool history rolled off"
11218
+ );
11219
+ const compactedFailureAfterMutation = structuredClone(compactedRepeatedFailureState);
11220
+ compactedFailureAfterMutation.meta.toolLoop.stagnationEpoch = 5;
11221
+ assertStrict.equal(
11222
+ repeatedNoProgressToolBlock(
11223
+ compactedFailureAfterMutation,
11224
+ "run_command",
11225
+ repeatedProbeArgs,
11226
+ { commandCwd: workspace }
11227
+ ),
11228
+ null,
11229
+ "a verified later state change did not release retained failed-command history"
11230
+ );
11162
11231
  const newlyAuthoritativeVerificationState = structuredClone(repeatedFailureState);
11163
11232
  newlyAuthoritativeVerificationState.meta.projectVerification = {
11164
11233
  mutationRevision: 6,
@@ -10956,11 +10956,12 @@ function expectedRepeatedObservationCommand(command = "") {
10956
10956
  );
10957
10957
  }
10958
10958
 
10959
- function runCommandResultHasDurableProgress(toolResult = {}) {
10959
+ export function runCommandResultHasDurableProgress(toolResult = {}) {
10960
10960
  const policy = toolResult.commandPolicy || {};
10961
10961
  const policyAllowsMutation =
10962
- policy.mayMutateProject === true ||
10963
- (policy.mayMutateProject === undefined && policy.writesWorkspace === true);
10962
+ policy.semanticMayMutateProject !== false &&
10963
+ (policy.mayMutateProject === true ||
10964
+ (policy.mayMutateProject === undefined && policy.writesWorkspace === true));
10964
10965
  return Boolean(
10965
10966
  policyAllowsMutation ||
10966
10967
  policy.substantiveTest === true ||
@@ -10971,6 +10972,54 @@ function runCommandResultHasDurableProgress(toolResult = {}) {
10971
10972
  );
10972
10973
  }
10973
10974
 
10975
+ function failedCommandAttempt(toolLoop = {}, signature = "", stagnationEpoch = 0) {
10976
+ return (Array.isArray(toolLoop.failedCommandAttempts)
10977
+ ? toolLoop.failedCommandAttempts
10978
+ : []
10979
+ ).find(
10980
+ (entry) =>
10981
+ entry?.signature === signature &&
10982
+ Number(entry?.stagnationEpoch || 0) === Number(stagnationEpoch || 0)
10983
+ );
10984
+ }
10985
+
10986
+ export function recordFailedCommandAttempt(toolLoop = {}, entry = {}) {
10987
+ if (
10988
+ entry?.toolName !== "run_command" ||
10989
+ entry?.ok !== false ||
10990
+ entry?.blocked === true ||
10991
+ entry?.noProgressProbe !== true ||
10992
+ !entry?.signature
10993
+ ) {
10994
+ return null;
10995
+ }
10996
+ toolLoop.failedCommandAttempts = Array.isArray(toolLoop.failedCommandAttempts)
10997
+ ? toolLoop.failedCommandAttempts
10998
+ : [];
10999
+ const prior = failedCommandAttempt(
11000
+ toolLoop,
11001
+ entry.signature,
11002
+ entry.stagnationEpoch
11003
+ );
11004
+ if (prior) {
11005
+ prior.count = Math.max(0, Number(prior.count || 0)) + 1;
11006
+ prior.lastOutcomeFingerprint = String(entry.outcomeFingerprint || "");
11007
+ prior.lastAt = String(entry.at || new Date().toISOString());
11008
+ } else {
11009
+ toolLoop.failedCommandAttempts.push({
11010
+ signature: entry.signature,
11011
+ count: 1,
11012
+ stagnationEpoch: Math.max(0, Number(entry.stagnationEpoch || 0)),
11013
+ goalRevision: Math.max(0, Number(entry.goalRevision || 0)),
11014
+ mutationRevision: Math.max(0, Number(entry.mutationRevision || 0)),
11015
+ lastOutcomeFingerprint: String(entry.outcomeFingerprint || ""),
11016
+ lastAt: String(entry.at || new Date().toISOString()),
11017
+ });
11018
+ }
11019
+ toolLoop.failedCommandAttempts = toolLoop.failedCommandAttempts.slice(-40);
11020
+ return prior || toolLoop.failedCommandAttempts.at(-1);
11021
+ }
11022
+
10974
11023
  function isStaticDiscoveryToolResult(toolResult = {}) {
10975
11024
  if (isStaticDiscoveryToolCall(toolResult.toolName, toolResult.args || {})) return true;
10976
11025
  if (toolResult.toolName !== "run_command") return false;
@@ -11163,6 +11212,30 @@ export function repeatedNoProgressToolBlock(state, toolName, args = {}, config =
11163
11212
  Number(entry?.stagnationEpoch || 0) === stagnationEpoch &&
11164
11213
  Boolean(entry?.outcomeFingerprint)
11165
11214
  );
11215
+ const retainedFailure = failedCommandAttempt(
11216
+ toolLoop,
11217
+ signature,
11218
+ stagnationEpoch
11219
+ );
11220
+ if (Number(retainedFailure?.count || 0) >= 2) {
11221
+ return {
11222
+ reason:
11223
+ "The same command already failed twice without an intervening verified workspace, artifact, browser, or task-state change.",
11224
+ category: "repeated-no-progress-call",
11225
+ permissionAdvice: {
11226
+ category: "repeated-no-progress-call",
11227
+ autoRecover: true,
11228
+ summary: "This is a failed-command convergence guard, not a permission blocker.",
11229
+ instruction:
11230
+ "Do not rerun the command or a cosmetically equivalent form. Use the retained failure evidence, change the command or repair the implicated source, then run the smallest relevant validation.",
11231
+ options: [
11232
+ "Choose the correct compiler, interpreter, working directory, or command flags from the observed failure.",
11233
+ "Apply one bounded source repair that addresses the failure, then rerun validation.",
11234
+ "Finish with a concrete external blocker only when no enabled tool can make progress.",
11235
+ ],
11236
+ },
11237
+ };
11238
+ }
11166
11239
  if (matches.length < 2) return null;
11167
11240
  const repeatedFailures = matches.slice(-2).every((entry) => entry?.ok === false);
11168
11241
  if (repeatedFailures) {
@@ -19847,6 +19920,7 @@ async function applyToolLoopGuard(state, toolResult, store, observers, config =
19847
19920
  };
19848
19921
  state.meta.toolLoop.recent.push(entry);
19849
19922
  state.meta.toolLoop.recent = state.meta.toolLoop.recent.slice(-20);
19923
+ recordFailedCommandAttempt(state.meta.toolLoop, entry);
19850
19924
 
19851
19925
  const activeRefresh = activePatchContextRefresh(state);
19852
19926
  if (requiredPatchContextRefresh && !activeRefresh) {
@@ -137,6 +137,25 @@ function isReadOnlyDiffCommand(command = "") {
137
137
  );
138
138
  }
139
139
 
140
+ function isReadOnlyXargsListFilter(command = "") {
141
+ const normalized = stripBenignRedirections(command);
142
+ if (hasActiveShellExpansion(normalized)) return false;
143
+ const tokens = tokenizeShellWords(normalized);
144
+ if (!tokens.length || tokens[0] !== "xargs") return false;
145
+ let index = 1;
146
+ while (
147
+ ["-0", "--null", "-r", "--no-run-if-empty"].includes(tokens[index])
148
+ ) {
149
+ index += 1;
150
+ }
151
+ if (tokens[index] !== "ls") return false;
152
+ return tokens.slice(index + 1).every((token) =>
153
+ /^(?:-[A-Za-z0-9]+|--(?:all|almost-all|directory|inode|long|numeric-uid-gid|reverse|size|human-readable))$/.test(
154
+ token
155
+ )
156
+ );
157
+ }
158
+
140
159
  function isReadOnlyFindCommand(command = "") {
141
160
  const normalized = stripBenignRedirections(command);
142
161
  if (!/^find\s+/.test(normalized)) return false;
@@ -209,6 +228,16 @@ function isReadOnlyFindCommand(command = "") {
209
228
  return parenthesisDepth === 0 && maxDepth !== null;
210
229
  }
211
230
 
231
+ function isNonMutatingFindCommand(command = "") {
232
+ const normalized = stripBenignRedirections(command);
233
+ if (!/^find\s+/.test(normalized)) return false;
234
+ if (/(^|\s)(-delete|-exec|-execdir|-ok|-okdir|-fprint|-fprintf|-fls)\b/.test(normalized)) {
235
+ return false;
236
+ }
237
+ const unquoted = stripQuotedSegments(normalized);
238
+ return !/[|<>;&`$]/.test(unquoted) && !hasActiveShellExpansion(normalized);
239
+ }
240
+
212
241
  function isUnboundedRecursiveGrep(command = "") {
213
242
  const normalized = stripBenignRedirections(command);
214
243
  if (!/^grep\s+/.test(normalized)) return false;
@@ -1860,6 +1889,7 @@ function classifySimpleCommand(normalized) {
1860
1889
  isReadOnlyUniqFilter(commandForPatternMatching) ||
1861
1890
  isReadOnlyDigestCommand(commandForPatternMatching) ||
1862
1891
  isReadOnlyDiffCommand(commandForPatternMatching) ||
1892
+ isReadOnlyXargsListFilter(commandForPatternMatching) ||
1863
1893
  isReadOnlyFindCommand(normalized) ||
1864
1894
  (!hasActiveShellExpansion(benignRedirectCommand) && isReadOnlyShellCondition(benignRedirectCommand))
1865
1895
  ) {
@@ -1921,6 +1951,17 @@ function classifySimpleCommand(normalized) {
1921
1951
  return { category: "env-setup", needsNetwork: false, writesWorkspace: true };
1922
1952
  }
1923
1953
 
1954
+ if (isNonMutatingFindCommand(commandForPatternMatching)) {
1955
+ return {
1956
+ category: "general-shell",
1957
+ needsNetwork: false,
1958
+ writesWorkspace: true,
1959
+ semanticMayMutateProject: false,
1960
+ reason:
1961
+ "Unbounded find inspection remains under trusted shell policy but cannot mutate project state.",
1962
+ };
1963
+ }
1964
+
1924
1965
  return {
1925
1966
  category: "general-shell",
1926
1967
  needsNetwork: false,
@@ -2770,6 +2811,13 @@ function classifyPipelineSequence(normalized) {
2770
2811
  category: "general-shell",
2771
2812
  needsNetwork: classifications.some((classification) => classification.needsNetwork),
2772
2813
  writesWorkspace: classifications.some((classification) => classification.writesWorkspace),
2814
+ ...(classifications.every(
2815
+ (classification) =>
2816
+ classification.category === "read-only" ||
2817
+ classification.semanticMayMutateProject === false
2818
+ )
2819
+ ? { semanticMayMutateProject: false }
2820
+ : {}),
2773
2821
  reason: `Shell pipeline includes a broad segment and requires trusted shell policy: ${normalized}`,
2774
2822
  };
2775
2823
  }