@vercel/factory 0.0.3 → 0.0.4

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 (70) hide show
  1. package/dist/approval-h5aj8HYN.d.mts +510 -0
  2. package/dist/blob/index.d.mts +32 -0
  3. package/dist/blob/index.mjs +141 -0
  4. package/dist/blob/index.mjs.map +1 -0
  5. package/dist/connectors/index.d.mts +2 -0
  6. package/dist/connectors/index.mjs +49 -0
  7. package/dist/connectors/index.mjs.map +1 -0
  8. package/dist/driver-Bbd25rNM.mjs +23 -0
  9. package/dist/driver-Bbd25rNM.mjs.map +1 -0
  10. package/dist/driver-CrZsy7sm.d.mts +34 -0
  11. package/dist/eve/index.d.mts +1057 -0
  12. package/dist/eve/index.mjs +777 -0
  13. package/dist/eve/index.mjs.map +1 -0
  14. package/dist/github-5qQPCaXj.d.mts +123 -0
  15. package/dist/github.d.mts +2 -0
  16. package/dist/github.mjs +286 -0
  17. package/dist/github.mjs.map +1 -0
  18. package/dist/id-DwCihQr4.mjs +31 -0
  19. package/dist/id-DwCihQr4.mjs.map +1 -0
  20. package/dist/index-WNGZJRRH.d.mts +67 -0
  21. package/dist/index.d.mts +175 -0
  22. package/dist/index.mjs +882 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/next-page.d.mts +5 -0
  25. package/dist/next-page.mjs +71 -0
  26. package/dist/next-page.mjs.map +1 -0
  27. package/dist/next.d.mts +12 -0
  28. package/dist/{next.js → next.mjs} +13 -14
  29. package/dist/next.mjs.map +1 -0
  30. package/dist/postgres/index.d.mts +137 -0
  31. package/dist/postgres/index.mjs +96 -0
  32. package/dist/postgres/index.mjs.map +1 -0
  33. package/dist/signal-CHVEmzvI.d.mts +59 -0
  34. package/dist/task-Cj7Ei9Nh.mjs +336 -0
  35. package/dist/task-Cj7Ei9Nh.mjs.map +1 -0
  36. package/package.json +25 -22
  37. package/dist/approval-Z7iaWibT.d.ts +0 -541
  38. package/dist/blob/index.d.ts +0 -26
  39. package/dist/blob/index.js +0 -127
  40. package/dist/blob/index.js.map +0 -1
  41. package/dist/chunk-2JFES6BA.js +0 -41
  42. package/dist/chunk-2JFES6BA.js.map +0 -1
  43. package/dist/chunk-CEF6VJZC.js +0 -28
  44. package/dist/chunk-CEF6VJZC.js.map +0 -1
  45. package/dist/chunk-D2XMABG4.js +0 -311
  46. package/dist/chunk-D2XMABG4.js.map +0 -1
  47. package/dist/chunk-EFL4PDDE.js +0 -325
  48. package/dist/chunk-EFL4PDDE.js.map +0 -1
  49. package/dist/common-CaHaEOAS.d.ts +0 -18
  50. package/dist/connectors/index.d.ts +0 -53
  51. package/dist/connectors/index.js +0 -64
  52. package/dist/connectors/index.js.map +0 -1
  53. package/dist/driver-tRNggCCJ.d.ts +0 -32
  54. package/dist/eve/index.d.ts +0 -1053
  55. package/dist/eve/index.js +0 -752
  56. package/dist/eve/index.js.map +0 -1
  57. package/dist/github.d.ts +0 -139
  58. package/dist/github.js +0 -27
  59. package/dist/github.js.map +0 -1
  60. package/dist/index.d.ts +0 -164
  61. package/dist/index.js +0 -940
  62. package/dist/index.js.map +0 -1
  63. package/dist/next-page.d.ts +0 -5
  64. package/dist/next-page.js +0 -75
  65. package/dist/next-page.js.map +0 -1
  66. package/dist/next.d.ts +0 -12
  67. package/dist/next.js.map +0 -1
  68. package/dist/postgres/index.d.ts +0 -137
  69. package/dist/postgres/index.js +0 -118
  70. package/dist/postgres/index.js.map +0 -1
