@vercel/factory 0.0.6 → 0.0.9
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 +9 -2
- package/dist/approval-DdvEKLiE.mjs +132 -0
- package/dist/approval-DdvEKLiE.mjs.map +1 -0
- package/dist/build-factory.d.mts +12 -0
- package/dist/build-factory.mjs +16 -0
- package/dist/build-factory.mjs.map +1 -0
- package/dist/connectors/index.d.mts +1 -1
- package/dist/connectors/index.mjs +2 -2
- package/dist/connectors/index.mjs.map +1 -1
- package/dist/connectors/slack.d.mts +32 -2
- package/dist/connectors/slack.mjs +91 -1
- package/dist/connectors/slack.mjs.map +1 -1
- package/dist/eve/index.d.mts +1 -1
- package/dist/eve/index.mjs +2 -1
- package/dist/eve/index.mjs.map +1 -1
- package/dist/factory-config-Bu4mklm9.d.mts +39 -0
- package/dist/factory-config-ifd3JxiU.mjs +49 -0
- package/dist/factory-config-ifd3JxiU.mjs.map +1 -0
- package/dist/{github-lprHgbx7.d.mts → github-DMOypeIu.d.mts} +2 -2
- package/dist/github.d.mts +1 -1
- package/dist/{index-WNGZJRRH.d.mts → index-Cy4jfXh5.d.mts} +2 -2
- package/dist/index.d.mts +2 -46
- package/dist/index.mjs +4 -57
- package/dist/index.mjs.map +1 -1
- package/dist/{task-BxTU3MJb.mjs → task-DMHuvBI0.mjs} +2 -129
- package/dist/task-DMHuvBI0.mjs.map +1 -0
- package/package.json +6 -1
- package/dist/task-BxTU3MJb.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @vercel/factory
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The Factory Development Kit: typed constructs, workflows, schemas, execution contracts,
|
|
4
|
+
persistence, agent tools, and provider adapters for durable, auditable software factories.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
The FDK extends Eve incrementally. Durable workflows retain authority over policy, budgets,
|
|
7
|
+
state transitions, retries, and external effects while agents and coding harnesses return bounded
|
|
8
|
+
results. The V1 reference runtime uses Postgres, Blob, AI SDK `HarnessAgent`, and Vercel Sandbox,
|
|
9
|
+
with independent verification and human-owned merges.
|
|
10
|
+
|
|
11
|
+
See the [repository README](../../README.md) for development instructions and the
|
|
12
|
+
[architecture](../../docs/factory-architecture.md) for the complete contract.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { p as vocabularySchema } from "./id-DwCihQr4.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
//#region src/schema/finding.ts
|
|
5
|
+
const findingPrioritySchema = z.enum([
|
|
6
|
+
"P0",
|
|
7
|
+
"P1",
|
|
8
|
+
"P2",
|
|
9
|
+
"P3"
|
|
10
|
+
]).describe("Urgency of a finding: P0 is drop-everything, P3 is nice-to-have.");
|
|
11
|
+
const findingCategorySchema = z.enum([
|
|
12
|
+
"security",
|
|
13
|
+
"correctness",
|
|
14
|
+
"reliability",
|
|
15
|
+
"performance",
|
|
16
|
+
"compatibility",
|
|
17
|
+
"accessibility",
|
|
18
|
+
"documentation",
|
|
19
|
+
"maintainability",
|
|
20
|
+
"testing",
|
|
21
|
+
"usability"
|
|
22
|
+
]);
|
|
23
|
+
const findingVerificationLevelSchema = z.enum([
|
|
24
|
+
"unverified",
|
|
25
|
+
"supported",
|
|
26
|
+
"confirmed"
|
|
27
|
+
]).describe("How sure the reporter is: unverified = suspected from reading; supported = evidence points to it; confirmed = demonstrated.");
|
|
28
|
+
const findingVerificationMethodSchema = z.enum([
|
|
29
|
+
"code_inspection",
|
|
30
|
+
"static_analysis",
|
|
31
|
+
"manual_reproduction",
|
|
32
|
+
"unit_test",
|
|
33
|
+
"integration_test",
|
|
34
|
+
"e2e_test",
|
|
35
|
+
"runtime_observation",
|
|
36
|
+
"documentation"
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* What was done to check the finding. Forces a reporter to distinguish "I
|
|
40
|
+
* think this is a problem" from "I demonstrated it", which is the axis a human
|
|
41
|
+
* approving fixes cares about most.
|
|
42
|
+
*/
|
|
43
|
+
const findingVerificationSchema = z.strictObject({
|
|
44
|
+
level: findingVerificationLevelSchema,
|
|
45
|
+
method: findingVerificationMethodSchema,
|
|
46
|
+
summary: z.string().min(1).max(2e3).describe("What was checked and what it showed."),
|
|
47
|
+
limitations: z.string().min(1).max(2e3).optional().describe("What could not be checked.")
|
|
48
|
+
});
|
|
49
|
+
/** A single problem found in a repository, as a scanner or agent reports it. */
|
|
50
|
+
const findingSchema = z.strictObject({
|
|
51
|
+
title: z.string().min(1).max(200),
|
|
52
|
+
body: z.string().min(1).max(8e3).describe("The problem, its impact, and how to fix it. Markdown."),
|
|
53
|
+
path: z.string().min(1).max(500).optional().describe("Repository-relative file path."),
|
|
54
|
+
line: z.number().int().min(1).optional(),
|
|
55
|
+
endLine: z.number().int().min(1).optional(),
|
|
56
|
+
priority: findingPrioritySchema,
|
|
57
|
+
category: findingCategorySchema,
|
|
58
|
+
verification: findingVerificationSchema,
|
|
59
|
+
tool: z.string().min(1).max(100).optional().describe("Scanner that produced it, e.g. deepsec."),
|
|
60
|
+
ruleId: z.string().min(1).max(200).optional().describe("Scanner rule or check id, when there is one.")
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Stable identity of a finding across scans, used as the signal's delivery id
|
|
64
|
+
* so re-reporting is a no-op instead of a second task. A scanner finding is
|
|
65
|
+
* the same problem when the same tool reports the same rule on the same file;
|
|
66
|
+
* its title is model-written prose and changes between runs, so it is left
|
|
67
|
+
* out. Without a rule id the title is all there is, normalised.
|
|
68
|
+
*/
|
|
69
|
+
function findingFingerprint(repositoryId, finding) {
|
|
70
|
+
const parts = finding.tool !== void 0 && finding.ruleId !== void 0 && finding.path !== void 0 ? [
|
|
71
|
+
repositoryId,
|
|
72
|
+
finding.tool,
|
|
73
|
+
finding.ruleId,
|
|
74
|
+
finding.path
|
|
75
|
+
] : [
|
|
76
|
+
repositoryId,
|
|
77
|
+
finding.tool ?? "",
|
|
78
|
+
finding.ruleId ?? "",
|
|
79
|
+
finding.path ?? "",
|
|
80
|
+
finding.title.trim().toLowerCase().replaceAll(/\s+/gu, " ")
|
|
81
|
+
];
|
|
82
|
+
return createHash("sha256").update(parts.join(" ")).digest("hex").slice(0, 32);
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/approval.ts
|
|
86
|
+
/**
|
|
87
|
+
* One approval rule. Every listed field must match for the rule to apply;
|
|
88
|
+
* absent fields match anything. Rules are evaluated first-match.
|
|
89
|
+
*/
|
|
90
|
+
const approvalRuleSchema = z.strictObject({
|
|
91
|
+
source: vocabularySchema.optional().describe("Signal source, e.g. security_scan or github_issue."),
|
|
92
|
+
kind: vocabularySchema.optional().describe("Task kind, e.g. code_change."),
|
|
93
|
+
priority: z.array(findingPrioritySchema).min(1).optional().describe("Finding priorities the rule applies to."),
|
|
94
|
+
require: z.boolean().describe("Whether matching tasks wait for a human before dispatch.")
|
|
95
|
+
});
|
|
96
|
+
const approvalPolicySchema = z.array(approvalRuleSchema).describe("First-match rules deciding which tasks need a human before they run.");
|
|
97
|
+
/**
|
|
98
|
+
* The default: fixes for security findings wait for a human; everything else
|
|
99
|
+
* runs as it always has. Handoffs between agents (verification) never wait,
|
|
100
|
+
* because a human already approved the work they continue.
|
|
101
|
+
*/
|
|
102
|
+
const defaultApprovalPolicy = [
|
|
103
|
+
{
|
|
104
|
+
kind: "verification",
|
|
105
|
+
require: false
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
source: "security_scan",
|
|
109
|
+
require: true
|
|
110
|
+
},
|
|
111
|
+
{ require: false }
|
|
112
|
+
];
|
|
113
|
+
/**
|
|
114
|
+
* Decide at task-creation time whether a task needs approval. Pure: the answer
|
|
115
|
+
* is stamped on the task and never re-evaluated, so a later policy change
|
|
116
|
+
* cannot silently release work a human was expecting to see.
|
|
117
|
+
*/
|
|
118
|
+
function requiresApproval(policy, subject) {
|
|
119
|
+
for (const rule of policy) {
|
|
120
|
+
if (rule.source !== void 0 && rule.source !== subject.source) continue;
|
|
121
|
+
if (rule.kind !== void 0 && rule.kind !== subject.kind) continue;
|
|
122
|
+
if (rule.priority !== void 0) {
|
|
123
|
+
if (subject.priority === void 0 || !rule.priority.includes(subject.priority)) continue;
|
|
124
|
+
}
|
|
125
|
+
return rule.require;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { findingCategorySchema as a, findingSchema as c, findingVerificationSchema as d, requiresApproval as i, findingVerificationLevelSchema as l, approvalRuleSchema as n, findingFingerprint as o, defaultApprovalPolicy as r, findingPrioritySchema as s, approvalPolicySchema as t, findingVerificationMethodSchema as u };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=approval-DdvEKLiE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-DdvEKLiE.mjs","names":[],"sources":["../src/schema/finding.ts","../src/approval.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nexport const findingPrioritySchema = z\n .enum([\"P0\", \"P1\", \"P2\", \"P3\"])\n .describe(\"Urgency of a finding: P0 is drop-everything, P3 is nice-to-have.\");\n\nexport const findingCategorySchema = z.enum([\n \"security\",\n \"correctness\",\n \"reliability\",\n \"performance\",\n \"compatibility\",\n \"accessibility\",\n \"documentation\",\n \"maintainability\",\n \"testing\",\n \"usability\",\n]);\n\nexport const findingVerificationLevelSchema = z\n .enum([\"unverified\", \"supported\", \"confirmed\"])\n .describe(\n \"How sure the reporter is: unverified = suspected from reading; supported = evidence points to it; confirmed = demonstrated.\",\n );\n\nexport const findingVerificationMethodSchema = z.enum([\n \"code_inspection\",\n \"static_analysis\",\n \"manual_reproduction\",\n \"unit_test\",\n \"integration_test\",\n \"e2e_test\",\n \"runtime_observation\",\n \"documentation\",\n]);\n\n/**\n * What was done to check the finding. Forces a reporter to distinguish \"I\n * think this is a problem\" from \"I demonstrated it\", which is the axis a human\n * approving fixes cares about most.\n */\nexport const findingVerificationSchema = z.strictObject({\n level: findingVerificationLevelSchema,\n method: findingVerificationMethodSchema,\n summary: z.string().min(1).max(2000).describe(\"What was checked and what it showed.\"),\n limitations: z.string().min(1).max(2000).optional().describe(\"What could not be checked.\"),\n});\n\n/** A single problem found in a repository, as a scanner or agent reports it. */\nexport const findingSchema = z.strictObject({\n title: z.string().min(1).max(200),\n body: z.string().min(1).max(8000).describe(\"The problem, its impact, and how to fix it. Markdown.\"),\n path: z.string().min(1).max(500).optional().describe(\"Repository-relative file path.\"),\n line: z.number().int().min(1).optional(),\n endLine: z.number().int().min(1).optional(),\n priority: findingPrioritySchema,\n category: findingCategorySchema,\n verification: findingVerificationSchema,\n tool: z.string().min(1).max(100).optional().describe(\"Scanner that produced it, e.g. deepsec.\"),\n ruleId: z.string().min(1).max(200).optional().describe(\"Scanner rule or check id, when there is one.\"),\n});\n\nexport type FindingPriority = z.infer<typeof findingPrioritySchema>;\nexport type FindingCategory = z.infer<typeof findingCategorySchema>;\nexport type FindingVerification = z.infer<typeof findingVerificationSchema>;\nexport type Finding = z.infer<typeof findingSchema>;\n\n/**\n * Stable identity of a finding across scans, used as the signal's delivery id\n * so re-reporting is a no-op instead of a second task. A scanner finding is\n * the same problem when the same tool reports the same rule on the same file;\n * its title is model-written prose and changes between runs, so it is left\n * out. Without a rule id the title is all there is, normalised.\n */\nexport function findingFingerprint(repositoryId: string, finding: Finding): string {\n const parts =\n finding.tool !== undefined && finding.ruleId !== undefined && finding.path !== undefined\n ? [repositoryId, finding.tool, finding.ruleId, finding.path]\n : [\n repositoryId,\n finding.tool ?? \"\",\n finding.ruleId ?? \"\",\n finding.path ?? \"\",\n finding.title.trim().toLowerCase().replaceAll(/\\s+/gu, \" \"),\n ];\n return createHash(\"sha256\").update(parts.join(\" \")).digest(\"hex\").slice(0, 32);\n}\n","import { z } from \"zod\";\nimport { vocabularySchema } from \"./schema/common\";\nimport { findingPrioritySchema, type FindingPriority } from \"./schema/finding\";\n\n/**\n * One approval rule. Every listed field must match for the rule to apply;\n * absent fields match anything. Rules are evaluated first-match.\n */\nexport const approvalRuleSchema = z.strictObject({\n source: vocabularySchema.optional().describe(\"Signal source, e.g. security_scan or github_issue.\"),\n kind: vocabularySchema.optional().describe(\"Task kind, e.g. code_change.\"),\n priority: z\n .array(findingPrioritySchema)\n .min(1)\n .optional()\n .describe(\"Finding priorities the rule applies to.\"),\n require: z.boolean().describe(\"Whether matching tasks wait for a human before dispatch.\"),\n});\n\nexport const approvalPolicySchema = z\n .array(approvalRuleSchema)\n .describe(\"First-match rules deciding which tasks need a human before they run.\");\n\nexport type ApprovalRule = z.infer<typeof approvalRuleSchema>;\nexport type ApprovalPolicy = z.infer<typeof approvalPolicySchema>;\n\n/**\n * The default: fixes for security findings wait for a human; everything else\n * runs as it always has. Handoffs between agents (verification) never wait,\n * because a human already approved the work they continue.\n */\nexport const defaultApprovalPolicy: ApprovalPolicy = [\n { kind: \"verification\", require: false },\n { source: \"security_scan\", require: true },\n { require: false },\n];\n\nexport interface ApprovalSubject {\n source?: string;\n kind: string;\n priority?: FindingPriority;\n}\n\n/**\n * Decide at task-creation time whether a task needs approval. Pure: the answer\n * is stamped on the task and never re-evaluated, so a later policy change\n * cannot silently release work a human was expecting to see.\n */\nexport function requiresApproval(policy: ApprovalPolicy, subject: ApprovalSubject): boolean {\n for (const rule of policy) {\n if (rule.source !== undefined && rule.source !== subject.source) continue;\n if (rule.kind !== undefined && rule.kind !== subject.kind) continue;\n if (rule.priority !== undefined) {\n if (subject.priority === undefined || !rule.priority.includes(subject.priority)) continue;\n }\n return rule.require;\n }\n return false;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { n as FactoryConfigInput } from "./factory-config-Bu4mklm9.mjs";
|
|
2
|
+
import { EveNextConfig, EveNextConfigFunction, EveNextConfigInput } from "eve/next";
|
|
3
|
+
//#region src/build-factory.d.ts
|
|
4
|
+
interface FactoryArgs<TConfig extends EveNextConfig = EveNextConfig> {
|
|
5
|
+
factoryConfig: FactoryConfigInput;
|
|
6
|
+
nextConfig: EveNextConfigInput<TConfig>;
|
|
7
|
+
root?: string;
|
|
8
|
+
}
|
|
9
|
+
declare function buildFactory<TConfig extends EveNextConfig>({ factoryConfig, nextConfig, root }: FactoryArgs<TConfig>): EveNextConfigFunction<TConfig>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { FactoryArgs, buildFactory };
|
|
12
|
+
//# sourceMappingURL=build-factory.d.mts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { t as defineFactory } from "./factory-config-ifd3JxiU.mjs";
|
|
2
|
+
import { readdirSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/build-factory.ts
|
|
5
|
+
function buildFactory({ factoryConfig, nextConfig, root = process.cwd() }) {
|
|
6
|
+
defineFactory(factoryConfig);
|
|
7
|
+
const agentDefinitions = Object.fromEntries(readdirSync(path.join(root, "agents"), { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => [entry.name, path.join(root, "agents", entry.name)]));
|
|
8
|
+
return async (phase, context) => {
|
|
9
|
+
const { withEve } = await import("eve/next");
|
|
10
|
+
return withEve(nextConfig, { agents: agentDefinitions })(phase, context);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { buildFactory };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=build-factory.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-factory.mjs","names":[],"sources":["../src/build-factory.ts"],"sourcesContent":["import { readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n type EveNextConfig,\n type EveNextConfigFunction,\n type EveNextConfigInput,\n type WithEveAgentsConfig,\n} from \"eve/next\";\nimport { defineFactory, type FactoryConfigInput } from \"./schema/factory-config\";\n\nexport interface FactoryArgs<TConfig extends EveNextConfig = EveNextConfig> {\n factoryConfig: FactoryConfigInput;\n nextConfig: EveNextConfigInput<TConfig>;\n root?: string;\n}\n\nexport function buildFactory<TConfig extends EveNextConfig>({\n factoryConfig,\n nextConfig,\n root = process.cwd(),\n}: FactoryArgs<TConfig>): EveNextConfigFunction<TConfig> {\n defineFactory(factoryConfig);\n const agentDefinitions: WithEveAgentsConfig = Object.fromEntries(\n readdirSync(path.join(root, \"agents\"), { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => [entry.name, path.join(root, \"agents\", entry.name)]),\n );\n return async (phase, context) => {\n const { withEve } = await import(\"eve/next\");\n return withEve(nextConfig, { agents: agentDefinitions })(phase, context);\n };\n}\n"],"mappings":";;;;AAgBA,SAAgB,aAA4C,EAC1D,eACA,YACA,OAAO,QAAQ,IAAI,KACoC;CACvD,cAAc,aAAa;CAC3B,MAAM,mBAAwC,OAAO,YACnD,YAAY,KAAK,KAAK,MAAM,QAAQ,GAAG,EAAE,eAAe,KAAK,CAAC,CAAC,CAC5D,QAAQ,UAAU,MAAM,YAAY,CAAC,CAAC,CACtC,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,KAAK,MAAM,UAAU,MAAM,IAAI,CAAC,CAAC,CACvE;CACA,OAAO,OAAO,OAAO,YAAY;EAC/B,MAAM,EAAE,YAAY,MAAM,OAAO;EACjC,OAAO,QAAQ,YAAY,EAAE,QAAQ,iBAAiB,CAAC,CAAC,CAAC,OAAO,OAAO;CACzE;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as NormalizedSignal, c as communicationEventSchema, d as createCommunicationRouter, i as CommunicationRouter, l as communicationOutcomeSchema, n as CommunicationEvent, o as SignalNormalizer, r as CommunicationOutcome, s as WebhookEvent, t as CommunicationAdapter, u as communicationOutcomes } from "../index-
|
|
1
|
+
import { a as NormalizedSignal, c as communicationEventSchema, d as createCommunicationRouter, i as CommunicationRouter, l as communicationOutcomeSchema, n as CommunicationEvent, o as SignalNormalizer, r as CommunicationOutcome, s as WebhookEvent, t as CommunicationAdapter, u as communicationOutcomes } from "../index-Cy4jfXh5.mjs";
|
|
2
2
|
export { CommunicationAdapter, CommunicationEvent, CommunicationOutcome, CommunicationRouter, NormalizedSignal, SignalNormalizer, WebhookEvent, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes, createCommunicationRouter };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as repositoryIdsSchema, s as taskIdSchema, t as changeIdSchema, u as replyToSchema } from "../id-DwCihQr4.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/connectors/index.ts
|
|
4
4
|
const communicationOutcomes = [
|
|
@@ -17,7 +17,7 @@ const communicationOutcomes = [
|
|
|
17
17
|
const communicationOutcomeSchema = z.enum(communicationOutcomes).describe("Lifecycle outcome a connector should render and deliver.");
|
|
18
18
|
const communicationEventSchema = z.strictObject({
|
|
19
19
|
eventId: z.string().min(1).describe("Stable idempotency key for this delivery."),
|
|
20
|
-
|
|
20
|
+
repositoryIds: repositoryIdsSchema,
|
|
21
21
|
taskId: taskIdSchema.optional(),
|
|
22
22
|
changeId: changeIdSchema.optional(),
|
|
23
23
|
replyTo: replyToSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/connectors/index.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { changeIdSchema,
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/connectors/index.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { changeIdSchema, repositoryIdsSchema, taskIdSchema } from \"../schema/id\";\nimport { replyToSchema } from \"../schema/common\";\nimport type { ReplyTo } from \"../schema/common\";\nimport type { SignalSource } from \"../schema/signal\";\n\nexport interface WebhookEvent {\n deliveryId: string;\n event: string;\n payload: unknown;\n}\n\nexport interface NormalizedSignal<TSource extends SignalSource = SignalSource> {\n source: TSource;\n deliveryId: string;\n replyTo: ReplyTo;\n payload: unknown;\n}\n\nexport type SignalNormalizer<TSignal extends NormalizedSignal = NormalizedSignal> = (\n event: WebhookEvent,\n) => TSignal | null;\n\nexport const communicationOutcomes = [\n \"accepted\",\n \"started\",\n \"succeeded\",\n \"failed\",\n \"needs_human\",\n \"question\",\n \"digest\",\n \"rolled_back\",\n \"budget_warning\",\n \"budget_exhausted\",\n \"message\",\n] as const;\n\nexport const communicationOutcomeSchema = z\n .enum(communicationOutcomes)\n .describe(\"Lifecycle outcome a connector should render and deliver.\");\n\nexport const communicationEventSchema = z.strictObject({\n eventId: z.string().min(1).describe(\"Stable idempotency key for this delivery.\"),\n repositoryIds: repositoryIdsSchema,\n taskId: taskIdSchema.optional(),\n changeId: changeIdSchema.optional(),\n replyTo: replyToSchema,\n outcome: communicationOutcomeSchema,\n data: z\n .record(z.string(), z.unknown())\n .describe(\"Outcome-specific data rendered by the selected connector.\"),\n});\n\nexport type CommunicationOutcome = z.infer<typeof communicationOutcomeSchema>;\nexport type CommunicationEvent = z.infer<typeof communicationEventSchema>;\n\nexport interface CommunicationAdapter {\n readonly channel: string;\n deliver(event: CommunicationEvent): Promise<void>;\n}\n\nexport interface CommunicationRouter {\n readonly channels: readonly string[];\n deliver(event: CommunicationEvent): Promise<void>;\n}\n\nexport function createCommunicationRouter(\n adapters: readonly CommunicationAdapter[],\n): CommunicationRouter {\n const byChannel = new Map<string, CommunicationAdapter>();\n for (const adapter of adapters) {\n const channel = adapter.channel.trim();\n if (channel.length === 0) {\n throw new Error(\"Communication adapter channel is required.\");\n }\n if (byChannel.has(channel)) {\n throw new Error(`Communication adapter ${channel} is registered more than once.`);\n }\n byChannel.set(channel, adapter);\n }\n const channels = Object.freeze([...byChannel.keys()]);\n\n return Object.freeze({\n channels,\n async deliver(input: CommunicationEvent): Promise<void> {\n const event = communicationEventSchema.parse(input);\n const adapter = byChannel.get(event.replyTo.channel);\n if (adapter === undefined) {\n throw new Error(`No communication adapter is registered for ${event.replyTo.channel}.`);\n }\n await adapter.deliver(event);\n },\n });\n}\n"],"mappings":";;;AAuBA,MAAa,wBAAwB;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,6BAA6B,EACvC,KAAK,qBAAqB,CAAC,CAC3B,SAAS,0DAA0D;AAEtE,MAAa,2BAA2B,EAAE,aAAa;CACrD,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,2CAA2C;CAC/E,eAAe;CACf,QAAQ,aAAa,SAAS;CAC9B,UAAU,eAAe,SAAS;CAClC,SAAS;CACT,SAAS;CACT,MAAM,EACH,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,CAC/B,SAAS,2DAA2D;AACzE,CAAC;AAeD,SAAgB,0BACd,UACqB;CACrB,MAAM,4BAAY,IAAI,IAAkC;CACxD,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,UAAU,QAAQ,QAAQ,KAAK;EACrC,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MAAM,4CAA4C;EAE9D,IAAI,UAAU,IAAI,OAAO,GACvB,MAAM,IAAI,MAAM,yBAAyB,QAAQ,+BAA+B;EAElF,UAAU,IAAI,SAAS,OAAO;CAChC;CACA,MAAM,WAAW,OAAO,OAAO,CAAC,GAAG,UAAU,KAAK,CAAC,CAAC;CAEpD,OAAO,OAAO,OAAO;EACnB;EACA,MAAM,QAAQ,OAA0C;GACtD,MAAM,QAAQ,yBAAyB,MAAM,KAAK;GAClD,MAAM,UAAU,UAAU,IAAI,MAAM,QAAQ,OAAO;GACnD,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,MAAM,8CAA8C,MAAM,QAAQ,QAAQ,EAAE;GAExF,MAAM,QAAQ,QAAQ,KAAK;EAC7B;CACF,CAAC;AACH"}
|
|
@@ -1,5 +1,35 @@
|
|
|
1
|
-
import { a as NormalizedSignal } from "../index-
|
|
1
|
+
import { a as NormalizedSignal, n as CommunicationEvent, t as CommunicationAdapter } from "../index-Cy4jfXh5.mjs";
|
|
2
2
|
//#region src/connectors/slack.d.ts
|
|
3
|
+
interface SlackCommunicationDelivery {
|
|
4
|
+
eventId: string;
|
|
5
|
+
clientMessageId: string;
|
|
6
|
+
text: string;
|
|
7
|
+
target: {
|
|
8
|
+
type: "owner";
|
|
9
|
+
} | {
|
|
10
|
+
type: "thread";
|
|
11
|
+
channelId: string;
|
|
12
|
+
threadTs: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface SlackCommunicationAdapter extends CommunicationAdapter {
|
|
16
|
+
readonly channel: "slack";
|
|
17
|
+
render(event: CommunicationEvent): string;
|
|
18
|
+
}
|
|
19
|
+
interface SlackCommunicationAdapterOptions {
|
|
20
|
+
send(delivery: SlackCommunicationDelivery): Promise<void>;
|
|
21
|
+
render?: (event: CommunicationEvent) => string;
|
|
22
|
+
}
|
|
23
|
+
interface SlackReplyTarget {
|
|
24
|
+
channelId: string;
|
|
25
|
+
threadTs: string;
|
|
26
|
+
}
|
|
27
|
+
/** Parse Eve's Slack continuation token into its channel and thread target. */
|
|
28
|
+
declare function parseSlackReplyAddress(address: string): SlackReplyTarget;
|
|
29
|
+
/** Render a provider-neutral communication event as plain Slack text. */
|
|
30
|
+
declare function renderSlackCommunicationEvent(input: CommunicationEvent): string;
|
|
31
|
+
/** Bind Slack rendering and delivery to the channel-neutral communication contract. */
|
|
32
|
+
declare function createSlackCommunicationAdapter(options: SlackCommunicationAdapterOptions): SlackCommunicationAdapter;
|
|
3
33
|
interface SlackMessageEvent {
|
|
4
34
|
teamId: string;
|
|
5
35
|
channelId: string;
|
|
@@ -39,5 +69,5 @@ type SlackRepositorySelection = {
|
|
|
39
69
|
*/
|
|
40
70
|
declare function selectSlackRepository(text: string, repositories: readonly string[]): SlackRepositorySelection;
|
|
41
71
|
//#endregion
|
|
42
|
-
export { SlackMessageEvent, SlackMessageSignal, SlackRepositorySelection, normalizeSlackMessage, selectSlackRepository };
|
|
72
|
+
export { SlackCommunicationAdapter, SlackCommunicationAdapterOptions, SlackCommunicationDelivery, SlackMessageEvent, SlackMessageSignal, SlackReplyTarget, SlackRepositorySelection, createSlackCommunicationAdapter, normalizeSlackMessage, parseSlackReplyAddress, renderSlackCommunicationEvent, selectSlackRepository };
|
|
43
73
|
//# sourceMappingURL=slack.d.mts.map
|
|
@@ -1,4 +1,94 @@
|
|
|
1
|
+
import { communicationEventSchema } from "./index.mjs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
1
3
|
//#region src/connectors/slack.ts
|
|
4
|
+
const slackOutcomeLabels = {
|
|
5
|
+
accepted: "Request accepted",
|
|
6
|
+
started: "Work started",
|
|
7
|
+
succeeded: "Work completed",
|
|
8
|
+
failed: "Work failed",
|
|
9
|
+
needs_human: "Human action required",
|
|
10
|
+
question: "Question requires an answer",
|
|
11
|
+
digest: "Factory digest",
|
|
12
|
+
rolled_back: "Change rolled back",
|
|
13
|
+
budget_warning: "Factory budget warning",
|
|
14
|
+
budget_exhausted: "Factory budget exhausted",
|
|
15
|
+
message: "Factory update"
|
|
16
|
+
};
|
|
17
|
+
const slackDetailFields = [
|
|
18
|
+
"title",
|
|
19
|
+
"message",
|
|
20
|
+
"summary",
|
|
21
|
+
"question",
|
|
22
|
+
"requestedAction",
|
|
23
|
+
"reason",
|
|
24
|
+
"url"
|
|
25
|
+
];
|
|
26
|
+
const slackListFields = ["items", "options"];
|
|
27
|
+
const slackTextLimit = 12e3;
|
|
28
|
+
const slackOwnerOutcomes = /* @__PURE__ */ new Set([
|
|
29
|
+
"digest",
|
|
30
|
+
"budget_warning",
|
|
31
|
+
"budget_exhausted"
|
|
32
|
+
]);
|
|
33
|
+
/** Parse Eve's Slack continuation token into its channel and thread target. */
|
|
34
|
+
function parseSlackReplyAddress(address) {
|
|
35
|
+
const separator = address.indexOf(":");
|
|
36
|
+
const channelId = address.slice(0, separator);
|
|
37
|
+
const threadTs = address.slice(separator + 1);
|
|
38
|
+
if (separator <= 0 || !/^[A-Z][A-Z0-9]+$/.test(channelId) || !/^\d+\.\d+$/.test(threadTs)) throw new Error(`Invalid Slack reply address: ${address}.`);
|
|
39
|
+
return {
|
|
40
|
+
channelId,
|
|
41
|
+
threadTs
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Render a provider-neutral communication event as plain Slack text. */
|
|
45
|
+
function renderSlackCommunicationEvent(input) {
|
|
46
|
+
const event = communicationEventSchema.parse(input);
|
|
47
|
+
const headline = slackOutcomeLabels[event.outcome];
|
|
48
|
+
const details = slackDetailFields.flatMap((field) => {
|
|
49
|
+
const value = event.data[field];
|
|
50
|
+
return typeof value === "string" && value.trim() !== "" ? [value.trim()] : [];
|
|
51
|
+
});
|
|
52
|
+
const lists = slackListFields.flatMap((field) => {
|
|
53
|
+
const value = event.data[field];
|
|
54
|
+
if (!Array.isArray(value)) return [];
|
|
55
|
+
return value.flatMap((item) => typeof item === "string" && item.trim() !== "" ? [`- ${item.trim()}`] : []);
|
|
56
|
+
});
|
|
57
|
+
const text = [headline, .../* @__PURE__ */ new Set([...details, ...lists])].join("\n");
|
|
58
|
+
return text.length <= slackTextLimit ? text : `${text.slice(0, 11999)}…`;
|
|
59
|
+
}
|
|
60
|
+
function slackClientMessageId(eventId) {
|
|
61
|
+
const digest = createHash("sha256").update(eventId).digest("hex").slice(0, 32);
|
|
62
|
+
return [
|
|
63
|
+
digest.slice(0, 8),
|
|
64
|
+
digest.slice(8, 12),
|
|
65
|
+
digest.slice(12, 16),
|
|
66
|
+
digest.slice(16, 20),
|
|
67
|
+
digest.slice(20)
|
|
68
|
+
].join("-");
|
|
69
|
+
}
|
|
70
|
+
/** Bind Slack rendering and delivery to the channel-neutral communication contract. */
|
|
71
|
+
function createSlackCommunicationAdapter(options) {
|
|
72
|
+
const render = options.render ?? renderSlackCommunicationEvent;
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
channel: "slack",
|
|
75
|
+
render,
|
|
76
|
+
async deliver(input) {
|
|
77
|
+
const event = communicationEventSchema.parse(input);
|
|
78
|
+
if (event.replyTo.channel !== "slack") throw new Error(`Slack communication adapter cannot deliver to ${event.replyTo.channel}.`);
|
|
79
|
+
const target = slackOwnerOutcomes.has(event.outcome) ? { type: "owner" } : {
|
|
80
|
+
type: "thread",
|
|
81
|
+
...parseSlackReplyAddress(event.replyTo.address)
|
|
82
|
+
};
|
|
83
|
+
await options.send({
|
|
84
|
+
eventId: event.eventId,
|
|
85
|
+
clientMessageId: slackClientMessageId(event.eventId),
|
|
86
|
+
text: render(event),
|
|
87
|
+
target
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
2
92
|
/** Normalize an Eve-verified Slack message after the caller resolves its repository. */
|
|
3
93
|
function normalizeSlackMessage(event) {
|
|
4
94
|
return {
|
|
@@ -45,6 +135,6 @@ function selectSlackRepository(text, repositories) {
|
|
|
45
135
|
return { status: "required" };
|
|
46
136
|
}
|
|
47
137
|
//#endregion
|
|
48
|
-
export { normalizeSlackMessage, selectSlackRepository };
|
|
138
|
+
export { createSlackCommunicationAdapter, normalizeSlackMessage, parseSlackReplyAddress, renderSlackCommunicationEvent, selectSlackRepository };
|
|
49
139
|
|
|
50
140
|
//# sourceMappingURL=slack.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.mjs","names":[],"sources":["../../src/connectors/slack.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"slack.mjs","names":[],"sources":["../../src/connectors/slack.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n communicationEventSchema,\n type CommunicationAdapter,\n type CommunicationEvent,\n type CommunicationOutcome,\n type NormalizedSignal,\n} from \"./index\";\n\nconst slackOutcomeLabels: Record<CommunicationOutcome, string> = {\n accepted: \"Request accepted\",\n started: \"Work started\",\n succeeded: \"Work completed\",\n failed: \"Work failed\",\n needs_human: \"Human action required\",\n question: \"Question requires an answer\",\n digest: \"Factory digest\",\n rolled_back: \"Change rolled back\",\n budget_warning: \"Factory budget warning\",\n budget_exhausted: \"Factory budget exhausted\",\n message: \"Factory update\",\n};\n\nconst slackDetailFields = [\"title\", \"message\", \"summary\", \"question\", \"requestedAction\", \"reason\", \"url\"] as const;\nconst slackListFields = [\"items\", \"options\"] as const;\nconst slackTextLimit = 12_000;\nconst slackOwnerOutcomes = new Set<CommunicationOutcome>([\n \"digest\",\n \"budget_warning\",\n \"budget_exhausted\",\n]);\n\nexport interface SlackCommunicationDelivery {\n eventId: string;\n clientMessageId: string;\n text: string;\n target:\n | { type: \"owner\" }\n | { type: \"thread\"; channelId: string; threadTs: string };\n}\n\nexport interface SlackCommunicationAdapter extends CommunicationAdapter {\n readonly channel: \"slack\";\n render(event: CommunicationEvent): string;\n}\n\nexport interface SlackCommunicationAdapterOptions {\n send(delivery: SlackCommunicationDelivery): Promise<void>;\n render?: (event: CommunicationEvent) => string;\n}\n\nexport interface SlackReplyTarget {\n channelId: string;\n threadTs: string;\n}\n\n/** Parse Eve's Slack continuation token into its channel and thread target. */\nexport function parseSlackReplyAddress(address: string): SlackReplyTarget {\n const separator = address.indexOf(\":\");\n const channelId = address.slice(0, separator);\n const threadTs = address.slice(separator + 1);\n if (separator <= 0 || !/^[A-Z][A-Z0-9]+$/.test(channelId) || !/^\\d+\\.\\d+$/.test(threadTs)) {\n throw new Error(`Invalid Slack reply address: ${address}.`);\n }\n return { channelId, threadTs };\n}\n\n/** Render a provider-neutral communication event as plain Slack text. */\nexport function renderSlackCommunicationEvent(input: CommunicationEvent): string {\n const event = communicationEventSchema.parse(input);\n const headline = slackOutcomeLabels[event.outcome];\n const details = slackDetailFields.flatMap((field) => {\n const value = event.data[field];\n return typeof value === \"string\" && value.trim() !== \"\" ? [value.trim()] : [];\n });\n const lists = slackListFields.flatMap((field) => {\n const value = event.data[field];\n if (!Array.isArray(value)) return [];\n return value.flatMap((item) =>\n typeof item === \"string\" && item.trim() !== \"\" ? [`- ${item.trim()}`] : []\n );\n });\n const text = [headline, ...new Set([...details, ...lists])].join(\"\\n\");\n return text.length <= slackTextLimit ? text : `${text.slice(0, slackTextLimit - 1)}…`;\n}\n\nfunction slackClientMessageId(eventId: string): string {\n const digest = createHash(\"sha256\").update(eventId).digest(\"hex\").slice(0, 32);\n return [\n digest.slice(0, 8),\n digest.slice(8, 12),\n digest.slice(12, 16),\n digest.slice(16, 20),\n digest.slice(20),\n ].join(\"-\");\n}\n\n/** Bind Slack rendering and delivery to the channel-neutral communication contract. */\nexport function createSlackCommunicationAdapter(\n options: SlackCommunicationAdapterOptions,\n): SlackCommunicationAdapter {\n const render = options.render ?? renderSlackCommunicationEvent;\n return Object.freeze({\n channel: \"slack\" as const,\n render,\n async deliver(input: CommunicationEvent): Promise<void> {\n const event = communicationEventSchema.parse(input);\n if (event.replyTo.channel !== \"slack\") {\n throw new Error(`Slack communication adapter cannot deliver to ${event.replyTo.channel}.`);\n }\n const target = slackOwnerOutcomes.has(event.outcome)\n ? { type: \"owner\" as const }\n : { type: \"thread\" as const, ...parseSlackReplyAddress(event.replyTo.address) };\n await options.send({\n eventId: event.eventId,\n clientMessageId: slackClientMessageId(event.eventId),\n text: render(event),\n target,\n });\n },\n });\n}\n\nexport interface SlackMessageEvent {\n teamId: string;\n channelId: string;\n messageTs: string;\n threadTs: string;\n actorId: string;\n text: string;\n replyAddress: string;\n repositorySlug: string;\n payload: unknown;\n}\n\nexport interface SlackMessageSignal extends NormalizedSignal<\"slack\"> {\n repositorySlug: string;\n message: {\n teamId: string;\n channelId: string;\n messageTs: string;\n threadTs: string;\n actorId: string;\n text: string;\n };\n}\n\n/** Normalize an Eve-verified Slack message after the caller resolves its repository. */\nexport function normalizeSlackMessage(event: SlackMessageEvent): SlackMessageSignal {\n return {\n source: \"slack\",\n deliveryId: `${event.teamId}:${event.channelId}:${event.messageTs}`,\n repositorySlug: event.repositorySlug.toLowerCase(),\n replyTo: { channel: \"slack\", address: event.replyAddress },\n message: {\n teamId: event.teamId,\n channelId: event.channelId,\n messageTs: event.messageTs,\n threadTs: event.threadTs,\n actorId: event.actorId,\n text: event.text,\n },\n payload: event.payload,\n };\n}\n\nexport type SlackRepositorySelection =\n | { status: \"selected\"; repositorySlug: string }\n | { status: \"required\" }\n | { status: \"ambiguous\"; repositorySlugs: readonly string[] };\n\n/**\n * Select the only configured repository, or require an explicit owner/name\n * when a Slack surface fronts more than one repository.\n */\nexport function selectSlackRepository(\n text: string,\n repositories: readonly string[],\n): SlackRepositorySelection {\n const normalized = [...new Set(repositories.map((slug) => slug.toLowerCase()))];\n if (normalized.length === 1) {\n return { status: \"selected\", repositorySlug: normalized[0]! };\n }\n\n const matches = normalized.filter((slug) => {\n const escaped = slug.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`(^|[^a-z0-9._/-])${escaped}($|[^a-z0-9._/-])`, \"i\").test(text);\n });\n if (matches.length === 1) {\n return { status: \"selected\", repositorySlug: matches[0]! };\n }\n if (matches.length > 1) {\n return { status: \"ambiguous\", repositorySlugs: matches };\n }\n return { status: \"required\" };\n}\n"],"mappings":"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"}
|
package/dist/eve/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as Finding, k as FactoryStores, t as ApprovalPolicy } from "../approval-MoiwGpkh.mjs";
|
|
2
|
-
import { d as GitHubReviewComment } from "../github-
|
|
2
|
+
import { d as GitHubReviewComment } from "../github-DMOypeIu.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/eve/fetch.d.ts
|
|
5
5
|
/** The subset of the eve sandbox handle a credentialed git operation needs. */
|
package/dist/eve/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { i as repositoryIdSchema, o as signalIdSchema, p as vocabularySchema, s as taskIdSchema, t as changeIdSchema } from "../id-DwCihQr4.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { c as findingSchema, i as requiresApproval, o as findingFingerprint, r as defaultApprovalPolicy } from "../approval-DdvEKLiE.mjs";
|
|
3
|
+
import { g as dispatchTask, p as levelSchema, s as taskQuestionSchema, u as changeEvidenceSchema } from "../task-DMHuvBI0.mjs";
|
|
3
4
|
import { answerReviewComment, readPullRequestHead, readReviewComment } from "../github.mjs";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
import { defineHook } from "eve/hooks";
|