@lazyingart/agintiflow 0.20.211 → 0.20.213
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/supervision-campaign-ledger.md +55 -0
- package/package.json +1 -1
- package/scripts/smoke-cli-chat.js +20 -10
- package/scripts/smoke-dynamic-step-budget.js +124 -0
- package/scripts/smoke-model-roles.js +18 -0
- package/scripts/smoke-progressive-tool-selection.js +41 -0
- package/src/agent-runner.js +62 -10
- package/src/scs-evidence.js +47 -12
|
@@ -75,3 +75,58 @@ The incident established two general contracts:
|
|
|
75
75
|
canvas/download filename derived from the task title and source purpose.
|
|
76
76
|
Internal collision identifiers are short suffixes, never the leading or only
|
|
77
77
|
visible filename information.
|
|
78
|
+
|
|
79
|
+
### QA repair continuity and evidence intent
|
|
80
|
+
|
|
81
|
+
`qa-incident-metrics-001` passed after two reusable runtime fixes. A normal,
|
|
82
|
+
underspecified QA prompt led the DeepSeek-backed agent to reproduce and diagnose
|
|
83
|
+
compound-duration parsing, percentile interpolation/mutation, and deterministic
|
|
84
|
+
summary-order defects. The first partial patch advanced the mutation revision,
|
|
85
|
+
but the runtime then forgot the retained failing test and prematurely reduced
|
|
86
|
+
the tool surface to test-only mode. Source-next restored the failed-test repair
|
|
87
|
+
state until a fresh current-revision test passed, allowing the same durable
|
|
88
|
+
session to finish the coherent patch, add regressions, run 15 tests at 100%
|
|
89
|
+
statement coverage, clean debris, and commit `667891f`.
|
|
90
|
+
|
|
91
|
+
Independent `pytest` and the hidden `qa_incident_metrics_contract.py` checker
|
|
92
|
+
both passed. The run also exposed an evidence-intent false positive: `figure
|
|
93
|
+
out` and `clean up generated test debris` were interpreted as a request for a
|
|
94
|
+
canvas artifact. Evidence inference now excludes those non-production phrases
|
|
95
|
+
while retaining the artifact gate for real generated figures. Exact session
|
|
96
|
+
evidence remains in
|
|
97
|
+
`~/.agintiflow/sessions/aginti-qa-incident-metrics-001/events.jsonl` and the
|
|
98
|
+
machine ledger records the run as `passed_after_fix`.
|
|
99
|
+
|
|
100
|
+
### GitHub maintenance hidden acceptance and goal-scoped evidence
|
|
101
|
+
|
|
102
|
+
`github-safe-maintenance-012` started from a normal maintenance prompt rather
|
|
103
|
+
than a checker-shaped instruction. The agent repaired and verified the target,
|
|
104
|
+
but its first completion omitted the exact `docs/maintenance-handoff.md` file
|
|
105
|
+
required by independent acceptance. The same retained session inspected the
|
|
106
|
+
external failure, created the missing handoff, committed and pushed target
|
|
107
|
+
commit `36fa6c0`, and left `main` clean and synchronized. The hidden
|
|
108
|
+
`github_maintenance_contract.py` checker then passed.
|
|
109
|
+
|
|
110
|
+
Supervision of the repair exposed four runtime defects that could affect other
|
|
111
|
+
profiles:
|
|
112
|
+
|
|
113
|
+
- A test command wrapped as `command; echo "EXIT:$?"` could have shell exit zero
|
|
114
|
+
even when the real command failed. Explicit final `EXIT`, `STATUS`, or
|
|
115
|
+
`RESULT` probes are now parsed; a missing or nonzero marker is failing
|
|
116
|
+
evidence.
|
|
117
|
+
- A genuinely new continuation could inherit completed artifact, SCS,
|
|
118
|
+
project-verification, and repair state from the prior goal. New goals now
|
|
119
|
+
clear only goal-scoped execution evidence while preserving conversation,
|
|
120
|
+
durable goal history, and goal-keyed research memory.
|
|
121
|
+
- An acceptance sentence listing screenshots, PDFs, reports, or app launches
|
|
122
|
+
"as appropriate" could force irrelevant visual work. Optional evidence
|
|
123
|
+
examples no longer become mandatory categories.
|
|
124
|
+
- Merely naming a read-only checker such as `contract.py` could force a file
|
|
125
|
+
artifact. File evidence now requires both mutation intent and a workspace
|
|
126
|
+
file/source target; a virtual canvas artifact remains artifact evidence only.
|
|
127
|
+
|
|
128
|
+
The patched source resumed `aginti-github-maintenance-001`, invoked the hidden
|
|
129
|
+
checker exactly once, performed no file, canvas, commit, or push side effect,
|
|
130
|
+
and completed with a clean repository-state check. The full npm suite and the
|
|
131
|
+
focused dynamic-budget, SCS/model-role, and web-canvas regressions pass for
|
|
132
|
+
AgInTiFlow `0.20.213`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.213",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -74,6 +74,12 @@ function cellWidth(value = "") {
|
|
|
74
74
|
return [...String(value || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "")].reduce((sum, char) => sum + charCellWidth(char), 0);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
function stripTerminalControls(value = "") {
|
|
78
|
+
return String(value || "")
|
|
79
|
+
.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "")
|
|
80
|
+
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
81
|
+
}
|
|
82
|
+
|
|
77
83
|
function runChat(inputText) {
|
|
78
84
|
return runCli(
|
|
79
85
|
["chat", "--provider", "mock", "--routing", "manual", "--profile", "code", "--headless", "--sandbox-mode", "host"],
|
|
@@ -596,7 +602,7 @@ try {
|
|
|
596
602
|
animated: false,
|
|
597
603
|
webAppUrl: "http://127.0.0.1:3210",
|
|
598
604
|
}).join("\n");
|
|
599
|
-
if (!launchHeaderWithWeb.includes("webapp: http://127.0.0.1:3210")) {
|
|
605
|
+
if (!stripTerminalControls(launchHeaderWithWeb).includes("webapp: http://127.0.0.1:3210")) {
|
|
600
606
|
throw new Error("launch header did not render the active webapp URL in the tagline row");
|
|
601
607
|
}
|
|
602
608
|
const launchHeaderWithWebError = buildLaunchHeaderLines({
|
|
@@ -749,17 +755,21 @@ try {
|
|
|
749
755
|
throw new Error("terminal prompt layout did not render live input queue and cwd footer");
|
|
750
756
|
}
|
|
751
757
|
const hintPromptLayout = buildPromptLayout("/mo", 3, 90, 24, { suggestions: ["/models", "/model"], suggestionIndex: 1 });
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
758
|
+
const hintRaw = hintPromptLayout.renderedRows.join("\n");
|
|
759
|
+
const hintText = stripTerminalControls(hintRaw);
|
|
760
|
+
const selectedHintRendered =
|
|
761
|
+
hintText.includes("hint /models >/model") ||
|
|
762
|
+
(hintText.includes("hint /models /model") && hintRaw.includes("\x1b[1m/model\x1b[0m"));
|
|
763
|
+
if (!hintText.includes(" user> /mo") || !selectedHintRendered) {
|
|
764
|
+
throw new Error(`terminal prompt layout did not align user and hint text columns: ${JSON.stringify(hintText)}`);
|
|
757
765
|
}
|
|
758
766
|
const exactHintLayout = buildPromptLayout("/model", 6, 90, 24);
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
767
|
+
const exactHintRaw = exactHintLayout.renderedRows.join("\n");
|
|
768
|
+
const exactHintText = stripTerminalControls(exactHintRaw);
|
|
769
|
+
const exactHintSelected =
|
|
770
|
+
exactHintText.includes("hint >/model") ||
|
|
771
|
+
(exactHintText.includes("hint /model") && exactHintRaw.includes("\x1b[1m/model\x1b[0m"));
|
|
772
|
+
if (!exactHintSelected || exactHintText.includes("/models")) {
|
|
763
773
|
throw new Error("exact slash commands should not show broader prefix matches");
|
|
764
774
|
}
|
|
765
775
|
if (classifyEscapeAction({ active: false }) !== "noop") {
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
recordProjectVerificationOutcome,
|
|
27
27
|
recordExactOutputProgress,
|
|
28
28
|
recordStaticDiscoveryProgress,
|
|
29
|
+
resetGoalScopedRuntimeState,
|
|
29
30
|
resetStaticDiscoveryAfterContextLoss,
|
|
30
31
|
rememberCompletedDeepResearch,
|
|
31
32
|
repeatedNoProgressToolBlock,
|
|
@@ -37,6 +38,7 @@ import {
|
|
|
37
38
|
import {
|
|
38
39
|
augmentScsTaskContractWithProjectVerification,
|
|
39
40
|
buildScsEvidenceLedger,
|
|
41
|
+
deriveScsTaskContract,
|
|
40
42
|
evaluateScsEvidence,
|
|
41
43
|
} from "../src/scs-evidence.js";
|
|
42
44
|
import {
|
|
@@ -72,6 +74,128 @@ function toolMessage(payload) {
|
|
|
72
74
|
|
|
73
75
|
try {
|
|
74
76
|
assert(normalizeDynamicStepsMode("off") === "off", "dynamic mode off did not normalize");
|
|
77
|
+
const failedExitProbeState = { meta: {} };
|
|
78
|
+
const failedExitProbeResult = {
|
|
79
|
+
toolName: "run_command",
|
|
80
|
+
ok: true,
|
|
81
|
+
exitCode: 0,
|
|
82
|
+
args: { command: 'python -m unittest discover -s tests; echo "EXIT:$?"' },
|
|
83
|
+
stdout: "external contract did not pass\nEXIT:1\n",
|
|
84
|
+
stderr: "",
|
|
85
|
+
};
|
|
86
|
+
recordProjectVerificationOutcome(failedExitProbeState, failedExitProbeResult, {
|
|
87
|
+
commandCwd: workspace,
|
|
88
|
+
taskProfile: "qa",
|
|
89
|
+
});
|
|
90
|
+
assert(
|
|
91
|
+
failedExitProbeResult.projectTest?.passed === false &&
|
|
92
|
+
failedExitProbeResult.projectTest?.explicitExitStatus === 1,
|
|
93
|
+
"a wrapped nonzero test status was recorded as passing"
|
|
94
|
+
);
|
|
95
|
+
const missingExitProbeState = { meta: {} };
|
|
96
|
+
const missingExitProbeResult = {
|
|
97
|
+
toolName: "run_command",
|
|
98
|
+
ok: true,
|
|
99
|
+
exitCode: 0,
|
|
100
|
+
args: { command: 'python -m unittest discover -s tests; printf "EXIT=%s\\n" "$?"' },
|
|
101
|
+
stdout: "external contract output ended before the status marker\n",
|
|
102
|
+
stderr: "",
|
|
103
|
+
};
|
|
104
|
+
recordProjectVerificationOutcome(missingExitProbeState, missingExitProbeResult, {
|
|
105
|
+
commandCwd: workspace,
|
|
106
|
+
taskProfile: "qa",
|
|
107
|
+
});
|
|
108
|
+
assert(
|
|
109
|
+
missingExitProbeResult.projectTest?.passed === false &&
|
|
110
|
+
missingExitProbeResult.projectTest?.explicitExitStatus === null,
|
|
111
|
+
"a missing wrapped test status was recorded as passing"
|
|
112
|
+
);
|
|
113
|
+
const passingExitProbeState = { meta: {} };
|
|
114
|
+
const passingExitProbeResult = {
|
|
115
|
+
toolName: "run_command",
|
|
116
|
+
ok: true,
|
|
117
|
+
exitCode: 0,
|
|
118
|
+
args: { command: 'python -m unittest discover -s tests; echo "EXIT=$?"' },
|
|
119
|
+
stdout: "contract checks passed\nEXIT=0\n",
|
|
120
|
+
stderr: "",
|
|
121
|
+
};
|
|
122
|
+
recordProjectVerificationOutcome(passingExitProbeState, passingExitProbeResult, {
|
|
123
|
+
commandCwd: workspace,
|
|
124
|
+
taskProfile: "qa",
|
|
125
|
+
});
|
|
126
|
+
assert(
|
|
127
|
+
passingExitProbeResult.projectTest?.passed === true &&
|
|
128
|
+
passingExitProbeResult.projectTest?.explicitExitStatus === 0,
|
|
129
|
+
"a wrapped zero test status was not accepted"
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const newGoalState = {
|
|
133
|
+
meta: {
|
|
134
|
+
artifactProgress: { complete: true },
|
|
135
|
+
completionEvidenceRepair: { attempts: 1 },
|
|
136
|
+
dataProjectWorkflow: { ready: true },
|
|
137
|
+
durableEvidenceCategories: ["file", "visual"],
|
|
138
|
+
durableGitActions: ["commit"],
|
|
139
|
+
durableGitEvidence: [{ action: "commit", goalRevision: 1 }],
|
|
140
|
+
failedTestRecoveryPacket: { content: "old failure" },
|
|
141
|
+
goalContract: { revision: 2 },
|
|
142
|
+
projectVerification: { mutationRevision: 4 },
|
|
143
|
+
scs: { taskContract: { exactOutputPaths: ["old-output.md"] } },
|
|
144
|
+
completedDeepResearch: [{ goalKey: "retained-other-goal" }],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const removedGoalState = resetGoalScopedRuntimeState(newGoalState);
|
|
148
|
+
assert(removedGoalState.includes("artifactProgress"), "new goal did not clear stale artifact progress");
|
|
149
|
+
assert(!newGoalState.meta.projectVerification, "new goal retained stale project verification");
|
|
150
|
+
assert(!newGoalState.meta.scs, "new goal retained the previous SCS task contract");
|
|
151
|
+
assert(!newGoalState.meta.durableEvidenceCategories, "new goal inherited completed evidence categories");
|
|
152
|
+
assert(newGoalState.meta.goalContract?.revision === 2, "new goal reset its durable goal contract");
|
|
153
|
+
assert(newGoalState.meta.completedDeepResearch?.length === 1, "new goal discarded goal-keyed research cache");
|
|
154
|
+
|
|
155
|
+
const optionalVisualContract = deriveScsTaskContract({
|
|
156
|
+
goal: "Repair the repository, verify it, commit, and push the intentional work.",
|
|
157
|
+
taskProfile: "github",
|
|
158
|
+
acceptanceCriteria: [
|
|
159
|
+
"Do not rely only on chat summaries; verify files, commands, screenshots, PDFs, reports, or app launches as appropriate.",
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
assert(
|
|
163
|
+
!optionalVisualContract.requiredEvidence.some((item) => item.category === "visual"),
|
|
164
|
+
"an optional evidence example forced irrelevant visual validation"
|
|
165
|
+
);
|
|
166
|
+
const explicitVisualContract = deriveScsTaskContract({
|
|
167
|
+
goal: "Capture and inspect a screenshot of the repaired interface.",
|
|
168
|
+
taskProfile: "website",
|
|
169
|
+
});
|
|
170
|
+
assert(
|
|
171
|
+
explicitVisualContract.requiredEvidence.some((item) => item.category === "visual"),
|
|
172
|
+
"an explicit screenshot request lost visual validation"
|
|
173
|
+
);
|
|
174
|
+
const readOnlyCheckerContract = deriveScsTaskContract({
|
|
175
|
+
goal:
|
|
176
|
+
"Re-run /tmp/acceptance/github_maintenance_contract.py once, verify the repository, and do not edit, commit, or push anything.",
|
|
177
|
+
taskProfile: "github",
|
|
178
|
+
});
|
|
179
|
+
assert(
|
|
180
|
+
!readOnlyCheckerContract.requiredEvidence.some((item) => item.category === "file"),
|
|
181
|
+
"a read-only checker path was mistaken for a requested file change"
|
|
182
|
+
);
|
|
183
|
+
const canvasOnlyContract = deriveScsTaskContract({
|
|
184
|
+
goal: "Create a canvas artifact preview for this smoke test.",
|
|
185
|
+
});
|
|
186
|
+
assert(
|
|
187
|
+
canvasOnlyContract.requiredEvidence.some((item) => item.category === "artifact") &&
|
|
188
|
+
!canvasOnlyContract.requiredEvidence.some((item) => item.category === "file"),
|
|
189
|
+
"a virtual canvas artifact was mistaken for a workspace-file mutation"
|
|
190
|
+
);
|
|
191
|
+
const sourceRepairContract = deriveScsTaskContract({
|
|
192
|
+
goal: "Fix src/runtime.py and verify the focused tests.",
|
|
193
|
+
taskProfile: "python",
|
|
194
|
+
});
|
|
195
|
+
assert(
|
|
196
|
+
sourceRepairContract.requiredEvidence.some((item) => item.category === "file"),
|
|
197
|
+
"an explicit source repair lost its file-change evidence gate"
|
|
198
|
+
);
|
|
75
199
|
assert(
|
|
76
200
|
completionEvidenceNeedsCommand({ missingProjectCommands: ["python analysis.py"] }),
|
|
77
201
|
"a pending canonical command did not reopen command execution"
|
|
@@ -986,6 +986,14 @@ const generatedReviewContract = deriveScsTaskContract({
|
|
|
986
986
|
].join("\n"),
|
|
987
987
|
taskProfile: "review",
|
|
988
988
|
});
|
|
989
|
+
const qaCleanupContract = deriveScsTaskContract({
|
|
990
|
+
goal: "Inspect the project, figure out what is actually wrong, fix the failing tests, clean up generated test debris, and commit only the intentional fix.",
|
|
991
|
+
taskProfile: "qa",
|
|
992
|
+
});
|
|
993
|
+
const generatedFigureContract = deriveScsTaskContract({
|
|
994
|
+
goal: "Generate a publication-ready figure that compares the two repair strategies.",
|
|
995
|
+
taskProfile: "design",
|
|
996
|
+
});
|
|
989
997
|
assert(
|
|
990
998
|
!explainCodeContract.requiresExternalEvidence,
|
|
991
999
|
"code profile alone should not force external evidence for a pure explanation"
|
|
@@ -1037,6 +1045,16 @@ assert(
|
|
|
1037
1045
|
!generatedReviewContract.requiredEvidence.some((item) => item.category === "artifact"),
|
|
1038
1046
|
"review profile should not turn review-format boilerplate into file/artifact production requirements"
|
|
1039
1047
|
);
|
|
1048
|
+
assert(
|
|
1049
|
+
qaCleanupContract.requiredEvidence.some((item) => item.category === "command") &&
|
|
1050
|
+
qaCleanupContract.requiredEvidence.some((item) => item.category === "git") &&
|
|
1051
|
+
!qaCleanupContract.requiredEvidence.some((item) => item.category === "artifact"),
|
|
1052
|
+
"cleaning generated test debris should not invent an unrelated artifact-delivery requirement"
|
|
1053
|
+
);
|
|
1054
|
+
assert(
|
|
1055
|
+
generatedFigureContract.requiredEvidence.some((item) => item.category === "artifact"),
|
|
1056
|
+
"tightening QA intent phrases removed the real generated-figure artifact gate"
|
|
1057
|
+
);
|
|
1040
1058
|
const fileOnlyLedger = buildScsEvidenceLedger({
|
|
1041
1059
|
context: { events: [{ type: "file.changed", data: { path: "src/app.js" } }] },
|
|
1042
1060
|
});
|
|
@@ -725,6 +725,47 @@ const constrainedInstructionWrite = failedTestRepairWithRequiredInstruction.find
|
|
|
725
725
|
assertStrict.deepEqual(constrainedInstructionWrite.function.parameters.properties.path.enum, ["AGINTI.md"]);
|
|
726
726
|
assertStrict.deepEqual(constrainedInstructionWrite.function.parameters.properties.mode.enum, ["create"]);
|
|
727
727
|
|
|
728
|
+
const continuedFailedTestRepairRuntime = nextStepRuntimeConfig(
|
|
729
|
+
{ provider: "localllm", taskProfile: "qa" },
|
|
730
|
+
{
|
|
731
|
+
meta: {
|
|
732
|
+
projectVerification: {
|
|
733
|
+
mutationRevision: 1,
|
|
734
|
+
discoveredTests: ["python -m pytest -q"],
|
|
735
|
+
requiredOutputs: [],
|
|
736
|
+
testRuns: [
|
|
737
|
+
{
|
|
738
|
+
command: "python -m pytest -q",
|
|
739
|
+
mutationRevision: 0,
|
|
740
|
+
passed: false,
|
|
741
|
+
failureSignature: "baseline-failure",
|
|
742
|
+
},
|
|
743
|
+
],
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
messages: [],
|
|
747
|
+
}
|
|
748
|
+
);
|
|
749
|
+
assertStrict.equal(
|
|
750
|
+
continuedFailedTestRepairRuntime.testFailureRepairActive,
|
|
751
|
+
true,
|
|
752
|
+
"a partial repair forgot the retained failed test after the mutation revision advanced"
|
|
753
|
+
);
|
|
754
|
+
assertStrict.equal(
|
|
755
|
+
continuedFailedTestRepairRuntime.testFailureCommand,
|
|
756
|
+
"python -m pytest -q",
|
|
757
|
+
"a partial repair lost the retained failing test command"
|
|
758
|
+
);
|
|
759
|
+
sameNames(
|
|
760
|
+
selectProgressiveTools(allTools, {
|
|
761
|
+
config: continuedFailedTestRepairRuntime,
|
|
762
|
+
goal: "Continue the coherent repair, then retest.",
|
|
763
|
+
profile: "qa",
|
|
764
|
+
}),
|
|
765
|
+
["read_file", "search_files", "apply_patch", "run_command", "finish"],
|
|
766
|
+
"a partial repair was forced into test-only mode before the coherent patch was complete"
|
|
767
|
+
);
|
|
768
|
+
|
|
728
769
|
const pendingTestTools = selectProgressiveTools(allTools, {
|
|
729
770
|
config: {
|
|
730
771
|
provider: "localllm",
|
package/src/agent-runner.js
CHANGED
|
@@ -2423,6 +2423,28 @@ async function finishWithDirectAnswer({ config, state, store, observers, session
|
|
|
2423
2423
|
};
|
|
2424
2424
|
}
|
|
2425
2425
|
|
|
2426
|
+
export function resetGoalScopedRuntimeState(state = {}) {
|
|
2427
|
+
state.meta = state.meta || {};
|
|
2428
|
+
const keys = [
|
|
2429
|
+
"artifactProgress",
|
|
2430
|
+
"completionEvidenceRepair",
|
|
2431
|
+
"dataProjectWorkflow",
|
|
2432
|
+
"durableEvidenceCategories",
|
|
2433
|
+
"durableGitActions",
|
|
2434
|
+
"durableGitEvidence",
|
|
2435
|
+
"failedTestRecoveryPacket",
|
|
2436
|
+
"projectVerification",
|
|
2437
|
+
"scs",
|
|
2438
|
+
];
|
|
2439
|
+
const removed = [];
|
|
2440
|
+
for (const key of keys) {
|
|
2441
|
+
if (!(key in state.meta)) continue;
|
|
2442
|
+
delete state.meta[key];
|
|
2443
|
+
removed.push(key);
|
|
2444
|
+
}
|
|
2445
|
+
return removed;
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2426
2448
|
async function applyContinuationPrompt(state, config, observers) {
|
|
2427
2449
|
if (!config.resume || !config.goal) return null;
|
|
2428
2450
|
|
|
@@ -2439,6 +2461,9 @@ async function applyContinuationPrompt(state, config, observers) {
|
|
|
2439
2461
|
state.meta = state.meta || {};
|
|
2440
2462
|
const preserveTaskBoundary = preservesCurrentTaskBoundary(state, config.goal);
|
|
2441
2463
|
const goalUpdate = updateGoalContract(state, config.goal, { preserveTaskBoundary });
|
|
2464
|
+
if (!preserveTaskBoundary) {
|
|
2465
|
+
resetGoalScopedRuntimeState(state);
|
|
2466
|
+
}
|
|
2442
2467
|
if (
|
|
2443
2468
|
preserveTaskBoundary &&
|
|
2444
2469
|
continuationAddsConcreteRequirement(config.goal) &&
|
|
@@ -2961,6 +2986,19 @@ function commandReportsTestFailure(result = {}) {
|
|
|
2961
2986
|
);
|
|
2962
2987
|
}
|
|
2963
2988
|
|
|
2989
|
+
function explicitExitProbeStatus(command = "", result = {}) {
|
|
2990
|
+
const normalizedCommand = normalizeProjectCommand(command);
|
|
2991
|
+
const hasExitProbe = /(?:^|;)\s*(?:echo|printf)\b[^;&|]*(?:EXIT|STATUS|RESULT)[^;&|]*\$\?[^;&|]*$/i.test(
|
|
2992
|
+
normalizedCommand
|
|
2993
|
+
);
|
|
2994
|
+
if (!hasExitProbe) return { present: false, status: null };
|
|
2995
|
+
|
|
2996
|
+
const output = `${String(result.stdout || "")}\n${String(result.stderr || "")}`;
|
|
2997
|
+
const matches = [...output.matchAll(/(?:^|\n)\s*(?:EXIT|STATUS|RESULT)(?:_CODE)?\s*[:=]\s*(-?\d+)\s*(?=\n|$)/gim)];
|
|
2998
|
+
if (!matches.length) return { present: true, status: null };
|
|
2999
|
+
return { present: true, status: Number(matches.at(-1)[1]) };
|
|
3000
|
+
}
|
|
3001
|
+
|
|
2964
3002
|
function actionableTestWarnings(result = {}) {
|
|
2965
3003
|
const output = redactSensitiveText(`${String(result.stderr || "")}\n${String(result.stdout || "")}`);
|
|
2966
3004
|
const warnings = [];
|
|
@@ -3133,17 +3171,24 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
|
|
|
3133
3171
|
|
|
3134
3172
|
if (toolName === "run_command") {
|
|
3135
3173
|
const command = normalizeProjectCommand(toolResult.args?.command || "");
|
|
3174
|
+
const exitProbe = explicitExitProbeStatus(command, toolResult);
|
|
3136
3175
|
const run = {
|
|
3137
3176
|
command,
|
|
3138
3177
|
at: now,
|
|
3139
|
-
ok:
|
|
3178
|
+
ok:
|
|
3179
|
+
toolResult.ok !== false &&
|
|
3180
|
+
Number(toolResult.exitCode ?? 0) === 0 &&
|
|
3181
|
+
(!exitProbe.present || exitProbe.status === 0),
|
|
3140
3182
|
mutationRevision: verification.mutationRevision,
|
|
3183
|
+
...(exitProbe.present ? { explicitExitStatus: exitProbe.status } : {}),
|
|
3141
3184
|
};
|
|
3142
3185
|
verification.commandRuns = [...verification.commandRuns, run].slice(-40);
|
|
3143
3186
|
toolResult.projectMutationRevision = verification.mutationRevision;
|
|
3144
3187
|
if (isSubstantiveTestCommand(command)) {
|
|
3145
3188
|
const zeroTests = commandReportsZeroTests(toolResult);
|
|
3146
|
-
const reportedFailure =
|
|
3189
|
+
const reportedFailure =
|
|
3190
|
+
commandReportsTestFailure(toolResult) ||
|
|
3191
|
+
(exitProbe.present && exitProbe.status !== 0);
|
|
3147
3192
|
const qualityWarnings = actionableTestWarnings(toolResult);
|
|
3148
3193
|
const passed = run.ok && !zeroTests && !reportedFailure && qualityWarnings.length === 0;
|
|
3149
3194
|
const failedEvidence = passed ? {} : compactFailedTestEvidence(toolResult, config);
|
|
@@ -4239,13 +4284,23 @@ export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
|
4239
4284
|
};
|
|
4240
4285
|
const verification = state.meta?.projectVerification || {};
|
|
4241
4286
|
const mutationRevision = Number(verification.mutationRevision || 0);
|
|
4242
|
-
const
|
|
4287
|
+
const testRuns = Array.isArray(verification.testRuns) ? verification.testRuns : [];
|
|
4288
|
+
const latestCurrentTest = [...testRuns]
|
|
4243
4289
|
.reverse()
|
|
4244
4290
|
.find((run) => Number(run.mutationRevision || 0) === mutationRevision);
|
|
4245
|
-
|
|
4291
|
+
const latestRecordedTest = [...testRuns]
|
|
4292
|
+
.reverse()
|
|
4293
|
+
.find((run) => String(run?.command || "").trim());
|
|
4294
|
+
const retainedFailedTest =
|
|
4295
|
+
latestCurrentTest && latestCurrentTest.passed !== true
|
|
4296
|
+
? latestCurrentTest
|
|
4297
|
+
: !latestCurrentTest && latestRecordedTest?.passed === false
|
|
4298
|
+
? latestRecordedTest
|
|
4299
|
+
: null;
|
|
4300
|
+
if (retainedFailedTest) {
|
|
4246
4301
|
runtimeConfig.testFailureRepairActive = true;
|
|
4247
|
-
runtimeConfig.testFailureCommand = String(
|
|
4248
|
-
runtimeConfig.testFailureSignature = String(
|
|
4302
|
+
runtimeConfig.testFailureCommand = String(retainedFailedTest.command || "");
|
|
4303
|
+
runtimeConfig.testFailureSignature = String(retainedFailedTest.failureSignature || "");
|
|
4249
4304
|
const completedOutputs = new Set(
|
|
4250
4305
|
(state.meta?.artifactProgress?.completed || [])
|
|
4251
4306
|
.map((item) => String(item || "").replace(/\\/g, "/").replace(/^\.\//, ""))
|
|
@@ -4256,10 +4311,7 @@ export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
|
4256
4311
|
.filter((item) => /(?:^|\/)(?:AGINTI|AGENTS)\.md$/i.test(item))
|
|
4257
4312
|
.slice(0, 8);
|
|
4258
4313
|
} else if (!latestCurrentTest && mutationRevision > 0 && (verification.discoveredTests || []).length) {
|
|
4259
|
-
const retainedTestCommand =
|
|
4260
|
-
.reverse()
|
|
4261
|
-
.map((run) => String(run.command || ""))
|
|
4262
|
-
.find(Boolean);
|
|
4314
|
+
const retainedTestCommand = String(latestRecordedTest?.command || "");
|
|
4263
4315
|
if (retainedTestCommand) {
|
|
4264
4316
|
runtimeConfig.testVerificationPending = true;
|
|
4265
4317
|
runtimeConfig.testVerificationCommand = retainedTestCommand;
|
package/src/scs-evidence.js
CHANGED
|
@@ -487,6 +487,31 @@ function codeProfileRequiresCommand(goal = "") {
|
|
|
487
487
|
return substantiveCodeWork && !simpleDocumentWrite;
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
+
function goalRequestsWorkspaceMutation(goal = "") {
|
|
491
|
+
const text = normalizedText(goal);
|
|
492
|
+
return (
|
|
493
|
+
/\b(?:append|build|convert|copy|create|delete|edit|fix|generate|implement|modify|move|patch|refactor|remove|rename|repair|replace|rewrite|save|update|write)\b/.test(
|
|
494
|
+
text
|
|
495
|
+
) ||
|
|
496
|
+
/创建|写入|编辑|修复|实现|修改|更新|生成|保存|复制|移动|转换|删除|重命名|替换|追加/.test(text)
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function goalRequestsFileMutation(goal = "") {
|
|
501
|
+
const text = normalizedText(goal);
|
|
502
|
+
if (!goalRequestsWorkspaceMutation(text)) return false;
|
|
503
|
+
return (
|
|
504
|
+
/\b(?:code|codebase|document(?:ation)?|file|notes?|path|readme|repo(?:sitory)?|script|source|workspace)\b/.test(
|
|
505
|
+
text
|
|
506
|
+
) ||
|
|
507
|
+
/(?:^|[\s`'"(])(?:\.{0,2}\/|\/)?[a-z0-9_.-]+(?:\/[a-z0-9_.{}-]+)+/i.test(text) ||
|
|
508
|
+
/\.(?:c|cc|cpp|cs|css|csv|go|h|hpp|html?|java|js|jsx|json|kt|md|mjs|php|py|rb|rs|sh|swift|tex|ts|tsx|txt|ya?ml)\b/i.test(
|
|
509
|
+
text
|
|
510
|
+
) ||
|
|
511
|
+
/文件|文档|代码|代码库|仓库|脚本|源码|路径|工作区|说明书|笔记/.test(text)
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
490
515
|
function profileRequirementsForGoal(taskProfile = "", goal = "") {
|
|
491
516
|
const profile = String(taskProfile || "").toLowerCase();
|
|
492
517
|
const defaults = PROFILE_REQUIREMENTS[profile] || [];
|
|
@@ -510,8 +535,14 @@ function profileRequirementsForGoal(taskProfile = "", goal = "") {
|
|
|
510
535
|
"aaps",
|
|
511
536
|
]);
|
|
512
537
|
if (!codeLikeProfiles.has(profile)) return defaults;
|
|
513
|
-
|
|
514
|
-
|
|
538
|
+
let requirements = [...defaults];
|
|
539
|
+
if (!goalRequestsWorkspaceMutation(goal)) {
|
|
540
|
+
requirements = requirements.filter((category) => category !== "file");
|
|
541
|
+
}
|
|
542
|
+
if (!codeProfileRequiresCommand(goal)) {
|
|
543
|
+
requirements = requirements.filter((category) => category !== "command");
|
|
544
|
+
}
|
|
545
|
+
return requirements;
|
|
515
546
|
}
|
|
516
547
|
|
|
517
548
|
function isReadOnlyReadinessTask(goal = "") {
|
|
@@ -544,18 +575,22 @@ function requiresSourceGrounding(goal = "") {
|
|
|
544
575
|
function inferRequirementCategories(goal = "", taskProfile = "", acceptanceCriteria = []) {
|
|
545
576
|
const positiveGoal = stripForbiddenLanguage(goal);
|
|
546
577
|
const text = normalizedText(positiveGoal);
|
|
578
|
+
const artifactSignalText = text
|
|
579
|
+
.replace(
|
|
580
|
+
/\b(?:clean(?:\s+up)?|remove|delete|clear|purge)\b[^.\n;]{0,120}\b(?:generated|temporary|stale|test)?\s*(?:test\s+)?(?:debris|caches?|byproducts?)\b/gi,
|
|
581
|
+
""
|
|
582
|
+
)
|
|
583
|
+
.replace(/\bfigure\s+out\b/gi, "");
|
|
584
|
+
const mandatoryEvidenceText = artifactSignalText.replace(
|
|
585
|
+
/[^.\n]{0,240}\b(?:as appropriate|if appropriate|when useful|where applicable)\b/gi,
|
|
586
|
+
" "
|
|
587
|
+
);
|
|
547
588
|
const profile = String(taskProfile || "").toLowerCase();
|
|
548
589
|
const categories = new Set(
|
|
549
590
|
goalRequiresEvidence(positiveGoal, "") ? profileRequirementsForGoal(taskProfile, positiveGoal) : []
|
|
550
591
|
);
|
|
551
592
|
|
|
552
|
-
if (
|
|
553
|
-
textHas(
|
|
554
|
-
text,
|
|
555
|
-
/\b(file|path|workspace|edit|patch|fix|repair|refactor|convert|copy|move|remove|delete|source|script|code)\b|\.(?:md|txt|js|jsx|ts|tsx|mjs|cjs|json|ya?ml|py|tex|html|css|svg|csv)\b|\b(?:markdown|json|yaml|html|css|tex|latex)\s+file\b|\bfile\s+(?:as|in)\s+(?:markdown|json|yaml|html|css|tex|latex)\b/
|
|
556
|
-
) ||
|
|
557
|
-
/文件|写入文件|编辑|修复|转换|复制|移动|删除|脚本|代码/.test(text)
|
|
558
|
-
) {
|
|
593
|
+
if (goalRequestsFileMutation(positiveGoal)) {
|
|
559
594
|
categories.add("file");
|
|
560
595
|
}
|
|
561
596
|
const directCommandSignal =
|
|
@@ -571,13 +606,13 @@ function inferRequirementCategories(goal = "", taskProfile = "", acceptanceCrite
|
|
|
571
606
|
if (directCommandSignal || (validationSignal && codeProfileRequiresCommand(positiveGoal))) {
|
|
572
607
|
categories.add("command");
|
|
573
608
|
}
|
|
574
|
-
if (textHas(
|
|
609
|
+
if (textHas(mandatoryEvidenceText, /\b(artifact|canvas|pdf|image|video|screenshot|cover|plot|chart|figure|docx|archive|copy to|export|generated|generate|draft)\b/) || /输出|产物|图片|视频|截图|封面|生成/.test(mandatoryEvidenceText)) {
|
|
575
610
|
categories.add("artifact");
|
|
576
611
|
}
|
|
577
|
-
if (textHas(
|
|
612
|
+
if (textHas(mandatoryEvidenceText, /\b(browser|chrome|chromium|cdp|devtools|playwright|selenium|web[- ]?ui|website|page|tab|composer|click|type|upload|attach|submit|form)\b/) || /浏览器|网页|页面|上传|提交|附件|资产库/.test(mandatoryEvidenceText)) {
|
|
578
613
|
categories.add("browser");
|
|
579
614
|
}
|
|
580
|
-
if (textHas(
|
|
615
|
+
if (textHas(mandatoryEvidenceText, /\b(screenshot|visible|visual|see|inspect image|open image|read_image|thumbnail)\b/) || /截图|可见|缩略图/.test(mandatoryEvidenceText)) {
|
|
581
616
|
categories.add("visual");
|
|
582
617
|
}
|
|
583
618
|
if (
|