vibe-coding-master 0.3.29 → 0.3.30

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 (28) hide show
  1. package/README.md +9 -13
  2. package/dist/backend/api/project-routes.js +1 -1
  3. package/dist/backend/api/{codex-translation-routes.js → translation-worker-routes.js} +21 -21
  4. package/dist/backend/cli/install-vcm-harness.js +30 -25
  5. package/dist/backend/server.js +12 -22
  6. package/dist/backend/services/claude-hook-service.js +51 -1
  7. package/dist/backend/services/harness-service.js +49 -29
  8. package/dist/backend/services/session-service.js +56 -163
  9. package/dist/backend/services/translation-service.js +17 -17
  10. package/dist/backend/services/{codex-translation-service.js → translation-worker-service.js} +11 -11
  11. package/dist/backend/templates/harness/gate-review.js +2 -58
  12. package/dist/shared/constants.js +13 -10
  13. package/dist/shared/types/app-settings.js +1 -1
  14. package/dist/shared/types/session.js +2 -14
  15. package/dist-frontend/assets/{index-Cfum1Prr.css → index-BvUHLq4X.css} +1 -1
  16. package/dist-frontend/assets/{index-K34QFpWK.js → index-D0ezB4na.js} +36 -36
  17. package/dist-frontend/index.html +2 -2
  18. package/docs/{codex-translation-plan.md → claude-code-translation-plan.md} +151 -153
  19. package/docs/full-harness-baseline.md +3 -7
  20. package/docs/gateway-design.md +1 -1
  21. package/docs/product-design.md +2 -2
  22. package/docs/v0.4-custom-workflow-plan.md +0 -1
  23. package/docs/vcm-cc-best-practices.md +2 -5
  24. package/package.json +1 -1
  25. package/dist/backend/api/codex-hook-routes.js +0 -9
  26. package/dist/backend/codex-sandbox-mode.js +0 -28
  27. package/dist/backend/services/codex-hook-service.js +0 -71
  28. package/dist/shared/types/codex-hook.js +0 -1
@@ -1,16 +1,14 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
- import { CORE_VCM_ROLE_NAMES, isCodexRoleName, isDispatchableRole } from "../../shared/constants.js";
3
+ import { CORE_VCM_ROLE_NAMES, isDispatchableRole } from "../../shared/constants.js";
4
4
  import { VcmError } from "../errors.js";
5
5
  import { resolveRepoPath } from "../adapters/filesystem.js";
6
6
  import { claudeTranscriptPath } from "./claude-transcript-service.js";
7
7
  import { getTaskRuntimeRepoRoot } from "./task-service.js";
8
8
  const GATE_REVIEWER_ROLE = "gate-reviewer";
9
- const CODEX_TRANSLATOR_ROLE = "codex-translator";
10
- const CODEX_TRANSLATOR_DIR = ".ai/codex-translator";
11
- const CODEX_TRANSLATION_DIR = ".ai/vcm/translations";
12
- const CODEX_TRANSLATOR_SESSION_PATH = ".ai/vcm/translations/session.json";
13
- const CODEX_TRANSLATOR_CONFIG_PATH = ".ai/codex-translator/config.toml";
9
+ const TRANSLATOR_ROLE = "translator";
10
+ const TRANSLATION_DIR = ".ai/vcm/translations";
11
+ const TRANSLATOR_SESSION_PATH = ".ai/vcm/translations/session.json";
14
12
  const GATE_REVIEWER_SESSION_PATH = ".ai/vcm/gate-reviewer/session.json";
15
13
  const PROJECT_TRANSLATOR_SCOPE = "__project__";
16
14
  const PROJECT_GATE_REVIEWER_SCOPE = "__project_gate_reviewer__";
@@ -26,16 +24,9 @@ export function createSessionService(deps) {
26
24
  const taskRepoRoot = getTaskRuntimeRepoRoot(task);
27
25
  const paths = deps.artifactService.getHandoffPaths(taskRepoRoot, task.handoffDir);
28
26
  const persisted = await loadPersistedRoleRecordForRole(deps.fs, repoRoot, taskRepoRoot, config.stateRoot, taskSlug, role);
29
- const isCodexRole = isCodexRoleName(role);
30
- const isTranslator = role === CODEX_TRANSLATOR_ROLE;
31
- const sessionRepoRoot = isTranslator ? repoRoot : taskRepoRoot;
32
27
  const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
33
- const model = isCodexRole
34
- ? normalizeCodexModel(input.model ?? persisted?.model)
35
- : normalizeClaudeModel(input.model ?? persisted?.model);
36
- const effort = isCodexRole
37
- ? normalizeCodexEffort(input.effort ?? persisted?.effort)
38
- : normalizeClaudeEffort(input.effort ?? persisted?.effort);
28
+ const model = normalizeClaudeModel(input.model ?? persisted?.model);
29
+ const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort);
39
30
  const claudeSessionId = launchMode === "resume"
