vibe-coding-master 0.3.22 → 0.3.24

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 (35) hide show
  1. package/README.md +2 -2
  2. package/dist/backend/adapters/git-adapter.js +53 -0
  3. package/dist/backend/api/artifact-routes.js +1 -28
  4. package/dist/backend/api/codex-translation-routes.js +24 -0
  5. package/dist/backend/api/harness-routes.js +10 -0
  6. package/dist/backend/api/task-routes.js +2 -6
  7. package/dist/backend/gateway/gateway-service.js +3 -16
  8. package/dist/backend/server.js +5 -2
  9. package/dist/backend/services/artifact-service.js +1 -30
  10. package/dist/backend/services/codex-hook-service.js +30 -9
  11. package/dist/backend/services/codex-translation-service.js +17 -40
  12. package/dist/backend/services/harness-service.js +106 -0
  13. package/dist/backend/services/session-service.js +189 -15
  14. package/dist/backend/services/status-service.js +1 -6
  15. package/dist/backend/services/task-service.js +42 -110
  16. package/dist/backend/services/translation-service.js +15 -4
  17. package/dist/backend/templates/handoff.js +3 -3
  18. package/dist/backend/templates/harness/architect-agent.js +3 -2
  19. package/dist/backend/templates/harness/coder-agent.js +6 -1
  20. package/dist/backend/templates/harness/project-manager-agent.js +10 -1
  21. package/dist/backend/templates/harness/vcm-route-message-skill.js +8 -2
  22. package/dist/shared/types/app-settings.js +3 -2
  23. package/dist-frontend/assets/index-CKWy15WL.js +94 -0
  24. package/dist-frontend/assets/index-Cfum1Prr.css +32 -0
  25. package/dist-frontend/index.html +2 -2
  26. package/docs/cc-best-practices.md +1 -1
  27. package/docs/codex-translation-plan.md +41 -37
  28. package/docs/full-harness-baseline.md +0 -1
  29. package/docs/gateway-design.md +9 -13
  30. package/docs/product-design.md +23 -44
  31. package/docs/v0.4-custom-workflow-plan.md +785 -0
  32. package/docs/vcm-cc-best-practices.md +15 -13
  33. package/package.json +1 -1
  34. package/dist-frontend/assets/index-CEB6Bssn.js +0 -95
  35. package/dist-frontend/assets/index-Dmefx9m7.css +0 -32
package/README.md CHANGED
@@ -237,7 +237,7 @@ the base repo has uncommitted changes, when the current branch has no upstream,
237
237
  or when the active task is an inline task using the base repo directly. It does
238
238
  not stash, merge, or mutate task worktrees.
239
239
 
240
- When VCM is connected to an active task, the bottom of the sidebar shows a task status dock. It stays outside the collapsible groups and shows the active VCM Session title, task status, start time, total elapsed time, total Round count, and role active runtime. If a Round is currently running, the dock also shows the Current Round start time, total elapsed time, role active runtime, and Turn count.
240
+ When VCM is connected to an active task, the bottom of the sidebar shows a task status dock. It stays outside the collapsible groups and shows the active VCM Session title, task status, start time, total elapsed time, total Round count, and role active runtime. The dock also shows the Current Round while it is running, or the Last Round after a flow pause, including start time, total elapsed time, role active runtime, Turn count, and Round status.
241
241
 
242
242
  ## Mobile Gateway
243
243
 
