@agent-native/core 0.147.0 → 0.148.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 (172) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +115 -68
  3. package/corpus/templates/analytics/DEVELOPING.md +50 -3
  4. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
  5. package/corpus/templates/analytics/app/i18n/index.ts +14 -29
  6. package/corpus/templates/analytics/app/i18n-data.ts +10 -10
  7. package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
  8. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
  9. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +197 -18
  10. package/corpus/templates/analytics/server/db/schema.ts +23 -0
  11. package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
  12. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
  13. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
  14. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
  15. package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
  16. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
  17. package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
  18. package/corpus/templates/analytics/server/plugins/db.ts +42 -0
  19. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
  20. package/corpus/templates/assets/app/i18n/index.ts +14 -29
  21. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
  22. package/corpus/templates/brain/app/i18n/index.ts +14 -29
  23. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
  24. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  25. package/corpus/templates/chat/app/i18n/index.ts +14 -29
  26. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
  27. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
  28. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
  29. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
  30. package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
  31. package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
  32. package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
  33. package/corpus/templates/clips/desktop/src/app.tsx +1 -1
  34. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
  35. package/corpus/templates/content/app/i18n/index.ts +14 -29
  36. package/corpus/templates/crm/app/i18n/index.ts +14 -29
  37. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
  38. package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
  39. package/corpus/templates/design/actions/import-code.ts +2 -43
  40. package/corpus/templates/design/app/i18n/index.ts +14 -29
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
  42. package/corpus/templates/dispatch/app/i18n-data.ts +1 -1
  43. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
  44. package/corpus/templates/factory/AGENTS.md +40 -49
  45. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
  46. package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
  47. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
  48. package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
  49. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
  50. package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
  51. package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
  52. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  53. package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
  54. package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
  55. package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
  56. package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
  57. package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
  58. package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
  59. package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
  60. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
  61. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
  62. package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
  63. package/corpus/templates/factory/app/routes/factory.tsx +95 -42
  64. package/corpus/templates/factory/server/db/schema.ts +37 -0
  65. package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
  66. package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
  67. package/corpus/templates/factory/server/triage/audit.ts +79 -0
  68. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
  69. package/corpus/templates/forms/app/i18n/index.ts +14 -29
  70. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
  71. package/corpus/templates/macros/app/i18n/index.ts +14 -29
  72. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
  73. package/corpus/templates/mail/app/i18n/index.ts +14 -29
  74. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
  75. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
  76. package/corpus/templates/plan/app/i18n/index.ts +14 -29
  77. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
  78. package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
  79. package/corpus/templates/slides/actions/import-code.ts +2 -43
  80. package/corpus/templates/slides/app/i18n/index.ts +14 -29
  81. package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
  82. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
  83. package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
  84. package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
  85. package/corpus/templates/tasks/app/i18n/index.ts +14 -29
  86. package/dist/agent/engine/ai-sdk-engine.js +2 -1
  87. package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
  88. package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
  89. package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
  90. package/dist/agent/engine/provider-endpoint-validation.js +26 -0
  91. package/dist/agent/engine/provider-env-vars.d.ts +1 -1
  92. package/dist/agent/engine/provider-env-vars.js +1 -1
  93. package/dist/agent/engine/registry.d.ts +8 -6
  94. package/dist/agent/engine/registry.js +37 -24
  95. package/dist/agent/tool-search.d.ts +1 -0
  96. package/dist/agent/tool-search.js +17 -1
  97. package/dist/automations/service.js +4 -10
  98. package/dist/brand-kit/brand-signals.d.ts +13 -0
  99. package/dist/brand-kit/brand-signals.js +15 -0
  100. package/dist/brand-kit/index.d.ts +2 -2
  101. package/dist/brand-kit/index.js +1 -1
  102. package/dist/cli/plan-blocks.d.ts +3 -18
  103. package/dist/cli/plan-blocks.js +2 -95
  104. package/dist/cli/plan-publish-store.d.ts +2 -61
  105. package/dist/cli/plan-publish-store.js +2 -127
  106. package/dist/cli/skills-content/connection.d.ts +1 -1
  107. package/dist/cli/skills-content/connection.js +1 -52
  108. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  109. package/dist/cli/skills-content/visual-recap-skill.js +1 -553
  110. package/dist/client/agent-engine-key.d.ts +14 -4
  111. package/dist/client/agent-engine-key.js +51 -5
  112. package/dist/client/agent-provider-catalog.d.ts +20 -0
  113. package/dist/client/agent-provider-catalog.js +100 -0
  114. package/dist/client/blocks/index.d.ts +1 -0
  115. package/dist/client/blocks/index.js +1 -0
  116. package/dist/client/chat/run-recovery.js +4 -48
  117. package/dist/client/chat-first.js +4 -3
  118. package/dist/client/i18n.d.ts +14 -0
  119. package/dist/client/i18n.js +18 -0
  120. package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
  121. package/dist/client/settings/ProviderSetupForm.js +145 -0
  122. package/dist/client/settings/SettingsPanel.js +52 -49
  123. package/dist/client/settings/index.d.ts +2 -0
  124. package/dist/client/settings/index.js +2 -0
  125. package/dist/coding-tools/run-code.d.ts +5 -0
  126. package/dist/coding-tools/run-code.js +196 -36
  127. package/dist/coding-tools/tool-orchestration.d.ts +10 -0
  128. package/dist/coding-tools/tool-orchestration.js +107 -0
  129. package/dist/collab/awareness.d.ts +2 -2
  130. package/dist/jobs/frontmatter.d.ts +9 -0
  131. package/dist/jobs/frontmatter.js +16 -0
  132. package/dist/jobs/scheduler.js +8 -8
  133. package/dist/jobs/tools.d.ts +1 -1
  134. package/dist/jobs/tools.js +2 -7
  135. package/dist/localization/default-messages.d.ts +20 -0
  136. package/dist/localization/default-messages.js +25 -5
  137. package/dist/observability/routes.d.ts +3 -3
  138. package/dist/onboarding/app-profile.js +1 -1
  139. package/dist/progress/routes.d.ts +1 -1
  140. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  141. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  142. package/dist/resources/handlers.d.ts +1 -1
  143. package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
  144. package/dist/secrets/routes.d.ts +9 -9
  145. package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
  146. package/dist/server/agent-chat/run-code-tools.js +3 -1
  147. package/dist/server/agent-chat-plugin.js +6 -6
  148. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  149. package/dist/server/agent-engine-api-key-route.js +43 -12
  150. package/dist/server/agents-bundle.js +2 -1
  151. package/dist/server/auth.js +21 -5
  152. package/dist/server/credential-provider.js +1 -0
  153. package/dist/server/design-token-utils.d.ts +18 -0
  154. package/dist/server/design-token-utils.js +31 -0
  155. package/dist/server/onboarding-html.js +22 -2
  156. package/dist/server/realtime-token.d.ts +1 -1
  157. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  158. package/dist/templates/chat/app/i18n/index.ts +14 -29
  159. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  160. package/dist/templates/default/app/i18n/index.ts +14 -29
  161. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  162. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  163. package/dist/triggers/dispatcher.js +1 -8
  164. package/docs/content/environment-variables.mdx +39 -38
  165. package/package.json +4 -4
  166. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  167. package/src/templates/chat/app/i18n/index.ts +14 -29
  168. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  169. package/src/templates/default/app/i18n/index.ts +14 -29
  170. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  171. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  172. package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
