vibe-coding-master 0.3.28 → 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 (33) hide show
  1. package/README.md +14 -18
  2. package/dist/backend/api/project-routes.js +1 -1
  3. package/dist/backend/api/task-routes.js +2 -2
  4. package/dist/backend/api/{codex-translation-routes.js → translation-worker-routes.js} +21 -21
  5. package/dist/backend/cli/install-vcm-harness.js +30 -25
  6. package/dist/backend/gateway/gateway-service.js +24 -5
  7. package/dist/backend/server.js +12 -22
  8. package/dist/backend/services/claude-hook-service.js +93 -26
  9. package/dist/backend/services/harness-service.js +49 -29
  10. package/dist/backend/services/message-service.js +9 -6
  11. package/dist/backend/services/project-service.js +2 -2
  12. package/dist/backend/services/session-service.js +99 -172
  13. package/dist/backend/services/translation-service.js +17 -17
  14. package/dist/backend/services/{codex-translation-service.js → translation-worker-service.js} +11 -11
  15. package/dist/backend/templates/harness/gate-review.js +2 -58
  16. package/dist/shared/constants.js +19 -12
  17. package/dist/shared/types/app-settings.js +1 -1
  18. package/dist/shared/types/session.js +2 -14
  19. package/dist-frontend/assets/{index-Cfum1Prr.css → index-BvUHLq4X.css} +1 -1
  20. package/dist-frontend/assets/{index-x9I-bGUt.js → index-D0ezB4na.js} +36 -36
  21. package/dist-frontend/index.html +2 -2
  22. package/docs/{codex-translation-plan.md → claude-code-translation-plan.md} +151 -153
  23. package/docs/full-harness-baseline.md +3 -7
  24. package/docs/gate-review-gates.md +13 -5
  25. package/docs/gateway-design.md +1 -1
  26. package/docs/product-design.md +12 -11
  27. package/docs/v0.4-custom-workflow-plan.md +0 -1
  28. package/docs/vcm-cc-best-practices.md +2 -5
  29. package/package.json +1 -1
  30. package/dist/backend/api/codex-hook-routes.js +0 -9
  31. package/dist/backend/codex-sandbox-mode.js +0 -28
  32. package/dist/backend/services/codex-hook-service.js +0 -71
  33. 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 { 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
@@ -101,10 +90,10 @@ export function createSessionService(deps) {
101
90
  await persistRoleSessionRecord(deps.fs, repoRoot, taskRepoRoot, config.stateRoot, record);
102
91
  return record;
103
92
  }
104
- async function launchProjectGateReviewerSession(repoRoot, input, launchMode) {
93
+ async function launchProjectGateReviewerSession(repoRoot, input, launchMode, activeTaskSlug) {
105
94
  const live = toRoleSessionRecordView(getRegisteredProjectGateReviewerSession(deps.registry, deps.runtime), deps.runtime);
106
95
  if (live && live.status === "running") {
107
- return live;
96
+ return bindProjectGateReviewerSession(repoRoot, live, activeTaskSlug);
108
97
  }
109
98
  const config = await deps.projectService.loadConfig(repoRoot);
110
99
  const persisted = await loadPersistedProjectGateReviewerSession(deps.fs, repoRoot);
@@ -125,6 +114,10 @@ export function createSessionService(deps) {
125
114
  const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
126
115
  ? persisted.transcriptPath
127
116
  : claudeTranscriptPath(repoRoot, claudeSessionId);
117
+ const activeTask = activeTaskSlug
118
+ ? await deps.taskService.loadTask(repoRoot, activeTaskSlug)
119
+ : undefined;
120
+ const activeTaskRepoRoot = activeTask ? getTaskRuntimeRepoRoot(activeTask) : undefined;
128
121
  const startCommand = {
129
122
  ...deps.claude.buildRoleStartCommand(GATE_REVIEWER_ROLE, config.claudeCommand, permissionMode, claudeSessionId, launchMode === "resume", model, effort),
130
123
  cwd: repoRoot
@@ -137,8 +130,8 @@ export function createSessionService(deps) {
137
130
  cwd: startCommand.cwd,
138
131
  env: {
139
132
  VCM_API_URL: deps.apiUrl,
140
- VCM_TASK_REPO_ROOT: repoRoot,
141
- VCM_TASK_SLUG: PROJECT_GATE_REVIEWER_SCOPE,
133
+ VCM_TASK_REPO_ROOT: activeTaskRepoRoot ?? repoRoot,
134
+ VCM_TASK_SLUG: activeTaskSlug ?? PROJECT_GATE_REVIEWER_SCOPE,
142
135
  VCM_ROLE: GATE_REVIEWER_ROLE,
143
136
  VCM_SESSION_ID: claudeSessionId
144
137
  },
@@ -164,35 +157,63 @@ export function createSessionService(deps) {
164
157
  startedAt: runtimeSession.startedAt,
165
158
  updatedAt: timestamp,
166
159
  lastOutputAt: runtimeSession.lastOutputAt,
160
+ activeTaskSlug,
161
+ activeTaskRepoRoot,
167
162
  exitCode: runtimeSession.exitCode
168
163
  };
169
164
  deps.registry.upsert(record);
170
165
  await persistProjectGateReviewerSession(deps.fs, repoRoot, record);
171
166
  return record;
172
167
  }
168
+ async function bindProjectGateReviewerSession(repoRoot, record, activeTaskSlug) {
169
+ if (!activeTaskSlug) {
170
+ return record;
171
+ }
172
+ const task = await deps.taskService.loadTask(repoRoot, activeTaskSlug);
173
+ const activeTaskRepoRoot = getTaskRuntimeRepoRoot(task);
174
+ const updated = {
175
+ ...record,
176
+ taskSlug: PROJECT_GATE_REVIEWER_SCOPE,
177
+ activeTaskSlug,
178
+ activeTaskRepoRoot,
179
+ updatedAt: now()
180
+ };
181
+ deps.registry.upsert(updated);
182
+ await persistProjectGateReviewerSession(deps.fs, repoRoot, updated);
183
+ return updated;
184
+ }
173
185
  async function launchProjectTranslatorSession(repoRoot, input, launchMode) {
174
186
  const live = toRoleSessionRecordView(getRegisteredProjectTranslatorSession(deps.registry, deps.runtime), deps.runtime);
175
187
  if (live && live.status === "running") {
176
188
  return live;
177
189
  }
178
- const persisted = await loadPersistedCodexTranslatorSession(deps.fs, repoRoot);
179
- const model = normalizeCodexModel(input.model ?? persisted?.model);
180
- 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);
181
195
  const claudeSessionId = launchMode === "resume"
182
196
  ? persisted?.claudeSessionId
183
197
  : randomUUID();
184
198
  if (!claudeSessionId) {
185
199
  throw new VcmError({
186
- code: "CODEX_TRANSLATOR_SESSION_MISSING",
187
- 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.",
188
202
  statusCode: 409,
189
203
  hint: "Start the translator once before using Resume."
190
204
  });
191
205
  }
192
- 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
+ };
193
214
  const runtimeSession = await deps.runtime.createSession({
194
215
  taskSlug: PROJECT_TRANSLATOR_SCOPE,
195
- role: CODEX_TRANSLATOR_ROLE,
216
+ role: TRANSLATOR_ROLE,
196
217
  command: startCommand.command,
197
218
  args: startCommand.args,
198
219
  cwd: startCommand.cwd,
@@ -200,7 +221,7 @@ export function createSessionService(deps) {
200
221
  VCM_API_URL: deps.apiUrl,
201
222
  VCM_TASK_REPO_ROOT: repoRoot,
202
223
  VCM_TASK_SLUG: PROJECT_TRANSLATOR_SCOPE,
203
- VCM_ROLE: CODEX_TRANSLATOR_ROLE,
224
+ VCM_ROLE: TRANSLATOR_ROLE,
204
225
  VCM_SESSION_ID: claudeSessionId
205
226
  },
206
227
  cols: input.cols,
@@ -210,12 +231,13 @@ export function createSessionService(deps) {
210
231
  const record = {
211
232
  id: runtimeSession.id,
212
233
  claudeSessionId,
234
+ transcriptPath,
213
235
  taskSlug: PROJECT_TRANSLATOR_SCOPE,
214
- role: CODEX_TRANSLATOR_ROLE,
236
+ role: TRANSLATOR_ROLE,
215
237
  status: runtimeSession.status,
216
238
  activityStatus: "idle",
217
239
  command: startCommand.display,
218
- permissionMode: "default",
240
+ permissionMode,
219
241
  model,
220
242
  effort,
221
243
  cwd: startCommand.cwd,
@@ -227,7 +249,7 @@ export function createSessionService(deps) {
227
249
  exitCode: runtimeSession.exitCode
228
250
  };
229
251
  deps.registry.upsert(record);
230
- await persistCodexTranslatorSession(deps.fs, repoRoot, record);
252
+ await persistTranslatorSession(deps.fs, repoRoot, record);
231
253
  return record;
232
254
  }
233
255
  return {
@@ -303,7 +325,7 @@ export function createSessionService(deps) {
303
325
  if (!existing) {
304
326
  throw new VcmError({
305
327
  code: "SESSION_MISSING",
306
- message: "Codex Translator session has not been started.",
328
+ message: "Translator session has not been started.",
307
329
  statusCode: 404
308
330
  });
309
331
  }
@@ -317,7 +339,7 @@ export function createSessionService(deps) {
317
339
  updatedAt: now()
318
340
  };
319
341
  deps.registry.upsert(updated);
320
- await persistCodexTranslatorSession(deps.fs, repoRoot, updated);
342
+ await persistTranslatorSession(deps.fs, repoRoot, updated);
321
343
  return updated;
322
344
  },
323
345
  async restartProjectTranslatorSession(repoRoot, input = {}) {
@@ -333,7 +355,7 @@ export function createSessionService(deps) {
333
355
  },
334
356
  async getProjectTranslatorSession(repoRoot) {
335
357
  const record = getRegisteredProjectTranslatorSession(deps.registry, deps.runtime)
336
- ?? await loadPersistedCodexTranslatorSession(deps.fs, repoRoot);
358
+ ?? await loadPersistedTranslatorSession(deps.fs, repoRoot);
337
359
  return toRoleSessionRecordView(record, deps.runtime);
338
360
  },
339
361
  async ensureProjectTranslatorSession(repoRoot, input = {}) {
@@ -372,15 +394,15 @@ export function createSessionService(deps) {
372
394
  updatedAt: timestamp
373
395
  };
374
396
  deps.registry.upsert(updated);
375
- await persistCodexTranslatorSession(deps.fs, repoRoot, updated);
397
+ await persistTranslatorSession(deps.fs, repoRoot, updated);
376
398
  return updated;
377
399
  },
378
400
  startRoleSession(repoRoot, taskSlug, role, input = {}) {
379
401
  if (role === GATE_REVIEWER_ROLE) {
380
- return this.startProjectGateReviewerSession(repoRoot, input)
402
+ return launchProjectGateReviewerSession(repoRoot, input, "fresh", taskSlug)
381
403
  .then((session) => scopeProjectRoleSession(session, taskSlug));
382
404
  }
383
- if (role === CODEX_TRANSLATOR_ROLE) {
405
+ if (role === TRANSLATOR_ROLE) {
384
406
  void taskSlug;
385
407
  return this.startProjectTranslatorSession(repoRoot, input);
386
408
  }
@@ -388,10 +410,10 @@ export function createSessionService(deps) {
388
410
  },
389
411
  resumeRoleSession(repoRoot, taskSlug, role, input = {}) {
390
412
  if (role === GATE_REVIEWER_ROLE) {
391
- return this.resumeProjectGateReviewerSession(repoRoot, input)
413
+ return launchProjectGateReviewerSession(repoRoot, input, "resume", taskSlug)
392
414
  .then((session) => scopeProjectRoleSession(session, taskSlug));
393
415
  }
394
- if (role === CODEX_TRANSLATOR_ROLE) {
416
+ if (role === TRANSLATOR_ROLE) {
395
417
  void taskSlug;
396
418
  return this.resumeProjectTranslatorSession(repoRoot, input);
397
419
  }
@@ -401,7 +423,7 @@ export function createSessionService(deps) {
401
423
  if (role === GATE_REVIEWER_ROLE) {
402
424
  return scopeProjectRoleSession(await this.stopProjectGateReviewerSession(repoRoot), taskSlug);
403
425
  }
404
- if (role === CODEX_TRANSLATOR_ROLE) {
426
+ if (role === TRANSLATOR_ROLE) {
405
427
  void taskSlug;
406
428
  return this.stopProjectTranslatorSession(repoRoot);
407
429
  }
@@ -430,9 +452,16 @@ export function createSessionService(deps) {
430
452
  },
431
453
  async restartRoleSession(repoRoot, taskSlug, role, input = {}) {
432
454
  if (role === GATE_REVIEWER_ROLE) {
433
- return scopeProjectRoleSession(await this.restartProjectGateReviewerSession(repoRoot, input), taskSlug);
455
+ const existing = await this.getProjectGateReviewerSession(repoRoot);
456
+ if (existing && deps.runtime.getSession(existing.id)) {
457
+ await deps.runtime.stop(existing.id);
458
+ }
459
+ if (existing) {
460
+ deps.registry.remove(existing.id);
461
+ }
462
+ return scopeProjectRoleSession(await launchProjectGateReviewerSession(repoRoot, input, "fresh", taskSlug), taskSlug);
434
463
  }
435
- if (role === CODEX_TRANSLATOR_ROLE) {
464
+ if (role === TRANSLATOR_ROLE) {
436
465
  void taskSlug;
437
466
  return this.restartProjectTranslatorSession(repoRoot, input);
438
467
  }
@@ -448,9 +477,13 @@ export function createSessionService(deps) {
448
477
  },
449
478
  async getRoleSession(repoRoot, taskSlug, role) {
450
479
  if (role === GATE_REVIEWER_ROLE) {
451
- return scopeProjectRoleSession(await this.getProjectGateReviewerSession(repoRoot), taskSlug);
480
+ const session = await this.getProjectGateReviewerSession(repoRoot);
481
+ if (!session) {
482
+ return undefined;
483
+ }
484
+ return scopeProjectRoleSession(await bindProjectGateReviewerSession(repoRoot, session, taskSlug), taskSlug);
452
485
  }
453
- if (role === CODEX_TRANSLATOR_ROLE) {
486
+ if (role === TRANSLATOR_ROLE) {
454
487
  void taskSlug;
455
488
  return this.getProjectTranslatorSession(repoRoot);
456
489
  }
@@ -470,7 +503,7 @@ export function createSessionService(deps) {
470
503
  const task = await deps.taskService.loadTask(repoRoot, taskSlug);
471
504
  const taskRepoRoot = getTaskRuntimeRepoRoot(task);
472
505
  const persistedTaskSession = await loadPersistedTaskSessionRecord(deps.fs, taskRepoRoot, config.stateRoot, taskSlug);
473
- for (const role of VCM_ROLE_NAMES) {
506
+ for (const role of CORE_VCM_ROLE_NAMES) {
474
507
  const record = deps.registry.getByRole(taskSlug, role)
475
508
  ?? normalizePersistedRoleRecord(persistedTaskSession?.roles[role]?.record);
476
509
  const session = toRoleSessionRecordView(record, deps.runtime);
@@ -510,7 +543,7 @@ export function createSessionService(deps) {
510
543
  await persistProjectGateReviewerSession(deps.fs, repoRoot, updated);
511
544
  return scopeProjectRoleSession(updated, input.taskSlug);
512
545
  }
513
- if (input.role === CODEX_TRANSLATOR_ROLE) {
546
+ if (input.role === TRANSLATOR_ROLE) {
514
547
  void input.taskSlug;
515
548
  return this.recordProjectTranslatorHookEvent(repoRoot, {
516
549
  eventName: input.eventName,
@@ -633,80 +666,6 @@ function toRoleSessionRecordView(record, runtime) {
633
666
  exitCode: runtimeSession.exitCode
634
667
  };
635
668
  }
636
- async function buildCodexStartCommand(fs, baseRepoRoot, _taskRepoRoot, role, launchMode, selectedModel, selectedEffort, sandboxMode, resumeSessionId) {
637
- const isTranslator = role === CODEX_TRANSLATOR_ROLE;
638
- if (!isTranslator) {
639
- throw new VcmError({
640
- code: "CODEX_ROLE_UNSUPPORTED",
641
- message: `${role} is not a Codex role.`,
642
- statusCode: 400
643
- });
644
- }
645
- const codexDir = resolveRepoPath(baseRepoRoot, CODEX_TRANSLATOR_DIR);
646
- const outputDir = resolveRepoPath(baseRepoRoot, CODEX_TRANSLATION_DIR);
647
- if (!(await fs.pathExists(codexDir))) {
648
- throw new VcmError({
649
- code: "CODEX_TRANSLATOR_CONFIG_MISSING",
650
- message: `${CODEX_TRANSLATOR_DIR} does not exist.`,
651
- statusCode: 409,
652
- hint: "Apply the VCM harness before starting Codex Translator."
653
- });
654
- }
655
- await fs.ensureDir(outputDir);
656
- const config = await loadCodexSessionConfig(fs, baseRepoRoot, CODEX_TRANSLATOR_CONFIG_PATH);
657
- const args = launchMode === "resume"
658
- ? resumeSessionId ? ["resume", resumeSessionId] : ["resume", "--last"]
659
- : [];
660
- args.push("--cd", codexDir);
661
- if (isDevContainerSandbox(sandboxMode)) {
662
- args.push("--dangerously-bypass-approvals-and-sandbox");
663
- }
664
- else {
665
- args.push("--add-dir", baseRepoRoot);
666
- args.push("--sandbox", "workspace-write", "--ask-for-approval", "never");
667
- }
668
- args.push("--dangerously-bypass-hook-trust", "--search");
669
- if (selectedModel !== "default") {
670
- args.push("--model", selectedModel);
671
- }
672
- if (selectedEffort !== "default") {
673
- args.push("--config", `model_reasoning_effort="${selectedEffort}"`);
674
- }
675
- return {
676
- command: config.command,
677
- args,
678
- cwd: baseRepoRoot,
679
- display: [config.command, ...args].map(formatDisplayArg).join(" ")
680
- };
681
- }
682
- function hasCapturedCodexSession(record) {
683
- return Boolean(record?.lastHookEventAt || record?.transcriptPath);
684
- }
685
- function isDevContainerSandbox(value) {
686
- const normalized = value?.toLowerCase().replace(/[\s_-]+/g, "");
687
- return normalized === "devcontainer" ||
688
- normalized === "container" ||
689
- normalized === "docker" ||
690
- normalized === "podman" ||
691
- normalized === "codespaces" ||
692
- normalized === "bypass" ||
693
- normalized === "nosandbox" ||
694
- normalized === "disabled" ||
695
- normalized === "off" ||
696
- normalized === "none";
697
- }
698
- async function loadCodexSessionConfig(fs, repoRoot, configRelativePath) {
699
- const configPath = resolveRepoPath(repoRoot, configRelativePath);
700
- if (!(await fs.pathExists(configPath))) {
701
- return {
702
- command: "codex"
703
- };
704
- }
705
- const content = await fs.readText(configPath);
706
- return {
707
- command: parseTomlString(content, "command") ?? "codex"
708
- };
709
- }
710
669
  function matchesRoleHookSession(record, input) {
711
670
  if (input.sessionId && record.claudeSessionId === input.sessionId) {
712
671
  return true;
@@ -744,7 +703,7 @@ function getHandoffArtifactPath(paths, role) {
744
703
  return undefined;
745
704
  }
746
705
  function getRegisteredRoleSession(registry, runtime, taskSlug, role) {
747
- if (role !== CODEX_TRANSLATOR_ROLE && role !== GATE_REVIEWER_ROLE) {
706
+ if (role !== TRANSLATOR_ROLE && role !== GATE_REVIEWER_ROLE) {
748
707
  return registry.getByRole(taskSlug, role);
749
708
  }
750
709
  const candidates = registry.list().filter((session) => session.role === role);
@@ -760,7 +719,7 @@ function getRegisteredProjectGateReviewerSession(registry, runtime) {
760
719
  return live ?? candidates.sort(compareSessionUpdatedAtDesc)[0];
761
720
  }
762
721
  function getRegisteredProjectTranslatorSession(registry, runtime) {
763
- const candidates = registry.list().filter((session) => session.role === CODEX_TRANSLATOR_ROLE);
722
+ const candidates = registry.list().filter((session) => session.role === TRANSLATOR_ROLE);
764
723
  const live = candidates.find((session) => runtime.getSession(session.id)?.status === "running");
765
724
  return live ?? candidates.sort(compareSessionUpdatedAtDesc)[0];
766
725
  }
@@ -768,7 +727,7 @@ function compareSessionUpdatedAtDesc(left, right) {
768
727
  return (right.updatedAt ?? "").localeCompare(left.updatedAt ?? "");
769
728
  }
770
729
  function scopeProjectRoleSession(record, taskSlug) {
771
- if (!record || (record.role !== CODEX_TRANSLATOR_ROLE && record.role !== GATE_REVIEWER_ROLE)) {
730
+ if (!record || (record.role !== TRANSLATOR_ROLE && record.role !== GATE_REVIEWER_ROLE)) {
772
731
  return record;
773
732
  }
774
733
  return {
@@ -781,9 +740,9 @@ async function loadPersistedRoleRecordForRole(fs, baseRepoRoot, taskRepoRoot, st
781
740
  void taskSlug;
782
741
  return loadPersistedProjectGateReviewerSession(fs, baseRepoRoot);
783
742
  }
784
- if (role === CODEX_TRANSLATOR_ROLE) {
743
+ if (role === TRANSLATOR_ROLE) {
785
744
  void taskSlug;
786
- return loadPersistedCodexTranslatorSession(fs, baseRepoRoot);
745
+ return loadPersistedTranslatorSession(fs, baseRepoRoot);
787
746
  }
788
747
  return loadPersistedRoleRecord(fs, taskRepoRoot, stateRoot, taskSlug, role);
789
748
  }
@@ -802,8 +761,8 @@ async function loadPersistedProjectGateReviewerSession(fs, repoRoot) {
802
761
  taskSlug: PROJECT_GATE_REVIEWER_SCOPE
803
762
  };
804
763
  }
805
- async function loadPersistedCodexTranslatorSession(fs, repoRoot) {
806
- const sessionPath = resolveRepoPath(repoRoot, CODEX_TRANSLATOR_SESSION_PATH);
764
+ async function loadPersistedTranslatorSession(fs, repoRoot) {
765
+ const sessionPath = resolveRepoPath(repoRoot, TRANSLATOR_SESSION_PATH);
807
766
  if (!(await fs.pathExists(sessionPath))) {
808
767
  return undefined;
809
768
  }
@@ -840,12 +799,8 @@ function normalizePersistedRoleRecord(record) {
840
799
  ? legacy.lastStopAt
841
800
  : undefined),
842
801
  permissionMode: normalizeClaudePermissionMode(record.permissionMode),
843
- model: isCodexRoleName(record.role)
844
- ? normalizeCodexModel(record.model)
845
- : normalizeClaudeModel(record.model),
846
- effort: isCodexRoleName(record.role)
847
- ? normalizeCodexEffort(record.effort)
848
- : normalizeClaudeEffort(record.effort)
802
+ model: normalizeClaudeModel(record.model),
803
+ effort: normalizeClaudeEffort(record.effort)
849
804
  }
850
805
  : undefined;
851
806
  }
@@ -879,8 +834,8 @@ async function persistRoleSessionRecord(fs, baseRepoRoot, taskRepoRoot, stateRoo
879
834
  await persistProjectGateReviewerSession(fs, baseRepoRoot, session);
880
835
  return;
881
836
  }
882
- if (session.role === CODEX_TRANSLATOR_ROLE) {
883
- await persistCodexTranslatorSession(fs, baseRepoRoot, session);
837
+ if (session.role === TRANSLATOR_ROLE) {
838
+ await persistTranslatorSession(fs, baseRepoRoot, session);
884
839
  return;
885
840
  }
886
841
  await persistTaskSession(fs, taskRepoRoot, stateRoot, session);
@@ -896,8 +851,8 @@ async function persistProjectGateReviewerSession(fs, repoRoot, session) {
896
851
  }
897
852
  });
898
853
  }
899
- async function persistCodexTranslatorSession(fs, repoRoot, session) {
900
- 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), {
901
856
  version: 1,
902
857
  role: session.role,
903
858
  updatedAt: session.updatedAt,
@@ -943,12 +898,6 @@ function normalizeClaudeModel(value) {
943
898
  }
944
899
  return "default";
945
900
  }
946
- function normalizeCodexModel(value) {
947
- if (value === "default" || value === "gpt-5.5") {
948
- return value;
949
- }
950
- return "gpt-5.5";
951
- }
952
901
  function normalizeClaudeEffort(value) {
953
902
  if (value === "low"
954
903
  || value === "medium"
@@ -960,25 +909,3 @@ function normalizeClaudeEffort(value) {
960
909
  }
961
910
  return "default";
962
911
  }
963
- function normalizeCodexEffort(value) {
964
- if (value === "low"
965
- || value === "medium"
966
- || value === "high"
967
- || value === "xhigh") {
968
- return value;
969
- }
970
- return "default";
971
- }
972
- function parseTomlString(content, key) {
973
- const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"\\s*$`, "m");
974
- return pattern.exec(content)?.[1];
975
- }
976
- function escapeRegExp(value) {
977
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
978
- }
979
- function formatDisplayArg(value) {
980
- if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) {
981
- return value;
982
- }
983
- return `'${value.replaceAll("'", "'\\''")}'`;
984
- }
@@ -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
  }