@lazyingart/agintiflow 0.20.283 → 0.20.285

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.283",
3
+ "version": "0.20.285",
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",
@@ -180,6 +180,7 @@
180
180
  "smoke:localllm-model-tiers": "node scripts/smoke-localllm-model-tiers.js",
181
181
  "smoke:localllm-provider": "node scripts/smoke-localllm-provider.js",
182
182
  "smoke:progressive-tools": "node scripts/smoke-progressive-tool-selection.js",
183
+ "smoke:scoped-artifact-research": "node scripts/smoke-scoped-artifact-research.js",
183
184
  "smoke:truthful-completion": "node scripts/smoke-truthful-completion.js",
184
185
  "smoke:document-artifact-quality": "node scripts/smoke-document-artifact-quality.js",
185
186
  "smoke:writing-specialist-routing": "node scripts/smoke-writing-specialist-routing.js",
@@ -147,7 +147,7 @@ try {
147
147
  capabilities.tools?.taskProfiles?.some((profile) => profile.id === "pipeline"),
148
148
  "capabilities did not report pipeline task profile"
149
149
  );
150
- for (const profileId of ["docs", "data", "qa", "database", "devops", "security", "slides", "education", "java", "ios", "go", "rust", "dotnet", "php", "ruby"]) {
150
+ for (const profileId of ["docs", "data", "qa", "database", "devops", "security", "slides", "education", "java", "ios", "go", "rust", "dotnet", "php", "ruby", "cad"]) {
151
151
  assert(
152
152
  capabilities.tools?.taskProfiles?.some((profile) => profile.id === profileId),
153
153
  `capabilities did not report ${profileId} task profile`
@@ -157,6 +157,16 @@ try {
157
157
  assert(qaProfile, "QA profile is missing");
158
158
  assert(defaultMaxStepsForProfile("qa") >= 40, "QA profile step budget is too low for verification and cleanup");
159
159
  assert(defaultMaxStepsForProfile("pipeline") >= 44, "pipeline profile step budget is too low for repair/verify/resume loops");
160
+ const cadProfile = listTaskProfiles().find((profile) => profile.id === "cad");
161
+ assert(cadProfile, "CAD profile is missing");
162
+ assert(defaultMaxStepsForProfile("cad") >= 44, "CAD profile step budget is too low for build/render/validation repair loops");
163
+ assert(/one canonical machine-readable validation section/i.test(cadProfile.prompt), "CAD profile permits ambiguous validation aliases");
164
+ assert(/top-level `validation` object/i.test(cadProfile.prompt), "CAD profile does not establish one stable validation schema");
165
+ assert(/`solid_count`[\s\S]*`bbox_mm`[\s\S]*`watertight`[\s\S]*`object_count`/i.test(cadProfile.prompt), "CAD profile omits conventional format-native evidence field names");
166
+ assert(/enrich its missing direct evidence fields instead of renaming/i.test(cadProfile.prompt), "CAD profile permits same-failure schema oscillation");
167
+ assert(/STEP\/B-rep evidence needs solid count/i.test(cadProfile.prompt), "CAD profile does not require format-native STEP evidence");
168
+ assert(/STL evidence needs watertightness/i.test(cadProfile.prompt), "CAD profile does not require format-native STL evidence");
169
+ assert(/3MF evidence needs package validity/i.test(cadProfile.prompt), "CAD profile does not require format-native 3MF evidence");
160
170
  assert(!/misleading failing test/i.test(qaProfile.prompt), "QA profile still encourages misleading test fixtures");
161
171
  assert(/do not stage fake bugs/i.test(qaProfile.prompt), "QA profile does not discourage fake staged failures");
162
172
  const slidesProfile = listTaskProfiles().find((profile) => profile.id === "slides");
@@ -2,6 +2,7 @@
2
2
  import fs from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
+ import { spawnSync } from "node:child_process";
5
6
  import { fileURLToPath } from "node:url";
6
7
  import {
7
8
  buildModelTimeoutRetryMessages,
@@ -11,6 +12,7 @@ import {
11
12
  normalizeNoMatchQueryResult,
12
13
  applyModelTimeoutRetryRoute,
13
14
  recoverFocusedTextRewriteWithWritingSpecialist,
15
+ reconcileRuntimeRepositoryState,
14
16
  repairModelMessageHistory,
15
17
  shouldResetStaticDiscoveryPhase,
16
18
  runAgent,
@@ -99,7 +101,78 @@ async function runMock(goal, sessionId, { resume = false } = {}) {
99
101
  };
100
102
  }
101
103
 
104
+ async function verifyRunAgentRuntimeGitHygiene() {
105
+ const gitWorkspace = path.join(tempRoot, "runtime-git-workspace");
106
+ await fs.mkdir(gitWorkspace, { recursive: true });
107
+ const initialized = spawnSync("git", ["init"], {
108
+ cwd: gitWorkspace,
109
+ encoding: "utf8",
110
+ });
111
+ assert(initialized.status === 0, `runtime Git hygiene setup failed: ${initialized.stderr || initialized.stdout}`);
112
+ const config = resolveRuntimeConfig(
113
+ {
114
+ provider: "mock",
115
+ routingMode: "manual",
116
+ model: "mock-agent",
117
+ goal: "hello",
118
+ commandCwd: gitWorkspace,
119
+ maxSteps: 2,
120
+ },
121
+ {
122
+ baseDir: runtimeDir,
123
+ packageDir: repoRoot,
124
+ provider: "mock",
125
+ routingMode: "manual",
126
+ model: "mock-agent",
127
+ commandCwd: gitWorkspace,
128
+ allowShellTool: false,
129
+ allowFileTools: true,
130
+ sandboxMode: "host",
131
+ packageInstallPolicy: "block",
132
+ sessionId: "coding-runtime-git-hygiene",
133
+ }
134
+ );
135
+ await runAgent(config);
136
+ const localExclude = await fs.readFile(path.join(gitWorkspace, ".git", "info", "exclude"), "utf8");
137
+ assert(
138
+ localExclude.includes(".aginti/codebase-map.json") &&
139
+ localExclude.includes(".aginti/verification/"),
140
+ "runAgent did not protect runtime cache paths in its resolved commandCwd"
141
+ );
142
+ const gitignore = await fs.readFile(path.join(gitWorkspace, ".gitignore"), "utf8").catch(() => "");
143
+ assert(gitignore === "", "runAgent runtime setup edited the tracked .gitignore");
144
+ const staleRepositoryState = {
145
+ meta: {
146
+ projectVerification: {
147
+ mutationRevision: 4,
148
+ privateMutationRevision: 0,
149
+ testRuns: [{
150
+ command: "python3 acceptance.py",
151
+ mutationRevision: 4,
152
+ privateMutationRevision: 0,
153
+ passed: false,
154
+ failureSignature: "dirty-worktree",
155
+ failureSummary: "FAIL: Git worktree is not clean: ?? .aginti/",
156
+ }],
157
+ },
158
+ testFailureRepair: { key: "4:dirty-worktree" },
159
+ },
160
+ };
161
+ const reconciled = await reconcileRuntimeRepositoryState(
162
+ staleRepositoryState,
163
+ { commandCwd: gitWorkspace },
164
+ { changed: false }
165
+ );
166
+ assert(
167
+ reconciled?.clean === true &&
168
+ staleRepositoryState.meta.projectVerification.privateMutationRevision === 1 &&
169
+ !staleRepositoryState.meta.testFailureRepair,
170
+ "a retained cleanliness failure was not invalidated after the live worktree became clean"
171
+ );
172
+ }
173
+
102
174
  try {
175
+ await verifyRunAgentRuntimeGitHygiene();
103
176
  const externalValidatorPath = path.join(
104
177
  tempRoot,
105
178
  "private-acceptance",
@@ -145,6 +218,38 @@ try {
145
218
  `external validator source inspection escaped the opaque contract: ${inspectionCommand}`
146
219
  );
147
220
  }
221
+ const combinedValidatorCommand =
222
+ `python3 build_artifact.py; ${externalValidatorCommand}`;
223
+ const combinedValidatorDecision = evaluateCommandPolicy(
224
+ combinedValidatorCommand,
225
+ opaqueValidatorPolicy
226
+ );
227
+ assert(
228
+ combinedValidatorDecision.allowed === false &&
229
+ combinedValidatorDecision.category === "opaque-external-validator-inspection" &&
230
+ combinedValidatorDecision.recoverable === true,
231
+ "a combined producer and external validator command escaped the opaque validator contract"
232
+ );
233
+ const combinedValidatorAdvice = buildPermissionAdvice({
234
+ toolName: "run_command",
235
+ args: { command: combinedValidatorCommand },
236
+ guard: combinedValidatorDecision,
237
+ config: opaqueValidatorPolicy,
238
+ state: { sessionId: "opaque-validator-command-shape-smoke" },
239
+ });
240
+ assert(
241
+ combinedValidatorAdvice.autoRecover === true &&
242
+ !combinedValidatorAdvice.suggestedCommand &&
243
+ /separately|standalone/i.test(combinedValidatorAdvice.instruction) &&
244
+ /exact declared external validator command unchanged/i.test(
245
+ combinedValidatorAdvice.instruction
246
+ ) &&
247
+ !shouldPauseForPermissionAdvice({
248
+ blocked: true,
249
+ permissionAdvice: combinedValidatorAdvice,
250
+ }),
251
+ "a recoverable external-validator command-shape error became a permission pause"
252
+ );
148
253
 
149
254
  const genericArtifactBlock = await genericArtifactFilenameBlock(
150
255
  "write_file",
@@ -598,6 +703,16 @@ try {
598
703
  );
599
704
  assert(guidance.includes("Surgical editing contract:"), "engineering guidance did not include surgical editing contract");
600
705
  assert(guidance.includes("Evidence-card template:"), "engineering guidance did not include evidence-card template");
706
+ const cadGuidance = engineeringGuidanceForTask(
707
+ "Build a centered parametric CAD cradle and export STEP, STL, 3MF, and a render for 3D printing.",
708
+ "auto"
709
+ );
710
+ assert(cadGuidance.includes("CAD/fabrication:"), "auto guidance did not recognize a CAD fabrication task");
711
+ assert(cadGuidance.includes("one canonical validation section"), "auto CAD guidance permits ambiguous validation aliases");
712
+ assert(
713
+ recommendedMaxStepsForTask({ goal: "Build a CAD holder with STEP STL and 3MF validation.", taskProfile: "auto" }) >= 44,
714
+ "auto CAD task did not receive a complete build/render/validation budget"
715
+ );
601
716
  assert(
602
717
  shouldUseSurgicalContextForTask({
603
718
  goal: "fix this large repository bug by tracing callers",
@@ -2076,6 +2191,10 @@ try {
2076
2191
  assert(largeModelRead.contentTruncated, "large model-facing read was not bounded");
2077
2192
  assert(largeModelRead.content.length <= 12100, "large model-facing read exceeded the context cap");
2078
2193
  assert(largeModelRead.nextStartLine > 1, "large model-facing read omitted its continuation line");
2194
+ assert(
2195
+ largeModelRead.continuationHint.includes(`startLine=${largeModelRead.nextStartLine}`),
2196
+ "large model-facing read described a continuation argument that the read_file schema does not accept"
2197
+ );
2079
2198
  const largeModelList = toolResultForModel({
2080
2199
  ok: true,
2081
2200
  toolName: "list_files",
@@ -2991,6 +3110,7 @@ try {
2991
3110
  workspace,
2992
3111
  checks: [
2993
3112
  "deepseek_history_repair",
3113
+ "run_agent_runtime_git_hygiene",
2994
3114
  "interleaved_tool_history_repair",
2995
3115
  "blocked_tool_batch_short_circuit",
2996
3116
  "deepseek_pro_patch_route",
@@ -339,6 +339,53 @@ assert.ok(!runtimeText.includes("OLD-COMPACTION-MUST-NOT-RECUR"));
339
339
  assert.match(runtimeText, /Do not reread a listed source solely because compaction occurred/);
340
340
  assert.match(runtimeText, /never restart a full-file read loop after compaction/);
341
341
 
342
+ const latestSemanticCorrection = [
343
+ "Continue the current task from the preserved state.",
344
+ "LATEST-CORRECTION-AUTHORITY replaces the stale interpretation with a detailed current behavior contract that deliberately contains no exact file path or shell command.",
345
+ "Preserve verified progress, reconcile the remaining implementation against this correction, avoid repeating rejected states, and verify the current result before finishing.",
346
+ ].join(" ");
347
+ const staleGoalCompactionState = {
348
+ goal: "STALE-ACTIVE-GOAL continue the older interpretation.",
349
+ plan: "Use current evidence and finish the retained task.",
350
+ messages: [
351
+ { role: "system", content: "Preserve the latest substantive same-task correction." },
352
+ { role: "user", content: `Continue the current task from saved state: ${latestSemanticCorrection}` },
353
+ ],
354
+ meta: {
355
+ taskProfile: "auto",
356
+ goalContract: {
357
+ version: 3,
358
+ revision: 9,
359
+ activeGoalRevision: 8,
360
+ taskGoal: "Complete the retained task.",
361
+ activeGoal: "STALE-ACTIVE-GOAL continue the older interpretation.",
362
+ currentRequest: latestSemanticCorrection,
363
+ history: [{ revision: 9, refreshExecutionContract: false }],
364
+ },
365
+ },
366
+ };
367
+ const staleGoalCompactionMessages = buildContextBudgetCompactionMessages(
368
+ staleGoalCompactionState,
369
+ config,
370
+ { title: "", url: "" },
371
+ 17,
372
+ { reason: "recover the latest semantic correction from an older saved session" }
373
+ );
374
+ const staleGoalCompactionText = staleGoalCompactionMessages
375
+ .map((message) => message.content || "")
376
+ .join("\n");
377
+ const authoritativeGoalSection = staleGoalCompactionText
378
+ .split("Authoritative current goal:")[1]
379
+ ?.split("Current plan:")[0] || "";
380
+ assert.ok(
381
+ authoritativeGoalSection.includes("LATEST-CORRECTION-AUTHORITY"),
382
+ "compaction kept an older active goal above a substantive latest correction"
383
+ );
384
+ assert.ok(
385
+ !authoritativeGoalSection.includes("STALE-ACTIVE-GOAL"),
386
+ "compaction still labeled the stale active goal as authoritative"
387
+ );
388
+
342
389
  const deepSeekRuntimeMessages = buildContextBudgetCompactionMessages(
343
390
  compactionState,
344
391
  { ...config, provider: "deepseek", model: "deepseek-chat" },
@@ -728,4 +775,85 @@ assert.ok(
728
775
  "cumulative DeepSeek compaction exceeded the bounded retry target"
729
776
  );
730
777
 
778
+ const instructionReadPair = [
779
+ {
780
+ role: "assistant",
781
+ content: "",
782
+ reasoning_content: "Read the exact project instructions before editing.",
783
+ tool_calls: [
784
+ {
785
+ id: "read-project-instructions",
786
+ type: "function",
787
+ function: { name: "read_file", arguments: '{"path":"AGENTS.md"}' },
788
+ },
789
+ ],
790
+ },
791
+ {
792
+ role: "tool",
793
+ tool_call_id: "read-project-instructions",
794
+ content: JSON.stringify({
795
+ ok: true,
796
+ toolName: "read_file",
797
+ path: "AGENTS.md",
798
+ sha256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
799
+ content: "PROJECT-INSTRUCTION-MARKER preserve inputs and never inspect the external validator source.",
800
+ }),
801
+ },
802
+ ];
803
+ const instructionCompactionState = {
804
+ ...compactionState,
805
+ messages: [
806
+ { role: "system", content: "system" },
807
+ { role: "user", content: "Repair the canonical CAD producer." },
808
+ ...instructionReadPair,
809
+ { role: "user", content: "Continue the current task from saved state: repair after validation." },
810
+ ...Array.from({ length: 18 }, (_, index) => noisyFullReadPair(index + 1, 7)).flat(),
811
+ ],
812
+ };
813
+ const instructionCompacted = buildContextBudgetCompactionMessages(
814
+ instructionCompactionState,
815
+ { ...config, provider: "deepseek", model: "deepseek-chat" },
816
+ { title: "", url: "" },
817
+ 22,
818
+ { reason: "preserve project instructions across a continuation boundary" }
819
+ );
820
+ const instructionCompactedText = instructionCompacted
821
+ .map((message) => message.content || "")
822
+ .join("\n");
823
+ assert.ok(
824
+ instructionCompactedText.includes("PROJECT-INSTRUCTION-MARKER"),
825
+ "compaction lost a project instruction read that preceded the latest continuation boundary"
826
+ );
827
+ const instructionCompactedAgain = buildContextBudgetCompactionMessages(
828
+ {
829
+ ...instructionCompactionState,
830
+ messages: [
831
+ ...instructionCompacted,
832
+ { role: "user", content: "Continue the current task from saved state: apply the bounded repair." },
833
+ ...Array.from({ length: 18 }, (_, index) => noisyFullReadPair(index + 1, 8)).flat(),
834
+ ],
835
+ },
836
+ { ...config, provider: "deepseek", model: "deepseek-chat" },
837
+ { title: "", url: "" },
838
+ 26,
839
+ { reason: "preserve project instructions through repeated compaction" }
840
+ );
841
+ const instructionCompactedAgainText = instructionCompactedAgain
842
+ .map((message) => message.content || "")
843
+ .join("\n");
844
+ assert.ok(
845
+ instructionCompactedAgainText.includes("PROJECT-INSTRUCTION-MARKER"),
846
+ "repeated compaction dropped durable project instruction evidence"
847
+ );
848
+ assert.equal(
849
+ instructionCompactedAgain.filter(
850
+ (message) =>
851
+ message.role === "user" &&
852
+ /Tool:\s*read_file/.test(message.content || "") &&
853
+ /Arguments:\s*\{\"path\":\"AGENTS\.md\"\}/.test(message.content || "")
854
+ ).length,
855
+ 1,
856
+ "repeated compaction duplicated the durable AGENTS.md tool record"
857
+ );
858
+
731
859
  console.log("context budget recovery smoke passed");
@@ -343,6 +343,101 @@ async function main() {
343
343
  !Object.hasOwn(deepSeekActionPayload || {}, "reasoning_effort"),
344
344
  "DeepSeek action-only recovery sent a conflicting reasoning effort"
345
345
  );
346
+ let deepSeekRepairRethinkPayload = null;
347
+ await requestNextStep(
348
+ {
349
+ chat: {
350
+ completions: {
351
+ create: async (payload) => {
352
+ deepSeekRepairRethinkPayload = payload;
353
+ return {
354
+ choices: [{
355
+ message: {
356
+ role: "assistant",
357
+ reasoning_content: "The unchanged proposal must be revised from the retained failure evidence.",
358
+ content: "",
359
+ tool_calls: [{
360
+ id: "deepseek-rethink-patch",
361
+ type: "function",
362
+ function: {
363
+ name: "apply_patch",
364
+ arguments: JSON.stringify({
365
+ path: "service.py",
366
+ search: "old",
367
+ replace: "new",
368
+ expectedReplacements: 1,
369
+ }),
370
+ },
371
+ }],
372
+ },
373
+ }],
374
+ };
375
+ },
376
+ },
377
+ },
378
+ },
379
+ {
380
+ provider: "deepseek",
381
+ model: "deepseek-v4-pro",
382
+ reasoning: "xhigh",
383
+ goal: "Repair service.py from exact retained source.",
384
+ taskProfile: "code",
385
+ allowFileTools: true,
386
+ allowShellTool: false,
387
+ allowWebSearch: false,
388
+ allowMcpTools: false,
389
+ allowWrapperTools: false,
390
+ allowAuxiliaryTools: false,
391
+ completionFreshMutationRequired: true,
392
+ completionFreshMutationNeedsSourceRead: false,
393
+ completionFreshMutationPaths: ["service.py"],
394
+ },
395
+ [
396
+ { role: "user", content: "Repair the retained source from the exact failure evidence." },
397
+ {
398
+ role: "assistant",
399
+ content: "",
400
+ tool_calls: [{
401
+ id: "deepseek-rejected-patch",
402
+ type: "function",
403
+ function: {
404
+ name: "apply_patch",
405
+ arguments: JSON.stringify({
406
+ path: "service.py",
407
+ search: "old",
408
+ replace: "old",
409
+ expectedReplacements: 1,
410
+ }),
411
+ },
412
+ }],
413
+ },
414
+ {
415
+ role: "tool",
416
+ tool_call_id: "deepseek-rejected-patch",
417
+ content: JSON.stringify({
418
+ ok: false,
419
+ blocked: true,
420
+ recoverable: true,
421
+ toolName: "apply_patch",
422
+ category: "failed-test-nonrepairing-patch",
423
+ reason: "The replacement leaves the current actionable line unchanged.",
424
+ }),
425
+ },
426
+ { role: "user", content: "Continue from the exact tool result." },
427
+ ]
428
+ );
429
+ assert(
430
+ deepSeekRepairRethinkPayload?.thinking?.type === "enabled",
431
+ "DeepSeek did not restore thinking after a deterministic non-repairing patch rejection"
432
+ );
433
+ assert(
434
+ !Object.hasOwn(deepSeekRepairRethinkPayload || {}, "tool_choice"),
435
+ "DeepSeek repair rethink sent tool_choice even though V4 thinking tool calls reject it"
436
+ );
437
+ assert(
438
+ deepSeekRepairRethinkPayload?.tools?.some((tool) => tool?.function?.name === "apply_patch"),
439
+ "DeepSeek repair rethink dropped the constrained mutation tool"
440
+ );
346
441
  let deepSeekRepairReadPayload = null;
347
442
  await requestNextStep(
348
443
  {