@vibedeckx/linux-x64 0.2.6 → 0.2.8

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 +440 -33
  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();
@@ -223734,17 +223742,13 @@ async function generateSessionTitleWithModel(model, userMessage, options = {}) {
223734
223742
  }
223735
223743
  } : void 0;
223736
223744
  try {
223737
- const result = await Promise.race([
223738
- generateText({
223739
- model,
223740
- system: SYSTEM_PROMPT,
223741
- prompt: buildPrompt(userMessage),
223742
- experimental_telemetry: telemetry
223743
- }),
223744
- new Promise(
223745
- (_3, reject) => setTimeout(() => reject(new Error("title generation timed out")), AI_TIMEOUT_MS)
223746
- )
223747
- ]);
223745
+ const result = await generateText({
223746
+ model,
223747
+ system: SYSTEM_PROMPT,
223748
+ prompt: buildPrompt(userMessage),
223749
+ timeout: AI_TIMEOUT_MS,
223750
+ experimental_telemetry: telemetry
223751
+ });
223748
223752
  const text2 = result.text ?? "";
223749
223753
  const sanitized = sanitizeTitle(text2);
223750
223754
  return sanitized.length > 0 ? sanitized : null;
@@ -226027,6 +226031,13 @@ function mapRemoteRun(run2, remoteServerId, projectId) {
226027
226031
  reviewer_session_id: run2.reviewer_session_id ? `${prefix}${run2.reviewer_session_id}` : null
226028
226032
  };
226029
226033
  }
226034
+ function mapRemoteReviewerCandidate(candidate, remoteServerId, projectId) {
226035
+ if (!candidate?.sessionId) return candidate;
226036
+ return {
226037
+ ...candidate,
226038
+ sessionId: `remote-${remoteServerId}-${projectId}-${candidate.sessionId}`
226039
+ };
226040
+ }
226030
226041
  function runUpdatedEventFromRemoteFrame(parsed, sessionId, remoteInfo) {
226031
226042
  if (!("workflowRunUpdated" in parsed)) return null;
226032
226043
  const bare = parsed.workflowRunUpdated;
@@ -228707,15 +228718,28 @@ var WorkflowError = class extends Error {
228707
228718
  }
228708
228719
  code;
228709
228720
  };
228721
+ var MAX_CONTEXT_CHARS = 2e3;
228722
+ var MAX_SELF_REPORT_CHARS = 4e3;
228723
+ var SELF_REPORT_MIN_CHARS = 80;
228724
+ function cap(text2, max) {
228725
+ return text2.length > max ? text2.slice(0, max) + "\u2026" : text2;
228726
+ }
228727
+ function userTextOf(e) {
228728
+ if (e.type !== "user") return null;
228729
+ if (typeof e.content === "string") return e.content;
228730
+ const text2 = e.content.filter((p2) => p2.type === "text").map((p2) => p2.text).join("\n");
228731
+ return text2 || null;
228732
+ }
228710
228733
  function extractLatestTurnEndIndex(entries) {
228711
228734
  for (let i = entries.length - 1; i >= 0; i--) {
228712
228735
  if (entries[i]?.type === "turn_end") return i;
228713
228736
  }
228714
228737
  return null;
228715
228738
  }
