vibe-coding-master 0.7.33 → 0.7.35
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/README.md +25 -12
- package/dist/backend/adapters/git-adapter.js +21 -0
- package/dist/backend/api/gate-review-routes.js +5 -0
- package/dist/backend/services/auto-memory-service.js +115 -81
- package/dist/backend/services/ccr-integration-service.js +2 -1
- package/dist/backend/services/claude-hook-service.js +11 -0
- package/dist/backend/services/gate-review-service.js +354 -107
- package/dist/backend/services/harness-feedback-service.js +6 -3
- package/dist/backend/services/session-service.js +42 -52
- package/dist/backend/templates/handoff.js +17 -0
- package/dist/backend/templates/harness/architect-agent.js +5 -2
- package/dist/backend/templates/harness/claude-root.js +8 -0
- package/dist/backend/templates/harness/coder-agent.js +1 -1
- package/dist/backend/templates/harness/gate-review.js +2 -0
- package/dist/backend/templates/harness/harness-engineer-agent.js +14 -9
- package/dist/backend/templates/harness/tester-agent.js +1 -1
- package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -0
- package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +3 -1
- package/dist/backend/templates/harness/vcm-route-message-skill.js +1 -1
- package/dist/shared/types/session.js +1 -0
- package/dist-frontend/assets/index-DN9kwoOt.css +32 -0
- package/dist-frontend/assets/{index-Cmr9W7-n.js → index-DhLRhgSV.js} +35 -35
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/scripts/harness-tools/vcm-bash-guard +62 -8
- package/dist/backend/services/memory-review-validation.js +0 -286
- package/dist-frontend/assets/index-CguKU8Q5.css +0 -32
package/README.md
CHANGED
|
@@ -277,6 +277,11 @@ gateway from the native child process. Configure CCR without enabling its
|
|
|
277
277
|
global Claude Code or Claude App takeover if those clients should remain on
|
|
278
278
|
Anthropic.
|
|
279
279
|
|
|
280
|
+
CCR/GPT child processes use an effective context limit of `258400` tokens.
|
|
281
|
+
VCM leaves Claude Code's automatic compaction threshold unset so Claude Code
|
|
282
|
+
can compact against that limit. Native Claude sessions receive no VCM-owned
|
|
283
|
+
context override.
|
|
284
|
+
|
|
280
285
|
Resume keeps the provider recorded by the existing Session. Use Restart when
|
|
281
286
|
switching between a native Claude model and `GPT-5.6 Sol (CCR)`. If CCR is
|
|
282
287
|
disabled, unreachable, rejects the key, or does not expose
|
|
@@ -344,6 +349,11 @@ only:
|
|
|
344
349
|
Reviewer does not run tests and does not choose owners or fixes. PM routes
|
|
345
350
|
findings back to the responsible role.
|
|
346
351
|
|
|
352
|
+
Each run has its own request ID and immutable request artifacts. A running
|
|
353
|
+
review must be cancelled before replacement; cancellation restarts Reviewer,
|
|
354
|
+
and a late result from the cancelled request cannot replace the current Gate
|
|
355
|
+
state, stable report, or PM callback.
|
|
356
|
+
|
|
347
357
|
## Translation
|
|
348
358
|
|
|
349
359
|
Conversation translation is controlled from the sidebar `Translation` section.
|
|
@@ -467,8 +477,9 @@ files.
|
|
|
467
477
|
Review Task Harness after Final Acceptance, Project Manager, Architect, Coder,
|
|
468
478
|
Tester, and an enabled Reviewer submit proposals in sequence through
|
|
469
479
|
`vcm-propose-memory`. Harness Engineer verifies and consolidates them before VCM
|
|
470
|
-
|
|
471
|
-
edit active memory directly
|
|
480
|
+
records the result as part of the same Task Harness Retrospective. Workflow roles
|
|
481
|
+
cannot edit active memory directly; Harness Engineer edits it only during the
|
|
482
|
+
assigned Auto Memory Retrospective.
|
|
472
483
|
|
|
473
484
|
When Auto Memory is enabled, the planning Architect writes a provisional memory
|
|
474
485
|
candidate before its post-planning Session restart. VCM snapshots that candidate
|
|
@@ -477,11 +488,12 @@ the completed implementation and tests, and Harness Engineer reviews it with all
|
|
|
477
488
|
final role proposals before anything becomes active memory.
|
|
478
489
|
|
|
479
490
|
Shared memory is stored in the root `CLAUDE.md` `<VCM-memory>` block. Role memory
|
|
480
|
-
is stored in the matching `.claude/agents/*.md` block.
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
491
|
+
is stored in the matching `.claude/agents/*.md` block. Harness Engineer changes
|
|
492
|
+
only those blocks and creates a dedicated commit in the active task worktree.
|
|
493
|
+
VCM verifies the mechanical commit boundary and records the after snapshot and
|
|
494
|
+
diff. Harness Studio shows current memory and task-local applied history. Memory
|
|
495
|
+
is applied before user review; while the task worktree remains available, the
|
|
496
|
+
user can edit current memory or revert a recorded change through another commit.
|
|
485
497
|
|
|
486
498
|
Post-task processing is ordered by the backend:
|
|
487
499
|
|
|
@@ -493,7 +505,8 @@ Final Acceptance
|
|
|
493
505
|
-> Task Harness Retrospective
|
|
494
506
|
-> Review pending Harness Feedback
|
|
495
507
|
-> Review Auto Memory proposals, when Auto Memory is enabled
|
|
496
|
-
|
|
508
|
+
-> Harness Engineer updates and commits memory, when Auto Memory is enabled
|
|
509
|
+
-> VCM records the committed memory result
|
|
497
510
|
```
|
|
498
511
|
|
|
499
512
|
Memory proposal prompts sent to Project Manager, Architect, Coder, Tester, and
|
|
@@ -507,10 +520,10 @@ impact, and durable-document disposition, then evaluates every proposal item
|
|
|
507
520
|
independently. Each Add or Update decision records why the memory is necessary,
|
|
508
521
|
what happens if it is absent, whether it belongs in memory or a durable
|
|
509
522
|
document, and the exact final memory content when retained. VCM validates that
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
completion.
|
|
523
|
+
the commit changes only assigned memory host files and only their
|
|
524
|
+
`<VCM-memory>` blocks; it does not parse or apply Harness Engineer's semantic
|
|
525
|
+
decisions. Harness Engineer review and retrospective work remain tool role
|
|
526
|
+
activity and do not participate in Round completion.
|
|
514
527
|
|
|
515
528
|
When Auto Memory is disabled, Review Task Harness does not collect proposals or
|
|
516
529
|
ask Harness Engineer to update memory. When enabled, both automatic and manual
|
|
@@ -157,6 +157,27 @@ export function createGitAdapter(runner) {
|
|
|
157
157
|
}
|
|
158
158
|
return result.stdout;
|
|
159
159
|
},
|
|
160
|
+
async getChangedPaths(repoRoot, baseRef, headRef = null) {
|
|
161
|
+
const result = await runGit(runner, repoRoot, [
|
|
162
|
+
"diff",
|
|
163
|
+
"--name-only",
|
|
164
|
+
"-z",
|
|
165
|
+
baseRef,
|
|
166
|
+
...(headRef ? [headRef] : [])
|
|
167
|
+
]);
|
|
168
|
+
if (result.exitCode !== 0) {
|
|
169
|
+
throw new VcmError({
|
|
170
|
+
code: "GIT_ERROR",
|
|
171
|
+
message: "Unable to read changed Git paths.",
|
|
172
|
+
statusCode: 400,
|
|
173
|
+
hint: result.stderr
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return result.stdout
|
|
177
|
+
.split("\0")
|
|
178
|
+
.map((changedPath) => changedPath.trim())
|
|
179
|
+
.filter(Boolean);
|
|
180
|
+
},
|
|
160
181
|
async getCommitList(repoRoot, range) {
|
|
161
182
|
const result = await runGit(runner, repoRoot, ["log", "--format=%H%x00%s%x00%cI%x1e", range]);
|
|
162
183
|
if (result.exitCode !== 0) {
|
|
@@ -19,6 +19,11 @@ export function registerGateReviewRoutes(app, deps) {
|
|
|
19
19
|
const gate = parseGate(request.params.gate);
|
|
20
20
|
return deps.gateReviewService.retryReviewGate(project.repoRoot, request.params.taskSlug, gate);
|
|
21
21
|
});
|
|
22
|
+
app.post("/api/tasks/:taskSlug/gate-review/:gate/cancel", async (request) => {
|
|
23
|
+
const project = await requireCurrentProject(deps.projectService);
|
|
24
|
+
const gate = parseGate(request.params.gate);
|
|
25
|
+
return deps.gateReviewService.cancelReviewGate(project.repoRoot, request.params.taskSlug, gate, request.body);
|
|
26
|
+
});
|
|
22
27
|
app.post("/api/tasks/:taskSlug/gate-review/:gate/skip", async (request) => {
|
|
23
28
|
const project = await requireCurrentProject(deps.projectService);
|
|
24
29
|
const gate = parseGate(request.params.gate);
|
|
@@ -7,7 +7,6 @@ import { submitTerminalInput } from "../runtime/terminal-submit.js";
|
|
|
7
7
|
import { readVcmMemoryBlock, replaceVcmMemoryBlock } from "../templates/harness/memory-block.js";
|
|
8
8
|
import { ARCHITECT_PLANNING_MEMORY_CANDIDATE_PATH, architectPlanningCandidateSnapshotPath, MEMORY_REVIEW_RUNS_ROOT, MEMORY_REVIEW_STATE_PATH } from "./memory-review-paths.js";
|
|
9
9
|
import { parseMemoryProposal, validateMemoryProposal } from "./memory-proposal-validation.js";
|
|
10
|
-
import { parseMemoryReviewReport, validateMemoryReviewOutput } from "./memory-review-validation.js";
|
|
11
10
|
const MEMORY_FILE_DEFINITIONS = [
|
|
12
11
|
{ path: "CLAUDE.md", title: "Shared Memory" },
|
|
13
12
|
{ path: ".claude/agents/project-manager.md", title: "Project Manager Memory", role: "project-manager" },
|
|
@@ -97,6 +96,26 @@ export function createAutoMemoryService(deps) {
|
|
|
97
96
|
}
|
|
98
97
|
return memory;
|
|
99
98
|
}
|
|
99
|
+
async function writeRunMemoryHostSnapshot(taskRepoRoot, runId) {
|
|
100
|
+
for (const definition of MEMORY_FILE_DEFINITIONS) {
|
|
101
|
+
await deps.fs.writeText(resolveRepoPath(taskRepoRoot, memoryRunHostFilePath(runId, definition.path)), await deps.fs.readText(resolveRepoPath(taskRepoRoot, definition.path)));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function assertOnlyMemoryBlocksChanged(taskRepoRoot, runId, currentMemory) {
|
|
105
|
+
for (const definition of MEMORY_FILE_DEFINITIONS) {
|
|
106
|
+
const beforeHost = await deps.fs.readText(resolveRepoPath(taskRepoRoot, memoryRunHostFilePath(runId, definition.path)));
|
|
107
|
+
const currentHost = await deps.fs.readText(resolveRepoPath(taskRepoRoot, definition.path));
|
|
108
|
+
const expectedHost = replaceVcmMemoryBlock(beforeHost, currentMemory[definition.path]);
|
|
109
|
+
if (currentHost !== expectedHost) {
|
|
110
|
+
throw new VcmError({
|
|
111
|
+
code: "MEMORY_REVIEW_SCOPE_CHANGED",
|
|
112
|
+
message: `Harness Engineer changed content outside the VCM memory block: ${definition.path}`,
|
|
113
|
+
statusCode: 409,
|
|
114
|
+
hint: "Restore non-memory content, keep only the reviewed <VCM-memory> edit, and commit the correction."
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
100
119
|
async function getState(baseRepoRoot, taskRepoRoot) {
|
|
101
120
|
let [active, runs, memoryFiles] = await Promise.all([
|
|
102
121
|
loadActiveState(taskRepoRoot),
|
|
@@ -172,7 +191,6 @@ export function createAutoMemoryService(deps) {
|
|
|
172
191
|
};
|
|
173
192
|
const before = await readMemorySet(input.taskRepoRoot);
|
|
174
193
|
await writeRunMemorySet(input.taskRepoRoot, runId, "before", before);
|
|
175
|
-
await writeRunMemorySet(input.taskRepoRoot, runId, "after", before);
|
|
176
194
|
await snapshotArchitectPlanningCandidate(input.taskRepoRoot, runId);
|
|
177
195
|
await persistRun(input.taskRepoRoot, {
|
|
178
196
|
version: 1,
|
|
@@ -252,9 +270,31 @@ export function createAutoMemoryService(deps) {
|
|
|
252
270
|
});
|
|
253
271
|
}
|
|
254
272
|
const proposalCandidates = await readReviewCandidates(taskRepoRoot, state);
|
|
273
|
+
const currentMemory = await readMemorySet(taskRepoRoot);
|
|
274
|
+
const beforeMemory = await readRunMemorySet(taskRepoRoot, state.runId, "before");
|
|
275
|
+
if (!sameHashes(hashMemorySet(currentMemory), hashMemorySet(beforeMemory))) {
|
|
276
|
+
throw new VcmError({
|
|
277
|
+
code: "MEMORY_TARGET_CHANGED",
|
|
278
|
+
message: "VCM memory changed while role proposals were being collected.",
|
|
279
|
+
statusCode: 409,
|
|
280
|
+
hint: "Review the current memory, then retry Auto Memory."
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
const memoryPaths = MEMORY_FILE_DEFINITIONS.map((definition) => definition.path);
|
|
284
|
+
const existingDiff = await deps.git.getDiff(taskRepoRoot, "HEAD", null, memoryPaths);
|
|
285
|
+
if (existingDiff.trim()) {
|
|
286
|
+
throw new VcmError({
|
|
287
|
+
code: "MEMORY_HOST_FILE_DIRTY",
|
|
288
|
+
message: "A file containing VCM memory has uncommitted changes before Harness Engineer review.",
|
|
289
|
+
statusCode: 409,
|
|
290
|
+
hint: "Commit or discard the existing host-file changes before starting Task Harness Retrospective."
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
await writeRunMemoryHostSnapshot(taskRepoRoot, state.runId);
|
|
255
294
|
const timestamp = now();
|
|
256
295
|
state.reviewPromptDispatchedAt = timestamp;
|
|
257
296
|
state.retrospectiveReportPath = retrospectiveReportPath;
|
|
297
|
+
state.reviewBaseCommit = await deps.git.getHeadCommit(taskRepoRoot);
|
|
258
298
|
state.updatedAt = timestamp;
|
|
259
299
|
await persistActiveState(taskRepoRoot, state);
|
|
260
300
|
const runRoot = resolveRepoPath(taskRepoRoot, `${MEMORY_REVIEW_RUNS_ROOT}/${state.runId}`);
|
|
@@ -263,7 +303,7 @@ export function createAutoMemoryService(deps) {
|
|
|
263
303
|
runId: state.runId,
|
|
264
304
|
roleDraftsPath: path.join(runRoot, "drafts"),
|
|
265
305
|
currentMemoryPath: path.join(runRoot, "before"),
|
|
266
|
-
|
|
306
|
+
activeMemoryPaths: memoryPaths.map((memoryPath) => resolveRepoPath(taskRepoRoot, memoryPath)),
|
|
267
307
|
proposalCandidates,
|
|
268
308
|
...(planningCandidatePath
|
|
269
309
|
? { planningCandidatePath: resolveRepoPath(taskRepoRoot, planningCandidatePath) }
|
|
@@ -277,6 +317,7 @@ export function createAutoMemoryService(deps) {
|
|
|
277
317
|
}
|
|
278
318
|
delete state.reviewPromptDispatchedAt;
|
|
279
319
|
delete state.retrospectiveReportPath;
|
|
320
|
+
delete state.reviewBaseCommit;
|
|
280
321
|
state.updatedAt = now();
|
|
281
322
|
await persistActiveState(taskRepoRoot, state);
|
|
282
323
|
}
|
|
@@ -362,26 +403,12 @@ export function createAutoMemoryService(deps) {
|
|
|
362
403
|
return true;
|
|
363
404
|
}
|
|
364
405
|
try {
|
|
365
|
-
|
|
366
|
-
const currentMemorySnapshot = await readRunMemorySet(input.taskRepoRoot, state.runId, "before");
|
|
367
|
-
const reviewedMemorySnapshot = await readRunMemorySet(input.taskRepoRoot, state.runId, "after");
|
|
368
|
-
const candidates = await readReviewCandidates(input.taskRepoRoot, state);
|
|
369
|
-
const reportResult = parseMemoryReviewReport(report, candidates, hasSubstantiveMemory(currentMemorySnapshot));
|
|
370
|
-
if (reportResult.error) {
|
|
371
|
-
await failReview(input.taskRepoRoot, state, `Task Harness Retrospective memory review report ${reportResult.error}.`);
|
|
372
|
-
return true;
|
|
373
|
-
}
|
|
374
|
-
const outputError = await validateReviewedMemoryOutput(input.taskRepoRoot, currentMemorySnapshot, reviewedMemorySnapshot, reportResult.decisions ?? []);
|
|
375
|
-
if (outputError) {
|
|
376
|
-
await failReview(input.taskRepoRoot, state, `Task Harness Retrospective reviewed memory ${outputError}.`);
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
406
|
+
await recordHarnessEngineerMemoryResult(input.taskRepoRoot, state);
|
|
379
407
|
}
|
|
380
408
|
catch (error) {
|
|
381
|
-
await failReview(input.taskRepoRoot, state, `
|
|
409
|
+
await failReview(input.taskRepoRoot, state, `Harness Engineer memory result could not be recorded: ${errorMessage(error)}`);
|
|
382
410
|
return true;
|
|
383
411
|
}
|
|
384
|
-
await applyReviewedMemory(input.taskRepoRoot, state);
|
|
385
412
|
return true;
|
|
386
413
|
}
|
|
387
414
|
return true;
|
|
@@ -559,44 +586,75 @@ export function createAutoMemoryService(deps) {
|
|
|
559
586
|
}
|
|
560
587
|
return candidates;
|
|
561
588
|
}
|
|
562
|
-
async function
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
return await deps.fs.pathExists(resolveRepoPath(taskRepoRoot, durableDocPath));
|
|
570
|
-
}
|
|
571
|
-
catch {
|
|
572
|
-
return false;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
async function applyReviewedMemory(taskRepoRoot, state) {
|
|
578
|
-
try {
|
|
579
|
-
const before = await readRunMemorySet(taskRepoRoot, state.runId, "before");
|
|
580
|
-
const after = await readRunMemorySet(taskRepoRoot, state.runId, "after");
|
|
581
|
-
assertCompleteMemorySet(after);
|
|
582
|
-
await applyAndCommitMemorySet(taskRepoRoot, before, after, "chore: update VCM memory");
|
|
583
|
-
const timestamp = now();
|
|
584
|
-
const diff = renderMemoryDiff(before, after);
|
|
585
|
-
const run = await readRun(taskRepoRoot, state.runId);
|
|
586
|
-
await persistRun(taskRepoRoot, {
|
|
587
|
-
...run,
|
|
588
|
-
status: "applied",
|
|
589
|
-
updatedAt: timestamp,
|
|
590
|
-
appliedAt: timestamp,
|
|
591
|
-
afterHashes: hashMemorySet(after),
|
|
592
|
-
diff
|
|
589
|
+
async function recordHarnessEngineerMemoryResult(taskRepoRoot, state) {
|
|
590
|
+
if (!state.reviewBaseCommit) {
|
|
591
|
+
throw new VcmError({
|
|
592
|
+
code: "MEMORY_REVIEW_BASE_MISSING",
|
|
593
|
+
message: "Harness Engineer memory review has no recorded base commit.",
|
|
594
|
+
statusCode: 409,
|
|
595
|
+
hint: "Retry Auto Memory before running Task Harness Retrospective again."
|
|
593
596
|
});
|
|
594
|
-
await deps.fs.writeText(resolveRepoPath(taskRepoRoot, `${MEMORY_REVIEW_RUNS_ROOT}/${state.runId}/applied.patch`), diff);
|
|
595
|
-
await clearActiveState(taskRepoRoot);
|
|
596
597
|
}
|
|
597
|
-
|
|
598
|
-
|
|
598
|
+
const before = await readRunMemorySet(taskRepoRoot, state.runId, "before");
|
|
599
|
+
const after = await readMemorySet(taskRepoRoot);
|
|
600
|
+
await assertOnlyMemoryBlocksChanged(taskRepoRoot, state.runId, after);
|
|
601
|
+
const memoryPaths = MEMORY_FILE_DEFINITIONS.map((definition) => definition.path);
|
|
602
|
+
const uncommittedMemoryDiff = await deps.git.getDiff(taskRepoRoot, "HEAD", null, memoryPaths);
|
|
603
|
+
if (uncommittedMemoryDiff.trim()) {
|
|
604
|
+
throw new VcmError({
|
|
605
|
+
code: "MEMORY_REVIEW_NOT_COMMITTED",
|
|
606
|
+
message: "Harness Engineer left reviewed memory changes uncommitted.",
|
|
607
|
+
statusCode: 409,
|
|
608
|
+
hint: "Commit the reviewed <VCM-memory> changes before ending the retrospective turn."
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
const changedMemoryPaths = MEMORY_FILE_DEFINITIONS
|
|
612
|
+
.filter((definition) => before[definition.path] !== after[definition.path])
|
|
613
|
+
.map((definition) => definition.path);
|
|
614
|
+
const currentHead = await deps.git.getHeadCommit(taskRepoRoot);
|
|
615
|
+
const committedPaths = currentHead === state.reviewBaseCommit
|
|
616
|
+
? []
|
|
617
|
+
: await deps.git.getChangedPaths(taskRepoRoot, state.reviewBaseCommit, currentHead);
|
|
618
|
+
const unexpectedPaths = committedPaths.filter((changedPath) => !memoryPaths.includes(changedPath));
|
|
619
|
+
if (unexpectedPaths.length > 0) {
|
|
620
|
+
throw new VcmError({
|
|
621
|
+
code: "MEMORY_REVIEW_COMMIT_SCOPE_INVALID",
|
|
622
|
+
message: `Harness Engineer memory commit contains files outside managed memory hosts: ${unexpectedPaths.join(", ")}`,
|
|
623
|
+
statusCode: 409,
|
|
624
|
+
hint: "Move unrelated changes to a separate workflow and keep the memory review commit limited to managed memory files."
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
const missingCommittedPaths = changedMemoryPaths.filter((changedPath) => !committedPaths.includes(changedPath));
|
|
628
|
+
if (missingCommittedPaths.length > 0) {
|
|
629
|
+
throw new VcmError({
|
|
630
|
+
code: "MEMORY_REVIEW_COMMIT_MISSING",
|
|
631
|
+
message: `Harness Engineer did not commit reviewed memory files: ${missingCommittedPaths.join(", ")}`,
|
|
632
|
+
statusCode: 409,
|
|
633
|
+
hint: "Commit every changed memory host file before ending the retrospective turn."
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
if (changedMemoryPaths.length === 0 && committedPaths.length > 0) {
|
|
637
|
+
throw new VcmError({
|
|
638
|
+
code: "MEMORY_REVIEW_EMPTY_COMMIT_RANGE",
|
|
639
|
+
message: "Harness Engineer created memory-host commits but left no final memory change.",
|
|
640
|
+
statusCode: 409,
|
|
641
|
+
hint: "Remove the unnecessary memory commits or leave memory unchanged without committing."
|
|
642
|
+
});
|
|
599
643
|
}
|
|
644
|
+
await writeRunMemorySet(taskRepoRoot, state.runId, "after", after);
|
|
645
|
+
const timestamp = now();
|
|
646
|
+
const diff = renderMemoryDiff(before, after);
|
|
647
|
+
const run = await readRun(taskRepoRoot, state.runId);
|
|
648
|
+
await persistRun(taskRepoRoot, {
|
|
649
|
+
...run,
|
|
650
|
+
status: "applied",
|
|
651
|
+
updatedAt: timestamp,
|
|
652
|
+
appliedAt: timestamp,
|
|
653
|
+
afterHashes: hashMemorySet(after),
|
|
654
|
+
diff
|
|
655
|
+
});
|
|
656
|
+
await deps.fs.writeText(resolveRepoPath(taskRepoRoot, `${MEMORY_REVIEW_RUNS_ROOT}/${state.runId}/applied.patch`), diff);
|
|
657
|
+
await clearActiveState(taskRepoRoot);
|
|
600
658
|
}
|
|
601
659
|
async function createAppliedRun(taskRepoRoot, taskSlug, source, before, after) {
|
|
602
660
|
assertCompleteMemorySet(after);
|
|
@@ -815,27 +873,6 @@ function toReviewCandidates(source, currentRole, items) {
|
|
|
815
873
|
...(item.existing ? { existing: item.existing } : {})
|
|
816
874
|
}));
|
|
817
875
|
}
|
|
818
|
-
function memoryPathForTarget(target) {
|
|
819
|
-
if (target === "shared") {
|
|
820
|
-
return "CLAUDE.md";
|
|
821
|
-
}
|
|
822
|
-
const definition = MEMORY_FILE_DEFINITIONS.find((candidate) => "role" in candidate && candidate.role === target);
|
|
823
|
-
if (!definition) {
|
|
824
|
-
throw new Error(`Missing memory file definition for review target: ${target}`);
|
|
825
|
-
}
|
|
826
|
-
return definition.path;
|
|
827
|
-
}
|
|
828
|
-
function memorySetByTarget(memory) {
|
|
829
|
-
return {
|
|
830
|
-
shared: memory["CLAUDE.md"],
|
|
831
|
-
"project-manager": memory[memoryPathForTarget("project-manager")],
|
|
832
|
-
architect: memory[memoryPathForTarget("architect")],
|
|
833
|
-
coder: memory[memoryPathForTarget("coder")],
|
|
834
|
-
tester: memory[memoryPathForTarget("tester")],
|
|
835
|
-
reviewer: memory[memoryPathForTarget("reviewer")],
|
|
836
|
-
"harness-engineer": memory[memoryPathForTarget("harness-engineer")]
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
876
|
function toActiveReview(state) {
|
|
840
877
|
return {
|
|
841
878
|
runId: state.runId,
|
|
@@ -902,6 +939,9 @@ function requireSafeRunId(runId) {
|
|
|
902
939
|
function memoryRunFilePath(runId, snapshot, memoryPath) {
|
|
903
940
|
return `${MEMORY_REVIEW_RUNS_ROOT}/${runId}/${snapshot}/${memoryPath}`;
|
|
904
941
|
}
|
|
942
|
+
function memoryRunHostFilePath(runId, memoryPath) {
|
|
943
|
+
return `${MEMORY_REVIEW_RUNS_ROOT}/${runId}/host-before/${memoryPath}`;
|
|
944
|
+
}
|
|
905
945
|
function missingMemoryBlockError(filePath) {
|
|
906
946
|
return new VcmError({
|
|
907
947
|
code: "MEMORY_BLOCK_MISSING",
|
|
@@ -923,12 +963,6 @@ function hashMemorySet(memory) {
|
|
|
923
963
|
sha256(memory[definition.path] ?? "")
|
|
924
964
|
]));
|
|
925
965
|
}
|
|
926
|
-
function hasSubstantiveMemory(memory) {
|
|
927
|
-
return Object.values(memory).some((content) => {
|
|
928
|
-
const normalized = content.trim();
|
|
929
|
-
return Boolean(normalized && normalized !== "No accumulated project memory yet.");
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
966
|
function sameHashes(left, right) {
|
|
933
967
|
return MEMORY_FILE_DEFINITIONS.every((definition) => left[definition.path] === right[definition.path]);
|
|
934
968
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CCR_GATEWAY_BASE_URL, CCR_GPT_MODEL_ID, createSessionModelOptions, isCcrSessionModel } from "../../shared/types/session.js";
|
|
1
|
+
import { CCR_GATEWAY_BASE_URL, CCR_GPT_EFFECTIVE_CONTEXT_TOKENS, CCR_GPT_MODEL_ID, createSessionModelOptions, isCcrSessionModel } from "../../shared/types/session.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { VcmError } from "../errors.js";
|
|
@@ -162,6 +162,7 @@ export function createCcrIntegrationService(deps) {
|
|
|
162
162
|
CODEXL_CLAUDE_CODE_MODEL: CCR_GPT_MODEL_ID,
|
|
163
163
|
ANTHROPIC_SMALL_FAST_MODEL: CCR_GPT_MODEL_ID,
|
|
164
164
|
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
|
|
165
|
+
CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(CCR_GPT_EFFECTIVE_CONTEXT_TOKENS),
|
|
165
166
|
CLAUDE_CONFIG_DIR: configDir,
|
|
166
167
|
NO_PROXY: noProxy,
|
|
167
168
|
no_proxy: noProxy
|
|
@@ -16,6 +16,12 @@ const NON_RETRYABLE_STOP_FAILURE_ERRORS = new Set([
|
|
|
16
16
|
"terminal_session_exited",
|
|
17
17
|
"terminal_session_missing"
|
|
18
18
|
]);
|
|
19
|
+
const NON_RETRYABLE_CONTEXT_FAILURE_PATTERNS = [
|
|
20
|
+
"context_length_exceeded",
|
|
21
|
+
"request_too_large",
|
|
22
|
+
"prompt too long",
|
|
23
|
+
"maximum context length"
|
|
24
|
+
];
|
|
19
25
|
const DIAGNOSTIC_SNIPPET_MAX_LENGTH = 2000;
|
|
20
26
|
export function createClaudeHookService(deps) {
|
|
21
27
|
const stopFailureRetryTimers = new Map();
|
|
@@ -876,11 +882,16 @@ function parseStopFailureDiagnostic(event) {
|
|
|
876
882
|
const error = (normalizeDiagnosticString(event.error, 200) ?? "unknown").toLowerCase();
|
|
877
883
|
const errorDetails = normalizeDiagnosticString(event.error_details, DIAGNOSTIC_SNIPPET_MAX_LENGTH);
|
|
878
884
|
const lastAssistantMessage = normalizeDiagnosticString(event.last_assistant_message, DIAGNOSTIC_SNIPPET_MAX_LENGTH);
|
|
885
|
+
const diagnosticText = [error, errorDetails, lastAssistantMessage]
|
|
886
|
+
.filter((value) => Boolean(value))
|
|
887
|
+
.join("\n")
|
|
888
|
+
.toLowerCase();
|
|
879
889
|
return {
|
|
880
890
|
error,
|
|
881
891
|
errorDetails,
|
|
882
892
|
lastAssistantMessage,
|
|
883
893
|
retryable: !NON_RETRYABLE_STOP_FAILURE_ERRORS.has(error)
|
|
894
|
+
&& !NON_RETRYABLE_CONTEXT_FAILURE_PATTERNS.some((pattern) => diagnosticText.includes(pattern))
|
|
884
895
|
};
|
|
885
896
|
}
|
|
886
897
|
function normalizeDiagnosticString(value, maxLength) {
|