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) => {