@agent-native/core 0.136.4 → 0.137.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +53 -0
  3. package/corpus/templates/clips/actions/import-loom-recording.ts +7 -0
  4. package/corpus/templates/clips/actions/lib/loom-import-job.ts +24 -4
  5. package/corpus/templates/clips/app/components/import-menu.tsx +109 -0
  6. package/corpus/templates/clips/app/components/library/library-grid.tsx +8 -18
  7. package/corpus/templates/clips/app/components/library/library-layout.tsx +14 -50
  8. package/corpus/templates/clips/app/components/library/sort-menu.tsx +20 -6
  9. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +10 -34
  10. package/corpus/templates/clips/app/routes/_app.library._index.tsx +0 -2
  11. package/corpus/templates/clips/changelog/2026-08-03-restart-during-a-recording-now-immediately-starts-a-fresh-ta.md +6 -0
  12. package/corpus/templates/clips/changelog/2026-08-04-cleaner-library-controls-with-an-icon-only-sort-button.md +6 -0
  13. package/corpus/templates/clips/changelog/2026-08-04-upload-and-loom-imports-now-live-under-one-import-menu.md +6 -0
  14. package/corpus/templates/clips/desktop/src/app.tsx +81 -30
  15. package/corpus/templates/clips/desktop/src/lib/recorder.ts +436 -158
  16. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +14 -1
  17. package/corpus/templates/clips/server/plugins/auth.ts +9 -0
  18. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +13 -0
  19. package/corpus/templates/content/actions/_database-utils.ts +6 -0
  20. package/corpus/templates/content/actions/bind-content-database-source-field.ts +273 -97
  21. package/corpus/templates/content/actions/change-content-database-source-role.ts +65 -29
  22. package/corpus/templates/content/actions/configure-document-property.ts +113 -32
  23. package/corpus/templates/content/actions/delete-content-database.ts +5 -7
  24. package/corpus/templates/content/actions/delete-document-property.ts +109 -62
  25. package/corpus/templates/content/actions/delete-document.ts +178 -36
  26. package/corpus/templates/content/actions/duplicate-database-item.ts +27 -0
  27. package/corpus/templates/content/actions/duplicate-database-items.ts +31 -1
  28. package/corpus/templates/content/actions/permanently-delete-document.ts +4 -6
  29. package/corpus/templates/content/actions/remove-database-items.ts +13 -0
  30. package/corpus/templates/content/actions/set-document-property.ts +69 -1
  31. package/corpus/templates/content/actions/upsert-database-item-by-key.ts +814 -0
  32. package/corpus/templates/content/parity/matrix.md +1 -1
  33. package/corpus/templates/content/parity/matrix.ts +1 -0
  34. package/corpus/templates/content/server/db/schema.ts +27 -0
  35. package/corpus/templates/content/server/plugins/db.ts +24 -0
  36. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +122 -114
  37. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  38. package/corpus/templates/design/app/components/design/EditPanel.tsx +68 -34
  39. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +78 -4
  40. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +8 -0
  41. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +5 -2
  42. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +10 -9
  43. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +31 -69
  44. package/corpus/templates/design/app/components/design/inspector/design-icons.tsx +80 -106
  45. package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -0
  46. package/corpus/templates/design/app/hooks/use-editor-preferences.ts +75 -0
  47. package/corpus/templates/design/app/i18n-data.ts +8 -0
  48. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +61 -0
  49. package/corpus/templates/design/app/pages/design-editor/editor-preferences.ts +98 -0
  50. package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +491 -0
  51. package/corpus/templates/design/app/pages/design-editor/paste-placement.ts +133 -0
  52. package/corpus/templates/design/app/pages/design-editor/png-export-render.ts +8 -0
  53. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -12
  54. package/corpus/templates/design/changelog/2026-08-03-arrow-keys-now-reorder-a-layer-inside-an-auto-layout-frame.md +6 -0
  55. package/corpus/templates/design/changelog/2026-08-03-auto-layout-flow-icons-show-the-direction-at-a-glance-instea.md +6 -0
  56. package/corpus/templates/design/changelog/2026-08-03-auto-layout-icons-share-one-outlined-square-style-so-flow-ga.md +6 -0
  57. package/corpus/templates/design/changelog/2026-08-03-export-has-a-named-preview-section-you-can-expand-replacing-.md +6 -0
  58. package/corpus/templates/design/changelog/2026-08-03-export-preview-shows-the-selected-element-instead-of-the-who.md +6 -0
  59. package/corpus/templates/design/changelog/2026-08-03-inspector-sections-show-an-expand-indicator-so-it-is-clear-w.md +6 -0
  60. package/corpus/templates/design/changelog/2026-08-03-paste-lands-inside-a-selected-frame-instead-of-beside-it.md +6 -0
  61. package/corpus/templates/design/changelog/2026-08-03-the-add-breakpoint-button-sits-outside-the-breakpoint-select.md +6 -0
  62. package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-keep-space-below-the-continu.md +6 -0
  63. package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-share-the-chat-panel-s-backg.md +6 -0
  64. package/corpus/templates/design/changelog/2026-08-03-the-export-panel-no-longer-stacks-a-duplicate-export-block-o.md +6 -0
  65. package/corpus/templates/design/changelog/2026-08-03-the-text-tool-uses-a-t-icon-matching-the-letter-it-inserts.md +6 -0
  66. package/corpus/templates/factory/.env.example +17 -0
  67. package/corpus/templates/factory/AGENTS.md +27 -17
  68. package/corpus/templates/factory/README.md +19 -9
  69. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +90 -0
  70. package/corpus/templates/factory/actions/get-triage-config.ts +14 -3
  71. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +386 -0
  72. package/corpus/templates/factory/actions/list-factory-automations.ts +53 -0
  73. package/corpus/templates/factory/actions/list-triage-items.ts +7 -2
  74. package/corpus/templates/factory/actions/poll-github-sources.ts +203 -0
  75. package/corpus/templates/factory/actions/poll-sentry-errors.ts +161 -0
  76. package/corpus/templates/factory/actions/poll-slack-channel.ts +4 -2
  77. package/corpus/templates/factory/actions/run-factory-automation.ts +42 -0
  78. package/corpus/templates/factory/actions/save-factory-automation.ts +69 -0
  79. package/corpus/templates/factory/actions/save-triage-config.ts +28 -2
  80. package/corpus/templates/factory/actions/start-builder-for-item.ts +411 -0
  81. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +14 -3
  82. package/corpus/templates/factory/app/components/ui/checkbox.tsx +1 -0
  83. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +7 -0
  84. package/corpus/templates/factory/app/i18n/en-US.ts +35 -1
  85. package/corpus/templates/factory/app/routes/factory.tsx +462 -9
  86. package/corpus/templates/factory/changelog/2026-08-04-added-organization-owned-factory-automations-for-slack-bug-t.md +6 -0
  87. package/corpus/templates/factory/changelog/2026-08-04-automation-editor-controls-and-model-picker.md +6 -0
  88. package/corpus/templates/factory/netlify.toml +2 -2
  89. package/corpus/templates/factory/server/connectors/credentials.ts +6 -5
  90. package/corpus/templates/factory/server/connectors/slack.ts +133 -5
  91. package/corpus/templates/factory/server/db/schema.ts +6 -0
  92. package/corpus/templates/factory/server/lib/require-factory-automation.ts +65 -0
  93. package/corpus/templates/factory/server/plugins/agent-chat.ts +29 -11
  94. package/corpus/templates/factory/server/plugins/auth.ts +1 -0
  95. package/corpus/templates/factory/server/plugins/factory-migrations.ts +12 -0
  96. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +218 -67
  97. package/corpus/templates/factory/server/triage/builder-executor.ts +4 -0
  98. package/corpus/templates/factory/server/triage/contracts.ts +14 -1
  99. package/corpus/templates/factory/server/triage/github-client.ts +404 -0
  100. package/corpus/templates/factory/server/triage/metadata.ts +44 -0
  101. package/corpus/templates/factory/server/triage/pr-babysit.ts +5 -5
  102. package/corpus/templates/factory/server/triage/pr-policy.ts +144 -0
  103. package/corpus/templates/factory/server/triage/sentry-client.ts +180 -0
  104. package/corpus/templates/factory/server/triage/slack-client.ts +43 -0
  105. package/corpus/templates/factory/server/triage/slack-poller.ts +9 -2
  106. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +27 -0
  107. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +14 -4
  108. package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -0
  109. package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -0
  110. package/corpus/templates/plan/actions/create-plan-design.ts +8 -6
  111. package/corpus/templates/plan/actions/create-prototype-plan.ts +8 -6
  112. package/corpus/templates/plan/actions/create-ui-plan.ts +7 -7
  113. package/corpus/templates/plan/actions/get-plan-blocks.ts +7 -1
  114. package/corpus/templates/plan/actions/validate-plan-input.ts +39 -0
  115. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +167 -11
  116. package/corpus/templates/plan/app/i18n/en-US.ts +1 -0
  117. package/corpus/templates/plan/changelog/2026-08-04-canvas-zoom-controls-now-explain-the-command-ctrl-plus-scrol.md +6 -0
  118. package/corpus/templates/plan/changelog/2026-08-04-pinch-to-zoom-is-now-supported-on-touchscreens-in-visual-pla.md +6 -0
  119. package/corpus/templates/plan/shared/plan-content.ts +17 -2
  120. package/dist/cli/skills-content/canvas.d.ts +3 -3
  121. package/dist/cli/skills-content/canvas.js +16 -5
  122. package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
  123. package/dist/cli/skills-content/visual-plan-skill.js +27 -0
  124. package/dist/cli/skills-content/wireframe.d.ts +2 -2
  125. package/dist/cli/skills-content/wireframe.js +7 -0
  126. package/dist/client/AgentPanel.js +23 -8
  127. package/dist/client/api-path.d.ts +6 -0
  128. package/dist/client/api-path.js +55 -0
  129. package/dist/client/onboarding/FirstRunOnboarding.d.ts +3 -0
  130. package/dist/client/onboarding/FirstRunOnboarding.js +93 -0
  131. package/dist/client/onboarding/first-run-enabled.d.ts +2 -0
  132. package/dist/client/onboarding/first-run-enabled.js +9 -0
  133. package/dist/client/onboarding/index.d.ts +2 -1
  134. package/dist/client/onboarding/index.js +1 -0
  135. package/dist/client/onboarding/use-onboarding.d.ts +6 -1
  136. package/dist/client/onboarding/use-onboarding.js +32 -1
  137. package/dist/client/route-state.js +10 -0
  138. package/dist/client/settings/SecretsSection.js +32 -8
  139. package/dist/collab/struct-routes.d.ts +1 -1
  140. package/dist/connections/catalog.d.ts +4 -4
  141. package/dist/connections/catalog.js +3 -3
  142. package/dist/db/index.d.ts +1 -1
  143. package/dist/db/index.js +1 -1
  144. package/dist/localization/default-messages.d.ts +5 -0
  145. package/dist/localization/default-messages.js +5 -0
  146. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  147. package/dist/observability/routes.d.ts +3 -3
  148. package/dist/onboarding/app-profile.d.ts +4 -0
  149. package/dist/onboarding/app-profile.js +378 -0
  150. package/dist/onboarding/index.d.ts +2 -1
  151. package/dist/onboarding/index.js +1 -0
  152. package/dist/onboarding/plugin.d.ts +2 -0
  153. package/dist/onboarding/plugin.js +21 -1
  154. package/dist/onboarding/types.d.ts +19 -0
  155. package/dist/progress/routes.d.ts +1 -1
  156. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  157. package/dist/provider-api/actions/provider-api.d.ts +12 -12
  158. package/dist/secrets/routes.d.ts +2 -2
  159. package/dist/secrets/routes.js +41 -17
  160. package/dist/server/auth.js +21 -1
  161. package/dist/server/better-auth-instance.d.ts +3 -2
  162. package/dist/server/better-auth-instance.js +9 -7
  163. package/dist/server/email-markdown.js +3 -8
  164. package/dist/server/transcribe-voice.d.ts +1 -1
  165. package/dist/shared/first-run-onboarding.d.ts +3 -0
  166. package/dist/shared/first-run-onboarding.js +3 -0
  167. package/dist/triggers/index.d.ts +3 -0
  168. package/dist/triggers/index.js +5 -0
  169. package/docs/content/cross-app-sso.mdx +26 -0
  170. package/package.json +1 -1
@@ -0,0 +1,161 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { and, eq } from "drizzle-orm";
3
+ import { z } from "zod";
4
+
5
+ import { getDb } from "../server/db/index.js";
6
+ import { triageConfig, triageItems } from "../server/db/schema.js";
7
+ import { requireFactoryAutomation } from "../server/lib/require-factory-automation.js";
8
+ import {
9
+ requireWorkspaceMember,
10
+ workspaceMemberIdentityFromContext,
11
+ } from "../server/lib/require-workspace-member.js";
12
+ import { itemDedupeKey } from "../server/triage/ids.js";
13
+ import { mergeTriageMetadata } from "../server/triage/metadata.js";
14
+ import { createSentryClient } from "../server/triage/sentry-client.js";
15
+
16
+ export default defineAction({
17
+ description:
18
+ "Poll bounded unresolved Sentry issues for the configured organization and record them in the Factory queue. This does not change Sentry.",
19
+ schema: z.object({ limit: z.number().int().min(1).max(50).default(25) }),
20
+ http: false,
21
+ run: async ({ limit }, context) => {
22
+ const { userEmail, orgId } = await requireWorkspaceMember(
23
+ workspaceMemberIdentityFromContext(context),
24
+ );
25
+ await requireFactoryAutomation(
26
+ context,
27
+ { userEmail, orgId },
28
+ "sourcePolling",
29
+ );
30
+ const config = (
31
+ await getDb()
32
+ .select()
33
+ .from(triageConfig)
34
+ .where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
35
+ .limit(1)
36
+ )[0];
37
+ if (config?.sentryPollingEnabled !== 1) {
38
+ throw new Error("Enable Sentry polling before polling Sentry.");
39
+ }
40
+ if (!config.sentryOrgSlug) {
41
+ throw new Error("Configure a Sentry organization before polling Sentry.");
42
+ }
43
+ const query = [
44
+ "is:unresolved",
45
+ config.sentryProjectSlug ? `project:${config.sentryProjectSlug}` : "",
46
+ config.sentryEnvironment ? `environment:${config.sentryEnvironment}` : "",
47
+ ]
48
+ .filter(Boolean)
49
+ .join(" ");
50
+ const issues = await createSentryClient({
51
+ ownerEmail: userEmail,
52
+ orgId,
53
+ orgSlug: config.sentryOrgSlug,
54
+ }).listIssues(query, limit);
55
+ const cursor = config.lastSentrySeenAt
56
+ ? Date.parse(config.lastSentrySeenAt)
57
+ : null;
58
+ if (cursor !== null && Number.isNaN(cursor)) {
59
+ throw new Error("Stored Sentry polling cursor is not a valid timestamp.");
60
+ }
61
+ // Reconcile every bounded result by its stable Sentry issue id. Frequency
62
+ // ordering is not a safe timestamp cursor: an older issue can re-enter the
63
+ // top page after its frequency changes.
64
+ for (const issue of issues) {
65
+ const firstSeen = Date.parse(issue.firstSeen);
66
+ if (Number.isNaN(firstSeen))
67
+ throw new Error(
68
+ `Sentry issue ${issue.id} has an invalid firstSeen timestamp.`,
69
+ );
70
+ }
71
+ const observedIssues = issues;
72
+ const db = getDb();
73
+ const now = new Date().toISOString();
74
+
75
+ await db.transaction(async (tx) => {
76
+ for (const issue of observedIssues) {
77
+ const id = itemDedupeKey(
78
+ { source: "sentry", externalId: issue.id },
79
+ orgId,
80
+ );
81
+ const existing = (
82
+ await tx
83
+ .select()
84
+ .from(triageItems)
85
+ .where(and(eq(triageItems.id, id), eq(triageItems.orgId, orgId)))
86
+ .limit(1)
87
+ )[0];
88
+ const metadata = mergeTriageMetadata(existing?.metadataJson ?? "{}", {
89
+ kind: "sentry_issue",
90
+ sentryIssueId: issue.id,
91
+ shortId: issue.shortId,
92
+ culprit: issue.culprit,
93
+ level: issue.level,
94
+ projectSlug: issue.projectSlug,
95
+ errorReport: [issue.title, issue.culprit].filter(Boolean).join("\n"),
96
+ count: issue.count,
97
+ firstSeen: issue.firstSeen,
98
+ lastSeen: issue.lastSeen,
99
+ });
100
+ await tx
101
+ .insert(triageItems)
102
+ .values({
103
+ id,
104
+ source: "sentry",
105
+ externalId: issue.id,
106
+ sourceUrl: issue.permalink,
107
+ title: issue.title,
108
+ summary: [issue.culprit, `${issue.count} events`, issue.level]
109
+ .filter(Boolean)
110
+ .join(" · ")
111
+ .slice(0, 4_000),
112
+ status: existing?.status ?? "received",
113
+ risk: existing?.risk ?? "unknown",
114
+ coverage: existing?.coverage ?? "complete",
115
+ dedupeKey: id,
116
+ metadataJson: metadata,
117
+ lastSeenAt: issue.lastSeen,
118
+ createdAt: existing?.createdAt ?? now,
119
+ updatedAt: now,
120
+ ownerEmail: existing?.ownerEmail ?? userEmail,
121
+ orgId,
122
+ })
123
+ .onConflictDoUpdate({
124
+ target: triageItems.id,
125
+ set: {
126
+ sourceUrl: issue.permalink,
127
+ title: issue.title,
128
+ summary: [issue.culprit, `${issue.count} events`, issue.level]
129
+ .filter(Boolean)
130
+ .join(" · ")
131
+ .slice(0, 4_000),
132
+ metadataJson: metadata,
133
+ lastSeenAt: issue.lastSeen,
134
+ updatedAt: now,
135
+ },
136
+ });
137
+ }
138
+
139
+ await tx
140
+ .update(triageConfig)
141
+ .set({
142
+ lastSentrySeenAt:
143
+ observedIssues.reduce<string | null>((latest, issue) => {
144
+ if (!latest) return issue.firstSeen;
145
+ return Date.parse(issue.firstSeen) > Date.parse(latest)
146
+ ? issue.firstSeen
147
+ : latest;
148
+ }, config.lastSentrySeenAt) ?? config.lastSentrySeenAt,
149
+ updatedAt: now,
150
+ })
151
+ .where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)));
152
+ });
153
+
154
+ return {
155
+ ok: true,
156
+ observed: observedIssues.length,
157
+ fetched: issues.length,
158
+ sentryOrgSlug: config.sentryOrgSlug,
159
+ };
160
+ },
161
+ });
@@ -27,9 +27,12 @@ export default defineAction({
27
27
  await db
28
28
  .select()
29
29
  .from(triageConfig)
30
- .where(eq(triageConfig.id, orgId))
30
+ .where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
31
31
  .limit(1)
32
32
  )[0];
33
+ if (config?.pollingEnabled !== 1) {
34
+ throw new Error("Enable Slack polling before polling Slack.");
35
+ }
33
36
  const channelId = requestedChannelId ?? config?.slackChannelId;
34
37
  if (!channelId) {
35
38
  throw new Error("Configure a Slack channel before polling.");
@@ -83,7 +86,6 @@ export default defineAction({
83
86
  channelId: envelope.channelId ?? null,
84
87
  threadTs: envelope.threadTs ?? null,
85
88
  coverage: envelope.coverage,
86
- metadataJson: JSON.stringify(envelope.metadata ?? {}),
87
89
  lastSeenAt: now,
88
90
  updatedAt: now,
89
91
  },
@@ -0,0 +1,42 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import {
3
+ listAutomationDefinitions,
4
+ queueAutomationRunNow,
5
+ } from "@agent-native/core/triggers";
6
+ import { z } from "zod";
7
+
8
+ import {
9
+ requireWorkspaceMember,
10
+ workspaceMemberIdentityFromContext,
11
+ } from "../server/lib/require-workspace-member.js";
12
+
13
+ export default defineAction({
14
+ description:
15
+ "Queue one organization-scoped Factory automation for an immediate run and return its durable run id.",
16
+ agentTool: false,
17
+ schema: z.object({
18
+ factoryId: z.string().trim().min(1).optional(),
19
+ automationId: z.string().trim().min(1),
20
+ }),
21
+ http: { method: "POST" },
22
+ run: async ({ automationId }, context) => {
23
+ const { userEmail, orgId } = await requireWorkspaceMember(
24
+ workspaceMemberIdentityFromContext(context),
25
+ );
26
+ const definitions = await listAutomationDefinitions(
27
+ { userEmail, orgId },
28
+ "organization",
29
+ );
30
+ const definition = definitions.find(
31
+ (entry) =>
32
+ entry.meta.domain === "factory" && entry.resource.id === automationId,
33
+ );
34
+ if (!definition) throw new Error("Factory automation not found.");
35
+ return queueAutomationRunNow({
36
+ userEmail,
37
+ orgId,
38
+ scope: "organization",
39
+ name: definition.name,
40
+ });
41
+ },
42
+ });
@@ -0,0 +1,69 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import {
3
+ listAutomationDefinitions,
4
+ updateAutomation,
5
+ } from "@agent-native/core/triggers";
6
+ import { z } from "zod";
7
+
8
+ import {
9
+ requireWorkspaceMember,
10
+ workspaceMemberIdentityFromContext,
11
+ } from "../server/lib/require-workspace-member.js";
12
+
13
+ export default defineAction({
14
+ description:
15
+ "Edit a Factory automation's prompt, model, schedule, or enabled state in its organization-owned markdown resource.",
16
+ agentTool: false,
17
+ schema: z.object({
18
+ factoryId: z.string().trim().min(1).optional(),
19
+ automationId: z.string().trim().min(1),
20
+ name: z.string().trim().min(1).max(120),
21
+ prompt: z.string().trim().min(1).max(20_000),
22
+ model: z.string().trim().max(200).optional(),
23
+ schedule: z.string().trim().min(1).max(100),
24
+ enabled: z.boolean(),
25
+ }),
26
+ http: { method: "POST" },
27
+ run: async (
28
+ { automationId, name, prompt, model, schedule, enabled },
29
+ context,
30
+ ) => {
31
+ const { userEmail, orgId } = await requireWorkspaceMember(
32
+ workspaceMemberIdentityFromContext(context),
33
+ );
34
+ const definitions = await listAutomationDefinitions(
35
+ { userEmail, orgId },
36
+ "organization",
37
+ );
38
+ const definition = definitions.find(
39
+ (entry) =>
40
+ entry.meta.domain === "factory" && entry.resource.id === automationId,
41
+ );
42
+ if (!definition) throw new Error("Factory automation not found.");
43
+ if (definition.name !== name) {
44
+ throw new Error(
45
+ "Factory automation id and name do not refer to the same automation.",
46
+ );
47
+ }
48
+ const updated = await updateAutomation(
49
+ { userEmail, orgId },
50
+ {
51
+ name: definition.name,
52
+ scope: "organization",
53
+ body: prompt,
54
+ model: model?.trim() || null,
55
+ schedule,
56
+ enabled,
57
+ },
58
+ );
59
+ return {
60
+ ok: true,
61
+ id: definition.resource.id,
62
+ name: updated.name,
63
+ prompt: updated.body,
64
+ model: updated.meta.model ?? null,
65
+ schedule: updated.meta.schedule || null,
66
+ enabled: updated.meta.enabled,
67
+ };
68
+ },
69
+ });
@@ -12,12 +12,17 @@ const workspaceSchema = z.enum(["primary", "secondary"]);
12
12
 
13
13
  export default defineAction({
14
14
  description:
15
- "Save Factory observation settings. This controls read-only Slack polling and repository metadata; it never starts an executor or changes a provider.",
15
+ "Save Factory source settings. This controls Slack, GitHub, and Sentry polling; organization automations decide when governed provider work may start.",
16
16
  schema: z.object({
17
17
  slackWorkspace: workspaceSchema.default("primary"),
18
18
  slackChannelId: z.string().trim().min(1).max(128).optional(),
19
19
  slackChannelName: z.string().trim().max(200).optional(),
20
20
  pollingEnabled: z.boolean().default(false),
21
+ githubPollingEnabled: z.boolean().default(false),
22
+ sentryPollingEnabled: z.boolean().default(false),
23
+ sentryOrgSlug: z.string().trim().max(200).optional(),
24
+ sentryProjectSlug: z.string().trim().max(200).optional(),
25
+ sentryEnvironment: z.string().trim().max(200).optional(),
21
26
  repository: z.string().trim().max(256).optional(),
22
27
  }),
23
28
  http: { method: "POST" },
@@ -27,6 +32,11 @@ export default defineAction({
27
32
  slackChannelId,
28
33
  slackChannelName,
29
34
  pollingEnabled,
35
+ githubPollingEnabled,
36
+ sentryPollingEnabled,
37
+ sentryOrgSlug,
38
+ sentryProjectSlug,
39
+ sentryEnvironment,
30
40
  repository,
31
41
  },
32
42
  context,
@@ -44,6 +54,11 @@ export default defineAction({
44
54
  slackChannelId: slackChannelId ?? null,
45
55
  slackChannelName: slackChannelName ?? null,
46
56
  pollingEnabled: pollingEnabled ? 1 : 0,
57
+ githubPollingEnabled: githubPollingEnabled ? 1 : 0,
58
+ sentryPollingEnabled: sentryPollingEnabled ? 1 : 0,
59
+ sentryOrgSlug: sentryOrgSlug ?? null,
60
+ sentryProjectSlug: sentryProjectSlug ?? null,
61
+ sentryEnvironment: sentryEnvironment ?? null,
47
62
  repository: repository ?? null,
48
63
  createdAt: now,
49
64
  updatedAt: now,
@@ -57,11 +72,22 @@ export default defineAction({
57
72
  slackChannelId: slackChannelId ?? null,
58
73
  slackChannelName: slackChannelName ?? null,
59
74
  pollingEnabled: pollingEnabled ? 1 : 0,
75
+ githubPollingEnabled: githubPollingEnabled ? 1 : 0,
76
+ sentryPollingEnabled: sentryPollingEnabled ? 1 : 0,
77
+ sentryOrgSlug: sentryOrgSlug ?? null,
78
+ sentryProjectSlug: sentryProjectSlug ?? null,
79
+ sentryEnvironment: sentryEnvironment ?? null,
60
80
  repository: repository ?? null,
61
81
  updatedAt: now,
62
82
  ownerEmail: userEmail,
63
83
  },
64
84
  });
65
- return { ok: true, pollingEnabled, slackChannelId: slackChannelId ?? null };
85
+ return {
86
+ ok: true,
87
+ pollingEnabled,
88
+ githubPollingEnabled,
89
+ sentryPollingEnabled,
90
+ slackChannelId: slackChannelId ?? null,
91
+ };
66
92
  },
67
93
  });