vibe-coding-master 0.4.41 → 0.4.42
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.
|
@@ -17,6 +17,17 @@ export function registerTranslationRoutes(app, deps) {
|
|
|
17
17
|
const project = await requireCurrentProject(deps.projectService);
|
|
18
18
|
return deps.translationService.pollSessionEvents(request.params.sessionId, Number(request.query.after ?? "1"), request.query.limit === undefined ? undefined : Number(request.query.limit), { repoRoot: project.repoRoot });
|
|
19
19
|
});
|
|
20
|
+
app.get("/api/tasks/:taskSlug/translation/feed", async (request) => {
|
|
21
|
+
const project = await requireCurrentProject(deps.projectService);
|
|
22
|
+
const task = await deps.taskService.loadTask(project.repoRoot, request.params.taskSlug);
|
|
23
|
+
return deps.translationService.pollTaskFeed({
|
|
24
|
+
repoRoot: project.repoRoot,
|
|
25
|
+
taskRepoRoot: getTaskRuntimeRepoRoot(task),
|
|
26
|
+
taskSlug: request.params.taskSlug,
|
|
27
|
+
after: Number(request.query.after ?? "1"),
|
|
28
|
+
limit: request.query.limit === undefined ? undefined : Number(request.query.limit)
|
|
29
|
+
});
|
|
30
|
+
});
|
|
20
31
|
app.post("/api/tasks/:taskSlug/sessions/:role/translation/input", async (request) => {
|
|
21
32
|
const project = await requireCurrentProject(deps.projectService);
|
|
22
33
|
const role = parseRole(request.params.role);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { isVcmRoleName } from "../../shared/constants.js";
|
|
2
3
|
import { TRANSLATION_ENTRY_RETENTION_LIMIT } from "../../shared/types/translation.js";
|
|
3
4
|
import { VcmError } from "../errors.js";
|
|
4
5
|
import { submitTerminalInput } from "../runtime/terminal-submit.js";
|
|
@@ -11,12 +12,14 @@ const TRANSLATION_PROVIDER = "claude-code";
|
|
|
11
12
|
const TRANSLATION_MODEL = "translator";
|
|
12
13
|
const OUTPUT_TRANSLATION_BATCH_DELAY_MS = 10000;
|
|
13
14
|
const TRANSCRIPT_REPLAY_GRACE_MS = 5000;
|
|
15
|
+
const TRANSLATION_TASK_FEED_RETENTION_LIMIT = 2000;
|
|
14
16
|
export function createTranslationService(deps) {
|
|
15
17
|
const now = deps.now ?? (() => new Date().toISOString());
|
|
16
18
|
const id = deps.id ?? (() => `tr_${Date.now()}_${Math.random().toString(16).slice(2)}`);
|
|
17
19
|
const outputBatchDelayMs = Math.max(0, deps.outputBatchDelayMs ?? OUTPUT_TRANSLATION_BATCH_DELAY_MS);
|
|
18
20
|
const queues = createTranslationQueueRegistry();
|
|
19
21
|
const sessionStates = new Map();
|
|
22
|
+
const taskFeeds = new Map();
|
|
20
23
|
async function loadConfig() {
|
|
21
24
|
const preferences = await deps.appSettings.getPreferences();
|
|
22
25
|
return {
|
|
@@ -79,9 +82,49 @@ export function createTranslationService(deps) {
|
|
|
79
82
|
createdAt: now()
|
|
80
83
|
};
|
|
81
84
|
state.events.push(event);
|
|
85
|
+
appendTaskFeedEvent(sessionId, state, event);
|
|
82
86
|
void persistEvents(state);
|
|
83
87
|
return event;
|
|
84
88
|
}
|
|
89
|
+
function appendTaskFeedEvent(sessionId, state, event) {
|
|
90
|
+
if (!state.repoRoot || !state.taskSlug || !state.role) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const feed = getTaskFeed(state.repoRoot, state.taskSlug);
|
|
94
|
+
const sessionEventKey = getTaskFeedSessionEventKey(sessionId, event);
|
|
95
|
+
if (feed.seenSessionEvents.has(sessionEventKey)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
feed.seenSessionEvents.add(sessionEventKey);
|
|
99
|
+
feed.events.push({
|
|
100
|
+
seq: feed.nextSeq++,
|
|
101
|
+
sessionId,
|
|
102
|
+
role: state.role,
|
|
103
|
+
event
|
|
104
|
+
});
|
|
105
|
+
if (feed.events.length > TRANSLATION_TASK_FEED_RETENTION_LIMIT) {
|
|
106
|
+
feed.events = feed.events.slice(-TRANSLATION_TASK_FEED_RETENTION_LIMIT);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function syncTaskFeedFromSessionState(sessionId, state) {
|
|
110
|
+
for (const event of state.events) {
|
|
111
|
+
appendTaskFeedEvent(sessionId, state, event);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function getTaskFeed(repoRoot, taskSlug) {
|
|
115
|
+
const key = getTaskFeedKey(repoRoot, taskSlug);
|
|
116
|
+
const current = taskFeeds.get(key);
|
|
117
|
+
if (current) {
|
|
118
|
+
return current;
|
|
119
|
+
}
|
|
120
|
+
const created = {
|
|
121
|
+
events: [],
|
|
122
|
+
nextSeq: 1,
|
|
123
|
+
seenSessionEvents: new Set()
|
|
124
|
+
};
|
|
125
|
+
taskFeeds.set(key, created);
|
|
126
|
+
return created;
|
|
127
|
+
}
|
|
85
128
|
async function prepareCache(input) {
|
|
86
129
|
const state = getState(input.sessionId);
|
|
87
130
|
state.repoRoot = input.repoRoot;
|
|
@@ -99,6 +142,7 @@ export function createTranslationService(deps) {
|
|
|
99
142
|
state.cacheLoaded = true;
|
|
100
143
|
pruneTranslationEntries(input.sessionId);
|
|
101
144
|
}
|
|
145
|
+
syncTaskFeedFromSessionState(input.sessionId, state);
|
|
102
146
|
await deps.fs.ensureDir(path.dirname(cachePath));
|
|
103
147
|
return state;
|
|
104
148
|
}
|
|
@@ -629,6 +673,43 @@ export function createTranslationService(deps) {
|
|
|
629
673
|
events
|
|
630
674
|
};
|
|
631
675
|
},
|
|
676
|
+
async pollTaskFeed(input) {
|
|
677
|
+
const cursor = Number.isFinite(input.after) ? Math.max(1, Math.floor(input.after)) : 1;
|
|
678
|
+
const maxEvents = Math.min(Math.max(1, Math.floor(input.limit ?? 500)), 1000);
|
|
679
|
+
const roleSessions = await deps.sessionService.listRoleSessions(input.repoRoot, input.taskSlug);
|
|
680
|
+
const feedSessions = [];
|
|
681
|
+
for (const roleSession of roleSessions) {
|
|
682
|
+
if (!isVcmRoleName(roleSession.role)) {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
const state = await prepareCache({
|
|
686
|
+
repoRoot: input.taskRepoRoot,
|
|
687
|
+
baseRepoRoot: input.repoRoot,
|
|
688
|
+
taskSlug: input.taskSlug,
|
|
689
|
+
role: roleSession.role,
|
|
690
|
+
sessionId: roleSession.id
|
|
691
|
+
});
|
|
692
|
+
if (roleSession.status === "running") {
|
|
693
|
+
startTranscriptTail(roleSession);
|
|
694
|
+
}
|
|
695
|
+
feedSessions.push({
|
|
696
|
+
sessionId: roleSession.id,
|
|
697
|
+
role: roleSession.role,
|
|
698
|
+
status: state.status
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
const feed = getTaskFeed(input.taskRepoRoot, input.taskSlug);
|
|
702
|
+
const events = feed.events
|
|
703
|
+
.filter((event) => event.seq >= cursor)
|
|
704
|
+
.slice(0, maxEvents);
|
|
705
|
+
const nextCursor = events.length > 0 ? (events.at(-1)?.seq ?? cursor) + 1 : cursor;
|
|
706
|
+
return {
|
|
707
|
+
taskSlug: input.taskSlug,
|
|
708
|
+
nextCursor,
|
|
709
|
+
sessions: feedSessions,
|
|
710
|
+
events
|
|
711
|
+
};
|
|
712
|
+
},
|
|
632
713
|
async recordConversationBoundary(input) {
|
|
633
714
|
const config = await loadConfig();
|
|
634
715
|
const state = await prepareCache({
|
|
@@ -1060,6 +1141,12 @@ function getTranscriptSessionKey(roleSession) {
|
|
|
1060
1141
|
roleSession.transcriptPath
|
|
1061
1142
|
].join("\n");
|
|
1062
1143
|
}
|
|
1144
|
+
function getTaskFeedKey(repoRoot, taskSlug) {
|
|
1145
|
+
return `${repoRoot}\n${taskSlug}`;
|
|
1146
|
+
}
|
|
1147
|
+
function getTaskFeedSessionEventKey(sessionId, event) {
|
|
1148
|
+
return `${sessionId}:${event.seq}`;
|
|
1149
|
+
}
|
|
1063
1150
|
function formatStructuredTranscriptEvent(event) {
|
|
1064
1151
|
if (event.kind === "question") {
|
|
1065
1152
|
return event.question.questions.map((question, index) => {
|