@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
@@ -1,41 +1,146 @@
1
+ import { resolveOrgIdForEmail } from "@agent-native/core/org";
1
2
  import {
2
- WORKSPACE_OWNER,
3
+ organizationResourceOwner,
3
4
  resourceGetByPath,
4
5
  resourcePut,
6
+ WORKSPACE_OWNER,
5
7
  } from "@agent-native/core/resources";
6
8
  import { defineNitroPlugin } from "@agent-native/core/server";
9
+ import { and, eq } from "drizzle-orm";
7
10
 
8
- const JOB_PATH = "jobs/factory-observation-scheduler.md";
11
+ import { getDb } from "../db/index.js";
12
+ import { triageConfig } from "../db/schema.js";
9
13
 
10
- const JOB_BODY = `
11
- # Factory observation scheduler
14
+ const LEGACY_JOB_PATH = "jobs/factory-observation-scheduler.md";
15
+ const DEFAULT_SLACK_CHANNEL_ID = "C0ATH3CCZT4";
16
+ const DEFAULT_SLACK_CHANNEL_NAME = "product-agent-native-feedback";
12
17
 
13
- Call get-triage-config. If pollingEnabled is true and a Slack channel is
14
- configured, call poll-slack-channel. Then call list-triage-items with status
15
- received and evaluate each returned item with evaluate-triage-item. Keep the
16
- work bounded to the returned page. This scheduled observer does not start work
17
- or send provider messages; interactive approvals happen through the Factory agent.
18
- If any action reports an error, preserve that error and continue to the next
19
- item; do not report a false success.
20
- `;
18
+ type AutomationSeed = {
19
+ name: string;
20
+ schedule: string;
21
+ timezone?: string;
22
+ body: string;
23
+ };
24
+
25
+ const AUTOMATION_SEEDS: AutomationSeed[] = [
26
+ {
27
+ name: "factory-slack-feedback",
28
+ schedule: "* * * * *",
29
+ body: `
30
+ # Factory Slack feedback triage
31
+
32
+ Read the Factory configuration. When Slack polling is enabled and a channel is
33
+ configured, call poll-slack-channel first. Then list a bounded page of
34
+ received Slack items and call get-slack-feedback-context for each item before
35
+ classifying it.
36
+
37
+ Start work only for a clear bug: a concrete broken behavior, reproducible
38
+ failure, error, regression, stuck run, incorrect result, or a report with a
39
+ specific failing path and enough evidence to investigate. Do not treat feature
40
+ requests, wish-list ideas, broad UX suggestions, vague questions, or an
41
+ incomplete/truncated thread as clear bugs. The user's historical eyeball
42
+ reactions are calibration evidence, not an automatic authorization signal.
43
+ That history is strongest for concrete failures such as stuck or never-starting
44
+ runs, incorrect object/object output, disk-full or database-locked errors,
45
+ stale loops, broken uploads or duplicate imports, missing scaffolding or docs,
46
+ and concrete auth or configuration failures. Use those examples to recognize
47
+ the shape of a bug, not to turn similar-sounding reports into automatic work.
48
+
49
+ For a clear bug outside Clips, Design, and Content, call start-builder-for-item
50
+ with clearBug true and a short evidence-grounded reason. That action adds the
51
+ eyes reaction and tags @builderio in the Slack thread, asking Builder to fix
52
+ it in a reply and send a PR. Never add the reaction or tag Builder for
53
+ owner-managed Clips, Design, or Content work, or for a non-bug report.
54
+
55
+ Keep each run bounded. Preserve action errors and do not claim a Builder reply,
56
+ PR, merge, or fix unless an action returned that state.
57
+ `,
58
+ },
59
+ {
60
+ name: "factory-sentry-errors",
61
+ schedule: "0 9 * * *",
62
+ timezone: "America/Los_Angeles",
63
+ body: `
64
+ # Factory Sentry error triage
65
+
66
+ Read the Factory configuration. When Sentry polling is enabled and a Sentry
67
+ organization is configured, call poll-sentry-errors. List a bounded page of
68
+ received Sentry items and inspect the title, culprit, level, event count, and
69
+ errorReport metadata.
70
+
71
+ Only classify a concrete unresolved error as a clear bug when the Sentry
72
+ evidence is sufficient to investigate. Do not dispatch on noise, expected
73
+ errors, product ideas, or incomplete provider responses. Clips, Design, and
74
+ Content errors are owner-managed and must remain needs_manual.
75
+
76
+ For each eligible clear bug, call start-builder-for-item with clearBug true,
77
+ an evidence-grounded reason, and clearErrorReport containing only the bounded
78
+ Sentry evidence. Builder should open a PR; do not claim it did so until the
79
+ run callback or PR observation confirms it.
80
+ `,
81
+ },
82
+ {
83
+ name: "factory-github-issues",
84
+ schedule: "* * * * *",
85
+ body: `
86
+ # Factory GitHub issue triage
21
87
 
22
- function workspaceOwnerEmail() {
88
+ Read the Factory configuration. When GitHub source polling is enabled and a
89
+ repository is configured, call poll-github-sources with includeIssues true and
90
+ includePullRequests false. List a bounded page of received github_issue items.
91
+
92
+ Treat an issue as a clear bug only when it has a concrete error report,
93
+ reproduction, incorrect behavior, regression, or specific failing path. Do
94
+ not dispatch feature requests, vague questions, or issues without enough
95
+ evidence. Clips, Design, and Content remain owner-managed.
96
+
97
+ For each eligible item call start-builder-for-item with clearBug true,
98
+ evidence-grounded reason, and the bounded issue body as clearErrorReport.
99
+ Preserve failures and never report a successful Builder run without its action
100
+ confirmation.
101
+ `,
102
+ },
103
+ {
104
+ name: "factory-pr-governance",
105
+ schedule: "*/5 * * * *",
106
+ body: `
107
+ # Factory pull-request governance
108
+
109
+ Read the Factory configuration. When GitHub polling is enabled and a repository
110
+ is configured, call poll-github-sources with includeIssues false and
111
+ includePullRequests true. List a bounded page of github pull-request items.
112
+
113
+ For each open agent-native PR, inspect the item and classify whether it is a
114
+ clear bug fix or has product or UX implications. Call
115
+ govern-agent-native-pull-request with the item id, repository, pull request
116
+ number, clearBug, productUxImplications, and a short reason. The action fetches
117
+ fresh CI and review evidence before approving or merging. Clear internal bug
118
+ fixes with passing CI and handled review feedback may be auto-approved.
119
+
120
+ Only auto-merge when the PR proves its Factory origin by retaining the Factory
121
+ item id or source link in the PR description, or by using the Factory branch
122
+ name. A normal open PR must never be treated as a Builder-triggered run.
123
+
124
+ Never auto-approve or auto-merge Clips, Design, or Content PRs. Those apps are
125
+ fully owned by their product owners. Auto-merge is limited to PRs with a
126
+ verified Factory Builder run; all other PRs can at most pass the approval
127
+ policy. Do not call GitHub write actions directly or claim a merge unless the
128
+ governance action confirms it.
129
+ `,
130
+ },
131
+ ];
132
+
133
+ function workspaceOwnerEmail(): string | undefined {
23
134
  const email = process.env.WORKSPACE_OWNER_EMAIL?.trim().toLowerCase(); // guard:allow-env-credential - deployment owner identity, not a user credential
24
135
  if (!email || /[\r\n]/.test(email)) return undefined;
25
136
  return email;
26
137
  }
