@lazyingart/agintiflow 0.20.253 → 0.20.254

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.253",
3
+ "version": "0.20.254",
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",
@@ -204,6 +204,7 @@ const digest = (value) => crypto.createHash("sha256").update(value).digest("hex"
204
204
  const durableMutation = {
205
205
  revision: 4,
206
206
  goalRevision: 7,
207
+ taskHash: digest("Repair the sensor gateway lifecycle."),
207
208
  toolName: "apply_patch",
208
209
  paths: ["service_ctl.py"],
209
210
  patch: {
@@ -214,7 +215,17 @@ const durableMutation = {
214
215
  };
215
216
  const durablePatchState = {
216
217
  meta: {
217
- goalContract: { revision: 7 },
218
+ goalContract: {
219
+ revision: 7,
220
+ taskGoal: "Repair the sensor gateway lifecycle.",
221
+ history: [
222
+ {
223
+ revision: 7,
224
+ kind: "initial",
225
+ hash: digest("Repair the sensor gateway lifecycle."),
226
+ },
227
+ ],
228
+ },
218
229
  toolLoop: {
219
230
  recent: Array.from({ length: 20 }, (_, index) =>
220
231
  failed("apply_patch", `later-failure-${index}`)
@@ -240,7 +251,47 @@ assert.equal(
240
251
  ...durablePatchState,
241
252
  meta: {
242
253
  ...durablePatchState.meta,
243
- goalContract: { revision: 8 },
254
+ goalContract: {
255
+ revision: 8,
256
+ taskGoal: "Repair the sensor gateway lifecycle.",
257
+ history: [
258
+ ...durablePatchState.meta.goalContract.history,
259
+ {
260
+ revision: 8,
261
+ kind: "same-task-continuation",
262
+ relation: "same-task",
263
+ taskHash: digest("Repair the sensor gateway lifecycle."),
264
+ },
265
+ ],
266
+ },
267
+ },
268
+ },
269
+ "apply_patch",
270
+ durablePatchArgs,
271
+ { commandCwd: process.cwd() }
272
+ )?.category,
273
+ "repeated-successful-mutation",
274
+ "a same-task continuation must not forget an earlier successful mutation"
275
+ );
276
+ assert.equal(
277
+ repeatedSuccessfulMutationBlock(
278
+ {
279
+ ...durablePatchState,
280
+ meta: {
281
+ ...durablePatchState.meta,
282
+ goalContract: {
283
+ revision: 8,
284
+ taskGoal: "Implement a separate service migration.",
285
+ history: [
286
+ ...durablePatchState.meta.goalContract.history,
287
+ {
288
+ revision: 8,
289
+ kind: "continuation",
290
+ relation: "new-request",
291
+ taskHash: digest("Implement a separate service migration."),
292
+ },
293
+ ],
294
+ },
244
295
  },
245
296
  },
246
297
  "apply_patch",
@@ -248,7 +299,24 @@ assert.equal(
248
299
  { commandCwd: process.cwd() }
249
300
  ),
250
301
  null,
251
- "a genuine user continuation must permit deliberate reconsideration of an earlier patch"
302
+ "a genuine new task boundary must not inherit an unrelated mutation block"
303
+ );
304
+ const sanitizedDurablePatchArgs = {
305
+ ...durablePatchArgs,
306
+ search: durablePatchArgs.search.slice(0, 20),
307
+ replace: durablePatchArgs.replace.slice(0, 20),
308
+ searchHash: digest(durablePatchArgs.search),
309
+ replaceHash: digest(durablePatchArgs.replace),
310
+ };
311
+ assert.equal(
312
+ repeatedSuccessfulMutationBlock(
313
+ durablePatchState,
314
+ "apply_patch",
315
+ sanitizedDurablePatchArgs,
316
+ { commandCwd: process.cwd() }
317
+ )?.category,
318
+ "repeated-successful-mutation",
319
+ "redacted patch previews must use their retained full-content hashes for idempotency"
252
320
  );
253
321
  assert.equal(
254
322
  repeatedSuccessfulMutationBlock(
@@ -4397,9 +4397,13 @@ function clearRequiredCommandBatch(verification = {}) {
4397
4397
  }
4398
4398
 
4399
4399
  function appendProjectMutation(state = {}, verification = {}, mutation = {}, config = {}) {
4400
+ const taskGoal = String(
4401
+ state.meta?.goalContract?.taskGoal || state.goal || ""
4402
+ ).trim();
4400
4403
  const record = {
4401
4404
  ...mutation,
4402
4405
  goalRevision: Math.max(0, Number(state.meta?.goalContract?.revision || 0)),
4406
+ taskHash: taskGoal ? hashForLog(taskGoal) : "",
4403
4407
  revision: Math.max(0, Number(mutation.revision || verification.mutationRevision || 0)),
4404
4408
  paths: Array.isArray(mutation.paths)
4405
4409
  ? [...new Set(mutation.paths.map((item) => String(item || "")).filter(Boolean))].slice(0, 24)
@@ -6244,16 +6248,50 @@ export function repeatedSuccessfulMutationBlock(state, toolName, args = {}, conf
6244
6248
  ? [verification.lastMutation]
6245
6249
  : [];
6246
6250
  const targetPath = typeof args.path === "string" ? safeRecoveryEvidencePath(args.path) : "";
6247
- const searchHash = typeof args.search === "string" ? hashForLog(args.search) : "";
6248
- const replaceHash = typeof args.replace === "string" ? hashForLog(args.replace) : "";
6249
- const currentGoalRevision = Math.max(0, Number(state.meta?.goalContract?.revision || 0));
6251
+ const searchHash = String(
6252
+ args.searchHash || (typeof args.search === "string" ? hashForLog(args.search) : "")
6253
+ );
6254
+ const replaceHash = String(
6255
+ args.replaceHash || (typeof args.replace === "string" ? hashForLog(args.replace) : "")
6256
+ );
6257
+ const goalContract = state.meta?.goalContract || {};
6258
+ const currentGoalRevision = Math.max(0, Number(goalContract.revision || 0));
6259
+ const currentTaskGoal = String(goalContract.taskGoal || state.goal || "").trim();
6260
+ const currentTaskHash = currentTaskGoal ? hashForLog(currentTaskGoal) : "";
6261
+ const goalHistory = Array.isArray(goalContract.history) ? goalContract.history : [];
6262
+ const mutationBelongsToCurrentTask = (mutation = {}) => {
6263
+ const mutationGoalRevision = Math.max(0, Number(mutation.goalRevision || 0));
6264
+ const goalEntry = goalHistory.find(
6265
+ (entry) => Number(entry?.revision || 0) === mutationGoalRevision
6266
+ );
6267
+ const mutationTaskHash = String(
6268
+ mutation.taskHash ||
6269
+ goalEntry?.taskHash ||
6270
+ (goalEntry?.kind === "initial" ? goalEntry?.hash : "") ||
6271
+ ""
6272
+ );
6273
+ if (currentTaskHash && mutationTaskHash) return currentTaskHash === mutationTaskHash;
6274
+ if (mutationGoalRevision === currentGoalRevision) return true;
6275
+ const interveningGoalEntries = goalHistory.filter((entry) => {
6276
+ const revision = Number(entry?.revision || 0);
6277
+ return revision > mutationGoalRevision && revision <= currentGoalRevision;
6278
+ });
6279
+ return (
6280
+ interveningGoalEntries.length > 0 &&
6281
+ interveningGoalEntries.every(
6282
+ (entry) =>
6283
+ String(entry?.relation || "") === "same-task" ||
6284
+ String(entry?.kind || "") === "same-task-continuation"
6285
+ )
6286
+ );
6287
+ };
6250
6288
  let matchingHistoryIndex = -1;
6251
6289
  if (targetPath && searchHash && replaceHash) {
6252
6290
  for (let index = history.length - 1; index >= 0; index -= 1) {
6253
6291
  const mutation = history[index];
6254
6292
  if (
6255
6293
  mutation?.toolName === "apply_patch" &&
6256
- Number(mutation?.goalRevision || 0) === currentGoalRevision &&
6294
+ mutationBelongsToCurrentTask(mutation) &&
6257
6295
  safeRecoveryEvidencePath(mutation?.patch?.path) === targetPath &&
6258
6296
  String(mutation?.patch?.searchHash || "") === searchHash &&
6259
6297
  String(mutation?.patch?.replaceHash || "") === replaceHash
@@ -6267,7 +6305,7 @@ export function repeatedSuccessfulMutationBlock(state, toolName, args = {}, conf
6267
6305
  matchingHistoryIndex >= 0 &&
6268
6306
  !history.slice(matchingHistoryIndex + 1).some(
6269
6307
  (mutation) =>
6270
- Number(mutation?.goalRevision || 0) === currentGoalRevision &&
6308
+ mutationBelongsToCurrentTask(mutation) &&
6271
6309
  Number(mutation?.revision || 0) >
6272
6310
  Number(history[matchingHistoryIndex]?.revision || 0)
6273
6311
  );
@@ -6275,7 +6313,7 @@ export function repeatedSuccessfulMutationBlock(state, toolName, args = {}, conf
6275
6313
  if (!alreadyApplied) return null;
6276
6314
  return {
6277
6315
  reason:
6278
- "This exact patch already succeeded without an intervening successful mutation or user continuation.",
6316
+ "This exact patch already succeeded in the current task lineage without a later successful mutation that changed source state.",
6279
6317
  category: "repeated-successful-mutation",
6280
6318
  permissionAdvice: {
6281
6319
  category: "repeated-successful-mutation",