@@ -586,7 +586,7 @@ The normal path is timer-driven: `Stop` starts a backend timer, and `UserPromptS
586
586
 
587
587
  When `Flow pause alert` is enabled, the frontend polls the task Round state and deduplicates each stopped Round so the same stopped state does not alert on every poll. Flow duration is measured from the first `UserPromptSubmit` to `stoppedAt`, falling back to the last `Stop` when needed. If the flow lasted less than 2 minutes, it plays the local chime 3 times at 1.4 second intervals. If the flow lasted 2 minutes or longer, it shows a modal alert and repeats the local chime until the user clicks `Confirm`. A stopped Round can mean normal completion, user decision needed, dispatch failure, or another workflow interruption; the point is to get the user to look with the right amount of urgency.
588
588
 
589
- The Current Round dock separates wall-clock Round duration from role active runtime. `Total` is `now - Round.startedAt`; `Role runtime` is only the accumulated time between each Turn's `UserPromptSubmit` and `Stop`; `Turn count` is the number of accepted prompts inside the current Round.
589
+ The Round dock separates wall-clock Round duration from role active runtime. For a running Round, `Total` is `now - Round.startedAt`; for a stopped Round, it is `stoppedAt - Round.startedAt`. `Role runtime` is only the accumulated time between each Turn's `UserPromptSubmit` and `Stop`; `Turn count` is the number of accepted prompts inside the Round.
590
590
 
591
591
  ## Resume Behavior
592
592
 
@@ -81,6 +81,18 @@ export function createGitAdapter(runner) {
81
81
  }
82
82
  return result.stdout;
83
83
  },
84
+ async getStagedStatus(repoRoot) {
85
+ const result = await runGit(runner, repoRoot, ["diff", "--cached", "--name-status"]);
86
+ if (result.exitCode !== 0) {
87
+ throw new VcmError({
88
+ code: "GIT_ERROR",
89
+ message: "Unable to read staged Git changes.",
90
+ statusCode: 400,
91
+ hint: result.stderr
92
+ });
93
+ }
94
+ return result.stdout;
95
+ },
84
96
  async isIgnored(repoRoot, repoRelativePath) {
85
97
  const result = await runGit(runner, repoRoot, ["check-ignore", "-q", "--", repoRelativePath]);
86
98
  if (result.exitCode === 0) {
@@ -111,6 +123,47 @@ export function createGitAdapter(runner) {
111
123
  hint: result.stderr
112
124
  });
113
125
  },
126
+ async addPaths(repoRoot, paths) {
127
+ if (paths.length === 0) {
128
+ return;
129
+ }
130
+ const result = await runGit(runner, repoRoot, ["add", "--", ...paths]);
131
+ if (result.exitCode !== 0) {
132
+ throw new VcmError({
133
+ code: "GIT_ADD_FAILED",
134
+ message: "Unable to stage harness changes.",
135
+ statusCode: 409,
136
+ hint: result.stderr || result.stdout
137
+ });
138
+ }
139
+ },
140
+ async commit(repoRoot, message) {
141
+ const result = await runGit(runner, repoRoot, ["commit", "-m", message]);
142
+ if (result.exitCode !== 0) {
143
+ throw new VcmError({
144
+ code: "GIT_COMMIT_FAILED",
145
+ message: "Unable to commit harness changes.",
146
+ statusCode: 409,
147
+ hint: result.stderr || result.stdout
148
+ });
149
+ }
150
+ return this.getHeadCommit(repoRoot);
151
+ },
152
+ async rebase(repoRoot, upstream) {
153
+ const result = await runGit(runner, repoRoot, ["rebase", upstream]);
154
+ if (result.exitCode !== 0) {
155
+ throw new VcmError({
156
+ code: "GIT_REBASE_FAILED",
157
+ message: "Unable to rebase task branch onto the harness commit.",
158
+ statusCode: 409,
159
+ hint: result.stderr || result.stdout
160
+ });
161
+ }
162
+ return {
163
+ stdout: result.stdout,
164
+ stderr: result.stderr
165
+ };
166
+ },
114
167
  async createWorktree(input) {
115
168
  const result = await runGit(runner, input.repoRoot, [
116
169
  "worktree",
@@ -1,4 +1,4 @@
1
- import { isDispatchableRole, isRoleName } from "../../shared/constants.js";
1
+ import { isDispatchableRole } from "../../shared/constants.js";
2
2
  import { VcmError } from "../errors.js";
3
3
  import { getTaskRuntimeRepoRoot } from "../services/task-service.js";
4
4
  export function registerArtifactRoutes(app, deps) {
@@ -52,33 +52,6 @@ export function registerArtifactRoutes(app, deps) {
52
52
  });
53
53
  return { ok: true };
54
54
  });
55
- app.get("/api/tasks/:taskSlug/logs/:role", async (request) => {
56
- const project = await requireCurrentProject(deps.projectService);
57
- if (!isRoleName(request.params.role)) {
58
- throw new VcmError({
59
- code: "UNKNOWN_ROLE",
60
- message: `Unknown role: ${request.params.role}`,
61
- statusCode: 400
62
- });
63
- }
64
- const task = await deps.taskService.loadTask(project.repoRoot, request.params.taskSlug);
65
- const taskRepoRoot = getTaskRuntimeRepoRoot(task);
66
- const paths = deps.artifactService.getHandoffPaths(taskRepoRoot, task.handoffDir);
67
- const artifactPath = paths.roleLogPaths[request.params.role];
68
- if (!artifactPath) {
69
- return {
70
- role: request.params.role,
71
- content: ""
72
- };
73
- }
74
- return {
75
- role: request.params.role,
76
- content: await deps.artifactService.readArtifact({
77
- repoRoot: taskRepoRoot,
78
- artifactPath
79
- })
80
- };
81
- });
82
55
  }
83
56
  function parseDispatchableRole(role) {
84
57
  if (!isDispatchableRole(role)) {
@@ -4,6 +4,30 @@ export function registerCodexTranslationRoutes(app, deps) {
4
4
  const project = await requireCurrentProject(deps.projectService);
5
5
  return deps.codexTranslationService.getState(project.repoRoot);
6
6
  });
7
+ app.get("/api/translation/codex/session", async () => {
8
+ const project = await requireCurrentProject(deps.projectService);
9
+ return (await deps.sessionService.getProjectTranslatorSession(project.repoRoot)) ?? null;
10
+ });
11
+ app.post("/api/translation/codex/session/ensure", async (request) => {
12
+ const project = await requireCurrentProject(deps.projectService);
13
+ return deps.sessionService.ensureProjectTranslatorSession(project.repoRoot, request.body);
14
+ });
15
+ app.post("/api/translation/codex/session/start", async (request) => {
16
+ const project = await requireCurrentProject(deps.projectService);
17
+ return deps.sessionService.startProjectTranslatorSession(project.repoRoot, request.body);
18
+ });
19
+ app.post("/api/translation/codex/session/resume", async (request) => {
20
+ const project = await requireCurrentProject(deps.projectService);
21
+ return deps.sessionService.resumeProjectTranslatorSession(project.repoRoot, request.body);
22
+ });
23
+ app.post("/api/translation/codex/session/restart", async (request) => {
24
+ const project = await requireCurrentProject(deps.projectService);
25
+ return deps.sessionService.restartProjectTranslatorSession(project.repoRoot, request.body);
26
+ });
27
+ app.post("/api/translation/codex/session/stop", async () => {
28
+ const project = await requireCurrentProject(deps.projectService);
29
+ return deps.sessionService.stopProjectTranslatorSession(project.repoRoot);
30
+ });
7
31
  app.get("/api/translation/codex/source-files", async (request) => {
8
32
  const project = await requireCurrentProject(deps.projectService);
9
33
  return deps.codexTranslationService.browseSourceFiles(project.repoRoot, {
@@ -16,6 +16,16 @@ export function registerHarnessRoutes(app, deps) {
16
16
  const project = await requireCurrentProject(deps.projectService);
17
17
  return deps.harnessService.applyHarness(project.repoRoot);
18
18
  });
19
+ app.post("/api/projects/harness/tasks/:taskSlug/commit-and-rebase", async (request) => {
20
+ const project = await requireCurrentProject(deps.projectService);
21
+ const task = await deps.taskService.loadTask(project.repoRoot, request.params.taskSlug);
22
+ return deps.harnessService.commitAndRebaseTask(project.repoRoot, {
23
+ taskSlug: task.taskSlug,
24
+ branch: task.branch,
25
+ worktreePath: task.worktreePath,
26
+ changedFiles: request.body?.changedFiles ?? []
27
+ });
28
+ });
19
29
  app.get("/api/projects/harness/bootstrap", async () => {
20
30
  const project = await requireCurrentProject(deps.projectService);
21
31
  try {
@@ -1,4 +1,4 @@
1
- import { DISPATCHABLE_ROLES, ROLE_NAMES } from "../../shared/constants.js";
1
+ import { DISPATCHABLE_ROLES } from "../../shared/constants.js";
2
2
  import { isOpenFileLimitError, VcmError } from "../errors.js";
3
3
  import { getTaskRuntimeRepoRoot } from "../services/task-service.js";
4
4
  export function registerTaskRoutes(app, deps) {
@@ -66,6 +66,7 @@ function degradedTaskStatus(repoRoot, taskSlug, error) {
66
66
  createdAt: timestamp,
67
67
  updatedAt: timestamp,
68
68
  repoRoot,
69
+ worktreePath: `${repoRoot}/.claude/worktrees/${taskSlug}`,
69
70
  branch: "unknown",
70
71
  handoffDir,
71
72
  status: "stopped"
@@ -79,18 +80,13 @@ function degradedTaskStatus(repoRoot, taskSlug, error) {
79
80
  }
80
81
  function degradedArtifactSummary(handoffDir) {
81
82
  const roleCommandsDir = `${handoffDir}/role-commands`;
82
- const logsDir = `${handoffDir}/logs`;
83
83
  const messagesDir = `${handoffDir}/messages`;
84
84
  return {
85
85
  paths: {
86
86
  handoffDir,
87
87
  roleCommandsDir,
88
- logsDir,
89
88
  messagesDir,
90
89
  roleCommandPaths: Object.fromEntries(DISPATCHABLE_ROLES.map((role) => [role, `${roleCommandsDir}/${role}.md`])),
91
- roleLogPaths: Object.fromEntries(ROLE_NAMES
92
- .filter((role) => role !== "codex-translator")
93
- .map((role) => [role, `${logsDir}/${role}.log`])),
94
90
  messageRoutePaths: {},
95
91
  architecturePlanPath: `${handoffDir}/architecture-plan.md`,
96
92
  knownIssuesPath: `${handoffDir}/known-issues.md`,
@@ -352,17 +352,6 @@ export function createGatewayService(deps) {
352
352
  }
353
353
  async function pullCurrent() {
354
354
  const project = await ensureProject();
355
- const settings = await syncDesktopContext(await deps.settings.loadSettings());
356
- if (settings.currentTaskSlug) {
357
- const task = await deps.taskService.loadTask(project.repoRoot, settings.currentTaskSlug);
358
- if (!task.worktreePath && task.cleanupStatus !== "cleaned") {
359
- throw new VcmError({
360
- code: "GATEWAY_PULL_BLOCKED_BY_INLINE_TASK",
361
- message: `Inline task "${task.taskSlug}" uses the base repository.`,
362
- statusCode: 409
363
- });
364
- }
365
- }
366
355
  const pulled = await deps.projectService.pullCurrentProject();
367
356
  return [
368
357
  "Connected repository updated.",
@@ -377,7 +366,7 @@ export function createGatewayService(deps) {
377
366
  if (tasks.length === 0) {
378
367
  return "No tasks. Use /create-task <task-slug> [title] to create one.";
379
368
  }
380
- return tasks.map((task, index) => `${index + 1}. ${task.taskSlug} [${task.status}] ${task.worktreePath ? "worktree" : "inline"}`).join("\n");
369
+ return tasks.map((task, index) => `${index + 1}. ${task.taskSlug} [${task.status}] ${task.branch}`).join("\n");
381
370
  }
382
371
  async function useTask(selector) {
383
372
  const project = await ensureProject();
@@ -405,8 +394,7 @@ export function createGatewayService(deps) {
405
394
  const project = await ensureProject();
406
395
  const task = await deps.taskService.createTask(project.repoRoot, {
407
396
  taskSlug,
408
- title,
409
- createWorktree: true
397
+ title
410
398
  });
411
399
  const config = await deps.projectService.loadConfig(project.repoRoot);
412
400
  const taskRepoRoot = getTaskRuntimeRepoRoot(task);
@@ -460,7 +448,7 @@ export function createGatewayService(deps) {
460
448
  return [
461
449
  `Task created and initialized: ${task.taskSlug}`,
462
450
  `branch: ${task.branch}`,
463
- `worktree: ${task.worktreePath ?? task.repoRoot}`,
451
+ `worktree: ${task.worktreePath}`,
464
452
  `orchestration: ${template.autoOrchestration ? "auto" : "manual"}`,
465
453
  `translation: ${preferences.translationEnabled ? "on" : "off"}`,
466
454
  `sessions: ${startedRoles.join(", ")}`
@@ -523,7 +511,6 @@ export function createGatewayService(deps) {
523
511
  deps.roundService.stopTask(taskSlug);
524
512
  const result = await deps.taskService.cleanupTask(project.repoRoot, taskSlug, {
525
513
  force: true,
526
- deleteBranch: Boolean(task.worktreePath),
527
514
  forceDeleteBranch: true
528
515
  });
529
516
  clearFailedTranslation(project.repoRoot, taskSlug);
@@ -73,7 +73,8 @@ export async function createServer(deps, options = {}) {
73
73
  });
74
74
  registerCodexTranslationRoutes(app, {
75
75
  projectService: deps.projectService,
76
- codexTranslationService: deps.codexTranslationService
76
+ codexTranslationService: deps.codexTranslationService,
77
+ sessionService: deps.sessionService
77
78
  });
78
79
  registerProjectRoutes(app, {
79
80
  projectService: deps.projectService,
@@ -81,7 +82,8 @@ export async function createServer(deps, options = {}) {
81
82
  });
82
83
  registerHarnessRoutes(app, {
83
84
  projectService: deps.projectService,
84
- harnessService: deps.harnessService
85
+ harnessService: deps.harnessService,
86
+ taskService: deps.taskService
85
87
  });
86
88
  registerTaskRoutes(app, {
87
89
  projectService: deps.projectService,
@@ -169,6 +171,7 @@ export function createDefaultServerDeps(options = {}) {
169
171
  const projectService = createProjectService({ fs, git, appSettings });
170
172
  const harnessService = createHarnessService({
171
173
  fs,
174
+ git,
172
175
  runtime,
173
176
  projectService,
174
177
  apiUrl: options.apiUrl,
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { DISPATCHABLE_ROLES, ROLE_NAMES } from "../../shared/constants.js";
2
+ import { DISPATCHABLE_ROLES } from "../../shared/constants.js";
3
3
  import { checkMarkdownArtifact } from "../../shared/validation/artifact-check.js";
4
4
  import { VcmError } from "../errors.js";
5
5
  import { resolveRepoPath } from "../adapters/filesystem.js";
@@ -25,25 +25,16 @@ export function createArtifactService(fs) {
25
25
  return {
26
26
  getHandoffPaths(_repoRoot, handoffDir) {
27
27
  const roleCommandsDir = path.posix.join(handoffDir, "role-commands");
28
- const logsDir = path.posix.join(handoffDir, "logs");
29
28
  const messagesDir = path.posix.join(handoffDir, "messages");
30
29
  return {
31
30
  handoffDir,
32
31
  roleCommandsDir,
33
- logsDir,
34
32
  messagesDir,
35
33
  roleCommandPaths: {
36
34
  architect: path.posix.join(roleCommandsDir, "architect.md"),
37
35
  coder: path.posix.join(roleCommandsDir, "coder.md"),
38
36
  reviewer: path.posix.join(roleCommandsDir, "reviewer.md")
39
37
  },
40
- roleLogPaths: {
41
- "project-manager": path.posix.join(logsDir, "project-manager.log"),
42
- architect: path.posix.join(logsDir, "architect.log"),
43
- coder: path.posix.join(logsDir, "coder.log"),
44
- reviewer: path.posix.join(logsDir, "reviewer.log"),
45
- "codex-reviewer": path.posix.join(logsDir, "codex-reviewer.log")
46
- },
47
38
  messageRoutePaths: getDefaultMessageRoutePaths(messagesDir),
48
39
  architecturePlanPath: path.posix.join(handoffDir, "architecture-plan.md"),
49
40
  knownIssuesPath: path.posix.join(handoffDir, "known-issues.md"),
@@ -59,7 +50,6 @@ export function createArtifactService(fs) {
59
50
  const paths = this.getHandoffPaths(input.repoRoot, input.handoffDir);
60
51
  await fs.ensureDir(resolveRepoPath(input.repoRoot, paths.handoffDir));
61
52
  await fs.ensureDir(resolveRepoPath(input.repoRoot, paths.roleCommandsDir));
62
- await fs.ensureDir(resolveRepoPath(input.repoRoot, paths.logsDir));
63
53
  await fs.ensureDir(resolveRepoPath(input.repoRoot, paths.messagesDir));
64
54
  return paths;
65
55
  },
@@ -168,25 +158,6 @@ export function createArtifactService(fs) {
168
158
  async saveRoleCommand(input) {
169
159
  const paths = this.getHandoffPaths(input.repoRoot, input.handoffDir);
170
160
  await fs.writeText(resolveRepoPath(input.repoRoot, paths.roleCommandPaths[input.role]), input.content);
171
- },
172
- async appendRoleLog(input) {
173
- if (!ROLE_NAMES.includes(input.role)) {
174
- throw new VcmError({
175
- code: "UNKNOWN_ROLE",
176
- message: `Unknown role: ${input.role}`,
177
- statusCode: 400
178
- });
179
- }
180
- const paths = this.getHandoffPaths(input.repoRoot, input.handoffDir);
181
- const roleLogPath = paths.roleLogPaths[input.role];
182
- if (!roleLogPath) {
183
- throw new VcmError({
184
- code: "ROLE_LOG_UNAVAILABLE",
185
- message: `${input.role} does not write a handoff log.`,
186
- statusCode: 409
187
- });
188
- }
189
- await fs.appendText(resolveRepoPath(input.repoRoot, roleLogPath), input.content);
190
161
  }
191
162
  };
192
163
  }
@@ -18,6 +18,13 @@ export function createCodexHookService(deps) {
18
18
  statusCode: 409
19
19
  });
20
20
  }
21
+ if (input.role === "codex-translator") {
22
+ return {
23
+ project,
24
+ config: undefined,
25
+ taskRepoRoot: undefined
26
+ };
27
+ }
21
28
  const config = await deps.projectService.loadConfig(project.repoRoot);
22
29
  const task = await deps.taskService.loadTask(project.repoRoot, input.taskSlug);
23
30
  const taskRepoRoot = getTaskRuntimeRepoRoot(task);
@@ -37,16 +44,30 @@ export function createCodexHookService(deps) {
37
44
  });
38
45
  }
39
46
  const context = await getHookContext(input);
40
- const session = await deps.sessionService.recordRoleHookEvent(context.project.repoRoot, {
41
- taskSlug: input.taskSlug,
42
- role: input.role,
43
- eventName,
44
- sessionId: stringOrUndefined(input.event.session_id),
45
- transcriptPath: stringOrUndefined(input.event.transcript_path),
46
- cwd: stringOrUndefined(input.event.cwd),
47
- allowSessionMismatch: true
48
- });
47
+ const session = input.role === "codex-translator"
48
+ ? await deps.sessionService.recordProjectTranslatorHookEvent(context.project.repoRoot, {
49
+ eventName,
50
+ sessionId: stringOrUndefined(input.event.session_id),
51
+ transcriptPath: stringOrUndefined(input.event.transcript_path),
52
+ cwd: stringOrUndefined(input.event.cwd)
53
+ })
54
+ : await deps.sessionService.recordRoleHookEvent(context.project.repoRoot, {
55
+ taskSlug: input.taskSlug,
56
+ role: input.role,
57
+ eventName,
58
+ sessionId: stringOrUndefined(input.event.session_id),
59
+ transcriptPath: stringOrUndefined(input.event.transcript_path),
60
+ cwd: stringOrUndefined(input.event.cwd),
61
+ allowSessionMismatch: true
62
+ });
49
63
  if (input.role === "codex-reviewer") {
64
+ if (!context.config || !context.taskRepoRoot) {
65
+ throw new VcmError({
66
+ code: "CODEX_HOOK_TASK_CONTEXT_MISSING",
67
+ message: "Codex Reviewer hook is missing task context.",
68
+ statusCode: 500
69
+ });
70
+ }
50
71
  await deps.roundService.recordRoleTurnEvent({
51
72
  repoRoot: context.project.repoRoot,
52
73
  stateRepoRoot: context.taskRepoRoot,
@@ -178,7 +178,7 @@ export function createCodexTranslationService(deps) {
178
178
  await saveQueue(repoRoot, queue);
179
179
  return queued;
180
180
  }
181
- async function dispatchNext(repoRoot, taskSlug) {
181
+ async function dispatchNext(repoRoot) {
182
182
  if (!deps.runtime || !deps.sessionService) {
183
183
  return;
184
184
  }
@@ -207,7 +207,7 @@ export function createCodexTranslationService(deps) {
207
207
  queue.updatedAt = next.updatedAt;
208
208
  await saveQueue(repoRoot, queue);
209
209
  }
210
- const session = await ensureTranslatorSession(repoRoot, taskSlug, next.targetLanguage);
210
+ const session = await ensureTranslatorSession(repoRoot, next.targetLanguage);
211
211
  await submitTerminalInput(deps.runtime, session.id, batch?.prompt ?? await buildQueuePrompt(repoRoot, next));
212
212
  const dispatchedAt = now();
213
213
  for (const item of batch?.items ?? [next]) {
@@ -255,7 +255,7 @@ export function createCodexTranslationService(deps) {
255
255
  await saveQueue(repoRoot, queue);
256
256
  return { items: candidates, prompt };
257
257
  }
258
- async function ensureTranslatorSession(repoRoot, taskSlug, targetLanguage) {
258
+ async function ensureTranslatorSession(repoRoot, targetLanguage) {
259
259
  void targetLanguage;
260
260
  if (!deps.sessionService) {
261
261
  throw new VcmError({
@@ -264,17 +264,7 @@ export function createCodexTranslationService(deps) {
264
264
  statusCode: 500
265
265
  });
266
266
  }
267
- const existing = await deps.sessionService.getRoleSession(repoRoot, taskSlug, CODEX_TRANSLATOR_ROLE);
268
- if (existing?.status === "running") {
269
- return existing;
270
- }
271
- if (existing?.claudeSessionId) {
272
- return deps.sessionService.resumeRoleSession(repoRoot, taskSlug, CODEX_TRANSLATOR_ROLE, {
273
- model: existing.model,
274
- effort: existing.effort
275
- });
276
- }
277
- return deps.sessionService.startRoleSession(repoRoot, taskSlug, CODEX_TRANSLATOR_ROLE, {
267
+ return deps.sessionService.ensureProjectTranslatorSession(repoRoot, {
278
268
  model: "gpt-5.5",
279
269
  effort: "medium"
280
270
  });
@@ -902,9 +892,7 @@ export function createCodexTranslationService(deps) {
902
892
  const activeExisting = (await loadRuntimeFileJobs(repoRoot)).find((job) => fileTranslationReplacementKey(job) === replacementKey &&
903
893
  isActiveFileTranslationJobStatus(job.status));
904
894
  if (activeExisting) {
905
- if (input.taskSlug) {
906
- void dispatchNext(repoRoot, input.taskSlug);
907
- }
895
+ void dispatchNext(repoRoot);
908
896
  return activeExisting;
909
897
  }
910
898
  const timestamp = now();
@@ -1003,9 +991,7 @@ export function createCodexTranslationService(deps) {
1003
991
  lastUpdatedAt: timestamp
1004
992
  });
1005
993
  await deps.fs.writeText(resolveRepoPath(repoRoot, job.reportPath), `# Translation Report\n\nStatus: queued\nJob: ${jobId}\n`);