40
31
  ? persisted?.claudeSessionId
41
32
  : randomUUID();
@@ -49,13 +40,11 @@ export function createSessionService(deps) {
49
40
  }
50
41
  const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
51
42
  ? persisted.transcriptPath
52
- : isCodexRole ? undefined : claudeTranscriptPath(taskRepoRoot, claudeSessionId);
53
- const startCommand = isCodexRole
54
- ? await buildCodexStartCommand(deps.fs, repoRoot, taskRepoRoot, role, launchMode, model, effort, deps.sandboxMode, launchMode === "resume" && hasCapturedCodexSession(persisted) ? claudeSessionId : undefined)
55
- : {
56
- ...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
57
- cwd: taskRepoRoot
58
- };
43
+ : claudeTranscriptPath(taskRepoRoot, claudeSessionId);
44
+ const startCommand = {
45
+ ...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
46
+ cwd: taskRepoRoot
47
+ };
59
48
  const runtimeSession = await deps.runtime.createSession({
60
49
  taskSlug,
61
50
  role,
@@ -64,7 +53,7 @@ export function createSessionService(deps) {
64
53
  cwd: startCommand.cwd,
65
54
  env: {
66
55
  VCM_API_URL: deps.apiUrl,
67
- VCM_TASK_REPO_ROOT: sessionRepoRoot,
56
+ VCM_TASK_REPO_ROOT: taskRepoRoot,
68
57
  VCM_TASK_SLUG: taskSlug,
69
58
  VCM_ROLE: role,
70
59
  VCM_SESSION_ID: claudeSessionId
@@ -198,24 +187,33 @@ export function createSessionService(deps) {
198
187
  if (live && live.status === "running") {
199
188
  return live;
200
189
  }
201
- const persisted = await loadPersistedCodexTranslatorSession(deps.fs, repoRoot);
202
- const model = normalizeCodexModel(input.model ?? persisted?.model);
203
- const effort = normalizeCodexEffort(input.effort ?? persisted?.effort);
190
+ const config = await deps.projectService.loadConfig(repoRoot);
191
+ const persisted = await loadPersistedTranslatorSession(deps.fs, repoRoot);
192
+ const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
193
+ const model = normalizeClaudeModel(input.model ?? persisted?.model);
194
+ const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort);
204
195
  const claudeSessionId = launchMode === "resume"
205
196
  ? persisted?.claudeSessionId
206
197
  : randomUUID();
207
198
  if (!claudeSessionId) {
208
199
  throw new VcmError({
209
- code: "CODEX_TRANSLATOR_SESSION_MISSING",
210
- message: "Codex Translator does not have a session id to resume.",
200
+ code: "TRANSLATOR_SESSION_MISSING",
201
+ message: "Translator does not have a session id to resume.",
211
202
  statusCode: 409,
212
203
  hint: "Start the translator once before using Resume."
213
204
  });
214
205
  }
215
- const startCommand = await buildCodexStartCommand(deps.fs, repoRoot, repoRoot, CODEX_TRANSLATOR_ROLE, launchMode, model, effort, deps.sandboxMode, launchMode === "resume" && hasCapturedCodexSession(persisted) ? claudeSessionId : undefined);
206
+ await deps.fs.ensureDir(resolveRepoPath(repoRoot, TRANSLATION_DIR));
207
+ const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
208
+ ? persisted.transcriptPath
209
+ : claudeTranscriptPath(repoRoot, claudeSessionId);
210
+ const startCommand = {
211
+ ...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
212
+ cwd: repoRoot
213
+ };
216
214
  const runtimeSession = await deps.runtime.createSession({
217
215
  taskSlug: PROJECT_TRANSLATOR_SCOPE,
218
- role: CODEX_TRANSLATOR_ROLE,
216
+ role: TRANSLATOR_ROLE,
219
217
  command: startCommand.command,
220
218
  args: startCommand.args,
221
219
  cwd: startCommand.cwd,
@@ -223,7 +221,7 @@ export function createSessionService(deps) {
223
221
  VCM_API_URL: deps.apiUrl,
224
222
  VCM_TASK_REPO_ROOT: repoRoot,
225
223
  VCM_TASK_SLUG: PROJECT_TRANSLATOR_SCOPE,
226
- VCM_ROLE: CODEX_TRANSLATOR_ROLE,
224
+ VCM_ROLE: TRANSLATOR_ROLE,
227
225
  VCM_SESSION_ID: claudeSessionId
228
226
  },
229
227
  cols: input.cols,
@@ -233,12 +231,13 @@ export function createSessionService(deps) {
233
231
  const record = {
234
232
  id: runtimeSession.id,
235
233
  claudeSessionId,
234
+ transcriptPath,
236
235
  taskSlug: PROJECT_TRANSLATOR_SCOPE,
237
- role: CODEX_TRANSLATOR_ROLE,
236
+ role: TRANSLATOR_ROLE,
238
237
  status: runtimeSession.status,
239
238
  activityStatus: "idle",
240
239
  command: startCommand.display,
241
- permissionMode: "default",
240
+ permissionMode,
242
241
  model,
243
242
  effort,
244
243
  cwd: startCommand.cwd,
@@ -250,7 +249,7 @@ export function createSessionService(deps) {
250
249
  exitCode: runtimeSession.exitCode
251
250
  };
252
251
  deps.registry.upsert(record);
253
- await persistCodexTranslatorSession(deps.fs, repoRoot, record);
252
+ await persistTranslatorSession(deps.fs, repoRoot, record);
254
253
  return record;
255
254
  }
256
255
  return {
@@ -326,7 +325,7 @@ export function createSessionService(deps) {
326
325
  if (!existing) {
327
326
  throw new VcmError({
328
327
  code: "SESSION_MISSING",
329
- message: "Codex Translator session has not been started.",
328
+ message: "Translator session has not been started.",
330
329
  statusCode: 404
331
330
  });
332
331
  }
@@ -340,7 +339,7 @@ export function createSessionService(deps) {
340
339
  updatedAt: now()
341
340
  };
342
341
  deps.registry.upsert(updated);
343
- await persistCodexTranslatorSession(deps.fs, repoRoot, updated);
342
+ await persistTranslatorSession(deps.fs, repoRoot, updated);
344
343
  return updated;
345
344
  },
346
345
  async restartProjectTranslatorSession(repoRoot, input = {}) {
@@ -356,7 +355,7 @@ export function createSessionService(deps) {
356
355
  },
357
356
  async getProjectTranslatorSession(repoRoot) {
358
357
  const record = getRegisteredProjectTranslatorSession(deps.registry, deps.runtime)
359
- ?? await loadPersistedCodexTranslatorSession(deps.fs, repoRoot);
358
+ ?? await loadPersistedTranslatorSession(deps.fs, repoRoot);
360
359
  return toRoleSessionRecordView(record, deps.runtime);
361
360
  },
362
361
  async ensureProjectTranslatorSession(repoRoot, input = {}) {
@@ -395,7 +394,7 @@ export function createSessionService(deps) {
395
394
  updatedAt: timestamp
396
395
  };
397
396
  deps.registry.upsert(updated);
398
- await persistCodexTranslatorSession(deps.fs, repoRoot, updated);
397
+ await persistTranslatorSession(deps.fs, repoRoot, updated);
399
398
  return updated;
400
399
  },
401
400
  startRoleSession(repoRoot, taskSlug, role, input = {}) {
@@ -403,7 +402,7 @@ export function createSessionService(deps) {
403
402
  return launchProjectGateReviewerSession(repoRoot, input, "fresh", taskSlug)
404
403
  .then((session) => scopeProjectRoleSession(session, taskSlug));
405
404
  }
406
- if (role === CODEX_TRANSLATOR_ROLE) {
405
+ if (role === TRANSLATOR_ROLE) {
407
406
  void taskSlug;
408
407
  return this.startProjectTranslatorSession(repoRoot, input);
409
408
  }
@@ -414,7 +413,7 @@ export function createSessionService(deps) {
414
413
  return launchProjectGateReviewerSession(repoRoot, input, "resume", taskSlug)
415
414
  .then((session) => scopeProjectRoleSession(session, taskSlug));
416
415
  }
417
- if (role === CODEX_TRANSLATOR_ROLE) {
416
+ if (role === TRANSLATOR_ROLE) {
418
417
  void taskSlug;
419
418
  return this.resumeProjectTranslatorSession(repoRoot, input);
420
419
  }
@@ -424,7 +423,7 @@ export function createSessionService(deps) {
424
423
  if (role === GATE_REVIEWER_ROLE) {
425
424
  return scopeProjectRoleSession(await this.stopProjectGateReviewerSession(repoRoot), taskSlug);
426
425
  }
427
- if (role === CODEX_TRANSLATOR_ROLE) {
426
+ if (role === TRANSLATOR_ROLE) {
428
427
  void taskSlug;
429
428
  return this.stopProjectTranslatorSession(repoRoot);
430
429
  }
@@ -462,7 +461,7 @@ export function createSessionService(deps) {
462
461
  }
463
462
  return scopeProjectRoleSession(await launchProjectGateReviewerSession(repoRoot, input, "fresh", taskSlug), taskSlug);
464
463
  }
465
- if (role === CODEX_TRANSLATOR_ROLE) {
464
+ if (role === TRANSLATOR_ROLE) {
466
465
  void taskSlug;
467
466
  return this.restartProjectTranslatorSession(repoRoot, input);
468
467
  }
@@ -484,7 +483,7 @@ export function createSessionService(deps) {
484
483
  }
485
484
  return scopeProjectRoleSession(await bindProjectGateReviewerSession(repoRoot, session, taskSlug), taskSlug);
486
485
  }
487
- if (role === CODEX_TRANSLATOR_ROLE) {
486
+ if (role === TRANSLATOR_ROLE) {
488
487
  void taskSlug;
489
488
  return this.getProjectTranslatorSession(repoRoot);
490
489
  }
@@ -544,7 +543,7 @@ export function createSessionService(deps) {
544
543
  await persistProjectGateReviewerSession(deps.fs, repoRoot, updated);
545
544
  return scopeProjectRoleSession(updated, input.taskSlug);
546
545
  }
547
- if (input.role === CODEX_TRANSLATOR_ROLE) {
546
+ if (input.role === TRANSLATOR_ROLE) {
548
547
  void input.taskSlug;
549
548
  return this.recordProjectTranslatorHookEvent(repoRoot, {
550
549
  eventName: input.eventName,
@@ -667,80 +666,6 @@ function toRoleSessionRecordView(record, runtime) {
667
666
  exitCode: runtimeSession.exitCode
668
667
  };
669
668
  }
670
- async function buildCodexStartCommand(fs, baseRepoRoot, _taskRepoRoot, role, launchMode, selectedModel, selectedEffort, sandboxMode, resumeSessionId) {
671
- const isTranslator = role === CODEX_TRANSLATOR_ROLE;
672
- if (!isTranslator) {
673
- throw new VcmError({
674
- code: "CODEX_ROLE_UNSUPPORTED",
675
- message: `${role} is not a Codex role.`,
676
- statusCode: 400
677
- });
678
- }
679
- const codexDir = resolveRepoPath(baseRepoRoot, CODEX_TRANSLATOR_DIR);
680
- const outputDir = resolveRepoPath(baseRepoRoot, CODEX_TRANSLATION_DIR);
681
- if (!(await fs.pathExists(codexDir))) {
682
- throw new VcmError({
683
- code: "CODEX_TRANSLATOR_CONFIG_MISSING",
684
- message: `${CODEX_TRANSLATOR_DIR} does not exist.`,
685
- statusCode: 409,
686
- hint: "Apply the VCM harness before starting Codex Translator."
687
- });
688
- }
689
- await fs.ensureDir(outputDir);
690
- const config = await loadCodexSessionConfig(fs, baseRepoRoot, CODEX_TRANSLATOR_CONFIG_PATH);
691
- const args = launchMode === "resume"
692
- ? resumeSessionId ? ["resume", resumeSessionId] : ["resume", "--last"]
693
- : [];
694
- args.push("--cd", codexDir);
695
- if (isDevContainerSandbox(sandboxMode)) {
696
- args.push("--dangerously-bypass-approvals-and-sandbox");
697
- }
698
- else {
699
- args.push("--add-dir", baseRepoRoot);
700
- args.push("--sandbox", "workspace-write", "--ask-for-approval", "never");
701
- }
702
- args.push("--dangerously-bypass-hook-trust", "--search");
703
- if (selectedModel !== "default") {
704
- args.push("--model", selectedModel);
705
- }
706
- if (selectedEffort !== "default") {
707
- args.push("--config", `model_reasoning_effort="${selectedEffort}"`);
708
- }
709
- return {
710
- command: config.command,
711
- args,
712
- cwd: baseRepoRoot,
713
- display: [config.command, ...args].map(formatDisplayArg).join(" ")
714
- };
715
- }
716
- function hasCapturedCodexSession(record) {
717
- return Boolean(record?.lastHookEventAt || record?.transcriptPath);
718
- }
719
- function isDevContainerSandbox(value) {
720
- const normalized = value?.toLowerCase().replace(/[\s_-]+/g, "");
721
- return normalized === "devcontainer" ||
722
- normalized === "container" ||
723
- normalized === "docker" ||
724
- normalized === "podman" ||
725
- normalized === "codespaces" ||
726
- normalized === "bypass" ||
727
- normalized === "nosandbox" ||
728
- normalized === "disabled" ||
729
- normalized === "off" ||
730
- normalized === "none";
731
- }
732
- async function loadCodexSessionConfig(fs, repoRoot, configRelativePath) {
733
- const configPath = resolveRepoPath(repoRoot, configRelativePath);
734
- if (!(await fs.pathExists(configPath))) {
735
- return {
736
- command: "codex"
737
- };
738
- }
739
- const content = await fs.readText(configPath);
740
- return {
741
- command: parseTomlString(content, "command") ?? "codex"
742
- };
743
- }
744
669
  function matchesRoleHookSession(record, input) {
745
670
  if (input.sessionId && record.claudeSessionId === input.sessionId) {
746
671
  return true;
@@ -778,7 +703,7 @@ function getHandoffArtifactPath(paths, role) {
778
703
  return undefined;
779
704
  }
780
705
  function getRegisteredRoleSession(registry, runtime, taskSlug, role) {
781
- if (role !== CODEX_TRANSLATOR_ROLE && role !== GATE_REVIEWER_ROLE) {
706
+ if (role !== TRANSLATOR_ROLE && role !== GATE_REVIEWER_ROLE) {
782
707
  return registry.getByRole(taskSlug, role);
783
708
  }
784
709
  const candidates = registry.list().filter((session) => session.role === role);
@@ -794,7 +719,7 @@ function getRegisteredProjectGateReviewerSession(registry, runtime) {
794
719
  return live ?? candidates.sort(compareSessionUpdatedAtDesc)[0];
795
720
  }
796
721
  function getRegisteredProjectTranslatorSession(registry, runtime) {
797
- const candidates = registry.list().filter((session) => session.role === CODEX_TRANSLATOR_ROLE);
722
+ const candidates = registry.list().filter((session) => session.role === TRANSLATOR_ROLE);
798
723
  const live = candidates.find((session) => runtime.getSession(session.id)?.status === "running");
799
724
  return live ?? candidates.sort(compareSessionUpdatedAtDesc)[0];
800
725
  }
@@ -802,7 +727,7 @@ function compareSessionUpdatedAtDesc(left, right) {
802
727
  return (right.updatedAt ?? "").localeCompare(left.updatedAt ?? "");
803
728
  }
804
729
  function scopeProjectRoleSession(record, taskSlug) {
805
- if (!record || (record.role !== CODEX_TRANSLATOR_ROLE && record.role !== GATE_REVIEWER_ROLE)) {
730
+ if (!record || (record.role !== TRANSLATOR_ROLE && record.role !== GATE_REVIEWER_ROLE)) {
806
731
  return record;
807
732
  }
808
733
  return {
@@ -815,9 +740,9 @@ async function loadPersistedRoleRecordForRole(fs, baseRepoRoot, taskRepoRoot, st
815
740
  void taskSlug;
816
741
  return loadPersistedProjectGateReviewerSession(fs, baseRepoRoot);
817
742
  }
818
- if (role === CODEX_TRANSLATOR_ROLE) {
743
+ if (role === TRANSLATOR_ROLE) {
819
744
  void taskSlug;
820
- return loadPersistedCodexTranslatorSession(fs, baseRepoRoot);
745
+ return loadPersistedTranslatorSession(fs, baseRepoRoot);
821
746
  }
822
747
  return loadPersistedRoleRecord(fs, taskRepoRoot, stateRoot, taskSlug, role);
823
748
  }
@@ -836,8 +761,8 @@ async function loadPersistedProjectGateReviewerSession(fs, repoRoot) {
836
761
  taskSlug: PROJECT_GATE_REVIEWER_SCOPE
837
762
  };
838
763
  }
839
- async function loadPersistedCodexTranslatorSession(fs, repoRoot) {
840
- const sessionPath = resolveRepoPath(repoRoot, CODEX_TRANSLATOR_SESSION_PATH);
764
+ async function loadPersistedTranslatorSession(fs, repoRoot) {
765
+ const sessionPath = resolveRepoPath(repoRoot, TRANSLATOR_SESSION_PATH);
841
766
  if (!(await fs.pathExists(sessionPath))) {
842
767
  return undefined;
843
768
  }
@@ -874,12 +799,8 @@ function normalizePersistedRoleRecord(record) {
874
799
  ? legacy.lastStopAt
875
800
  : undefined),
876
801
  permissionMode: normalizeClaudePermissionMode(record.permissionMode),
877
- model: isCodexRoleName(record.role)
878
- ? normalizeCodexModel(record.model)
879
- : normalizeClaudeModel(record.model),
880
- effort: isCodexRoleName(record.role)
881
- ? normalizeCodexEffort(record.effort)
882
- : normalizeClaudeEffort(record.effort)
802
+ model: normalizeClaudeModel(record.model),
803
+ effort: normalizeClaudeEffort(record.effort)
883
804
  }
884
805
  : undefined;
885
806
  }
@@ -913,8 +834,8 @@ async function persistRoleSessionRecord(fs, baseRepoRoot, taskRepoRoot, stateRoo
913
834
  await persistProjectGateReviewerSession(fs, baseRepoRoot, session);
914
835
  return;
915
836
  }
916
- if (session.role === CODEX_TRANSLATOR_ROLE) {
917
- await persistCodexTranslatorSession(fs, baseRepoRoot, session);
837
+ if (session.role === TRANSLATOR_ROLE) {
838
+ await persistTranslatorSession(fs, baseRepoRoot, session);
918
839
  return;
919
840
  }
920
841
  await persistTaskSession(fs, taskRepoRoot, stateRoot, session);
@@ -930,8 +851,8 @@ async function persistProjectGateReviewerSession(fs, repoRoot, session) {
930
851
  }
931
852
  });
932
853
  }
933
- async function persistCodexTranslatorSession(fs, repoRoot, session) {
934
- await fs.writeJsonAtomic(resolveRepoPath(repoRoot, CODEX_TRANSLATOR_SESSION_PATH), {
854
+ async function persistTranslatorSession(fs, repoRoot, session) {
855
+ await fs.writeJsonAtomic(resolveRepoPath(repoRoot, TRANSLATOR_SESSION_PATH), {
935
856
  version: 1,
936
857
  role: session.role,
937
858
  updatedAt: session.updatedAt,
@@ -977,12 +898,6 @@ function normalizeClaudeModel(value) {
977
898
  }
978
899
  return "default";
979
900
  }
980
- function normalizeCodexModel(value) {
981
- if (value === "default" || value === "gpt-5.5") {
982
- return value;
983
- }
984
- return "gpt-5.5";
985
- }
986
901
  function normalizeClaudeEffort(value) {
987
902
  if (value === "low"
988
903
  || value === "medium"
@@ -994,25 +909,3 @@ function normalizeClaudeEffort(value) {
994
909
  }
995
910
  return "default";
996
911
  }
997
- function normalizeCodexEffort(value) {
998
- if (value === "low"
999
- || value === "medium"
1000
- || value === "high"
1001
- || value === "xhigh") {
1002
- return value;
1003
- }
1004
- return "default";
1005
- }
1006
- function parseTomlString(content, key) {
1007
- const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"\\s*$`, "m");
1008
- return pattern.exec(content)?.[1];
1009
- }
1010
- function escapeRegExp(value) {
1011
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1012
- }
1013
- function formatDisplayArg(value) {
1014
- if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) {
1015
- return value;
1016
- }
1017
- return `'${value.replaceAll("'", "'\\''")}'`;
1018
- }
@@ -7,8 +7,8 @@ const TRANSLATION_SOURCE_LANGUAGE = "auto";
7
7
  const TRANSLATION_INPUT_MODE = "review-before-send";
8
8
  const TRANSLATION_CONTEXT_ENABLED = false;
9
9
  const TRANSLATION_TIMEOUT_MS = 120000;
10
- const TRANSLATION_PROVIDER = "codex";
11
- const TRANSLATION_MODEL = "codex-translator";
10
+ const TRANSLATION_PROVIDER = "claude-code";
11
+ const TRANSLATION_MODEL = "translator";
12
12
  const OUTPUT_TRANSLATION_BATCH_DELAY_MS = 10000;
13
13
  const TRANSCRIPT_REPLAY_GRACE_MS = 5000;
14
14
  export function createTranslationService(deps) {
@@ -313,7 +313,7 @@ export function createTranslationService(deps) {
313
313
  const jobs = [];
314
314
  for (let index = 0; index < items.length; index += 1) {
315
315
  const item = items[index];
316
- const job = await createCodexConversationJob({
316
+ const job = await createConversationJob({
317
317
  repoRoot: item.repoRoot,
318
318
  taskSlug: item.entry.taskSlug,
319
319
  role: item.entry.role,
@@ -329,7 +329,7 @@ export function createTranslationService(deps) {
329
329
  }
330
330
  for (const { item, job } of jobs) {
331
331
  try {
332
- const result = await waitForCodexConversationResult(item.repoRoot, job, item.config.requestTimeoutMs);
332
+ const result = await waitForConversationResult(item.repoRoot, job, item.config.requestTimeoutMs);
333
333
  const completed = {
334
334
  ...item.entry,
335
335
  status: "translated",
@@ -882,28 +882,28 @@ export function createTranslationService(deps) {
882
882
  await submitTerminalInput(deps.runtime, record.id, text);
883
883
  }
884
884
  async function translateText(input) {
885
- const job = await createCodexConversationJob(input);
886
- const result = await waitForCodexConversationResult(input.repoRoot, job, input.config.requestTimeoutMs);
885
+ const job = await createConversationJob(input);
886
+ const result = await waitForConversationResult(input.repoRoot, job, input.config.requestTimeoutMs);
887
887
  return {
888
888
  text: result.translatedText
889
889
  };
890
890
  }
891
- async function createCodexConversationJob(input) {
892
- if (!deps.codexTranslationService) {
891
+ async function createConversationJob(input) {
892
+ if (!deps.translationWorkerService) {
893
893
  throw new VcmError({
894
- code: "CODEX_TRANSLATION_UNAVAILABLE",
895
- message: "Codex translation service is unavailable.",
894
+ code: "TRANSLATION_WORKER_UNAVAILABLE",
895
+ message: "translation service is unavailable.",
896
896
  statusCode: 500
897
897
  });
898
898
  }
899
899
  if (!input.repoRoot) {
900
900
  throw new VcmError({
901
901
  code: "TRANSLATION_REPO_ROOT_MISSING",
902
- message: "Codex translation requires a base repository root.",
902
+ message: "translation requires a base repository root.",
903
903
  statusCode: 500
904
904
  });
905
905
  }
906
- return deps.codexTranslationService.createConversationJob(input.repoRoot, {
906
+ return deps.translationWorkerService.createConversationJob(input.repoRoot, {
907
907
  direction: input.direction,
908
908
  sourceText: input.text,
909
909
  sourceLanguage: input.sourceLanguage,
@@ -912,18 +912,18 @@ export function createTranslationService(deps) {
912
912
  deferDispatch: input.deferDispatch
913
913
  });
914
914
  }
915
- async function waitForCodexConversationResult(repoRoot, job, timeoutMs) {
915
+ async function waitForConversationResult(repoRoot, job, timeoutMs) {
916
916
  const deadline = Date.now() + timeoutMs;
917
917
  let lastError;
918
918
  while (Date.now() <= deadline) {
919
- const state = await deps.codexTranslationService.getState(repoRoot);
919
+ const state = await deps.translationWorkerService.getState(repoRoot);
920
920
  const item = job.queueItemId
921
921
  ? state.queue.items.find((candidate) => candidate.id === job.queueItemId)
922
922
  : undefined;
923
923
  if (item && ["failed", "cancelled", "interrupted", "skipped"].includes(item.status)) {
924
924
  throw new VcmError({
925
925
  code: "TRANSLATION_FAILED",
926
- message: item.error ?? "Codex translation failed.",
926
+ message: item.error ?? "translation failed.",
927
927
  statusCode: 502
928
928
  });
929
929
  }
@@ -932,7 +932,7 @@ export function createTranslationService(deps) {
932
932
  continue;
933
933
  }
934
934
  try {
935
- return await deps.codexTranslationService.validateConversationResult(repoRoot, {
935
+ return await deps.translationWorkerService.validateConversationResult(repoRoot, {
936
936
  resultPath: job.resultPath,
937
937
  sourceHash: job.sourceHash,
938
938
  targetLanguage: job.targetLanguage
@@ -948,7 +948,7 @@ export function createTranslationService(deps) {
948
948
  }
949
949
  throw new VcmError({
950
950
  code: "TRANSLATION_TIMEOUT",
951
- message: lastError instanceof Error ? `Codex translation timed out: ${lastError.message}` : "Codex translation timed out.",
951
+ message: lastError instanceof Error ? `translation timed out: ${lastError.message}` : "translation timed out.",
952
952
  statusCode: 504
953
953
  });
954
954
  }
@@ -19,7 +19,7 @@ const DEFAULT_CHUNK_SOURCE_TOKEN_TARGET = 80000;
19
19
  const BOOTSTRAP_DEFAULT_LIMIT = 12;
20
20
  const MEMORY_TOTAL_LIMIT_BYTES = 80 * 1024;
21
21
  const MEMORY_FILE_NAMES = ["glossary.md", "style-guide.md", "project-context.md", "decisions.md"];
22
- const CODEX_TRANSLATOR_ROLE = "codex-translator";
22
+ const TRANSLATOR_ROLE = "translator";
23
23
  const FILE_BROWSER_DEFAULT_LIMIT = 200;
24
24
  const FILE_BROWSER_MAX_LIMIT = 500;
25
25
  const FILE_BROWSER_SEARCH_MAX_DEPTH = 6;
@@ -94,7 +94,7 @@ const BINARY_LIKE_EXTENSIONS = new Set([
94
94
  ".webp",
95
95
  ".zip"
96
96
  ]);
97
- export function createCodexTranslationService(deps) {
97
+ export function createTranslationWorkerService(deps) {
98
98
  const now = deps.now ?? (() => new Date().toISOString());
99
99
  const createId = deps.id ?? (() => randomUUID());
100
100
  async function ensureLayout(repoRoot) {
@@ -225,7 +225,7 @@ export function createCodexTranslationService(deps) {
225
225
  const failedAt = now();
226
226
  for (const item of failedItems) {
227
227
  item.status = "failed";
228
- item.error = error instanceof Error ? error.message : "Failed to dispatch Codex Translator task.";
228
+ item.error = error instanceof Error ? error.message : "Failed to dispatch Translator task.";
229
229
  item.updatedAt = failedAt;
230
230
  }
231
231
  queue.activeItemId = undefined;
@@ -259,13 +259,13 @@ export function createCodexTranslationService(deps) {
259
259
  void targetLanguage;
260
260
  if (!deps.sessionService) {
261
261
  throw new VcmError({
262
- code: "CODEX_TRANSLATOR_SESSION_UNAVAILABLE",
263
- message: "Codex Translator session service is unavailable.",
262
+ code: "TRANSLATOR_SESSION_UNAVAILABLE",
263
+ message: "Translator session service is unavailable.",
264
264
  statusCode: 500
265
265
  });
266
266
  }
267
267
  return deps.sessionService.ensureProjectTranslatorSession(repoRoot, {
268
- model: "gpt-5.5",
268
+ model: "default",
269
269
  effort: "medium"
270
270
  });
271
271
  }
@@ -284,7 +284,7 @@ export function createCodexTranslationService(deps) {
284
284
  ? resolveRepoPath(repoRoot, item.reportPath)
285
285
  : undefined;
286
286
  return [
287
- "[VCM CODEX TRANSLATION TASK]",
287
+ "[VCM TRANSLATION TASK]",
288
288
  `Queue Item: ${item.id}`,
289
289
  `Type: ${item.type}`,
290
290
  `Target Language: ${item.targetLanguage}`,
@@ -376,7 +376,7 @@ export function createCodexTranslationService(deps) {
376
376
  const requestPath = resolveRepoPath(repoRoot, item.requestPath);
377
377
  const memoryDir = resolveRepoPath(repoRoot, MEMORY_DIR);
378
378
  return [
379
- "[VCM CODEX TRANSLATION TASK]",
379
+ "[VCM TRANSLATION TASK]",
380
380
  `Queue Item: ${item.id}`,
381
381
  "Type: memory-update",
382
382
  `Target Language: ${item.targetLanguage}`,
@@ -386,7 +386,7 @@ export function createCodexTranslationService(deps) {
386
386
  requestPath,
387
387
  "",
388
388
  "Task: update and compact VCM translation memory.",
389
- "Use the current Codex Translator session context, recent stable user corrections, completed translation behavior, and existing memory files.",
389
+ "Use the current Translator session context, recent stable user corrections, completed translation behavior, and existing memory files.",
390
390
  "Only keep stable, reusable translation knowledge. Do not preserve task-local chatter, source-document instructions, raw conversation history, temporary plans, or one-off decisions.",
391
391
  "",
392
392
  `Memory directory: ${memoryDir}`,
@@ -1265,7 +1265,7 @@ export function createCodexTranslationService(deps) {
1265
1265
  await deps.fs.writeText(absoluteTargetPath, content);
1266
1266
  return job;
1267
1267
  },
1268
- async handleCodexHook(repoRoot, eventName, taskSlug) {
1268
+ async handleTranslatorHook(repoRoot, eventName, taskSlug) {
1269
1269
  void taskSlug;
1270
1270
  if (eventName === "UserPromptSubmit") {
1271
1271
  const queue = await loadQueue(repoRoot);
@@ -1281,7 +1281,7 @@ export function createCodexTranslationService(deps) {
1281
1281
  }
1282
1282
  return;
1283
1283
  }
1284
- if (eventName === "Stop") {
1284
+ if (eventName === "Stop" || eventName === "StopFailure") {
1285
1285
  await validateActiveQueueItem(repoRoot);
1286
1286
  await dispatchNext(repoRoot);
1287
1287
  }