@agent-native/core 0.132.2 → 0.133.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 (436) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +161 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/correlation.ts +11 -1
  5. package/corpus/core/src/a2a/types.ts +11 -3
  6. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +33 -46
  7. package/corpus/core/src/agent/engine/builder-engine.ts +22 -10
  8. package/corpus/core/src/agent/engine/error-detail.ts +184 -3
  9. package/corpus/core/src/agent/engine/failure-taxonomy.ts +123 -0
  10. package/corpus/core/src/agent/engine/index.ts +8 -0
  11. package/corpus/core/src/agent/engine/registry.ts +92 -8
  12. package/corpus/core/src/agent/engine/translate-ai-sdk.ts +17 -2
  13. package/corpus/core/src/agent/production-agent.ts +13 -33
  14. package/corpus/core/src/agent/run-loop-with-resume.ts +1 -1
  15. package/corpus/core/src/agent/run-manager.ts +43 -14
  16. package/corpus/core/src/agent/run-store.ts +30 -3
  17. package/corpus/core/src/agent/thread-data-builder.ts +36 -4
  18. package/corpus/core/src/cli/templates-meta.ts +13 -0
  19. package/corpus/core/src/cli/workspace-dev.ts +1 -0
  20. package/corpus/core/src/client/error-format.ts +12 -0
  21. package/corpus/core/src/credentials/index.ts +51 -12
  22. package/corpus/core/src/extensions/url-safety.ts +46 -12
  23. package/corpus/core/src/integrations/adapters/slack.ts +32 -10
  24. package/corpus/core/src/integrations/google-docs-poller.ts +12 -1
  25. package/corpus/core/src/integrations/installations-store.ts +30 -0
  26. package/corpus/core/src/integrations/plugin.ts +5 -5
  27. package/corpus/core/src/integrations/types.ts +7 -0
  28. package/corpus/core/src/integrations/webhook-handler.ts +24 -1
  29. package/corpus/core/src/jobs/background-automation-runner.ts +38 -1
  30. package/corpus/core/src/provider-api/index.ts +8 -2
  31. package/corpus/core/src/scripts/call-agent.ts +6 -0
  32. package/corpus/core/src/server/agent-chat-plugin.ts +30 -9
  33. package/corpus/core/src/server/agent-discovery.ts +2 -0
  34. package/corpus/core/src/server/builder-design-systems.ts +21 -0
  35. package/corpus/core/src/server/credential-provider.ts +51 -12
  36. package/corpus/core/src/server/index.ts +1 -0
  37. package/corpus/core/src/server/onboarding-html.ts +37 -2
  38. package/corpus/core/src/shared/reasoning-effort.ts +1 -1
  39. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
  40. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +6 -2
  41. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +39 -19
  42. package/corpus/templates/assets/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  43. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +30 -3
  44. package/corpus/templates/brain/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  45. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
  46. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +54 -25
  47. package/corpus/templates/calendar/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  48. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +30 -3
  49. package/corpus/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  50. package/corpus/templates/clips/app/components/library/library-layout.tsx +37 -21
  51. package/corpus/templates/clips/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  52. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +1 -1
  53. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +1 -1
  54. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +34 -17
  55. package/corpus/templates/content/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  56. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -2
  57. package/corpus/templates/design/actions/create-design-system.ts +1 -0
  58. package/corpus/templates/design/actions/set-default-design-system.ts +34 -26
  59. package/corpus/templates/design/app/components/layout/Sidebar.tsx +21 -4
  60. package/corpus/templates/design/app/i18n-data.ts +39 -0
  61. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +229 -12
  62. package/corpus/templates/design/app/pages/DesignSystems.tsx +10 -4
  63. package/corpus/templates/design/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  64. package/corpus/templates/design/changelog/2026-07-31-design-system-indexing-keeps-your-chosen-name-when-you-uploa.md +6 -0
  65. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +19 -4
  66. package/corpus/templates/dispatch/AGENTS.md +4 -0
  67. package/corpus/templates/factory/.agents/skills/actions/SKILL.md +562 -0
  68. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +198 -0
  69. package/corpus/templates/factory/.agents/skills/agent-native-docs/SKILL.md +115 -0
  70. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +182 -0
  71. package/corpus/templates/factory/.agents/skills/capture-learnings/SKILL.md +89 -0
  72. package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +221 -0
  73. package/corpus/templates/factory/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  74. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  75. package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +52 -0
  76. package/corpus/templates/factory/.agents/skills/feature-flags/SKILL.md +169 -0
  77. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +174 -0
  78. package/corpus/templates/factory/.agents/skills/real-time-sync/SKILL.md +232 -0
  79. package/corpus/templates/factory/.agents/skills/security/SKILL.md +324 -0
  80. package/corpus/templates/factory/.agents/skills/self-modifying-code/SKILL.md +119 -0
  81. package/corpus/templates/factory/.agents/skills/shadcn-ui/SKILL.md +123 -0
  82. package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +232 -0
  83. package/corpus/templates/factory/.agents/skills/storing-data/SKILL.md +178 -0
  84. package/corpus/templates/factory/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
  85. package/corpus/templates/factory/.env.example +24 -0
  86. package/corpus/templates/factory/.ignore +0 -0
  87. package/corpus/templates/factory/.oxfmtrc.json +8 -0
  88. package/corpus/templates/factory/AGENTS.md +90 -0
  89. package/corpus/templates/factory/CHANGELOG.md +10 -0
  90. package/corpus/templates/factory/DEVELOPING.md +185 -0
  91. package/corpus/templates/factory/README.md +64 -0
  92. package/corpus/templates/factory/_gitignore +42 -0
  93. package/corpus/templates/factory/actions/add-factory-comment.ts +55 -0
  94. package/corpus/templates/factory/actions/approve-factory-item.ts +214 -0
  95. package/corpus/templates/factory/actions/babysit-pull-request.ts +126 -0
  96. package/corpus/templates/factory/actions/evaluate-triage-item.ts +172 -0
  97. package/corpus/templates/factory/actions/get-factory-graph.ts +62 -0
  98. package/corpus/templates/factory/actions/get-triage-config.ts +42 -0
  99. package/corpus/templates/factory/actions/get-triage-item.ts +74 -0
  100. package/corpus/templates/factory/actions/hello.ts +13 -0
  101. package/corpus/templates/factory/actions/ingest-github-observation.ts +100 -0
  102. package/corpus/templates/factory/actions/list-factories.ts +40 -0
  103. package/corpus/templates/factory/actions/list-factory-comments.ts +40 -0
  104. package/corpus/templates/factory/actions/list-triage-items.ts +85 -0
  105. package/corpus/templates/factory/actions/list-triage-rules.ts +44 -0
  106. package/corpus/templates/factory/actions/navigate.ts +41 -0
  107. package/corpus/templates/factory/actions/poll-slack-channel.ts +118 -0
  108. package/corpus/templates/factory/actions/reconcile-triage-run.ts +179 -0
  109. package/corpus/templates/factory/actions/record-triage-feedback.ts +58 -0
  110. package/corpus/templates/factory/actions/run.ts +2 -0
  111. package/corpus/templates/factory/actions/save-factory-graph.ts +129 -0
  112. package/corpus/templates/factory/actions/save-triage-config.ts +67 -0
  113. package/corpus/templates/factory/actions/save-triage-rule.ts +89 -0
  114. package/corpus/templates/factory/actions/suggest-factory-rules.ts +70 -0
  115. package/corpus/templates/factory/actions/view-screen.ts +73 -0
  116. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +441 -0
  117. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +388 -0
  118. package/corpus/templates/factory/app/components/layout/Header.tsx +60 -0
  119. package/corpus/templates/factory/app/components/layout/Layout.tsx +190 -0
  120. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +538 -0
  121. package/corpus/templates/factory/app/components/triage/triage-status-pill.tsx +28 -0
  122. package/corpus/templates/factory/app/components/ui/button.tsx +1 -0
  123. package/corpus/templates/factory/app/components/ui/card.tsx +1 -0
  124. package/corpus/templates/factory/app/components/ui/dropdown-menu.tsx +1 -0
  125. package/corpus/templates/factory/app/components/ui/input.tsx +1 -0
  126. package/corpus/templates/factory/app/components/ui/label.tsx +1 -0
  127. package/corpus/templates/factory/app/components/ui/sheet.tsx +1 -0
  128. package/corpus/templates/factory/app/components/ui/textarea.tsx +1 -0
  129. package/corpus/templates/factory/app/components/ui/toolkit-provider.tsx +17 -0
  130. package/corpus/templates/factory/app/components/ui/tooltip.tsx +1 -0
  131. package/corpus/templates/factory/app/design-system.ts +3 -0
  132. package/corpus/templates/factory/app/entry.client.tsx +22 -0
  133. package/corpus/templates/factory/app/entry.server.tsx +10 -0
  134. package/corpus/templates/factory/app/global.css +93 -0
  135. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +113 -0
  136. package/corpus/templates/factory/app/i18n/en-US.ts +211 -0
  137. package/corpus/templates/factory/app/i18n/index.ts +34 -0
  138. package/corpus/templates/factory/app/i18n-data.ts +556 -0
  139. package/corpus/templates/factory/app/lib/agent-page.tsx +49 -0
  140. package/corpus/templates/factory/app/lib/app-config.ts +11 -0
  141. package/corpus/templates/factory/app/lib/tab-id.ts +1 -0
  142. package/corpus/templates/factory/app/lib/utils.ts +1 -0
  143. package/corpus/templates/factory/app/root.tsx +175 -0
  144. package/corpus/templates/factory/app/routes/_index.tsx +94 -0
  145. package/corpus/templates/factory/app/routes/agent.tsx +24 -0
  146. package/corpus/templates/factory/app/routes/chat.$threadId.tsx +1 -0
  147. package/corpus/templates/factory/app/routes/database.tsx +17 -0
  148. package/corpus/templates/factory/app/routes/extensions.$id.$slug.tsx +2 -0
  149. package/corpus/templates/factory/app/routes/extensions.$id.tsx +11 -0
  150. package/corpus/templates/factory/app/routes/extensions._index.tsx +11 -0
  151. package/corpus/templates/factory/app/routes/extensions.tsx +5 -0
  152. package/corpus/templates/factory/app/routes/factory.tsx +1003 -0
  153. package/corpus/templates/factory/app/routes/observability.tsx +19 -0
  154. package/corpus/templates/factory/app/routes/settings.tsx +89 -0
  155. package/corpus/templates/factory/app/routes/team.tsx +11 -0
  156. package/corpus/templates/factory/app/routes.ts +4 -0
  157. package/corpus/templates/factory/app/vite-env.d.ts +6 -0
  158. package/corpus/templates/factory/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  159. package/corpus/templates/factory/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  160. package/corpus/templates/factory/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
  161. package/corpus/templates/factory/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  162. package/corpus/templates/factory/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  163. package/corpus/templates/factory/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  164. package/corpus/templates/factory/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  165. package/corpus/templates/factory/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
  166. package/corpus/templates/factory/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  167. package/corpus/templates/factory/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  168. package/corpus/templates/factory/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
  169. package/corpus/templates/factory/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
  170. package/corpus/templates/factory/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  171. package/corpus/templates/factory/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  172. package/corpus/templates/factory/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  173. package/corpus/templates/factory/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  174. package/corpus/templates/factory/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  175. package/corpus/templates/factory/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  176. package/corpus/templates/factory/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  177. package/corpus/templates/factory/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  178. package/corpus/templates/factory/changelog/2026-07-29-sidebar-footers-now-keep-feedback-search-and-collapse-togeth.md +6 -0
  179. package/corpus/templates/factory/changelog/2026-07-31-added-an-inspectable-observe-only-factory-queue-for-slack-.md +6 -0
  180. package/corpus/templates/factory/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  181. package/corpus/templates/factory/components.json +20 -0
  182. package/corpus/templates/factory/data/sync-config.json +1 -0
  183. package/corpus/templates/factory/learnings.defaults.md +5 -0
  184. package/corpus/templates/factory/netlify.toml +10 -0
  185. package/corpus/templates/factory/package.json +105 -0
  186. package/corpus/templates/factory/public/agent-native-icon-dark.svg +10 -0
  187. package/corpus/templates/factory/public/agent-native-icon-light.svg +10 -0
  188. package/corpus/templates/factory/public/agent-native-logo-dark.svg +21 -0
  189. package/corpus/templates/factory/public/agent-native-logo-light.svg +21 -0
  190. package/corpus/templates/factory/public/favicon.svg +1 -0
  191. package/corpus/templates/factory/public/icon-180.svg +1 -0
  192. package/corpus/templates/factory/public/icon-192.svg +1 -0
  193. package/corpus/templates/factory/public/icon-512.svg +1 -0
  194. package/corpus/templates/factory/public/manifest.json +21 -0
  195. package/corpus/templates/factory/react-router.config.ts +7 -0
  196. package/corpus/templates/factory/server/connectors/credentials.ts +129 -0
  197. package/corpus/templates/factory/server/connectors/slack.ts +129 -0
  198. package/corpus/templates/factory/server/db/index.ts +6 -0
  199. package/corpus/templates/factory/server/db/schema.ts +193 -0
  200. package/corpus/templates/factory/server/factory-graph/contracts.ts +212 -0
  201. package/corpus/templates/factory/server/factory-graph/store.ts +121 -0
  202. package/corpus/templates/factory/server/lib/require-workspace-member.ts +55 -0
  203. package/corpus/templates/factory/server/middleware/auth.ts +15 -0
  204. package/corpus/templates/factory/server/plugins/agent-chat.ts +58 -0
  205. package/corpus/templates/factory/server/plugins/auth.ts +14 -0
  206. package/corpus/templates/factory/server/plugins/factory-migrations.ts +247 -0
  207. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +116 -0
  208. package/corpus/templates/factory/server/plugins/integrations.ts +46 -0
  209. package/corpus/templates/factory/server/routes/[...page].get.ts +5 -0
  210. package/corpus/templates/factory/server/routes/_agent-native/factory/builder-callback.post.ts +116 -0
  211. package/corpus/templates/factory/server/triage/ai-services-git.ts +197 -0
  212. package/corpus/templates/factory/server/triage/builder-executor.ts +118 -0
  213. package/corpus/templates/factory/server/triage/contracts.ts +164 -0
  214. package/corpus/templates/factory/server/triage/github-ingestion.ts +35 -0
  215. package/corpus/templates/factory/server/triage/guards.ts +121 -0
  216. package/corpus/templates/factory/server/triage/ids.ts +27 -0
  217. package/corpus/templates/factory/server/triage/pr-babysit.ts +100 -0
  218. package/corpus/templates/factory/server/triage/pr-monitor.ts +220 -0
  219. package/corpus/templates/factory/server/triage/slack-client.ts +55 -0
  220. package/corpus/templates/factory/server/triage/slack-poller.ts +152 -0
  221. package/corpus/templates/factory/ssr-entry.ts +15 -0
  222. package/corpus/templates/factory/tsconfig.json +21 -0
  223. package/corpus/templates/factory/vite.config.ts +19 -0
  224. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +55 -5
  225. package/corpus/templates/forms/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  226. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +6 -2
  227. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +21 -6
  228. package/corpus/templates/plan/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  229. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +12 -0
  230. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -0
  231. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +11 -0
  232. package/corpus/templates/slides/AGENTS.md +5 -6
  233. package/corpus/templates/slides/actions/_await-fit-check.ts +20 -10
  234. package/corpus/templates/slides/actions/add-slide.ts +17 -4
  235. package/corpus/templates/slides/actions/create-design-system.ts +16 -4
  236. package/corpus/templates/slides/actions/delete-design-system.ts +18 -2
  237. package/corpus/templates/slides/actions/get-layout-overflows.ts +88 -0
  238. package/corpus/templates/slides/actions/import-file.ts +1 -0
  239. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +8 -0
  240. package/corpus/templates/slides/actions/patch-deck.ts +48 -16
  241. package/corpus/templates/slides/actions/set-default-design-system.ts +49 -19
  242. package/corpus/templates/slides/actions/update-slide.ts +13 -3
  243. package/corpus/templates/slides/actions/view-screen.ts +87 -10
  244. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +155 -28
  245. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +21 -17
  246. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +849 -341
  247. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +102 -2
  248. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +13 -16
  249. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +60 -12
  250. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +11 -1
  251. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +66 -19
  252. package/corpus/templates/slides/app/i18n/en-US.ts +23 -0
  253. package/corpus/templates/slides/app/lib/normalize-slide-padding.ts +8 -15
  254. package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -3
  255. package/corpus/templates/slides/app/pages/DesignSystems.tsx +4 -3
  256. package/corpus/templates/slides/app/pages/Index.tsx +1 -1
  257. package/corpus/templates/slides/changelog/2026-07-31-design-system-details-now-show-their-source-and-indexing-sta.md +6 -0
  258. package/corpus/templates/slides/changelog/2026-07-31-design-system-indexing-keeps-your-chosen-name-when-you-uploa.md +6 -0
  259. package/corpus/templates/slides/changelog/2026-07-31-design-system-setup-keeps-focus-rings-visible-and-uses-a-sim.md +6 -0
  260. package/corpus/templates/slides/changelog/2026-07-31-skip-prompt-is-now-available-in-the-new-deck-prompt-header.md +6 -0
  261. package/corpus/templates/slides/changelog/2026-07-31-slides-now-detect-real-text-and-box-overflow-across-the-deck.md +6 -0
  262. package/corpus/templates/slides/changelog/2026-07-31-the-agent-native-logo-stays-visible-when-the-sidebar-is-coll.md +6 -0
  263. package/corpus/templates/slides/server/handlers/design-systems.ts +1 -0
  264. package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +1 -0
  265. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +23 -4
  266. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
  267. package/corpus/templates/slides/server/plugins/creative-context.ts +16 -3
  268. package/corpus/templates/slides/server/workspace-defaults.ts +5 -1
  269. package/corpus/templates/slides/shared/slide-fit.ts +26 -0
  270. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +30 -3
  271. package/corpus/templates/tasks/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  272. package/dist/a2a/correlation.d.ts +4 -1
  273. package/dist/a2a/correlation.d.ts.map +1 -1
  274. package/dist/a2a/correlation.js +8 -1
  275. package/dist/a2a/correlation.js.map +1 -1
  276. package/dist/a2a/types.d.ts +11 -3
  277. package/dist/a2a/types.d.ts.map +1 -1
  278. package/dist/a2a/types.js.map +1 -1
  279. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  280. package/dist/agent/engine/ai-sdk-engine.js +27 -41
  281. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  282. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  283. package/dist/agent/engine/builder-engine.js +18 -8
  284. package/dist/agent/engine/builder-engine.js.map +1 -1
  285. package/dist/agent/engine/error-detail.d.ts +61 -0
  286. package/dist/agent/engine/error-detail.d.ts.map +1 -1
  287. package/dist/agent/engine/error-detail.js +145 -3
  288. package/dist/agent/engine/error-detail.js.map +1 -1
  289. package/dist/agent/engine/failure-taxonomy.d.ts +24 -0
  290. package/dist/agent/engine/failure-taxonomy.d.ts.map +1 -0
  291. package/dist/agent/engine/failure-taxonomy.js +88 -0
  292. package/dist/agent/engine/failure-taxonomy.js.map +1 -0
  293. package/dist/agent/engine/index.d.ts +2 -1
  294. package/dist/agent/engine/index.d.ts.map +1 -1
  295. package/dist/agent/engine/index.js +2 -1
  296. package/dist/agent/engine/index.js.map +1 -1
  297. package/dist/agent/engine/registry.d.ts +16 -0
  298. package/dist/agent/engine/registry.d.ts.map +1 -1
  299. package/dist/agent/engine/registry.js +71 -6
  300. package/dist/agent/engine/registry.js.map +1 -1
  301. package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
  302. package/dist/agent/engine/translate-ai-sdk.js +14 -2
  303. package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
  304. package/dist/agent/production-agent.d.ts.map +1 -1
  305. package/dist/agent/production-agent.js +13 -24
  306. package/dist/agent/production-agent.js.map +1 -1
  307. package/dist/agent/run-loop-with-resume.js +1 -1
  308. package/dist/agent/run-loop-with-resume.js.map +1 -1
  309. package/dist/agent/run-manager.d.ts.map +1 -1
  310. package/dist/agent/run-manager.js +39 -10
  311. package/dist/agent/run-manager.js.map +1 -1
  312. package/dist/agent/run-store.d.ts +10 -0
  313. package/dist/agent/run-store.d.ts.map +1 -1
  314. package/dist/agent/run-store.js +20 -3
  315. package/dist/agent/run-store.js.map +1 -1
  316. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  317. package/dist/agent/thread-data-builder.js +28 -4
  318. package/dist/agent/thread-data-builder.js.map +1 -1
  319. package/dist/cli/templates-meta.d.ts.map +1 -1
  320. package/dist/cli/templates-meta.js +13 -0
  321. package/dist/cli/templates-meta.js.map +1 -1
  322. package/dist/cli/workspace-dev.d.ts.map +1 -1
  323. package/dist/cli/workspace-dev.js +1 -0
  324. package/dist/cli/workspace-dev.js.map +1 -1
  325. package/dist/client/error-format.d.ts.map +1 -1
  326. package/dist/client/error-format.js +10 -0
  327. package/dist/client/error-format.js.map +1 -1
  328. package/dist/collab/awareness.d.ts +2 -2
  329. package/dist/collab/awareness.d.ts.map +1 -1
  330. package/dist/credentials/index.d.ts +9 -2
  331. package/dist/credentials/index.d.ts.map +1 -1
  332. package/dist/credentials/index.js +50 -8
  333. package/dist/credentials/index.js.map +1 -1
  334. package/dist/extensions/url-safety.d.ts.map +1 -1
  335. package/dist/extensions/url-safety.js +38 -12
  336. package/dist/extensions/url-safety.js.map +1 -1
  337. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  338. package/dist/integrations/adapters/slack.js +25 -8
  339. package/dist/integrations/adapters/slack.js.map +1 -1
  340. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  341. package/dist/integrations/google-docs-poller.js +13 -1
  342. package/dist/integrations/google-docs-poller.js.map +1 -1
  343. package/dist/integrations/installations-store.d.ts +10 -0
  344. package/dist/integrations/installations-store.d.ts.map +1 -1
  345. package/dist/integrations/installations-store.js +24 -0
  346. package/dist/integrations/installations-store.js.map +1 -1
  347. package/dist/integrations/plugin.d.ts.map +1 -1
  348. package/dist/integrations/plugin.js +6 -5
  349. package/dist/integrations/plugin.js.map +1 -1
  350. package/dist/integrations/types.d.ts +7 -0
  351. package/dist/integrations/types.d.ts.map +1 -1
  352. package/dist/integrations/types.js.map +1 -1
  353. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  354. package/dist/integrations/webhook-handler.js +16 -1
  355. package/dist/integrations/webhook-handler.js.map +1 -1
  356. package/dist/jobs/background-automation-runner.d.ts.map +1 -1
  357. package/dist/jobs/background-automation-runner.js +35 -2
  358. package/dist/jobs/background-automation-runner.js.map +1 -1
  359. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  360. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  361. package/dist/notifications/routes.d.ts +3 -3
  362. package/dist/observability/routes.d.ts +3 -3
  363. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  364. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  365. package/dist/provider-api/index.js +7 -3
  366. package/dist/provider-api/index.js.map +1 -1
  367. package/dist/scripts/call-agent.d.ts.map +1 -1
  368. package/dist/scripts/call-agent.js +6 -1
  369. package/dist/scripts/call-agent.js.map +1 -1
  370. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  371. package/dist/server/agent-chat-plugin.js +25 -7
  372. package/dist/server/agent-chat-plugin.js.map +1 -1
  373. package/dist/server/agent-discovery.d.ts +2 -0
  374. package/dist/server/agent-discovery.d.ts.map +1 -1
  375. package/dist/server/agent-discovery.js.map +1 -1
  376. package/dist/server/builder-design-systems.d.ts +4 -1
  377. package/dist/server/builder-design-systems.d.ts.map +1 -1
  378. package/dist/server/builder-design-systems.js +11 -1
  379. package/dist/server/builder-design-systems.js.map +1 -1
  380. package/dist/server/credential-provider.d.ts.map +1 -1
  381. package/dist/server/credential-provider.js +49 -12
  382. package/dist/server/credential-provider.js.map +1 -1
  383. package/dist/server/index.d.ts +1 -1
  384. package/dist/server/index.d.ts.map +1 -1
  385. package/dist/server/index.js.map +1 -1
  386. package/dist/server/onboarding-html.d.ts.map +1 -1
  387. package/dist/server/onboarding-html.js +37 -2
  388. package/dist/server/onboarding-html.js.map +1 -1
  389. package/dist/server/transcribe-voice.d.ts +1 -1
  390. package/dist/shared/reasoning-effort.d.ts +1 -0
  391. package/dist/shared/reasoning-effort.d.ts.map +1 -1
  392. package/dist/shared/reasoning-effort.js +1 -1
  393. package/dist/shared/reasoning-effort.js.map +1 -1
  394. package/dist/templates/chat/app/components/layout/Sidebar.tsx +30 -3
  395. package/dist/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  396. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
  397. package/package.json +1 -1
  398. package/src/a2a/correlation.ts +11 -1
  399. package/src/a2a/types.ts +11 -3
  400. package/src/agent/engine/ai-sdk-engine.ts +33 -46
  401. package/src/agent/engine/builder-engine.ts +22 -10
  402. package/src/agent/engine/error-detail.ts +184 -3
  403. package/src/agent/engine/failure-taxonomy.ts +123 -0
  404. package/src/agent/engine/index.ts +8 -0
  405. package/src/agent/engine/registry.ts +92 -8
  406. package/src/agent/engine/translate-ai-sdk.ts +17 -2
  407. package/src/agent/production-agent.ts +13 -33
  408. package/src/agent/run-loop-with-resume.ts +1 -1
  409. package/src/agent/run-manager.ts +43 -14
  410. package/src/agent/run-store.ts +30 -3
  411. package/src/agent/thread-data-builder.ts +36 -4
  412. package/src/cli/templates-meta.ts +13 -0
  413. package/src/cli/workspace-dev.ts +1 -0
  414. package/src/client/error-format.ts +12 -0
  415. package/src/credentials/index.ts +51 -12
  416. package/src/extensions/url-safety.ts +46 -12
  417. package/src/integrations/adapters/slack.ts +32 -10
  418. package/src/integrations/google-docs-poller.ts +12 -1
  419. package/src/integrations/installations-store.ts +30 -0
  420. package/src/integrations/plugin.ts +5 -5
  421. package/src/integrations/types.ts +7 -0
  422. package/src/integrations/webhook-handler.ts +24 -1
  423. package/src/jobs/background-automation-runner.ts +38 -1
  424. package/src/provider-api/index.ts +8 -2
  425. package/src/scripts/call-agent.ts +6 -0
  426. package/src/server/agent-chat-plugin.ts +30 -9
  427. package/src/server/agent-discovery.ts +2 -0
  428. package/src/server/builder-design-systems.ts +21 -0
  429. package/src/server/credential-provider.ts +51 -12
  430. package/src/server/index.ts +1 -0
  431. package/src/server/onboarding-html.ts +37 -2
  432. package/src/shared/reasoning-effort.ts +1 -1
  433. package/src/templates/chat/app/components/layout/Sidebar.tsx +30 -3
  434. package/src/templates/chat/changelog/2026-07-31-clicking-the-agent-native-logo-now-toggles-the-app-sidebar.md +6 -0
  435. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +11 -0
  436. package/corpus/templates/clips/desktop/src/lib/media-device-selection.ts +0 -157
