vibe-coding-master 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/backend/api/artifact-routes.js +8 -1
- package/dist/backend/api/codex-translation-routes.js +24 -0
- package/dist/backend/api/project-routes.js +3 -1
- package/dist/backend/api/task-routes.js +3 -1
- package/dist/backend/server.js +10 -1
- package/dist/backend/services/artifact-service.js +10 -3
- package/dist/backend/services/codex-hook-service.js +30 -9
- package/dist/backend/services/codex-translation-service.js +183 -79
- package/dist/backend/services/session-service.js +190 -38
- package/dist/backend/services/status-service.js +3 -1
- package/dist/backend/services/translation-service.js +15 -4
- package/dist/backend/templates/handoff.js +3 -3
- package/dist/backend/templates/harness/architect-agent.js +3 -2
- package/dist/backend/templates/harness/coder-agent.js +6 -1
- package/dist/shared/types/app-settings.js +3 -2
- package/dist-frontend/assets/index-C2xTNLmb.js +95 -0
- package/dist-frontend/assets/index-Cfum1Prr.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/codex-translation-plan.md +108 -86
- package/docs/product-design.md +5 -5
- package/docs/v0.4-custom-workflow-plan.md +785 -0
- package/docs/vcm-cc-best-practices.md +15 -12
- package/package.json +1 -1
- package/dist-frontend/assets/index-DOKW0Y5n.js +0 -95
- package/dist-frontend/assets/index-Dmefx9m7.css +0 -32
|
@@ -7,7 +7,6 @@ const TRANSLATIONS_ROOT = ".ai/vcm/translations";
|
|
|
7
7
|
const TRANSLATIONS_RUNTIME_DIR = `${TRANSLATIONS_ROOT}/runtime`;
|
|
8
8
|
const MEMORY_DIR = `${TRANSLATIONS_ROOT}/memory`;
|
|
9
9
|
const QUEUE_PATH = `${TRANSLATIONS_RUNTIME_DIR}/queue.json`;
|
|
10
|
-
const LEGACY_QUEUE_PATH = `${TRANSLATIONS_ROOT}/queue.json`;
|
|
11
10
|
const FILE_INDEX_PATH = `${TRANSLATIONS_ROOT}/files/index.json`;
|
|
12
11
|
const FILE_COMPLETED_DIR = `${TRANSLATIONS_ROOT}/files/completed`;
|
|
13
12
|
const FILE_RUNTIME_JOBS_DIR = `${TRANSLATIONS_RUNTIME_DIR}/files/jobs`;
|
|
@@ -15,10 +14,6 @@ const BOOTSTRAP_INDEX_PATH = `${TRANSLATIONS_ROOT}/bootstrap/index.json`;
|
|
|
15
14
|
const BOOTSTRAP_RUNTIME_RUNS_DIR = `${TRANSLATIONS_RUNTIME_DIR}/bootstrap/runs`;
|
|
16
15
|
const CONVERSATION_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/conversations`;
|
|
17
16
|
const MEMORY_UPDATE_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/memory-updates`;
|
|
18
|
-
const TRANSLATOR_LOG_PATHS = [
|
|
19
|
-
`${TRANSLATIONS_RUNTIME_DIR}/codex-translator.log`,
|
|
20
|
-
`${TRANSLATIONS_ROOT}/codex-translator.log`
|
|
21
|
-
];
|
|
22
17
|
const DEFAULT_PROFILE = "default";
|
|
23
18
|
const DEFAULT_CHUNK_SOURCE_TOKEN_TARGET = 80000;
|
|
24
19
|
const BOOTSTRAP_DEFAULT_LIMIT = 12;
|
|
@@ -115,7 +110,6 @@ export function createCodexTranslationService(deps) {
|
|
|
115
110
|
await ensureMemoryFile(repoRoot, "style-guide.md", "# Style Guide\n");
|
|
116
111
|
await ensureMemoryFile(repoRoot, "project-context.md", "# Project Context\n");
|
|
117
112
|
await ensureMemoryFile(repoRoot, "decisions.md", "# Decisions\n");
|
|
118
|
-
await cleanupTranslatorLogs(repoRoot);
|
|
119
113
|
}
|
|
120
114
|
async function ensureMemoryFile(repoRoot, fileName, content) {
|
|
121
115
|
await deps.fs.ensureFile(resolveRepoPath(repoRoot, `${MEMORY_DIR}/${fileName}`), content);
|
|
@@ -129,19 +123,9 @@ export function createCodexTranslationService(deps) {
|
|
|
129
123
|
force: true
|
|
130
124
|
});
|
|
131
125
|
}
|
|
132
|
-
async function cleanupTranslatorLogs(repoRoot) {
|
|
133
|
-
await Promise.all(TRANSLATOR_LOG_PATHS.map((relativePath) => removeRepoPath(repoRoot, relativePath)));
|
|
134
|
-
}
|
|
135
126
|
async function loadQueue(repoRoot) {
|
|
136
127
|
const queuePath = resolveRepoPath(repoRoot, QUEUE_PATH);
|
|
137
128
|
if (!(await deps.fs.pathExists(queuePath))) {
|
|
138
|
-
const legacyQueuePath = resolveRepoPath(repoRoot, LEGACY_QUEUE_PATH);
|
|
139
|
-
if (await deps.fs.pathExists(legacyQueuePath)) {
|
|
140
|
-
const migrated = normalizeQueue(await deps.fs.readJson(legacyQueuePath));
|
|
141
|
-
await deps.fs.writeJsonAtomic(queuePath, migrated);
|
|
142
|
-
await removeRepoPath(repoRoot, LEGACY_QUEUE_PATH);
|
|
143
|
-
return migrated;
|
|
144
|
-
}
|
|
145
129
|
return {
|
|
146
130
|
version: 1,
|
|
147
131
|
updatedAt: now(),
|
|
@@ -152,7 +136,6 @@ export function createCodexTranslationService(deps) {
|
|
|
152
136
|
}
|
|
153
137
|
async function saveQueue(repoRoot, queue) {
|
|
154
138
|
await deps.fs.writeJsonAtomic(resolveRepoPath(repoRoot, QUEUE_PATH), queue);
|
|
155
|
-
await removeRepoPath(repoRoot, LEGACY_QUEUE_PATH);
|
|
156
139
|
}
|
|
157
140
|
async function loadFileIndex(repoRoot) {
|
|
158
141
|
const indexPath = resolveRepoPath(repoRoot, FILE_INDEX_PATH);
|
|
@@ -195,7 +178,7 @@ export function createCodexTranslationService(deps) {
|
|
|
195
178
|
await saveQueue(repoRoot, queue);
|
|
196
179
|
return queued;
|
|
197
180
|
}
|
|
198
|
-
async function dispatchNext(repoRoot
|
|
181
|
+
async function dispatchNext(repoRoot) {
|
|
199
182
|
if (!deps.runtime || !deps.sessionService) {
|
|
200
183
|
return;
|
|
201
184
|
}
|
|
@@ -224,7 +207,7 @@ export function createCodexTranslationService(deps) {
|
|
|
224
207
|
queue.updatedAt = next.updatedAt;
|
|
225
208
|
await saveQueue(repoRoot, queue);
|
|
226
209
|
}
|
|
227
|
-
const session = await ensureTranslatorSession(repoRoot,
|
|
210
|
+
const session = await ensureTranslatorSession(repoRoot, next.targetLanguage);
|
|
228
211
|
await submitTerminalInput(deps.runtime, session.id, batch?.prompt ?? await buildQueuePrompt(repoRoot, next));
|
|
229
212
|
const dispatchedAt = now();
|
|
230
213
|
for (const item of batch?.items ?? [next]) {
|
|
@@ -272,7 +255,7 @@ export function createCodexTranslationService(deps) {
|
|
|
272
255
|
await saveQueue(repoRoot, queue);
|
|
273
256
|
return { items: candidates, prompt };
|
|
274
257
|
}
|
|
275
|
-
async function ensureTranslatorSession(repoRoot,
|
|
258
|
+
async function ensureTranslatorSession(repoRoot, targetLanguage) {
|
|
276
259
|
void targetLanguage;
|
|
277
260
|
if (!deps.sessionService) {
|
|
278
261
|
throw new VcmError({
|
|
@@ -281,17 +264,7 @@ export function createCodexTranslationService(deps) {
|
|
|
281
264
|
statusCode: 500
|
|
282
265
|
});
|
|
283
266
|
}
|
|
284
|
-
|
|
285
|
-
if (existing?.status === "running") {
|
|
286
|
-
return existing;
|
|
287
|
-
}
|
|
288
|
-
if (existing?.claudeSessionId) {
|
|
289
|
-
return deps.sessionService.resumeRoleSession(repoRoot, taskSlug, CODEX_TRANSLATOR_ROLE, {
|
|
290
|
-
model: existing.model,
|
|
291
|
-
effort: existing.effort
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
return deps.sessionService.startRoleSession(repoRoot, taskSlug, CODEX_TRANSLATOR_ROLE, {
|
|
267
|
+
return deps.sessionService.ensureProjectTranslatorSession(repoRoot, {
|
|
295
268
|
model: "gpt-5.5",
|
|
296
269
|
effort: "medium"
|
|
297
270
|
});
|
|
@@ -577,6 +550,7 @@ export function createCodexTranslationService(deps) {
|
|
|
577
550
|
try {
|
|
578
551
|
const request = await deps.fs.readJson(resolveRepoPath(repoRoot, requestPath));
|
|
579
552
|
return {
|
|
553
|
+
job: isFileJob(request.job) ? request.job : undefined,
|
|
580
554
|
chunks: Array.isArray(request.chunks) ? request.chunks.filter(isFileTranslationChunk) : []
|
|
581
555
|
};
|
|
582
556
|
}
|
|
@@ -584,6 +558,37 @@ export function createCodexTranslationService(deps) {
|
|
|
584
558
|
return {};
|
|
585
559
|
}
|
|
586
560
|
}
|
|
561
|
+
async function loadRuntimeFileJob(repoRoot, item) {
|
|
562
|
+
if (!isFileTranslationQueueItem(item)) {
|
|
563
|
+
return undefined;
|
|
564
|
+
}
|
|
565
|
+
const request = await loadFileTranslationRequest(repoRoot, item.requestPath);
|
|
566
|
+
if (!request.job) {
|
|
567
|
+
return undefined;
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
...request.job,
|
|
571
|
+
status: toFileJobStatus(item.status),
|
|
572
|
+
queueItemId: item.id,
|
|
573
|
+
updatedAt: item.updatedAt
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
async function loadRuntimeFileJobs(repoRoot, queue) {
|
|
577
|
+
const sourceQueue = queue ?? await loadQueue(repoRoot);
|
|
578
|
+
const jobs = await Promise.all(sourceQueue.items
|
|
579
|
+
.filter(isFileTranslationQueueItem)
|
|
580
|
+
.filter((item) => item.status !== "completed")
|
|
581
|
+
.map((item) => loadRuntimeFileJob(repoRoot, item)));
|
|
582
|
+
return jobs
|
|
583
|
+
.filter((job) => Boolean(job))
|
|
584
|
+
.sort(compareFileJobUpdatedAtDesc);
|
|
585
|
+
}
|
|
586
|
+
async function findRuntimeFileJobById(repoRoot, jobId) {
|
|
587
|
+
const queue = await loadQueue(repoRoot);
|
|
588
|
+
const item = queue.items.find((candidate) => isFileTranslationQueueItem(candidate) &&
|
|
589
|
+
candidate.jobId === jobId);
|
|
590
|
+
return item ? loadRuntimeFileJob(repoRoot, item) : undefined;
|
|
591
|
+
}
|
|
587
592
|
async function validateMemoryBudget(repoRoot) {
|
|
588
593
|
const [usage, unexpectedEntries] = await Promise.all([
|
|
589
594
|
getMemoryUsage(repoRoot, deps.fs),
|
|
@@ -630,19 +635,29 @@ export function createCodexTranslationService(deps) {
|
|
|
630
635
|
if (item.type === "conversation") {
|
|
631
636
|
return;
|
|
632
637
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
await finalizeCompletedFileJob(repoRoot, job);
|
|
640
|
-
job.completedAt = job.updatedAt;
|
|
641
|
-
await cleanupSupersededCompletedFileJobs(repoRoot, index);
|
|
642
|
-
}
|
|
643
|
-
index.updatedAt = job.updatedAt;
|
|
644
|
-
await saveFileIndex(repoRoot, index);
|
|
638
|
+
if (!isFileTranslationQueueItem(item) || item.status !== "completed") {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const runtimeJob = await loadRuntimeFileJob(repoRoot, item);
|
|
642
|
+
if (!runtimeJob) {
|
|
643
|
+
return;
|
|
645
644
|
}
|
|
645
|
+
const completedAt = now();
|
|
646
|
+
const completedJob = {
|
|
647
|
+
...runtimeJob,
|
|
648
|
+
status: "completed",
|
|
649
|
+
updatedAt: completedAt,
|
|
650
|
+
completedAt
|
|
651
|
+
};
|
|
652
|
+
await finalizeCompletedFileJob(repoRoot, completedJob);
|
|
653
|
+
const index = await loadFileIndex(repoRoot);
|
|
654
|
+
index.jobs = [
|
|
655
|
+
completedJob,
|
|
656
|
+
...index.jobs.filter((job) => job.id !== completedJob.id)
|
|
657
|
+
];
|
|
658
|
+
await cleanupSupersededCompletedFileJobs(repoRoot, index);
|
|
659
|
+
index.updatedAt = completedAt;
|
|
660
|
+
await saveFileIndex(repoRoot, index);
|
|
646
661
|
}
|
|
647
662
|
async function cleanupCompletedRuntime(repoRoot) {
|
|
648
663
|
const fileIndex = await loadFileIndex(repoRoot);
|
|
@@ -674,6 +689,48 @@ export function createCodexTranslationService(deps) {
|
|
|
674
689
|
.map((item) => cleanupRuntimeDirectoryForPath(repoRoot, item.requestPath)));
|
|
675
690
|
await pruneQueueItems(repoRoot, isPrunableCompletedQueueItem);
|
|
676
691
|
}
|
|
692
|
+
async function cleanupStartupFileIndex(repoRoot) {
|
|
693
|
+
const indexPath = resolveRepoPath(repoRoot, FILE_INDEX_PATH);
|
|
694
|
+
if (!(await deps.fs.pathExists(indexPath))) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const index = await loadFileIndex(repoRoot);
|
|
698
|
+
const seenKeys = new Set();
|
|
699
|
+
const jobs = [];
|
|
700
|
+
for (const job of index.jobs) {
|
|
701
|
+
if (job.status !== "completed" || !isCompletedFileResultPath(job.resultPath)) {
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
const key = fileTranslationReplacementKey(job);
|
|
705
|
+
if (seenKeys.has(key)) {
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (await deps.fs.pathExists(resolveRepoPath(repoRoot, job.resultPath))) {
|
|
709
|
+
seenKeys.add(key);
|
|
710
|
+
jobs.push(job);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (jobs.length === index.jobs.length) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
index.jobs = jobs;
|
|
717
|
+
index.updatedAt = now();
|
|
718
|
+
await saveFileIndex(repoRoot, index);
|
|
719
|
+
}
|
|
720
|
+
async function cleanupStartupBootstrapIndex(repoRoot) {
|
|
721
|
+
const indexPath = resolveRepoPath(repoRoot, BOOTSTRAP_INDEX_PATH);
|
|
722
|
+
if (!(await deps.fs.pathExists(indexPath))) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const index = await loadBootstrapIndex(repoRoot);
|
|
726
|
+
const runs = index.runs.filter((run) => run.status === "completed");
|
|
727
|
+
if (runs.length === index.runs.length) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
index.runs = runs;
|
|
731
|
+
index.updatedAt = now();
|
|
732
|
+
await saveBootstrapIndex(repoRoot, index);
|
|
733
|
+
}
|
|
677
734
|
async function finalizeCompletedFileJob(repoRoot, job) {
|
|
678
735
|
const finalResultPath = completedFileResultPath(job.sourcePath, job.targetLanguage, job.translationProfile);
|
|
679
736
|
const absoluteCurrentResultPath = resolveRepoPath(repoRoot, job.resultPath);
|
|
@@ -750,6 +807,11 @@ export function createCodexTranslationService(deps) {
|
|
|
750
807
|
await saveQueue(repoRoot, queue);
|
|
751
808
|
}
|
|
752
809
|
return {
|
|
810
|
+
async cleanupStartupRuntime(repoRoot) {
|
|
811
|
+
await removeRepoPath(repoRoot, TRANSLATIONS_RUNTIME_DIR, true);
|
|
812
|
+
await cleanupStartupFileIndex(repoRoot);
|
|
813
|
+
await cleanupStartupBootstrapIndex(repoRoot);
|
|
814
|
+
},
|
|
753
815
|
async getState(repoRoot) {
|
|
754
816
|
await ensureLayout(repoRoot);
|
|
755
817
|
await cleanupCompletedRuntime(repoRoot);
|
|
@@ -759,9 +821,13 @@ export function createCodexTranslationService(deps) {
|
|
|
759
821
|
loadBootstrapIndex(repoRoot),
|
|
760
822
|
isMemoryInitialized(repoRoot, deps.fs)
|
|
761
823
|
]);
|
|
824
|
+
const runtimeFileJobs = await loadRuntimeFileJobs(repoRoot, queue);
|
|
762
825
|
return {
|
|
763
826
|
queue,
|
|
764
|
-
fileIndex
|
|
827
|
+
fileIndex: visibleFileTranslationIndex({
|
|
828
|
+
...fileIndex,
|
|
829
|
+
jobs: [...runtimeFileJobs, ...fileIndex.jobs]
|
|
830
|
+
}),
|
|
765
831
|
bootstrapIndex,
|
|
766
832
|
memoryInitialized
|
|
767
833
|
};
|
|
@@ -822,7 +888,13 @@ export function createCodexTranslationService(deps) {
|
|
|
822
888
|
const targetLanguage = input.targetLanguage.trim() || "zh-CN";
|
|
823
889
|
const profile = input.translationProfile?.trim() || DEFAULT_PROFILE;
|
|
824
890
|
const dedupeKey = sha256(`${sourceHash}|${targetLanguage}|${profile}`);
|
|
825
|
-
const
|
|
891
|
+
const replacementKey = fileTranslationReplacementKeyFromParts(sourcePath, targetLanguage, profile);
|
|
892
|
+
const activeExisting = (await loadRuntimeFileJobs(repoRoot)).find((job) => fileTranslationReplacementKey(job) === replacementKey &&
|
|
893
|
+
isActiveFileTranslationJobStatus(job.status));
|
|
894
|
+
if (activeExisting) {
|
|
895
|
+
void dispatchNext(repoRoot);
|
|
896
|
+
return activeExisting;
|
|
897
|
+
}
|
|
826
898
|
const timestamp = now();
|
|
827
899
|
const jobId = `file-${safeId(path.basename(sourcePath, path.extname(sourcePath)))}-${Date.now()}-${createId().slice(0, 8)}`;
|
|
828
900
|
const jobRoot = `${FILE_RUNTIME_JOBS_DIR}/${jobId}`;
|
|
@@ -919,12 +991,7 @@ export function createCodexTranslationService(deps) {
|
|
|
919
991
|
lastUpdatedAt: timestamp
|
|
920
992
|
});
|
|
921
993
|
await deps.fs.writeText(resolveRepoPath(repoRoot, job.reportPath), `# Translation Report\n\nStatus: queued\nJob: ${jobId}\n`);
|
|
922
|
-
|
|
923
|
-
index.updatedAt = timestamp;
|
|
924
|
-
await saveFileIndex(repoRoot, index);
|
|
925
|
-
if (input.taskSlug) {
|
|
926
|
-
void dispatchNext(repoRoot, input.taskSlug);
|
|
927
|
-
}
|
|
994
|
+
void dispatchNext(repoRoot);
|
|
928
995
|
return job;
|
|
929
996
|
},
|
|
930
997
|
async createBootstrapRun(repoRoot, input) {
|
|
@@ -981,9 +1048,7 @@ export function createCodexTranslationService(deps) {
|
|
|
981
1048
|
index.runs = [run, ...index.runs];
|
|
982
1049
|
index.updatedAt = timestamp;
|
|
983
1050
|
await saveBootstrapIndex(repoRoot, index);
|
|
984
|
-
|
|
985
|
-
void dispatchNext(repoRoot, input.taskSlug);
|
|
986
|
-
}
|
|
1051
|
+
void dispatchNext(repoRoot);
|
|
987
1052
|
return run;
|
|
988
1053
|
},
|
|
989
1054
|
async createMemoryUpdate(repoRoot, input) {
|
|
@@ -1039,15 +1104,14 @@ export function createCodexTranslationService(deps) {
|
|
|
1039
1104
|
"Delete or merge stale, duplicate, temporary, and task-local content."
|
|
1040
1105
|
]
|
|
1041
1106
|
});
|
|
1042
|
-
|
|
1043
|
-
void dispatchNext(repoRoot, input.taskSlug);
|
|
1044
|
-
}
|
|
1107
|
+
void dispatchNext(repoRoot);
|
|
1045
1108
|
return queueItem;
|
|
1046
1109
|
},
|
|
1047
1110
|
async readFileJobOutput(repoRoot, jobId) {
|
|
1048
1111
|
await ensureLayout(repoRoot);
|
|
1049
1112
|
const index = await loadFileIndex(repoRoot);
|
|
1050
|
-
const job = index.jobs.find((candidate) => candidate.id === jobId)
|
|
1113
|
+
const job = index.jobs.find((candidate) => candidate.id === jobId)
|
|
1114
|
+
?? await findRuntimeFileJobById(repoRoot, jobId);
|
|
1051
1115
|
if (!job) {
|
|
1052
1116
|
throw new VcmError({
|
|
1053
1117
|
code: "TRANSLATION_JOB_MISSING",
|
|
@@ -1074,14 +1138,12 @@ export function createCodexTranslationService(deps) {
|
|
|
1074
1138
|
});
|
|
1075
1139
|
}
|
|
1076
1140
|
const timestamp = now();
|
|
1077
|
-
const jobId = `conversation-${
|
|
1078
|
-
const jobRoot = `${CONVERSATION_RUNTIME_DIR}
|
|
1141
|
+
const jobId = `conversation-${Date.now()}-${createId().slice(0, 8)}`;
|
|
1142
|
+
const jobRoot = `${CONVERSATION_RUNTIME_DIR}/jobs/${jobId}`;
|
|
1079
1143
|
const sourceHash = `sha256:${sha256(sourceText)}`;
|
|
1080
1144
|
const targetLanguage = input.targetLanguage.trim() || "zh-CN";
|
|
1081
1145
|
const job = {
|
|
1082
1146
|
id: jobId,
|
|
1083
|
-
taskSlug: input.taskSlug,
|
|
1084
|
-
role: input.role,
|
|
1085
1147
|
direction: input.direction,
|
|
1086
1148
|
sourceHash,
|
|
1087
1149
|
sourceLanguage: input.sourceLanguage.trim() || "auto",
|
|
@@ -1105,15 +1167,10 @@ export function createCodexTranslationService(deps) {
|
|
|
1105
1167
|
version: 1,
|
|
1106
1168
|
baseRepoRoot: repoRoot,
|
|
1107
1169
|
pathBase: "baseRepoRoot",
|
|
1108
|
-
job,
|
|
1109
|
-
taskSlug: input.taskSlug,
|
|
1110
|
-
role: input.role,
|
|
1111
1170
|
direction: input.direction,
|
|
1112
|
-
sourceHash,
|
|
1113
1171
|
sourceLanguage: job.sourceLanguage,
|
|
1114
1172
|
targetLanguage,
|
|
1115
1173
|
translationProfile: input.translationProfile?.trim() || DEFAULT_PROFILE,
|
|
1116
|
-
contextText: input.contextText,
|
|
1117
1174
|
sourceContentBoundary: "VCM_TEXT",
|
|
1118
1175
|
sourceText,
|
|
1119
1176
|
absolutePaths: {
|
|
@@ -1126,8 +1183,8 @@ export function createCodexTranslationService(deps) {
|
|
|
1126
1183
|
format: "plain-text"
|
|
1127
1184
|
}
|
|
1128
1185
|
});
|
|
1129
|
-
if (
|
|
1130
|
-
void dispatchNext(repoRoot
|
|
1186
|
+
if (!input.deferDispatch) {
|
|
1187
|
+
void dispatchNext(repoRoot);
|
|
1131
1188
|
}
|
|
1132
1189
|
return job;
|
|
1133
1190
|
},
|
|
@@ -1208,6 +1265,7 @@ export function createCodexTranslationService(deps) {
|
|
|
1208
1265
|
return job;
|
|
1209
1266
|
},
|
|
1210
1267
|
async handleCodexHook(repoRoot, eventName, taskSlug) {
|
|
1268
|
+
void taskSlug;
|
|
1211
1269
|
if (eventName === "UserPromptSubmit") {
|
|
1212
1270
|
const queue = await loadQueue(repoRoot);
|
|
1213
1271
|
const active = queue.activeItemId
|
|
@@ -1224,10 +1282,11 @@ export function createCodexTranslationService(deps) {
|
|
|
1224
1282
|
}
|
|
1225
1283
|
if (eventName === "Stop") {
|
|
1226
1284
|
await validateActiveQueueItem(repoRoot);
|
|
1227
|
-
|
|
1228
|
-
await dispatchNext(repoRoot, taskSlug);
|
|
1229
|
-
}
|
|
1285
|
+
await dispatchNext(repoRoot);
|
|
1230
1286
|
}
|
|
1287
|
+
},
|
|
1288
|
+
ensureTranslatorSession(repoRoot) {
|
|
1289
|
+
return ensureTranslatorSession(repoRoot, "zh-CN");
|
|
1231
1290
|
}
|
|
1232
1291
|
};
|
|
1233
1292
|
}
|
|
@@ -1607,12 +1666,60 @@ function completedFileResultPath(sourcePath, targetLanguage, translationProfile)
|
|
|
1607
1666
|
return `${FILE_COMPLETED_DIR}/${sourceBaseName}-${languagePart}-${profilePart}-${sourcePathHash}.md`;
|
|
1608
1667
|
}
|
|
1609
1668
|
function fileTranslationReplacementKey(job) {
|
|
1669
|
+
return fileTranslationReplacementKeyFromParts(job.sourcePath, job.targetLanguage, job.translationProfile);
|
|
1670
|
+
}
|
|
1671
|
+
function fileTranslationReplacementKeyFromParts(sourcePath, targetLanguage, translationProfile) {
|
|
1610
1672
|
return [
|
|
1611
|
-
normalizeRepoRelative(
|
|
1612
|
-
|
|
1613
|
-
(
|
|
1673
|
+
normalizeRepoRelative(sourcePath),
|
|
1674
|
+
targetLanguage.trim(),
|
|
1675
|
+
(translationProfile || DEFAULT_PROFILE).trim()
|
|
1614
1676
|
].join("\0");
|
|
1615
1677
|
}
|
|
1678
|
+
function isActiveFileTranslationJobStatus(status) {
|
|
1679
|
+
return status === "queued" || status === "running" || status === "validating";
|
|
1680
|
+
}
|
|
1681
|
+
function isFileTranslationQueueItem(item) {
|
|
1682
|
+
return item.type === "file" || item.type === "force-retranslate";
|
|
1683
|
+
}
|
|
1684
|
+
function toFileJobStatus(status) {
|
|
1685
|
+
if (status === "dispatching") {
|
|
1686
|
+
return "running";
|
|
1687
|
+
}
|
|
1688
|
+
return status === "completed" ||
|
|
1689
|
+
status === "queued" ||
|
|
1690
|
+
status === "running" ||
|
|
1691
|
+
status === "validating" ||
|
|
1692
|
+
status === "needs_review" ||
|
|
1693
|
+
status === "failed" ||
|
|
1694
|
+
status === "interrupted" ||
|
|
1695
|
+
status === "skipped" ||
|
|
1696
|
+
status === "cancelled"
|
|
1697
|
+
? status
|
|
1698
|
+
: "failed";
|
|
1699
|
+
}
|
|
1700
|
+
function compareFileJobUpdatedAtDesc(left, right) {
|
|
1701
|
+
return (right.updatedAt ?? "").localeCompare(left.updatedAt ?? "");
|
|
1702
|
+
}
|
|
1703
|
+
function isCompletedFileResultPath(relativePath) {
|
|
1704
|
+
const normalized = normalizeRepoRelative(relativePath);
|
|
1705
|
+
return normalized.startsWith(`${FILE_COMPLETED_DIR}/`);
|
|
1706
|
+
}
|
|
1707
|
+
function visibleFileTranslationIndex(index) {
|
|
1708
|
+
const seenKeys = new Set();
|
|
1709
|
+
const jobs = [];
|
|
1710
|
+
for (const job of index.jobs) {
|
|
1711
|
+
const key = fileTranslationReplacementKey(job);
|
|
1712
|
+
if (seenKeys.has(key)) {
|
|
1713
|
+
continue;
|
|
1714
|
+
}
|
|
1715
|
+
seenKeys.add(key);
|
|
1716
|
+
jobs.push(job);
|
|
1717
|
+
}
|
|
1718
|
+
return {
|
|
1719
|
+
...index,
|
|
1720
|
+
jobs
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1616
1723
|
function retainedCompletedFileJobIds(index) {
|
|
1617
1724
|
const seenKeys = new Set();
|
|
1618
1725
|
const retainedIds = new Set();
|
|
@@ -1637,10 +1744,7 @@ function isPrunableCompletedQueueItem(item) {
|
|
|
1637
1744
|
}
|
|
1638
1745
|
function isTranslationRuntimeDirectory(relativePath) {
|
|
1639
1746
|
const normalized = normalizeRepoRelative(relativePath);
|
|
1640
|
-
return normalized.startsWith(`${TRANSLATIONS_RUNTIME_DIR}/`)
|
|
1641
|
-
normalized.startsWith(`${TRANSLATIONS_ROOT}/files/jobs/`) ||
|
|
1642
|
-
normalized.startsWith(`${TRANSLATIONS_ROOT}/bootstrap/runs/`) ||
|
|
1643
|
-
normalized.startsWith(`${TRANSLATIONS_ROOT}/conversations/`);
|
|
1747
|
+
return normalized.startsWith(`${TRANSLATIONS_RUNTIME_DIR}/`);
|
|
1644
1748
|
}
|
|
1645
1749
|
async function readStandaloneConversationResult(repoRoot, resultRelativePath, fs) {
|
|
1646
1750
|
const resultPath = resolveRepoPath(repoRoot, resultRelativePath);
|