@lazyingart/agintiflow 0.20.275 → 0.20.276
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.276",
|
|
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",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
+
import assertStrict from "node:assert/strict";
|
|
3
4
|
import { spawnSync } from "node:child_process";
|
|
4
5
|
import fs from "node:fs/promises";
|
|
5
6
|
import net from "node:net";
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
buildFailedTestRecoveryPacket,
|
|
31
32
|
buildKnownConstrainedPhasePlan,
|
|
32
33
|
buildTaskOwnedCommitCommand,
|
|
34
|
+
changedGitStatusPaths,
|
|
33
35
|
bindPatchContextRepairArguments,
|
|
34
36
|
canonicalizeVerifiedArtifactCompletion,
|
|
35
37
|
completedDeepResearchReuse,
|
|
@@ -52,6 +54,7 @@ import {
|
|
|
52
54
|
mergeDurableGitEvidence,
|
|
53
55
|
nextStepRuntimeConfig,
|
|
54
56
|
patchContextScopeMismatchAttemptCount,
|
|
57
|
+
parseGitPorcelainStatus,
|
|
55
58
|
pythonMainGuardOrderDefects,
|
|
56
59
|
projectAcceptanceFromMarkdown,
|
|
57
60
|
projectTestVerificationFinishBlock,
|
|
@@ -62,6 +65,7 @@ import {
|
|
|
62
65
|
recordCanonicalGeneratedOutputProgress,
|
|
63
66
|
recordAlreadyCommittedRepositoryRepair,
|
|
64
67
|
recordProjectVerificationOutcome,
|
|
68
|
+
repositoryStateInspectionCommand,
|
|
65
69
|
recordExactOutputProgress,
|
|
66
70
|
recordStaticDiscoveryProgress,
|
|
67
71
|
resetGoalScopedRuntimeState,
|
|
@@ -6495,6 +6499,101 @@ try {
|
|
|
6495
6499
|
}),
|
|
6496
6500
|
"plain task prose was mistaken for a repository-state gate without Git-status evidence"
|
|
6497
6501
|
);
|
|
6502
|
+
assertStrict.deepEqual(
|
|
6503
|
+
parseGitPorcelainStatus(
|
|
6504
|
+
" M build_deck.py\n D dist/deck.pdf\n?? output/deck.pptx\n?? output/slide 1.png\n"
|
|
6505
|
+
),
|
|
6506
|
+
[
|
|
6507
|
+
{ status: " M", path: "build_deck.py" },
|
|
6508
|
+
{ status: " D", path: "dist/deck.pdf" },
|
|
6509
|
+
{ status: "??", path: "output/deck.pptx" },
|
|
6510
|
+
{ status: "??", path: "output/slide 1.png" },
|
|
6511
|
+
],
|
|
6512
|
+
"bounded porcelain parsing lost a deletion, untracked output, or path containing spaces"
|
|
6513
|
+
);
|
|
6514
|
+
assertStrict.deepEqual(
|
|
6515
|
+
changedGitStatusPaths(
|
|
6516
|
+
{
|
|
6517
|
+
entries: [
|
|
6518
|
+
{ status: " M", path: "unrelated.md", fingerprint: "same" },
|
|
6519
|
+
{ status: " M", path: "build_deck.py", fingerprint: "before" },
|
|
6520
|
+
],
|
|
6521
|
+
},
|
|
6522
|
+
{
|
|
6523
|
+
entries: [
|
|
6524
|
+
{ status: " M", path: "unrelated.md", fingerprint: "same" },
|
|
6525
|
+
{ status: " M", path: "build_deck.py", fingerprint: "after" },
|
|
6526
|
+
{ status: " D", path: "dist/deck.pdf", fingerprint: "missing" },
|
|
6527
|
+
{ status: "??", path: "output/deck.pptx", fingerprint: "file:1" },
|
|
6528
|
+
],
|
|
6529
|
+
}
|
|
6530
|
+
),
|
|
6531
|
+
["build_deck.py", "dist/deck.pdf", "output/deck.pptx"],
|
|
6532
|
+
"command-bound Git capture included untouched pre-existing dirt or lost generated relocation paths"
|
|
6533
|
+
);
|
|
6534
|
+
const producerMutationState = {
|
|
6535
|
+
goal: "Rebuild the generated presentation.",
|
|
6536
|
+
meta: {
|
|
6537
|
+
goalContract: { revision: 3, taskGoal: "Rebuild the generated presentation." },
|
|
6538
|
+
activeExecutionContract: { revision: 3 },
|
|
6539
|
+
},
|
|
6540
|
+
};
|
|
6541
|
+
recordProjectVerificationOutcome(
|
|
6542
|
+
producerMutationState,
|
|
6543
|
+
{
|
|
6544
|
+
toolName: "run_command",
|
|
6545
|
+
ok: true,
|
|
6546
|
+
exitCode: 0,
|
|
6547
|
+
args: { command: "python3 build_deck.py" },
|
|
6548
|
+
commandPolicy: {
|
|
6549
|
+
category: "toolchain",
|
|
6550
|
+
writesWorkspace: true,
|
|
6551
|
+
mayMutateProject: false,
|
|
6552
|
+
},
|
|
6553
|
+
projectMutationPaths: [
|
|
6554
|
+
"build_deck.py",
|
|
6555
|
+
"dist/deck.pdf",
|
|
6556
|
+
"output/deck.pptx",
|
|
6557
|
+
],
|
|
6558
|
+
stdout: "wrote output/deck.pptx",
|
|
6559
|
+
stderr: "",
|
|
6560
|
+
},
|
|
6561
|
+
{ commandCwd: workspace, taskProfile: "slides" }
|
|
6562
|
+
);
|
|
6563
|
+
assertStrict.deepEqual(
|
|
6564
|
+
producerMutationState.meta.projectVerification.mutationHistory.at(-1).paths,
|
|
6565
|
+
["build_deck.py", "dist/deck.pdf", "output/deck.pptx"],
|
|
6566
|
+
"a producer command discarded the exact Git paths captured across its execution boundary"
|
|
6567
|
+
);
|
|
6568
|
+
const inspectedRepositoryState = structuredClone(repositoryCleanGateFailure);
|
|
6569
|
+
inspectedRepositoryState.meta.goalContract = { revision: 4 };
|
|
6570
|
+
const repositoryInspectionResult = {
|
|
6571
|
+
toolName: "run_command",
|
|
6572
|
+
ok: true,
|
|
6573
|
+
exitCode: 0,
|
|
6574
|
+
args: { command: repositoryStateInspectionCommand() },
|
|
6575
|
+
stdout: " D dist/deck.pdf\n?? output/deck.pptx\n",
|
|
6576
|
+
stderr: "",
|
|
6577
|
+
};
|
|
6578
|
+
recordProjectVerificationOutcome(
|
|
6579
|
+
inspectedRepositoryState,
|
|
6580
|
+
repositoryInspectionResult,
|
|
6581
|
+
{
|
|
6582
|
+
commandCwd: workspace,
|
|
6583
|
+
taskProfile: "slides",
|
|
6584
|
+
testFailureRepositoryStateRepair: true,
|
|
6585
|
+
testFailureSignature: "repository-state-gate",
|
|
6586
|
+
}
|
|
6587
|
+
);
|
|
6588
|
+
const inspectedRepositoryRuntime = nextStepRuntimeConfig(
|
|
6589
|
+
{ provider: "deepseek", taskProfile: "slides", commandCwd: workspace },
|
|
6590
|
+
inspectedRepositoryState
|
|
6591
|
+
);
|
|
6592
|
+
assertStrict.deepEqual(
|
|
6593
|
+
inspectedRepositoryRuntime.repositoryStateRepairObservedPaths,
|
|
6594
|
+
["dist/deck.pdf", "output/deck.pptx"],
|
|
6595
|
+
"a bounded dirty-worktree inspection did not persist exact path candidates for the next turn"
|
|
6596
|
+
);
|
|
6498
6597
|
assert(
|
|
6499
6598
|
unchangedFailedTestRerunBlock(
|
|
6500
6599
|
repositoryCleanGateFailure,
|
|
@@ -2003,10 +2003,15 @@ sameNames(
|
|
|
2003
2003
|
"repository-state repair exposed document mutation tools"
|
|
2004
2004
|
);
|
|
2005
2005
|
assert(
|
|
2006
|
-
/
|
|
2006
|
+
/exact bounded Git status inspection once/i.test(
|
|
2007
2007
|
repositoryStateRepairTools.find((item) => item.function.name === "run_command")?.function.description || ""
|
|
2008
2008
|
),
|
|
2009
|
-
"repository-state repair did not
|
|
2009
|
+
"repository-state repair did not constrain the first recovery turn to one status inspection"
|
|
2010
|
+
);
|
|
2011
|
+
assertStrict.deepEqual(
|
|
2012
|
+
repositoryStateRepairTools[0].function.parameters.properties.command.enum,
|
|
2013
|
+
["git status --porcelain=v1 --untracked-files=all"],
|
|
2014
|
+
"repository-state repair exposed an open-ended shell instead of the exact status command"
|
|
2010
2015
|
);
|
|
2011
2016
|
const conciseRepositoryStateRepairRuntime = nextStepRuntimeConfig(
|
|
2012
2017
|
{ provider: "deepseek", taskProfile: "slides" },
|
|
@@ -2056,6 +2061,63 @@ sameNames(
|
|
|
2056
2061
|
["run_command", "finish"],
|
|
2057
2062
|
"a concise clean-repository acceptance failure exposed content mutation tools"
|
|
2058
2063
|
);
|
|
2064
|
+
const observedRepositoryState = {
|
|
2065
|
+
meta: {
|
|
2066
|
+
goalContract: { revision: 7 },
|
|
2067
|
+
projectVerification: {
|
|
2068
|
+
mutationRevision: 18,
|
|
2069
|
+
repositoryStateInspection: {
|
|
2070
|
+
version: 1,
|
|
2071
|
+
mutationRevision: 18,
|
|
2072
|
+
failureSignature: "observed-repository-state-gate",
|
|
2073
|
+
command: "git status --porcelain=v1 --untracked-files=all",
|
|
2074
|
+
entries: [
|
|
2075
|
+
{ status: " D", path: "dist/deck.pdf" },
|
|
2076
|
+
{ status: "??", path: "output/deck.pptx" },
|
|
2077
|
+
{ status: " M", path: "unrelated-notes.md" },
|
|
2078
|
+
],
|
|
2079
|
+
},
|
|
2080
|
+
testRuns: [{
|
|
2081
|
+
command: "python acceptance.py --phase final",
|
|
2082
|
+
mutationRevision: 18,
|
|
2083
|
+
passed: false,
|
|
2084
|
+
failureEvidenceVersion: 2,
|
|
2085
|
+
failureSignature: "observed-repository-state-gate",
|
|
2086
|
+
failureSummary: "acceptance failed: repository is not clean",
|
|
2087
|
+
}],
|
|
2088
|
+
},
|
|
2089
|
+
},
|
|
2090
|
+
messages: [],
|
|
2091
|
+
};
|
|
2092
|
+
const observedRepositoryRuntime = nextStepRuntimeConfig(
|
|
2093
|
+
{ provider: "deepseek", taskProfile: "slides" },
|
|
2094
|
+
observedRepositoryState
|
|
2095
|
+
);
|
|
2096
|
+
assertStrict.deepEqual(
|
|
2097
|
+
observedRepositoryRuntime.repositoryStateRepairObservedPaths,
|
|
2098
|
+
["dist/deck.pdf", "output/deck.pptx", "unrelated-notes.md"],
|
|
2099
|
+
"the clean-state recovery phase lost exact paths from its bounded status observation"
|
|
2100
|
+
);
|
|
2101
|
+
const observedRepositoryTools = selectProgressiveTools(allTools, {
|
|
2102
|
+
config: observedRepositoryRuntime,
|
|
2103
|
+
goal: "Commit only the accepted presentation relocation.",
|
|
2104
|
+
profile: "slides",
|
|
2105
|
+
});
|
|
2106
|
+
sameNames(
|
|
2107
|
+
observedRepositoryTools,
|
|
2108
|
+
["commit_project_changes", "finish"],
|
|
2109
|
+
"an observed clean-state recovery did not replace the shell with path-enumerated commit selection"
|
|
2110
|
+
);
|
|
2111
|
+
assertStrict.deepEqual(
|
|
2112
|
+
observedRepositoryTools[0].function.parameters.required,
|
|
2113
|
+
["paths", "message"],
|
|
2114
|
+
"the observed-path commit did not require explicit task-owned path selection"
|
|
2115
|
+
);
|
|
2116
|
+
assertStrict.deepEqual(
|
|
2117
|
+
observedRepositoryTools[0].function.parameters.properties.paths.items.enum,
|
|
2118
|
+
["dist/deck.pdf", "output/deck.pptx", "unrelated-notes.md"],
|
|
2119
|
+
"the observed-path commit broadened its candidate scope beyond the exact status result"
|
|
2120
|
+
);
|
|
2059
2121
|
const taskOwnedRepositoryState = {
|
|
2060
2122
|
meta: {
|
|
2061
2123
|
goalContract: { revision: 6 },
|
package/src/agent-runner.js
CHANGED
|
@@ -2237,6 +2237,11 @@ export function buildKnownConstrainedPhasePlan(
|
|
|
2237
2237
|
Array.isArray(runtimeConfig.repositoryStateRepairCommitPaths) &&
|
|
2238
2238
|
runtimeConfig.repositoryStateRepairCommitPaths.length > 0
|
|
2239
2239
|
);
|
|
2240
|
+
const repositoryObservedCommitReady = Boolean(
|
|
2241
|
+
repositoryStateRepair &&
|
|
2242
|
+
Array.isArray(runtimeConfig.repositoryStateRepairObservedPaths) &&
|
|
2243
|
+
runtimeConfig.repositoryStateRepairObservedPaths.length > 0
|
|
2244
|
+
);
|
|
2240
2245
|
const plan = repositoryStateRepair
|
|
2241
2246
|
? repositoryCommitReady
|
|
2242
2247
|
? [
|
|
@@ -2244,9 +2249,16 @@ export function buildKnownConstrainedPhasePlan(
|
|
|
2244
2249
|
`2. Run the exact retained verification command after that commit: ${command}`,
|
|
2245
2250
|
"3. Finish only after that exact command passes at the current mutation revision.",
|
|
2246
2251
|
].join("\n")
|
|
2252
|
+
: repositoryObservedCommitReady
|
|
2253
|
+
? [
|
|
2254
|
+
"1. Use the offered commit_project_changes tool once and select the complete task-owned subset from the exact observed dirty paths.",
|
|
2255
|
+
"2. Include intended generated-output relocations and deletions, but exclude unrelated pre-existing work.",
|
|
2256
|
+
`3. Run the exact retained verification command after that commit: ${command}`,
|
|
2257
|
+
"4. Finish only after that exact command passes at the current mutation revision.",
|
|
2258
|
+
].join("\n")
|
|
2247
2259
|
: [
|
|
2248
|
-
"1.
|
|
2249
|
-
"2.
|
|
2260
|
+
"1. Run the one exact bounded Git status inspection offered by the runtime.",
|
|
2261
|
+
"2. On the next turn, select and commit only task-owned paths from the runtime-enumerated candidates.",
|
|
2250
2262
|
`3. Run the exact retained verification command after the repository state is clean: ${command}`,
|
|
2251
2263
|
"4. Finish only after that exact command passes at the current mutation revision.",
|
|
2252
2264
|
].join("\n")
|
|
@@ -2294,7 +2306,9 @@ export function buildKnownConstrainedPhasePlan(
|
|
|
2294
2306
|
const recoveryInstruction = repositoryStateRepair
|
|
2295
2307
|
? repositoryCommitReady
|
|
2296
2308
|
? "The current failure is only a repository-state acceptance gate. The runtime already knows the task-owned changed paths, so call commit_project_changes with a concise factual subject. Do not inspect again or invent another source edit."
|
|
2297
|
-
:
|
|
2309
|
+
: repositoryObservedCommitReady
|
|
2310
|
+
? "The bounded status inspection is complete. Select the complete task-owned subset from the exact offered paths and call commit_project_changes once. Do not include unrelated work, inspect again, or invent another source edit."
|
|
2311
|
+
: "The current failure is a repository-state acceptance gate, not a content defect. Run only the exact offered status inspection once. The runtime will convert its result into a bounded path-selection commit phase; do not start a status/diff loop or invent another source edit."
|
|
2298
2312
|
: freshSourceMutation
|
|
2299
2313
|
? runtimeConfig.completionFreshMutationNeedsSourceRead === true
|
|
2300
2314
|
? "Completion was rejected because a fresh canonical source correction is still missing. Read one exact offered project file now; command, test, Git, and finish actions remain intentionally unavailable until the source is grounded and materially patched."
|
|
@@ -6055,6 +6069,15 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
|
|
|
6055
6069
|
generatedArtifactProducer
|
|
6056
6070
|
)
|
|
6057
6071
|
);
|
|
6072
|
+
const commandMutationPaths = Array.isArray(toolResult.projectMutationPaths)
|
|
6073
|
+
? [
|
|
6074
|
+
...new Set(
|
|
6075
|
+
toolResult.projectMutationPaths
|
|
6076
|
+
.map(safeTaskOwnedCommitPath)
|
|
6077
|
+
.filter(Boolean)
|
|
6078
|
+
),
|
|
6079
|
+
].slice(0, 64)
|
|
6080
|
+
: [];
|
|
6058
6081
|
let requiredBatch = currentRequiredCommandBatch(verification, requiredCommands);
|
|
6059
6082
|
const activeExecutionContract = state.meta?.activeExecutionContract;
|
|
6060
6083
|
const activeTurnCommands =
|
|
@@ -6074,6 +6097,7 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
|
|
|
6074
6097
|
let batchRequiredCommands = requiredBatch?.requiredCommands || requiredCommands;
|
|
6075
6098
|
if (projectContentMutation) {
|
|
6076
6099
|
delete state.meta.verifiedCompletionCandidate;
|
|
6100
|
+
delete verification.repositoryStateInspection;
|
|
6077
6101
|
if (
|
|
6078
6102
|
requiredCommand &&
|
|
6079
6103
|
batchRequiredCommands.includes(requiredCommand) &&
|
|
@@ -6108,7 +6132,7 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
|
|
|
6108
6132
|
revision: verification.mutationRevision,
|
|
6109
6133
|
at: now,
|
|
6110
6134
|
toolName,
|
|
6111
|
-
paths:
|
|
6135
|
+
paths: commandMutationPaths,
|
|
6112
6136
|
commandCategory: String(commandPolicy.category || "general-shell"),
|
|
6113
6137
|
}, config);
|
|
6114
6138
|
if (
|
|
@@ -6125,8 +6149,61 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
|
|
|
6125
6149
|
generatedArtifactProducer,
|
|
6126
6150
|
]),
|
|
6127
6151
|
].slice(0, 12);
|
|
6152
|
+
if (commandMutationPaths.length) {
|
|
6153
|
+
activeExecutionContract.materialMutationPaths = [
|
|
6154
|
+
...new Set([
|
|
6155
|
+
...(Array.isArray(activeExecutionContract.materialMutationPaths)
|
|
6156
|
+
? activeExecutionContract.materialMutationPaths
|
|
6157
|
+
: []),
|
|
6158
|
+
...commandMutationPaths,
|
|
6159
|
+
]),
|
|
6160
|
+
].slice(0, 24);
|
|
6161
|
+
}
|
|
6128
6162
|
}
|
|
6129
6163
|
}
|
|
6164
|
+
if (commandIncludesGitCommit(command)) {
|
|
6165
|
+
delete verification.repositoryStateInspection;
|
|
6166
|
+
}
|
|
6167
|
+
if (
|
|
6168
|
+
commandSucceeded &&
|
|
6169
|
+
config.testFailureRepositoryStateRepair === true &&
|
|
6170
|
+
projectCommandsEquivalent(
|
|
6171
|
+
command,
|
|
6172
|
+
REPOSITORY_STATE_INSPECTION_COMMAND,
|
|
6173
|
+
config
|
|
6174
|
+
)
|
|
6175
|
+
) {
|
|
6176
|
+
const currentFailure = [...verification.testRuns]
|
|
6177
|
+
.reverse()
|
|
6178
|
+
.find(
|
|
6179
|
+
(candidate) =>
|
|
6180
|
+
candidate?.passed !== true &&
|
|
6181
|
+
Number(candidate?.mutationRevision || 0) ===
|
|
6182
|
+
verification.mutationRevision
|
|
6183
|
+
);
|
|
6184
|
+
const entries = (
|
|
6185
|
+
Array.isArray(toolResult.repositoryStateStatusEntries)
|
|
6186
|
+
? toolResult.repositoryStateStatusEntries
|
|
6187
|
+
.map((entry) => ({
|
|
6188
|
+
status: String(entry?.status || ""),
|
|
6189
|
+
path: safeTaskOwnedCommitPath(entry?.path),
|
|
6190
|
+
}))
|
|
6191
|
+
.filter((entry) => entry.path)
|
|
6192
|
+
: parseGitPorcelainStatus(toolResult.stdout)
|
|
6193
|
+
).slice(0, 64);
|
|
6194
|
+
verification.repositoryStateInspection = {
|
|
6195
|
+
version: REPOSITORY_STATE_INSPECTION_VERSION,
|
|
6196
|
+
mutationRevision: verification.mutationRevision,
|
|
6197
|
+
failureSignature: String(
|
|
6198
|
+
config.testFailureSignature || currentFailure?.failureSignature || ""
|
|
6199
|
+
),
|
|
6200
|
+
command: REPOSITORY_STATE_INSPECTION_COMMAND,
|
|
6201
|
+
entries,
|
|
6202
|
+
at: now,
|
|
6203
|
+
};
|
|
6204
|
+
toolResult.repositoryStateInspection = verification.repositoryStateInspection;
|
|
6205
|
+
toolResult.repositoryStateObservedPaths = entries.map((entry) => entry.path);
|
|
6206
|
+
}
|
|
6130
6207
|
const run = {
|
|
6131
6208
|
command,
|
|
6132
6209
|
at: now,
|
|
@@ -6400,6 +6477,158 @@ function safeTaskOwnedCommitPath(value = "") {
|
|
|
6400
6477
|
return candidate;
|
|
6401
6478
|
}
|
|
6402
6479
|
|
|
6480
|
+
const REPOSITORY_STATE_INSPECTION_VERSION = 1;
|
|
6481
|
+
const REPOSITORY_STATE_INSPECTION_COMMAND =
|
|
6482
|
+
"git status --porcelain=v1 --untracked-files=all";
|
|
6483
|
+
|
|
6484
|
+
export function repositoryStateInspectionCommand() {
|
|
6485
|
+
return REPOSITORY_STATE_INSPECTION_COMMAND;
|
|
6486
|
+
}
|
|
6487
|
+
|
|
6488
|
+
function decodeGitPorcelainPath(value = "") {
|
|
6489
|
+
const raw = String(value || "").trimEnd();
|
|
6490
|
+
if (!raw.startsWith('"')) return raw;
|
|
6491
|
+
// Git's quoted-path grammar overlaps JSON for ordinary escaped paths. Octal
|
|
6492
|
+
// escapes are deliberately rejected instead of guessing a path that may be
|
|
6493
|
+
// staged later.
|
|
6494
|
+
try {
|
|
6495
|
+
return JSON.parse(raw);
|
|
6496
|
+
} catch {
|
|
6497
|
+
return "";
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
|
|
6501
|
+
export function parseGitPorcelainStatus(output = "") {
|
|
6502
|
+
const entries = [];
|
|
6503
|
+
const seen = new Set();
|
|
6504
|
+
for (const line of String(output || "").replace(/\r/g, "").split("\n")) {
|
|
6505
|
+
if (line.length < 4 || line[2] !== " ") continue;
|
|
6506
|
+
const status = line.slice(0, 2);
|
|
6507
|
+
if (!/^[ MADRCUT?!]{2}$/u.test(status)) continue;
|
|
6508
|
+
const candidate = safeTaskOwnedCommitPath(
|
|
6509
|
+
decodeGitPorcelainPath(line.slice(3))
|
|
6510
|
+
);
|
|
6511
|
+
if (!candidate || seen.has(candidate)) continue;
|
|
6512
|
+
seen.add(candidate);
|
|
6513
|
+
entries.push({ status, path: candidate });
|
|
6514
|
+
if (entries.length >= 256) break;
|
|
6515
|
+
}
|
|
6516
|
+
return entries;
|
|
6517
|
+
}
|
|
6518
|
+
|
|
6519
|
+
async function gitStatusPathFingerprint(commandCwd = "", relativePath = "") {
|
|
6520
|
+
const root = path.resolve(commandCwd || process.cwd());
|
|
6521
|
+
const candidate = safeTaskOwnedCommitPath(relativePath);
|
|
6522
|
+
if (!candidate) return "";
|
|
6523
|
+
const absolutePath = path.resolve(root, candidate);
|
|
6524
|
+
const relative = path.relative(root, absolutePath);
|
|
6525
|
+
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return "";
|
|
6526
|
+
try {
|
|
6527
|
+
const stat = await fs.lstat(absolutePath);
|
|
6528
|
+
return [
|
|
6529
|
+
stat.isFile() ? "file" : stat.isDirectory() ? "directory" : stat.isSymbolicLink() ? "symlink" : "other",
|
|
6530
|
+
Number(stat.mode || 0),
|
|
6531
|
+
Number(stat.size || 0),
|
|
6532
|
+
Number(stat.mtimeMs || 0),
|
|
6533
|
+
Number(stat.ctimeMs || 0),
|
|
6534
|
+
Number(stat.ino || 0),
|
|
6535
|
+
].join(":");
|
|
6536
|
+
} catch (error) {
|
|
6537
|
+
return error?.code === "ENOENT" ? "missing" : "unavailable";
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
|
|
6541
|
+
async function captureGitWorktreeSnapshot(config = {}) {
|
|
6542
|
+
const commandCwd = path.resolve(config.commandCwd || process.cwd());
|
|
6543
|
+
const stdout = await new Promise((resolve) => {
|
|
6544
|
+
let text = "";
|
|
6545
|
+
let settled = false;
|
|
6546
|
+
let timer;
|
|
6547
|
+
const finish = (value = null) => {
|
|
6548
|
+
if (settled) return;
|
|
6549
|
+
settled = true;
|
|
6550
|
+
if (timer) clearTimeout(timer);
|
|
6551
|
+
resolve(value);
|
|
6552
|
+
};
|
|
6553
|
+
const child = spawn(
|
|
6554
|
+
"git",
|
|
6555
|
+
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
6556
|
+
{
|
|
6557
|
+
cwd: commandCwd,
|
|
6558
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
6559
|
+
windowsHide: true,
|
|
6560
|
+
}
|
|
6561
|
+
);
|
|
6562
|
+
timer = setTimeout(() => {
|
|
6563
|
+
child.kill("SIGKILL");
|
|
6564
|
+
finish(null);
|
|
6565
|
+
}, 5000);
|
|
6566
|
+
child.stdout?.on("data", (chunk) => {
|
|
6567
|
+
text += String(chunk || "");
|
|
6568
|
+
if (text.length > 1024 * 1024) {
|
|
6569
|
+
child.kill("SIGKILL");
|
|
6570
|
+
finish(null);
|
|
6571
|
+
}
|
|
6572
|
+
});
|
|
6573
|
+
child.on("error", () => finish(null));
|
|
6574
|
+
child.on("close", (code) => finish(code === 0 ? text : null));
|
|
6575
|
+
});
|
|
6576
|
+
if (typeof stdout !== "string") return null;
|
|
6577
|
+
const parsed = parseGitPorcelainStatus(stdout);
|
|
6578
|
+
if (parsed.length >= 256) return null;
|
|
6579
|
+
const entries = await Promise.all(
|
|
6580
|
+
parsed.map(async (entry) => ({
|
|
6581
|
+
...entry,
|
|
6582
|
+
fingerprint: await gitStatusPathFingerprint(commandCwd, entry.path),
|
|
6583
|
+
}))
|
|
6584
|
+
);
|
|
6585
|
+
return { version: 1, entries };
|
|
6586
|
+
}
|
|
6587
|
+
|
|
6588
|
+
export function changedGitStatusPaths(before = null, after = null) {
|
|
6589
|
+
if (!before || !after || !Array.isArray(before.entries) || !Array.isArray(after.entries)) {
|
|
6590
|
+
return [];
|
|
6591
|
+
}
|
|
6592
|
+
const prior = new Map(
|
|
6593
|
+
before.entries
|
|
6594
|
+
.filter((entry) => safeTaskOwnedCommitPath(entry?.path))
|
|
6595
|
+
.map((entry) => [String(entry.path), entry])
|
|
6596
|
+
);
|
|
6597
|
+
return [
|
|
6598
|
+
...new Set(
|
|
6599
|
+
after.entries
|
|
6600
|
+
.filter((entry) => {
|
|
6601
|
+
const candidate = safeTaskOwnedCommitPath(entry?.path);
|
|
6602
|
+
if (!candidate) return false;
|
|
6603
|
+
const previous = prior.get(candidate);
|
|
6604
|
+
return Boolean(
|
|
6605
|
+
!previous ||
|
|
6606
|
+
String(previous.status || "") !== String(entry.status || "") ||
|
|
6607
|
+
String(previous.fingerprint || "") !== String(entry.fingerprint || "")
|
|
6608
|
+
);
|
|
6609
|
+
})
|
|
6610
|
+
.map((entry) => safeTaskOwnedCommitPath(entry.path))
|
|
6611
|
+
.filter(Boolean)
|
|
6612
|
+
),
|
|
6613
|
+
].slice(0, 64);
|
|
6614
|
+
}
|
|
6615
|
+
|
|
6616
|
+
function currentRepositoryStateInspection(verification = {}, testRun = {}) {
|
|
6617
|
+
const inspection = verification.repositoryStateInspection;
|
|
6618
|
+
if (
|
|
6619
|
+
!inspection ||
|
|
6620
|
+
Number(inspection.version || 0) !== REPOSITORY_STATE_INSPECTION_VERSION ||
|
|
6621
|
+
Number(inspection.mutationRevision || 0) !==
|
|
6622
|
+
Math.max(0, Number(verification.mutationRevision || 0)) ||
|
|
6623
|
+
String(inspection.failureSignature || "") !==
|
|
6624
|
+
String(testRun?.failureSignature || "") ||
|
|
6625
|
+
!Array.isArray(inspection.entries)
|
|
6626
|
+
) {
|
|
6627
|
+
return null;
|
|
6628
|
+
}
|
|
6629
|
+
return inspection;
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6403
6632
|
function taskOwnedMutationPathsSinceLatestCommit(verification = {}) {
|
|
6404
6633
|
const latestCommittedRevision = [...(verification.commandRuns || [])]
|
|
6405
6634
|
.reverse()
|
|
@@ -7932,6 +8161,8 @@ function runCommandResultHasDurableProgress(toolResult = {}) {
|
|
|
7932
8161
|
return Boolean(
|
|
7933
8162
|
policyAllowsMutation ||
|
|
7934
8163
|
policy.substantiveTest === true ||
|
|
8164
|
+
(Array.isArray(toolResult.projectMutationPaths) &&
|
|
8165
|
+
toolResult.projectMutationPaths.length > 0) ||
|
|
7935
8166
|
(Array.isArray(toolResult.verifiedGeneratedOutputPaths) &&
|
|
7936
8167
|
toolResult.verifiedGeneratedOutputPaths.length > 0)
|
|
7937
8168
|
);
|
|
@@ -11944,6 +12175,29 @@ export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
|
11944
12175
|
runtimeConfig.testFailureRepairPatchTargets = [];
|
|
11945
12176
|
runtimeConfig.repositoryStateRepairCommitPaths =
|
|
11946
12177
|
taskOwnedMutationPathsSinceLatestCommit(verification);
|
|
12178
|
+
const repositoryInspection = currentRepositoryStateInspection(
|
|
12179
|
+
verification,
|
|
12180
|
+
retainedFailedTest
|
|
12181
|
+
);
|
|
12182
|
+
const observedEntries = runtimeConfig.repositoryStateRepairCommitPaths.length
|
|
12183
|
+
? []
|
|
12184
|
+
: (repositoryInspection?.entries || [])
|
|
12185
|
+
.map((entry) => ({
|
|
12186
|
+
status: String(entry?.status || ""),
|
|
12187
|
+
path: safeTaskOwnedCommitPath(entry?.path),
|
|
12188
|
+
}))
|
|
12189
|
+
.filter((entry) => entry.path)
|
|
12190
|
+
.slice(0, 64);
|
|
12191
|
+
runtimeConfig.repositoryStateRepairObservedEntries = observedEntries;
|
|
12192
|
+
runtimeConfig.repositoryStateRepairObservedPaths = observedEntries.map(
|
|
12193
|
+
(entry) => entry.path
|
|
12194
|
+
);
|
|
12195
|
+
runtimeConfig.repositoryStateRepairInspectionCommand =
|
|
12196
|
+
REPOSITORY_STATE_INSPECTION_COMMAND;
|
|
12197
|
+
runtimeConfig.repositoryStateRepairInspectionRequired = Boolean(
|
|
12198
|
+
runtimeConfig.repositoryStateRepairCommitPaths.length === 0 &&
|
|
12199
|
+
!repositoryInspection
|
|
12200
|
+
);
|
|
11947
12201
|
} else if (retainedRepositoryStateRepair) {
|
|
11948
12202
|
delete runtimeConfig.completionFreshMutationRequired;
|
|
11949
12203
|
delete runtimeConfig.completionFreshMutationRevision;
|
|
@@ -14258,20 +14512,45 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
14258
14512
|
? config.taskOwnedCommitPaths
|
|
14259
14513
|
: []),
|
|
14260
14514
|
].map(safeTaskOwnedCommitPath).filter(Boolean);
|
|
14515
|
+
const observedRepositoryCommitPaths = Array.isArray(
|
|
14516
|
+
config.repositoryStateRepairObservedPaths
|
|
14517
|
+
)
|
|
14518
|
+
? config.repositoryStateRepairObservedPaths
|
|
14519
|
+
.map(safeTaskOwnedCommitPath)
|
|
14520
|
+
.filter(Boolean)
|
|
14521
|
+
: [];
|
|
14522
|
+
const requestedObservedCommitPaths = Array.isArray(args.paths)
|
|
14523
|
+
? [...new Set(args.paths.map(safeTaskOwnedCommitPath).filter(Boolean))]
|
|
14524
|
+
: [];
|
|
14525
|
+
const observedCommitPathSet = new Set(observedRepositoryCommitPaths);
|
|
14526
|
+
const selectedRepositoryCommitPaths = repositoryCommitPaths.length
|
|
14527
|
+
? repositoryCommitPaths
|
|
14528
|
+
: requestedObservedCommitPaths.length > 0 &&
|
|
14529
|
+
requestedObservedCommitPaths.length === args.paths?.length &&
|
|
14530
|
+
requestedObservedCommitPaths.every((candidate) =>
|
|
14531
|
+
observedCommitPathSet.has(candidate)
|
|
14532
|
+
)
|
|
14533
|
+
? requestedObservedCommitPaths
|
|
14534
|
+
: [];
|
|
14261
14535
|
if (
|
|
14262
14536
|
requestedToolName === "commit_project_changes" &&
|
|
14263
|
-
|
|
14537
|
+
selectedRepositoryCommitPaths.length > 0 &&
|
|
14264
14538
|
typeof args.message === "string" &&
|
|
14265
14539
|
args.message.trim()
|
|
14266
14540
|
) {
|
|
14267
|
-
const commitCommand = buildTaskOwnedCommitCommand(
|
|
14541
|
+
const commitCommand = buildTaskOwnedCommitCommand(
|
|
14542
|
+
selectedRepositoryCommitPaths,
|
|
14543
|
+
args.message
|
|
14544
|
+
);
|
|
14268
14545
|
if (commitCommand) {
|
|
14269
14546
|
args = { command: commitCommand };
|
|
14270
14547
|
toolName = "run_command";
|
|
14271
14548
|
autoCorrection = {
|
|
14272
14549
|
requestedToolName,
|
|
14273
14550
|
toolName,
|
|
14274
|
-
reason:
|
|
14551
|
+
reason: repositoryCommitPaths.length
|
|
14552
|
+
? "evidence-derived-task-owned-commit"
|
|
14553
|
+
: "observed-task-owned-commit-selection",
|
|
14275
14554
|
};
|
|
14276
14555
|
}
|
|
14277
14556
|
}
|
|
@@ -15057,6 +15336,20 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
15057
15336
|
const policy = evaluateCommandPolicy(rawCommand, config);
|
|
15058
15337
|
const observedCommand = normalizeProjectCommand(rawCommand);
|
|
15059
15338
|
const observedInnerCommand = explicitExitProbeStatus(observedCommand).command;
|
|
15339
|
+
const repositoryStateInspectionRequested = projectCommandsEquivalent(
|
|
15340
|
+
observedCommand,
|
|
15341
|
+
REPOSITORY_STATE_INSPECTION_COMMAND,
|
|
15342
|
+
config
|
|
15343
|
+
);
|
|
15344
|
+
const generatedArtifactProducer = safeProjectProducerSegment(
|
|
15345
|
+
observedCommand,
|
|
15346
|
+
config
|
|
15347
|
+
);
|
|
15348
|
+
const captureMutationScope = Boolean(
|
|
15349
|
+
policy.writesWorkspace ||
|
|
15350
|
+
policy.mayMutateProject === true ||
|
|
15351
|
+
generatedArtifactProducer
|
|
15352
|
+
);
|
|
15060
15353
|
const requiredCommands = effectiveRequiredProjectCommands(
|
|
15061
15354
|
state,
|
|
15062
15355
|
state.meta?.projectVerification || {},
|
|
@@ -15068,10 +15361,24 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
15068
15361
|
const exactOutputSnapshotsBefore = isRequiredCommand && policy.writesWorkspace
|
|
15069
15362
|
? await captureExactOutputSnapshots(state, config)
|
|
15070
15363
|
: [];
|
|
15364
|
+
const gitWorktreeBefore = captureMutationScope
|
|
15365
|
+
? await captureGitWorktreeSnapshot(config)
|
|
15366
|
+
: null;
|
|
15071
15367
|
if (config.useDockerSandbox) {
|
|
15072
15368
|
await ensureDockerSandboxReady(config, observers);
|
|
15073
15369
|
}
|
|
15074
15370
|
const commandResult = await runShellCommand(rawCommand, config, policy);
|
|
15371
|
+
const gitWorktreeAfter = captureMutationScope
|
|
15372
|
+
? await captureGitWorktreeSnapshot(config)
|
|
15373
|
+
: null;
|
|
15374
|
+
const repositoryStateInspectionSnapshot =
|
|
15375
|
+
repositoryStateInspectionRequested && commandResult.ok !== false
|
|
15376
|
+
? await captureGitWorktreeSnapshot(config)
|
|
15377
|
+
: null;
|
|
15378
|
+
const projectMutationPaths = changedGitStatusPaths(
|
|
15379
|
+
gitWorktreeBefore,
|
|
15380
|
+
gitWorktreeAfter
|
|
15381
|
+
);
|
|
15075
15382
|
const generatedOutputPaths = commandResult.ok !== false
|
|
15076
15383
|
? await verifiedGeneratedOutputPaths(state, exactOutputSnapshotsBefore, config)
|
|
15077
15384
|
: [];
|
|
@@ -15106,6 +15413,20 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
15106
15413
|
...(generatedOutputPaths.length
|
|
15107
15414
|
? { verifiedGeneratedOutputPaths: generatedOutputPaths }
|
|
15108
15415
|
: {}),
|
|
15416
|
+
...(projectMutationPaths.length
|
|
15417
|
+
? { projectMutationPaths }
|
|
15418
|
+
: {}),
|
|
15419
|
+
...(repositoryStateInspectionSnapshot
|
|
15420
|
+
? {
|
|
15421
|
+
repositoryStateStatusEntries:
|
|
15422
|
+
repositoryStateInspectionSnapshot.entries.map(
|
|
15423
|
+
({ status, path: statusPath }) => ({
|
|
15424
|
+
status,
|
|
15425
|
+
path: statusPath,
|
|
15426
|
+
})
|
|
15427
|
+
),
|
|
15428
|
+
}
|
|
15429
|
+
: {}),
|
|
15109
15430
|
...commandResult,
|
|
15110
15431
|
...(permissionAdvice ? { permissionAdvice } : {}),
|
|
15111
15432
|
};
|
|
@@ -1646,6 +1646,51 @@ function constrainRepositoryStateCommit(tool, paths = []) {
|
|
|
1646
1646
|
};
|
|
1647
1647
|
}
|
|
1648
1648
|
|
|
1649
|
+
function constrainObservedRepositoryStateCommit(tool, paths = []) {
|
|
1650
|
+
if (!tool || !Array.isArray(paths) || paths.length === 0) return null;
|
|
1651
|
+
const candidates = [...new Set(paths.map((item) => String(item || "").trim()).filter(Boolean))]
|
|
1652
|
+
.slice(0, 64);
|
|
1653
|
+
if (!candidates.length) return null;
|
|
1654
|
+
return {
|
|
1655
|
+
...tool,
|
|
1656
|
+
function: {
|
|
1657
|
+
...tool.function,
|
|
1658
|
+
name: "commit_project_changes",
|
|
1659
|
+
description: [
|
|
1660
|
+
"Commit only the task-owned subset of the exact currently dirty paths discovered by the bounded repository inspection.",
|
|
1661
|
+
"Select every intended task source, generated output, relocation deletion, or manifest involved in this task, and exclude unrelated pre-existing work.",
|
|
1662
|
+
"The runtime rejects paths outside this exact observation and stages only the selected paths before creating one local commit.",
|
|
1663
|
+
`Observed candidates: ${candidates.join(", ")}.`,
|
|
1664
|
+
].join(" "),
|
|
1665
|
+
parameters: {
|
|
1666
|
+
type: "object",
|
|
1667
|
+
properties: {
|
|
1668
|
+
paths: {
|
|
1669
|
+
type: "array",
|
|
1670
|
+
minItems: 1,
|
|
1671
|
+
maxItems: candidates.length,
|
|
1672
|
+
uniqueItems: true,
|
|
1673
|
+
items: {
|
|
1674
|
+
type: "string",
|
|
1675
|
+
enum: candidates,
|
|
1676
|
+
},
|
|
1677
|
+
description: "The complete task-owned subset of the exact observed dirty paths.",
|
|
1678
|
+
},
|
|
1679
|
+
message: {
|
|
1680
|
+
type: "string",
|
|
1681
|
+
minLength: 3,
|
|
1682
|
+
maxLength: 120,
|
|
1683
|
+
pattern: "^[^\\r\\n\\u0000]+$",
|
|
1684
|
+
description: "A concise factual Git commit subject for the selected task-owned changes.",
|
|
1685
|
+
},
|
|
1686
|
+
},
|
|
1687
|
+
required: ["paths", "message"],
|
|
1688
|
+
additionalProperties: false,
|
|
1689
|
+
},
|
|
1690
|
+
},
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1649
1694
|
function roundRobinToolNames(groups) {
|
|
1650
1695
|
const names = [];
|
|
1651
1696
|
const maxLength = Math.max(0, ...groups.map((group) => group.length));
|
|
@@ -2000,18 +2045,18 @@ export function selectProgressiveTools(
|
|
|
2000
2045
|
if (commitProjectChanges) {
|
|
2001
2046
|
return [commitProjectChanges, finish].filter(Boolean);
|
|
2002
2047
|
}
|
|
2003
|
-
const
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2048
|
+
const observedCommit = constrainObservedRepositoryStateCommit(
|
|
2049
|
+
runCommand,
|
|
2050
|
+
config.repositoryStateRepairObservedPaths
|
|
2051
|
+
);
|
|
2052
|
+
if (observedCommit) {
|
|
2053
|
+
return [observedCommit, finish].filter(Boolean);
|
|
2054
|
+
}
|
|
2055
|
+
const repositoryStateTool = constrainRunCommand(
|
|
2056
|
+
runCommand,
|
|
2057
|
+
config.repositoryStateRepairInspectionCommand,
|
|
2058
|
+
"Run this exact bounded Git status inspection once. The runtime records the current dirty paths and will offer a path-enumerated commit tool on the next turn; do not substitute a broader status/diff loop or alter project content."
|
|
2059
|
+
);
|
|
2015
2060
|
return [repositoryStateTool, finish].filter(Boolean);
|
|
2016
2061
|
}
|
|
2017
2062
|
const constrainedRepairPatch = annotateRequiredSymbolRepair(
|