@@ -0,0 +1,1003 @@
1
+ import {
2
+ useActionMutation,
3
+ useActionQuery,
4
+ } from "@agent-native/core/client/hooks";
5
+ import { useT } from "@agent-native/core/client/i18n";
6
+ import {
7
+ IconAlertCircle,
8
+ IconExternalLink,
9
+ IconLoader2,
10
+ IconPlayerPlay,
11
+ IconPlus,
12
+ } from "@tabler/icons-react";
13
+ import { useEffect, useMemo, useState } from "react";
14
+ import { useSearchParams } from "react-router";
15
+
16
+ import {
17
+ FactoryCanvas,
18
+ type FactoryCanvasEdge,
19
+ type FactoryCanvasGraph,
20
+ type FactoryCanvasNode,
21
+ } from "@/components/factory/FactoryCanvas";
22
+ import {
23
+ FactoryInspector,
24
+ type FactoryComment,
25
+ } from "@/components/factory/FactoryInspector";
26
+ import { TriageStatusPill } from "@/components/triage/triage-status-pill";
27
+ import { Button } from "@/components/ui/button";
28
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
29
+ import { Input } from "@/components/ui/input";
30
+ import { Label } from "@/components/ui/label";
31
+ import { Textarea } from "@/components/ui/textarea";
32
+
33
+ type FactoryGraphResponse = {
34
+ factory: {
35
+ id: string;
36
+ name: string;
37
+ description: string;
38
+ prompt: string;
39
+ graphVersion: number;
40
+ virtual: boolean;
41
+ };
42
+ graph: FactoryCanvasGraph;
43
+ metrics: {
44
+ totalItems: number;
45
+ slackItems: number;
46
+ githubItems: number;
47
+ decisions: number;
48
+ manualItems: number;
49
+ runs: number;
50
+ completedRuns: number;
51
+ };
52
+ nodeMetrics: Record<string, number>;
53
+ };
54
+
55
+ type FactorySummary = {
56
+ id: string;
57
+ name: string;
58
+ description: string;
59
+ graphVersion: number;
60
+ virtual?: boolean;
61
+ };
62
+
63
+ type TriageDecision = {
64
+ decisionId: string;
65
+ summary?: string | null;
66
+ reason?: string | null;
67
+ };
68
+
69
+ type TriageItem = {
70
+ itemId?: string;
71
+ id?: string;
72
+ source?: string | null;
73
+ sourceName?: string | null;
74
+ sourceUrl?: string | null;
75
+ risk?: string | null;
76
+ status?: string | null;
77
+ coverage?: string | number | null;
78
+ reason?: string | null;
79
+ decisionSummary?: string | null;
80
+ decisions?: TriageDecision[] | null;
81
+ };
82
+
83
+ type TriageRule = {
84
+ id: string;
85
+ name: string;
86
+ promptText: string;
87
+ mode: string;
88
+ enabled: boolean;
89
+ promptVersion: number;
90
+ };
91
+
92
+ type TriageConfig = {
93
+ slackWorkspace?: "primary" | "secondary";
94
+ slackChannelId?: string | null;
95
+ slackChannelName?: string | null;
96
+ pollingEnabled?: boolean;
97
+ repository?: string | null;
98
+ };
99
+
100
+ type Verdict = "correct" | "incorrect" | "uncertain";
101
+ type WorkspaceTab = "map" | "inbox" | "rules" | "settings";
102
+
103
+ const DEFAULT_FACTORY_ID = "product-feedback";
104
+
105
+ export function meta() {
106
+ return [{ title: "Factory" }];
107
+ }
108
+
109
+ export default function FactoryRoute() {
110
+ const t = useT();
111
+ const [searchParams, setSearchParams] = useSearchParams();
112
+ const activeTab = parseWorkspaceTab(searchParams.get("tab"));
113
+ const factoryId = searchParams.get("factoryId") || DEFAULT_FACTORY_ID;
114
+ const [creating, setCreating] = useState(false);
115
+ const [draftGraph, setDraftGraph] = useState<FactoryCanvasGraph | null>(null);
116
+ const [dirty, setDirty] = useState(false);
117
+ const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
118
+ const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null);
119
+
120
+ function setActiveTab(tab: WorkspaceTab) {
121
+ setSearchParams(
122
+ (current) => {
123
+ const next = new URLSearchParams(current);
124
+ if (tab === "map") next.delete("tab");
125
+ else next.set("tab", tab);
126
+ return next;
127
+ },
128
+ { replace: true },
129
+ );
130
+ }
131
+
132
+ function setFactoryId(nextFactoryId: string) {
133
+ setSearchParams(
134
+ (current) => {
135
+ const next = new URLSearchParams(current);
136
+ if (nextFactoryId === DEFAULT_FACTORY_ID) next.delete("factoryId");
137
+ else next.set("factoryId", nextFactoryId);
138
+ return next;
139
+ },
140
+ { replace: true },
141
+ );
142
+ }
143
+
144
+ const factoryListQuery = useActionQuery("list-factories", {});
145
+ const graphQuery = useActionQuery("get-factory-graph", { factoryId });
146
+ const graphData = graphQuery.data as FactoryGraphResponse | undefined;
147
+ const graph = draftGraph ?? graphData?.graph ?? null;
148
+ const graphVersion = graphData?.factory.graphVersion ?? graph?.version ?? 1;
149
+ const commentsQuery = useActionQuery(
150
+ "list-factory-comments",
151
+ { factoryId, graphVersion },
152
+ { enabled: Boolean(graph) },
153
+ );
154
+ const saveGraphMutation = useActionMutation("save-factory-graph");
155
+ const addCommentMutation = useActionMutation("add-factory-comment");
156
+ const factoryList = (factoryListQuery.data ?? []) as FactorySummary[];
157
+ const comments = (commentsQuery.data ?? []) as FactoryComment[];
158
+
159
+ useEffect(() => {
160
+ if (!graphData || creating) return;
161
+ setDraftGraph(graphData.graph);
162
+ setDirty(false);
163
+ setSelectedNodeId(null);
164
+ setSelectedEdgeId(null);
165
+ }, [creating, graphData]);
166
+
167
+ useEffect(() => {
168
+ setSelectedNodeId(searchParams.get("node"));
169
+ setSelectedEdgeId(searchParams.get("edge"));
170
+ }, [searchParams]);
171
+
172
+ const selectedNode = graph?.nodes.find((node) => node.id === selectedNodeId);
173
+ const selectedEdge = graph?.edges.find((edge) => edge.id === selectedEdgeId);
174
+ const commentCounts = useMemo(() => {
175
+ const counts: Record<string, number> = {};
176
+ for (const comment of comments) {
177
+ if (comment.targetId)
178
+ counts[comment.targetId] = (counts[comment.targetId] ?? 0) + 1;
179
+ }
180
+ return counts;
181
+ }, [comments]);
182
+
183
+ function selectNode(nodeId: string) {
184
+ setSelectedNodeId(nodeId);
185
+ setSelectedEdgeId(null);
186
+ setSearchParams(
187
+ (current) => {
188
+ const next = new URLSearchParams(current);
189
+ next.set("node", nodeId);
190
+ next.delete("edge");
191
+ return next;
192
+ },
193
+ { replace: true },
194
+ );
195
+ }
196
+
197
+ function selectEdge(edgeId: string) {
198
+ setSelectedEdgeId(edgeId);
199
+ setSelectedNodeId(null);
200
+ setSearchParams(
201
+ (current) => {
202
+ const next = new URLSearchParams(current);
203
+ next.set("edge", edgeId);
204
+ next.delete("node");
205
+ return next;
206
+ },
207
+ { replace: true },
208
+ );
209
+ }
210
+
211
+ function updateGraph(next: FactoryCanvasGraph) {
212
+ setDraftGraph(next);
213
+ setDirty(true);
214
+ }
215
+
216
+ function addNode() {
217
+ if (!graph) return;
218
+ const id = `step-${Date.now().toString(36)}`;
219
+ const nextNode: FactoryCanvasNode = {
220
+ id,
221
+ label: t("factoryRoute.newStep"),
222
+ description: t("factoryRoute.newStepDescription"),
223
+ kind: "decision",
224
+ provider: "factory",
225
+ position: { x: 560, y: 520 },
226
+ };
227
+ updateGraph({ ...graph, nodes: [...graph.nodes, nextNode] });
228
+ selectNode(id);
229
+ }
230
+
231
+ function deleteNode(nodeId: string) {
232
+ if (!graph || graph.nodes.length <= 1) return;
233
+ updateGraph({
234
+ ...graph,
235
+ nodes: graph.nodes.filter((node) => node.id !== nodeId),
236
+ edges: graph.edges.filter(
237
+ (edge) => edge.source !== nodeId && edge.target !== nodeId,
238
+ ),
239
+ });
240
+ setSelectedNodeId(null);
241
+ }
242
+
243
+ function connectNodes(sourceId: string, targetId: string) {
244
+ if (!graph || sourceId === targetId) return;
245
+ const alreadyConnected = graph.edges.some(
246
+ (edge) => edge.source === sourceId && edge.target === targetId,
247
+ );
248
+ if (alreadyConnected) return;
249
+ updateGraph({
250
+ ...graph,
251
+ edges: [
252
+ ...graph.edges,
253
+ {
254
+ id: `route-${Date.now().toString(36)}`,
255
+ source: sourceId,
256
+ target: targetId,
257
+ label: t("factoryRoute.newRoute"),
258
+ condition: "",
259
+ },
260
+ ],
261
+ });
262
+ }
263
+
264
+ function startNewFactory() {
265
+ const base = graph ?? graphData?.graph;
266
+ if (!base) return;
267
+ const id = `factory-${Date.now().toString(36)}`;
268
+ setFactoryId(id);
269
+ setCreating(true);
270
+ setDraftGraph({
271
+ ...structuredClone(base),
272
+ version: 1,
273
+ name: t("factoryRoute.newFactory"),
274
+ description: t("factoryRoute.newFactoryDescription"),
275
+ });
276
+ setDirty(true);
277
+ setSelectedNodeId(null);
278
+ setSelectedEdgeId(null);
279
+ setActiveTab("map");
280
+ }
281
+
282
+ async function saveGraph() {
283
+ if (!graph) return;
284
+ await saveGraphMutation.mutateAsync({
285
+ factoryId,
286
+ name: graph.name,
287
+ description: graph.description,
288
+ prompt: graphData?.factory.prompt ?? "",
289
+ source: "manual",
290
+ changeSummary: creating
291
+ ? "Created from the Factory visual editor."
292
+ : "Updated in the Factory visual editor.",
293
+ graph,
294
+ });
295
+ setCreating(false);
296
+ setDirty(false);
297
+ await Promise.all([graphQuery.refetch(), factoryListQuery.refetch()]);
298
+ }
299
+
300
+ async function addComment(
301
+ targetType: "canvas" | "node" | "edge",
302
+ targetId?: string,
303
+ body?: string,
304
+ ) {
305
+ if (!body || !graph) return;
306
+ await addCommentMutation.mutateAsync({
307
+ factoryId,
308
+ graphVersion,
309
+ targetType,
310
+ ...(targetId ? { targetId } : {}),
311
+ body,
312
+ });
313
+ await commentsQuery.refetch();
314
+ }
315
+
316
+ if (!graph) {
317
+ return (
318
+ <div className="flex h-full items-center justify-center p-8 text-sm text-muted-foreground">
319
+ {graphQuery.isError
320
+ ? "Could not load this Factory."
321
+ : "Loading Factory..."}
322
+ </div>
323
+ );
324
+ }
325
+
326
+ return (
327
+ <div className="flex h-full min-h-0 flex-col overflow-hidden bg-background">
328
+ <header className="shrink-0 border-b bg-background px-4 py-2.5 lg:px-6">
329
+ <div className="flex flex-wrap items-center justify-between gap-3">
330
+ <div className="min-w-0 flex-1">
331
+ <div className="flex items-center gap-2">
332
+ <h1 className="truncate text-base font-semibold">{graph.name}</h1>
333
+ <span className="shrink-0 text-xs text-muted-foreground">
334
+ v{graphVersion}
335
+ </span>
336
+ </div>
337
+ <p className="hidden truncate text-xs text-muted-foreground md:block">
338
+ {graph.description}
339
+ </p>
340
+ </div>
341
+ <div className="flex items-center gap-2">
342
+ <select
343
+ aria-label={t("factoryRoute.selectFactory")}
344
+ value={factoryId}
345
+ onChange={(event) => {
346
+ setFactoryId(event.target.value);
347
+ setCreating(false);
348
+ setDraftGraph(null);
349
+ setDirty(false);
350
+ }}
351
+ className="h-9 max-w-[220px] rounded-md border bg-background px-3 text-sm"
352
+ >
353
+ {factoryList.map((factory) => (
354
+ <option key={factory.id} value={factory.id}>
355
+ {factory.name}
356
+ </option>
357
+ ))}
358
+ </select>
359
+ <Button
360
+ type="button"
361
+ variant="outline"
362
+ size="sm"
363
+ onClick={startNewFactory}
364
+ >
365
+ <IconPlus className="size-4" />
366
+ New
367
+ </Button>
368
+ </div>
369
+ </div>
370
+ <nav
371
+ className="mt-4 flex items-center gap-1 overflow-x-auto"
372
+ aria-label={t("factoryRoute.factoryViews")}
373
+ >
374
+ <TabButton
375
+ active={activeTab === "map"}
376
+ onClick={() => setActiveTab("map")}
377
+ >
378
+ Map
379
+ </TabButton>
380
+ <TabButton
381
+ active={activeTab === "inbox"}
382
+ onClick={() => setActiveTab("inbox")}
383
+ >
384
+ Inbox
385
+ </TabButton>
386
+ <TabButton
387
+ active={activeTab === "rules"}
388
+ onClick={() => setActiveTab("rules")}
389
+ >
390
+ Rules
391
+ </TabButton>
392
+ <TabButton
393
+ active={activeTab === "settings"}
394
+ onClick={() => setActiveTab("settings")}
395
+ >
396
+ Settings
397
+ </TabButton>
398
+ </nav>
399
+ </header>
400
+
401
+ <main className="min-h-0 flex-1 overflow-y-auto">
402
+ {activeTab === "map" ? (
403
+ <div className="grid min-h-full gap-0 xl:grid-cols-[minmax(0,1fr)_360px]">
404
+ <section className="min-w-0 p-4 lg:p-6">
405
+ <div className="mb-3 flex flex-wrap items-center justify-between gap-3">
406
+ <span className="text-xs font-medium text-muted-foreground">
407
+ {t("factoryRoute.mapEyebrow")}
408
+ </span>
409
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
410
+ <Metric
411
+ label={t("factoryRoute.metricSignals")}
412
+ value={graphData?.metrics.totalItems ?? 0}
413
+ />
414
+ <Metric
415
+ label={t("factoryRoute.metricDecisions")}
416
+ value={graphData?.metrics.decisions ?? 0}
417
+ />
418
+ <Metric
419
+ label={t("factoryRoute.metricRuns")}
420
+ value={graphData?.metrics.runs ?? 0}
421
+ />
422
+ </div>
423
+ </div>
424
+ <FactoryCanvas
425
+ graph={graph}
426
+ nodeMetrics={graphData?.nodeMetrics}
427
+ commentCounts={commentCounts}
428
+ selectedNodeId={selectedNodeId}
429
+ selectedEdgeId={selectedEdgeId}
430
+ onSelectNode={selectNode}
431
+ onSelectEdge={selectEdge}
432
+ onMoveNode={(nodeId, position) => {
433
+ updateGraph({
434
+ ...graph,
435
+ nodes: graph.nodes.map((node) =>
436
+ node.id === nodeId ? { ...node, position } : node,
437
+ ),
438
+ });
439
+ }}
440
+ onComment={addComment}
441
+ />
442
+ <div className="mt-4 flex flex-wrap items-center justify-between gap-3 text-xs text-muted-foreground">
443
+ <span>{t("factoryRoute.mapHint")}</span>
444
+ {dirty && (
445
+ <span className="text-amber-700 dark:text-amber-300">
446
+ {t("factoryRoute.unsavedChanges")}
447
+ </span>
448
+ )}
449
+ </div>
450
+ </section>
451
+ <FactoryInspector
452
+ graph={graph}
453
+ selectedNode={selectedNode}
454
+ selectedEdge={selectedEdge}
455
+ comments={comments}
456
+ dirty={dirty}
457
+ saving={saveGraphMutation.isPending}
458
+ onGraphChange={updateGraph}
459
+ onSave={() => void saveGraph()}
460
+ onAddComment={addComment}
461
+ onAddNode={addNode}
462
+ onDeleteNode={deleteNode}
463
+ onConnect={connectNodes}
464
+ />
465
+ </div>
466
+ ) : activeTab === "inbox" ? (
467
+ <InboxView t={t} />
468
+ ) : activeTab === "rules" ? (
469
+ <RulesView t={t} />
470
+ ) : (
471
+ <SettingsView t={t} />
472
+ )}
473
+ </main>
474
+ </div>
475
+ );
476
+ }
477
+
478
+ function TabButton({
479
+ active,
480
+ children,
481
+ onClick,
482
+ }: {
483
+ active: boolean;
484
+ children: React.ReactNode;
485
+ onClick: () => void;
486
+ }) {
487
+ return (
488
+ <button
489
+ type="button"
490
+ className={`inline-flex h-8 items-center gap-2 rounded-md px-3 text-sm transition-colors ${active ? "bg-muted font-medium text-foreground" : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"}`}
491
+ aria-current={active ? "page" : undefined}
492
+ onClick={onClick}
493
+ >
494
+ {children}
495
+ </button>
496
+ );
497
+ }
498
+
499
+ function parseWorkspaceTab(value: string | null): WorkspaceTab {
500
+ return value === "inbox" || value === "rules" || value === "settings"
501
+ ? value
502
+ : "map";
503
+ }
504
+
505
+ function Metric({ label, value }: { label: string; value: number }) {
506
+ return (
507
+ <span className="rounded-md border bg-card px-2.5 py-1.5">
508
+ <span className="font-medium text-foreground">
509
+ {value.toLocaleString()}
510
+ </span>{" "}
511
+ {label}
512
+ </span>
513
+ );
514
+ }
515
+
516
+ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
517
+ const [status, setStatus] = useState("");
518
+ const [selectedId, setSelectedId] = useState<string | null>(null);
519
+ const [feedbackNote, setFeedbackNote] = useState("");
520
+ const [verdict, setVerdict] = useState<Verdict | null>(null);
521
+ const listQuery = useActionQuery("list-triage-items", {
522
+ limit: 50,
523
+ ...(status.trim()
524
+ ? {
525
+ status: status.trim() as
526
+ | "received"
527
+ | "context_fetching"
528
+ | "evidence_ready"
529
+ | "classified"
530
+ | "shadow_decided"
531
+ | "needs_manual"
532
+ | "failed"
533
+ | "reconciliation_required",
534
+ }
535
+ : {}),
536
+ });
537
+ const detailQuery = useActionQuery(
538
+ "get-triage-item",
539
+ selectedId ? { itemId: selectedId } : undefined,
540
+ { enabled: Boolean(selectedId) },
541
+ );
542
+ const feedbackMutation = useActionMutation("record-triage-feedback");
543
+ const approveMutation = useActionMutation("approve-factory-item");
544
+ const items =
545
+ (listQuery.data as { items?: TriageItem[] } | TriageItem[] | undefined) ??
546
+ [];
547
+ const normalizedItems = Array.isArray(items) ? items : (items.items ?? []);
548
+ const selectedItem = detailQuery.data as TriageItem | undefined;
549
+
550
+ return (
551
+ <div className="grid gap-4 p-4 lg:grid-cols-[minmax(0,1.3fr)_minmax(320px,.7fr)] lg:p-6">
552
+ <Card>
553
+ <CardHeader className="gap-3 border-b sm:flex-row sm:items-end sm:justify-between">
554
+ <div>
555
+ <CardTitle className="text-base">
556
+ {t("factoryRoute.inboxTitle")}
557
+ </CardTitle>
558
+ <p className="mt-1 text-sm text-muted-foreground">
559
+ {t("factoryRoute.inboxDescription")}
560
+ </p>
561
+ </div>
562
+ <div className="flex items-end gap-2">
563
+ <div className="grid gap-1.5">
564
+ <Label htmlFor="factory-status-filter">Status</Label>
565
+ <Input
566
+ id="factory-status-filter"
567
+ className="h-8 w-36"
568
+ value={status}
569
+ onChange={(event) => setStatus(event.target.value)}
570
+ placeholder={t("triage.statusPlaceholder")}
571
+ />
572
+ </div>
573
+ <Button
574
+ variant="outline"
575
+ size="sm"
576
+ onClick={() => void listQuery.refetch()}
577
+ disabled={listQuery.isFetching}
578
+ >
579
+ {listQuery.isFetching && <IconLoader2 className="animate-spin" />}
580
+ Refresh
581
+ </Button>
582
+ </div>
583
+ </CardHeader>
584
+ <CardContent className="p-0">
585
+ {listQuery.isError ? (
586
+ <ErrorState
587
+ message="Could not load observations."
588
+ onRetry={() => void listQuery.refetch()}
589
+ />
590
+ ) : listQuery.isLoading ? (
591
+ <p className="p-4 text-sm text-muted-foreground">
592
+ {t("triage.loading")}
593
+ </p>
594
+ ) : normalizedItems.length === 0 ? (
595
+ <p className="p-4 text-sm text-muted-foreground">
596
+ {t("triage.empty")}
597
+ </p>
598
+ ) : (
599
+ <div className="divide-y">
600
+ {normalizedItems.map((item) => {
601
+ const id = item.itemId ?? item.id ?? "";
602
+ return (
603
+ <button
604
+ key={id}
605
+ type="button"
606
+ 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" : ""}`}
607
+ onClick={() => {
608
+ setSelectedId(id);
609
+ setVerdict(null);
610
+ setFeedbackNote("");
611
+ }}
612
+ >
613
+ <span className="min-w-0">
614
+ <span className="block truncate text-sm font-medium">
615
+ {item.sourceName ?? item.source ?? "Unknown source"}
616
+ </span>
617
+ {item.sourceUrl && (
618
+ <span className="mt-1 flex max-w-full items-center gap-1 truncate text-xs text-primary">
619
+ {item.sourceUrl}
620
+ <IconExternalLink className="size-3 shrink-0" />
621
+ </span>
622
+ )}
623
+ </span>
624
+ <span>
625
+ <span className="block text-[11px] uppercase tracking-wide text-muted-foreground">
626
+ Risk
627
+ </span>
628
+ <TriageStatusPill status={item.risk} />
629
+ </span>
630
+ <span>
631
+ <span className="block text-[11px] uppercase tracking-wide text-muted-foreground">
632
+ Status
633
+ </span>
634
+ <TriageStatusPill status={item.status} />
635
+ </span>
636
+ <span className="truncate">
637
+ <span className="block text-[11px] uppercase tracking-wide text-muted-foreground">
638
+ Reason
639
+ </span>
640
+ <span className="text-sm">
641
+ {item.reason ?? item.decisionSummary ?? "-"}
642
+ </span>
643
+ </span>
644
+ </button>
645
+ );
646
+ })}
647
+ </div>
648
+ )}
649
+ </CardContent>
650
+ </Card>
651
+ <Card>
652
+ <CardHeader>
653
+ <CardTitle className="text-base">{t("triage.detailTitle")}</CardTitle>
654
+ </CardHeader>
655
+ <CardContent className="space-y-4">
656
+ {!selectedItem ? (
657
+ <p className="text-sm text-muted-foreground">
658
+ {t("factoryRoute.selectObservation")}
659
+ </p>
660
+ ) : (
661
+ <>
662
+ <div className="flex items-start justify-between gap-3">
663
+ <div>
664
+ <p className="text-sm font-medium">
665
+ {selectedItem.sourceName ?? selectedItem.source}
666
+ </p>
667
+ {selectedItem.sourceUrl && (
668
+ <a
669
+ href={selectedItem.sourceUrl}
670
+ target="_blank"
671
+ rel="noreferrer"
672
+ className="mt-1 inline-flex items-center gap-1 text-xs text-primary hover:underline"
673
+ >
674
+ {t("triage.openSource")}
675
+ <IconExternalLink className="size-3" />
676
+ </a>
677
+ )}
678
+ </div>
679
+ <TriageStatusPill status={selectedItem.status} />
680
+ </div>
681
+ <Button
682
+ size="sm"
683
+ onClick={() => {
684
+ const decision =
685
+ selectedItem.decisions?.[
686
+ (selectedItem.decisions?.length ?? 1) - 1
687
+ ];
688
+ if (!decision) return;
689
+ approveMutation.mutate({
690
+ itemId: selectedItem.itemId ?? selectedItem.id ?? "",
691
+ decisionId: decision.decisionId,
692
+ confirm: true,
693
+ });
694
+ }}
695
+ disabled={
696
+ !selectedItem.decisions?.length || approveMutation.isPending
697
+ }
698
+ >
699
+ <IconPlayerPlay className="size-4" />
700
+ {t("factoryRoute.approveAndStart")}
701
+ </Button>
702
+ {selectedItem.decisionSummary && (
703
+ <p className="rounded-md bg-muted px-3 py-2 text-sm">
704
+ {selectedItem.decisionSummary}
705
+ </p>
706
+ )}
707
+ <div className="border-t pt-4">
708
+ <p className="text-sm font-medium">{t("triage.decisions")}</p>
709
+ {selectedItem.decisions?.length ? (
710
+ selectedItem.decisions.map((decision) => (
711
+ <div
712
+ key={decision.decisionId}
713
+ className="mt-3 space-y-3 rounded-md border p-3"
714
+ >
715
+ <p className="text-sm">
716
+ {decision.summary ?? decision.reason}
717
+ </p>
718
+ <div className="flex flex-wrap gap-2">
719
+ {(
720
+ ["correct", "incorrect", "uncertain"] as Verdict[]
721
+ ).map((value) => (
722
+ <Button
723
+ key={value}
724
+ size="sm"
725
+ variant={verdict === value ? "default" : "outline"}
726
+ onClick={() => setVerdict(value)}
727
+ >
728
+ {value}
729
+ </Button>
730
+ ))}
731
+ </div>
732
+ <Input
733
+ value={feedbackNote}
734
+ onChange={(event) =>
735
+ setFeedbackNote(event.target.value)
736
+ }
737
+ placeholder={t("triage.notePlaceholder")}
738
+ />
739
+ <Button
740
+ size="sm"
741
+ onClick={() => {
742
+ if (verdict)
743
+ feedbackMutation.mutate({
744
+ decisionId: decision.decisionId,
745
+ verdict,
746
+ ...(feedbackNote.trim()
747
+ ? { note: feedbackNote.trim() }
748
+ : {}),
749
+ });
750
+ }}
751
+ disabled={!verdict || feedbackMutation.isPending}
752
+ >
753
+ Record feedback
754
+ </Button>
755
+ </div>
756
+ ))
757
+ ) : (
758
+ <p className="mt-2 text-sm text-muted-foreground">
759
+ {t("triage.noDecisions")}
760
+ </p>
761
+ )}
762
+ </div>
763
+ </>
764
+ )}
765
+ </CardContent>
766
+ </Card>
767
+ </div>
768
+ );
769
+ }
770
+
771
+ function RulesView({ t }: { t: ReturnType<typeof useT> }) {
772
+ const [editingId, setEditingId] = useState<string | null>(null);
773
+ const [name, setName] = useState("");
774
+ const [prompt, setPrompt] = useState("");
775
+ const rulesQuery = useActionQuery("list-triage-rules", {});
776
+ const saveMutation = useActionMutation("save-triage-rule");
777
+ const rules = (rulesQuery.data ?? []) as TriageRule[];
778
+ function selectRule(rule: TriageRule) {
779
+ setEditingId(rule.id);
780
+ setName(rule.name);
781
+ setPrompt(rule.promptText);
782
+ }
783
+ return (
784
+ <div className="grid gap-4 p-4 lg:grid-cols-[280px_minmax(0,1fr)] lg:p-6">
785
+ <Card>
786
+ <CardHeader>
787
+ <CardTitle className="text-base">Rules</CardTitle>
788
+ <p className="text-sm text-muted-foreground">
789
+ {t("factoryRoute.rulesDescription")}
790
+ </p>
791
+ </CardHeader>
792
+ <CardContent className="space-y-2">
793
+ <Button
794
+ variant="outline"
795
+ className="w-full justify-start"
796
+ onClick={() => {
797
+ setEditingId(null);
798
+ setName("");
799
+ setPrompt("");
800
+ }}
801
+ >
802
+ <IconPlus className="size-4" />
803
+ {t("triage.newRule")}
804
+ </Button>
805
+ {rules.map((rule) => (
806
+ <Button
807
+ key={rule.id}
808
+ variant={editingId === rule.id ? "secondary" : "ghost"}
809
+ className="h-auto w-full justify-between gap-2 px-3 py-2 text-left"
810
+ onClick={() => selectRule(rule)}
811
+ >
812
+ <span className="min-w-0 truncate">{rule.name}</span>
813
+ <span className="text-xs text-muted-foreground">Shadow</span>
814
+ </Button>
815
+ ))}
816
+ </CardContent>
817
+ </Card>
818
+ <Card>
819
+ <CardHeader className="flex-row items-start justify-between gap-3">
820
+ <div>
821
+ <CardTitle className="text-base">
822
+ {t("factoryRoute.editRule")}
823
+ </CardTitle>
824
+ <p className="mt-1 text-sm text-muted-foreground">
825
+ Use prompts for classification; keep safety in structured guards.
826
+ </p>
827
+ </div>
828
+ </CardHeader>
829
+ <CardContent className="space-y-4">
830
+ <div className="grid gap-1.5">
831
+ <Label htmlFor="factory-rule-name">Name</Label>
832
+ <Input
833
+ id="factory-rule-name"
834
+ value={name}
835
+ onChange={(event) => setName(event.target.value)}
836
+ placeholder={t("triage.ruleNamePlaceholder")}
837
+ />
838
+ </div>
839
+ <div className="grid gap-1.5">
840
+ <Label htmlFor="factory-rule-prompt">
841
+ {t("triage.rulePrompt")}
842
+ </Label>
843
+ <Textarea
844
+ id="factory-rule-prompt"
845
+ value={prompt}
846
+ onChange={(event) => setPrompt(event.target.value)}
847
+ rows={8}
848
+ placeholder={t("triage.rulePromptPlaceholder")}
849
+ />
850
+ </div>
851
+ <div className="rounded-lg bg-muted/60 px-3 py-2 text-xs leading-5 text-muted-foreground">
852
+ {t("triage.hardGuards")}
853
+ </div>
854
+ <Button
855
+ onClick={() => {
856
+ if (!name.trim() || !prompt.trim()) return;
857
+ saveMutation.mutate({
858
+ ...(editingId ? { id: editingId } : {}),
859
+ name,
860
+ description: "",
861
+ promptText: prompt,
862
+ mode: "shadow",
863
+ enabled: true,
864
+ });
865
+ }}
866
+ disabled={!name.trim() || !prompt.trim() || saveMutation.isPending}
867
+ >
868
+ {saveMutation.isPending && <IconLoader2 className="animate-spin" />}
869
+ Save rule
870
+ </Button>
871
+ </CardContent>
872
+ </Card>
873
+ </div>
874
+ );
875
+ }
876
+
877
+ function SettingsView({ t }: { t: ReturnType<typeof useT> }) {
878
+ const [workspace, setWorkspace] = useState<"primary" | "secondary">(
879
+ "primary",
880
+ );
881
+ const [channelId, setChannelId] = useState("");
882
+ const [channelName, setChannelName] = useState("");
883
+ const [repository, setRepository] = useState("");
884
+ const [polling, setPolling] = useState(false);
885
+ const query = useActionQuery("get-triage-config", {});
886
+ const mutation = useActionMutation("save-triage-config");
887
+ useEffect(() => {
888
+ const data = query.data as TriageConfig | undefined;
889
+ if (!data) return;
890
+ setWorkspace(data.slackWorkspace ?? "primary");
891
+ setChannelId(data.slackChannelId ?? "");
892
+ setChannelName(data.slackChannelName ?? "");
893
+ setRepository(data.repository ?? "");
894
+ setPolling(data.pollingEnabled ?? false);
895
+ }, [query.data]);
896
+ return (
897
+ <div className="max-w-4xl p-4 lg:p-6">
898
+ <Card>
899
+ <CardHeader>
900
+ <CardTitle className="text-base">
901
+ {t("triage.settingsTitle")}
902
+ </CardTitle>
903
+ <p className="text-sm text-muted-foreground">
904
+ {t("factoryRoute.settingsDescription")}
905
+ </p>
906
+ </CardHeader>
907
+ <CardContent className="grid gap-4 sm:grid-cols-2">
908
+ <div className="grid gap-1.5">
909
+ <Label htmlFor="factory-slack-workspace">
910
+ {t("triage.slackWorkspace")}
911
+ </Label>
912
+ <select
913
+ id="factory-slack-workspace"
914
+ value={workspace}
915
+ onChange={(event) =>
916
+ setWorkspace(event.target.value as "primary" | "secondary")
917
+ }
918
+ className="h-9 rounded-md border bg-background px-3 text-sm"
919
+ >
920
+ <option value="primary">primary</option>
921
+ <option value="secondary">secondary</option>
922
+ </select>
923
+ </div>
924
+ <div className="grid gap-1.5">
925
+ <Label htmlFor="factory-slack-channel-id">
926
+ {t("triage.slackChannelId")}
927
+ </Label>
928
+ <Input
929
+ id="factory-slack-channel-id"
930
+ value={channelId}
931
+ onChange={(event) => setChannelId(event.target.value)}
932
+ placeholder="C0123456789"
933
+ />
934
+ </div>
935
+ <div className="grid gap-1.5">
936
+ <Label htmlFor="factory-slack-channel-name">
937
+ {t("triage.slackChannelName")}
938
+ </Label>
939
+ <Input
940
+ id="factory-slack-channel-name"
941
+ value={channelName}
942
+ onChange={(event) => setChannelName(event.target.value)}
943
+ placeholder="product-agent-native-feedback"
944
+ />
945
+ </div>
946
+ <div className="grid gap-1.5">
947
+ <Label htmlFor="factory-repository">Repository</Label>
948
+ <Input
949
+ id="factory-repository"
950
+ value={repository}
951
+ onChange={(event) => setRepository(event.target.value)}
952
+ placeholder={t("triage.repositoryPlaceholder")}
953
+ />
954
+ </div>
955
+ <label className="flex items-center gap-2 text-sm sm:col-span-2">
956
+ <input
957
+ type="checkbox"
958
+ checked={polling}
959
+ onChange={(event) => setPolling(event.target.checked)}
960
+ className="size-4 rounded border"
961
+ />
962
+ {t("triage.enablePolling")}
963
+ </label>
964
+ <div className="sm:col-span-2">
965
+ <Button
966
+ onClick={() =>
967
+ mutation.mutate({
968
+ slackWorkspace: workspace,
969
+ slackChannelId: channelId,
970
+ slackChannelName: channelName,
971
+ repository,
972
+ pollingEnabled: polling,
973
+ })
974
+ }
975
+ disabled={mutation.isPending}
976
+ >
977
+ {mutation.isPending && <IconLoader2 className="animate-spin" />}
978
+ Save settings
979
+ </Button>
980
+ </div>
981
+ </CardContent>
982
+ </Card>
983
+ </div>
984
+ );
985
+ }
986
+
987
+ function ErrorState({
988
+ message,
989
+ onRetry,
990
+ }: {
991
+ message: string;
992
+ onRetry: () => void;
993
+ }) {
994
+ return (
995
+ <div className="flex items-center gap-2 p-4 text-sm text-destructive">
996
+ <IconAlertCircle className="size-4 shrink-0" />
997
+ <span>{message}</span>
998
+ <Button variant="link" size="sm" className="h-auto p-0" onClick={onRetry}>
999
+ Retry
1000
+ </Button>
1001
+ </div>
1002
+ );
1003
+ }