@lazyingart/agintiflow 0.20.280 → 0.20.282
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 +1 -1
- package/scripts/smoke-dynamic-step-budget.js +185 -0
- package/scripts/smoke-scs-evidence-visibility.js +68 -0
- package/scripts/smoke-web-api.js +12 -1
- package/src/agent-runner.js +77 -2
- package/src/command-policy.js +8 -1
- package/src/scs-controller.js +25 -1
- package/src/scs-evidence.js +323 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.282",
|
|
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",
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
isSubstantiveTestCommand,
|
|
54
54
|
mergeDurableGitEvidence,
|
|
55
55
|
nextStepRuntimeConfig,
|
|
56
|
+
patchContextRefreshDecision,
|
|
56
57
|
patchContextScopeMismatchAttemptCount,
|
|
57
58
|
parseGitPorcelainStatus,
|
|
58
59
|
pythonMainGuardOrderDefects,
|
|
@@ -1216,6 +1217,100 @@ try {
|
|
|
1216
1217
|
Boolean(unrelatedInsertionBinding?.scopeIssue),
|
|
1217
1218
|
"an unrelated missing declaration escaped the active failed-test symbol contract"
|
|
1218
1219
|
);
|
|
1220
|
+
const declarationBoundarySource = [
|
|
1221
|
+
"def build_preview():",
|
|
1222
|
+
" path = 'preview.png'",
|
|
1223
|
+
" return path",
|
|
1224
|
+
"",
|
|
1225
|
+
"def main():",
|
|
1226
|
+
" return build_preview()",
|
|
1227
|
+
"",
|
|
1228
|
+
].join("\n");
|
|
1229
|
+
const declarationBoundaryOffset = declarationBoundarySource.indexOf("def main():");
|
|
1230
|
+
const declarationBoundaryAnchor = declarationBoundarySource.slice(
|
|
1231
|
+
0,
|
|
1232
|
+
declarationBoundaryOffset
|
|
1233
|
+
);
|
|
1234
|
+
const declarationBoundaryState = {
|
|
1235
|
+
meta: {
|
|
1236
|
+
goalContract: { revision: 1 },
|
|
1237
|
+
projectVerification: { mutationRevision: 1, privateMutationRevision: 0 },
|
|
1238
|
+
toolLoop: {
|
|
1239
|
+
patchContextRepair: {
|
|
1240
|
+
version: 1,
|
|
1241
|
+
path: "preview_builder.py",
|
|
1242
|
+
goalRevision: 1,
|
|
1243
|
+
mutationRevision: 1,
|
|
1244
|
+
privateMutationRevision: 0,
|
|
1245
|
+
search: declarationBoundaryAnchor,
|
|
1246
|
+
searchHash: crypto
|
|
1247
|
+
.createHash("sha256")
|
|
1248
|
+
.update(declarationBoundaryAnchor)
|
|
1249
|
+
.digest("hex"),
|
|
1250
|
+
sourceHash: crypto
|
|
1251
|
+
.createHash("sha256")
|
|
1252
|
+
.update(declarationBoundarySource)
|
|
1253
|
+
.digest("hex"),
|
|
1254
|
+
anchorKind: "declaration-identity",
|
|
1255
|
+
anchorIdentity: "build_preview",
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
},
|
|
1259
|
+
};
|
|
1260
|
+
const declarationBoundaryBinding = bindPatchContextRepairArguments(
|
|
1261
|
+
declarationBoundaryState,
|
|
1262
|
+
{
|
|
1263
|
+
replace: [
|
|
1264
|
+
"def build_preview():",
|
|
1265
|
+
" path = 'preview-fixed.png'",
|
|
1266
|
+
" return path",
|
|
1267
|
+
].join("\n"),
|
|
1268
|
+
}
|
|
1269
|
+
);
|
|
1270
|
+
assert(
|
|
1271
|
+
declarationBoundaryBinding?.boundaryWhitespacePreserved === true &&
|
|
1272
|
+
declarationBoundaryBinding.args.replace.endsWith("\n") &&
|
|
1273
|
+
/return path\n+def main\(\):/.test(
|
|
1274
|
+
`${declarationBoundaryBinding.args.replace}${declarationBoundarySource.slice(declarationBoundaryOffset)}`
|
|
1275
|
+
),
|
|
1276
|
+
"revision-bound declaration replacement did not preserve its Python declaration boundary"
|
|
1277
|
+
);
|
|
1278
|
+
await fs.writeFile(
|
|
1279
|
+
path.join(workspace, "preview_builder.py"),
|
|
1280
|
+
declarationBoundarySource,
|
|
1281
|
+
"utf8"
|
|
1282
|
+
);
|
|
1283
|
+
assert(
|
|
1284
|
+
(await prospectivePythonExactPatchSyntaxBlock(
|
|
1285
|
+
"apply_patch",
|
|
1286
|
+
declarationBoundaryBinding.args,
|
|
1287
|
+
{ commandCwd: workspace }
|
|
1288
|
+
)) === null,
|
|
1289
|
+
"the boundary-preserved declaration replacement still produced invalid Python"
|
|
1290
|
+
);
|
|
1291
|
+
const syntaxRefreshDecision = patchContextRefreshDecision(
|
|
1292
|
+
{
|
|
1293
|
+
meta: {
|
|
1294
|
+
goalContract: { revision: 1 },
|
|
1295
|
+
projectVerification: { mutationRevision: 1, privateMutationRevision: 0 },
|
|
1296
|
+
toolLoop: { stagnationEpoch: 0, recent: [] },
|
|
1297
|
+
},
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
toolName: "apply_patch",
|
|
1301
|
+
ok: false,
|
|
1302
|
+
category: "python-syntax-regression",
|
|
1303
|
+
args: {
|
|
1304
|
+
path: "preview_builder.py",
|
|
1305
|
+
search: declarationBoundaryAnchor,
|
|
1306
|
+
},
|
|
1307
|
+
}
|
|
1308
|
+
);
|
|
1309
|
+
assert(
|
|
1310
|
+
syntaxRefreshDecision?.triggerCategory === "python-syntax-regression" &&
|
|
1311
|
+
syntaxRefreshDecision.path === "preview_builder.py",
|
|
1312
|
+
"a prospective Python syntax regression did not force a fresh exact-source read"
|
|
1313
|
+
);
|
|
1219
1314
|
const semanticScopeMismatchState = {
|
|
1220
1315
|
meta: {
|
|
1221
1316
|
goalContract: { revision: 4 },
|
|
@@ -2782,6 +2877,16 @@ try {
|
|
|
2782
2877
|
acceptanceClassification.substantiveTest === true,
|
|
2783
2878
|
"a structurally named Python acceptance validator was treated as an arbitrary mutating script"
|
|
2784
2879
|
);
|
|
2880
|
+
const externalAcceptanceValidator =
|
|
2881
|
+
"/opt/conda/bin/python /tmp/supervision/acceptance/webapp_booking_contract.py";
|
|
2882
|
+
const externalAcceptanceClassification = classifyCommand(externalAcceptanceValidator);
|
|
2883
|
+
assert(
|
|
2884
|
+
externalAcceptanceClassification.category === "general-shell" &&
|
|
2885
|
+
externalAcceptanceClassification.writesWorkspace === true &&
|
|
2886
|
+
externalAcceptanceClassification.mayMutateProject === true &&
|
|
2887
|
+
externalAcceptanceClassification.semanticMayMutateProject === false,
|
|
2888
|
+
"trusted-shell authorization for an external Python validator was conflated with project mutation"
|
|
2889
|
+
);
|
|
2785
2890
|
assert(
|
|
2786
2891
|
classifyCommand(`${acceptanceValidator} --output report.json`).writesWorkspace === true,
|
|
2787
2892
|
"a Python acceptance validator with an explicit output destination was treated as read-only"
|
|
@@ -3679,6 +3784,32 @@ try {
|
|
|
3679
3784
|
readOnlyValidatorResult.projectMutationRevision === 0,
|
|
3680
3785
|
"a semantically read-only validator fabricated project mutation progress"
|
|
3681
3786
|
);
|
|
3787
|
+
const externalReadOnlyValidatorState = { meta: { goalContract: { revision: 1 } } };
|
|
3788
|
+
const externalReadOnlyValidatorCommand =
|
|
3789
|
+
"cd . && /opt/conda/bin/python /tmp/supervision/acceptance/webapp_booking_contract.py && git status --short";
|
|
3790
|
+
const externalReadOnlyValidatorResult = {
|
|
3791
|
+
toolName: "run_command",
|
|
3792
|
+
ok: true,
|
|
3793
|
+
exitCode: 0,
|
|
3794
|
+
args: { command: externalReadOnlyValidatorCommand },
|
|
3795
|
+
stdout: "webapp booking contract: PASS\n",
|
|
3796
|
+
stderr: "",
|
|
3797
|
+
commandPolicy: classifyCommand(externalReadOnlyValidatorCommand),
|
|
3798
|
+
};
|
|
3799
|
+
recordProjectVerificationOutcome(
|
|
3800
|
+
externalReadOnlyValidatorState,
|
|
3801
|
+
externalReadOnlyValidatorResult,
|
|
3802
|
+
{
|
|
3803
|
+
commandCwd: workspace,
|
|
3804
|
+
taskProfile: "app",
|
|
3805
|
+
sandboxMode: "host",
|
|
3806
|
+
}
|
|
3807
|
+
);
|
|
3808
|
+
assert(
|
|
3809
|
+
externalReadOnlyValidatorState.meta.projectVerification?.mutationRevision === 0 &&
|
|
3810
|
+
externalReadOnlyValidatorResult.projectMutationRevision === 0,
|
|
3811
|
+
"an absolute-interpreter acceptance validator forced a post-validation commit"
|
|
3812
|
+
);
|
|
3682
3813
|
const shellMutationState = { meta: { goalContract: { revision: 1 } } };
|
|
3683
3814
|
const shellMutationResult = {
|
|
3684
3815
|
toolName: "run_command",
|
|
@@ -10328,6 +10459,60 @@ try {
|
|
|
10328
10459
|
assert(resumedBudget?.extensionsUsed === 0, "runAgent retained stale extension usage after an explicit resumed max-steps patch");
|
|
10329
10460
|
assert(resumedBudget?.resetFromExplicitOverride === true, "runAgent did not record the explicit budget reset boundary");
|
|
10330
10461
|
|
|
10462
|
+
const educationWorkspace = path.join(tempRoot, "education-artifact-contract");
|
|
10463
|
+
await fs.mkdir(path.join(educationWorkspace, "workshop"), { recursive: true });
|
|
10464
|
+
await fs.writeFile(path.join(educationWorkspace, "workshop", "lesson-deck.md"), "# Lesson\n");
|
|
10465
|
+
const educationGoal =
|
|
10466
|
+
"Create an editable lesson deck, a separate practice sheet and answer key, printable materials, a helpful preview, and a reproducible build entrypoint. Verify and commit the work.";
|
|
10467
|
+
const educationState = {
|
|
10468
|
+
goal: educationGoal,
|
|
10469
|
+
commandCwd: educationWorkspace,
|
|
10470
|
+
plan: "Create every requested artifact, verify them, and commit the complete set.",
|
|
10471
|
+
messages: [
|
|
10472
|
+
{
|
|
10473
|
+
role: "tool",
|
|
10474
|
+
content: JSON.stringify({
|
|
10475
|
+
ok: true,
|
|
10476
|
+
toolName: "write_file",
|
|
10477
|
+
path: "workshop/lesson-deck.md",
|
|
10478
|
+
goalRevision: 1,
|
|
10479
|
+
projectMutationRevision: 1,
|
|
10480
|
+
}),
|
|
10481
|
+
},
|
|
10482
|
+
],
|
|
10483
|
+
meta: {
|
|
10484
|
+
taskProfile: "education",
|
|
10485
|
+
goalContract: {
|
|
10486
|
+
revision: 1,
|
|
10487
|
+
currentRequest: educationGoal,
|
|
10488
|
+
taskGoal: educationGoal,
|
|
10489
|
+
activeGoalRevision: 1,
|
|
10490
|
+
lifecycle: [{ at: new Date(Date.now() - 2000).toISOString() }],
|
|
10491
|
+
},
|
|
10492
|
+
projectVerification: {
|
|
10493
|
+
mutationRevision: 1,
|
|
10494
|
+
mutationHistory: [
|
|
10495
|
+
{
|
|
10496
|
+
revision: 1,
|
|
10497
|
+
at: new Date().toISOString(),
|
|
10498
|
+
toolName: "write_file",
|
|
10499
|
+
paths: ["workshop/lesson-deck.md"],
|
|
10500
|
+
goalRevision: 1,
|
|
10501
|
+
},
|
|
10502
|
+
],
|
|
10503
|
+
},
|
|
10504
|
+
},
|
|
10505
|
+
};
|
|
10506
|
+
const incompleteEducationRuntime = nextStepRuntimeConfig(
|
|
10507
|
+
{ goal: educationGoal, taskProfile: "education", commandCwd: educationWorkspace },
|
|
10508
|
+
educationState
|
|
10509
|
+
);
|
|
10510
|
+
assert(
|
|
10511
|
+
incompleteEducationRuntime.requestedArtifactRequirementsPending === true &&
|
|
10512
|
+
incompleteEducationRuntime.taskOwnedCommitPending !== true,
|
|
10513
|
+
"a partial multi-artifact task entered task-owned Git completion before its deliverables existed"
|
|
10514
|
+
);
|
|
10515
|
+
|
|
10331
10516
|
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
10332
10517
|
console.log("smoke-dynamic-step-budget ok");
|
|
10333
10518
|
} catch (error) {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
buildScsEvidenceLedger,
|
|
14
14
|
deriveScsTaskContract,
|
|
15
|
+
evaluateRequestedArtifactRequirements,
|
|
15
16
|
evaluateScsSemanticContract,
|
|
16
17
|
extractMarkdownCommandEvidence,
|
|
17
18
|
extractMarkdownPathEvidence,
|
|
@@ -19,6 +20,73 @@ import {
|
|
|
19
20
|
finishResultClaimsIncompleteWork,
|
|
20
21
|
} from "../src/scs-evidence.js";
|
|
21
22
|
|
|
23
|
+
const educationArtifactGoal =
|
|
24
|
+
"Create an editable lesson deck, a separate practice sheet and answer key, printable materials, a helpful preview, and a reproducible build entrypoint.";
|
|
25
|
+
const educationArtifactContract = deriveScsTaskContract({
|
|
26
|
+
goal: educationArtifactGoal,
|
|
27
|
+
taskProfile: "education",
|
|
28
|
+
});
|
|
29
|
+
assert.deepEqual(
|
|
30
|
+
educationArtifactContract.requiredArtifactKinds.map((item) => item.id).sort(),
|
|
31
|
+
[
|
|
32
|
+
"answer-material",
|
|
33
|
+
"editable-presentation",
|
|
34
|
+
"practice-material",
|
|
35
|
+
"printable-document",
|
|
36
|
+
"reproducible-build-entrypoint",
|
|
37
|
+
"visual-preview",
|
|
38
|
+
],
|
|
39
|
+
"multi-artifact education request did not retain each semantic deliverable"
|
|
40
|
+
);
|
|
41
|
+
assert.deepEqual(
|
|
42
|
+
deriveScsTaskContract({
|
|
43
|
+
goal: "Create a canvas artifact preview for this smoke test.",
|
|
44
|
+
taskProfile: "auto",
|
|
45
|
+
}).requiredArtifactKinds,
|
|
46
|
+
[],
|
|
47
|
+
"a generic frontend canvas preview was incorrectly converted into a required image-file artifact"
|
|
48
|
+
);
|
|
49
|
+
const educationWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), "aginti-education-artifacts-"));
|
|
50
|
+
const educationState = {
|
|
51
|
+
meta: {
|
|
52
|
+
goalContract: {
|
|
53
|
+
lifecycle: [{ at: new Date(Date.now() - 2000).toISOString() }],
|
|
54
|
+
},
|
|
55
|
+
projectVerification: { mutationHistory: [] },
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
fs.mkdirSync(path.join(educationWorkspace, "workshop"), { recursive: true });
|
|
59
|
+
fs.writeFileSync(path.join(educationWorkspace, "workshop", "lesson-deck.md"), "# Lesson deck\n");
|
|
60
|
+
const incompleteEducationArtifacts = evaluateRequestedArtifactRequirements(
|
|
61
|
+
educationArtifactContract,
|
|
62
|
+
{ commandCwd: educationWorkspace, state: educationState }
|
|
63
|
+
);
|
|
64
|
+
assert.equal(incompleteEducationArtifacts.ok, false);
|
|
65
|
+
assert.ok(
|
|
66
|
+
incompleteEducationArtifacts.missing.some((item) => item.id === "editable-presentation"),
|
|
67
|
+
"Markdown-only deck incorrectly satisfied the editable presentation contract"
|
|
68
|
+
);
|
|
69
|
+
for (const [name, content] of [
|
|
70
|
+
["lesson-deck.pptx", "pptx"],
|
|
71
|
+
["practice-sheet.md", "practice"],
|
|
72
|
+
["answer-key.md", "answers"],
|
|
73
|
+
["practice-sheet.pdf", "pdf"],
|
|
74
|
+
["lesson-preview.png", "png"],
|
|
75
|
+
["build_materials.py", "print('build')\n"],
|
|
76
|
+
]) {
|
|
77
|
+
fs.writeFileSync(path.join(educationWorkspace, "workshop", name), content);
|
|
78
|
+
}
|
|
79
|
+
const completeEducationArtifacts = evaluateRequestedArtifactRequirements(
|
|
80
|
+
educationArtifactContract,
|
|
81
|
+
{ commandCwd: educationWorkspace, state: educationState }
|
|
82
|
+
);
|
|
83
|
+
assert.equal(
|
|
84
|
+
completeEducationArtifacts.ok,
|
|
85
|
+
true,
|
|
86
|
+
`complete semantic artifact set was rejected: ${completeEducationArtifacts.reason}`
|
|
87
|
+
);
|
|
88
|
+
fs.rmSync(educationWorkspace, { recursive: true, force: true });
|
|
89
|
+
|
|
22
90
|
function fakeStudentClient(json) {
|
|
23
91
|
return {
|
|
24
92
|
chat: {
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -68,12 +68,23 @@ async function waitForHealth() {
|
|
|
68
68
|
async function waitForRun(sessionId, terminalStatuses = ["finished", "failed"]) {
|
|
69
69
|
const acceptedStatuses = new Set(terminalStatuses);
|
|
70
70
|
const deadline = Date.now() + 20000;
|
|
71
|
+
let lastRun = null;
|
|
71
72
|
while (Date.now() < deadline) {
|
|
72
73
|
const run = await fetchJson(`/api/runs/${encodeURIComponent(sessionId)}`);
|
|
74
|
+
lastRun = run;
|
|
73
75
|
if (acceptedStatuses.has(run.status)) return run;
|
|
74
76
|
await delay(400);
|
|
75
77
|
}
|
|
76
|
-
throw new Error(
|
|
78
|
+
throw new Error(
|
|
79
|
+
`run ${sessionId} did not finish in time; ` +
|
|
80
|
+
`lastRun=${JSON.stringify(lastRun ? {
|
|
81
|
+
status: lastRun.status,
|
|
82
|
+
error: lastRun.error,
|
|
83
|
+
endedAt: lastRun.endedAt,
|
|
84
|
+
logs: Array.isArray(lastRun.logs) ? lastRun.logs.slice(-12) : [],
|
|
85
|
+
} : null)} ` +
|
|
86
|
+
`stdout=${stdout.slice(-1000)} stderr=${stderr.slice(-1000)}`
|
|
87
|
+
);
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
try {
|
package/src/agent-runner.js
CHANGED
|
@@ -128,6 +128,7 @@ import {
|
|
|
128
128
|
deterministicFinishBlocker,
|
|
129
129
|
evaluateScsEvidence,
|
|
130
130
|
evaluateScsSemanticContract,
|
|
131
|
+
evaluateRequestedArtifactRequirements,
|
|
131
132
|
augmentScsTaskContractWithProjectVerification,
|
|
132
133
|
agintiEvidenceScopeLine,
|
|
133
134
|
filterExplicitlyExcludedOutputPaths,
|
|
@@ -1754,6 +1755,11 @@ function buildCompactedRuntimeMessages(state, config, snapshot, step, options =
|
|
|
1754
1755
|
title: snapshot?.title || "",
|
|
1755
1756
|
url: snapshot?.url || "",
|
|
1756
1757
|
plan: effectivePlan,
|
|
1758
|
+
missingRequestedArtifactRequirements: Array.isArray(
|
|
1759
|
+
config.missingRequestedArtifactRequirements
|
|
1760
|
+
)
|
|
1761
|
+
? config.missingRequestedArtifactRequirements
|
|
1762
|
+
: [],
|
|
1757
1763
|
};
|
|
1758
1764
|
const compactedContent = [
|
|
1759
1765
|
options.heading || "Continue from this compacted, valid transcript.",
|
|
@@ -10110,6 +10116,12 @@ function commandCanMutateProjectContent(command = "", commandPolicy = {}) {
|
|
|
10110
10116
|
const category = String(commandPolicy.category || "");
|
|
10111
10117
|
const requiresGitMutationInspection =
|
|
10112
10118
|
["git-workflow", "git-remote"].includes(category);
|
|
10119
|
+
if (
|
|
10120
|
+
commandPolicy.semanticMayMutateProject === false &&
|
|
10121
|
+
!requiresGitMutationInspection
|
|
10122
|
+
) {
|
|
10123
|
+
return false;
|
|
10124
|
+
}
|
|
10113
10125
|
if (requiresGitMutationInspection && commandPolicy.gitOnly === true) {
|
|
10114
10126
|
return inferGitActionsFromCommand(command, { requireFailurePropagation: false }).some((action) =>
|
|
10115
10127
|
WORKTREE_CHANGING_GIT_ACTIONS.has(action)
|
|
@@ -11501,6 +11513,21 @@ export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
|
11501
11513
|
? state.meta.activeExecutionContract || {}
|
|
11502
11514
|
: {};
|
|
11503
11515
|
const groundingTaskContract = completionTaskContract(config, state);
|
|
11516
|
+
const requestedArtifactEvaluation = evaluateRequestedArtifactRequirements(
|
|
11517
|
+
groundingTaskContract,
|
|
11518
|
+
{
|
|
11519
|
+
commandCwd: config.commandCwd || state.commandCwd || process.cwd(),
|
|
11520
|
+
state,
|
|
11521
|
+
}
|
|
11522
|
+
);
|
|
11523
|
+
if (!requestedArtifactEvaluation.ok) {
|
|
11524
|
+
runtimeConfig.requestedArtifactRequirementsPending = true;
|
|
11525
|
+
runtimeConfig.missingRequestedArtifactRequirements =
|
|
11526
|
+
requestedArtifactEvaluation.missing.map((item) => ({
|
|
11527
|
+
id: String(item?.id || ""),
|
|
11528
|
+
description: String(item?.description || item?.id || ""),
|
|
11529
|
+
}));
|
|
11530
|
+
}
|
|
11504
11531
|
const groundingMutationRevision = Math.max(
|
|
11505
11532
|
0,
|
|
11506
11533
|
Number(state.meta?.projectVerification?.mutationRevision || 0)
|
|
@@ -12257,7 +12284,12 @@ export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
|
12257
12284
|
runtimeConfig.requiredProjectCommand = pendingRequiredProjectCommands[0];
|
|
12258
12285
|
runtimeConfig.pendingRequiredProjectCommands = pendingRequiredProjectCommands;
|
|
12259
12286
|
}
|
|
12260
|
-
if (
|
|
12287
|
+
if (
|
|
12288
|
+
!state.meta?.artifactProgress &&
|
|
12289
|
+
!retainedFailedTest &&
|
|
12290
|
+
!implementationOpen &&
|
|
12291
|
+
requestedArtifactEvaluation.ok
|
|
12292
|
+
) {
|
|
12261
12293
|
const completionContract = completionTaskContract(config, state);
|
|
12262
12294
|
const completionEvaluation = evaluateScsEvidence(
|
|
12263
12295
|
completionContract,
|
|
@@ -13287,6 +13319,25 @@ function comparablePatchContextText(value = "") {
|
|
|
13287
13319
|
.trim();
|
|
13288
13320
|
}
|
|
13289
13321
|
|
|
13322
|
+
function preservePatchContextBoundaryWhitespace(marker = {}, replacement = "") {
|
|
13323
|
+
const value = String(replacement || "");
|
|
13324
|
+
if (String(marker.anchorKind || "") === "complete-file") {
|
|
13325
|
+
return { replacement: value, preserved: false };
|
|
13326
|
+
}
|
|
13327
|
+
const search = String(marker.search || "");
|
|
13328
|
+
const trailingBoundary = search.match(/(?:(?:\r\n|\n|\r)[\t ]*)+$/)?.[0] || "";
|
|
13329
|
+
if (!trailingBoundary) return { replacement: value, preserved: false };
|
|
13330
|
+
const withoutTrailingBoundary = value.replace(
|
|
13331
|
+
/(?:(?:\r\n|\n|\r)[\t ]*)+$/,
|
|
13332
|
+
""
|
|
13333
|
+
);
|
|
13334
|
+
const normalized = `${withoutTrailingBoundary}${trailingBoundary}`;
|
|
13335
|
+
return {
|
|
13336
|
+
replacement: normalized,
|
|
13337
|
+
preserved: normalized !== value,
|
|
13338
|
+
};
|
|
13339
|
+
}
|
|
13340
|
+
|
|
13290
13341
|
function patchContextDeclarationCounts(content = "") {
|
|
13291
13342
|
const counts = new Map();
|
|
13292
13343
|
for (const record of sourceLineRecords(content)) {
|
|
@@ -13514,6 +13565,11 @@ export function bindPatchContextRepairArguments(state = {}, requestedArgs = {})
|
|
|
13514
13565
|
: patchContextReplacementScopeIssue(boundMarker, replace);
|
|
13515
13566
|
}
|
|
13516
13567
|
}
|
|
13568
|
+
const boundaryWhitespace = preservePatchContextBoundaryWhitespace(
|
|
13569
|
+
boundMarker,
|
|
13570
|
+
replace
|
|
13571
|
+
);
|
|
13572
|
+
replace = boundaryWhitespace.replacement;
|
|
13517
13573
|
if (boundedSubrange && state.meta?.toolLoop?.patchContextRepair) {
|
|
13518
13574
|
state.meta.toolLoop.patchContextRepair.lastBoundSearchHash =
|
|
13519
13575
|
boundedSubrange.searchHash;
|
|
@@ -13532,6 +13588,7 @@ export function bindPatchContextRepairArguments(state = {}, requestedArgs = {})
|
|
|
13532
13588
|
: "",
|
|
13533
13589
|
boundedRequestedSubrange: Boolean(boundedSubrange),
|
|
13534
13590
|
incrementalDeclarationRecovery,
|
|
13591
|
+
boundaryWhitespacePreserved: boundaryWhitespace.preserved,
|
|
13535
13592
|
noOp:
|
|
13536
13593
|
comparablePatchContextText(replace) ===
|
|
13537
13594
|
comparablePatchContextText(boundMarker.search),
|
|
@@ -13607,8 +13664,21 @@ export function patchContextRefreshDecision(state = {}, toolResult = {}) {
|
|
|
13607
13664
|
toolResult.ok === false &&
|
|
13608
13665
|
toolResult.category === "patch-context-scope-mismatch"
|
|
13609
13666
|
);
|
|
13667
|
+
const pythonSyntaxRegression = Boolean(
|
|
13668
|
+
String(toolResult.toolName || "") === "apply_patch" &&
|
|
13669
|
+
toolResult.ok === false &&
|
|
13670
|
+
toolResult.category === "python-syntax-regression"
|
|
13671
|
+
);
|
|
13610
13672
|
const missingSearch = patchSearchTextWasNotFound(toolResult);
|
|
13611
|
-
if (
|
|
13673
|
+
if (
|
|
13674
|
+
!missingSearch &&
|
|
13675
|
+
!idempotencyBlock &&
|
|
13676
|
+
!topologyBlock &&
|
|
13677
|
+
!scopeMismatch &&
|
|
13678
|
+
!pythonSyntaxRegression
|
|
13679
|
+
) {
|
|
13680
|
+
return null;
|
|
13681
|
+
}
|
|
13612
13682
|
const targetPath = toolResultWorkspacePath(toolResult);
|
|
13613
13683
|
if (!targetPath) return null;
|
|
13614
13684
|
const toolLoop = state.meta?.toolLoop || {};
|
|
@@ -13636,6 +13706,7 @@ export function patchContextRefreshDecision(state = {}, toolResult = {}) {
|
|
|
13636
13706
|
!idempotencyBlock &&
|
|
13637
13707
|
!topologyBlock &&
|
|
13638
13708
|
!scopeMismatch &&
|
|
13709
|
+
!pythonSyntaxRegression &&
|
|
13639
13710
|
stalePatchFailureCount < 1 &&
|
|
13640
13711
|
!followedIdempotencyBlock
|
|
13641
13712
|
) {
|
|
@@ -13667,6 +13738,8 @@ export function patchContextRefreshDecision(state = {}, toolResult = {}) {
|
|
|
13667
13738
|
? "required-symbol-topology"
|
|
13668
13739
|
: scopeMismatch
|
|
13669
13740
|
? "patch-context-scope-mismatch"
|
|
13741
|
+
: pythonSyntaxRegression
|
|
13742
|
+
? "python-syntax-regression"
|
|
13670
13743
|
: "stale-patch-search",
|
|
13671
13744
|
failedSearchPreview,
|
|
13672
13745
|
failedSearchHash: String(toolResult.args?.searchHash || ""),
|
|
@@ -14648,6 +14721,8 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
14648
14721
|
patchContextBinding.boundedRequestedSubrange === true,
|
|
14649
14722
|
incrementalDeclarationRecovery:
|
|
14650
14723
|
Boolean(patchContextBinding.incrementalDeclarationRecovery),
|
|
14724
|
+
boundaryWhitespacePreserved:
|
|
14725
|
+
patchContextBinding.boundaryWhitespacePreserved === true,
|
|
14651
14726
|
};
|
|
14652
14727
|
await store.appendEvent("patch_context.anchor_injected", detail);
|
|
14653
14728
|
observers.event("patch_context.anchor_injected", detail);
|
package/src/command-policy.js
CHANGED
|
@@ -293,8 +293,9 @@ function delegatedValidationPlanClassification(tokens = []) {
|
|
|
293
293
|
const args = tokens.slice(1).map((token) => String(token || ""));
|
|
294
294
|
const executablePath = validationExecutablePathMetadata(tokens[0]);
|
|
295
295
|
const basenameCommand = [executable, ...args].join(" ");
|
|
296
|
+
const structuredValidation = structuredValidationCommand(basenameCommand);
|
|
296
297
|
const validationLike = Boolean(
|
|
297
|
-
|
|
298
|
+
structuredValidation || matchAny(TEST_PATTERNS, basenameCommand)
|
|
298
299
|
);
|
|
299
300
|
if (executablePath.outsideWorkspace && validationLike) {
|
|
300
301
|
return {
|
|
@@ -302,6 +303,12 @@ function delegatedValidationPlanClassification(tokens = []) {
|
|
|
302
303
|
needsNetwork: true,
|
|
303
304
|
writesWorkspace: true,
|
|
304
305
|
mayMutateProject: true,
|
|
306
|
+
// Requiring trusted-shell authorization for an external executable does
|
|
307
|
+
// not prove that its bounded validation invocation mutates the project.
|
|
308
|
+
// Preserve that semantic distinction for post-command evidence tracking.
|
|
309
|
+
semanticMayMutateProject:
|
|
310
|
+
structuredValidation?.mayMutateProject ??
|
|
311
|
+
validationCommandMayMutateProject(basenameCommand),
|
|
305
312
|
substantiveTest: false,
|
|
306
313
|
reason:
|
|
307
314
|
"The validation executable is outside the current workspace and requires trusted shell policy.",
|
package/src/scs-controller.js
CHANGED
|
@@ -508,6 +508,11 @@ function formatHardContractForPrompt(contract = {}) {
|
|
|
508
508
|
const requiredExecutableTerms = normalizeStringList(contract.requiredExecutableTerms, []);
|
|
509
509
|
const forbiddenTextTerms = normalizeStringList(contract.forbiddenTextTerms, []);
|
|
510
510
|
const forbiddenActions = normalizeStringList(contract.forbiddenActions, []);
|
|
511
|
+
const requiredArtifactKinds = (Array.isArray(contract.requiredArtifactKinds)
|
|
512
|
+
? contract.requiredArtifactKinds
|
|
513
|
+
: [])
|
|
514
|
+
.map((item) => compact(item?.description || item?.id || "", 160))
|
|
515
|
+
.filter(Boolean);
|
|
511
516
|
if (exactOutputPaths.length) lines.push(`Exact output path(s): ${exactOutputPaths.join(", ")}`);
|
|
512
517
|
if (exactInputPaths.length) lines.push(`Exact input/reference path(s) to use: ${exactInputPaths.join(", ")}`);
|
|
513
518
|
if (requiredTextTerms.length) lines.push(`Required text term(s) in the output: ${requiredTextTerms.join(", ")}`);
|
|
@@ -516,6 +521,9 @@ function formatHardContractForPrompt(contract = {}) {
|
|
|
516
521
|
}
|
|
517
522
|
if (forbiddenTextTerms.length) lines.push(`Forbidden text term(s) in the output: ${forbiddenTextTerms.join(", ")}`);
|
|
518
523
|
if (forbiddenActions.length) lines.push(`Forbidden action(s): ${forbiddenActions.join("; ")}`);
|
|
524
|
+
if (requiredArtifactKinds.length) {
|
|
525
|
+
lines.push(`Required artifact deliverable(s): ${requiredArtifactKinds.join(", ")}`);
|
|
526
|
+
}
|
|
519
527
|
return lines.length
|
|
520
528
|
? [
|
|
521
529
|
"Inferred hard task contract. Preserve these literally; do not replace them with weaker or contradictory criteria:",
|
|
@@ -531,8 +539,19 @@ function deterministicPlanContractIssue(committee = {}, contract = {}) {
|
|
|
531
539
|
const missingRequiredTerms = normalizeStringList(contract.requiredTextTerms, []).filter((item) => !planText.includes(item));
|
|
532
540
|
const missingExecutableTerms = normalizeStringList(contract.requiredExecutableTerms, []).filter((item) => !planText.includes(item));
|
|
533
541
|
const forbiddenTermsInPlan = normalizeStringList(contract.forbiddenTextTerms, []).filter((item) => planText.includes(item));
|
|
542
|
+
const missingArtifactKinds = (Array.isArray(contract.requiredArtifactKinds)
|
|
543
|
+
? contract.requiredArtifactKinds
|
|
544
|
+
: []).filter((item) => {
|
|
545
|
+
const id = String(item?.id || "").replace(/^format:/, "");
|
|
546
|
+
const description = String(item?.description || "");
|
|
547
|
+
const terms = [id, description]
|
|
548
|
+
.flatMap((value) => value.toLocaleLowerCase("en-US").split(/[^a-z0-9.]+/))
|
|
549
|
+
.filter((value) => value.length >= 3);
|
|
550
|
+
const normalizedPlan = planText.toLocaleLowerCase("en-US");
|
|
551
|
+
return terms.length > 0 && !terms.some((term) => normalizedPlan.includes(term));
|
|
552
|
+
});
|
|
534
553
|
const actionContradiction = deterministicPlanActionContradiction(planText, contract);
|
|
535
|
-
if (missingPath.length || missingInputPath.length || missingRequiredTerms.length || missingExecutableTerms.length || forbiddenTermsInPlan.length || actionContradiction) {
|
|
554
|
+
if (missingPath.length || missingInputPath.length || missingRequiredTerms.length || missingExecutableTerms.length || forbiddenTermsInPlan.length || missingArtifactKinds.length || actionContradiction) {
|
|
536
555
|
return {
|
|
537
556
|
decision: "veto_plan",
|
|
538
557
|
confidence: 0.94,
|
|
@@ -542,6 +561,11 @@ function deterministicPlanContractIssue(committee = {}, contract = {}) {
|
|
|
542
561
|
missingRequiredTerms.length ? `Plan omitted required text term(s): ${missingRequiredTerms.join(", ")}` : "",
|
|
543
562
|
missingExecutableTerms.length ? `Plan omitted required executable source expression(s): ${missingExecutableTerms.join(", ")}` : "",
|
|
544
563
|
forbiddenTermsInPlan.length ? `Plan includes forbidden text term(s): ${forbiddenTermsInPlan.join(", ")}` : "",
|
|
564
|
+
missingArtifactKinds.length
|
|
565
|
+
? `Plan omitted requested artifact deliverable(s): ${missingArtifactKinds
|
|
566
|
+
.map((item) => item.description || item.id)
|
|
567
|
+
.join(", ")}`
|
|
568
|
+
: "",
|
|
545
569
|
actionContradiction || "",
|
|
546
570
|
].filter(Boolean),
|
|
547
571
|
reason:
|
package/src/scs-evidence.js
CHANGED
|
@@ -521,6 +521,120 @@ function inferExactOutputPaths(goal = "") {
|
|
|
521
521
|
return uniqueLimited(filterShadowedBasenames(paths), 16);
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
+
const REQUESTED_ARTIFACT_FORMATS = [
|
|
525
|
+
{ extension: ".pptx", pattern: /\b(?:pptx|powerpoint)\b/i, description: "editable PowerPoint deck" },
|
|
526
|
+
{ extension: ".odp", pattern: /\b(?:odp|open(?:office|document) presentation)\b/i, description: "editable ODP deck" },
|
|
527
|
+
{ extension: ".pdf", pattern: /\bpdf\b/i, description: "PDF document" },
|
|
528
|
+
{ extension: ".docx", pattern: /\b(?:docx|word document)\b/i, description: "editable Word document" },
|
|
529
|
+
{ extension: ".xlsx", pattern: /\b(?:xlsx|excel workbook)\b/i, description: "editable spreadsheet" },
|
|
530
|
+
{ extension: ".png", pattern: /\bpng\b/i, description: "PNG image" },
|
|
531
|
+
{ extension: ".svg", pattern: /\bsvg\b/i, description: "editable SVG image" },
|
|
532
|
+
];
|
|
533
|
+
|
|
534
|
+
function artifactRequestHasOutputIntent(goal = "", taskProfile = "") {
|
|
535
|
+
const source = stripForbiddenLanguage(String(goal || ""));
|
|
536
|
+
return Boolean(
|
|
537
|
+
goalRequestsFileMutation(source, taskProfile) ||
|
|
538
|
+
/\b(?:create|deliver|export|generate|leave|make|output|prepare|produce|save|write)\b/i.test(
|
|
539
|
+
source
|
|
540
|
+
) ||
|
|
541
|
+
/\b(?:i|we)\s+(?:also\s+)?(?:need|want|would like)\b[^.\n;]{0,100}\b(?:answer|artifact|deck|document|handout|material|pdf|presentation|preview|report|sheet|slides?|workbook|worksheet)\b/i.test(
|
|
542
|
+
source
|
|
543
|
+
) ||
|
|
544
|
+
/\b(?:give|provide|return|send)\b[^.\n;]{0,80}\b(?:answer|artifact|deck|document|handout|material|pdf|presentation|preview|report|sheet|slides?|workbook|worksheet)\b/i.test(
|
|
545
|
+
source
|
|
546
|
+
) ||
|
|
547
|
+
/(?:创建|生成|输出|导出|保存|提供|需要|交付)/u.test(source)
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export function inferRequestedArtifactRequirements(goal = "", taskProfile = "") {
|
|
552
|
+
const source = stripForbiddenLanguage(String(goal || ""));
|
|
553
|
+
if (!artifactRequestHasOutputIntent(source, taskProfile)) return [];
|
|
554
|
+
|
|
555
|
+
const requirements = [];
|
|
556
|
+
const add = (requirement) => {
|
|
557
|
+
if (!requirement?.id || requirements.some((item) => item.id === requirement.id)) return;
|
|
558
|
+
requirements.push(requirement);
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
for (const format of REQUESTED_ARTIFACT_FORMATS) {
|
|
562
|
+
if (!format.pattern.test(source)) continue;
|
|
563
|
+
add({
|
|
564
|
+
id: `format:${format.extension}`,
|
|
565
|
+
kind: "format",
|
|
566
|
+
extension: format.extension,
|
|
567
|
+
description: format.description,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const deckRequested = /\b(?:deck|presentation|slides?|slide deck|lecture deck)\b/i.test(source);
|
|
572
|
+
const editableRequested = /\b(?:editable|edit-friendly|native(?:ly)? editable)\b/i.test(source);
|
|
573
|
+
if (deckRequested && editableRequested) {
|
|
574
|
+
add({
|
|
575
|
+
id: "editable-presentation",
|
|
576
|
+
kind: "editable-presentation",
|
|
577
|
+
extensions: [".pptx", ".odp", ".key"],
|
|
578
|
+
description: "editable presentation deck",
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (
|
|
583
|
+
/\bprintable\b/i.test(source) &&
|
|
584
|
+
/\b(?:answer|deck|document|handout|material|practice|sheet|slides?|worksheet)\b/i.test(source)
|
|
585
|
+
) {
|
|
586
|
+
add({
|
|
587
|
+
id: "printable-document",
|
|
588
|
+
kind: "printable-document",
|
|
589
|
+
extensions: [".pdf"],
|
|
590
|
+
description: "printable PDF material",
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (
|
|
595
|
+
/\b(?:helpful|high[- ]resolution|useful|visual)\s+preview\b|\b(?:image|png|rendered|screenshot)\s+preview\b|\bpreview\s+(?:image|png|render|screenshot)\b/i.test(
|
|
596
|
+
source
|
|
597
|
+
)
|
|
598
|
+
) {
|
|
599
|
+
add({
|
|
600
|
+
id: "visual-preview",
|
|
601
|
+
kind: "visual-preview",
|
|
602
|
+
extensions: [".png", ".jpg", ".jpeg", ".webp"],
|
|
603
|
+
description: "visual preview image",
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (/\b(?:practice sheet|worksheet|exercise sheet|learner handout)\b/i.test(source)) {
|
|
608
|
+
add({
|
|
609
|
+
id: "practice-material",
|
|
610
|
+
kind: "practice-material",
|
|
611
|
+
description: "separate practice or worksheet material",
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (/\b(?:answer key|answers?|worked solutions?|solutions? sheet)\b/i.test(source)) {
|
|
616
|
+
add({
|
|
617
|
+
id: "answer-material",
|
|
618
|
+
kind: "answer-material",
|
|
619
|
+
description: "separate answer or solution material",
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (
|
|
624
|
+
/\b(?:reproducible|repeatable)\b[^.\n;]{0,80}\b(?:build|generation|export|render)\b|\b(?:build|generation|export|render)\s+(?:entrypoint|script|command)\b/i.test(
|
|
625
|
+
source
|
|
626
|
+
)
|
|
627
|
+
) {
|
|
628
|
+
add({
|
|
629
|
+
id: "reproducible-build-entrypoint",
|
|
630
|
+
kind: "reproducible-build-entrypoint",
|
|
631
|
+
description: "reproducible build or export entrypoint",
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return requirements.slice(0, 16);
|
|
636
|
+
}
|
|
637
|
+
|
|
524
638
|
function normalizedContractPath(value = "") {
|
|
525
639
|
return String(value || "")
|
|
526
640
|
.trim()
|
|
@@ -1581,6 +1695,10 @@ export function deriveScsTaskContract({ goal = "", taskProfile = "", acceptanceC
|
|
|
1581
1695
|
excludedOutputPaths
|
|
1582
1696
|
).filter((item) => !inferredOutputPaths.includes(item) && !exactOutputPaths.includes(item));
|
|
1583
1697
|
const declaredSourceRoots = inferDeclaredSourceRoots(evidenceGoal);
|
|
1698
|
+
const requiredArtifactKinds = inferRequestedArtifactRequirements(
|
|
1699
|
+
positiveEvidenceGoal,
|
|
1700
|
+
taskProfile
|
|
1701
|
+
);
|
|
1584
1702
|
return {
|
|
1585
1703
|
version: 1,
|
|
1586
1704
|
outcome: compact(evidenceGoal || "Complete the requested task.", 500),
|
|
@@ -1589,6 +1707,7 @@ export function deriveScsTaskContract({ goal = "", taskProfile = "", acceptanceC
|
|
|
1589
1707
|
requiredEvidence,
|
|
1590
1708
|
forbiddenActions: inferForbiddenActions(evidenceGoal),
|
|
1591
1709
|
exactOutputPaths,
|
|
1710
|
+
requiredArtifactKinds,
|
|
1592
1711
|
excludedOutputPaths,
|
|
1593
1712
|
artifactRoot,
|
|
1594
1713
|
exactInputPaths,
|
|
@@ -1817,6 +1936,190 @@ function resolveContractPath(commandCwd = process.cwd(), rawPath = "") {
|
|
|
1817
1936
|
return path.resolve(commandCwd || process.cwd(), text);
|
|
1818
1937
|
}
|
|
1819
1938
|
|
|
1939
|
+
const ARTIFACT_SCAN_IGNORED_DIRECTORIES = new Set([
|
|
1940
|
+
".aginti",
|
|
1941
|
+
".aginti-sessions",
|
|
1942
|
+
".git",
|
|
1943
|
+
".hg",
|
|
1944
|
+
".svn",
|
|
1945
|
+
"__pycache__",
|
|
1946
|
+
"node_modules",
|
|
1947
|
+
"vendor",
|
|
1948
|
+
]);
|
|
1949
|
+
|
|
1950
|
+
function taskArtifactFreshnessBoundary(state = {}) {
|
|
1951
|
+
const lifecycle = Array.isArray(state.meta?.goalContract?.lifecycle)
|
|
1952
|
+
? state.meta.goalContract.lifecycle
|
|
1953
|
+
: [];
|
|
1954
|
+
const lifecycleTimes = lifecycle
|
|
1955
|
+
.map((item) => Date.parse(String(item?.at || "")))
|
|
1956
|
+
.filter(Number.isFinite);
|
|
1957
|
+
if (lifecycleTimes.length) return Math.min(...lifecycleTimes) - 2000;
|
|
1958
|
+
const mutationTimes = (Array.isArray(state.meta?.projectVerification?.mutationHistory)
|
|
1959
|
+
? state.meta.projectVerification.mutationHistory
|
|
1960
|
+
: [])
|
|
1961
|
+
.map((item) => Date.parse(String(item?.at || "")))
|
|
1962
|
+
.filter(Number.isFinite);
|
|
1963
|
+
return mutationTimes.length ? Math.min(...mutationTimes) - 2000 : 0;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
function taskArtifactMutationPaths(state = {}) {
|
|
1967
|
+
const paths = new Set();
|
|
1968
|
+
for (const mutation of Array.isArray(state.meta?.projectVerification?.mutationHistory)
|
|
1969
|
+
? state.meta.projectVerification.mutationHistory
|
|
1970
|
+
: []) {
|
|
1971
|
+
for (const candidate of Array.isArray(mutation?.paths) ? mutation.paths : []) {
|
|
1972
|
+
const normalized = normalizedContractPath(candidate);
|
|
1973
|
+
if (normalized) paths.add(normalized.toLocaleLowerCase("en-US"));
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return paths;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
function collectRequestedArtifactCandidates(commandCwd = process.cwd(), state = {}) {
|
|
1980
|
+
const root = path.resolve(commandCwd || process.cwd());
|
|
1981
|
+
const freshnessBoundary = taskArtifactFreshnessBoundary(state);
|
|
1982
|
+
const mutationPaths = taskArtifactMutationPaths(state);
|
|
1983
|
+
const candidates = [];
|
|
1984
|
+
const queue = [{ absolutePath: root, depth: 0 }];
|
|
1985
|
+
let visited = 0;
|
|
1986
|
+
while (queue.length && candidates.length < 3000 && visited < 6000) {
|
|
1987
|
+
const current = queue.shift();
|
|
1988
|
+
visited += 1;
|
|
1989
|
+
let entries = [];
|
|
1990
|
+
try {
|
|
1991
|
+
entries = fs.readdirSync(current.absolutePath, { withFileTypes: true });
|
|
1992
|
+
} catch {
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
for (const entry of entries) {
|
|
1996
|
+
if (entry.isSymbolicLink()) continue;
|
|
1997
|
+
const absolutePath = path.join(current.absolutePath, entry.name);
|
|
1998
|
+
if (entry.isDirectory()) {
|
|
1999
|
+
if (
|
|
2000
|
+
current.depth < 7 &&
|
|
2001
|
+
!ARTIFACT_SCAN_IGNORED_DIRECTORIES.has(entry.name) &&
|
|
2002
|
+
!entry.name.startsWith(".cache")
|
|
2003
|
+
) {
|
|
2004
|
+
queue.push({ absolutePath, depth: current.depth + 1 });
|
|
2005
|
+
}
|
|
2006
|
+
continue;
|
|
2007
|
+
}
|
|
2008
|
+
if (!entry.isFile()) continue;
|
|
2009
|
+
let stat;
|
|
2010
|
+
try {
|
|
2011
|
+
stat = fs.statSync(absolutePath);
|
|
2012
|
+
} catch {
|
|
2013
|
+
continue;
|
|
2014
|
+
}
|
|
2015
|
+
if (!stat.size) continue;
|
|
2016
|
+
const relativePath = path.relative(root, absolutePath).replace(/\\/g, "/");
|
|
2017
|
+
const normalized = normalizedContractPath(relativePath).toLocaleLowerCase("en-US");
|
|
2018
|
+
const fresh = Boolean(
|
|
2019
|
+
mutationPaths.has(normalized) ||
|
|
2020
|
+
freshnessBoundary === 0 ||
|
|
2021
|
+
Number(stat.mtimeMs || 0) >= freshnessBoundary
|
|
2022
|
+
);
|
|
2023
|
+
candidates.push({
|
|
2024
|
+
path: relativePath,
|
|
2025
|
+
extension: path.extname(entry.name).toLocaleLowerCase("en-US"),
|
|
2026
|
+
basename: entry.name.toLocaleLowerCase("en-US"),
|
|
2027
|
+
bytes: stat.size,
|
|
2028
|
+
mtimeMs: Number(stat.mtimeMs || 0),
|
|
2029
|
+
fresh,
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
return candidates;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function requestedArtifactRequirementMatches(requirement = {}, candidate = {}) {
|
|
2037
|
+
const extension = String(candidate.extension || "").toLocaleLowerCase("en-US");
|
|
2038
|
+
const basename = String(candidate.basename || "").toLocaleLowerCase("en-US");
|
|
2039
|
+
if (requirement.kind === "format") {
|
|
2040
|
+
return extension === String(requirement.extension || "").toLocaleLowerCase("en-US");
|
|
2041
|
+
}
|
|
2042
|
+
if (["editable-presentation", "printable-document", "visual-preview"].includes(requirement.kind)) {
|
|
2043
|
+
const extensions = new Set(
|
|
2044
|
+
(Array.isArray(requirement.extensions) ? requirement.extensions : [])
|
|
2045
|
+
.map((item) => String(item || "").toLocaleLowerCase("en-US"))
|
|
2046
|
+
);
|
|
2047
|
+
if (!extensions.has(extension)) return false;
|
|
2048
|
+
if (requirement.kind === "visual-preview") {
|
|
2049
|
+
return /(?:preview|contact[-_ ]?sheet|overview|thumbnail|render)/i.test(basename);
|
|
2050
|
+
}
|
|
2051
|
+
return true;
|
|
2052
|
+
}
|
|
2053
|
+
if (requirement.kind === "practice-material") {
|
|
2054
|
+
return /(?:practice|worksheet|exercise|handout|activity)/i.test(basename) &&
|
|
2055
|
+
/\.(?:docx|html?|md|odt|pdf|tex|txt)$/i.test(extension);
|
|
2056
|
+
}
|
|
2057
|
+
if (requirement.kind === "answer-material") {
|
|
2058
|
+
return /(?:answer|solution|worked[-_ ]?answers?)/i.test(basename) &&
|
|
2059
|
+
/\.(?:docx|html?|md|odt|pdf|tex|txt)$/i.test(extension);
|
|
2060
|
+
}
|
|
2061
|
+
if (requirement.kind === "reproducible-build-entrypoint") {
|
|
2062
|
+
return (
|
|
2063
|
+
/^(?:makefile|justfile)$/i.test(basename) ||
|
|
2064
|
+
/(?:^|[-_])(?:build|compile|export|generate|render)(?:[-_.]|$)/i.test(basename)
|
|
2065
|
+
) && /(?:^makefile$|^justfile$|\.(?:cjs|js|mjs|py|sh|ts)$)/i.test(basename);
|
|
2066
|
+
}
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
export function evaluateRequestedArtifactRequirements(
|
|
2071
|
+
contract = {},
|
|
2072
|
+
{ commandCwd = process.cwd(), state = {} } = {}
|
|
2073
|
+
) {
|
|
2074
|
+
const requirements = Array.isArray(contract.requiredArtifactKinds)
|
|
2075
|
+
? contract.requiredArtifactKinds
|
|
2076
|
+
: [];
|
|
2077
|
+
if (!requirements.length) {
|
|
2078
|
+
return {
|
|
2079
|
+
ok: true,
|
|
2080
|
+
checked: false,
|
|
2081
|
+
requirements: [],
|
|
2082
|
+
satisfied: [],
|
|
2083
|
+
missing: [],
|
|
2084
|
+
candidates: [],
|
|
2085
|
+
reason: "No semantic artifact-set contract was inferred.",
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
const candidates = collectRequestedArtifactCandidates(commandCwd, state);
|
|
2089
|
+
const requireFresh = contract.requiresFileMutation === true;
|
|
2090
|
+
const usableCandidates = requireFresh
|
|
2091
|
+
? candidates.filter((candidate) => candidate.fresh)
|
|
2092
|
+
: candidates;
|
|
2093
|
+
const satisfied = [];
|
|
2094
|
+
const missing = [];
|
|
2095
|
+
for (const requirement of requirements) {
|
|
2096
|
+
const matches = usableCandidates.filter((candidate) =>
|
|
2097
|
+
requestedArtifactRequirementMatches(requirement, candidate)
|
|
2098
|
+
);
|
|
2099
|
+
if (matches.length) {
|
|
2100
|
+
satisfied.push({
|
|
2101
|
+
...requirement,
|
|
2102
|
+
paths: matches.map((candidate) => candidate.path).slice(0, 12),
|
|
2103
|
+
});
|
|
2104
|
+
} else {
|
|
2105
|
+
missing.push(requirement);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
return {
|
|
2109
|
+
ok: missing.length === 0,
|
|
2110
|
+
checked: true,
|
|
2111
|
+
requirements,
|
|
2112
|
+
satisfied,
|
|
2113
|
+
missing,
|
|
2114
|
+
candidates: usableCandidates.slice(0, 120),
|
|
2115
|
+
reason: missing.length
|
|
2116
|
+
? `Missing requested artifact deliverables: ${missing
|
|
2117
|
+
.map((item) => item.description || item.id)
|
|
2118
|
+
.join(", ")}.`
|
|
2119
|
+
: "Every requested artifact format and role has a fresh workspace deliverable.",
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
|
|
1820
2123
|
const PROJECT_SOURCE_PATH_PATTERN =
|
|
1821
2124
|
/\.(?:c|cc|cpp|cxx|h|hh|hpp|cs|go|java|js|jsx|ts|tsx|mjs|cjs|kt|kts|php|py|r|rb|rs|scala|sh|sql|swift|vue|svelte|jl)$/i;
|
|
1822
2125
|
const PROJECT_SOURCE_MANIFEST_PATTERN =
|
|
@@ -2993,11 +3296,16 @@ export function evaluateScsSemanticContract(
|
|
|
2993
3296
|
const forbiddenTextTerms = Array.isArray(contract.forbiddenTextTerms) ? contract.forbiddenTextTerms : [];
|
|
2994
3297
|
const projectTestVerification = evaluateProjectTestVerification(events);
|
|
2995
3298
|
const executable = evaluateRequiredExecutableTerms(contract, { commandCwd, events, state });
|
|
3299
|
+
const requestedArtifacts = evaluateRequestedArtifactRequirements(contract, {
|
|
3300
|
+
commandCwd,
|
|
3301
|
+
state,
|
|
3302
|
+
});
|
|
2996
3303
|
if (
|
|
2997
3304
|
!exactOutputPaths.length &&
|
|
2998
3305
|
!requiredTextTerms.length &&
|
|
2999
3306
|
!requiredExecutableTerms.length &&
|
|
3000
|
-
!forbiddenTextTerms.length
|
|
3307
|
+
!forbiddenTextTerms.length &&
|
|
3308
|
+
!requestedArtifacts.checked
|
|
3001
3309
|
) {
|
|
3002
3310
|
return projectTestVerification.checked
|
|
3003
3311
|
? {
|
|
@@ -3009,18 +3317,23 @@ export function evaluateScsSemanticContract(
|
|
|
3009
3317
|
: { ok: true, checked: false, projectTestVerification, reason: "No semantic file contract was inferred." };
|
|
3010
3318
|
}
|
|
3011
3319
|
if (!exactOutputPaths.length) {
|
|
3012
|
-
const ok = executable.ok && projectTestVerification.ok;
|
|
3320
|
+
const ok = executable.ok && projectTestVerification.ok && requestedArtifacts.ok;
|
|
3013
3321
|
return {
|
|
3014
3322
|
ok,
|
|
3015
|
-
checked: executable.checked || projectTestVerification.checked,
|
|
3016
|
-
reason: !
|
|
3323
|
+
checked: executable.checked || projectTestVerification.checked || requestedArtifacts.checked,
|
|
3324
|
+
reason: !requestedArtifacts.ok
|
|
3325
|
+
? requestedArtifacts.reason
|
|
3326
|
+
: !executable.ok
|
|
3017
3327
|
? executable.reason
|
|
3018
3328
|
: !projectTestVerification.ok
|
|
3019
3329
|
? projectTestVerification.reason
|
|
3020
|
-
:
|
|
3330
|
+
: requestedArtifacts.checked
|
|
3331
|
+
? requestedArtifacts.reason
|
|
3332
|
+
: "Semantic text terms were inferred, but no exact output path was inferred for deterministic file inspection.",
|
|
3021
3333
|
requiredTextTerms,
|
|
3022
3334
|
requiredExecutableTerms,
|
|
3023
3335
|
forbiddenTextTerms,
|
|
3336
|
+
requestedArtifacts,
|
|
3024
3337
|
missingExecutableTerms: executable.missingExecutableTerms,
|
|
3025
3338
|
executableSourcePaths: executable.executableSourcePaths,
|
|
3026
3339
|
executable,
|
|
@@ -3048,7 +3361,8 @@ export function evaluateScsSemanticContract(
|
|
|
3048
3361
|
presentForbiddenText.length === 0 &&
|
|
3049
3362
|
executable.ok &&
|
|
3050
3363
|
sourceGrounding.ok &&
|
|
3051
|
-
projectTestVerification.ok
|
|
3364
|
+
projectTestVerification.ok &&
|
|
3365
|
+
requestedArtifacts.ok;
|
|
3052
3366
|
return {
|
|
3053
3367
|
ok,
|
|
3054
3368
|
checked: true,
|
|
@@ -3056,6 +3370,7 @@ export function evaluateScsSemanticContract(
|
|
|
3056
3370
|
requiredTextTerms,
|
|
3057
3371
|
requiredExecutableTerms,
|
|
3058
3372
|
forbiddenTextTerms,
|
|
3373
|
+
requestedArtifacts,
|
|
3059
3374
|
missingFiles,
|
|
3060
3375
|
missingRequiredText,
|
|
3061
3376
|
missingExecutableTerms: executable.missingExecutableTerms,
|
|
@@ -3085,6 +3400,7 @@ export function evaluateScsSemanticContract(
|
|
|
3085
3400
|
presentForbiddenText.length ? `Forbidden text terms present: ${presentForbiddenText.join(", ")}` : "",
|
|
3086
3401
|
!sourceGrounding.ok ? sourceGrounding.reason : "",
|
|
3087
3402
|
!projectTestVerification.ok ? projectTestVerification.reason : "",
|
|
3403
|
+
!requestedArtifacts.ok ? requestedArtifacts.reason : "",
|
|
3088
3404
|
]
|
|
3089
3405
|
.filter(Boolean)
|
|
3090
3406
|
.join("; "),
|
|
@@ -3619,6 +3935,7 @@ export function summarizeScsContractEvidence({ contract = {}, ledger = {}, evalu
|
|
|
3619
3935
|
})),
|
|
3620
3936
|
forbiddenActions: contract.forbiddenActions || [],
|
|
3621
3937
|
exactOutputPaths: contract.exactOutputPaths || [],
|
|
3938
|
+
requiredArtifactKinds: contract.requiredArtifactKinds || [],
|
|
3622
3939
|
exactInputPaths: contract.exactInputPaths || [],
|
|
3623
3940
|
requiredToolCalls: contract.requiredToolCalls || [],
|
|
3624
3941
|
requiredGitActions: contract.requiredGitActions || [],
|