@lazyingart/agintiflow 0.20.258 → 0.20.260
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.
|
|
3
|
+
"version": "0.20.260",
|
|
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",
|
|
@@ -2618,6 +2618,114 @@ try {
|
|
|
2618
2618
|
JSON.stringify(["npm test"]),
|
|
2619
2619
|
"console output lines were mistaken for required shell commands"
|
|
2620
2620
|
);
|
|
2621
|
+
const operatorGuideMarkdown = [
|
|
2622
|
+
"# Service",
|
|
2623
|
+
"",
|
|
2624
|
+
"The controller must provide start, status, restart, and stop.",
|
|
2625
|
+
"",
|
|
2626
|
+
"## Operator Guidance",
|
|
2627
|
+
"",
|
|
2628
|
+
"### Start the service",
|
|
2629
|
+
"",
|
|
2630
|
+
"```bash",
|
|
2631
|
+
"python3 service_ctl.py start --state-dir .runtime",
|
|
2632
|
+
"```",
|
|
2633
|
+
"",
|
|
2634
|
+
"### Check service status",
|
|
2635
|
+
"",
|
|
2636
|
+
"```bash",
|
|
2637
|
+
"python3 service_ctl.py status --state-dir .runtime",
|
|
2638
|
+
"```",
|
|
2639
|
+
"",
|
|
2640
|
+
"## Running Tests",
|
|
2641
|
+
"",
|
|
2642
|
+
"Run the visible regression suite with:",
|
|
2643
|
+
"",
|
|
2644
|
+
"```bash",
|
|
2645
|
+
"python3 -m unittest discover -s tests -v",
|
|
2646
|
+
"```",
|
|
2647
|
+
].join("\n");
|
|
2648
|
+
assert(
|
|
2649
|
+
JSON.stringify(projectAcceptanceFromMarkdown(operatorGuideMarkdown, "README.md").requiredCommands) ===
|
|
2650
|
+
JSON.stringify(["python3 -m unittest discover -s tests -v"]),
|
|
2651
|
+
"ordinary operator examples became mandatory acceptance commands"
|
|
2652
|
+
);
|
|
2653
|
+
const refreshedAcceptanceState = {
|
|
2654
|
+
commandCwd: workspace,
|
|
2655
|
+
meta: {
|
|
2656
|
+
projectVerification: {
|
|
2657
|
+
requiredOutputs: ["stale-output.txt"],
|
|
2658
|
+
requiredCommands: [
|
|
2659
|
+
"python3 service_ctl.py start --state-dir .runtime",
|
|
2660
|
+
"python3 service_ctl.py status --state-dir .runtime",
|
|
2661
|
+
],
|
|
2662
|
+
acceptanceSource: "README.md",
|
|
2663
|
+
},
|
|
2664
|
+
},
|
|
2665
|
+
};
|
|
2666
|
+
recordProjectVerificationOutcome(
|
|
2667
|
+
refreshedAcceptanceState,
|
|
2668
|
+
{
|
|
2669
|
+
ok: true,
|
|
2670
|
+
toolName: "read_file",
|
|
2671
|
+
path: "README.md",
|
|
2672
|
+
content: operatorGuideMarkdown,
|
|
2673
|
+
},
|
|
2674
|
+
{ commandCwd: workspace, taskProfile: "devops" }
|
|
2675
|
+
);
|
|
2676
|
+
assert(
|
|
2677
|
+
JSON.stringify(refreshedAcceptanceState.meta.projectVerification.requiredCommands) ===
|
|
2678
|
+
JSON.stringify(["python3 -m unittest discover -s tests -v"]) &&
|
|
2679
|
+
refreshedAcceptanceState.meta.projectVerification.requiredOutputs.length === 0,
|
|
2680
|
+
"rereading one authoritative source retained its stale acceptance requirements"
|
|
2681
|
+
);
|
|
2682
|
+
const resumedAcceptanceWorkspace = path.join(workspace, "resumed-acceptance");
|
|
2683
|
+
await fs.mkdir(resumedAcceptanceWorkspace, { recursive: true });
|
|
2684
|
+
await fs.writeFile(
|
|
2685
|
+
path.join(resumedAcceptanceWorkspace, "README.md"),
|
|
2686
|
+
operatorGuideMarkdown,
|
|
2687
|
+
"utf8"
|
|
2688
|
+
);
|
|
2689
|
+
const resumedAcceptanceState = {
|
|
2690
|
+
commandCwd: resumedAcceptanceWorkspace,
|
|
2691
|
+
goal: "Repair the service and run its tests.",
|
|
2692
|
+
meta: {
|
|
2693
|
+
goalContract: { revision: 4, currentRequest: "Repair the service and run its tests." },
|
|
2694
|
+
projectVerification: {
|
|
2695
|
+
requiredCommands: [
|
|
2696
|
+
"python3 service_ctl.py start --state-dir .runtime",
|
|
2697
|
+
"python3 service_ctl.py status --state-dir .runtime",
|
|
2698
|
+
],
|
|
2699
|
+
acceptanceSource: "README.md",
|
|
2700
|
+
},
|
|
2701
|
+
},
|
|
2702
|
+
};
|
|
2703
|
+
resetSameTaskExecutionContract(resumedAcceptanceState, 4);
|
|
2704
|
+
assert(
|
|
2705
|
+
JSON.stringify(resumedAcceptanceState.meta.projectVerification.requiredCommands) ===
|
|
2706
|
+
JSON.stringify(["python3 -m unittest discover -s tests -v"]),
|
|
2707
|
+
"same-task resume retained stale requirements from an older README parser"
|
|
2708
|
+
);
|
|
2709
|
+
recordProjectVerificationOutcome(
|
|
2710
|
+
refreshedAcceptanceState,
|
|
2711
|
+
{
|
|
2712
|
+
ok: true,
|
|
2713
|
+
toolName: "read_file",
|
|
2714
|
+
path: "TASK.md",
|
|
2715
|
+
content: requiredCommandMarkdown,
|
|
2716
|
+
},
|
|
2717
|
+
{ commandCwd: workspace, taskProfile: "devops" }
|
|
2718
|
+
);
|
|
2719
|
+
assert(
|
|
2720
|
+
JSON.stringify(refreshedAcceptanceState.meta.projectVerification.requiredCommands) ===
|
|
2721
|
+
JSON.stringify([
|
|
2722
|
+
"python3 -m unittest discover -s tests -v",
|
|
2723
|
+
"npm run check",
|
|
2724
|
+
"npm test",
|
|
2725
|
+
"python scripts/verify.py --strict",
|
|
2726
|
+
]),
|
|
2727
|
+
"source-scoped acceptance refresh discarded a distinct authoritative source"
|
|
2728
|
+
);
|
|
2621
2729
|
const operatorContinuedCommandMarkdown = [
|
|
2622
2730
|
"# Task",
|
|
2623
2731
|
"",
|
|
@@ -60,6 +60,14 @@ const coordinatedForbiddenOutputContract = deriveScsTaskContract({
|
|
|
60
60
|
taskProfile: "devops",
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
const recoveryInstructionContract = deriveScsTaskContract({
|
|
64
|
+
goal: [
|
|
65
|
+
"Use the retained source and these two failures: the tests invoke ../service_ctl.py, while `python3 service_ctl.py start --state-dir .runtime` fails. Preserve the lifecycle assertions rather than replacing them, repair service_ctl.py, and remove the accidental untracked resume-after-git-baseline-recovery-dev-prompt.txt.",
|
|
66
|
+
"Then run `python3 -m unittest discover -s tests -v`, followed by `PYTHONDONTWRITEBYTECODE=1 python3 /tmp/devops_sensor_gateway_contract.py`. Do not use LocalLLM or change provider.",
|
|
67
|
+
].join("\n"),
|
|
68
|
+
taskProfile: "devops",
|
|
69
|
+
});
|
|
70
|
+
|
|
63
71
|
const readOnlyReviewContract = deriveScsTaskContract({
|
|
64
72
|
goal: [
|
|
65
73
|
"Review focus: changed files only.",
|
|
@@ -136,6 +144,33 @@ assert.deepEqual(
|
|
|
136
144
|
["scratch.py", "temporary.py"],
|
|
137
145
|
"a coordinated list did not retain each path governed by one exclusion"
|
|
138
146
|
);
|
|
147
|
+
assert(
|
|
148
|
+
recoveryInstructionContract.excludedOutputPaths.includes("resume-after-git-baseline-recovery-dev-prompt.txt") &&
|
|
149
|
+
!recoveryInstructionContract.exactOutputPaths.includes("resume-after-git-baseline-recovery-dev-prompt.txt"),
|
|
150
|
+
"an explicit deletion target became a required output"
|
|
151
|
+
);
|
|
152
|
+
assert.deepEqual(
|
|
153
|
+
recoveryInstructionContract.requiredTextTerms,
|
|
154
|
+
[],
|
|
155
|
+
"verification commands became required document prose"
|
|
156
|
+
);
|
|
157
|
+
assert.deepEqual(
|
|
158
|
+
recoveryInstructionContract.requiredExecutableTerms,
|
|
159
|
+
[],
|
|
160
|
+
"an environment assignment inside a verification command became required production source"
|
|
161
|
+
);
|
|
162
|
+
assert(
|
|
163
|
+
!recoveryInstructionContract.exactInputPaths.some((item) => item.includes("PYTHONDONTWRITEBYTECODE")),
|
|
164
|
+
"a quoted verification command became an input path"
|
|
165
|
+
);
|
|
166
|
+
assert.deepEqual(
|
|
167
|
+
recoveryInstructionContract.requiredProjectCommands,
|
|
168
|
+
[
|
|
169
|
+
"python3 -m unittest discover -s tests -v",
|
|
170
|
+
"PYTHONDONTWRITEBYTECODE=1 python3 /tmp/devops_sensor_gateway_contract.py",
|
|
171
|
+
],
|
|
172
|
+
"a verification command introduced by 'followed by' was not retained"
|
|
173
|
+
);
|
|
139
174
|
assert.equal(
|
|
140
175
|
readOnlyReviewContract.requiresWorkspaceMutation,
|
|
141
176
|
false,
|
package/src/agent-runner.js
CHANGED
|
@@ -3769,6 +3769,67 @@ export function resetGoalScopedRuntimeState(state = {}) {
|
|
|
3769
3769
|
return removed;
|
|
3770
3770
|
}
|
|
3771
3771
|
|
|
3772
|
+
function refreshPersistedProjectAcceptance(state = {}) {
|
|
3773
|
+
const verification = state.meta?.projectVerification;
|
|
3774
|
+
if (!verification || typeof verification !== "object") return false;
|
|
3775
|
+
const priorBySource =
|
|
3776
|
+
verification.acceptanceBySource &&
|
|
3777
|
+
typeof verification.acceptanceBySource === "object" &&
|
|
3778
|
+
!Array.isArray(verification.acceptanceBySource)
|
|
3779
|
+
? verification.acceptanceBySource
|
|
3780
|
+
: {};
|
|
3781
|
+
const sources = [
|
|
3782
|
+
...Object.keys(priorBySource),
|
|
3783
|
+
String(verification.acceptanceSource || "").trim(),
|
|
3784
|
+
].filter(Boolean);
|
|
3785
|
+
if (!sources.length) return false;
|
|
3786
|
+
|
|
3787
|
+
const commandCwd = path.resolve(state.commandCwd || process.cwd());
|
|
3788
|
+
const refreshed = {};
|
|
3789
|
+
let inspected = 0;
|
|
3790
|
+
for (const source of [...new Set(sources)].slice(-16)) {
|
|
3791
|
+
const sourceKey = normalizedProjectAcceptancePath(source, commandCwd);
|
|
3792
|
+
if (!sourceKey || !projectAcceptanceSourceIsAuthoritative(sourceKey, {
|
|
3793
|
+
commandCwd,
|
|
3794
|
+
authoritativePaths: [sourceKey],
|
|
3795
|
+
})) {
|
|
3796
|
+
continue;
|
|
3797
|
+
}
|
|
3798
|
+
const absolutePath = path.resolve(commandCwd, sourceKey);
|
|
3799
|
+
if (absolutePath !== commandCwd && !absolutePath.startsWith(`${commandCwd}${path.sep}`)) continue;
|
|
3800
|
+
inspected += 1;
|
|
3801
|
+
if (!fsSync.existsSync(absolutePath)) continue;
|
|
3802
|
+
try {
|
|
3803
|
+
const acceptance = projectAcceptanceFromMarkdown(
|
|
3804
|
+
fsSync.readFileSync(absolutePath, "utf8"),
|
|
3805
|
+
sourceKey,
|
|
3806
|
+
{ commandCwd, authoritativePaths: [sourceKey] }
|
|
3807
|
+
);
|
|
3808
|
+
refreshed[sourceKey] = {
|
|
3809
|
+
requiredOutputs: acceptance.requiredOutputs,
|
|
3810
|
+
requiredCommands: acceptance.requiredCommands,
|
|
3811
|
+
readAt: new Date().toISOString(),
|
|
3812
|
+
};
|
|
3813
|
+
} catch {
|
|
3814
|
+
inspected -= 1;
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
if (!inspected) return false;
|
|
3818
|
+
verification.acceptanceBySource = refreshed;
|
|
3819
|
+
verification.requiredOutputs = [
|
|
3820
|
+
...new Set(
|
|
3821
|
+
Object.values(refreshed).flatMap((item) => item.requiredOutputs || [])
|
|
3822
|
+
),
|
|
3823
|
+
].slice(0, 64);
|
|
3824
|
+
verification.requiredCommands = [
|
|
3825
|
+
...new Set(
|
|
3826
|
+
Object.values(refreshed).flatMap((item) => item.requiredCommands || [])
|
|
3827
|
+
),
|
|
3828
|
+
].slice(0, 24);
|
|
3829
|
+
verification.acceptanceRefreshedAt = new Date().toISOString();
|
|
3830
|
+
return true;
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3772
3833
|
export function resetSameTaskExecutionContract(state = {}, revision = 0) {
|
|
3773
3834
|
state.meta = state.meta || {};
|
|
3774
3835
|
const keys = [
|
|
@@ -3786,6 +3847,7 @@ export function resetSameTaskExecutionContract(state = {}, revision = 0) {
|
|
|
3786
3847
|
removed.push(key);
|
|
3787
3848
|
}
|
|
3788
3849
|
state.plan = "";
|
|
3850
|
+
refreshPersistedProjectAcceptance(state);
|
|
3789
3851
|
const activeRevision = Math.max(0, Number(revision || state.meta?.goalContract?.revision || 0));
|
|
3790
3852
|
const currentRequest = String(state.meta?.goalContract?.currentRequest || state.goal || "").trim();
|
|
3791
3853
|
const currentTurnContract = deriveScsTaskContract({
|
|
@@ -5281,20 +5343,59 @@ function canonicalRequiredFenceCommand(value = "") {
|
|
|
5281
5343
|
);
|
|
5282
5344
|
}
|
|
5283
5345
|
|
|
5346
|
+
function markdownFenceRequiresCommandExecution(content = "", fenceIndex = 0) {
|
|
5347
|
+
const lines = String(content || "")
|
|
5348
|
+
.slice(0, Math.max(0, Number(fenceIndex || 0)))
|
|
5349
|
+
.split(/\r?\n/);
|
|
5350
|
+
while (lines.length && !lines.at(-1).trim()) lines.pop();
|
|
5351
|
+
if (!lines.length) return false;
|
|
5352
|
+
|
|
5353
|
+
const nearest = String(lines.at(-1) || "").trim();
|
|
5354
|
+
const heading = nearest.match(/^#{1,6}\s+(.+?)\s*#*$/);
|
|
5355
|
+
if (heading) {
|
|
5356
|
+
return /\b(?:acceptance|tests?|validation|verification)\b|(?:验收|驗收|测试|測試|验证|驗證)|(?:受入|テスト|検証)/iu.test(
|
|
5357
|
+
heading[1]
|
|
5358
|
+
);
|
|
5359
|
+
}
|
|
5360
|
+
|
|
5361
|
+
const paragraph = [];
|
|
5362
|
+
while (lines.length && paragraph.length < 5) {
|
|
5363
|
+
const line = String(lines.pop() || "").trim();
|
|
5364
|
+
if (!line || /^#{1,6}\s+/.test(line) || /^```/.test(line)) break;
|
|
5365
|
+
paragraph.unshift(line);
|
|
5366
|
+
}
|
|
5367
|
+
const cue = paragraph.join(" ").trim();
|
|
5368
|
+
if (!cue) return false;
|
|
5369
|
+
|
|
5370
|
+
const execution = /\b(?:commands?|execute|invoke|launch|regenerate|run|tests?|validate|validation|verify|verification)\b/iu.test(
|
|
5371
|
+
cue
|
|
5372
|
+
);
|
|
5373
|
+
const obligation = /\b(?:before\s+completion|must|required|shall|should)\b/iu.test(cue);
|
|
5374
|
+
const directImperative = /^(?:please\s+)?(?:execute|invoke|launch|regenerate|run|validate|verify)\b/iu.test(cue);
|
|
5375
|
+
const validationLead = /^(?:to\s+(?:check|test|validate|verify)\b|(?:required|mandatory)\s+(?:checks?|commands?|tests?|validation|verification)\b)/iu.test(
|
|
5376
|
+
cue
|
|
5377
|
+
);
|
|
5378
|
+
const cjkExecution = /(?:运行|運行|执行|執行|验证|驗證|检查|檢查|测试|測試|验收|驗收|実行|検証|確認|テスト)/u.test(cue);
|
|
5379
|
+
const cjkObligation = /(?:必须|必須|需要|应当|應當|完成前|完了前)/u.test(cue);
|
|
5380
|
+
const cjkDirectImperative = /^(?:请|請)?(?:运行|運行|执行|執行|验证|驗證|检查|檢查|测试|測試|验收|驗收|実行|検証|確認|テスト)/u.test(
|
|
5381
|
+
cue
|
|
5382
|
+
);
|
|
5383
|
+
return (
|
|
5384
|
+
(execution && obligation) ||
|
|
5385
|
+
directImperative ||
|
|
5386
|
+
validationLead ||
|
|
5387
|
+
(cjkExecution && cjkObligation) ||
|
|
5388
|
+
cjkDirectImperative
|
|
5389
|
+
);
|
|
5390
|
+
}
|
|
5391
|
+
|
|
5284
5392
|
function markdownRequiredCommands(content = "") {
|
|
5285
5393
|
const text = String(content || "");
|
|
5286
5394
|
const commands = [];
|
|
5287
5395
|
for (const match of text.matchAll(/```([A-Za-z0-9_-]*)[ \t]*\n([\s\S]*?)```/gi)) {
|
|
5288
5396
|
const language = String(match[1] || "").toLowerCase();
|
|
5289
5397
|
if (language && !["bash", "sh", "shell", "console"].includes(language)) continue;
|
|
5290
|
-
|
|
5291
|
-
if (
|
|
5292
|
-
!/(?:run|running|execute|command|verify|validation|regenerate)[^\n.]{0,180}(?:must|required|should|use|run|regenerate)|(?:必须|需要|应当|运行|执行|验证)[^。\n]{0,180}(?:命令|生成|输出|交付)/i.test(
|
|
5293
|
-
before
|
|
5294
|
-
)
|
|
5295
|
-
) {
|
|
5296
|
-
continue;
|
|
5297
|
-
}
|
|
5398
|
+
if (!markdownFenceRequiresCommandExecution(text, match.index)) continue;
|
|
5298
5399
|
const body = String(match[2] || "");
|
|
5299
5400
|
const fenceCommandStart = commands.length;
|
|
5300
5401
|
const context = [];
|
|
@@ -5342,11 +5443,9 @@ function normalizedProjectAcceptancePath(sourcePath = "", commandCwd = "") {
|
|
|
5342
5443
|
return raw.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
5343
5444
|
}
|
|
5344
5445
|
|
|
5345
|
-
|
|
5446
|
+
function projectAcceptanceSourceIsAuthoritative(sourcePath = "", options = {}) {
|
|
5346
5447
|
const basename = path.basename(String(sourcePath || "")).toLowerCase();
|
|
5347
|
-
if (!/^(?:readme|task|agents?|aginti)(?:\.[^.]+)?$/.test(basename))
|
|
5348
|
-
return { requiredOutputs: [], requiredCommands: [] };
|
|
5349
|
-
}
|
|
5448
|
+
if (!/^(?:readme|task|agents?|aginti)(?:\.[^.]+)?$/.test(basename)) return false;
|
|
5350
5449
|
const normalizedSource = normalizedProjectAcceptancePath(
|
|
5351
5450
|
sourcePath,
|
|
5352
5451
|
options.commandCwd
|
|
@@ -5356,10 +5455,11 @@ export function projectAcceptanceFromMarkdown(content = "", sourcePath = "", opt
|
|
|
5356
5455
|
.map((item) => normalizedProjectAcceptancePath(item, options.commandCwd))
|
|
5357
5456
|
.filter(Boolean)
|
|
5358
5457
|
);
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5458
|
+
return !normalizedSource.includes("/") || authoritativePaths.has(normalizedSource);
|
|
5459
|
+
}
|
|
5460
|
+
|
|
5461
|
+
export function projectAcceptanceFromMarkdown(content = "", sourcePath = "", options = {}) {
|
|
5462
|
+
if (!projectAcceptanceSourceIsAuthoritative(sourcePath, options)) {
|
|
5363
5463
|
return { requiredOutputs: [], requiredCommands: [] };
|
|
5364
5464
|
}
|
|
5365
5465
|
return {
|
|
@@ -5739,29 +5839,72 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
|
|
|
5739
5839
|
goal: completionContractGoal(config, state),
|
|
5740
5840
|
taskProfile: config.taskProfile || state.meta?.taskProfile || "auto",
|
|
5741
5841
|
});
|
|
5842
|
+
const sourcePath = String(toolResult.path || toolResult.args?.path || "");
|
|
5843
|
+
const acceptanceOptions = {
|
|
5844
|
+
commandCwd: config.commandCwd || state.commandCwd || process.cwd(),
|
|
5845
|
+
authoritativePaths: [
|
|
5846
|
+
...(Array.isArray(currentTaskContract.exactInputPaths)
|
|
5847
|
+
? currentTaskContract.exactInputPaths
|
|
5848
|
+
: []),
|
|
5849
|
+
...(Array.isArray(currentTaskContract.exactOutputPaths)
|
|
5850
|
+
? currentTaskContract.exactOutputPaths
|
|
5851
|
+
: []),
|
|
5852
|
+
],
|
|
5853
|
+
};
|
|
5742
5854
|
const acceptance = projectAcceptanceFromMarkdown(
|
|
5743
5855
|
toolResult.content,
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
commandCwd: config.commandCwd || state.commandCwd || process.cwd(),
|
|
5747
|
-
authoritativePaths: [
|
|
5748
|
-
...(Array.isArray(currentTaskContract.exactInputPaths)
|
|
5749
|
-
? currentTaskContract.exactInputPaths
|
|
5750
|
-
: []),
|
|
5751
|
-
...(Array.isArray(currentTaskContract.exactOutputPaths)
|
|
5752
|
-
? currentTaskContract.exactOutputPaths
|
|
5753
|
-
: []),
|
|
5754
|
-
],
|
|
5755
|
-
}
|
|
5856
|
+
sourcePath,
|
|
5857
|
+
acceptanceOptions
|
|
5756
5858
|
);
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5859
|
+
if (projectAcceptanceSourceIsAuthoritative(sourcePath, acceptanceOptions)) {
|
|
5860
|
+
const sourceKey = normalizedProjectAcceptancePath(
|
|
5861
|
+
sourcePath,
|
|
5862
|
+
acceptanceOptions.commandCwd
|
|
5863
|
+
);
|
|
5864
|
+
const acceptanceBySource =
|
|
5865
|
+
verification.acceptanceBySource &&
|
|
5866
|
+
typeof verification.acceptanceBySource === "object" &&
|
|
5867
|
+
!Array.isArray(verification.acceptanceBySource)
|
|
5868
|
+
? { ...verification.acceptanceBySource }
|
|
5869
|
+
: {};
|
|
5870
|
+
const legacySourceKey = normalizedProjectAcceptancePath(
|
|
5871
|
+
verification.acceptanceSource || "",
|
|
5872
|
+
acceptanceOptions.commandCwd
|
|
5873
|
+
);
|
|
5874
|
+
if (
|
|
5875
|
+
!Object.keys(acceptanceBySource).length &&
|
|
5876
|
+
legacySourceKey &&
|
|
5877
|
+
legacySourceKey !== sourceKey
|
|
5878
|
+
) {
|
|
5879
|
+
acceptanceBySource[legacySourceKey] = {
|
|
5880
|
+
requiredOutputs: [...verification.requiredOutputs],
|
|
5881
|
+
requiredCommands: [...verification.requiredCommands],
|
|
5882
|
+
readAt: verification.acceptanceReadAt || "",
|
|
5883
|
+
};
|
|
5884
|
+
}
|
|
5885
|
+
acceptanceBySource[sourceKey] = {
|
|
5886
|
+
requiredOutputs: acceptance.requiredOutputs,
|
|
5887
|
+
requiredCommands: acceptance.requiredCommands,
|
|
5888
|
+
readAt: now,
|
|
5889
|
+
};
|
|
5890
|
+
verification.acceptanceBySource = Object.fromEntries(
|
|
5891
|
+
Object.entries(acceptanceBySource).slice(-16)
|
|
5892
|
+
);
|
|
5893
|
+
verification.requiredOutputs = [
|
|
5894
|
+
...new Set(
|
|
5895
|
+
Object.values(verification.acceptanceBySource).flatMap((item) =>
|
|
5896
|
+
Array.isArray(item?.requiredOutputs) ? item.requiredOutputs : []
|
|
5897
|
+
)
|
|
5898
|
+
),
|
|
5899
|
+
].slice(0, 64);
|
|
5900
|
+
verification.requiredCommands = [
|
|
5901
|
+
...new Set(
|
|
5902
|
+
Object.values(verification.acceptanceBySource).flatMap((item) =>
|
|
5903
|
+
Array.isArray(item?.requiredCommands) ? item.requiredCommands : []
|
|
5904
|
+
)
|
|
5905
|
+
),
|
|
5906
|
+
].slice(0, 24);
|
|
5907
|
+
verification.acceptanceSource = sourcePath;
|
|
5765
5908
|
verification.acceptanceReadAt = now;
|
|
5766
5909
|
}
|
|
5767
5910
|
}
|
package/src/scs-evidence.js
CHANGED
|
@@ -326,6 +326,56 @@ function quotedTerms(text = "") {
|
|
|
326
326
|
return terms;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
function looksLikeShellCommandLiteral(value = "") {
|
|
330
|
+
const text = String(value || "").trim();
|
|
331
|
+
if (!text || /[\r\n]/.test(text)) return false;
|
|
332
|
+
const command = text.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)+/, "");
|
|
333
|
+
const tokens = tokenizeShellWords(command);
|
|
334
|
+
if (!tokens.length) return false;
|
|
335
|
+
const executable = path.basename(String(tokens[0] || "")).toLowerCase();
|
|
336
|
+
return new Set([
|
|
337
|
+
"aginti",
|
|
338
|
+
"bash",
|
|
339
|
+
"bun",
|
|
340
|
+
"cargo",
|
|
341
|
+
"cmake",
|
|
342
|
+
"curl",
|
|
343
|
+
"dotnet",
|
|
344
|
+
"ffmpeg",
|
|
345
|
+
"git",
|
|
346
|
+
"gh",
|
|
347
|
+
"go",
|
|
348
|
+
"java",
|
|
349
|
+
"javac",
|
|
350
|
+
"latexmk",
|
|
351
|
+
"make",
|
|
352
|
+
"node",
|
|
353
|
+
"npm",
|
|
354
|
+
"npx",
|
|
355
|
+
"perl",
|
|
356
|
+
"php",
|
|
357
|
+
"pnpm",
|
|
358
|
+
"pytest",
|
|
359
|
+
"python",
|
|
360
|
+
"python3",
|
|
361
|
+
"ruby",
|
|
362
|
+
"sh",
|
|
363
|
+
"wget",
|
|
364
|
+
"xelatex",
|
|
365
|
+
"yarn",
|
|
366
|
+
"zsh",
|
|
367
|
+
]).has(executable);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function indexFallsInsideInlineCommand(source = "", index = 0) {
|
|
371
|
+
for (const match of String(source || "").matchAll(/`([^`\r\n]+)`/g)) {
|
|
372
|
+
const start = Number(match.index || 0);
|
|
373
|
+
const end = start + String(match[0] || "").length;
|
|
374
|
+
if (index >= start && index < end && looksLikeShellCommandLiteral(match[1])) return true;
|
|
375
|
+
}
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
329
379
|
function splitInlineTerms(text = "") {
|
|
330
380
|
return String(text || "")
|
|
331
381
|
.split(/[、,,;;]/)
|
|
@@ -515,6 +565,10 @@ export function inferExplicitlyExcludedOutputPaths(goal = "") {
|
|
|
515
565
|
/(?:不要|不得|禁止|无需|不需要)[^。!?;\n]{0,140}(?:运行|执行|重跑|创建|建立|写入|生成|保存|输出|修改|编辑|提交|暂存)/u;
|
|
516
566
|
const cjkNegativeActionAfter =
|
|
517
567
|
/^(?:(?:不得|禁止|不要)(?:被)?(?:运行|执行|重跑|创建|建立|写入|生成|保存|输出|修改|编辑|提交|暂存)|(?:を)?(?:作成|生成|実行|再実行|保存|編集|変更|コミット)[^。!?;\n]{0,40}(?:しない|しなくてよい|してはいけない))/u;
|
|
568
|
+
const directRemovalBefore =
|
|
569
|
+
/\b(?:remove|delete|unlink|discard)\s+(?:(?:the|an?|this|that)\s+)?(?:(?:accidental|stale|temporary|untracked|generated|obsolete|old|private|empty)\s+)*(?:file\s+)?$/i;
|
|
570
|
+
const cjkDirectRemovalBefore =
|
|
571
|
+
/(?:删除|刪除|移除|清除)(?:(?:这个|這個|该|該|意外的|暂存的|暫存的|临时的|臨時的|未跟踪的|未追蹤的|旧的|舊的|私有的)\s*)*(?:文件)?\s*$/u;
|
|
518
572
|
const excluded = [];
|
|
519
573
|
for (const clause of source.split(/[;;\n]|(?<=[.!?。!?])\s+/u)) {
|
|
520
574
|
pathPattern.lastIndex = 0;
|
|
@@ -545,7 +599,9 @@ export function inferExplicitlyExcludedOutputPaths(goal = "") {
|
|
|
545
599
|
negativeActionAfter.test(after) ||
|
|
546
600
|
keepAbsent.test(`${before}${after}`) ||
|
|
547
601
|
cjkNegativeActionBefore.test(before) ||
|
|
548
|
-
cjkNegativeActionAfter.test(after)
|
|
602
|
+
cjkNegativeActionAfter.test(after) ||
|
|
603
|
+
directRemovalBefore.test(before) ||
|
|
604
|
+
cjkDirectRemovalBefore.test(before)
|
|
549
605
|
);
|
|
550
606
|
previousExcluded = directExclusion || coordinatedWithExcludedPrevious;
|
|
551
607
|
if (previousExcluded) {
|
|
@@ -655,7 +711,13 @@ function inferRequiredTextTerms(goal = "") {
|
|
|
655
711
|
}
|
|
656
712
|
// A quoted filename in "save as `report.md`" is an output location, not
|
|
657
713
|
// required prose inside that report. Path existence is validated separately.
|
|
658
|
-
return uniqueLimited(
|
|
714
|
+
return uniqueLimited(
|
|
715
|
+
terms.filter((term) => {
|
|
716
|
+
const cleaned = String(term).trim();
|
|
717
|
+
return !outputPathTerms.has(cleaned) && !looksLikeShellCommandLiteral(cleaned);
|
|
718
|
+
}),
|
|
719
|
+
24
|
|
720
|
+
);
|
|
659
721
|
}
|
|
660
722
|
|
|
661
723
|
const EXECUTABLE_SOURCE_EXTENSIONS = new Set([
|
|
@@ -701,6 +763,7 @@ export function inferRequiredExecutableTerms(goal = "") {
|
|
|
701
763
|
for (const match of source.matchAll(assignmentPattern)) {
|
|
702
764
|
const index = Number(match.index || 0);
|
|
703
765
|
if (executableRequirementIsNegated(source, index)) continue;
|
|
766
|
+
if (indexFallsInsideInlineCommand(source, index)) continue;
|
|
704
767
|
const window = source.slice(Math.max(0, index - 180), Math.min(source.length, index + match[0].length + 220));
|
|
705
768
|
const implementationRequirement =
|
|
706
769
|
/\b(?:actual|canonical|executable|implementation|source|code|call|argument|keyword|parameter|repair|fix|correct|replace|add|set|pass|use|must|required)\b/iu.test(window) ||
|
|
@@ -1316,9 +1379,9 @@ function prefixRequestsInlineCommandExecution(prefix = "") {
|
|
|
1316
1379
|
while (preamble.test(clause)) clause = clause.replace(preamble, "").trim();
|
|
1317
1380
|
clause = clause.replace(/(?:[::]|--?)\s*$/, "").trim();
|
|
1318
1381
|
return (
|
|
1319
|
-
/^(?:run|rerun|re-run|execute|invoke|launch|verify|validate|check|confirm)(?:\s+(?:(?:the|this|that)\s+)?(?:following\s+)?command(?:\s+named)?)?\s*$/i.test(
|
|
1382
|
+
/^(?:followed\s+by|run|rerun|re-run|execute|invoke|launch|verify|validate|check|confirm)(?:\s+(?:(?:the|this|that)\s+)?(?:following\s+)?command(?:\s+named)?)?\s*$/i.test(
|
|
1320
1383
|
clause
|
|
1321
|
-
) || /^(
|
|
1384
|
+
) || /^(?:随后运行|隨後運行|接着运行|接著運行|运行|運行|执行|執行|调用|調用|验证|驗證|检查|檢查|确认|確認)\s*$/.test(clause)
|
|
1322
1385
|
);
|
|
1323
1386
|
}
|
|
1324
1387
|
|
|
@@ -1408,6 +1471,7 @@ function inferExplicitRequestedCommands(goal = "") {
|
|
|
1408
1471
|
|
|
1409
1472
|
export function deriveScsTaskContract({ goal = "", taskProfile = "", acceptanceCriteria = [] } = {}) {
|
|
1410
1473
|
const evidenceGoal = scopedChatopsEvidenceGoal(goal, taskProfile);
|
|
1474
|
+
const positiveEvidenceGoal = stripForbiddenLanguage(evidenceGoal);
|
|
1411
1475
|
const artifactRoot = scopedArtifactRoot(goal);
|
|
1412
1476
|
const requirementCategories = inferRequirementCategories(evidenceGoal, taskProfile, acceptanceCriteria);
|
|
1413
1477
|
const requiredToolCalls = inferRequiredToolCalls(evidenceGoal);
|
|
@@ -1431,7 +1495,7 @@ export function deriveScsTaskContract({ goal = "", taskProfile = "", acceptanceC
|
|
|
1431
1495
|
excludedOutputPaths
|
|
1432
1496
|
);
|
|
1433
1497
|
const exactInputPaths = filterExplicitlyExcludedOutputPaths(
|
|
1434
|
-
inferExactInputPaths(
|
|
1498
|
+
inferExactInputPaths(positiveEvidenceGoal),
|
|
1435
1499
|
excludedOutputPaths
|
|
1436
1500
|
).filter((item) => !inferredOutputPaths.includes(item) && !exactOutputPaths.includes(item));
|
|
1437
1501
|
const declaredSourceRoots = inferDeclaredSourceRoots(evidenceGoal);
|
|
@@ -1455,8 +1519,8 @@ export function deriveScsTaskContract({ goal = "", taskProfile = "", acceptanceC
|
|
|
1455
1519
|
requiresWorkspaceMutation: goalRequestsWorkspaceMutation(evidenceGoal, taskProfile),
|
|
1456
1520
|
requiresFileMutation: goalRequestsFileMutation(evidenceGoal, taskProfile),
|
|
1457
1521
|
requiresSourceGrounding: requiresSourceGrounding(evidenceGoal),
|
|
1458
|
-
requiredTextTerms: inferRequiredTextTerms(
|
|
1459
|
-
requiredExecutableTerms: inferRequiredExecutableTerms(
|
|
1522
|
+
requiredTextTerms: inferRequiredTextTerms(positiveEvidenceGoal),
|
|
1523
|
+
requiredExecutableTerms: inferRequiredExecutableTerms(positiveEvidenceGoal),
|
|
1460
1524
|
forbiddenTextTerms: inferForbiddenTextTerms(evidenceGoal),
|
|
1461
1525
|
successCriteria: unique(acceptanceCriteria).slice(0, 10),
|
|
1462
1526
|
};
|