@agent-native/core 0.168.6 → 0.168.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +8 -1
- package/corpus/templates/calendar/actions/delete-event.ts +34 -18
- package/corpus/templates/calendar/actions/delete-events.ts +13 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +22 -3
- package/corpus/templates/calendar/app/lib/calendar-timezone.ts +19 -0
- package/corpus/templates/calendar/server/lib/google-api.ts +18 -1
- package/corpus/templates/clips/actions/finalize-meeting.ts +16 -0
- package/corpus/templates/clips/actions/list-meetings.ts +1 -0
- package/corpus/templates/clips/actions/list-recordings.ts +14 -0
- package/corpus/templates/clips/actions/sync-calendars.ts +10 -29
- package/corpus/templates/clips/app/components/library/recording-card.tsx +2 -2
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +20 -3
- package/corpus/templates/clips/app/hooks/use-library.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +12 -5
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +28 -12
- package/corpus/templates/clips/desktop/src/lib/meeting-stop.ts +16 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +19 -9
- package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +5 -0
- package/corpus/templates/factory/AGENTS.md +12 -22
- package/corpus/templates/factory/README.md +11 -8
- package/corpus/templates/factory/actions/approve-factory-item.ts +35 -8
- package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +24 -9
- package/corpus/templates/factory/actions/create-factory.ts +211 -0
- package/corpus/templates/factory/actions/evaluate-triage-item.ts +29 -5
- package/corpus/templates/factory/actions/get-factory-graph.ts +1 -1
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -11
- package/corpus/templates/factory/actions/get-triage-config.ts +13 -12
- package/corpus/templates/factory/actions/get-triage-item.ts +28 -6
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +33 -17
- package/corpus/templates/factory/actions/ingest-github-observation.ts +10 -4
- package/corpus/templates/factory/actions/list-factory-audit.ts +11 -4
- package/corpus/templates/factory/actions/list-factory-automations.ts +34 -12
- package/corpus/templates/factory/actions/list-triage-items.ts +11 -3
- package/corpus/templates/factory/actions/list-triage-rules.ts +11 -4
- package/corpus/templates/factory/actions/poll-github-sources.ts +24 -10
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +26 -11
- package/corpus/templates/factory/actions/poll-slack-channel.ts +31 -13
- package/corpus/templates/factory/actions/reconcile-triage-run.ts +59 -9
- package/corpus/templates/factory/actions/record-triage-feedback.ts +14 -3
- package/corpus/templates/factory/actions/run-factory-automation.ts +12 -2
- package/corpus/templates/factory/actions/save-factory-automation.ts +82 -21
- package/corpus/templates/factory/actions/save-factory-graph.ts +10 -1
- package/corpus/templates/factory/actions/save-triage-config.ts +90 -36
- package/corpus/templates/factory/actions/save-triage-rule.ts +16 -3
- package/corpus/templates/factory/actions/start-builder-for-item.ts +28 -20
- package/corpus/templates/factory/actions/suggest-factory-rules.ts +11 -4
- package/corpus/templates/factory/actions/view-screen.ts +22 -30
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +7 -2
- package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +7 -22
- package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +260 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +2 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -3
- package/corpus/templates/factory/app/i18n/en-US.ts +18 -1
- package/corpus/templates/factory/app/routes/factory.tsx +78 -62
- package/corpus/templates/factory/app/routes/{factory-settings.tsx → new-factory.tsx} +12 -9
- package/corpus/templates/factory/server/db/schema.ts +18 -3
- package/corpus/templates/factory/server/factory-graph/contracts.ts +24 -0
- package/corpus/templates/factory/server/factory-graph/store.ts +16 -9
- package/corpus/templates/factory/server/lib/factory-automation-plan.ts +136 -0
- package/corpus/templates/factory/server/lib/factory-automation-repair.ts +31 -0
- package/corpus/templates/factory/server/lib/factory-scope.ts +416 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +11 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +4 -2
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +80 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +142 -43
- package/corpus/templates/factory/server/triage/audit.ts +4 -0
- package/corpus/templates/factory/server/triage/ids.ts +4 -0
- package/dist/cli/native-dependencies.js +4 -1
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +9 -4
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/chat-first/apps-rail.js +6 -6
- package/dist/client/chat-first/primary-nav.js +14 -6
- package/dist/client/integrations/IntegrationsPanel.d.ts +15 -0
- package/dist/client/integrations/IntegrationsPanel.js +29 -21
- package/dist/client/integrations/index.d.ts +1 -1
- package/dist/client/integrations/index.js +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/ssr-handler.js +19 -1
- package/package.json +1 -1
|
@@ -4,6 +4,12 @@ import { z } from "zod";
|
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../server/db/index.js";
|
|
6
6
|
import { triageItems, triageDecisions } from "../server/db/schema.js";
|
|
7
|
+
import { DEFAULT_FACTORY_ID } from "../server/factory-graph/store.js";
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
orgFactoryDecisionFilter,
|
|
11
|
+
orgFactoryItemFilter,
|
|
12
|
+
} from "../server/lib/factory-scope.js";
|
|
7
13
|
import {
|
|
8
14
|
requireWorkspaceMember,
|
|
9
15
|
workspaceMemberIdentityFromContext,
|
|
@@ -18,6 +24,7 @@ export default defineAction({
|
|
|
18
24
|
description:
|
|
19
25
|
"List the Factory observation queue. Results are scoped to the active workspace and include the latest shadow decision summary. Scheduled reviewers must pass needsReview true with a bounded source and limit so unchanged items are not re-reviewed.",
|
|
20
26
|
schema: z.object({
|
|
27
|
+
factoryId: factoryIdSchema.default(DEFAULT_FACTORY_ID),
|
|
21
28
|
status: triageItemStatusSchema.optional(),
|
|
22
29
|
source: triageSourceSchema.optional(),
|
|
23
30
|
needsReview: z.boolean().default(false),
|
|
@@ -25,7 +32,7 @@ export default defineAction({
|
|
|
25
32
|
}),
|
|
26
33
|
http: { method: "GET" },
|
|
27
34
|
readOnly: true,
|
|
28
|
-
run: async ({ status, source, needsReview, limit }, context) => {
|
|
35
|
+
run: async ({ factoryId, status, source, needsReview, limit }, context) => {
|
|
29
36
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
30
37
|
workspaceMemberIdentityFromContext(context),
|
|
31
38
|
);
|
|
@@ -41,7 +48,7 @@ export default defineAction({
|
|
|
41
48
|
.from(triageItems)
|
|
42
49
|
.where(
|
|
43
50
|
and(
|
|
44
|
-
|
|
51
|
+
orgFactoryItemFilter(orgId, factoryId),
|
|
45
52
|
needsReview
|
|
46
53
|
? inArray(triageItems.status, reviewStatuses)
|
|
47
54
|
: status
|
|
@@ -56,7 +63,7 @@ export default defineAction({
|
|
|
56
63
|
const decisions = await db
|
|
57
64
|
.select()
|
|
58
65
|
.from(triageDecisions)
|
|
59
|
-
.where(
|
|
66
|
+
.where(orgFactoryDecisionFilter(orgId, factoryId))
|
|
60
67
|
.orderBy(desc(triageDecisions.createdAt))
|
|
61
68
|
.limit(500);
|
|
62
69
|
const latestByItem = new Map<string, (typeof decisions)[number]>();
|
|
@@ -116,6 +123,7 @@ export default defineAction({
|
|
|
116
123
|
decision: item.latestDecision?.outcome ?? null,
|
|
117
124
|
},
|
|
118
125
|
},
|
|
126
|
+
factoryId,
|
|
119
127
|
);
|
|
120
128
|
}
|
|
121
129
|
return listedItems;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
-
import { desc
|
|
2
|
+
import { desc } from "drizzle-orm";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../server/db/index.js";
|
|
6
6
|
import { triageRules } from "../server/db/schema.js";
|
|
7
|
+
import { DEFAULT_FACTORY_ID } from "../server/factory-graph/store.js";
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
orgFactoryRuleFilter,
|
|
11
|
+
} from "../server/lib/factory-scope.js";
|
|
7
12
|
import {
|
|
8
13
|
requireWorkspaceMember,
|
|
9
14
|
workspaceMemberIdentityFromContext,
|
|
@@ -13,17 +18,19 @@ import { normalizeTriagePolicyGuards } from "../server/triage/contracts.js";
|
|
|
13
18
|
export default defineAction({
|
|
14
19
|
description:
|
|
15
20
|
"List editable Factory rules for the active workspace. Rules are disabled or shadow-only until the owner explicitly promotes them.",
|
|
16
|
-
schema: z.object({
|
|
21
|
+
schema: z.object({
|
|
22
|
+
factoryId: factoryIdSchema.default(DEFAULT_FACTORY_ID),
|
|
23
|
+
}),
|
|
17
24
|
http: { method: "GET" },
|
|
18
25
|
readOnly: true,
|
|
19
|
-
run: async (
|
|
26
|
+
run: async ({ factoryId }, context) => {
|
|
20
27
|
const { orgId } = await requireWorkspaceMember(
|
|
21
28
|
workspaceMemberIdentityFromContext(context),
|
|
22
29
|
);
|
|
23
30
|
const rows = await getDb()
|
|
24
31
|
.select()
|
|
25
32
|
.from(triageRules)
|
|
26
|
-
.where(
|
|
33
|
+
.where(orgFactoryRuleFilter(orgId, factoryId))
|
|
27
34
|
.orderBy(desc(triageRules.updatedAt));
|
|
28
35
|
return rows.map((row) => ({
|
|
29
36
|
...row,
|
|
@@ -3,7 +3,12 @@ import { and, eq } from "drizzle-orm";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../server/db/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { triageItems } from "../server/db/schema.js";
|
|
7
|
+
import { repairFactoryAutomationsFromConfig } from "../server/lib/factory-automation-repair.js";
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
readTriageConfigRow,
|
|
11
|
+
} from "../server/lib/factory-scope.js";
|
|
7
12
|
import { requireFactoryAutomation } from "../server/lib/require-factory-automation.js";
|
|
8
13
|
import {
|
|
9
14
|
requireWorkspaceMember,
|
|
@@ -30,11 +35,12 @@ export default defineAction({
|
|
|
30
35
|
description:
|
|
31
36
|
"Poll the configured GitHub repository for bounded open issues and pull requests and record them in the Factory queue. This does not write to GitHub.",
|
|
32
37
|
schema: z.object({
|
|
38
|
+
factoryId: factoryIdSchema,
|
|
33
39
|
includeIssues: z.boolean().default(true),
|
|
34
40
|
includePullRequests: z.boolean().default(true),
|
|
35
41
|
}),
|
|
36
42
|
http: false,
|
|
37
|
-
run: async ({ includeIssues, includePullRequests }, context) => {
|
|
43
|
+
run: async ({ factoryId, includeIssues, includePullRequests }, context) => {
|
|
38
44
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
39
45
|
workspaceMemberIdentityFromContext(context),
|
|
40
46
|
);
|
|
@@ -42,14 +48,11 @@ export default defineAction({
|
|
|
42
48
|
context,
|
|
43
49
|
{ userEmail, orgId },
|
|
44
50
|
"sourcePolling",
|
|
51
|
+
factoryId,
|
|
45
52
|
);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.from(triageConfig)
|
|
50
|
-
.where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
|
|
51
|
-
.limit(1)
|
|
52
|
-
)[0];
|
|
53
|
+
const db = getDb();
|
|
54
|
+
const config = await readTriageConfigRow(db, orgId, factoryId);
|
|
55
|
+
await repairFactoryAutomationsFromConfig(userEmail, orgId, factoryId);
|
|
53
56
|
if (config?.githubPollingEnabled !== 1) {
|
|
54
57
|
throw new Error("Enable GitHub polling before polling GitHub sources.");
|
|
55
58
|
}
|
|
@@ -68,7 +71,6 @@ export default defineAction({
|
|
|
68
71
|
? client.listOpenPullRequests(repository, 50)
|
|
69
72
|
: Promise.resolve([]),
|
|
70
73
|
]);
|
|
71
|
-
const db = getDb();
|
|
72
74
|
const now = new Date().toISOString();
|
|
73
75
|
let issueCount = 0;
|
|
74
76
|
let pullRequestCount = 0;
|
|
@@ -81,6 +83,7 @@ export default defineAction({
|
|
|
81
83
|
externalId: `${repositoryName}#${issue.number}`,
|
|
82
84
|
},
|
|
83
85
|
orgId,
|
|
86
|
+
factoryId,
|
|
84
87
|
);
|
|
85
88
|
const existing = (
|
|
86
89
|
await tx
|
|
@@ -133,6 +136,7 @@ export default defineAction({
|
|
|
133
136
|
updatedAt,
|
|
134
137
|
ownerEmail: existing?.ownerEmail ?? userEmail,
|
|
135
138
|
orgId,
|
|
139
|
+
factoryId,
|
|
136
140
|
})
|
|
137
141
|
.onConflictDoUpdate({
|
|
138
142
|
target: triageItems.id,
|
|
@@ -144,6 +148,7 @@ export default defineAction({
|
|
|
144
148
|
metadataJson: metadata,
|
|
145
149
|
lastSeenAt,
|
|
146
150
|
updatedAt,
|
|
151
|
+
factoryId,
|
|
147
152
|
},
|
|
148
153
|
});
|
|
149
154
|
issueCount += 1;
|
|
@@ -158,6 +163,7 @@ export default defineAction({
|
|
|
158
163
|
pullRequestNumber: pullRequest.number,
|
|
159
164
|
},
|
|
160
165
|
orgId,
|
|
166
|
+
factoryId,
|
|
161
167
|
);
|
|
162
168
|
const existing = (
|
|
163
169
|
await tx
|
|
@@ -213,6 +219,7 @@ export default defineAction({
|
|
|
213
219
|
updatedAt,
|
|
214
220
|
ownerEmail: existing?.ownerEmail ?? userEmail,
|
|
215
221
|
orgId,
|
|
222
|
+
factoryId,
|
|
216
223
|
})
|
|
217
224
|
.onConflictDoUpdate({
|
|
218
225
|
target: triageItems.id,
|
|
@@ -227,6 +234,7 @@ export default defineAction({
|
|
|
227
234
|
metadataJson: metadata,
|
|
228
235
|
lastSeenAt,
|
|
229
236
|
updatedAt,
|
|
237
|
+
factoryId,
|
|
230
238
|
},
|
|
231
239
|
});
|
|
232
240
|
pullRequestCount += 1;
|
|
@@ -244,6 +252,7 @@ export default defineAction({
|
|
|
244
252
|
summary: "No open GitHub issues or pull requests were observed.",
|
|
245
253
|
details: { repository: repositoryName },
|
|
246
254
|
},
|
|
255
|
+
factoryId,
|
|
247
256
|
);
|
|
248
257
|
} else {
|
|
249
258
|
for (const issue of issues) {
|
|
@@ -259,12 +268,14 @@ export default defineAction({
|
|
|
259
268
|
externalId: `${repositoryName}#${issue.number}`,
|
|
260
269
|
},
|
|
261
270
|
orgId,
|
|
271
|
+
factoryId,
|
|
262
272
|
),
|
|
263
273
|
source: "github_issue",
|
|
264
274
|
sourceUrl: issue.htmlUrl,
|
|
265
275
|
summary: issue.title,
|
|
266
276
|
details: { repository: repositoryName, number: issue.number },
|
|
267
277
|
},
|
|
278
|
+
factoryId,
|
|
268
279
|
);
|
|
269
280
|
}
|
|
270
281
|
for (const pullRequest of pullRequests) {
|
|
@@ -282,18 +293,21 @@ export default defineAction({
|
|
|
282
293
|
pullRequestNumber: pullRequest.number,
|
|
283
294
|
},
|
|
284
295
|
orgId,
|
|
296
|
+
factoryId,
|
|
285
297
|
),
|
|
286
298
|
source: "github",
|
|
287
299
|
sourceUrl: pullRequest.htmlUrl,
|
|
288
300
|
summary: pullRequest.title,
|
|
289
301
|
details: { repository: repositoryName, number: pullRequest.number },
|
|
290
302
|
},
|
|
303
|
+
factoryId,
|
|
291
304
|
);
|
|
292
305
|
}
|
|
293
306
|
}
|
|
294
307
|
|
|
295
308
|
return {
|
|
296
309
|
ok: true,
|
|
310
|
+
factoryId,
|
|
297
311
|
repository: repositoryName,
|
|
298
312
|
issues: issueCount,
|
|
299
313
|
pullRequests: pullRequestCount,
|
|
@@ -4,6 +4,12 @@ import { z } from "zod";
|
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../server/db/index.js";
|
|
6
6
|
import { triageConfig, triageItems } from "../server/db/schema.js";
|
|
7
|
+
import { repairFactoryAutomationsFromConfig } from "../server/lib/factory-automation-repair.js";
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
readTriageConfigRow,
|
|
11
|
+
triageConfigUpdateRowId,
|
|
12
|
+
} from "../server/lib/factory-scope.js";
|
|
7
13
|
import { requireFactoryAutomation } from "../server/lib/require-factory-automation.js";
|
|
8
14
|
import {
|
|
9
15
|
requireWorkspaceMember,
|
|
@@ -21,9 +27,12 @@ import { createSentryClient } from "../server/triage/sentry-client.js";
|
|
|
21
27
|
export default defineAction({
|
|
22
28
|
description:
|
|
23
29
|
"Poll bounded unresolved Sentry issues for the configured organization and record them in the Factory queue. This does not change Sentry.",
|
|
24
|
-
schema: z.object({
|
|
30
|
+
schema: z.object({
|
|
31
|
+
factoryId: factoryIdSchema,
|
|
32
|
+
limit: z.number().int().min(1).max(50).default(25),
|
|
33
|
+
}),
|
|
25
34
|
http: false,
|
|
26
|
-
run: async ({ limit }, context) => {
|
|
35
|
+
run: async ({ factoryId, limit }, context) => {
|
|
27
36
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
28
37
|
workspaceMemberIdentityFromContext(context),
|
|
29
38
|
);
|
|
@@ -31,14 +40,11 @@ export default defineAction({
|
|
|
31
40
|
context,
|
|
32
41
|
{ userEmail, orgId },
|
|
33
42
|
"sourcePolling",
|
|
43
|
+
factoryId,
|
|
34
44
|
);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.from(triageConfig)
|
|
39
|
-
.where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
|
|
40
|
-
.limit(1)
|
|
41
|
-
)[0];
|
|
45
|
+
const db = getDb();
|
|
46
|
+
const config = await readTriageConfigRow(db, orgId, factoryId);
|
|
47
|
+
await repairFactoryAutomationsFromConfig(userEmail, orgId, factoryId);
|
|
42
48
|
if (config?.sentryPollingEnabled !== 1) {
|
|
43
49
|
throw new Error("Enable Sentry polling before polling Sentry.");
|
|
44
50
|
}
|
|
@@ -74,14 +80,15 @@ export default defineAction({
|
|
|
74
80
|
);
|
|
75
81
|
}
|
|
76
82
|
const observedIssues = issues;
|
|
77
|
-
const db = getDb();
|
|
78
83
|
const now = new Date().toISOString();
|
|
84
|
+
const configRowId = triageConfigUpdateRowId(config, orgId, factoryId);
|
|
79
85
|
|
|
80
86
|
await db.transaction(async (tx) => {
|
|
81
87
|
for (const issue of observedIssues) {
|
|
82
88
|
const id = itemDedupeKey(
|
|
83
89
|
{ source: "sentry", externalId: issue.id },
|
|
84
90
|
orgId,
|
|
91
|
+
factoryId,
|
|
85
92
|
);
|
|
86
93
|
const existing = (
|
|
87
94
|
await tx
|
|
@@ -140,6 +147,7 @@ export default defineAction({
|
|
|
140
147
|
updatedAt,
|
|
141
148
|
ownerEmail: existing?.ownerEmail ?? userEmail,
|
|
142
149
|
orgId,
|
|
150
|
+
factoryId,
|
|
143
151
|
})
|
|
144
152
|
.onConflictDoUpdate({
|
|
145
153
|
target: triageItems.id,
|
|
@@ -151,6 +159,7 @@ export default defineAction({
|
|
|
151
159
|
metadataJson: metadata,
|
|
152
160
|
lastSeenAt,
|
|
153
161
|
updatedAt,
|
|
162
|
+
factoryId,
|
|
154
163
|
},
|
|
155
164
|
});
|
|
156
165
|
}
|
|
@@ -167,7 +176,9 @@ export default defineAction({
|
|
|
167
176
|
}, config.lastSentrySeenAt) ?? config.lastSentrySeenAt,
|
|
168
177
|
updatedAt: now,
|
|
169
178
|
})
|
|
170
|
-
.where(
|
|
179
|
+
.where(
|
|
180
|
+
and(eq(triageConfig.id, configRowId), eq(triageConfig.orgId, orgId)),
|
|
181
|
+
);
|
|
171
182
|
});
|
|
172
183
|
|
|
173
184
|
if (observedIssues.length === 0) {
|
|
@@ -181,6 +192,7 @@ export default defineAction({
|
|
|
181
192
|
summary: "No unresolved Sentry errors were observed.",
|
|
182
193
|
details: { sentryOrgSlug: config.sentryOrgSlug },
|
|
183
194
|
},
|
|
195
|
+
factoryId,
|
|
184
196
|
);
|
|
185
197
|
} else {
|
|
186
198
|
for (const issue of observedIssues) {
|
|
@@ -193,6 +205,7 @@ export default defineAction({
|
|
|
193
205
|
itemId: itemDedupeKey(
|
|
194
206
|
{ source: "sentry", externalId: issue.id },
|
|
195
207
|
orgId,
|
|
208
|
+
factoryId,
|
|
196
209
|
),
|
|
197
210
|
source: "sentry",
|
|
198
211
|
sourceUrl: issue.permalink,
|
|
@@ -204,12 +217,14 @@ export default defineAction({
|
|
|
204
217
|
projectSlug: issue.projectSlug,
|
|
205
218
|
},
|
|
206
219
|
},
|
|
220
|
+
factoryId,
|
|
207
221
|
);
|
|
208
222
|
}
|
|
209
223
|
}
|
|
210
224
|
|
|
211
225
|
return {
|
|
212
226
|
ok: true,
|
|
227
|
+
factoryId,
|
|
213
228
|
observed: observedIssues.length,
|
|
214
229
|
fetched: issues.length,
|
|
215
230
|
sentryOrgSlug: config.sentryOrgSlug,
|
|
@@ -4,6 +4,12 @@ import { z } from "zod";
|
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../server/db/index.js";
|
|
6
6
|
import { triageConfig, triageItems } from "../server/db/schema.js";
|
|
7
|
+
import { repairFactoryAutomationsFromConfig } from "../server/lib/factory-automation-repair.js";
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
readTriageConfigRow,
|
|
11
|
+
triageConfigUpdateRowId,
|
|
12
|
+
} from "../server/lib/factory-scope.js";
|
|
7
13
|
import { requireFactoryAutomation } from "../server/lib/require-factory-automation.js";
|
|
8
14
|
import {
|
|
9
15
|
requireWorkspaceMember,
|
|
@@ -21,10 +27,11 @@ export default defineAction({
|
|
|
21
27
|
description:
|
|
22
28
|
"Poll the configured Slack channel and append new messages to the Factory queue. This action only observes and never posts, replies, starts work, or changes a provider.",
|
|
23
29
|
schema: z.object({
|
|
30
|
+
factoryId: factoryIdSchema,
|
|
24
31
|
channelId: z.string().trim().min(1).max(128).optional(),
|
|
25
32
|
}),
|
|
26
33
|
http: false,
|
|
27
|
-
run: async ({ channelId: requestedChannelId }, context) => {
|
|
34
|
+
run: async ({ factoryId, channelId: requestedChannelId }, context) => {
|
|
28
35
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
29
36
|
workspaceMemberIdentityFromContext(context),
|
|
30
37
|
);
|
|
@@ -32,22 +39,24 @@ export default defineAction({
|
|
|
32
39
|
context,
|
|
33
40
|
{ userEmail, orgId },
|
|
34
41
|
"sourcePolling",
|
|
42
|
+
factoryId,
|
|
35
43
|
);
|
|
36
44
|
const db = getDb();
|
|
37
|
-
const config = (
|
|
38
|
-
|
|
39
|
-
.select()
|
|
40
|
-
.from(triageConfig)
|
|
41
|
-
.where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
|
|
42
|
-
.limit(1)
|
|
43
|
-
)[0];
|
|
45
|
+
const config = await readTriageConfigRow(db, orgId, factoryId);
|
|
46
|
+
await repairFactoryAutomationsFromConfig(userEmail, orgId, factoryId);
|
|
44
47
|
if (config?.pollingEnabled !== 1) {
|
|
45
48
|
throw new Error("Enable Slack polling before polling Slack.");
|
|
46
49
|
}
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
50
|
+
const configuredChannelId = config?.slackChannelId;
|
|
51
|
+
if (!configuredChannelId) {
|
|
49
52
|
throw new Error("Configure a Slack channel before polling.");
|
|
50
53
|
}
|
|
54
|
+
if (requestedChannelId && requestedChannelId !== configuredChannelId) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
"Poll only the Slack channel configured for this factory.",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const channelId = configuredChannelId;
|
|
51
60
|
|
|
52
61
|
const result = await pollSlackChannel({
|
|
53
62
|
workspace:
|
|
@@ -59,10 +68,11 @@ export default defineAction({
|
|
|
59
68
|
orgId,
|
|
60
69
|
});
|
|
61
70
|
const now = new Date().toISOString();
|
|
71
|
+
const configRowId = triageConfigUpdateRowId(config, orgId, factoryId);
|
|
62
72
|
|
|
63
73
|
await db.transaction(async (tx) => {
|
|
64
74
|
for (const envelope of result.envelopes) {
|
|
65
|
-
const id = itemDedupeKey(envelope, orgId);
|
|
75
|
+
const id = itemDedupeKey(envelope, orgId, factoryId);
|
|
66
76
|
const existing = (
|
|
67
77
|
await tx
|
|
68
78
|
.select()
|
|
@@ -117,6 +127,7 @@ export default defineAction({
|
|
|
117
127
|
updatedAt,
|
|
118
128
|
ownerEmail: userEmail,
|
|
119
129
|
orgId,
|
|
130
|
+
factoryId,
|
|
120
131
|
})
|
|
121
132
|
.onConflictDoUpdate({
|
|
122
133
|
target: triageItems.id,
|
|
@@ -131,6 +142,7 @@ export default defineAction({
|
|
|
131
142
|
status,
|
|
132
143
|
lastSeenAt,
|
|
133
144
|
updatedAt,
|
|
145
|
+
factoryId,
|
|
134
146
|
},
|
|
135
147
|
});
|
|
136
148
|
}
|
|
@@ -144,7 +156,10 @@ export default defineAction({
|
|
|
144
156
|
updatedAt: now,
|
|
145
157
|
})
|
|
146
158
|
.where(
|
|
147
|
-
and(
|
|
159
|
+
and(
|
|
160
|
+
eq(triageConfig.id, configRowId),
|
|
161
|
+
eq(triageConfig.orgId, orgId),
|
|
162
|
+
),
|
|
148
163
|
);
|
|
149
164
|
}
|
|
150
165
|
});
|
|
@@ -163,6 +178,7 @@ export default defineAction({
|
|
|
163
178
|
coverage: result.hasMore ? "partial" : "complete",
|
|
164
179
|
},
|
|
165
180
|
},
|
|
181
|
+
factoryId,
|
|
166
182
|
);
|
|
167
183
|
} else {
|
|
168
184
|
for (const envelope of result.envelopes) {
|
|
@@ -172,7 +188,7 @@ export default defineAction({
|
|
|
172
188
|
{
|
|
173
189
|
action: "poll-slack-channel",
|
|
174
190
|
kind: "observed",
|
|
175
|
-
itemId: itemDedupeKey(envelope, orgId),
|
|
191
|
+
itemId: itemDedupeKey(envelope, orgId, factoryId),
|
|
176
192
|
source: envelope.source,
|
|
177
193
|
sourceUrl: envelope.sourceUrl ?? null,
|
|
178
194
|
summary: envelope.summary ?? envelope.title,
|
|
@@ -182,6 +198,7 @@ export default defineAction({
|
|
|
182
198
|
coverage: envelope.coverage,
|
|
183
199
|
},
|
|
184
200
|
},
|
|
201
|
+
factoryId,
|
|
185
202
|
);
|
|
186
203
|
}
|
|
187
204
|
}
|
|
@@ -189,6 +206,7 @@ export default defineAction({
|
|
|
189
206
|
return {
|
|
190
207
|
ok: true,
|
|
191
208
|
source: "slack",
|
|
209
|
+
factoryId,
|
|
192
210
|
channelId,
|
|
193
211
|
observed: result.envelopes.length,
|
|
194
212
|
hasMore: result.hasMore,
|
|
@@ -4,6 +4,11 @@ import { z } from "zod";
|
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../server/db/index.js";
|
|
6
6
|
import { triageItems, triageRuns } from "../server/db/schema.js";
|
|
7
|
+
import { DEFAULT_FACTORY_ID } from "../server/factory-graph/store.js";
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
orgFactoryScopedItemWhere,
|
|
11
|
+
} from "../server/lib/factory-scope.js";
|
|
7
12
|
import {
|
|
8
13
|
requireWorkspaceMember,
|
|
9
14
|
workspaceMemberIdentityFromContext,
|
|
@@ -48,6 +53,7 @@ export default defineAction({
|
|
|
48
53
|
"Reconcile an observe-only pull-request monitoring run from ai-services callback and provider observations. Missing callbacks or provider reads remain typed failure states; no executor or GitHub write occurs.",
|
|
49
54
|
schema: z.object({
|
|
50
55
|
itemId: z.string().min(1),
|
|
56
|
+
factoryId: factoryIdSchema.optional(),
|
|
51
57
|
runId: z.string().min(1),
|
|
52
58
|
source: triageSourceSchema.default("github"),
|
|
53
59
|
callback: z
|
|
@@ -70,6 +76,7 @@ export default defineAction({
|
|
|
70
76
|
run: async (
|
|
71
77
|
{
|
|
72
78
|
itemId,
|
|
79
|
+
factoryId: factoryIdInput,
|
|
73
80
|
runId,
|
|
74
81
|
source,
|
|
75
82
|
callback,
|
|
@@ -82,8 +89,25 @@ export default defineAction({
|
|
|
82
89
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
83
90
|
workspaceMemberIdentityFromContext(context),
|
|
84
91
|
);
|
|
92
|
+
const db = getDb();
|
|
93
|
+
const item = (
|
|
94
|
+
await db
|
|
95
|
+
.select({ factoryId: triageItems.factoryId })
|
|
96
|
+
.from(triageItems)
|
|
97
|
+
.where(and(eq(triageItems.id, itemId), eq(triageItems.orgId, orgId)))
|
|
98
|
+
.limit(1)
|
|
99
|
+
)[0];
|
|
100
|
+
if (!item)
|
|
101
|
+
throw new Error("Factory item not found for run reconciliation.");
|
|
102
|
+
const itemFactoryId = item.factoryId ?? DEFAULT_FACTORY_ID;
|
|
103
|
+
if (factoryIdInput && factoryIdInput !== itemFactoryId) {
|
|
104
|
+
throw new Error("Factory item does not belong to this factory.");
|
|
105
|
+
}
|
|
106
|
+
const factoryId = itemFactoryId;
|
|
85
107
|
const now = new Date().toISOString();
|
|
86
|
-
const
|
|
108
|
+
const scopedRunId = stableId("run", orgId, factoryId, runId);
|
|
109
|
+
const legacyRunId =
|
|
110
|
+
factoryId === DEFAULT_FACTORY_ID ? stableId("run", orgId, runId) : null;
|
|
87
111
|
const result = reconcilePullRequestRun({
|
|
88
112
|
triageItemId: itemId,
|
|
89
113
|
runId,
|
|
@@ -93,17 +117,42 @@ export default defineAction({
|
|
|
93
117
|
now,
|
|
94
118
|
timeoutMs,
|
|
95
119
|
});
|
|
96
|
-
const db = getDb();
|
|
97
120
|
const existingRun = (
|
|
98
121
|
await db
|
|
99
|
-
.select({
|
|
122
|
+
.select({
|
|
123
|
+
id: triageRuns.id,
|
|
124
|
+
progressLogJson: triageRuns.progressLogJson,
|
|
125
|
+
factoryId: triageRuns.factoryId,
|
|
126
|
+
})
|
|
100
127
|
.from(triageRuns)
|
|
101
|
-
.where(
|
|
102
|
-
and(eq(triageRuns.id, databaseRunId), eq(triageRuns.orgId, orgId)),
|
|
103
|
-
)
|
|
128
|
+
.where(and(eq(triageRuns.id, scopedRunId), eq(triageRuns.orgId, orgId)))
|
|
104
129
|
.limit(1)
|
|
105
130
|
)[0];
|
|
106
|
-
|
|
131
|
+
let databaseRunId = scopedRunId;
|
|
132
|
+
let existing = existingRun;
|
|
133
|
+
if (!existing && legacyRunId) {
|
|
134
|
+
const legacyRun = (
|
|
135
|
+
await db
|
|
136
|
+
.select({
|
|
137
|
+
id: triageRuns.id,
|
|
138
|
+
progressLogJson: triageRuns.progressLogJson,
|
|
139
|
+
factoryId: triageRuns.factoryId,
|
|
140
|
+
})
|
|
141
|
+
.from(triageRuns)
|
|
142
|
+
.where(
|
|
143
|
+
and(eq(triageRuns.id, legacyRunId), eq(triageRuns.orgId, orgId)),
|
|
144
|
+
)
|
|
145
|
+
.limit(1)
|
|
146
|
+
)[0];
|
|
147
|
+
if (legacyRun) {
|
|
148
|
+
existing = legacyRun;
|
|
149
|
+
databaseRunId = legacyRunId;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (existing && (existing.factoryId ?? DEFAULT_FACTORY_ID) !== factoryId) {
|
|
153
|
+
throw new Error("Run id is already used by another factory.");
|
|
154
|
+
}
|
|
155
|
+
const progressLog = appendProgressLog(existing?.progressLogJson, {
|
|
107
156
|
at: now,
|
|
108
157
|
state: result.state,
|
|
109
158
|
reason: result.reason,
|
|
@@ -131,6 +180,7 @@ export default defineAction({
|
|
|
131
180
|
: null,
|
|
132
181
|
ownerEmail: userEmail,
|
|
133
182
|
orgId,
|
|
183
|
+
factoryId,
|
|
134
184
|
})
|
|
135
185
|
.onConflictDoUpdate({
|
|
136
186
|
target: triageRuns.id,
|
|
@@ -154,12 +204,12 @@ export default defineAction({
|
|
|
154
204
|
await tx
|
|
155
205
|
.update(triageItems)
|
|
156
206
|
.set(result.triageItemPatch)
|
|
157
|
-
.where(
|
|
207
|
+
.where(orgFactoryScopedItemWhere(itemId, orgId, factoryId));
|
|
158
208
|
} else if (result.state === "reconciliation_required") {
|
|
159
209
|
await tx
|
|
160
210
|
.update(triageItems)
|
|
161
211
|
.set({ status: "reconciliation_required", updatedAt: now })
|
|
162
|
-
.where(
|
|
212
|
+
.where(orgFactoryScopedItemWhere(itemId, orgId, factoryId));
|
|
163
213
|
}
|
|
164
214
|
});
|
|
165
215
|
|
|
@@ -6,6 +6,11 @@ import { z } from "zod";
|
|
|
6
6
|
|
|
7
7
|
import { getDb } from "../server/db/index.js";
|
|
8
8
|
import { triageDecisions, triageFeedback } from "../server/db/schema.js";
|
|
9
|
+
import { DEFAULT_FACTORY_ID } from "../server/factory-graph/store.js";
|
|
10
|
+
import {
|
|
11
|
+
factoryIdSchema,
|
|
12
|
+
orgFactoryDecisionFilter,
|
|
13
|
+
} from "../server/lib/factory-scope.js";
|
|
9
14
|
import {
|
|
10
15
|
requireWorkspaceMember,
|
|
11
16
|
workspaceMemberIdentityFromContext,
|
|
@@ -18,27 +23,32 @@ export default defineAction({
|
|
|
18
23
|
"Record human feedback on a Factory shadow decision. Feedback is append-only and changes no provider or executor state.",
|
|
19
24
|
schema: z.object({
|
|
20
25
|
decisionId: z.string().min(1),
|
|
26
|
+
factoryId: factoryIdSchema.default(DEFAULT_FACTORY_ID),
|
|
21
27
|
verdict: verdictSchema,
|
|
22
28
|
note: z.string().trim().max(2_000).optional(),
|
|
23
29
|
}),
|
|
24
30
|
http: { method: "POST" },
|
|
25
|
-
run: async ({ decisionId, verdict, note }, context) => {
|
|
31
|
+
run: async ({ decisionId, factoryId, verdict, note }, context) => {
|
|
26
32
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
27
33
|
workspaceMemberIdentityFromContext(context),
|
|
28
34
|
);
|
|
29
35
|
const decision = (
|
|
30
36
|
await getDb()
|
|
31
|
-
.select({
|
|
37
|
+
.select({
|
|
38
|
+
id: triageDecisions.id,
|
|
39
|
+
factoryId: triageDecisions.factoryId,
|
|
40
|
+
})
|
|
32
41
|
.from(triageDecisions)
|
|
33
42
|
.where(
|
|
34
43
|
and(
|
|
35
44
|
eq(triageDecisions.id, decisionId),
|
|
36
|
-
|
|
45
|
+
orgFactoryDecisionFilter(orgId, factoryId),
|
|
37
46
|
),
|
|
38
47
|
)
|
|
39
48
|
.limit(1)
|
|
40
49
|
)[0];
|
|
41
50
|
if (!decision) throw new Error("Triage decision not found");
|
|
51
|
+
const resolvedFactoryId = decision.factoryId ?? factoryId;
|
|
42
52
|
|
|
43
53
|
const createdAt = new Date().toISOString();
|
|
44
54
|
const id = randomUUID();
|
|
@@ -52,6 +62,7 @@ export default defineAction({
|
|
|
52
62
|
createdAt,
|
|
53
63
|
ownerEmail: userEmail,
|
|
54
64
|
orgId,
|
|
65
|
+
factoryId: resolvedFactoryId,
|
|
55
66
|
});
|
|
56
67
|
return { ok: true, id };
|
|
57
68
|
},
|