27
138
 
28
- function readFrontmatterField(content: string, key: string): string | null {
29
- if (!content.startsWith("---\n")) return null;
30
- const end = content.indexOf("\n---", 4);
31
- if (end === -1) return null;
32
- const match = content
33
- .slice(4, end)
34
- .match(new RegExp(`^${key}:\\s*(.*)$`, "m"));
35
- return match ? match[1].trim() : null;
36
- }
37
-
38
- function setFrontmatterField(content: string, key: string, value: string) {
139
+ function setFrontmatterField(
140
+ content: string,
141
+ key: string,
142
+ value: string,
143
+ ): string {
39
144
  if (!content.startsWith("---\n")) return content;
40
145
  const end = content.indexOf("\n---", 4);
41
146
  if (end === -1) return content;
@@ -47,70 +152,116 @@ function setFrontmatterField(content: string, key: string, value: string) {
47
152
  return `${content.slice(0, end)}\n${key}: ${value}${content.slice(end)}`;
48
153
  }
49
154
 
50
- function jobContent(createdBy: string) {
155
+ function automationContent(
156
+ ownerEmail: string,
157
+ orgId: string,
158
+ seed: AutomationSeed,
159
+ ): string {
51
160
  return `---
52
- schedule: "* * * * *"
53
- enabled: true
54
- createdBy: ${createdBy}
161
+ schedule: "${seed.schedule}"
162
+ ${seed.timezone ? `timezone: ${seed.timezone}\n` : ""}enabled: true
163
+ triggerType: schedule
164
+ domain: factory
165
+ orgId: ${orgId}
166
+ createdBy: ${ownerEmail}
55
167
  runAs: creator
56
168
  ---
57
- ${JOB_BODY}`;
169
+ ${seed.body.trim()}
170
+ `;
58
171
  }
59
172
 
60
- async function ensureSchedulerJob() {
61
- const existing = await resourceGetByPath(WORKSPACE_OWNER, JOB_PATH);
62
- const ownerEmail = workspaceOwnerEmail();
63
-
64
- if (!existing) {
65
- if (!ownerEmail) {
66
- throw new Error(
67
- "WORKSPACE_OWNER_EMAIL is required to seed the Factory scheduler",
68
- );
69
- }
173
+ async function disableLegacyObserver(): Promise<void> {
174
+ const existing = await resourceGetByPath(WORKSPACE_OWNER, LEGACY_JOB_PATH);
175
+ if (!existing) return;
176
+ const disabled = setFrontmatterField(existing.content, "enabled", "false");
177
+ if (disabled !== existing.content) {
70
178
  await resourcePut(
71
179
  WORKSPACE_OWNER,
72
- JOB_PATH,
73
- jobContent(ownerEmail),
180
+ LEGACY_JOB_PATH,
181
+ disabled,
182
+ "text/markdown",
183
+ );
184
+ }
185
+ }
186
+
187
+ async function ensureOrganizationAutomations(
188
+ ownerEmail: string,
189
+ orgId: string,
190
+ ): Promise<void> {
191
+ const owner = organizationResourceOwner(orgId);
192
+ for (const seed of AUTOMATION_SEEDS) {
193
+ const path = `jobs/${seed.name}.md`;
194
+ if (await resourceGetByPath(owner, path)) continue;
195
+ await resourcePut(
196
+ owner,
197
+ path,
198
+ automationContent(ownerEmail, orgId, seed),
74
199
  "text/markdown",
75
200
  );
76
- return;
77
201
  }
202
+ }
78
203
 
204
+ async function ensureDefaultTriageConfig(
205
+ ownerEmail: string,
206
+ orgId: string,
207
+ ): Promise<void> {
208
+ const db = getDb();
209
+ const existing = (
210
+ await db
211
+ .select({ id: triageConfig.id })
212
+ .from(triageConfig)
213
+ .where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
214
+ .limit(1)
215
+ )[0];
216
+ if (existing) return;
217
+ const now = new Date().toISOString();
218
+ await db.insert(triageConfig).values({
219
+ id: orgId,
220
+ slackWorkspace: "primary",
221
+ slackChannelId: DEFAULT_SLACK_CHANNEL_ID,
222
+ slackChannelName: DEFAULT_SLACK_CHANNEL_NAME,
223
+ pollingEnabled: 1,
224
+ githubPollingEnabled: 0,
225
+ sentryPollingEnabled: 0,
226
+ lastSlackTs: null,
227
+ slackHistoryCursor: null,
228
+ repository: null,
229
+ sentryOrgSlug: null,
230
+ sentryProjectSlug: null,
231
+ sentryEnvironment: null,
232
+ lastSentrySeenAt: null,
233
+ createdAt: now,
234
+ updatedAt: now,
235
+ ownerEmail,
236
+ orgId,
237
+ });
238
+ }
239
+
240
+ async function ensureSchedulerJobs(): Promise<void> {
241
+ const ownerEmail = workspaceOwnerEmail();
79
242
  if (!ownerEmail) {
80
- const runAs = readFrontmatterField(existing.content, "runAs");
81
- const createdBy = readFrontmatterField(existing.content, "createdBy");
82
- if (runAs === "creator" && createdBy) return;
83
243
  throw new Error(
84
- "WORKSPACE_OWNER_EMAIL is required to repair the Factory scheduler",
244
+ "WORKSPACE_OWNER_EMAIL is required to seed Factory automations",
85
245
  );
86
246
  }
87
-
88
- if (
89
- !existing.content.startsWith("---\n") ||
90
- !existing.content.includes("\n---", 4)
91
- ) {
92
- await resourcePut(
93
- WORKSPACE_OWNER,
94
- JOB_PATH,
95
- jobContent(ownerEmail),
96
- "text/markdown",
247
+ const orgId = await resolveOrgIdForEmail(ownerEmail);
248
+ if (!orgId) {
249
+ throw new Error(
250
+ "The Factory deployment owner must belong to an organization before automations can be seeded",
97
251
  );
98
- return;
99
252
  }
100
-
101
- const migrated = setFrontmatterField(
102
- setFrontmatterField(existing.content, "createdBy", ownerEmail),
103
- "runAs",
104
- "creator",
105
- );
106
- if (migrated === existing.content) return;
107
- await resourcePut(WORKSPACE_OWNER, JOB_PATH, migrated, "text/markdown");
253
+ await ensureDefaultTriageConfig(ownerEmail, orgId);
254
+ await ensureOrganizationAutomations(ownerEmail, orgId);
255
+ await disableLegacyObserver();
108
256
  }
109
257
 
110
258
  export default defineNitroPlugin(async () => {
111
259
  try {
112
- await ensureSchedulerJob();
260
+ await ensureSchedulerJobs();
113
261
  } catch (error) {
114
- console.error("[factory-scheduler-job] failed to seed job:", error);
262
+ console.error(
263
+ "[factory-scheduler-job] failed to seed organization automations:",
264
+ error,
265
+ );
115
266
  }
116
267
  });
@@ -8,6 +8,7 @@ export interface BuilderExecutorInput {
8
8
  repository?: string | null;
9
9
  summary?: string | null;
10
10
  sourceUrl?: string | null;
11
+ instructions?: string | null;
11
12
  }
12
13
 
13
14
  export interface BuilderExecutorResult {
@@ -63,12 +64,15 @@ export async function startBuilderRun(
63
64
  const branchName = `factory/${input.itemId.slice(0, 12)}`;
64
65
  const prompt = [
65
66
  "Work on this approved Factory item.",
67
+ `Factory item: ${input.itemId}`,
66
68
  input.summary
67
69
  ? `Feedback: ${input.summary}`
68
70
  : "No feedback summary was supplied.",
69
71
  input.sourceUrl ? `Source: ${input.sourceUrl}` : "",
70
72
  input.repository ? `Repository: ${input.repository}` : "",
73
+ input.instructions ? `Triage instructions: ${input.instructions}` : "",
71
74
  "Inspect the repository, make the smallest safe fix, run validation, and open a pull request.",
75
+ "Include the Factory item id and source link in the pull request description.",
72
76
  "Do not touch protected areas: auth/session/identity, credentials/vault, migrations, payments, security, or publishable packages.",
73
77
  ]
74
78
  .filter(Boolean)
@@ -1,6 +1,11 @@
1
1
  import { z } from "zod";
2
2
 
3
- export const triageSourceSchema = z.enum(["slack", "github"]);
3
+ export const triageSourceSchema = z.enum([
4
+ "slack",
5
+ "github",
6
+ "github_issue",
7
+ "sentry",
8
+ ]);
4
9
  export type TriageSource = z.infer<typeof triageSourceSchema>;
5
10
 
6
11
  export const triageItemStatusSchema = z.enum([
@@ -12,6 +17,10 @@ export const triageItemStatusSchema = z.enum([
12
17
  "needs_manual",
13
18
  "failed",
14
19
  "reconciliation_required",
20
+ "automation_started",
21
+ "pr_observed",
22
+ "auto_approved",
23
+ "merged",
15
24
  ]);
16
25
  export type TriageItemStatus = z.infer<typeof triageItemStatusSchema>;
17
26
 
@@ -37,6 +46,8 @@ export const triageDecisionOutcomeSchema = z.enum([
37
46
  "needs_manual",
38
47
  "propose_fix",
39
48
  "propose_review",
49
+ "auto_approve",
50
+ "auto_merge",
40
51
  ]);
41
52
  export type TriageDecisionOutcome = z.infer<typeof triageDecisionOutcomeSchema>;
42
53
 
@@ -68,6 +79,7 @@ export const triageGuardCodeSchema = z.enum([
68
79
  "path_denied",
69
80
  "diff_too_large",
70
81
  "unknown_change",
82
+ "owner_owned",
71
83
  ]);
72
84
  export type TriageGuardCode = z.infer<typeof triageGuardCodeSchema>;
73
85
 
@@ -81,6 +93,7 @@ export const hardNeverAutomateGuardCodes: TriageGuardCode[] = [
81
93
  "payments",
82
94
  "security",
83
95
  "publishable_package",
96
+ "owner_owned",
84
97
  ];
85
98
 
86
99
  export const triagePolicyGuardsSchema = z.object({