@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.
- package/dist/bin.js +440 -33
- 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({
|
|
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
|
|
223738
|
-
|
|
223739
|
-
|
|
223740
|
-
|
|
223741
|
-
|
|
223742
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
228815
|
-
throw new WorkflowError("
|
|
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
|
|
228820
|
-
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
238916
|
+
const cap2 = Math.min(limit, MAX_OUTPUT_BYTES);
|
|
238510
238917
|
handle = await fs3.open(filePath, "r");
|
|
238511
|
-
const buffer = Buffer.alloc(
|
|
238512
|
-
const { bytesRead } = await handle.read(buffer, 0,
|
|
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,
|