@vercel/factory 0.0.13 → 0.0.14
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/README.md +1 -1
- package/dist/api.d.mts +225 -0
- package/dist/api.mjs +312 -0
- package/dist/api.mjs.map +1 -0
- package/dist/approval-JX2Px9IC.mjs +132 -0
- package/dist/approval-JX2Px9IC.mjs.map +1 -0
- package/dist/blob/index.d.mts +4 -3
- package/dist/blob/index.mjs +21 -15
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget-DzSiIA4t.mjs +42 -0
- package/dist/budget-DzSiIA4t.mjs.map +1 -0
- package/dist/build-factory.mjs +1 -1
- package/dist/{change-CzEAoFVE.mjs → change-D2U6v7Ek.mjs} +10 -76
- package/dist/change-D2U6v7Ek.mjs.map +1 -0
- package/dist/communication-Kd9nU5RP.mjs +30 -0
- package/dist/communication-Kd9nU5RP.mjs.map +1 -0
- package/dist/connectors/index.d.mts +3 -2
- package/dist/connectors/index.mjs +53 -26
- package/dist/connectors/index.mjs.map +1 -1
- package/dist/connectors/slack.d.mts +13 -2
- package/dist/connectors/slack.mjs +18 -2
- package/dist/connectors/slack.mjs.map +1 -1
- package/dist/coordination-Cq08hb1l.mjs +76 -0
- package/dist/coordination-Cq08hb1l.mjs.map +1 -0
- package/dist/dispatch-BaZS3PT8.d.mts +76 -0
- package/dist/{driver-BJutB0PE.d.mts → driver-BC0Mt5KG.d.mts} +2 -2
- package/dist/driver-Bbd25rNM.mjs.map +1 -1
- package/dist/{dispatch-B77cuPxh.d.mts → engine-DFHU2Pe-.d.mts} +356 -288
- package/dist/engine-WaUM2cGA.mjs +1068 -0
- package/dist/engine-WaUM2cGA.mjs.map +1 -0
- package/dist/eve/index.d.mts +44 -334
- package/dist/eve/index.mjs +51 -38
- package/dist/eve/index.mjs.map +1 -1
- package/dist/{factory-config-BJcjsPKl.mjs → factory-config-BoRXOw43.mjs} +5 -41
- package/dist/factory-config-BoRXOw43.mjs.map +1 -0
- package/dist/{github-DXsxboUL.d.mts → github-C5HNBN6p.d.mts} +11 -3
- package/dist/github.d.mts +2 -2
- package/dist/github.mjs +15 -1
- package/dist/github.mjs.map +1 -1
- package/dist/{id-DwCihQr4.mjs → id-BLIkCVue.mjs} +4 -2
- package/dist/{id-DwCihQr4.mjs.map → id-BLIkCVue.mjs.map} +1 -1
- package/dist/index-Bw6hggoH.d.mts +40 -0
- package/dist/index.d.mts +176 -6
- package/dist/index.mjs +50 -652
- package/dist/index.mjs.map +1 -1
- package/dist/postgres/index.d.mts +1 -1
- package/dist/{approval-CwhTP1XB.mjs → task-CcOyOJ3k.mjs} +18 -133
- package/dist/task-CcOyOJ3k.mjs.map +1 -0
- package/package.json +5 -1
- package/dist/approval-CwhTP1XB.mjs.map +0 -1
- package/dist/change-CzEAoFVE.mjs.map +0 -1
- package/dist/factory-config-BJcjsPKl.mjs.map +0 -1
- package/dist/index-Cy4jfXh5.d.mts +0 -67
- package/dist/signal-CHVEmzvI.d.mts +0 -59
package/dist/eve/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { c as signalIdSchema, h as vocabularySchema, l as taskIdSchema, n as changeIdSchema, o as repositoryIdSchema } from "../id-BLIkCVue.mjs";
|
|
2
|
+
import { l as taskQuestionSchema } from "../task-CcOyOJ3k.mjs";
|
|
3
|
+
import { c as findingSchema, i as requiresApproval, o as findingFingerprint, r as defaultApprovalPolicy } from "../approval-JX2Px9IC.mjs";
|
|
4
|
+
import { c as dispatchTask, i as levelSchema, t as changeEvidenceSchema } from "../change-D2U6v7Ek.mjs";
|
|
5
|
+
import { t as createCoordination } from "../coordination-Cq08hb1l.mjs";
|
|
4
6
|
import { answerReviewComment, readPullRequestHead, readReviewComment, submitPullRequestReview } from "../github.mjs";
|
|
5
7
|
import { z } from "zod";
|
|
6
8
|
import { createHash } from "node:crypto";
|
|
@@ -371,8 +373,20 @@ function fetchPullRequestTool(stores, options) {
|
|
|
371
373
|
const depth = options.depth ?? 50;
|
|
372
374
|
return defineTool({
|
|
373
375
|
description: "Fetch the pull request and the repository's current default branch, then check out the pull request as local branch `pr`. Call this after get_task, before inspecting or testing the change. Returns an exact diff command based on their merge base; use it instead of origin/HEAD.",
|
|
374
|
-
inputSchema: z.object({ changeId: changeIdSchema.describe("The
|
|
376
|
+
inputSchema: z.object({ changeId: changeIdSchema.describe("The Change linked to your Task, returned by get_task.") }),
|
|
375
377
|
async execute({ changeId }, ctx) {
|
|
378
|
+
const boundTaskId = taskIdSchema.safeParse(ctx.session?.auth.initiator?.attributes.factoryTaskId);
|
|
379
|
+
if (boundTaskId.success) {
|
|
380
|
+
const task = await stores.tasks.get(boundTaskId.data);
|
|
381
|
+
if (task === null) return {
|
|
382
|
+
fetched: false,
|
|
383
|
+
reason: `task ${boundTaskId.data} does not exist`
|
|
384
|
+
};
|
|
385
|
+
if (task.changeId !== void 0 && task.changeId !== changeId) return {
|
|
386
|
+
fetched: false,
|
|
387
|
+
reason: `change ${changeId} is not linked to task ${boundTaskId.data}`
|
|
388
|
+
};
|
|
389
|
+
}
|
|
376
390
|
const change = await stores.changes.get(changeId);
|
|
377
391
|
if (change === null) return {
|
|
378
392
|
fetched: false,
|
|
@@ -524,6 +538,15 @@ function delegateAgentTool(stores, options) {
|
|
|
524
538
|
const repository = await stores.repositories.get(repositoryId);
|
|
525
539
|
if (repository === null || !repository.enabled) throw new Error(`Repository ${repositoryId} is not enabled in this factory.`);
|
|
526
540
|
}
|
|
541
|
+
const boundChange = changeIdSchema.safeParse(attributes?.factoryChangeId);
|
|
542
|
+
if (attributes?.factoryChangeId !== void 0 && !boundChange.success) throw new Error("Connector-authenticated change context is invalid.");
|
|
543
|
+
if (input.changeId !== void 0 && boundChange.success && input.changeId !== boundChange.data) throw new Error("Delegated change does not match the connector-authenticated context.");
|
|
544
|
+
const changeId = input.changeId ?? (boundChange.success ? boundChange.data : void 0);
|
|
545
|
+
if (changeId !== void 0) {
|
|
546
|
+
if (input.repositoryIds.length !== 1) throw new Error("Delegating work on an existing Change requires exactly one repository.");
|
|
547
|
+
const change = await stores.changes.get(changeId);
|
|
548
|
+
if (change === null || change.repositoryId !== input.repositoryIds[0] || change.state !== "verifying" && change.state !== "ready") throw new Error(`Change ${changeId} is not an open pull request in the selected repository.`);
|
|
549
|
+
}
|
|
527
550
|
if (input.signalId !== void 0) {
|
|
528
551
|
const signal = await stores.signals.get(input.signalId);
|
|
529
552
|
if (signal === null) throw new Error(`Signal ${input.signalId} does not exist.`);
|
|
@@ -545,7 +568,7 @@ function delegateAgentTool(stores, options) {
|
|
|
545
568
|
...input.signalId === void 0 ? {} : { dedupeKey: `route:${input.signalId}:${route.id}` },
|
|
546
569
|
...input.signalId === void 0 ? { operator } : { signalId: input.signalId }
|
|
547
570
|
});
|
|
548
|
-
await Promise.all(coordination.children.filter((child) => child.state === "queued").map((child) => dispatchRouteTask(stores, options, doFetch, route, child,
|
|
571
|
+
await Promise.all(coordination.children.filter((child) => child.state === "queued").map((child) => dispatchRouteTask(stores, options, doFetch, route, child, changeId, input.brief)));
|
|
549
572
|
return {
|
|
550
573
|
taskId: coordination.parent.id,
|
|
551
574
|
childTaskIds: coordination.children.map(({ id }) => id),
|
|
@@ -559,10 +582,10 @@ function delegateAgentTool(stores, options) {
|
|
|
559
582
|
kind: route.taskKind,
|
|
560
583
|
origin: input.signalId !== void 0 ? { signalId: input.signalId } : input.parentTaskId !== void 0 ? { parentTaskId: input.parentTaskId } : { operator },
|
|
561
584
|
replyTo,
|
|
562
|
-
changeId
|
|
585
|
+
changeId,
|
|
563
586
|
...input.signalId === void 0 ? {} : { dedupeKey: `route:${input.signalId}:${route.id}` }
|
|
564
587
|
});
|
|
565
|
-
const running = task.state === "queued" ? await dispatchRouteTask(stores, options, doFetch, route, task,
|
|
588
|
+
const running = task.state === "queued" ? await dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief) : task;
|
|
566
589
|
return {
|
|
567
590
|
taskId: running.id,
|
|
568
591
|
routeId: route.id,
|
|
@@ -876,9 +899,9 @@ function recordVerificationTool(stores, options = {}) {
|
|
|
876
899
|
recorded: false,
|
|
877
900
|
reason: `change ${changeId} does not exist`
|
|
878
901
|
};
|
|
879
|
-
if (change.state !== "verifying") return {
|
|
902
|
+
if (change.state !== "verifying" && change.state !== "ready") return {
|
|
880
903
|
recorded: false,
|
|
881
|
-
reason: `change is ${change.state}, not
|
|
904
|
+
reason: `change is ${change.state}, not open for verification; nothing to record`
|
|
882
905
|
};
|
|
883
906
|
const task = await stores.tasks.get(taskId);
|
|
884
907
|
if (task === null || task.kind !== "verification") return {
|
|
@@ -886,20 +909,22 @@ function recordVerificationTool(stores, options = {}) {
|
|
|
886
909
|
reason: `${taskId} is not a verification task`
|
|
887
910
|
};
|
|
888
911
|
let updatedChange = change;
|
|
889
|
-
if (verdict === "pass") updatedChange = await stores.changes.transition(changeId, "ready", {
|
|
912
|
+
if (verdict === "pass" && change.state === "verifying") updatedChange = await stores.changes.transition(changeId, "ready", {
|
|
890
913
|
patch: {
|
|
891
914
|
evidence,
|
|
892
915
|
confidence
|
|
893
916
|
},
|
|
894
917
|
reason: summary
|
|
895
918
|
});
|
|
919
|
+
else if (verdict === "fail" && change.state === "ready") updatedChange = await stores.changes.transition(changeId, "verifying", { reason: summary });
|
|
896
920
|
let finished;
|
|
897
921
|
if (verdict === "pass") {
|
|
898
922
|
await stores.tasks.transition(taskId, "verifying", { reason: summary });
|
|
899
923
|
finished = await stores.tasks.transition(taskId, "succeeded");
|
|
900
924
|
} else finished = await stores.tasks.transition(taskId, "failed", { reason: summary });
|
|
901
|
-
|
|
902
|
-
|
|
925
|
+
const sourceTaskId = task.origin.parentTaskId ?? change.taskId;
|
|
926
|
+
if (sourceTaskId !== void 0) {
|
|
927
|
+
if ((await stores.tasks.get(sourceTaskId))?.state === "verifying") await stores.tasks.transition(sourceTaskId, verdict === "pass" ? "succeeded" : "failed", verdict === "pass" ? {} : { reason: `verification failed: ${summary}` }).catch(() => void 0);
|
|
903
928
|
}
|
|
904
929
|
await options.presentResult?.({
|
|
905
930
|
taskId,
|
|
@@ -944,7 +969,10 @@ async function createAndPresentQuestion(stores, parent, question, presentQuestio
|
|
|
944
969
|
});
|
|
945
970
|
const waiting = await stores.tasks.transition(task.id, "needs_human");
|
|
946
971
|
try {
|
|
947
|
-
if (parent.state === "running") await stores.tasks.transition(parent.id, "needs_human", {
|
|
972
|
+
if (parent.state === "running") await stores.tasks.transition(parent.id, "needs_human", {
|
|
973
|
+
reason: `waiting for answer to question ${waiting.id}`,
|
|
974
|
+
questionTaskId: waiting.id
|
|
975
|
+
});
|
|
948
976
|
await presentQuestion(waiting);
|
|
949
977
|
} catch (error) {
|
|
950
978
|
if ((await stores.tasks.get(parent.id))?.state === "needs_human") await stores.tasks.transition(parent.id, "queued", { reason: `delivery of question ${waiting.id} failed` });
|
|
@@ -1097,39 +1125,24 @@ function factoryTools(stores, options = {}) {
|
|
|
1097
1125
|
}
|
|
1098
1126
|
}),
|
|
1099
1127
|
answer_question: defineTool({
|
|
1100
|
-
description: "Record the authenticated user's answer to a waiting question and requeue the parent specialist task.",
|
|
1128
|
+
description: "Record the authenticated user's answer to a waiting question and requeue the parent specialist task. A blocked parent task id is also accepted and resolves its persisted QuestionTask id.",
|
|
1101
1129
|
inputSchema: z.object({
|
|
1102
1130
|
questionTaskId: taskIdSchema,
|
|
1103
1131
|
answer: z.string().min(1).max(4e3)
|
|
1104
1132
|
}),
|
|
1105
1133
|
async execute({ questionTaskId, answer }, ctx) {
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1134
|
+
const requestedTask = await stores.tasks.get(questionTaskId);
|
|
1135
|
+
const resolvedQuestionTaskId = requestedTask?.kind === "question" ? requestedTask.id : requestedTask?.questionTaskId;
|
|
1136
|
+
const questionTask = resolvedQuestionTaskId === void 0 ? null : await stores.tasks.get(resolvedQuestionTaskId);
|
|
1137
|
+
if (questionTask === null || questionTask.kind !== "question" || questionTask.question === void 0) throw new Error(`Question task ${questionTaskId} does not exist.`);
|
|
1108
1138
|
const attributes = ctx.session.auth.initiator?.attributes;
|
|
1109
1139
|
if (attributes?.factoryReplyChannel !== questionTask.replyTo.channel || attributes.factoryReplyAddress !== questionTask.replyTo.address) throw new Error("Question reply does not match the authenticated conversation.");
|
|
1110
|
-
const repositoryAttribute = attributes.factoryRepositoryIds;
|
|
1111
|
-
const boundRepositoryIds = typeof repositoryAttribute === "string" ? repositoryAttribute.split(",").filter(Boolean) : repositoryAttribute;
|
|
1112
|
-
if (boundRepositoryIds === void 0 || boundRepositoryIds.length !== questionTask.repositoryIds.length || boundRepositoryIds.some((id) => !questionTask.repositoryIds.includes(id))) throw new Error("Question repositories do not match the authenticated conversation.");
|
|
1113
1140
|
const answeredBy = ctx.session.auth.initiator?.principalId ?? "operator";
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
answeredBy
|
|
1119
|
-
} },
|
|
1120
|
-
reason: "question answered through Foreman"
|
|
1141
|
+
return stores.tasks.answerQuestion({
|
|
1142
|
+
questionTaskId: questionTask.id,
|
|
1143
|
+
answer,
|
|
1144
|
+
answeredBy
|
|
1121
1145
|
});
|
|
1122
|
-
const parentTaskId = questionTask.origin.parentTaskId;
|
|
1123
|
-
if (parentTaskId === void 0) return {
|
|
1124
|
-
questionTask: answered,
|
|
1125
|
-
parentTask: null
|
|
1126
|
-
};
|
|
1127
|
-
const parent = await stores.tasks.get(parentTaskId);
|
|
1128
|
-
if (parent === null) throw new Error(`Parent task ${parentTaskId} does not exist.`);
|
|
1129
|
-
return {
|
|
1130
|
-
questionTask: answered,
|
|
1131
|
-
parentTask: parent.state === "needs_human" ? await stores.tasks.transition(parent.id, "queued", { reason: `question ${questionTask.id} answered by ${answeredBy}` }) : parent
|
|
1132
|
-
};
|
|
1133
1146
|
}
|
|
1134
1147
|
}),
|
|
1135
1148
|
append_receipt: defineTool({
|
|
@@ -1196,7 +1209,7 @@ function factoryHooks(stores, options) {
|
|
|
1196
1209
|
if (taskId !== void 0) await settleIfTerminal(taskId).catch(() => void 0);
|
|
1197
1210
|
const address = ctx.channel.continuationToken;
|
|
1198
1211
|
if (address === void 0) return;
|
|
1199
|
-
const abandoned = (await stores.tasks.list()).filter((task) => task.state === "running" && task.replyTo.address === address);
|
|
1212
|
+
const abandoned = (await stores.tasks.list().catch(() => [])).filter((task) => task.state === "running" && task.replyTo.address === address);
|
|
1200
1213
|
for (const task of abandoned) await createAndPresentQuestion(stores, task, {
|
|
1201
1214
|
text: "The worker stopped without recording an outcome. Please review the task and reply when Factory should retry it.",
|
|
1202
1215
|
options: ["Retry the task"]
|