package/dist/eve/index.js DELETED
@@ -1,752 +0,0 @@
1
- import {
2
- answerReviewComment,
3
- readReviewComment
4
- } from "../chunk-D2XMABG4.js";
5
- import {
6
- changeEvidenceSchema,
7
- defaultApprovalPolicy,
8
- dispatchTask,
9
- findingFingerprint,
10
- findingSchema,
11
- levelSchema,
12
- requiresApproval,
13
- taskQuestionSchema
14
- } from "../chunk-EFL4PDDE.js";
15
- import {
16
- changeIdSchema,
17
- repositoryIdSchema,
18
- signalIdSchema,
19
- taskIdSchema,
20
- vocabularySchema
21
- } from "../chunk-2JFES6BA.js";
22
- import "../chunk-CEF6VJZC.js";
23
-
24
- // src/eve/index.ts
25
- import { defineHook } from "eve/hooks";
26
- import { defineTool as defineTool8 } from "eve/tools";
27
- import { z as z8 } from "zod";
28
-
29
- // src/eve/conflict.ts
30
- import { z } from "zod";
31
- import { defineTool } from "eve/tools";
32
- var gitShaSchema = z.string().regex(/^[0-9a-f]{40}$/);
33
- var gitBranchSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/).refine((value) => !value.includes(".."));
34
- var mergeConflictPayloadSchema = z.strictObject({
35
- repository: z.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/),
36
- prNumber: z.number().int().positive(),
37
- prUrl: z.url(),
38
- headBranch: gitBranchSchema.refine((value) => value.startsWith("factory/")),
39
- headSha: gitShaSchema,
40
- baseBranch: gitBranchSchema,
41
- baseSha: gitShaSchema
42
- });
43
- function mergeConflictTools(stores, options) {
44
- const repoPath = options.repoPath ?? "repo";
45
- async function context(taskId) {
46
- const task = await stores.tasks.get(taskId);
47
- if (task === null) throw new Error(`task ${taskId} does not exist`);
48
- if (task.kind !== "merge_conflict") throw new Error(`task ${taskId} is ${task.kind}, not merge_conflict`);
49
- if (task.state !== "running") throw new Error(`task ${taskId} is ${task.state}, not running`);
50
- if (task.origin.signalId === void 0) throw new Error(`task ${taskId} has no conflict signal`);
51
- const signal = await stores.signals.get(task.origin.signalId);
52
- if (signal === null || signal.source !== "github_merge_conflict") {
53
- throw new Error(`task ${taskId} has no GitHub merge conflict signal`);
54
- }
55
- return { task, conflict: mergeConflictPayloadSchema.parse(signal.payload) };
56
- }
57
- return {
58
- fetch_merge_conflict: defineTool({
59
- description: "Fetch the conflicted factory pull request and its current base, check out the PR head, and start the merge. Resolve every file returned in conflictedFiles, then test and commit the merge.",
60
- inputSchema: z.object({ taskId: taskIdSchema }),
61
- async execute({ taskId }, ctx) {
62
- const { conflict } = await context(taskId);
63
- const sandbox = await ctx.getSandbox();
64
- const git = `git -C ${repoPath}`;
65
- const headRef = `refs/remotes/origin/conflict-pr-${conflict.prNumber}`;
66
- const baseRef = `refs/remotes/origin/conflict-base-${conflict.prNumber}`;
67
- const fetch2 = await options.withCredential(
68
- sandbox,
69
- conflict.repository,
70
- `${git} fetch --depth 100 origin +refs/pull/${conflict.prNumber}/head:${headRef} +refs/heads/${conflict.baseBranch}:${baseRef}`
71
- );
72
- if (typeof fetch2.exitCode === "number" && fetch2.exitCode !== 0) {
73
- throw new Error(`could not fetch pull request: ${(fetch2.stderr ?? "").trim()}`);
74
- }
75
- const fetched = await sandbox.run({
76
- command: `${git} rev-parse ${headRef} && ${git} rev-parse ${baseRef}`
77
- });
78
- const [headSha, baseSha] = fetched.stdout.trim().split("\n");
79
- if (headSha !== conflict.headSha || baseSha !== conflict.baseSha) {
80
- throw new Error("pull request or base changed while conflict resolution was starting; retry from a new task");
81
- }
82
- await sandbox.run({ command: `${git} checkout -B conflict-pr ${headRef}` });
83
- const merge = await sandbox.run({ command: `${git} merge --no-commit --no-ff ${baseRef}` });
84
- const conflicts = await sandbox.run({ command: `${git} diff --name-only --diff-filter=U` });
85
- const conflictedFiles = conflicts.stdout.trim().split("\n").filter(Boolean);
86
- if (conflictedFiles.length === 0) {
87
- throw new Error(
88
- typeof merge.exitCode === "number" && merge.exitCode !== 0 ? `merge failed without file conflicts: ${(merge.stderr ?? "").trim()}` : "pull request no longer has merge conflicts"
89
- );
90
- }
91
- return { ...conflict, conflictedFiles };
92
- }
93
- }),
94
- publish_merge_resolution: defineTool({
95
- description: "Publish a tested, committed conflict resolution to the existing pull request branch. Fails if the remote branch changed since this task started.",
96
- inputSchema: z.object({
97
- taskId: taskIdSchema,
98
- summary: z.string().min(1).max(2e3)
99
- }),
100
- async execute({ taskId, summary }, ctx) {
101
- const { conflict } = await context(taskId);
102
- const sandbox = await ctx.getSandbox();
103
- const git = `git -C ${repoPath}`;
104
- const clean = await sandbox.run({ command: `${git} status --porcelain` });
105
- if (clean.stdout.trim() !== "") throw new Error("commit the conflict resolution before publishing");
106
- const unresolved = await sandbox.run({ command: `${git} diff --name-only --diff-filter=U` });
107
- if (unresolved.stdout.trim() !== "") throw new Error("merge conflicts remain unresolved");
108
- const push = await options.withCredential(
109
- sandbox,
110
- conflict.repository,
111
- `${git} push --force-with-lease=refs/heads/${conflict.headBranch}:${conflict.headSha} origin HEAD:refs/heads/${conflict.headBranch}`
112
- );
113
- if (typeof push.exitCode === "number" && push.exitCode !== 0) {
114
- throw new Error(`could not update pull request branch: ${(push.stderr ?? "").trim()}`);
115
- }
116
- await stores.tasks.transition(taskId, "verifying", { reason: summary });
117
- await stores.tasks.transition(taskId, "succeeded", { reason: summary });
118
- return { updated: true, prNumber: conflict.prNumber, prUrl: conflict.prUrl };
119
- }
120
- })
121
- };
122
- }
123
-
124
- // src/eve/deepsec.ts
125
- import { defineTool as defineTool3 } from "eve/tools";
126
- import { z as z3 } from "zod";
127
-
128
- // src/eve/finding.ts
129
- import { defineTool as defineTool2 } from "eve/tools";
130
- import { z as z2 } from "zod";
131
- async function recordFinding(stores, repositoryId, finding, options = {}) {
132
- const source = options.source ?? "security_scan";
133
- const policy = options.approval ?? defaultApprovalPolicy;
134
- const repository = await stores.repositories.get(repositoryId);
135
- if (repository === null) {
136
- return { recorded: false, reason: `repository ${repositoryId} does not exist` };
137
- }
138
- const fingerprint = findingFingerprint(repositoryId, finding);
139
- const signal = await stores.signals.create({
140
- repositoryId,
141
- source,
142
- deliveryId: fingerprint,
143
- replyTo: { channel: "finding", address: fingerprint },
144
- payload: finding
145
- });
146
- if (signal.outcome !== "pending") {
147
- return { recorded: false, duplicate: true, signalId: signal.id, reason: `already recorded as ${signal.id}` };
148
- }
149
- await stores.signals.decide(signal.id, "accepted", {
150
- detail: finding.priority,
151
- reason: `${finding.tool ?? "scanner"} reported it, ${finding.verification.level} by ${finding.verification.method}`
152
- });
153
- const approval = requiresApproval(policy, { source, kind: "code_change", priority: finding.priority });
154
- const task = await stores.tasks.create({
155
- repositoryIds: [repositoryId],
156
- kind: "code_change",
157
- origin: { signalId: signal.id },
158
- replyTo: signal.replyTo,
159
- ...approval ? { approval: "required" } : {}
160
- });
161
- return { recorded: true, signalId: signal.id, taskId: task.id, approval: approval ? "required" : "not_required" };
162
- }
163
- function recordFindingTool(stores, options = {}) {
164
- return defineTool2({
165
- description: "Record one problem you found in the repository. Creates a fix task (which may wait for human approval) unless the same finding was already recorded. Report every finding separately; never batch.",
166
- inputSchema: z2.object({
167
- repositoryId: repositoryIdSchema,
168
- finding: findingSchema
169
- }),
170
- execute({ repositoryId, finding }) {
171
- return recordFinding(stores, repositoryId, finding, options);
172
- }
173
- });
174
- }
175
-
176
- // src/eve/deepsec.ts
177
- var deepsecFindingSchema = z3.looseObject({
178
- title: z3.string().min(1),
179
- description: z3.string().min(1),
180
- severity: z3.string().min(1),
181
- metadata: z3.looseObject({
182
- filePath: z3.string().min(1).optional(),
183
- lineNumbers: z3.array(z3.number().int().min(1)).optional(),
184
- vulnSlug: z3.string().min(1).optional(),
185
- confidence: z3.string().optional(),
186
- runId: z3.string().optional()
187
- }).optional()
188
- });
189
- var deepsecExportSchema = z3.union([
190
- z3.array(deepsecFindingSchema),
191
- z3.looseObject({ findings: z3.array(deepsecFindingSchema) }).transform((value) => value.findings)
192
- ]);
193
- var priorityBySeverity = {
194
- CRITICAL: "P0",
195
- HIGH: "P1",
196
- MEDIUM: "P2",
197
- HIGH_BUG: "P2",
198
- BUG: "P3",
199
- LOW: "P3"
200
- };
201
- function findingFromDeepsec(raw) {
202
- const meta = raw.metadata ?? {};
203
- const confidence = (meta.confidence ?? "").toLowerCase();
204
- const line = meta.lineNumbers?.[0];
205
- const endLine = meta.lineNumbers === void 0 ? void 0 : Math.max(...meta.lineNumbers);
206
- return findingSchema.parse({
207
- title: raw.title.replace(/^\[[A-Z_]+\]\s*/u, "").slice(0, 200),
208
- body: raw.description.slice(0, 8e3),
209
- ...meta.filePath === void 0 ? {} : { path: meta.filePath.slice(0, 500) },
210
- ...line === void 0 ? {} : { line },
211
- ...endLine === void 0 || endLine === line ? {} : { endLine },
212
- priority: priorityBySeverity[raw.severity.toUpperCase()] ?? "P3",
213
- category: "security",
214
- verification: {
215
- // deepsec's agent reads the code; it does not run it. Say so.
216
- level: confidence === "high" ? "supported" : "unverified",
217
- method: "static_analysis",
218
- summary: `deepsec ${meta.vulnSlug ?? "finding"} with ${confidence || "unknown"} confidence${meta.runId === void 0 ? "" : ` (run ${meta.runId})`}.`,
219
- limitations: "AI-assisted static analysis of the checkout; not reproduced at runtime."
220
- },
221
- tool: "deepsec",
222
- ...meta.vulnSlug === void 0 ? {} : { ruleId: meta.vulnSlug }
223
- });
224
- }
225
- var findingsPath = "/tmp/deepsec-findings.json";
226
- function candidateCount(output) {
227
- const match = /(\d+)\s+file\(s\) with candidates/u.exec(output);
228
- return match?.[1] === void 0 ? null : Number(match[1]);
229
- }
230
- function tail(output, lines = 4) {
231
- const ansi = /\u001b\[[0-9;]*m/gu;
232
- return output.replaceAll(ansi, "").split("\n").map((line) => line.trim()).filter((line) => line !== "").slice(-lines).join(" | ").slice(0, 600);
233
- }
234
- function deepsecScanTool(stores, options) {
235
- const repoPath = options.repoPath ?? "repo";
236
- const workspace = options.workspacePath ?? "deepsec";
237
- const version = options.version ?? "2.3.8";
238
- const agent = options.agent ?? "claude";
239
- const model = options.model ?? "claude-sonnet-4-6";
240
- const thinkingLevel = options.thinkingLevel ?? "medium";
241
- const concurrency = options.concurrency ?? 4;
242
- const prefix = options.commandPrefix === void 0 ? [] : [options.commandPrefix];
243
- const filesPerRun = options.filesPerRun ?? 40;
244
- return defineTool3({
245
- description: "Run deepsec, the AI-powered vulnerability scanner, over the repository checkout and record every finding it reports as a fix task. Takes minutes. Call once per scan; then summarize the result.",
246
- inputSchema: z3.object({
247
- repositoryId: repositoryIdSchema,
248
- limit: z3.number().int().min(1).max(500).optional().describe("Candidate files to investigate this run (default 40; deepsec resumes where it left off next time).")
249
- }),
250
- async execute({ repositoryId, limit }, ctx) {
251
- const repository = await stores.repositories.get(repositoryId);
252
- if (repository === null) {
253
- return { scanned: false, reason: `repository ${repositoryId} does not exist` };
254
- }
255
- const sandbox = await ctx.getSandbox();
256
- const projectId = repository.slug.replace(/[^a-z0-9]+/gu, "-");
257
- const startedAt = Date.now();
258
- const phase = async (state, reason) => {
259
- await stores.receipts.append({
260
- repositoryIds: [repositoryId],
261
- kind: "deepsec",
262
- state,
263
- phase: "security_scan",
264
- reason: `${reason} (${Math.round((Date.now() - startedAt) / 1e3)}s in)`
265
- });
266
- };
267
- await phase("started", `deepsec ${version} on ${repository.slug}, ${agent}/${model}`);
268
- const prepare = await sandbox.run({
269
- command: [
270
- ...prefix,
271
- "set -e",
272
- `if [ ! -f ${workspace}/deepsec.config.ts ]; then`,
273
- ` npx -y deepsec@${version} init ./${workspace} ./${repoPath} --id ${projectId} --scaffold-only --headless --yes --skip-install`,
274
- ` (cd ${workspace} && npm install --no-audit --no-fund --loglevel=error)`,
275
- "fi",
276
- // deepsec resolves deepsec.config.ts from the current directory, so
277
- // every deepsec command runs inside the workspace. No --root: it is a
278
- // one-off override that skips the project's file records, leaving
279
- // `process` with nothing to investigate.
280
- `cd ${workspace}`,
281
- `./node_modules/.bin/deepsec scan --project-id ${projectId}`
282
- ].join("\n")
283
- });
284
- if (typeof prepare.exitCode === "number" && prepare.exitCode !== 0) {
285
- const detail = (prepare.stderr ?? prepare.stdout).trim().slice(-1500);
286
- await phase("failed", `scan phase failed: ${detail.slice(-300)}`);
287
- return { scanned: false, reason: `deepsec scan failed: ${detail}` };
288
- }
289
- const candidates = candidateCount(prepare.stdout);
290
- await phase(
291
- "scanned",
292
- `${candidates === null ? "scan output" : `${candidates} files with candidates`}: ${tail(`${prepare.stdout}
293
- ${prepare.stderr ?? ""}`)}`
294
- );
295
- const investigate = await options.withModelCredential(
296
- sandbox,
297
- [
298
- ...prefix,
299
- "set -e",
300
- `cd ${workspace}`,
301
- `./node_modules/.bin/deepsec process --project-id ${projectId} --agent ${agent} --model ${model} --thinking-level ${thinkingLevel} --concurrency ${concurrency} --limit ${limit ?? filesPerRun}`,
302
- `./node_modules/.bin/deepsec export --project-id ${projectId} --format json --out ${findingsPath}`
303
- ].join("\n")
304
- );
305
- if (typeof investigate.exitCode === "number" && investigate.exitCode !== 0) {
306
- const detail = (investigate.stderr ?? investigate.stdout).trim().slice(-1500);
307
- await phase("failed", `process phase failed: ${detail.slice(-300)}`);
308
- return { scanned: false, reason: `deepsec process failed: ${detail}` };
309
- }
310
- const exported = await sandbox.run({ command: `cat ${findingsPath}` });
311
- let parsed;
312
- try {
313
- parsed = JSON.parse(exported.stdout);
314
- } catch {
315
- return { scanned: false, reason: "deepsec export was not valid JSON" };
316
- }
317
- const raw = deepsecExportSchema.safeParse(parsed);
318
- if (!raw.success) {
319
- return { scanned: false, reason: `deepsec export had an unexpected shape: ${raw.error.issues[0]?.message ?? "unknown"}` };
320
- }
321
- const results = [];
322
- for (const entry of raw.data) {
323
- const finding = findingFromDeepsec(entry);
324
- results.push({ finding, result: await recordFinding(stores, repositoryId, finding, options) });
325
- }
326
- const recorded = results.filter(({ result }) => result.recorded);
327
- await phase(
328
- "completed",
329
- `${results.length} findings, ${recorded.length} new. process output: ${tail(`${investigate.stdout}
330
- ${investigate.stderr ?? ""}`, 6)}`
331
- );
332
- return {
333
- scanned: true,
334
- tool: "deepsec",
335
- found: results.length,
336
- recorded: recorded.length,
337
- duplicates: results.length - recorded.length,
338
- awaitingApproval: recorded.filter(({ result }) => result.recorded && result.approval === "required").length,
339
- findings: results.map(({ finding, result }) => ({
340
- title: finding.title,
341
- priority: finding.priority,
342
- path: finding.path,
343
- line: finding.line,
344
- ...result.recorded ? { taskId: result.taskId, approval: result.approval } : { duplicate: true }
345
- }))
346
- };
347
- }
348
- });
349
- }
350
-
351
- // src/eve/fetch.ts
352
- import { defineTool as defineTool4 } from "eve/tools";
353
- import { z as z4 } from "zod";
354
- function fetchPullRequestTool(stores, options) {
355
- const repoPath = options.repoPath ?? "repo";
356
- const depth = options.depth ?? 50;
357
- return defineTool4({
358
- description: "Fetch the pull request under review into the checkout and check it out as the local branch `pr`. Call this after get_task, before inspecting or testing the change. Returns the PR number, URL, and the commit you are now on.",
359
- inputSchema: z4.object({
360
- changeId: changeIdSchema.describe("The change under review, from your starting message.")
361
- }),
362
- async execute({ changeId }, ctx) {
363
- const change = await stores.changes.get(changeId);
364
- if (change === null) {
365
- return { fetched: false, reason: `change ${changeId} does not exist` };
366
- }
367
- if (change.state !== "verifying") {
368
- return { fetched: false, reason: `change is ${change.state}, not verifying; nothing to fetch` };
369
- }
370
- const repository = await stores.repositories.get(change.repositoryId);
371
- if (repository === null) {
372
- return { fetched: false, reason: `repository ${change.repositoryId} does not exist` };
373
- }
374
- const sandbox = await ctx.getSandbox();
375
- const tracking = `refs/remotes/origin/pr-${change.prNumber}`;
376
- const git = `git -C ${repoPath}`;
377
- const result = await options.withCredential(
378
- sandbox,
379
- repository.slug,
380
- `${git} fetch --depth ${depth} origin +refs/pull/${change.prNumber}/head:${tracking} && ${git} checkout -B pr ${tracking}`
381
- );
382
- if (typeof result.exitCode === "number" && result.exitCode !== 0) {
383
- return {
384
- fetched: false,
385
- reason: `git fetch of pull request ${change.prNumber} failed: ${(result.stderr ?? "").trim()}`
386
- };
387
- }
388
- const head = await sandbox.run({ command: `${git} rev-parse HEAD` });
389
- return {
390
- fetched: true,
391
- prNumber: change.prNumber,
392
- prUrl: change.prUrl,
393
- branch: "pr",
394
- headSha: head.stdout.trim()
395
- };
396
- }
397
- });
398
- }
399
-
400
- // src/eve/invoke.ts
401
- import { defineTool as defineTool5 } from "eve/tools";
402
- import { z as z5 } from "zod";
403
- function invokeAgentTool(stores, options) {
404
- const doFetch = options.fetch ?? fetch;
405
- return defineTool5({
406
- description: `${options.description} Hands the work to a separate agent in a fresh session; you will not see its reasoning. Returns the handoff task id.`,
407
- inputSchema: z5.object({
408
- repositoryIds: z5.array(repositoryIdSchema).min(1),
409
- parentTaskId: taskIdSchema.optional().describe("Your own task, when this work continues it."),
410
- changeId: changeIdSchema.optional().describe("The change the target agent should work on."),
411
- channel: z5.string().min(1).describe("Reply connector, e.g. github."),
412
- address: z5.string().min(1).describe("Reply address, e.g. acme/web#41."),
413
- brief: z5.string().min(1).max(4e3).describe("What the target agent needs to know. Facts and references only, not your reasoning.")
414
- }),
415
- async execute(input) {
416
- const task = await stores.tasks.create({
417
- repositoryIds: input.repositoryIds,
418
- kind: options.kind,
419
- origin: input.parentTaskId === void 0 ? { operator: "factory" } : { parentTaskId: input.parentTaskId },
420
- replyTo: { channel: input.channel, address: input.address }
421
- });
422
- const running = await dispatchTask(stores, task.id, {
423
- launch: async (dispatched) => {
424
- const headers = {
425
- "content-type": "application/json",
426
- ...await options.headers?.()
427
- };
428
- const token = await options.token?.();
429
- if (token !== void 0) {
430
- headers.authorization = `Bearer ${token}`;
431
- }
432
- const message = [
433
- `You are assigned factory task ${dispatched.id}.`,
434
- input.changeId === void 0 ? null : `Change under review: ${input.changeId}.`,
435
- `Reply address: ${input.channel} ${input.address}.`,
436
- "",
437
- input.brief,
438
- "",
439
- "Read the task with get_task first, do the work, and record the outcome with your factory tools."
440
- ].filter((line) => line !== null).join("\n");
441
- const response = await doFetch(`${options.targetUrl()}/eve/v1/session`, {
442
- method: "POST",
443
- headers,
444
- body: JSON.stringify({ message, operationId: `factory-task:${dispatched.id}` })
445
- });
446
- if (!response.ok) {
447
- throw new Error(`target agent refused the session: ${response.status}`);
448
- }
449
- }
450
- });
451
- return { taskId: running.id, kind: running.kind, state: running.state };
452
- }
453
- });
454
- }
455
-
456
- // src/eve/review.ts
457
- import { defineTool as defineTool6 } from "eve/tools";
458
- import { z as z6 } from "zod";
459
- function reviewCommentTools(stores, options) {
460
- const commentInputSchema = z6.object({
461
- changeId: changeIdSchema.describe("The change under review, from your starting message."),
462
- commentId: z6.number().int().positive().describe("The GitHub review comment id.")
463
- });
464
- async function context(changeId) {
465
- const change = await stores.changes.get(changeId);
466
- if (change === null) {
467
- throw new Error(`change ${changeId} does not exist`);
468
- }
469
- if (change.state !== "verifying") {
470
- throw new Error(`change is ${change.state}, not verifying`);
471
- }
472
- const repository = await stores.repositories.get(change.repositoryId);
473
- if (repository === null) {
474
- throw new Error(`repository ${change.repositoryId} does not exist`);
475
- }
476
- return {
477
- repository: repository.slug,
478
- token: await options.tokenForRepository(repository.slug),
479
- fetch: options.fetch,
480
- pullRequestNumber: change.prNumber
481
- };
482
- }
483
- return {
484
- read_review_comment: defineTool6({
485
- description: "Read an inline review comment on the change under verification. By default this refuses to return comments from people without write access to the repository.",
486
- inputSchema: commentInputSchema,
487
- async execute({ changeId, commentId }) {
488
- return readReviewComment({ ...await context(changeId), commentId });
489
- }
490
- }),
491
- answer_review_comment: defineTool6({
492
- description: "Reply to an inline review comment on the change under verification. The repository and pull request come from the ledger, and the comment author must have write access.",
493
- inputSchema: commentInputSchema.extend({
494
- body: z6.string().min(1).max(5e3).describe("The Markdown reply to post.")
495
- }),
496
- async execute({ changeId, commentId, body }) {
497
- return answerReviewComment({ ...await context(changeId), commentId, body });
498
- }
499
- })
500
- };
501
- }
502
-
503
- // src/eve/verification.ts
504
- import { defineTool as defineTool7 } from "eve/tools";
505
- import { z as z7 } from "zod";
506
- function recordVerificationTool(stores) {
507
- return defineTool7({
508
- description: "Record the outcome of a verification task: the evidence you gathered and whether the change passed. Pass moves the change to ready for human review; fail keeps it out of review.",
509
- inputSchema: z7.object({
510
- taskId: taskIdSchema.describe("Your verification task."),
511
- changeId: changeIdSchema.describe("The change you verified."),
512
- verdict: z7.enum(["pass", "fail"]),
513
- evidence: changeEvidenceSchema,
514
- confidence: levelSchema.describe("How certain you are in the verdict."),
515
- summary: z7.string().min(1).max(2e3).describe("What you checked and what you found.")
516
- }),
517
- async execute({ taskId, changeId, verdict, evidence, confidence, summary }) {
518
- const change = await stores.changes.get(changeId);
519
- if (change === null) {
520
- return { recorded: false, reason: `change ${changeId} does not exist` };
521
- }
522
- if (change.state !== "verifying") {
523
- return {
524
- recorded: false,
525
- reason: `change is ${change.state}, not verifying; nothing to record`
526
- };
527
- }
528
- const task = await stores.tasks.get(taskId);
529
- if (task === null || task.kind !== "verification") {
530
- return { recorded: false, reason: `${taskId} is not a verification task` };
531
- }
532
- let updatedChange = change;
533
- if (verdict === "pass") {
534
- updatedChange = await stores.changes.transition(changeId, "ready", {
535
- patch: { evidence, confidence },
536
- reason: summary
537
- });
538
- }
539
- let finished;
540
- if (verdict === "pass") {
541
- await stores.tasks.transition(taskId, "verifying", { reason: summary });
542
- finished = await stores.tasks.transition(taskId, "succeeded");
543
- } else {
544
- finished = await stores.tasks.transition(taskId, "failed", { reason: summary });
545
- }
546
- if (change.taskId !== void 0) {
547
- const parent = await stores.tasks.get(change.taskId);
548
- if (parent?.state === "verifying") {
549
- await stores.tasks.transition(
550
- change.taskId,
551
- verdict === "pass" ? "succeeded" : "failed",
552
- verdict === "pass" ? {} : { reason: `verification failed: ${summary}` }
553
- ).catch(() => void 0);
554
- }
555
- }
556
- return {
557
- recorded: true,
558
- verdict,
559
- changeState: updatedChange.state,
560
- taskState: finished.state
561
- };
562
- }
563
- });
564
- }
565
-
566
- // src/eve/index.ts
567
- var decidedOutcomeSchema = z8.enum([
568
- "accepted",
569
- "duplicate",
570
- "unactionable",
571
- "low_confidence",
572
- "ignored"
573
- ]);
574
- var recordChangeInputSchema = z8.object({
575
- taskId: taskIdSchema,
576
- repositoryId: repositoryIdSchema,
577
- prNumber: z8.number().int().min(1),
578
- prUrl: z8.url(),
579
- risk: levelSchema.describe("Blast radius of the change if it is wrong."),
580
- confidence: levelSchema.describe("How certain you are the change is correct."),
581
- summary: z8.string().min(1).describe("One-sentence description of the change.")
582
- });
583
- function factoryTools(stores, options = {}) {
584
- const recordChangeGate = options.recordChangeGate;
585
- return {
586
- get_task: defineTool8({
587
- description: "Read one factory task by id: its kind, state, repositories, origin, and results.",
588
- inputSchema: z8.object({ taskId: taskIdSchema }),
589
- async execute({ taskId }) {
590
- const task = await stores.tasks.get(taskId);
591
- return task ?? { found: false, taskId };
592
- }
593
- }),
594
- decide_signal: defineTool8({
595
- description: "Record the triage decision for a pending signal. A signal is decided exactly once.",
596
- inputSchema: z8.object({
597
- signalId: signalIdSchema,
598
- outcome: decidedOutcomeSchema,
599
- reason: z8.string().min(1).describe("Why this outcome was chosen."),
600
- detail: z8.string().min(1).optional().describe("Severity if accepted, or the duplicate-of signal id.")
601
- }),
602
- execute({ signalId, outcome, reason, detail }) {
603
- return stores.signals.decide(signalId, outcome, { reason, detail });
604
- }
605
- }),
606
- record_change: defineTool8({
607
- description: "Record the pull request produced for a code_change task. Creates the change, links it to the task, and moves the task to verifying.",
608
- inputSchema: recordChangeInputSchema,
609
- ...recordChangeGate === void 0 ? {} : {
610
- approval: async ({ toolInput }) => {
611
- const parsed = recordChangeInputSchema.safeParse(toolInput);
612
- if (!parsed.success) {
613
- return {
614
- type: "denied",
615
- reason: "record_change input is malformed; fix the arguments and retry"
616
- };
617
- }
618
- const verdict = await recordChangeGate(parsed.data);
619
- return verdict.ok ? "not-applicable" : { type: "denied", reason: verdict.reason };
620
- }
621
- },
622
- async execute({ taskId, repositoryId, prNumber, prUrl, risk, confidence, summary }) {
623
- const change = await stores.changes.create({
624
- repositoryId,
625
- taskId,
626
- prNumber,
627
- prUrl,
628
- author: "factory",
629
- risk,
630
- confidence,
631
- evidence: {
632
- reproVerified: false,
633
- testsAdded: 0,
634
- checksGreen: false,
635
- attackPassed: false,
636
- scopeMatches: false
637
- }
638
- });
639
- const task = await stores.tasks.transition(taskId, "verifying", {
640
- patch: { changeId: change.id },
641
- reason: summary
642
- });
643
- return { change, task };
644
- }
645
- }),
646
- finish_task: defineTool8({
647
- description: "Mark your task finished with a summary, for task kinds that have no other outcome tool (for example a security_scan). Not for code_change or verification tasks: those end with record_change and record_verification.",
648
- inputSchema: z8.object({
649
- taskId: taskIdSchema,
650
- summary: z8.string().min(1).max(4e3).describe("What you did and found, for the ledger.")
651
- }),
652
- async execute({ taskId, summary }) {
653
- const task = await stores.tasks.get(taskId);
654
- if (task === null) {
655
- return { finished: false, reason: `${taskId} does not exist` };
656
- }
657
- if (task.kind === "code_change" || task.kind === "verification") {
658
- return { finished: false, reason: `${task.kind} tasks end with their own outcome tool` };
659
- }
660
- if (task.state !== "running") {
661
- return { finished: false, reason: `task is ${task.state}, not running` };
662
- }
663
- await stores.tasks.transition(taskId, "verifying", { reason: summary });
664
- const finished = await stores.tasks.transition(taskId, "succeeded", { reason: summary });
665
- return { finished: true, taskState: finished.state };
666
- }
667
- }),
668
- report_blocked: defineTool8({
669
- description: "Mark a running task as needing a human because the work cannot continue without help.",
670
- inputSchema: z8.object({
671
- taskId: taskIdSchema,
672
- reason: z8.string().min(1).describe("What is blocking, written for the human.")
673
- }),
674
- execute({ taskId, reason }) {
675
- return stores.tasks.transition(taskId, "needs_human", { reason });
676
- }
677
- }),
678
- ask_question: defineTool8({
679
- description: "Raise a question a human operator must answer. Creates a question task that waits for the answer.",
680
- inputSchema: z8.object({
681
- repositoryIds: z8.array(repositoryIdSchema).min(1),
682
- parentTaskId: taskIdSchema.optional().describe("Task the answer unblocks, when there is one."),
683
- channel: z8.string().min(1).describe("Reply connector, e.g. github."),
684
- address: z8.string().min(1).describe("Reply address, e.g. acme/web#41."),
685
- question: taskQuestionSchema.omit({ answer: true, answeredBy: true })
686
- }),
687
- async execute({ repositoryIds, parentTaskId, channel, address, question }) {
688
- const task = await stores.tasks.create({
689
- repositoryIds,
690
- kind: "question",
691
- origin: parentTaskId === void 0 ? { operator: "factory" } : { parentTaskId },
692
- replyTo: { channel, address },
693
- question
694
- });
695
- return stores.tasks.transition(task.id, "needs_human");
696
- }
697
- }),
698
- append_receipt: defineTool8({
699
- description: "Append one immutable audit receipt for an event worth remembering. Receipts can never be edited or deleted.",
700
- inputSchema: z8.object({
701
- repositoryIds: z8.array(repositoryIdSchema).min(1),
702
- taskId: taskIdSchema.optional(),
703
- changeId: changeIdSchema.optional(),
704
- kind: vocabularySchema.describe("What the receipt is about, e.g. investigation."),
705
- state: vocabularySchema.describe("What happened, e.g. flag_ramped."),
706
- reason: z8.string().min(1).max(2e3).optional(),
707
- dedupeKey: z8.string().min(1).optional().describe("Same key twice records one receipt instead of two.")
708
- }),
709
- execute(input) {
710
- return stores.receipts.append(input);
711
- }
712
- })
713
- };
714
- }
715
- function factoryHooks(stores) {
716
- return {
717
- factory: defineHook({
718
- events: {
719
- async "turn.completed"(_event, ctx) {
720
- const address = ctx.channel.continuationToken;
721
- if (address === void 0) {
722
- return;
723
- }
724
- const tasks = await stores.tasks.list();
725
- const abandoned = tasks.filter(
726
- (task) => task.state === "running" && task.replyTo.address === address
727
- );
728
- for (const task of abandoned) {
729
- await stores.tasks.transition(task.id, "needs_human", {
730
- reason: "agent turn ended without recording an outcome"
731
- }).catch(() => void 0);
732
- }
733
- }
734
- }
735
- })
736
- };
737
- }
738
- export {
739
- deepsecScanTool,
740
- factoryHooks,
741
- factoryTools,
742
- fetchPullRequestTool,
743
- findingFromDeepsec,
744
- invokeAgentTool,
745
- mergeConflictPayloadSchema,
746
- mergeConflictTools,
747
- recordFinding,
748
- recordFindingTool,
749
- recordVerificationTool,
750
- reviewCommentTools
751
- };
752
- //# sourceMappingURL=index.js.map