vibe-coding-master 0.4.39 → 0.4.41

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.
@@ -14,8 +14,8 @@ export function registerTranslationRoutes(app, deps) {
14
14
  });
15
15
  });
16
16
  app.get("/api/translation/sessions/:sessionId/events", async (request) => {
17
- await requireCurrentProject(deps.projectService);
18
- return deps.translationService.pollSessionEvents(request.params.sessionId, Number(request.query.after ?? "1"), request.query.limit === undefined ? undefined : Number(request.query.limit));
17
+ const project = await requireCurrentProject(deps.projectService);
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
20
  app.post("/api/tasks/:taskSlug/sessions/:role/translation/input", async (request) => {
21
21
  const project = await requireCurrentProject(deps.projectService);
@@ -124,6 +124,7 @@ export function createTranslationService(deps) {
124
124
  for (const event of state.events) {
125
125
  if (event.type === "entry") {
126
126
  state.entries = upsertEntry(state.entries, event.entry);
127
+ state.seenTranscriptIds.add(event.entry.id);
127
128
  }
128
129
  else if (event.type === "status") {
129
130
  state.status = event.status;
@@ -159,10 +160,20 @@ export function createTranslationService(deps) {
159
160
  const state = getState(roleSession.id);
160
161
  state.taskSlug = roleSession.taskSlug;
161
162
  state.role = roleSession.role;
162
- if (state.unsubscribeTranscript) {
163
+ const transcriptSessionKey = getTranscriptSessionKey(roleSession);
164
+ if (!transcriptSessionKey) {
163
165
  return;
164
166
  }
167
+ if (state.unsubscribeTranscript) {
168
+ if (state.transcriptSessionKey === transcriptSessionKey) {
169
+ return;
170
+ }
171
+ state.unsubscribeTranscript();
172
+ state.unsubscribeTranscript = undefined;
173
+ state.transcriptSessionKey = undefined;
174
+ }
165
175
  const replaySince = getTranscriptReplaySince(roleSession);
176
+ state.transcriptSessionKey = transcriptSessionKey;
166
177
  state.unsubscribeTranscript = deps.transcripts.subscribeToRoleSession(roleSession, (event) => {
167
178
  void handleTranscriptEvent(roleSession.id, event).catch((error) => {
168
179
  publishError(roleSession.id, normalizeTranslationError(error, "Process Claude transcript event for translation failed."));
@@ -537,6 +548,7 @@ export function createTranslationService(deps) {
537
548
  state.unsubscribeTranscript();
538
549
  state.unsubscribeTranscript = undefined;
539
550
  }
551
+ state.transcriptSessionKey = undefined;
540
552
  if (state.outputBatch?.timer) {
541
553
  clearTimeout(state.outputBatch.timer);
542
554
  }
@@ -551,6 +563,24 @@ export function createTranslationService(deps) {
551
563
  await state.persistChain?.catch(() => undefined);
552
564
  sessionStates.delete(sessionId);
553
565
  }
566
+ async function ensureSessionForPoll(sessionId, context) {
567
+ const roleSession = deps.sessionRegistry.get(sessionId);
568
+ const existing = sessionStates.get(sessionId);
569
+ if (!roleSession || roleSession.status !== "running") {
570
+ return existing;
571
+ }
572
+ const state = context
573
+ ? await prepareCache({
574
+ repoRoot: roleSession.cwd,
575
+ baseRepoRoot: context.repoRoot,
576
+ taskSlug: roleSession.taskSlug,
577
+ role: roleSession.role,
578
+ sessionId: roleSession.id
579
+ })
580
+ : getState(sessionId);
581
+ startTranscriptTail(roleSession);
582
+ return state;
583
+ }
554
584
  return {
555
585
  async startSession(input) {
556
586
  const roleSession = await deps.sessionService.getRoleSession(input.repoRoot, input.taskSlug, input.role);
@@ -575,9 +605,9 @@ export function createTranslationService(deps) {
575
605
  nextCursor: 1
576
606
  };
577
607
  },
578
- async pollSessionEvents(sessionId, after, limit = 200) {
608
+ async pollSessionEvents(sessionId, after, limit = 200, context) {
579
609
  const cursor = Number.isFinite(after) ? Math.max(1, Math.floor(after)) : 1;
580
- const state = sessionStates.get(sessionId);
610
+ const state = await ensureSessionForPoll(sessionId, context);
581
611
  if (!state) {
582
612
  return {
583
613
  sessionId,
@@ -1010,13 +1040,26 @@ function delay(ms) {
1010
1040
  return new Promise((resolve) => setTimeout(resolve, ms));
1011
1041
  }
1012
1042
  function getTranscriptReplaySince(roleSession) {
1013
- const rawTimestamp = roleSession.startedAt ?? roleSession.updatedAt;
1043
+ const rawTimestamp = roleSession.lastTurnStartedAt
1044
+ ?? roleSession.lastHookEventAt
1045
+ ?? roleSession.updatedAt
1046
+ ?? roleSession.startedAt;
1014
1047
  const timestampMs = Date.parse(rawTimestamp);
1015
1048
  if (!Number.isFinite(timestampMs)) {
1016
1049
  return undefined;
1017
1050
  }
1018
1051
  return new Date(Math.max(0, timestampMs - TRANSCRIPT_REPLAY_GRACE_MS)).toISOString();
1019
1052
  }
1053
+ function getTranscriptSessionKey(roleSession) {
1054
+ if (!roleSession.claudeSessionId && !roleSession.transcriptPath) {
1055
+ return undefined;
1056
+ }
1057
+ return [
1058
+ roleSession.cwd,
1059
+ roleSession.claudeSessionId,
1060
+ roleSession.transcriptPath
1061
+ ].join("\n");
1062
+ }
1020
1063
  function formatStructuredTranscriptEvent(event) {
1021
1064
  if (event.kind === "question") {
1022
1065
  return event.question.questions.map((question, index) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.4.39",
3
+ "version": "0.4.41",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [