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