228716
- function extractLastAssistantBefore(entries, beforeIndex) {
228739
+ function extractLastAssistantInTurn(entries, beforeIndex) {
228717
228740
  for (let i = beforeIndex - 1; i >= 0; i--) {
228718
228741
  const e = entries[i];
228742
+ if (e?.type === "user") return null;
228719
228743
  if (e?.type === "assistant" && typeof e.content === "string" && e.content.trim()) return e.content;
228720
228744
  }
228721
228745
  return null;
@@ -228729,23 +228753,89 @@ function extractTaskContextBefore(entries, turnEndIndex) {
228729
228753
  }
228730
228754
  return null;
228731
228755
  }
228756
+ function extractFirstUserMessage(entries) {
228757
+ for (let i = 0; i < entries.length; i++) {
228758
+ const e = entries[i];
228759
+ if (e?.type !== "user" || e.event) continue;
228760
+ const text2 = userTextOf(e)?.trim();
228761
+ if (text2) return cap(text2, MAX_CONTEXT_CHARS);
228762
+ }
228763
+ return null;
228764
+ }
228765
+ function extractAuthorSelfReport(entries, beforeIndex, opts) {
228766
+ let fallback = null;
228767
+ for (let i = beforeIndex - 1; i >= 0; i--) {
228768
+ const e = entries[i];
228769
+ if (e?.type === "user" && opts?.withinTurn) break;
228770
+ if (e?.type !== "assistant" || typeof e.content !== "string") continue;
228771
+ const text2 = e.content.trim();
228772
+ if (!text2) continue;
228773
+ if (text2.length >= SELF_REPORT_MIN_CHARS) return cap(text2, MAX_SELF_REPORT_CHARS);
228774
+ if (fallback === null) fallback = text2;
228775
+ }
228776
+ return fallback;
228777
+ }
228778
+ function selfReportSection(report) {
228779
+ if (!report) return null;
228780
+ return [
228781
+ "\n## Author's self-report (unverified)",
228782
+ "The implementing agent described its own work as follows. Treat every claim as unverified \u2014 check each one against the actual code, and look for problems the self-report does not mention.",
228783
+ "<author-self-report>",
228784
+ report,
228785
+ "</author-self-report>"
228786
+ ].join("\n");
228787
+ }
228732
228788
  function buildReviewerPrompt(opts) {
228789
+ const intent = opts.originalIntent !== opts.taskContext ? opts.originalIntent : null;
228790
+ const brief = opts.intentBrief || null;
228791
+ const hasExcerpt = Boolean(intent || opts.taskContext || opts.authorSelfReport);
228733
228792
  return [
228734
228793
  "You are a code reviewer agent. Another agent just completed work in this workspace; review it critically and independently.",
228794
+ brief ? `
228795
+ ## Intent brief (distilled from the source conversation)
228796
+ ${brief}` : null,
228797
+ !brief && intent ? `
228798
+ ## Original request (the user's first message in this session, verbatim)
228799
+ ${intent}` : null,
228735
228800
  opts.taskContext ? `
228736
228801
  ## Original task
228737
228802
  ${opts.taskContext}` : null,
228803
+ brief ? null : selfReportSection(opts.authorSelfReport),
228738
228804
  opts.reviewFocus ? `
228739
228805
  ## Review focus (from the user)
228740
228806
  ${opts.reviewFocus}` : null,
228741
228807
  "\n## How to review",
228742
228808
  "- Do NOT modify any files \u2014 you are in read-only review mode.",
228743
228809
  "- Inspect the actual workspace state yourself: read the relevant files, run `git diff`, `git status` and `git log`.",
228744
- opts.target.baseHead ? `- The work was captured at commit ${opts.target.baseHead}${opts.target.diffStat ? ` with uncommitted changes (${opts.target.diffStat})` : " with no uncommitted changes"}.` : null,
228810
+ reviewTargetPromptLine(opts.target),
228745
228811
  "- Judge correctness, completeness against the task, and code quality. Be specific: reference files and lines.",
228746
- "\nEnd your final message with a clear, actionable list of feedback items \u2014 or state explicitly that the work looks good."
228812
+ "\nEnd your final message with a clear, actionable list of feedback items \u2014 or state explicitly that the work looks good.",
228813
+ brief ? "\n(review context: distilled intent brief + live workspace)" : hasExcerpt ? "\n(review context: deterministic excerpt of the source conversation + live workspace)" : "\n(review context: live workspace only \u2014 the source conversation was unavailable)"
228747
228814
  ].filter((l) => l !== null).join("\n");
228748
228815
  }
228816
+ function reviewTargetPromptLine(target) {
228817
+ return target.baseHead ? `- The work was captured at commit ${target.baseHead}${target.diffStat ? ` with uncommitted changes (${target.diffStat})` : " with no uncommitted changes"}.` : null;
228818
+ }
228819
+ function buildRereviewerPrompt(opts) {
228820
+ return [
228821
+ "The source agent has addressed feedback from your previous review.",
228822
+ "Review the latest workspace state again.",
228823
+ opts.taskContext ? `
228824
+ ## Latest source turn
228825
+ ${opts.taskContext}` : null,
228826
+ selfReportSection(opts.authorSelfReport),
228827
+ opts.reviewFocus ? `
228828
+ ## Review focus
228829
+ ${opts.reviewFocus}` : null,
228830
+ "\n## How to review",
228831
+ "- Verify whether your previous feedback was addressed correctly.",
228832
+ "- Treat the changed areas as new code: look for bugs the fix itself may have introduced, not only whether your old items were closed.",
228833
+ "- Check for regressions and remaining correctness or test gaps.",
228834
+ "- Do NOT modify files \u2014 remain in read-only review mode.",
228835
+ reviewTargetPromptLine(opts.target),
228836
+ "- End with actionable feedback, or explicitly state that it looks good."
228837
+ ].filter((line) => line !== null).join("\n");
228838
+ }
228749
228839
  function buildFeedbackMessage(feedback) {
228750
228840
  return [
228751
228841
  "[Review Feedback]",
@@ -228754,6 +228844,7 @@ function buildFeedbackMessage(feedback) {
228754
228844
  feedback
228755
228845
  ].join("\n");
228756
228846
  }
228847
+ var REVIEWER_AGENT_TYPES = /* @__PURE__ */ new Set(["claude-code", "codex"]);
228757
228848
  var WorkflowEngine = class {
228758
228849
  constructor(storage, agentOps) {
228759
228850
  this.storage = storage;
@@ -228803,6 +228894,15 @@ var WorkflowEngine = class {
228803
228894
  if (p2.runId === run2.id) this.participants.delete(sid);
228804
228895
  }
228805
228896
  }
228897
+ releaseReservations(runId) {
228898
+ for (const [sid, participant] of this.participants) {
228899
+ if (participant.runId === runId) this.participants.delete(sid);
228900
+ }
228901
+ }
228902
+ async failRun(run2, error48) {
228903
+ const failed = await this.storage.workflowRuns.update(run2.id, { status: "failed", error: error48 });
228904
+ if (failed) this.untrackRun(failed);
228905
+ }
228806
228906
  /** Sync check used by ChatSessionManager before waking the commander model. */
228807
228907
  shouldSuppressAgentEvent(sessionId) {
228808
228908
  return this.participants.get(sessionId)?.role === "reviewer";
@@ -228810,14 +228910,62 @@ var WorkflowEngine = class {
228810
228910
  isSessionInActiveRun(sessionId) {
228811
228911
  return this.participants.has(sessionId);
228812
228912
  }
228913
+ async getReviewerCandidate(sourceSessionId) {
228914
+ const previous = await this.storage.workflowRuns.getLatestCompletedBySource(sourceSessionId);
228915
+ if (!previous?.reviewer_session_id) return null;
228916
+ const unavailable = (reason) => ({
228917
+ available: false,
228918
+ sessionId: null,
228919
+ title: null,
228920
+ agentType: null,
228921
+ reason
228922
+ });
228923
+ const source = await this.storage.agentSessions.getById(sourceSessionId);
228924
+ const reviewer = await this.storage.agentSessions.getById(previous.reviewer_session_id);
228925
+ if (!reviewer) return unavailable("deleted");
228926
+ if (!source || reviewer.project_id !== source.project_id || reviewer.project_id !== previous.project_id) {
228927
+ return unavailable("project-mismatch");
228928
+ }
228929
+ if ((reviewer.branch || null) !== (source.branch || null) || (reviewer.branch || null) !== previous.branch) {
228930
+ return unavailable("branch-mismatch");
228931
+ }
228932
+ if (!REVIEWER_AGENT_TYPES.has(reviewer.agent_type)) {
228933
+ return unavailable("unsupported-agent");
228934
+ }
228935
+ if (reviewer.status === "running") return unavailable("running");
228936
+ if (reviewer.status !== "stopped") return unavailable("unavailable");
228937
+ if (this.participants.has(reviewer.id) || await this.storage.workflowRuns.getActiveBySession(reviewer.id)) {
228938
+ return unavailable("busy");
228939
+ }
228940
+ return {
228941
+ available: true,
228942
+ sessionId: reviewer.id,
228943
+ title: reviewer.title ?? null,
228944
+ agentType: reviewer.agent_type,
228945
+ reason: null
228946
+ };
228947
+ }
228813
228948
  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");
228949
+ if (opts.reviewerSessionId === opts.sourceSessionId) {
228950
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u4E0D\u80FD\u4E0E source session \u76F8\u540C");
228951
+ }
228952
+ const runId = randomUUID5();
228953
+ const participantIds = [opts.sourceSessionId, opts.reviewerSessionId].filter((id) => Boolean(id));
228954
+ for (const sessionId of participantIds) {
228955
+ if (this.participants.has(sessionId)) {
228956
+ throw new WorkflowError("session-busy", "\u8BE5 session \u5DF2\u5728\u4E00\u4E2A\u8FDB\u884C\u4E2D\u7684 review \u91CC");
228957
+ }
228958
+ }
228959
+ this.participants.set(opts.sourceSessionId, { runId, role: "source" });
228960
+ if (opts.reviewerSessionId) {
228961
+ this.participants.set(opts.reviewerSessionId, { runId, role: "reviewer" });
228816
228962
  }
228817
- this.participants.set(opts.sourceSessionId, { runId: "pending", role: "source" });
228818
228963
  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");
228964
+ for (const sessionId of participantIds) {
228965
+ if (await this.storage.workflowRuns.getActiveBySession(sessionId)) {
228966
+ throw new WorkflowError("session-busy", "\u8BE5 session \u5DF2\u5728\u4E00\u4E2A\u8FDB\u884C\u4E2D\u7684 review \u91CC");
228967
+ }
228968
+ }
228821
228969
  const sourceSession = await this.storage.agentSessions.getById(opts.sourceSessionId);
228822
228970
  if (sourceSession?.status === "running") {
228823
228971
  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 +228977,64 @@ var WorkflowEngine = class {
228829
228977
  }
228830
228978
  const worktreePath = resolveWorktreePath(opts.project.path, opts.branch);
228831
228979
  const target = captureReviewTarget(worktreePath);
228980
+ let reviewerSession = null;
228981
+ if (opts.reviewerSessionId) {
228982
+ reviewerSession = await this.storage.agentSessions.getById(opts.reviewerSessionId);
228983
+ if (!reviewerSession) {
228984
+ throw new WorkflowError("reviewer-unavailable", "\u4E0A\u6B21 reviewer session \u5DF2\u4E0D\u5B58\u5728");
228985
+ }
228986
+ if (reviewerSession.project_id !== opts.project.id) {
228987
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u4E0D\u5C5E\u4E8E\u5F53\u524D\u9879\u76EE");
228988
+ }
228989
+ if ((reviewerSession.branch || null) !== opts.branch) {
228990
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u4E0D\u5C5E\u4E8E\u5F53\u524D branch");
228991
+ }
228992
+ if (!REVIEWER_AGENT_TYPES.has(reviewerSession.agent_type)) {
228993
+ throw new WorkflowError("reviewer-unavailable", "reviewer agent \u7C7B\u578B\u4E0D\u53EF\u7528");
228994
+ }
228995
+ if (reviewerSession.status !== "stopped") {
228996
+ throw new WorkflowError("reviewer-unavailable", "reviewer session \u6B63\u5728\u8FD0\u884C\u6216\u4E0D\u53EF\u7528");
228997
+ }
228998
+ }
228832
228999
  const run2 = await this.storage.workflowRuns.create({
228833
- id: randomUUID5(),
229000
+ id: runId,
228834
229001
  project_id: opts.project.id,
228835
229002
  branch: opts.branch,
228836
229003
  source_session_id: opts.sourceSessionId,
228837
229004
  source_turn_end_index: turnEndIndex,
228838
229005
  review_focus: opts.reviewFocus ?? null,
228839
- review_target: JSON.stringify(target)
229006
+ review_target: JSON.stringify(target),
229007
+ reviewer_session_id: opts.reviewerSessionId ?? null
228840
229008
  });
228841
229009
  this.trackParticipants(run2);
229010
+ if (opts.reviewerSessionId && reviewerSession) {
229011
+ if (reviewerSession.permission_mode !== "plan") {
229012
+ let switched = false;
229013
+ try {
229014
+ switched = await this.agentOps.switchMode(opts.reviewerSessionId, opts.project.path, "plan");
229015
+ } catch {
229016
+ }
229017
+ if (!switched) {
229018
+ await this.failRun(run2, "\u65E0\u6CD5\u5C06 reviewer \u6062\u590D\u4E3A\u53EA\u8BFB plan \u6A21\u5F0F");
229019
+ throw new WorkflowError("reviewer-unavailable", "\u65E0\u6CD5\u5C06 reviewer \u6062\u590D\u4E3A\u53EA\u8BFB plan \u6A21\u5F0F");
229020
+ }
229021
+ }
229022
+ const prompt = buildRereviewerPrompt({
229023
+ taskContext: extractTaskContextBefore(entries, turnEndIndex),
229024
+ // Scoped to the fix turn: an older turn's summary would describe the
229025
+ // pre-review state and mislead the acceptance pass.
229026
+ authorSelfReport: extractAuthorSelfReport(entries, turnEndIndex, { withinTurn: true }),
229027
+ reviewFocus: opts.reviewFocus ?? null,
229028
+ target
229029
+ });
229030
+ const sent = await this.agentOps.sendUserMessage(opts.reviewerSessionId, prompt, opts.project.path).catch(() => false);
229031
+ if (!sent) {
229032
+ await this.failRun(run2, "\u5411\u4E0A\u6B21 reviewer \u6295\u9012\u590D\u5BA1\u4EFB\u52A1\u5931\u8D25");
229033
+ throw new WorkflowError("send-failed", "\u5411\u4E0A\u6B21 reviewer \u6295\u9012\u590D\u5BA1\u4EFB\u52A1\u5931\u8D25");
229034
+ }
229035
+ this.emitRunUpdated(run2);
229036
+ return run2;
229037
+ }
228842
229038
  try {
228843
229039
  const reviewerId = await this.agentOps.createNewSession(
228844
229040
  opts.project.id,
@@ -228856,6 +229052,9 @@ var WorkflowEngine = class {
228856
229052
  ).catch((err) => console.warn(`[WorkflowEngine] failed to set reviewer title for ${reviewerId}:`, err));
228857
229053
  const prompt = buildReviewerPrompt({
228858
229054
  taskContext,
229055
+ originalIntent: extractFirstUserMessage(entries),
229056
+ authorSelfReport: extractAuthorSelfReport(entries, turnEndIndex),
229057
+ intentBrief: opts.intentBrief ?? null,
228859
229058
  reviewFocus: opts.reviewFocus ?? null,
228860
229059
  target
228861
229060
  });
@@ -228882,9 +229081,7 @@ var WorkflowEngine = class {
228882
229081
  throw new WorkflowError("spawn-failed", "\u521B\u5EFA reviewer session \u5931\u8D25");
228883
229082
  }
228884
229083
  } catch (err) {
228885
- if (this.participants.get(opts.sourceSessionId)?.runId === "pending") {
228886
- this.participants.delete(opts.sourceSessionId);
228887
- }
229084
+ this.releaseReservations(runId);
228888
229085
  throw err;
228889
229086
  }
228890
229087
  }
@@ -228895,7 +229092,7 @@ var WorkflowEngine = class {
228895
229092
  if (!run2 || run2.status !== "waiting_reviewer") return;
228896
229093
  const entries = this.agentOps.getRawMessages(event.sessionId);
228897
229094
  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)";
229095
+ const feedback = extractLastAssistantInTurn(entries, boundary) ?? "(reviewer \u6CA1\u6709\u8F93\u51FA\u53EF\u7528\u7684\u53CD\u9988\u6587\u672C)";
228899
229096
  let driftNote = null;
228900
229097
  try {
228901
229098
  const target = run2.review_target ? JSON.parse(run2.review_target) : null;
@@ -234932,6 +235129,9 @@ var routes11 = async (fastify2) => {
234932
235129
  );
234933
235130
  return reply.code(proxyStatus(result)).send(result.data);
234934
235131
  }
235132
+ if (fastify2.workflowEngine.isSessionInActiveRun(req.params.sessionId)) {
235133
+ return reply.code(409).send({ error: "Session is participating in an active review" });
235134
+ }
234935
235135
  const session = fastify2.agentSessionManager.getSession(req.params.sessionId);
234936
235136
  if (!session) {
234937
235137
  return reply.code(404).send({ error: "Session not found" });
@@ -234971,6 +235171,9 @@ var routes11 = async (fastify2) => {
234971
235171
  );
234972
235172
  return reply.code(proxyStatus(result)).send(result.data);
234973
235173
  }
235174
+ if (fastify2.workflowEngine.isSessionInActiveRun(req.params.sessionId)) {
235175
+ return reply.code(409).send({ error: "Session is participating in an active review" });
235176
+ }
234974
235177
  const session = fastify2.agentSessionManager.getSession(req.params.sessionId);
234975
235178
  if (!session) {
234976
235179
  return reply.code(404).send({ error: "Session not found" });
@@ -235909,7 +236112,87 @@ var command_routes_default = (0, import_fastify_plugin18.default)(routes17, { na
235909
236112
 
235910
236113
  // src/routes/workflow-run-routes.ts
235911
236114
  var import_fastify_plugin19 = __toESM(require_plugin2(), 1);
235912
- var REVIEWER_AGENT_TYPES = /* @__PURE__ */ new Set(["claude-code", "codex"]);
236115
+
236116
+ // src/utils/review-brief.ts
236117
+ var AI_TIMEOUT_MS2 = 15e3;
236118
+ var PER_MESSAGE_MAX_CHARS = 1500;
236119
+ var TOTAL_INPUT_MAX_CHARS = 24e3;
236120
+ var HEAD_CHARS = 8e3;
236121
+ var TAIL_CHARS = 15e3;
236122
+ var BRIEF_MAX_CHARS = 4e3;
236123
+ var SYSTEM_PROMPT2 = [
236124
+ "You distill a coding-agent conversation into an intent brief for an independent code reviewer.",
236125
+ "The reviewer will NOT see the conversation \u2014 only your brief plus the actual code, so capture what the code alone cannot show:",
236126
+ "1. The original request and its goal.",
236127
+ "2. Constraints and explicit user decisions, including approaches the user rejected.",
236128
+ "3. The intended scope of the changes.",
236129
+ "4. Trade-offs or limitations that were acknowledged and accepted.",
236130
+ "Do NOT include the agent's reasoning, self-assessment, or claims that the work is correct or complete \u2014 the reviewer must judge that independently.",
236131
+ "Write concise markdown bullets under those numbered headings, under 400 words total, in the same language as the conversation. Reply with the brief only."
236132
+ ].join("\n");
236133
+ function capText(text2, max) {
236134
+ return text2.length > max ? text2.slice(0, max) + "\u2026" : text2;
236135
+ }
236136
+ function serializeConversationForBrief(messages) {
236137
+ const lines = [];
236138
+ for (const msg of messages) {
236139
+ if (!msg) continue;
236140
+ if (msg.type === "user" && !msg.event) {
236141
+ const text2 = extractUserText(msg.content).trim();
236142
+ if (text2) lines.push(`User: ${capText(text2, PER_MESSAGE_MAX_CHARS)}`);
236143
+ } else if (msg.type === "assistant" && typeof msg.content === "string") {
236144
+ const text2 = msg.content.trim();
236145
+ if (text2) lines.push(`Agent: ${capText(text2, PER_MESSAGE_MAX_CHARS)}`);
236146
+ }
236147
+ }
236148
+ const full = lines.join("\n\n");
236149
+ if (full.length <= TOTAL_INPUT_MAX_CHARS) return full;
236150
+ return `${full.slice(0, HEAD_CHARS)}
236151
+
236152
+ [\u2026 middle of the conversation omitted \u2026]
236153
+
236154
+ ${full.slice(-TAIL_CHARS)}`;
236155
+ }
236156
+ async function generateIntentBriefWithModel(model, conversation, options = {}) {
236157
+ if (conversation.trim().length === 0) return null;
236158
+ const telemetry = options.userId ? {
236159
+ isEnabled: true,
236160
+ functionId: "review-intent-brief",
236161
+ metadata: {
236162
+ userId: options.userId,
236163
+ tags: ["vibedeckx", "review-intent-brief"]
236164
+ }
236165
+ } : void 0;
236166
+ try {
236167
+ const result = await generateText({
236168
+ model,
236169
+ system: SYSTEM_PROMPT2,
236170
+ prompt: `Distill this conversation into an intent brief:
236171
+
236172
+ ${conversation}`,
236173
+ timeout: AI_TIMEOUT_MS2,
236174
+ experimental_telemetry: telemetry
236175
+ });
236176
+ const text2 = (result.text ?? "").trim();
236177
+ return text2.length > 0 ? capText(text2, BRIEF_MAX_CHARS) : null;
236178
+ } catch (error48) {
236179
+ console.warn("[ReviewBrief] AI generation failed:", error48.message);
236180
+ return null;
236181
+ }
236182
+ }
236183
+ async function generateIntentBrief(storage, userId, messages) {
236184
+ try {
236185
+ if (!await isChatModelConfigured(storage, userId)) return null;
236186
+ const conversation = serializeConversationForBrief(messages);
236187
+ if (!conversation) return null;
236188
+ return await generateIntentBriefWithModel(await resolveFastChatModel(storage, userId), conversation, { userId });
236189
+ } catch (error48) {
236190
+ console.warn("[ReviewBrief] generation failed:", error48.message);
236191
+ return null;
236192
+ }
236193
+ }
236194
+
236195
+ // src/routes/workflow-run-routes.ts
235913
236196
  function parseReviewerAgentType(raw) {
235914
236197
  if (raw === void 0) return void 0;
235915
236198
  return typeof raw === "string" && REVIEWER_AGENT_TYPES.has(raw) ? raw : null;
@@ -235921,6 +236204,8 @@ function errStatus(err) {
235921
236204
  return 409;
235922
236205
  case "source-running":
235923
236206
  return 409;
236207
+ case "reviewer-unavailable":
236208
+ return 409;
235924
236209
  case "bad-state":
235925
236210
  return 409;
235926
236211
  case "no-completed-turn":
@@ -235960,6 +236245,14 @@ async function routes18(fastify2) {
235960
236245
  if (!projectId || !sourceSessionId) return reply.code(400).send({ error: "projectId and sourceSessionId are required" });
235961
236246
  const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
235962
236247
  if (reviewerAgentType === null) return reply.code(400).send({ error: "reviewerAgentType must be one of: claude-code, codex" });
236248
+ const reviewerSessionIdRaw = req.body?.reviewerSessionId;
236249
+ if (reviewerSessionIdRaw !== void 0 && (typeof reviewerSessionIdRaw !== "string" || reviewerSessionIdRaw.trim() === "")) {
236250
+ return reply.code(400).send({ error: "reviewerSessionId must be a non-empty string" });
236251
+ }
236252
+ if (reviewerSessionIdRaw !== void 0 && reviewerAgentType !== void 0) {
236253
+ return reply.code(400).send({ error: "reviewerSessionId and reviewerAgentType are mutually exclusive" });
236254
+ }
236255
+ const reviewerSessionId = reviewerSessionIdRaw?.trim();
235963
236256
  if (sourceSessionId.startsWith("remote-")) {
235964
236257
  const remoteInfo = fastify2.remoteSessionMap.get(sourceSessionId);
235965
236258
  if (!remoteInfo) return reply.code(404).send({ error: "Session not found" });
@@ -235967,11 +236260,37 @@ async function routes18(fastify2) {
235967
236260
  if (derivedProjectId !== projectId) return reply.code(404).send({ error: "Session not found" });
235968
236261
  const remoteProject = await fastify2.storage.projects.getById(projectId, userId);
235969
236262
  if (!remoteProject) return reply.code(404).send({ error: "Project not found" });
236263
+ let bareReviewerSessionId;
236264
+ if (reviewerSessionId) {
236265
+ const reviewerInfo = fastify2.remoteSessionMap.get(reviewerSessionId);
236266
+ if (!reviewerInfo || reviewerInfo.remoteServerId !== remoteInfo.remoteServerId || projectIdFromRemoteSessionId(reviewerSessionId, reviewerInfo) !== projectId) {
236267
+ return reply.code(404).send({ error: "Reviewer session not found" });
236268
+ }
236269
+ bareReviewerSessionId = reviewerInfo.remoteSessionId;
236270
+ }
236271
+ let intentBrief2;
236272
+ if (!bareReviewerSessionId) {
236273
+ try {
236274
+ const historyResult = await proxyAuto(
236275
+ remoteInfo,
236276
+ "GET",
236277
+ `/api/agent-sessions/${remoteInfo.remoteSessionId}`
236278
+ );
236279
+ if (historyResult.ok) {
236280
+ const messages = historyResult.data.messages ?? [];
236281
+ intentBrief2 = await generateIntentBrief(fastify2.storage, resolveUserId(userId), messages) ?? void 0;
236282
+ }
236283
+ } catch (err) {
236284
+ console.warn("[WorkflowRuns] intent brief generation failed (remote source):", err);
236285
+ }
236286
+ }
235970
236287
  const result = await proxyAuto(remoteInfo, "POST", "/api/path/workflow-runs", {
235971
236288
  sourceSessionId: remoteInfo.remoteSessionId,
235972
236289
  reviewFocus,
235973
236290
  sourceTurnEndIndex,
235974
- reviewerAgentType
236291
+ reviewerAgentType,
236292
+ reviewerSessionId: bareReviewerSessionId,
236293
+ intentBrief: intentBrief2
235975
236294
  });
235976
236295
  if (!result.ok) return sendProxyFailure(reply, result);
235977
236296
  const bareRun = result.data.run;
@@ -236037,6 +236356,18 @@ async function routes18(fastify2) {
236037
236356
  if (branch !== void 0 && (branch || null) !== runBranch) {
236038
236357
  return reply.code(400).send({ error: "branch does not match source session" });
236039
236358
  }
236359
+ let intentBrief;
236360
+ if (!reviewerSessionId) {
236361
+ try {
236362
+ intentBrief = await generateIntentBrief(
236363
+ fastify2.storage,
236364
+ resolveUserId(userId),
236365
+ fastify2.agentSessionManager.getMessages(sourceSessionId)
236366
+ ) ?? void 0;
236367
+ } catch (err) {
236368
+ console.warn("[WorkflowRuns] intent brief generation failed (local source):", err);
236369
+ }
236370
+ }
236040
236371
  try {
236041
236372
  const run2 = await fastify2.workflowEngine.startAdhocReview({
236042
236373
  project: { id: project.id, path: project.path },
@@ -236044,7 +236375,9 @@ async function routes18(fastify2) {
236044
236375
  sourceSessionId,
236045
236376
  reviewFocus,
236046
236377
  sourceTurnEndIndex,
236047
- reviewerAgentType
236378
+ reviewerAgentType,
236379
+ reviewerSessionId,
236380
+ intentBrief
236048
236381
  });
236049
236382
  return reply.code(201).send({ run: run2 });
236050
236383
  } catch (err) {
@@ -236053,6 +236386,55 @@ async function routes18(fastify2) {
236053
236386
  throw err;
236054
236387
  }
236055
236388
  });
236389
+ fastify2.get("/api/workflow-runs/reviewer-candidate", async (req, reply) => {
236390
+ const userId = requireAuth(req, reply);
236391
+ if (userId === null) return;
236392
+ const { projectId, sourceSessionId } = req.query;
236393
+ if (!projectId || !sourceSessionId) {
236394
+ return reply.code(400).send({ error: "projectId and sourceSessionId are required" });
236395
+ }
236396
+ const project = await fastify2.storage.projects.getById(projectId, userId);
236397
+ if (!project) return reply.code(404).send({ error: "Project not found" });
236398
+ if (sourceSessionId.startsWith("remote-")) {
236399
+ const remoteInfo = fastify2.remoteSessionMap.get(sourceSessionId);
236400
+ if (!remoteInfo || projectIdFromRemoteSessionId(sourceSessionId, remoteInfo) !== projectId) {
236401
+ return reply.code(404).send({ error: "Session not found" });
236402
+ }
236403
+ const params = new URLSearchParams({ sourceSessionId: remoteInfo.remoteSessionId });
236404
+ const result = await proxyAuto(
236405
+ remoteInfo,
236406
+ "GET",
236407
+ `/api/path/workflow-runs/reviewer-candidate?${params}`
236408
+ );
236409
+ if (!result.ok) return sendProxyFailure(reply, result);
236410
+ const bareCandidate = result.data.candidate;
236411
+ const candidate2 = mapRemoteReviewerCandidate(bareCandidate, remoteInfo.remoteServerId, projectId);
236412
+ if (bareCandidate?.sessionId && candidate2?.sessionId) {
236413
+ const reviewerInfo = {
236414
+ remoteServerId: remoteInfo.remoteServerId,
236415
+ remoteUrl: remoteInfo.remoteUrl,
236416
+ remoteApiKey: remoteInfo.remoteApiKey,
236417
+ remoteSessionId: bareCandidate.sessionId,
236418
+ branch: remoteInfo.branch
236419
+ };
236420
+ fastify2.remoteSessionMap.set(candidate2.sessionId, reviewerInfo);
236421
+ await fastify2.storage.remoteSessionMappings.upsert(
236422
+ candidate2.sessionId,
236423
+ projectId,
236424
+ remoteInfo.remoteServerId,
236425
+ bareCandidate.sessionId,
236426
+ remoteInfo.branch ?? null
236427
+ );
236428
+ }
236429
+ return reply.send({ candidate: candidate2 });
236430
+ }
236431
+ const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236432
+ if (!sourceSession || sourceSession.project_id !== projectId) {
236433
+ return reply.code(404).send({ error: "Session not found" });
236434
+ }
236435
+ const candidate = await fastify2.workflowEngine.getReviewerCandidate(sourceSessionId);
236436
+ return reply.send({ candidate });
236437
+ });
236056
236438
  fastify2.get(
236057
236439
  "/api/workflow-runs",
236058
236440
  async (req, reply) => {
@@ -236173,8 +236555,21 @@ async function routes18(fastify2) {
236173
236555
  if (userId === null) return;
236174
236556
  const { sourceSessionId, reviewFocus, sourceTurnEndIndex } = req.body ?? {};
236175
236557
  if (!sourceSessionId) return reply.code(400).send({ error: "sourceSessionId is required" });
236558
+ const intentBriefRaw = req.body?.intentBrief;
236559
+ if (intentBriefRaw !== void 0 && typeof intentBriefRaw !== "string") {
236560
+ return reply.code(400).send({ error: "intentBrief must be a string" });
236561
+ }
236562
+ const intentBrief = intentBriefRaw?.trim() ? intentBriefRaw.length > 8e3 ? intentBriefRaw.slice(0, 8e3) + "\u2026" : intentBriefRaw : void 0;
236176
236563
  const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
236177
236564
  if (reviewerAgentType === null) return reply.code(400).send({ error: "reviewerAgentType must be one of: claude-code, codex" });
236565
+ const reviewerSessionIdRaw = req.body?.reviewerSessionId;
236566
+ if (reviewerSessionIdRaw !== void 0 && (typeof reviewerSessionIdRaw !== "string" || reviewerSessionIdRaw.trim() === "")) {
236567
+ return reply.code(400).send({ error: "reviewerSessionId must be a non-empty string" });
236568
+ }
236569
+ if (reviewerSessionIdRaw !== void 0 && reviewerAgentType !== void 0) {
236570
+ return reply.code(400).send({ error: "reviewerSessionId and reviewerAgentType are mutually exclusive" });
236571
+ }
236572
+ const reviewerSessionId = reviewerSessionIdRaw?.trim();
236178
236573
  const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236179
236574
  if (!sourceSession) return reply.code(404).send({ error: "Session not found" });
236180
236575
  const project = await fastify2.storage.projects.getById(sourceSession.project_id);
@@ -236187,7 +236582,9 @@ async function routes18(fastify2) {
236187
236582
  sourceSessionId,
236188
236583
  reviewFocus,
236189
236584
  sourceTurnEndIndex,
236190
- reviewerAgentType
236585
+ reviewerAgentType,
236586
+ reviewerSessionId,
236587
+ intentBrief
236191
236588
  });
236192
236589
  return reply.code(201).send({ run: run2 });
236193
236590
  } catch (err) {
@@ -236196,6 +236593,16 @@ async function routes18(fastify2) {
236196
236593
  throw err;
236197
236594
  }
236198
236595
  });
236596
+ fastify2.get("/api/path/workflow-runs/reviewer-candidate", async (req, reply) => {
236597
+ const userId = requireAuth(req, reply);
236598
+ if (userId === null) return;
236599
+ const { sourceSessionId } = req.query;
236600
+ if (!sourceSessionId) return reply.code(400).send({ error: "sourceSessionId is required" });
236601
+ const sourceSession = await fastify2.storage.agentSessions.getById(sourceSessionId);
236602
+ if (!sourceSession) return reply.code(404).send({ error: "Session not found" });
236603
+ const candidate = await fastify2.workflowEngine.getReviewerCandidate(sourceSessionId);
236604
+ return reply.send({ candidate });
236605
+ });
236199
236606
  fastify2.get("/api/path/workflow-runs", async (req, reply) => {
236200
236607
  const userId = requireAuth(req, reply);
236201
236608
  if (userId === null) return;
@@ -238506,10 +238913,10 @@ var routes27 = async (fastify2) => {
238506
238913
  if (offset >= stat2.size) {
238507
238914
  return reply.send({ content: "", truncated: false, size: stat2.size });
238508
238915
  }
238509
- const cap = Math.min(limit, MAX_OUTPUT_BYTES);
238916
+ const cap2 = Math.min(limit, MAX_OUTPUT_BYTES);
238510
238917
  handle = await fs3.open(filePath, "r");
238511
- const buffer = Buffer.alloc(cap);
238512
- const { bytesRead } = await handle.read(buffer, 0, cap, offset);
238918
+ const buffer = Buffer.alloc(cap2);
238919
+ const { bytesRead } = await handle.read(buffer, 0, cap2, offset);
238513
238920
  return reply.send({
238514
238921
  content: buffer.subarray(0, bytesRead).toString("utf8"),
238515
238922
  truncated: offset + bytesRead < stat2.size,
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.8",
4
4
  "description": "Vibedeckx platform binaries for Linux x64",
5
5
  "os": [
6
6
  "linux"