@@ -154,6 +154,31 @@ const messages = {
154
154
  metricRuns: "Runs",
155
155
  rulesTab: "Rules",
156
156
  automationsTab: "Automations",
157
+ auditTab: "Audit",
158
+ auditTitle: "Run audit",
159
+ auditDescription:
160
+ "See exactly which sources the agent inspected, what it decided, and which actions it took.",
161
+ auditRefresh: "Refresh",
162
+ auditLoadError: "Could not load the run audit.",
163
+ auditEmpty: "No automation runs have been recorded yet.",
164
+ auditRuns: "Recent runs",
165
+ auditEventsShort: "events",
166
+ auditActionsShort: "actions",
167
+ auditRunDetail: "Evidence and decisions recorded for this run.",
168
+ auditStarted: "Started",
169
+ auditFinished: "Finished",
170
+ auditItems: "Items",
171
+ auditRunError: "Run error",
172
+ auditEvidence: "Evidence and decisions",
173
+ auditNoEvents:
174
+ "No item-level audit events were recorded. Open the agent thread for the full trace.",
175
+ auditOpenThread: "Open thread",
176
+ auditOpenItem: "Open item",
177
+ auditOpenSource: "Open source",
178
+ auditInternal: "Factory",
179
+ auditClearBug: "Clear bug",
180
+ auditUxImpact: "UX impact",
181
+ auditOwnerArea: "Owner area",
157
182
  automationsTitle: "Automations",
158
183
  automationsDescription: "Schedule and run Factory prompts.",
159
184
  automationsLoading: "Loading automations...",
@@ -10,10 +10,12 @@ import {
10
10
  IconLoader2,
11
11
  IconPlayerPlay,
12
12
  IconPlus,
13
+ IconRefresh,
13
14
  } from "@tabler/icons-react";
14
15
  import { useEffect, useMemo, useState } from "react";
15
16
  import { useSearchParams } from "react-router";
16
17
 
18
+ import { FactoryAuditView } from "@/components/factory/FactoryAuditView";
17
19
  import {
18
20
  FactoryCanvas,
19
21
  type FactoryCanvasEdge,
@@ -111,7 +113,13 @@ type TriageConfig = {
111
113
  };
112
114
 
113
115
  type Verdict = "correct" | "incorrect" | "uncertain";
114
- type WorkspaceTab = "map" | "inbox" | "rules" | "automations" | "settings";
116
+ type WorkspaceTab =
117
+ | "map"
118
+ | "inbox"
119
+ | "rules"
120
+ | "automations"
121
+ | "audit"
122
+ | "settings";
115
123
 
116
124
  type FactoryAutomationRun = {
117
125
  id?: string;
@@ -164,6 +172,7 @@ export default function FactoryRoute() {
164
172
  const [dirty, setDirty] = useState(false);
165
173
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
166
174
  const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null);
175
+ const [auditRefreshToken, setAuditRefreshToken] = useState(0);
167
176
 
168
177
  function setActiveTab(tab: WorkspaceTab) {
169
178
  setSearchParams(
@@ -418,41 +427,62 @@ export default function FactoryRoute() {
418
427
  </Button>
419
428
  </div>
420
429
  </div>
421
- <nav
422
- className="mt-4 flex items-center gap-1 overflow-x-auto"
423
- aria-label={t("factoryRoute.factoryViews")}
424
- >
425
- <TabButton
426
- active={activeTab === "map"}
427
- onClick={() => setActiveTab("map")}
428
- >
429
- Map
430
- </TabButton>
431
- <TabButton
432
- active={activeTab === "inbox"}
433
- onClick={() => setActiveTab("inbox")}
434
- >
435
- Inbox
436
- </TabButton>
437
- <TabButton
438
- active={activeTab === "rules"}
439
- onClick={() => setActiveTab("rules")}
440
- >
441
- {t("factoryRoute.rulesTab")}
442
- </TabButton>
443
- <TabButton
444
- active={activeTab === "automations"}
445
- onClick={() => setActiveTab("automations")}
430
+ <div className="mt-4 flex items-center gap-2">
431
+ <nav
432
+ className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto"
433
+ aria-label={t("factoryRoute.factoryViews")}
446
434
  >
447
- {t("factoryRoute.automationsTab")}
448
- </TabButton>
449
- <TabButton
450
- active={activeTab === "settings"}
451
- onClick={() => setActiveTab("settings")}
452
- >
453
- Settings
454
- </TabButton>
455
- </nav>
435
+ <TabButton
436
+ active={activeTab === "map"}
437
+ onClick={() => setActiveTab("map")}
438
+ >
439
+ Map
440
+ </TabButton>
441
+ <TabButton
442
+ active={activeTab === "inbox"}
443
+ onClick={() => setActiveTab("inbox")}
444
+ >
445
+ Inbox
446
+ </TabButton>
447
+ <TabButton
448
+ active={activeTab === "rules"}
449
+ onClick={() => setActiveTab("rules")}
450
+ >
451
+ {t("factoryRoute.rulesTab")}
452
+ </TabButton>
453
+ <TabButton
454
+ active={activeTab === "automations"}
455
+ onClick={() => setActiveTab("automations")}
456
+ >
457
+ {t("factoryRoute.automationsTab")}
458
+ </TabButton>
459
+ <TabButton
460
+ active={activeTab === "audit"}
461
+ onClick={() => setActiveTab("audit")}
462
+ >
463
+ {t("factoryRoute.auditTab")}
464
+ </TabButton>
465
+ <TabButton
466
+ active={activeTab === "settings"}
467
+ onClick={() => setActiveTab("settings")}
468
+ >
469
+ Settings
470
+ </TabButton>
471
+ </nav>
472
+ {activeTab === "audit" && (
473
+ <Button
474
+ type="button"
475
+ variant="ghost"
476
+ size="icon"
477
+ className="size-8 shrink-0 text-muted-foreground hover:text-foreground"
478
+ aria-label={t("factoryRoute.auditRefresh")}
479
+ title={t("factoryRoute.auditRefresh")}
480
+ onClick={() => setAuditRefreshToken((current) => current + 1)}
481
+ >
482
+ <IconRefresh className="size-4" />
483
+ </Button>
484
+ )}
485
+ </div>
456
486
  </header>
457
487
 
458
488
  <main className="min-h-0 flex-1 overflow-y-auto">
@@ -526,6 +556,11 @@ export default function FactoryRoute() {
526
556
  <RulesView t={t} />
527
557
  ) : activeTab === "automations" ? (
528
558
  <AutomationsView factoryId={factoryId} t={t} />
559
+ ) : activeTab === "audit" ? (
560
+ <FactoryAuditView
561
+ factoryId={factoryId}
562
+ refreshToken={auditRefreshToken}
563
+ />
529
564
  ) : (
530
565
  <SettingsView t={t} />
531
566
  )}
@@ -559,6 +594,7 @@ function parseWorkspaceTab(value: string | null): WorkspaceTab {
559
594
  return value === "inbox" ||
560
595
  value === "rules" ||
561
596
  value === "automations" ||
597
+ value === "audit" ||
562
598
  value === "settings"
563
599
  ? value
564
600
  : "map";
@@ -729,7 +765,7 @@ function AutomationsView({
729
765
  <span className="block truncate text-sm font-medium">
730
766
  {automation.name}
731
767
  </span>
732
- <span className="mt-1 block text-xs text-muted-foreground">
768
+ <span className="mt-1 block truncate text-xs text-muted-foreground">
733
769
  {automation.enabled
734
770
  ? t("factoryRoute.automationEnabled")
735
771
  : t("factoryRoute.automationDisabled")}
@@ -956,8 +992,11 @@ function formatModelName(model: string | null | undefined) {
956
992
  }
957
993
 
958
994
  function InboxView({ t }: { t: ReturnType<typeof useT> }) {
995
+ const [searchParams, setSearchParams] = useSearchParams();
959
996
  const [status, setStatus] = useState("");
960
- const [selectedId, setSelectedId] = useState<string | null>(null);
997
+ const [selectedId, setSelectedId] = useState<string | null>(
998
+ searchParams.get("itemId"),
999
+ );
961
1000
  const [feedbackNote, setFeedbackNote] = useState("");
962
1001
  const [verdict, setVerdict] = useState<Verdict | null>(null);
963
1002
  const listQuery = useActionQuery("list-triage-items", {
@@ -989,6 +1028,24 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
989
1028
  const normalizedItems = Array.isArray(items) ? items : (items.items ?? []);
990
1029
  const selectedItem = detailQuery.data as TriageItem | undefined;
991
1030
 
1031
+ useEffect(() => {
1032
+ setSelectedId(searchParams.get("itemId"));
1033
+ }, [searchParams]);
1034
+
1035
+ function selectItem(itemId: string) {
1036
+ setSelectedId(itemId);
1037
+ setVerdict(null);
1038
+ setFeedbackNote("");
1039
+ setSearchParams(
1040
+ (current) => {
1041
+ const next = new URLSearchParams(current);
1042
+ next.set("itemId", itemId);
1043
+ return next;
1044
+ },
1045
+ { replace: true },
1046
+ );
1047
+ }
1048
+
992
1049
  return (
993
1050
  <div className="grid gap-4 p-4 lg:grid-cols-[minmax(0,1.3fr)_minmax(320px,.7fr)] lg:p-6">
994
1051
  <Card>
@@ -1046,11 +1103,7 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1046
1103
  key={id}
1047
1104
  type="button"
1048
1105
  className={`grid w-full gap-3 p-4 text-left transition-colors hover:bg-muted/50 sm:grid-cols-[1.1fr_.7fr_.9fr_1.6fr] ${selectedId === id ? "bg-muted/60" : ""}`}
1049
- onClick={() => {
1050
- setSelectedId(id);
1051
- setVerdict(null);
1052
- setFeedbackNote("");
1053
- }}
1106
+ onClick={() => selectItem(id)}
1054
1107
  >
1055
1108
  <span className="min-w-0">
1056
1109
  <span className="block truncate text-sm font-medium">
@@ -132,6 +132,43 @@ export const triageConfig = table("factory_config", {
132
132
  orgId: text("org_id"),
133
133
  });
134
134
 
135
+ export const factoryAuditEvents = table(
136
+ "factory_audit_events",
137
+ {
138
+ id: text("id").primaryKey(),
139
+ automationRunId: text("automation_run_id"),
140
+ automationThreadId: text("automation_thread_id"),
141
+ automationName: text("automation_name"),
142
+ itemId: text("item_id"),
143
+ source: text("source"),
144
+ sourceUrl: text("source_url"),
145
+ action: text("action").notNull(),
146
+ kind: text("kind").notNull(),
147
+ status: text("status").notNull(),
148
+ summary: text("summary").notNull(),
149
+ detailsJson: text("details_json").notNull().default("{}"),
150
+ createdAt: text("created_at").notNull().default(now()),
151
+ ownerEmail: text("owner_email").notNull(),
152
+ orgId: text("org_id"),
153
+ },
154
+ (event) => ({
155
+ orgCreatedIdx: index("factory_audit_events_org_created_idx").on(
156
+ event.orgId,
157
+ event.createdAt,
158
+ ),
159
+ runCreatedIdx: index("factory_audit_events_run_created_idx").on(
160
+ event.orgId,
161
+ event.automationRunId,
162
+ event.createdAt,
163
+ ),
164
+ itemCreatedIdx: index("factory_audit_events_item_created_idx").on(
165
+ event.orgId,
166
+ event.itemId,
167
+ event.createdAt,
168
+ ),
169
+ }),
170
+ );
171
+
135
172
  export const factoryDefinitions = table(
136
173
  "factory_definitions",
137
174
  {
@@ -45,7 +45,11 @@ export async function requireFactoryAutomation(
45
45
 
46
46
  const definition = (
47
47
  await listAutomationDefinitions(
48
- { userEmail: identity.userEmail, orgId: identity.orgId },
48
+ {
49
+ userEmail: identity.userEmail,
50
+ orgId: identity.orgId,
51
+ appId: "factory",
52
+ },
49
53
  "organization",
50
54
  )
51
55
  ).find((entry) => entry.resource.id === lineage.triggerId);
@@ -258,6 +258,35 @@ const migrations = [
258
258
  ALTER TABLE factory_config ADD COLUMN last_automation_failure_alert_at TEXT;
259
259
  `,
260
260
  },
261
+ {
262
+ version: 18,
263
+ name: "factory-audit-events-table",
264
+ sql: `
265
+ CREATE TABLE IF NOT EXISTS factory_audit_events (
266
+ id TEXT PRIMARY KEY,
267
+ automation_run_id TEXT,
268
+ automation_thread_id TEXT,
269
+ automation_name TEXT,
270
+ item_id TEXT,
271
+ source TEXT,
272
+ source_url TEXT,
273
+ action TEXT NOT NULL,
274
+ kind TEXT NOT NULL,
275
+ status TEXT NOT NULL,
276
+ summary TEXT NOT NULL,
277
+ details_json TEXT NOT NULL DEFAULT '{}',
278
+ created_at TEXT NOT NULL,
279
+ owner_email TEXT NOT NULL,
280
+ org_id TEXT
281
+ );
282
+ CREATE INDEX IF NOT EXISTS factory_audit_events_org_created_idx
283
+ ON factory_audit_events (org_id, created_at);
284
+ CREATE INDEX IF NOT EXISTS factory_audit_events_run_created_idx
285
+ ON factory_audit_events (org_id, automation_run_id, created_at);
286
+ CREATE INDEX IF NOT EXISTS factory_audit_events_item_created_idx
287
+ ON factory_audit_events (org_id, item_id, created_at);
288
+ `,
289
+ },
261
290
  ];
262
291
 
263
292
  export const runFactoryMigrations = runMigrations(migrations, {
@@ -0,0 +1,79 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ import type { ActionRunContext } from "@agent-native/core/action";
4
+
5
+ import { getDb } from "../db/index.js";
6
+ import { factoryAuditEvents } from "../db/schema.js";
7
+
8
+ const MAX_SUMMARY_LENGTH = 500;
9
+ const MAX_DETAILS_LENGTH = 4_000;
10
+
11
+ export type FactoryAuditKind =
12
+ | "observed"
13
+ | "read"
14
+ | "decision"
15
+ | "external_action"
16
+ | "governance";
17
+
18
+ export type FactoryAuditStatus = "success" | "error" | "skipped";
19
+
20
+ export interface FactoryAuditInput {
21
+ action: string;
22
+ kind: FactoryAuditKind;
23
+ status?: FactoryAuditStatus;
24
+ itemId?: string | null;
25
+ source?: string | null;
26
+ sourceUrl?: string | null;
27
+ summary: string;
28
+ details?: Record<string, unknown>;
29
+ }
30
+
31
+ function boundedText(value: string, max: number): string {
32
+ const compact = value.replace(/\s+/g, " ").trim();
33
+ return compact.length > max ? `${compact.slice(0, max - 1)}…` : compact;
34
+ }
35
+
36
+ function boundedDetails(value: Record<string, unknown> | undefined): string {
37
+ if (!value) return "{}";
38
+ let serialized: string;
39
+ try {
40
+ serialized = JSON.stringify(value);
41
+ } catch {
42
+ throw new Error("Factory audit details are not serializable.");
43
+ }
44
+ return serialized.length > MAX_DETAILS_LENGTH
45
+ ? JSON.stringify({ truncated: true })
46
+ : serialized;
47
+ }
48
+
49
+ /**
50
+ * Persist a bounded, source-linked explanation of an automation action. The
51
+ * agent run id is the join key to core's automation history; no raw provider
52
+ * payload is copied into this table.
53
+ */
54
+ export async function recordFactoryAudit(
55
+ context: ActionRunContext | undefined,
56
+ identity: { userEmail: string; orgId: string },
57
+ input: FactoryAuditInput,
58
+ ): Promise<void> {
59
+ if (context?.caller !== "automation" || !context.runId) return;
60
+ await getDb()
61
+ .insert(factoryAuditEvents)
62
+ .values({
63
+ id: randomUUID(),
64
+ automationRunId: context.runId,
65
+ automationThreadId: context.threadId ?? null,
66
+ automationName: context.automation?.triggerName ?? null,
67
+ itemId: input.itemId ?? null,
68
+ source: input.source ?? null,
69
+ sourceUrl: input.sourceUrl ?? null,
70
+ action: boundedText(input.action, 120),
71
+ kind: input.kind,
72
+ status: input.status ?? "success",
73
+ summary: boundedText(input.summary, MAX_SUMMARY_LENGTH),
74
+ detailsJson: boundedDetails(input.details),
75
+ createdAt: new Date().toISOString(),
76
+ ownerEmail: identity.userEmail,
77
+ orgId: identity.orgId,
78
+ });
79
+ }