1006
- if (input.taskSlug) {
1007
- void dispatchNext(repoRoot, input.taskSlug);
1008
- }
994
+ void dispatchNext(repoRoot);
1009
995
  return job;
1010
996
  },
1011
997
  async createBootstrapRun(repoRoot, input) {
@@ -1062,9 +1048,7 @@ export function createCodexTranslationService(deps) {
1062
1048
  index.runs = [run, ...index.runs];
1063
1049
  index.updatedAt = timestamp;
1064
1050
  await saveBootstrapIndex(repoRoot, index);
1065
- if (input.taskSlug) {
1066
- void dispatchNext(repoRoot, input.taskSlug);
1067
- }
1051
+ void dispatchNext(repoRoot);
1068
1052
  return run;
1069
1053
  },
1070
1054
  async createMemoryUpdate(repoRoot, input) {
@@ -1120,9 +1104,7 @@ export function createCodexTranslationService(deps) {
1120
1104
  "Delete or merge stale, duplicate, temporary, and task-local content."
1121
1105
  ]
1122
1106
  });
1123
- if (input.taskSlug) {
1124
- void dispatchNext(repoRoot, input.taskSlug);
1125
- }
1107
+ void dispatchNext(repoRoot);
1126
1108
  return queueItem;
1127
1109
  },
