@vibedeckx/linux-x64 0.2.6 → 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 +248 -17
  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();
@@ -226027,6 +226035,13 @@ function mapRemoteRun(run2, remoteServerId, projectId) {
226027
226035
  reviewer_session_id: run2.reviewer_session_id ? `${prefix}${run2.reviewer_session_id}` : null
226028
226036
  };
226029
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
+ }
226030
226045
  function runUpdatedEventFromRemoteFrame(parsed, sessionId, remoteInfo) {
226031
226046
  if (!("workflowRunUpdated" in parsed)) return null;
226032
226047
  const bare = parsed.workflowRunUpdated;
@@ -228713,9 +228728,10 @@ function extractLatestTurnEndIndex(entries) {
228713
228728
  }
228714
228729
  return null;
228715
228730
  }
228716
- function extractLastAssistantBefore(entries, beforeIndex) {
228731
+ function extractLastAssistantInTurn(entries, beforeIndex) {
228717
228732
  for (let i = beforeIndex - 1; i >= 0; i--) {
228718
228733
  const e = entries[i];
228734
+ if (e?.type === "user") return null;
228719
228735
  if (e?.type === "assistant" && typeof e.content === "string" && e.content.trim()) return e.content;
228720
228736
  }
228721
228737
  return null;
@@ -228746,6 +228762,27 @@ ${opts.reviewFocus}` : null,
228746
228762
  "\nEnd your final message with a clear, actionable list of feedback items \u2014 or state explicitly that the work looks good."
228747
228763
  ].filter((l) => l !== null).join("\n");
228748
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
+ }
228749
228786
  function buildFeedbackMessage(feedback) {
228750
228787
  return [
228751
228788
  "[Review Feedback]",
@@ -228754,6 +228791,7 @@ function buildFeedbackMessage(feedback) {
228754
228791
  feedback
228755
228792
  ].join("\n");
228756
228793
  }
228794
+ var REVIEWER_AGENT_TYPES = /* @__PURE__ */ new Set(["claude-code", "codex"]);
228757
228795
  var WorkflowEngine = class {
228758
228796
  constructor(storage, agentOps) {
228759
228797
  this.storage = storage;
@@ -228803,6 +228841,15 @@ var WorkflowEngine = class {
228803
228841
  if (p2.runId === run2.id) this.participants.delete(sid);
228804
228842
  }
228805
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
+ }
228806
228853
  /** Sync check used by ChatSessionManager before waking the commander model. */
228807
228854
  shouldSuppressAgentEvent(sessionId) {
228808
228855
  return this.participants.get(sessionId)?.role === "reviewer";
@@ -228810,14 +228857,62 @@ var WorkflowEngine = class {
228810
228857
  isSessionInActiveRun(sessionId) {
228811
228858
  return this.participants.has(sessionId);
228812
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
+ }
228813
228895
  async startAdhocReview(opts) {
228814
- if (this.participants.has(opts.sourceSessionId)) {
228815
- 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" });
228816
228909
  }
228817
- this.participants.set(opts.sourceSessionId, { runId: "pending", role: "source" });
228818
228910
  try {
228819
- const busy = await this.storage.workflowRuns.getActiveBySession(opts.sourceSessionId);
228820
- 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
+ }
228821
228916
  const sourceSession = await this.storage.agentSessions.getById(opts.sourceSessionId);
228822
228917
  if (sourceSession?.status === "running") {
228823
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");
@@ -228829,16 +228924,61 @@ var WorkflowEngine = class {
228829
228924
  }
228830
228925
  const worktreePath = resolveWorktreePath(opts.project.path, opts.branch);
228831
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
+ }
228832
228946
  const run2 = await this.storage.workflowRuns.create({
228833
- id: randomUUID5(),
228947
+ id: runId,
228834
228948
  project_id: opts.project.id,
228835
228949
  branch: opts.branch,
228836
228950
  source_session_id: opts.sourceSessionId,
228837
228951
  source_turn_end_index: turnEndIndex,
228838
228952
  review_focus: opts.reviewFocus ?? null,
228839
- review_target: JSON.stringify(target)
228953
+ review_target: JSON.stringify(target),
228954
+ reviewer_session_id: opts.reviewerSessionId ?? null
228840
228955
  });
228841
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
+ }
228842
228982
  try {
228843
228983
  const reviewerId = await this.agentOps.createNewSession(
228844
228984
  opts.project.id,
@@ -228882,9 +229022,7 @@ var WorkflowEngine = class {
228882
229022
  throw new WorkflowError("spawn-failed", "\u521B\u5EFA reviewer session \u5931\u8D25");
228883
229023
  }
228884
229024
  } catch (err) {
228885
- if (this.participants.get(opts.sourceSessionId)?.runId === "pending") {
228886
- this.participants.delete(opts.sourceSessionId);
228887
- }
229025
+ this.releaseReservations(runId);
228888
229026
  throw err;
228889
229027
  }
228890
229028
  }
@@ -228895,7 +229033,7 @@ var WorkflowEngine = class {
228895
229033
  if (!run2 || run2.status !== "waiting_reviewer") return;
228896
229034
  const entries = this.agentOps.getRawMessages(event.sessionId);
228897
229035
  const boundary = event.turnEndEntryIndex ?? extractLatestTurnEndIndex(entries) ?? entries.length;
228898
- 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)";
228899
229037
  let driftNote = null;
228900
229038
  try {
228901
229039
  const target = run2.review_target ? JSON.parse(run2.review_target) : null;
@@ -234932,6 +235070,9 @@ var routes11 = async (fastify2) => {
234932
235070
  );
234933
235071
  return reply.code(proxyStatus(result)).send(result.data);
234934
235072
  }
235073
+ if (fastify2.workflowEngine.isSessionInActiveRun(req.params.sessionId)) {
235074
+ return reply.code(409).send({ error: "Session is participating in an active review" });
235075
+ }
234935
235076
  const session = fastify2.agentSessionManager.getSession(req.params.sessionId);
234936
235077
  if (!session) {
234937
235078
  return reply.code(404).send({ error: "Session not found" });
@@ -234971,6 +235112,9 @@ var routes11 = async (fastify2) => {
234971
235112
  );
234972
235113
  return reply.code(proxyStatus(result)).send(result.data);
234973
235114
  }
235115
+ if (fastify2.workflowEngine.isSessionInActiveRun(req.params.sessionId)) {
235116
+ return reply.code(409).send({ error: "Session is participating in an active review" });
235117
+ }
234974
235118
  const session = fastify2.agentSessionManager.getSession(req.params.sessionId);
234975
235119
  if (!session) {
234976
235120
  return reply.code(404).send({ error: "Session not found" });
@@ -235909,7 +236053,6 @@ var command_routes_default = (0, import_fastify_plugin18.default)(routes17, { na
235909
236053
 
235910
236054
  // src/routes/workflow-run-routes.ts
235911
236055
  var import_fastify_plugin19 = __toESM(require_plugin2(), 1);
235912
- var REVIEWER_AGENT_TYPES = /* @__PURE__ */ new Set(["claude-code", "codex"]);
235913
236056
  function parseReviewerAgentType(raw) {
235914
236057
  if (raw === void 0) return void 0;
235915
236058
  return typeof raw === "string" && REVIEWER_AGENT_TYPES.has(raw) ? raw : null;
@@ -235921,6 +236064,8 @@ function errStatus(err) {
235921
236064
  return 409;
235922
236065
  case "source-running":
235923
236066
  return 409;
236067
+ case "reviewer-unavailable":
236068
+ return 409;
235924
236069
  case "bad-state":
235925
236070
  return 409;
235926
236071
  case "no-completed-turn":
@@ -235960,6 +236105,14 @@ async function routes18(fastify2) {
235960
236105
  if (!projectId || !sourceSessionId) return reply.code(400).send({ error: "projectId and sourceSessionId are required" });
235961
236106
  const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
235962
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();
235963
236116
  if (sourceSessionId.startsWith("remote-")) {
235964
236117
  const remoteInfo = fastify2.remoteSessionMap.get(sourceSessionId);
235965
236118
  if (!remoteInfo) return reply.code(404).send({ error: "Session not found" });
@@ -235967,11 +236120,20 @@ async function routes18(fastify2) {
235967
236120
  if (derivedProjectId !== projectId) return reply.code(404).send({ error: "Session not found" });
235968
236121
  const remoteProject = await fastify2.storage.projects.getById(projectId, userId);
235969
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
+ }
235970
236131
  const result = await proxyAuto(remoteInfo, "POST", "/api/path/workflow-runs", {
235971
236132
  sourceSessionId: remoteInfo.remoteSessionId,
235972
236133
  reviewFocus,
235973
236134
  sourceTurnEndIndex,
235974
- reviewerAgentType
236135
+ reviewerAgentType,
236136
+ reviewerSessionId: bareReviewerSessionId
235975
236137
  });
235976
236138
  if (!result.ok) return sendProxyFailure(reply, result);
235977
236139
  const bareRun = result.data.run;
@@ -236044,7 +236206,8 @@ async function routes18(fastify2) {
236044
236206
  sourceSessionId,
236045
236207
  reviewFocus,
236046
236208
  sourceTurnEndIndex,
236047
- reviewerAgentType
236209
+ reviewerAgentType,
236210
+ reviewerSessionId
236048
236211
  });
236049
236212
  return reply.code(201).send({ run: run2 });
236050
236213
  } catch (err) {
@@ -236053,6 +236216,55 @@ async function routes18(fastify2) {
236053
236216
  throw err;
236054
236217
  }
236055
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
+ });
236056
236268
  fastify2.get(
236057
236269
  "/api/workflow-runs",
236058
236270
  async (req, reply) => {
@@ -236175,6 +236387,14 @@ async function routes18(fastify2) {
236175
236387
  if (!sourceSessionId) return reply.code(400).send({ error: "sourceSessionId is required" });
236176
236388
  const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
236177
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();
236178
236398
  const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236179
236399
  if (!sourceSession) return reply.code(404).send({ error: "Session not found" });
236180
236400
  const project = await fastify2.storage.projects.getById(sourceSession.project_id);
@@ -236187,7 +236407,8 @@ async function routes18(fastify2) {
236187
236407
  sourceSessionId,
236188
236408
  reviewFocus,
236189
236409
  sourceTurnEndIndex,
236190
- reviewerAgentType
236410
+ reviewerAgentType,
236411
+ reviewerSessionId
236191
236412
  });
236192
236413
  return reply.code(201).send({ run: run2 });
236193
236414
  } catch (err) {
@@ -236196,6 +236417,16 @@ async function routes18(fastify2) {
236196
236417
  throw err;
236197
236418
  }
236198
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
+ });
236199
236430
  fastify2.get("/api/path/workflow-runs", async (req, reply) => {
236200
236431
  const userId = requireAuth(req, reply);
236201
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.6",
3
+ "version": "0.2.7",
4
4
  "description": "Vibedeckx platform binaries for Linux x64",
5
5
  "os": [
6
6
  "linux"