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.
Files changed (27) hide show
  1. package/README.md +25 -12
  2. package/dist/backend/adapters/git-adapter.js +21 -0
  3. package/dist/backend/api/gate-review-routes.js +5 -0
  4. package/dist/backend/services/auto-memory-service.js +115 -81
  5. package/dist/backend/services/ccr-integration-service.js +2 -1
  6. package/dist/backend/services/claude-hook-service.js +11 -0
  7. package/dist/backend/services/gate-review-service.js +354 -107
  8. package/dist/backend/services/harness-feedback-service.js +6 -3
  9. package/dist/backend/services/session-service.js +42 -52
  10. package/dist/backend/templates/handoff.js +17 -0
  11. package/dist/backend/templates/harness/architect-agent.js +5 -2
  12. package/dist/backend/templates/harness/claude-root.js +8 -0
  13. package/dist/backend/templates/harness/coder-agent.js +1 -1
  14. package/dist/backend/templates/harness/gate-review.js +2 -0
  15. package/dist/backend/templates/harness/harness-engineer-agent.js +14 -9
  16. package/dist/backend/templates/harness/tester-agent.js +1 -1
  17. package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -0
  18. package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +3 -1
  19. package/dist/backend/templates/harness/vcm-route-message-skill.js +1 -1
  20. package/dist/shared/types/session.js +1 -0
  21. package/dist-frontend/assets/index-DN9kwoOt.css +32 -0
  22. package/dist-frontend/assets/{index-Cmr9W7-n.js → index-DhLRhgSV.js} +35 -35
  23. package/dist-frontend/index.html +2 -2
  24. package/package.json +1 -1
  25. package/scripts/harness-tools/vcm-bash-guard +62 -8
  26. package/dist/backend/services/memory-review-validation.js +0 -286
  27. 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
- applies the result as part of the same Task Harness Retrospective. Roles cannot
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. VCM changes only block
481
- contents and creates a dedicated commit in the active task worktree. Harness
482
- Studio shows current memory and task-local applied history. Memory is applied
483
- before user review; while the task worktree remains available, the user can edit
484
- current memory or revert a recorded change through another commit.
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
- -> Apply reviewed memory, when Auto Memory is enabled
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
- every candidate has one matching decision and that the reviewed memory files
511
- implement those decisions before applying them. Harness Engineer review and
512
- retrospective work remain tool role activity and do not participate in Round
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
- reviewedMemoryPath: path.join(runRoot, "after"),
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
- const report = await deps.fs.readText(state.retrospectiveReportPath);
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, `Task Harness Retrospective memory review validation failed: ${errorMessage(error)}`);
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 validateReviewedMemoryOutput(taskRepoRoot, before, after, decisions) {
563
- return validateMemoryReviewOutput({
564
- before: memorySetByTarget(before),
565
- after: memorySetByTarget(after),
566
- decisions,
567
- durableDocExists: async (durableDocPath) => {
568
- try {
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
- catch (error) {
598
- await failReview(taskRepoRoot, state, `Harness Engineer memory result could not be applied: ${errorMessage(error)}`);
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) {