1128
1110
  async readFileJobOutput(repoRoot, jobId) {
@@ -1156,14 +1138,12 @@ export function createCodexTranslationService(deps) {
1156
1138
  });
1157
1139
  }
1158
1140
  const timestamp = now();
1159
- const jobId = `conversation-${safeId(input.role)}-${Date.now()}-${createId().slice(0, 8)}`;
1160
- const jobRoot = `${CONVERSATION_RUNTIME_DIR}/${safeId(input.taskSlug)}/${safeId(input.role)}/jobs/${jobId}`;
1141
+ const jobId = `conversation-${Date.now()}-${createId().slice(0, 8)}`;
1142
+ const jobRoot = `${CONVERSATION_RUNTIME_DIR}/jobs/${jobId}`;
1161
1143
  const sourceHash = `sha256:${sha256(sourceText)}`;
1162
1144
  const targetLanguage = input.targetLanguage.trim() || "zh-CN";
1163
1145
  const job = {
1164
1146
  id: jobId,
1165
- taskSlug: input.taskSlug,
1166
- role: input.role,
1167
1147
  direction: input.direction,
1168
1148
  sourceHash,
1169
1149
  sourceLanguage: input.sourceLanguage.trim() || "auto",
@@ -1187,15 +1167,10 @@ export function createCodexTranslationService(deps) {
1187
1167
  version: 1,
1188
1168
  baseRepoRoot: repoRoot,
1189
1169
  pathBase: "baseRepoRoot",
1190
- job,
1191
- taskSlug: input.taskSlug,
1192
- role: input.role,
1193
1170
  direction: input.direction,
1194
- sourceHash,
1195
1171
  sourceLanguage: job.sourceLanguage,
1196
1172
  targetLanguage,
1197
1173
  translationProfile: input.translationProfile?.trim() || DEFAULT_PROFILE,
1198
- contextText: input.contextText,
1199
1174
  sourceContentBoundary: "VCM_TEXT",
1200
1175
  sourceText,
1201
1176
  absolutePaths: {
@@ -1208,8 +1183,8 @@ export function createCodexTranslationService(deps) {
1208
1183
  format: "plain-text"
1209
1184
  }
1210
1185
  });
1211
- if (input.taskSlug && !input.deferDispatch) {
1212
- void dispatchNext(repoRoot, input.taskSlug);
1186
+ if (!input.deferDispatch) {
1187
+ void dispatchNext(repoRoot);
1213
1188
  }
1214
1189
  return job;
1215
1190
  },
@@ -1290,6 +1265,7 @@ export function createCodexTranslationService(deps) {
1290
1265
  return job;
1291
1266
  },
1292
1267
  async handleCodexHook(repoRoot, eventName, taskSlug) {
1268
+ void taskSlug;
1293
1269
  if (eventName === "UserPromptSubmit") {
1294
1270
  const queue = await loadQueue(repoRoot);
1295
1271
  const active = queue.activeItemId
@@ -1306,10 +1282,11 @@ export function createCodexTranslationService(deps) {
1306
1282
  }
1307
1283
  if (eventName === "Stop") {
1308
1284
  await validateActiveQueueItem(repoRoot);
1309
- if (taskSlug) {
1310
- await dispatchNext(repoRoot, taskSlug);
1311
- }
1285
+ await dispatchNext(repoRoot);
1312
1286
  }
1287
+ },
1288
+ ensureTranslatorSession(repoRoot) {
1289
+ return ensureTranslatorSession(repoRoot, "zh-CN");
1313
1290
  }
1314
1291
  };
1315
1292
  }