@agent-native/core 0.168.7 → 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/sync-calendars.ts +10 -29
- 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/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -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 { 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
|
},
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
} from "@agent-native/core/triggers";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
factoryIdSchema,
|
|
10
|
+
readAutomationFactoryId,
|
|
11
|
+
} from "../server/lib/factory-scope.js";
|
|
8
12
|
import {
|
|
9
13
|
requireWorkspaceMember,
|
|
10
14
|
workspaceMemberIdentityFromContext,
|
|
@@ -15,11 +19,11 @@ export default defineAction({
|
|
|
15
19
|
"Queue one organization-scoped Factory automation for an immediate run and return its durable run id.",
|
|
16
20
|
agentTool: false,
|
|
17
21
|
schema: z.object({
|
|
18
|
-
factoryId:
|
|
22
|
+
factoryId: factoryIdSchema,
|
|
19
23
|
automationId: z.string().trim().min(1),
|
|
20
24
|
}),
|
|
21
25
|
http: { method: "POST" },
|
|
22
|
-
run: async ({ automationId }, context) => {
|
|
26
|
+
run: async ({ factoryId, automationId }, context) => {
|
|
23
27
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
24
28
|
workspaceMemberIdentityFromContext(context),
|
|
25
29
|
);
|
|
@@ -32,6 +36,12 @@ export default defineAction({
|
|
|
32
36
|
entry.meta.domain === "factory" && entry.resource.id === automationId,
|
|
33
37
|
);
|
|
34
38
|
if (!definition) throw new Error("Factory automation not found.");
|
|
39
|
+
if (
|
|
40
|
+
readAutomationFactoryId(definition.meta, definition.resource.content) !==
|
|
41
|
+
factoryId
|
|
42
|
+
) {
|
|
43
|
+
throw new Error("Factory automation not found.");
|
|
44
|
+
}
|
|
35
45
|
return queueAutomationRunNow({
|
|
36
46
|
userEmail,
|
|
37
47
|
orgId,
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { isValidCron, nextOccurrence } from "@agent-native/core/jobs";
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "@agent-native/core/
|
|
4
|
+
resourceGetByPath,
|
|
5
|
+
resourcePutIfCurrent,
|
|
6
|
+
} from "@agent-native/core/resources";
|
|
7
|
+
import { listAutomationDefinitions } from "@agent-native/core/triggers";
|
|
6
8
|
import { z } from "zod";
|
|
7
9
|
|
|
10
|
+
import {
|
|
11
|
+
factoryIdSchema,
|
|
12
|
+
patchAutomationResource,
|
|
13
|
+
readAutomationEnabled,
|
|
14
|
+
readAutomationFactoryId,
|
|
15
|
+
readAutomationModel,
|
|
16
|
+
readAutomationSchedule,
|
|
17
|
+
resolveAutomationDisplayName,
|
|
18
|
+
setAutomationFrontmatterField,
|
|
19
|
+
} from "../server/lib/factory-scope.js";
|
|
8
20
|
import {
|
|
9
21
|
requireWorkspaceMember,
|
|
10
22
|
workspaceMemberIdentityFromContext,
|
|
@@ -12,12 +24,13 @@ import {
|
|
|
12
24
|
|
|
13
25
|
export default defineAction({
|
|
14
26
|
description:
|
|
15
|
-
"Edit a Factory automation's prompt, model, schedule, or enabled state in its organization-owned markdown resource.",
|
|
27
|
+
"Edit a Factory automation's display name, prompt, model, schedule, or enabled state in its organization-owned markdown resource.",
|
|
16
28
|
agentTool: false,
|
|
17
29
|
schema: z.object({
|
|
18
|
-
factoryId:
|
|
30
|
+
factoryId: factoryIdSchema,
|
|
19
31
|
automationId: z.string().trim().min(1),
|
|
20
32
|
name: z.string().trim().min(1).max(120),
|
|
33
|
+
displayName: z.string().trim().max(120).optional(),
|
|
21
34
|
prompt: z.string().trim().min(1).max(20_000),
|
|
22
35
|
model: z.string().trim().max(200).optional(),
|
|
23
36
|
schedule: z.string().trim().min(1).max(100),
|
|
@@ -25,7 +38,16 @@ export default defineAction({
|
|
|
25
38
|
}),
|
|
26
39
|
http: { method: "POST" },
|
|
27
40
|
run: async (
|
|
28
|
-
{
|
|
41
|
+
{
|
|
42
|
+
factoryId,
|
|
43
|
+
automationId,
|
|
44
|
+
name,
|
|
45
|
+
displayName,
|
|
46
|
+
prompt,
|
|
47
|
+
model,
|
|
48
|
+
schedule,
|
|
49
|
+
enabled,
|
|
50
|
+
},
|
|
29
51
|
context,
|
|
30
52
|
) => {
|
|
31
53
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
@@ -40,30 +62,69 @@ export default defineAction({
|
|
|
40
62
|
entry.meta.domain === "factory" && entry.resource.id === automationId,
|
|
41
63
|
);
|
|
42
64
|
if (!definition) throw new Error("Factory automation not found.");
|
|
65
|
+
if (
|
|
66
|
+
readAutomationFactoryId(definition.meta, definition.resource.content) !==
|
|
67
|
+
factoryId
|
|
68
|
+
) {
|
|
69
|
+
throw new Error("Factory automation not found.");
|
|
70
|
+
}
|
|
43
71
|
if (definition.name !== name) {
|
|
44
72
|
throw new Error(
|
|
45
73
|
"Factory automation id and name do not refer to the same automation.",
|
|
46
74
|
);
|
|
47
75
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
76
|
+
if (!definition.canUpdate) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"Only the automation's creator or an organization admin can update it.",
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (definition.meta.triggerType === "schedule" && !isValidCron(schedule)) {
|
|
82
|
+
throw new Error(`Invalid cron expression "${schedule}".`);
|
|
83
|
+
}
|
|
84
|
+
const resource = await resourceGetByPath(
|
|
85
|
+
definition.resource.owner,
|
|
86
|
+
definition.resource.path,
|
|
58
87
|
);
|
|
88
|
+
if (!resource) throw new Error("Factory automation not found.");
|
|
89
|
+
let content = patchAutomationResource(resource.content, {
|
|
90
|
+
body: prompt,
|
|
91
|
+
enabled,
|
|
92
|
+
schedule,
|
|
93
|
+
model: model?.trim() || null,
|
|
94
|
+
...(displayName !== undefined ? { displayName } : {}),
|
|
95
|
+
});
|
|
96
|
+
content = setAutomationFrontmatterField(content, "factoryId", factoryId);
|
|
97
|
+
if (definition.meta.triggerType === "schedule" && isValidCron(schedule)) {
|
|
98
|
+
const nextRun = nextOccurrence(
|
|
99
|
+
schedule,
|
|
100
|
+
undefined,
|
|
101
|
+
definition.meta.timezone,
|
|
102
|
+
).toISOString();
|
|
103
|
+
content = setAutomationFrontmatterField(content, "nextRun", nextRun);
|
|
104
|
+
}
|
|
105
|
+
const updated = await resourcePutIfCurrent({
|
|
106
|
+
owner: definition.resource.owner,
|
|
107
|
+
path: definition.resource.path,
|
|
108
|
+
content,
|
|
109
|
+
mimeType: "text/markdown",
|
|
110
|
+
expectedId: resource.id,
|
|
111
|
+
expectedUpdatedAt: resource.updatedAt,
|
|
112
|
+
expectedContent: resource.content,
|
|
113
|
+
});
|
|
114
|
+
if (!updated) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
"Factory automation changed concurrently. Refresh and try again.",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
59
119
|
return {
|
|
60
120
|
ok: true,
|
|
61
121
|
id: definition.resource.id,
|
|
62
|
-
name:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
122
|
+
name: definition.name,
|
|
123
|
+
displayName: resolveAutomationDisplayName(definition.name, content),
|
|
124
|
+
prompt,
|
|
125
|
+
model: readAutomationModel(content),
|
|
126
|
+
schedule: readAutomationSchedule(content),
|
|
127
|
+
enabled: readAutomationEnabled(content),
|
|
67
128
|
};
|
|
68
129
|
},
|
|
69
130
|
});
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
requireWorkspaceMember,
|
|
18
18
|
workspaceMemberIdentityFromContext,
|
|
19
19
|
} from "../server/lib/require-workspace-member.js";
|
|
20
|
+
import { ensureFactoryAutomations } from "../server/plugins/factory-scheduler-job.js";
|
|
20
21
|
import { stableId } from "../server/triage/ids.js";
|
|
21
22
|
|
|
22
23
|
export default defineAction({
|
|
@@ -63,8 +64,9 @@ export default defineAction({
|
|
|
63
64
|
workspaceMemberIdentityFromContext(context),
|
|
64
65
|
);
|
|
65
66
|
const db = getDb();
|
|
67
|
+
let createdNewFactory = false;
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
const result = await db.transaction(async (tx) => {
|
|
68
70
|
const existing = (
|
|
69
71
|
await tx
|
|
70
72
|
.select()
|
|
@@ -123,6 +125,7 @@ export default defineAction({
|
|
|
123
125
|
);
|
|
124
126
|
}
|
|
125
127
|
} else {
|
|
128
|
+
createdNewFactory = true;
|
|
126
129
|
await tx.insert(factoryDefinitions).values({
|
|
127
130
|
id: factoryId,
|
|
128
131
|
name,
|
|
@@ -158,5 +161,11 @@ export default defineAction({
|
|
|
158
161
|
source,
|
|
159
162
|
};
|
|
160
163
|
});
|
|
164
|
+
if (createdNewFactory) {
|
|
165
|
+
await ensureFactoryAutomations(userEmail, orgId, factoryId, {
|
|
166
|
+
enabled: false,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
161
170
|
},
|
|
162
171
|
});
|