@vibedeckx/linux-x64 0.2.5 → 0.2.7

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 (2) hide show
  1. package/dist/bin.js +299 -20
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -186146,7 +186146,11 @@ var asRun = (row) => row;
186146
186146
  var createWorkflowRunRepos = (kdb) => ({
186147
186147
  workflowRuns: {
186148
186148
  create: async (opts) => {
186149
- await kdb.insertInto("workflow_runs").values({ ...opts, status: "waiting_reviewer" }).execute();
186149
+ await kdb.insertInto("workflow_runs").values({
186150
+ ...opts,
186151
+ reviewer_session_id: opts.reviewer_session_id ?? null,
186152
+ status: "waiting_reviewer"
186153
+ }).execute();
186150
186154
  const row = await kdb.selectFrom("workflow_runs").selectAll().where("id", "=", opts.id).executeTakeFirstOrThrow();
186151
186155
  return asRun(row);
186152
186156
  },
@@ -186169,6 +186173,10 @@ var createWorkflowRunRepos = (kdb) => ({
186169
186173
  ])).executeTakeFirst();
186170
186174
  return row ? asRun(row) : void 0;
186171
186175
  },
186176
+ getLatestCompletedBySource: async (sourceSessionId) => {
186177
+ const row = await kdb.selectFrom("workflow_runs").selectAll().where("source_session_id", "=", sourceSessionId).where("status", "=", "completed").where("reviewer_session_id", "is not", null).orderBy("created_at", "desc").orderBy(sql`rowid`, "desc").executeTakeFirst();
186178
+ return row ? asRun(row) : void 0;
186179
+ },
186172
186180
  update: async (id, patch) => {
186173
186181
  if (Object.keys(patch).length > 0) {
186174
186182
  await kdb.updateTable("workflow_runs").set({ ...patch, updated_at: sql`datetime('now')` }).where("id", "=", id).execute();
@@ -224161,6 +224169,20 @@ var AgentSessionManager = class {
224161
224169
  this.titleResolved.add(sessionId);
224162
224170
  return true;
224163
224171
  }
224172
+ /**
224173
+ * Write a caller-determined final title and claim the one-shot slot so the
224174
+ * AI title generator never fires for this session — same pattern as the
224175
+ * Branch path's "Branch - <source title>". The slot is claimed before the
224176
+ * DB write: even if the write fails, a degraded placeholder title beats an
224177
+ * AI title racing in over the caller's intent.
224178
+ */
224179
+ async setFinalSessionTitle(sessionId, title) {
224180
+ this.markTitleResolved(sessionId);
224181
+ await this.storage.agentSessions.updateTitle(sessionId, title);
224182
+ this.broadcastRaw(sessionId, { titleUpdated: { title } });
224183
+ const session = this.sessions.get(sessionId);
224184
+ if (session) this.emitSessionTitle(session.projectId, session.branch, sessionId, title);
224185
+ }
224164
224186
  isProcessAlive(session) {
224165
224187
  return !!session.process && session.process.exitCode === null && !session.dormant;
224166
224188
  }
@@ -226013,6 +226035,13 @@ function mapRemoteRun(run2, remoteServerId, projectId) {
226013
226035
  reviewer_session_id: run2.reviewer_session_id ? `${prefix}${run2.reviewer_session_id}` : null
226014
226036
  };
226015
226037
  }
226038
+ function mapRemoteReviewerCandidate(candidate, remoteServerId, projectId) {
226039
+ if (!candidate?.sessionId) return candidate;
226040
+ return {
226041
+ ...candidate,
226042
+ sessionId: `remote-${remoteServerId}-${projectId}-${candidate.sessionId}`
226043
+ };
226044
+ }
226016
226045
  function runUpdatedEventFromRemoteFrame(parsed, sessionId, remoteInfo) {
226017
226046
  if (!("workflowRunUpdated" in parsed)) return null;
226018
226047
  const bare = parsed.workflowRunUpdated;
@@ -228699,9 +228728,10 @@ function extractLatestTurnEndIndex(entries) {
228699
228728
  }
228700
228729
  return null;
228701
228730
  }
228702
- function extractLastAssistantBefore(entries, beforeIndex) {
228731
+ function extractLastAssistantInTurn(entries, beforeIndex) {
228703
228732
  for (let i = beforeIndex - 1; i >= 0; i--) {
228704
228733
  const e = entries[i];
228734
+ if (e?.type === "user") return null;
228705
228735
  if (e?.type === "assistant" && typeof e.content === "string" && e.content.trim()) return e.content;
228706
228736
  }
228707
228737
  return null;
@@ -228732,6 +228762,27 @@ ${opts.reviewFocus}` : null,
228732
228762
  "\nEnd your final message with a clear, actionable list of feedback items \u2014 or state explicitly that the work looks good."
228733
228763
  ].filter((l) => l !== null).join("\n");
228734
228764
  }
228765
+ function reviewTargetPromptLine(target) {
228766
+ return target.baseHead ? `- The work was captured at commit ${target.baseHead}${target.diffStat ? ` with uncommitted changes (${target.diffStat})` : " with no uncommitted changes"}.` : null;
228767
+ }
228768
+ function buildRereviewerPrompt(opts) {
228769
+ return [
228770
+ "The source agent has addressed feedback from your previous review.",
228771
+ "Review the latest workspace state again.",
228772
+ opts.taskContext ? `
228773
+ ## Latest source turn
228774
+ ${opts.taskContext}` : null,
228775
+ opts.reviewFocus ? `
228776
+ ## Review focus
228777
+ ${opts.reviewFocus}` : null,
228778
+ "\n## How to review",
228779
+ "- Verify whether your previous feedback was addressed correctly.",
228780
+ "- Check for regressions and remaining correctness or test gaps.",
228781
+ "- Do NOT modify files \u2014 remain in read-only review mode.",
228782
+ reviewTargetPromptLine(opts.target),
228783
+ "- End with actionable feedback, or explicitly state that it looks good."
228784
+ ].filter((line) => line !== null).join("\n");
228785
+ }
228735
228786
  function buildFeedbackMessage(feedback) {
228736
228787
  return [
228737
228788
  "[Review Feedback]",
@@ -228740,6 +228791,7 @@ function buildFeedbackMessage(feedback) {
228740
228791
  feedback
228741
228792
  ].join("\n");
228742
228793
  }
228794
+ var REVIEWER_AGENT_TYPES = /* @__PURE__ */ new Set(["claude-code", "codex"]);
228743
228795
  var WorkflowEngine = class {
228744
228796
  constructor(storage, agentOps) {
228745
228797
  this.storage = storage;
@@ -228789,6 +228841,15 @@ var WorkflowEngine = class {
228789
228841
  if (p2.runId === run2.id) this.participants.delete(sid);
228790
228842
  }
228791
228843
  }
228844
+ releaseReservations(runId) {
228845
+ for (const [sid, participant] of this.participants) {
228846
+ if (participant.runId === runId) this.participants.delete(sid);
228847
+ }
228848
+ }
228849
+ async failRun(run2, error48) {
228850
+ const failed = await this.storage.workflowRuns.update(run2.id, { status: "failed", error: error48 });
228851
+ if (failed) this.untrackRun(failed);
228852
+ }
228792
228853
  /** Sync check used by ChatSessionManager before waking the commander model. */
228793
228854
  shouldSuppressAgentEvent(sessionId) {
228794
228855
  return this.participants.get(sessionId)?.role === "reviewer";
@@ -228796,14 +228857,62 @@ var WorkflowEngine = class {
228796
228857
  isSessionInActiveRun(sessionId) {
228797
228858
  return this.participants.has(sessionId);
228798
228859
  }
228860
+ async getReviewerCandidate(sourceSessionId) {
228861
+ const previous = await this.storage.workflowRuns.getLatestCompletedBySource(sourceSessionId);
228862
+ if (!previous?.reviewer_session_id) return null;
228863
+ const unavailable = (reason) => ({
228864
+ available: false,
228865
+ sessionId: null,
228866
+ title: null,
228867
+ agentType: null,
228868
+ reason
228869
+ });
228870
+ const source = await this.storage.agentSessions.getById(sourceSessionId);
228871
+ const reviewer = await this.storage.agentSessions.getById(previous.reviewer_session_id);
228872
+ if (!reviewer) return unavailable("deleted");
228873
+ if (!source || reviewer.project_id !== source.project_id || reviewer.project_id !== previous.project_id) {
228874
+ return unavailable("project-mismatch");
228875
+ }
228876
+ if ((reviewer.branch || null) !== (source.branch || null) || (reviewer.branch || null) !== previous.branch) {
228877
+ return unavailable("branch-mismatch");
228878
+ }
228879
+ if (!REVIEWER_AGENT_TYPES.has(reviewer.agent_type)) {
228880
+ return unavailable("unsupported-agent");
228881
+ }
228882
+ if (reviewer.status === "running") return unavailable("running");
228883
+ if (reviewer.status !== "stopped") return unavailable("unavailable");
228884
+ if (this.participants.has(reviewer.id) || await this.storage.workflowRuns.getActiveBySession(reviewer.id)) {
228885
+ return unavailable("busy");
228886
+ }
228887
+ return {
228888
+ available: true,
228889
+ sessionId: reviewer.id,
228890
+ title: reviewer.title ?? null,
228891
+ agentType: reviewer.agent_type,
228892
+ reason: null
228893
+ };
228894
+ }
228799
228895
  async startAdhocReview(opts) {
228800
- if (this.participants.has(opts.sourceSessionId)) {
228801
- throw new WorkflowError("session-busy", "\u8BE5 session \u5DF2\u5728\u4E00\u4E2A\u8FDB\u884C\u4E2D\u7684 review \u91CC");
228896
+ if (opts.reviewerSessionId === opts.sourceSessionId) {
228897
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u4E0D\u80FD\u4E0E source session \u76F8\u540C");
228898
+ }
228899
+ const runId = randomUUID5();
228900
+ const participantIds = [opts.sourceSessionId, opts.reviewerSessionId].filter((id) => Boolean(id));
228901
+ for (const sessionId of participantIds) {
228902
+ if (this.participants.has(sessionId)) {
228903
+ throw new WorkflowError("session-busy", "\u8BE5 session \u5DF2\u5728\u4E00\u4E2A\u8FDB\u884C\u4E2D\u7684 review \u91CC");
228904
+ }
228905
+ }
228906
+ this.participants.set(opts.sourceSessionId, { runId, role: "source" });
228907
+ if (opts.reviewerSessionId) {
228908
+ this.participants.set(opts.reviewerSessionId, { runId, role: "reviewer" });
228802
228909
  }
228803
- this.participants.set(opts.sourceSessionId, { runId: "pending", role: "source" });
228804
228910
  try {
228805
- const busy = await this.storage.workflowRuns.getActiveBySession(opts.sourceSessionId);
228806
- if (busy) throw new WorkflowError("session-busy", "\u8BE5 session \u5DF2\u5728\u4E00\u4E2A\u8FDB\u884C\u4E2D\u7684 review \u91CC");
228911
+ for (const sessionId of participantIds) {
228912
+ if (await this.storage.workflowRuns.getActiveBySession(sessionId)) {
228913
+ throw new WorkflowError("session-busy", "\u8BE5 session \u5DF2\u5728\u4E00\u4E2A\u8FDB\u884C\u4E2D\u7684 review \u91CC");
228914
+ }
228915
+ }
228807
228916
  const sourceSession = await this.storage.agentSessions.getById(opts.sourceSessionId);
228808
228917
  if (sourceSession?.status === "running") {
228809
228918
  throw new WorkflowError("source-running", "source session \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D turn \u5B8C\u6210\u540E\u518D\u53D1\u8D77 review");
@@ -228815,16 +228924,61 @@ var WorkflowEngine = class {
228815
228924
  }
228816
228925
  const worktreePath = resolveWorktreePath(opts.project.path, opts.branch);
228817
228926
  const target = captureReviewTarget(worktreePath);
228927
+ let reviewerSession = null;
228928
+ if (opts.reviewerSessionId) {
228929
+ reviewerSession = await this.storage.agentSessions.getById(opts.reviewerSessionId);
228930
+ if (!reviewerSession) {
228931
+ throw new WorkflowError("reviewer-unavailable", "\u4E0A\u6B21 reviewer session \u5DF2\u4E0D\u5B58\u5728");
228932
+ }
228933
+ if (reviewerSession.project_id !== opts.project.id) {
228934
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u4E0D\u5C5E\u4E8E\u5F53\u524D\u9879\u76EE");
228935
+ }
228936
+ if ((reviewerSession.branch || null) !== opts.branch) {
228937
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u4E0D\u5C5E\u4E8E\u5F53\u524D branch");
228938
+ }
228939
+ if (!REVIEWER_AGENT_TYPES.has(reviewerSession.agent_type)) {
228940
+ throw new WorkflowError("reviewer-unavailable", "reviewer agent \u7C7B\u578B\u4E0D\u53EF\u7528");
228941
+ }
228942
+ if (reviewerSession.status !== "stopped") {
228943
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u6B63\u5728\u8FD0\u884C\u6216\u4E0D\u53EF\u7528");
228944
+ }
228945
+ }
228818
228946
  const run2 = await this.storage.workflowRuns.create({
228819
- id: randomUUID5(),
228947
+ id: runId,
228820
228948
  project_id: opts.project.id,
228821
228949
  branch: opts.branch,
228822
228950
  source_session_id: opts.sourceSessionId,
228823
228951
  source_turn_end_index: turnEndIndex,
228824
228952
  review_focus: opts.reviewFocus ?? null,
228825
- review_target: JSON.stringify(target)
228953
+ review_target: JSON.stringify(target),
228954
+ reviewer_session_id: opts.reviewerSessionId ?? null
228826
228955
  });
228827
228956
  this.trackParticipants(run2);
228957
+ if (opts.reviewerSessionId && reviewerSession) {
228958
+ if (reviewerSession.permission_mode !== "plan") {
228959
+ let switched = false;
228960
+ try {
228961
+ switched = await this.agentOps.switchMode(opts.reviewerSessionId, opts.project.path, "plan");
228962
+ } catch {
228963
+ }
228964
+ if (!switched) {
228965
+ await this.failRun(run2, "\u65E0\u6CD5\u5C06 reviewer \u6062\u590D\u4E3A\u53EA\u8BFB plan \u6A21\u5F0F");
228966
+ throw new WorkflowError("reviewer-unavailable", "\u65E0\u6CD5\u5C06 reviewer \u6062\u590D\u4E3A\u53EA\u8BFB plan \u6A21\u5F0F");
228967
+ }
228968
+ }
228969
+ const prompt = buildRereviewerPrompt({
228970
+ taskContext: extractTaskContextBefore(entries, turnEndIndex),
228971
+ reviewFocus: opts.reviewFocus ?? null,
228972
+ target
228973
+ });
228974
+ const sent = await this.agentOps.sendUserMessage(opts.reviewerSessionId, prompt, opts.project.path).catch(() => false);
228975
+ if (!sent) {
228976
+ await this.failRun(run2, "\u5411\u4E0A\u6B21 reviewer \u6295\u9012\u590D\u5BA1\u4EFB\u52A1\u5931\u8D25");
228977
+ throw new WorkflowError("send-failed", "\u5411\u4E0A\u6B21 reviewer \u6295\u9012\u590D\u5BA1\u4EFB\u52A1\u5931\u8D25");
228978
+ }
228979
+ this.emitRunUpdated(run2);
228980
+ return run2;
228981
+ }
228828
228982
  try {
228829
228983
  const reviewerId = await this.agentOps.createNewSession(
228830
228984
  opts.project.id,
@@ -228832,11 +228986,16 @@ var WorkflowEngine = class {
228832
228986
  opts.project.path,
228833
228987
  false,
228834
228988
  "plan",
228835
- "claude-code",
228989
+ opts.reviewerAgentType ?? "claude-code",
228836
228990
  true
228837
228991
  );
228992
+ const taskContext = extractTaskContextBefore(entries, turnEndIndex);
228993
+ await this.agentOps.setFinalSessionTitle(
228994
+ reviewerId,
228995
+ `Review - ${sourceSession?.title || (taskContext ? snippetTitle(taskContext) : null) || "Conversation"}`
228996
+ ).catch((err) => console.warn(`[WorkflowEngine] failed to set reviewer title for ${reviewerId}:`, err));
228838
228997
  const prompt = buildReviewerPrompt({
228839
- taskContext: extractTaskContextBefore(entries, turnEndIndex),
228998
+ taskContext,
228840
228999
  reviewFocus: opts.reviewFocus ?? null,
228841
229000
  target
228842
229001
  });
@@ -228863,9 +229022,7 @@ var WorkflowEngine = class {
228863
229022
  throw new WorkflowError("spawn-failed", "\u521B\u5EFA reviewer session \u5931\u8D25");
228864
229023
  }
228865
229024
  } catch (err) {
228866
- if (this.participants.get(opts.sourceSessionId)?.runId === "pending") {
228867
- this.participants.delete(opts.sourceSessionId);
228868
- }
229025
+ this.releaseReservations(runId);
228869
229026
  throw err;
228870
229027
  }
228871
229028
  }
@@ -228876,7 +229033,7 @@ var WorkflowEngine = class {
228876
229033
  if (!run2 || run2.status !== "waiting_reviewer") return;
228877
229034
  const entries = this.agentOps.getRawMessages(event.sessionId);
228878
229035
  const boundary = event.turnEndEntryIndex ?? extractLatestTurnEndIndex(entries) ?? entries.length;
228879
- const feedback = extractLastAssistantBefore(entries, boundary) ?? "(reviewer \u6CA1\u6709\u8F93\u51FA\u53EF\u7528\u7684\u53CD\u9988\u6587\u672C)";
229036
+ const feedback = extractLastAssistantInTurn(entries, boundary) ?? "(reviewer \u6CA1\u6709\u8F93\u51FA\u53EF\u7528\u7684\u53CD\u9988\u6587\u672C)";
228880
229037
  let driftNote = null;
228881
229038
  try {
228882
229039
  const target = run2.review_target ? JSON.parse(run2.review_target) : null;
@@ -234913,6 +235070,9 @@ var routes11 = async (fastify2) => {
234913
235070
  );
234914
235071
  return reply.code(proxyStatus(result)).send(result.data);
234915
235072
  }
235073
+ if (fastify2.workflowEngine.isSessionInActiveRun(req.params.sessionId)) {
235074
+ return reply.code(409).send({ error: "Session is participating in an active review" });
235075
+ }
234916
235076
  const session = fastify2.agentSessionManager.getSession(req.params.sessionId);
234917
235077
  if (!session) {
234918
235078
  return reply.code(404).send({ error: "Session not found" });
@@ -234952,6 +235112,9 @@ var routes11 = async (fastify2) => {
234952
235112
  );
234953
235113
  return reply.code(proxyStatus(result)).send(result.data);
234954
235114
  }
235115
+ if (fastify2.workflowEngine.isSessionInActiveRun(req.params.sessionId)) {
235116
+ return reply.code(409).send({ error: "Session is participating in an active review" });
235117
+ }
234955
235118
  const session = fastify2.agentSessionManager.getSession(req.params.sessionId);
234956
235119
  if (!session) {
234957
235120
  return reply.code(404).send({ error: "Session not found" });
@@ -235890,6 +236053,10 @@ var command_routes_default = (0, import_fastify_plugin18.default)(routes17, { na
235890
236053
 
235891
236054
  // src/routes/workflow-run-routes.ts
235892
236055
  var import_fastify_plugin19 = __toESM(require_plugin2(), 1);
236056
+ function parseReviewerAgentType(raw) {
236057
+ if (raw === void 0) return void 0;
236058
+ return typeof raw === "string" && REVIEWER_AGENT_TYPES.has(raw) ? raw : null;
236059
+ }
235893
236060
  function errStatus(err) {
235894
236061
  if (!(err instanceof WorkflowError)) return null;
235895
236062
  switch (err.code) {
@@ -235897,6 +236064,8 @@ function errStatus(err) {
235897
236064
  return 409;
235898
236065
  case "source-running":
235899
236066
  return 409;
236067
+ case "reviewer-unavailable":
236068
+ return 409;
235900
236069
  case "bad-state":
235901
236070
  return 409;
235902
236071
  case "no-completed-turn":
@@ -235934,6 +236103,16 @@ async function routes18(fastify2) {
235934
236103
  if (userId === null) return;
235935
236104
  const { projectId, branch, sourceSessionId, reviewFocus, sourceTurnEndIndex } = req.body ?? {};
235936
236105
  if (!projectId || !sourceSessionId) return reply.code(400).send({ error: "projectId and sourceSessionId are required" });
236106
+ const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
236107
+ if (reviewerAgentType === null) return reply.code(400).send({ error: "reviewerAgentType must be one of: claude-code, codex" });
236108
+ const reviewerSessionIdRaw = req.body?.reviewerSessionId;
236109
+ if (reviewerSessionIdRaw !== void 0 && (typeof reviewerSessionIdRaw !== "string" || reviewerSessionIdRaw.trim() === "")) {
236110
+ return reply.code(400).send({ error: "reviewerSessionId must be a non-empty string" });
236111
+ }
236112
+ if (reviewerSessionIdRaw !== void 0 && reviewerAgentType !== void 0) {
236113
+ return reply.code(400).send({ error: "reviewerSessionId and reviewerAgentType are mutually exclusive" });
236114
+ }
236115
+ const reviewerSessionId = reviewerSessionIdRaw?.trim();
235937
236116
  if (sourceSessionId.startsWith("remote-")) {
235938
236117
  const remoteInfo = fastify2.remoteSessionMap.get(sourceSessionId);
235939
236118
  if (!remoteInfo) return reply.code(404).send({ error: "Session not found" });
@@ -235941,10 +236120,20 @@ async function routes18(fastify2) {
235941
236120
  if (derivedProjectId !== projectId) return reply.code(404).send({ error: "Session not found" });
235942
236121
  const remoteProject = await fastify2.storage.projects.getById(projectId, userId);
235943
236122
  if (!remoteProject) return reply.code(404).send({ error: "Project not found" });
236123
+ let bareReviewerSessionId;
236124
+ if (reviewerSessionId) {
236125
+ const reviewerInfo = fastify2.remoteSessionMap.get(reviewerSessionId);
236126
+ if (!reviewerInfo || reviewerInfo.remoteServerId !== remoteInfo.remoteServerId || projectIdFromRemoteSessionId(reviewerSessionId, reviewerInfo) !== projectId) {
236127
+ return reply.code(404).send({ error: "Reviewer session not found" });
236128
+ }
236129
+ bareReviewerSessionId = reviewerInfo.remoteSessionId;
236130
+ }
235944
236131
  const result = await proxyAuto(remoteInfo, "POST", "/api/path/workflow-runs", {
235945
236132
  sourceSessionId: remoteInfo.remoteSessionId,
235946
236133
  reviewFocus,
235947
- sourceTurnEndIndex
236134
+ sourceTurnEndIndex,
236135
+ reviewerAgentType,
236136
+ reviewerSessionId: bareReviewerSessionId
235948
236137
  });
235949
236138
  if (!result.ok) return sendProxyFailure(reply, result);
235950
236139
  const bareRun = result.data.run;
@@ -235957,13 +236146,14 @@ async function routes18(fastify2) {
235957
236146
  projectId
235958
236147
  });
235959
236148
  if (bareRun.reviewer_session_id && localRun.reviewer_session_id) {
235960
- fastify2.remoteSessionMap.set(localRun.reviewer_session_id, {
236149
+ const reviewerInfo = {
235961
236150
  remoteServerId: remoteInfo.remoteServerId,
235962
236151
  remoteUrl: remoteInfo.remoteUrl,
235963
236152
  remoteApiKey: remoteInfo.remoteApiKey,
235964
236153
  remoteSessionId: bareRun.reviewer_session_id,
235965
236154
  branch: bareRun.branch
235966
- });
236155
+ };
236156
+ fastify2.remoteSessionMap.set(localRun.reviewer_session_id, reviewerInfo);
235967
236157
  await fastify2.storage.remoteSessionMappings.upsert(
235968
236158
  localRun.reviewer_session_id,
235969
236159
  projectId,
@@ -235978,6 +236168,22 @@ async function routes18(fastify2) {
235978
236168
  eventBus: fastify2.eventBus,
235979
236169
  agentSessionManager: fastify2.agentSessionManager
235980
236170
  });
236171
+ fastify2.eventBus.emit({
236172
+ type: "session:process",
236173
+ projectId,
236174
+ branch: bareRun.branch,
236175
+ sessionId: localRun.reviewer_session_id,
236176
+ alive: true
236177
+ });
236178
+ fastify2.eventBus.emit({
236179
+ type: "session:status",
236180
+ projectId,
236181
+ branch: bareRun.branch,
236182
+ sessionId: localRun.reviewer_session_id,
236183
+ status: "running"
236184
+ });
236185
+ fastify2.agentSessionManager.markTitleResolved(localRun.reviewer_session_id);
236186
+ await fastify2.storage.remoteSessionMappings.markTitleResolved(localRun.reviewer_session_id);
235981
236187
  }
235982
236188
  fastify2.eventBus.emit({ type: "workflow:run-updated", projectId, branch: bareRun.branch, run: localRun });
235983
236189
  return reply.code(201).send({ run: localRun });
@@ -235999,7 +236205,9 @@ async function routes18(fastify2) {
235999
236205
  branch: runBranch,
236000
236206
  sourceSessionId,
236001
236207
  reviewFocus,
236002
- sourceTurnEndIndex
236208
+ sourceTurnEndIndex,
236209
+ reviewerAgentType,
236210
+ reviewerSessionId
236003
236211
  });
236004
236212
  return reply.code(201).send({ run: run2 });
236005
236213
  } catch (err) {
@@ -236008,6 +236216,55 @@ async function routes18(fastify2) {
236008
236216
  throw err;
236009
236217
  }
236010
236218
  });
236219
+ fastify2.get("/api/workflow-runs/reviewer-candidate", async (req, reply) => {
236220
+ const userId = requireAuth(req, reply);
236221
+ if (userId === null) return;
236222
+ const { projectId, sourceSessionId } = req.query;
236223
+ if (!projectId || !sourceSessionId) {
236224
+ return reply.code(400).send({ error: "projectId and sourceSessionId are required" });
236225
+ }
236226
+ const project = await fastify2.storage.projects.getById(projectId, userId);
236227
+ if (!project) return reply.code(404).send({ error: "Project not found" });
236228
+ if (sourceSessionId.startsWith("remote-")) {
236229
+ const remoteInfo = fastify2.remoteSessionMap.get(sourceSessionId);
236230
+ if (!remoteInfo || projectIdFromRemoteSessionId(sourceSessionId, remoteInfo) !== projectId) {
236231
+ return reply.code(404).send({ error: "Session not found" });
236232
+ }
236233
+ const params = new URLSearchParams({ sourceSessionId: remoteInfo.remoteSessionId });
236234
+ const result = await proxyAuto(
236235
+ remoteInfo,
236236
+ "GET",
236237
+ `/api/path/workflow-runs/reviewer-candidate?${params}`
236238
+ );
236239
+ if (!result.ok) return sendProxyFailure(reply, result);
236240
+ const bareCandidate = result.data.candidate;
236241
+ const candidate2 = mapRemoteReviewerCandidate(bareCandidate, remoteInfo.remoteServerId, projectId);
236242
+ if (bareCandidate?.sessionId && candidate2?.sessionId) {
236243
+ const reviewerInfo = {
236244
+ remoteServerId: remoteInfo.remoteServerId,
236245
+ remoteUrl: remoteInfo.remoteUrl,
236246
+ remoteApiKey: remoteInfo.remoteApiKey,
236247
+ remoteSessionId: bareCandidate.sessionId,
236248
+ branch: remoteInfo.branch
236249
+ };
236250
+ fastify2.remoteSessionMap.set(candidate2.sessionId, reviewerInfo);
236251
+ await fastify2.storage.remoteSessionMappings.upsert(
236252
+ candidate2.sessionId,
236253
+ projectId,
236254
+ remoteInfo.remoteServerId,
236255
+ bareCandidate.sessionId,
236256
+ remoteInfo.branch
236257
+ );
236258
+ }
236259
+ return reply.send({ candidate: candidate2 });
236260
+ }
236261
+ const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236262
+ if (!sourceSession || sourceSession.project_id !== projectId) {
236263
+ return reply.code(404).send({ error: "Session not found" });
236264
+ }
236265
+ const candidate = await fastify2.workflowEngine.getReviewerCandidate(sourceSessionId);
236266
+ return reply.send({ candidate });
236267
+ });
236011
236268
  fastify2.get(
236012
236269
  "/api/workflow-runs",
236013
236270
  async (req, reply) => {
@@ -236128,6 +236385,16 @@ async function routes18(fastify2) {
236128
236385
  if (userId === null) return;
236129
236386
  const { sourceSessionId, reviewFocus, sourceTurnEndIndex } = req.body ?? {};
236130
236387
  if (!sourceSessionId) return reply.code(400).send({ error: "sourceSessionId is required" });
236388
+ const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
236389
+ if (reviewerAgentType === null) return reply.code(400).send({ error: "reviewerAgentType must be one of: claude-code, codex" });
236390
+ const reviewerSessionIdRaw = req.body?.reviewerSessionId;
236391
+ if (reviewerSessionIdRaw !== void 0 && (typeof reviewerSessionIdRaw !== "string" || reviewerSessionIdRaw.trim() === "")) {
236392
+ return reply.code(400).send({ error: "reviewerSessionId must be a non-empty string" });
236393
+ }
236394
+ if (reviewerSessionIdRaw !== void 0 && reviewerAgentType !== void 0) {
236395
+ return reply.code(400).send({ error: "reviewerSessionId and reviewerAgentType are mutually exclusive" });
236396
+ }
236397
+ const reviewerSessionId = reviewerSessionIdRaw?.trim();
236131
236398
  const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236132
236399
  if (!sourceSession) return reply.code(404).send({ error: "Session not found" });
236133
236400
  const project = await fastify2.storage.projects.getById(sourceSession.project_id);
@@ -236139,7 +236406,9 @@ async function routes18(fastify2) {
236139
236406
  branch: sourceSession.branch || null,
236140
236407
  sourceSessionId,
236141
236408
  reviewFocus,
236142
- sourceTurnEndIndex
236409
+ sourceTurnEndIndex,
236410
+ reviewerAgentType,
236411
+ reviewerSessionId
236143
236412
  });
236144
236413
  return reply.code(201).send({ run: run2 });
236145
236414
  } catch (err) {
@@ -236148,6 +236417,16 @@ async function routes18(fastify2) {
236148
236417
  throw err;
236149
236418
  }
236150
236419
  });
236420
+ fastify2.get("/api/path/workflow-runs/reviewer-candidate", async (req, reply) => {
236421
+ const userId = requireAuth(req, reply);
236422
+ if (userId === null) return;
236423
+ const { sourceSessionId } = req.query;
236424
+ if (!sourceSessionId) return reply.code(400).send({ error: "sourceSessionId is required" });
236425
+ const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236426
+ if (!sourceSession) return reply.code(404).send({ error: "Session not found" });
236427
+ const candidate = await fastify2.workflowEngine.getReviewerCandidate(sourceSessionId);
236428
+ return reply.send({ candidate });
236429
+ });
236151
236430
  fastify2.get("/api/path/workflow-runs", async (req, reply) => {
236152
236431
  const userId = requireAuth(req, reply);
236153
236432
  if (userId === null) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibedeckx/linux-x64",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Vibedeckx platform binaries for Linux x64",
5
5
  "os": [
6
6
  "linux"