@lazyingart/agintiflow 0.20.212 → 0.20.213

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.
@@ -96,3 +96,37 @@ while retaining the artifact gate for real generated figures. Exact session
96
96
  evidence remains in
97
97
  `~/.agintiflow/sessions/aginti-qa-incident-metrics-001/events.jsonl` and the
98
98
  machine ledger records the run as `passed_after_fix`.
99
+
100
+ ### GitHub maintenance hidden acceptance and goal-scoped evidence
101
+
102
+ `github-safe-maintenance-012` started from a normal maintenance prompt rather
103
+ than a checker-shaped instruction. The agent repaired and verified the target,
104
+ but its first completion omitted the exact `docs/maintenance-handoff.md` file
105
+ required by independent acceptance. The same retained session inspected the
106
+ external failure, created the missing handoff, committed and pushed target
107
+ commit `36fa6c0`, and left `main` clean and synchronized. The hidden
108
+ `github_maintenance_contract.py` checker then passed.
109
+
110
+ Supervision of the repair exposed four runtime defects that could affect other
111
+ profiles:
112
+
113
+ - A test command wrapped as `command; echo "EXIT:$?"` could have shell exit zero
114
+ even when the real command failed. Explicit final `EXIT`, `STATUS`, or
115
+ `RESULT` probes are now parsed; a missing or nonzero marker is failing
116
+ evidence.
117
+ - A genuinely new continuation could inherit completed artifact, SCS,
118
+ project-verification, and repair state from the prior goal. New goals now
119
+ clear only goal-scoped execution evidence while preserving conversation,
120
+ durable goal history, and goal-keyed research memory.
121
+ - An acceptance sentence listing screenshots, PDFs, reports, or app launches
122
+ "as appropriate" could force irrelevant visual work. Optional evidence
123
+ examples no longer become mandatory categories.
124
+ - Merely naming a read-only checker such as `contract.py` could force a file
125
+ artifact. File evidence now requires both mutation intent and a workspace
126
+ file/source target; a virtual canvas artifact remains artifact evidence only.
127
+
128
+ The patched source resumed `aginti-github-maintenance-001`, invoked the hidden
129
+ checker exactly once, performed no file, canvas, commit, or push side effect,
130
+ and completed with a clean repository-state check. The full npm suite and the
131
+ focused dynamic-budget, SCS/model-role, and web-canvas regressions pass for
132
+ AgInTiFlow `0.20.213`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.212",
3
+ "version": "0.20.213",
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",
@@ -26,6 +26,7 @@ import {
26
26
  recordProjectVerificationOutcome,
27
27
  recordExactOutputProgress,
28
28
  recordStaticDiscoveryProgress,
29
+ resetGoalScopedRuntimeState,
29
30
  resetStaticDiscoveryAfterContextLoss,
30
31
  rememberCompletedDeepResearch,
31
32
  repeatedNoProgressToolBlock,
@@ -37,6 +38,7 @@ import {
37
38
  import {
38
39
  augmentScsTaskContractWithProjectVerification,
39
40
  buildScsEvidenceLedger,
41
+ deriveScsTaskContract,
40
42
  evaluateScsEvidence,
41
43
  } from "../src/scs-evidence.js";
42
44
  import {
@@ -72,6 +74,128 @@ function toolMessage(payload) {
72
74
 
73
75
  try {
74
76
  assert(normalizeDynamicStepsMode("off") === "off", "dynamic mode off did not normalize");
77
+ const failedExitProbeState = { meta: {} };
78
+ const failedExitProbeResult = {
79
+ toolName: "run_command",
80
+ ok: true,
81
+ exitCode: 0,
82
+ args: { command: 'python -m unittest discover -s tests; echo "EXIT:$?"' },
83
+ stdout: "external contract did not pass\nEXIT:1\n",
84
+ stderr: "",
85
+ };
86
+ recordProjectVerificationOutcome(failedExitProbeState, failedExitProbeResult, {
87
+ commandCwd: workspace,
88
+ taskProfile: "qa",
89
+ });
90
+ assert(
91
+ failedExitProbeResult.projectTest?.passed === false &&
92
+ failedExitProbeResult.projectTest?.explicitExitStatus === 1,
93
+ "a wrapped nonzero test status was recorded as passing"
94
+ );
95
+ const missingExitProbeState = { meta: {} };
96
+ const missingExitProbeResult = {
97
+ toolName: "run_command",
98
+ ok: true,
99
+ exitCode: 0,
100
+ args: { command: 'python -m unittest discover -s tests; printf "EXIT=%s\\n" "$?"' },
101
+ stdout: "external contract output ended before the status marker\n",
102
+ stderr: "",
103
+ };
104
+ recordProjectVerificationOutcome(missingExitProbeState, missingExitProbeResult, {
105
+ commandCwd: workspace,
106
+ taskProfile: "qa",
107
+ });
108
+ assert(
109
+ missingExitProbeResult.projectTest?.passed === false &&
110
+ missingExitProbeResult.projectTest?.explicitExitStatus === null,
111
+ "a missing wrapped test status was recorded as passing"
112
+ );
113
+ const passingExitProbeState = { meta: {} };
114
+ const passingExitProbeResult = {
115
+ toolName: "run_command",
116
+ ok: true,
117
+ exitCode: 0,
118
+ args: { command: 'python -m unittest discover -s tests; echo "EXIT=$?"' },
119
+ stdout: "contract checks passed\nEXIT=0\n",
120
+ stderr: "",
121
+ };
122
+ recordProjectVerificationOutcome(passingExitProbeState, passingExitProbeResult, {
123
+ commandCwd: workspace,
124
+ taskProfile: "qa",
125
+ });
126
+ assert(
127
+ passingExitProbeResult.projectTest?.passed === true &&
128
+ passingExitProbeResult.projectTest?.explicitExitStatus === 0,
129
+ "a wrapped zero test status was not accepted"
130
+ );
131
+
132
+ const newGoalState = {
133
+ meta: {
134
+ artifactProgress: { complete: true },
135
+ completionEvidenceRepair: { attempts: 1 },
136
+ dataProjectWorkflow: { ready: true },
137
+ durableEvidenceCategories: ["file", "visual"],
138
+ durableGitActions: ["commit"],
139
+ durableGitEvidence: [{ action: "commit", goalRevision: 1 }],
140
+ failedTestRecoveryPacket: { content: "old failure" },
141
+ goalContract: { revision: 2 },
142
+ projectVerification: { mutationRevision: 4 },
143
+ scs: { taskContract: { exactOutputPaths: ["old-output.md"] } },
144
+ completedDeepResearch: [{ goalKey: "retained-other-goal" }],
145
+ },
146
+ };
147
+ const removedGoalState = resetGoalScopedRuntimeState(newGoalState);
148
+ assert(removedGoalState.includes("artifactProgress"), "new goal did not clear stale artifact progress");
149
+ assert(!newGoalState.meta.projectVerification, "new goal retained stale project verification");
150
+ assert(!newGoalState.meta.scs, "new goal retained the previous SCS task contract");
151
+ assert(!newGoalState.meta.durableEvidenceCategories, "new goal inherited completed evidence categories");
152
+ assert(newGoalState.meta.goalContract?.revision === 2, "new goal reset its durable goal contract");
153
+ assert(newGoalState.meta.completedDeepResearch?.length === 1, "new goal discarded goal-keyed research cache");
154
+
155
+ const optionalVisualContract = deriveScsTaskContract({
156
+ goal: "Repair the repository, verify it, commit, and push the intentional work.",
157
+ taskProfile: "github",
158
+ acceptanceCriteria: [
159
+ "Do not rely only on chat summaries; verify files, commands, screenshots, PDFs, reports, or app launches as appropriate.",
160
+ ],
161
+ });
162
+ assert(
163
+ !optionalVisualContract.requiredEvidence.some((item) => item.category === "visual"),
164
+ "an optional evidence example forced irrelevant visual validation"
165
+ );
166
+ const explicitVisualContract = deriveScsTaskContract({
167
+ goal: "Capture and inspect a screenshot of the repaired interface.",
168
+ taskProfile: "website",
169
+ });
170
+ assert(
171
+ explicitVisualContract.requiredEvidence.some((item) => item.category === "visual"),
172
+ "an explicit screenshot request lost visual validation"
173
+ );
174
+ const readOnlyCheckerContract = deriveScsTaskContract({
175
+ goal:
176
+ "Re-run /tmp/acceptance/github_maintenance_contract.py once, verify the repository, and do not edit, commit, or push anything.",
177
+ taskProfile: "github",
178
+ });
179
+ assert(
180
+ !readOnlyCheckerContract.requiredEvidence.some((item) => item.category === "file"),
181
+ "a read-only checker path was mistaken for a requested file change"
182
+ );
183
+ const canvasOnlyContract = deriveScsTaskContract({
184
+ goal: "Create a canvas artifact preview for this smoke test.",
185
+ });
186
+ assert(
187
+ canvasOnlyContract.requiredEvidence.some((item) => item.category === "artifact") &&
188
+ !canvasOnlyContract.requiredEvidence.some((item) => item.category === "file"),
189
+ "a virtual canvas artifact was mistaken for a workspace-file mutation"
190
+ );
191
+ const sourceRepairContract = deriveScsTaskContract({
192
+ goal: "Fix src/runtime.py and verify the focused tests.",
193
+ taskProfile: "python",
194
+ });
195
+ assert(
196
+ sourceRepairContract.requiredEvidence.some((item) => item.category === "file"),
197
+ "an explicit source repair lost its file-change evidence gate"
198
+ );
75
199
  assert(
76
200
  completionEvidenceNeedsCommand({ missingProjectCommands: ["python analysis.py"] }),
77
201
  "a pending canonical command did not reopen command execution"
@@ -2423,6 +2423,28 @@ async function finishWithDirectAnswer({ config, state, store, observers, session
2423
2423
  };
2424
2424
  }
2425
2425
 
2426
+ export function resetGoalScopedRuntimeState(state = {}) {
2427
+ state.meta = state.meta || {};
2428
+ const keys = [
2429
+ "artifactProgress",
2430
+ "completionEvidenceRepair",
2431
+ "dataProjectWorkflow",
2432
+ "durableEvidenceCategories",
2433
+ "durableGitActions",
2434
+ "durableGitEvidence",
2435
+ "failedTestRecoveryPacket",
2436
+ "projectVerification",
2437
+ "scs",
2438
+ ];
2439
+ const removed = [];
2440
+ for (const key of keys) {
2441
+ if (!(key in state.meta)) continue;
2442
+ delete state.meta[key];
2443
+ removed.push(key);
2444
+ }
2445
+ return removed;
2446
+ }
2447
+
2426
2448
  async function applyContinuationPrompt(state, config, observers) {
2427
2449
  if (!config.resume || !config.goal) return null;
2428
2450
 
@@ -2439,6 +2461,9 @@ async function applyContinuationPrompt(state, config, observers) {
2439
2461
  state.meta = state.meta || {};
2440
2462
  const preserveTaskBoundary = preservesCurrentTaskBoundary(state, config.goal);
2441
2463
  const goalUpdate = updateGoalContract(state, config.goal, { preserveTaskBoundary });
2464
+ if (!preserveTaskBoundary) {
2465
+ resetGoalScopedRuntimeState(state);
2466
+ }
2442
2467
  if (
2443
2468
  preserveTaskBoundary &&
2444
2469
  continuationAddsConcreteRequirement(config.goal) &&
@@ -2961,6 +2986,19 @@ function commandReportsTestFailure(result = {}) {
2961
2986
  );
2962
2987
  }
2963
2988
 
2989
+ function explicitExitProbeStatus(command = "", result = {}) {
2990
+ const normalizedCommand = normalizeProjectCommand(command);
2991
+ const hasExitProbe = /(?:^|;)\s*(?:echo|printf)\b[^;&|]*(?:EXIT|STATUS|RESULT)[^;&|]*\$\?[^;&|]*$/i.test(
2992
+ normalizedCommand
2993
+ );
2994
+ if (!hasExitProbe) return { present: false, status: null };
2995
+
2996
+ const output = `${String(result.stdout || "")}\n${String(result.stderr || "")}`;
2997
+ const matches = [...output.matchAll(/(?:^|\n)\s*(?:EXIT|STATUS|RESULT)(?:_CODE)?\s*[:=]\s*(-?\d+)\s*(?=\n|$)/gim)];
2998
+ if (!matches.length) return { present: true, status: null };
2999
+ return { present: true, status: Number(matches.at(-1)[1]) };
3000
+ }
3001
+
2964
3002
  function actionableTestWarnings(result = {}) {
2965
3003
  const output = redactSensitiveText(`${String(result.stderr || "")}\n${String(result.stdout || "")}`);
2966
3004
  const warnings = [];
@@ -3133,17 +3171,24 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
3133
3171
 
3134
3172
  if (toolName === "run_command") {
3135
3173
  const command = normalizeProjectCommand(toolResult.args?.command || "");
3174
+ const exitProbe = explicitExitProbeStatus(command, toolResult);
3136
3175
  const run = {
3137
3176
  command,
3138
3177
  at: now,
3139
- ok: toolResult.ok !== false && Number(toolResult.exitCode ?? 0) === 0,
3178
+ ok:
3179
+ toolResult.ok !== false &&
3180
+ Number(toolResult.exitCode ?? 0) === 0 &&
3181
+ (!exitProbe.present || exitProbe.status === 0),
3140
3182
  mutationRevision: verification.mutationRevision,
3183
+ ...(exitProbe.present ? { explicitExitStatus: exitProbe.status } : {}),
3141
3184
  };
3142
3185
  verification.commandRuns = [...verification.commandRuns, run].slice(-40);
3143
3186
  toolResult.projectMutationRevision = verification.mutationRevision;
3144
3187
  if (isSubstantiveTestCommand(command)) {
3145
3188
  const zeroTests = commandReportsZeroTests(toolResult);
3146
- const reportedFailure = commandReportsTestFailure(toolResult);
3189
+ const reportedFailure =
3190
+ commandReportsTestFailure(toolResult) ||
3191
+ (exitProbe.present && exitProbe.status !== 0);
3147
3192
  const qualityWarnings = actionableTestWarnings(toolResult);
3148
3193
  const passed = run.ok && !zeroTests && !reportedFailure && qualityWarnings.length === 0;
3149
3194
  const failedEvidence = passed ? {} : compactFailedTestEvidence(toolResult, config);
@@ -487,6 +487,31 @@ function codeProfileRequiresCommand(goal = "") {
487
487
  return substantiveCodeWork && !simpleDocumentWrite;
488
488
  }
489
489
 
490
+ function goalRequestsWorkspaceMutation(goal = "") {
491
+ const text = normalizedText(goal);
492
+ return (
493
+ /\b(?:append|build|convert|copy|create|delete|edit|fix|generate|implement|modify|move|patch|refactor|remove|rename|repair|replace|rewrite|save|update|write)\b/.test(
494
+ text
495
+ ) ||
496
+ /创建|写入|编辑|修复|实现|修改|更新|生成|保存|复制|移动|转换|删除|重命名|替换|追加/.test(text)
497
+ );
498
+ }
499
+
500
+ function goalRequestsFileMutation(goal = "") {
501
+ const text = normalizedText(goal);
502
+ if (!goalRequestsWorkspaceMutation(text)) return false;
503
+ return (
504
+ /\b(?:code|codebase|document(?:ation)?|file|notes?|path|readme|repo(?:sitory)?|script|source|workspace)\b/.test(
505
+ text
506
+ ) ||
507
+ /(?:^|[\s`'"(])(?:\.{0,2}\/|\/)?[a-z0-9_.-]+(?:\/[a-z0-9_.{}-]+)+/i.test(text) ||
508
+ /\.(?:c|cc|cpp|cs|css|csv|go|h|hpp|html?|java|js|jsx|json|kt|md|mjs|php|py|rb|rs|sh|swift|tex|ts|tsx|txt|ya?ml)\b/i.test(
509
+ text
510
+ ) ||
511
+ /文件|文档|代码|代码库|仓库|脚本|源码|路径|工作区|说明书|笔记/.test(text)
512
+ );
513
+ }
514
+
490
515
  function profileRequirementsForGoal(taskProfile = "", goal = "") {
491
516
  const profile = String(taskProfile || "").toLowerCase();
492
517
  const defaults = PROFILE_REQUIREMENTS[profile] || [];
@@ -510,8 +535,14 @@ function profileRequirementsForGoal(taskProfile = "", goal = "") {
510
535
  "aaps",
511
536
  ]);
512
537
  if (!codeLikeProfiles.has(profile)) return defaults;
513
- if (codeProfileRequiresCommand(goal)) return defaults;
514
- return defaults.filter((category) => category !== "command");
538
+ let requirements = [...defaults];
539
+ if (!goalRequestsWorkspaceMutation(goal)) {
540
+ requirements = requirements.filter((category) => category !== "file");
541
+ }
542
+ if (!codeProfileRequiresCommand(goal)) {
543
+ requirements = requirements.filter((category) => category !== "command");
544
+ }
545
+ return requirements;
515
546
  }
516
547
 
517
548
  function isReadOnlyReadinessTask(goal = "") {
@@ -550,18 +581,16 @@ function inferRequirementCategories(goal = "", taskProfile = "", acceptanceCrite
550
581
  ""
551
582
  )
552
583
  .replace(/\bfigure\s+out\b/gi, "");
584
+ const mandatoryEvidenceText = artifactSignalText.replace(
585
+ /[^.\n]{0,240}\b(?:as appropriate|if appropriate|when useful|where applicable)\b/gi,
586
+ " "
587
+ );
553
588
  const profile = String(taskProfile || "").toLowerCase();
554
589
  const categories = new Set(
555
590
  goalRequiresEvidence(positiveGoal, "") ? profileRequirementsForGoal(taskProfile, positiveGoal) : []
556
591
  );
557
592
 
558
- if (
559
- textHas(
560
- text,
561
- /\b(file|path|workspace|edit|patch|fix|repair|refactor|convert|copy|move|remove|delete|source|script|code)\b|\.(?:md|txt|js|jsx|ts|tsx|mjs|cjs|json|ya?ml|py|tex|html|css|svg|csv)\b|\b(?:markdown|json|yaml|html|css|tex|latex)\s+file\b|\bfile\s+(?:as|in)\s+(?:markdown|json|yaml|html|css|tex|latex)\b/
562
- ) ||
563
- /文件|写入文件|编辑|修复|转换|复制|移动|删除|脚本|代码/.test(text)
564
- ) {
593
+ if (goalRequestsFileMutation(positiveGoal)) {
565
594
  categories.add("file");
566
595
  }
567
596
  const directCommandSignal =
@@ -577,13 +606,13 @@ function inferRequirementCategories(goal = "", taskProfile = "", acceptanceCrite
577
606
  if (directCommandSignal || (validationSignal && codeProfileRequiresCommand(positiveGoal))) {
578
607
  categories.add("command");
579
608
  }
580
- if (textHas(artifactSignalText, /\b(artifact|canvas|pdf|image|video|screenshot|cover|plot|chart|figure|docx|archive|copy to|export|generated|generate|draft)\b/) || /输出|产物|图片|视频|截图|封面|生成/.test(artifactSignalText)) {
609
+ if (textHas(mandatoryEvidenceText, /\b(artifact|canvas|pdf|image|video|screenshot|cover|plot|chart|figure|docx|archive|copy to|export|generated|generate|draft)\b/) || /输出|产物|图片|视频|截图|封面|生成/.test(mandatoryEvidenceText)) {
581
610
  categories.add("artifact");
582
611
  }
583
- if (textHas(text, /\b(browser|chrome|chromium|cdp|devtools|playwright|selenium|web[- ]?ui|website|page|tab|composer|click|type|upload|attach|submit|form)\b/) || /浏览器|网页|页面|上传|提交|附件|资产库/.test(text)) {
612
+ if (textHas(mandatoryEvidenceText, /\b(browser|chrome|chromium|cdp|devtools|playwright|selenium|web[- ]?ui|website|page|tab|composer|click|type|upload|attach|submit|form)\b/) || /浏览器|网页|页面|上传|提交|附件|资产库/.test(mandatoryEvidenceText)) {
584
613
  categories.add("browser");
585
614
  }
586
- if (textHas(text, /\b(screenshot|visible|visual|see|inspect image|open image|read_image|thumbnail)\b/) || /截图|可见|缩略图/.test(text)) {
615
+ if (textHas(mandatoryEvidenceText, /\b(screenshot|visible|visual|see|inspect image|open image|read_image|thumbnail)\b/) || /截图|可见|缩略图/.test(mandatoryEvidenceText)) {
587
616
  categories.add("visual");
588
617
  }
589
618
  if (