@agent-native/core 0.7.19 → 0.7.21

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 (263) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  3. package/dist/agent/engine/builder-engine.js +45 -2
  4. package/dist/agent/engine/builder-engine.js.map +1 -1
  5. package/dist/agent/loop-settings.d.ts +37 -0
  6. package/dist/agent/loop-settings.d.ts.map +1 -0
  7. package/dist/agent/loop-settings.js +127 -0
  8. package/dist/agent/loop-settings.js.map +1 -0
  9. package/dist/agent/production-agent.d.ts +8 -0
  10. package/dist/agent/production-agent.d.ts.map +1 -1
  11. package/dist/agent/production-agent.js +268 -29
  12. package/dist/agent/production-agent.js.map +1 -1
  13. package/dist/agent/run-manager.d.ts.map +1 -1
  14. package/dist/agent/run-manager.js +76 -3
  15. package/dist/agent/run-manager.js.map +1 -1
  16. package/dist/agent/run-store.d.ts +1 -1
  17. package/dist/agent/run-store.d.ts.map +1 -1
  18. package/dist/agent/run-store.js +65 -2
  19. package/dist/agent/run-store.js.map +1 -1
  20. package/dist/agent/thread-data-builder.d.ts +3 -0
  21. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  22. package/dist/agent/thread-data-builder.js +52 -10
  23. package/dist/agent/thread-data-builder.js.map +1 -1
  24. package/dist/agent/tool-search.d.ts +37 -0
  25. package/dist/agent/tool-search.d.ts.map +1 -0
  26. package/dist/agent/tool-search.js +201 -0
  27. package/dist/agent/tool-search.js.map +1 -0
  28. package/dist/agent/types.d.ts +8 -1
  29. package/dist/agent/types.d.ts.map +1 -1
  30. package/dist/agent/types.js.map +1 -1
  31. package/dist/cli/create.d.ts.map +1 -1
  32. package/dist/cli/create.js +44 -9
  33. package/dist/cli/create.js.map +1 -1
  34. package/dist/cli/workspacify.d.ts +2 -0
  35. package/dist/cli/workspacify.d.ts.map +1 -1
  36. package/dist/cli/workspacify.js +34 -1
  37. package/dist/cli/workspacify.js.map +1 -1
  38. package/dist/client/AssistantChat.d.ts.map +1 -1
  39. package/dist/client/AssistantChat.js +277 -18
  40. package/dist/client/AssistantChat.js.map +1 -1
  41. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  42. package/dist/client/ConnectBuilderCard.js +1 -1
  43. package/dist/client/ConnectBuilderCard.js.map +1 -1
  44. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  45. package/dist/client/MultiTabAssistantChat.js +14 -6
  46. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  47. package/dist/client/NewWorkspaceAppFlow.d.ts +14 -0
  48. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -0
  49. package/dist/client/NewWorkspaceAppFlow.js +198 -0
  50. package/dist/client/NewWorkspaceAppFlow.js.map +1 -0
  51. package/dist/client/PoweredByBadge.d.ts +10 -1
  52. package/dist/client/PoweredByBadge.d.ts.map +1 -1
  53. package/dist/client/PoweredByBadge.js +120 -8
  54. package/dist/client/PoweredByBadge.js.map +1 -1
  55. package/dist/client/agent-chat-adapter.d.ts +3 -5
  56. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  57. package/dist/client/agent-chat-adapter.js +26 -19
  58. package/dist/client/agent-chat-adapter.js.map +1 -1
  59. package/dist/client/agent-chat.d.ts.map +1 -1
  60. package/dist/client/agent-chat.js +15 -3
  61. package/dist/client/agent-chat.js.map +1 -1
  62. package/dist/client/analytics.d.ts +1 -1
  63. package/dist/client/analytics.d.ts.map +1 -1
  64. package/dist/client/analytics.js +141 -1
  65. package/dist/client/analytics.js.map +1 -1
  66. package/dist/client/builder-frame.d.ts +10 -0
  67. package/dist/client/builder-frame.d.ts.map +1 -0
  68. package/dist/client/builder-frame.js +94 -0
  69. package/dist/client/builder-frame.js.map +1 -0
  70. package/dist/client/composer/MentionPopover.d.ts.map +1 -1
  71. package/dist/client/composer/MentionPopover.js +5 -1
  72. package/dist/client/composer/MentionPopover.js.map +1 -1
  73. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  74. package/dist/client/composer/TiptapComposer.js +11 -6
  75. package/dist/client/composer/TiptapComposer.js.map +1 -1
  76. package/dist/client/error-format.d.ts +20 -1
  77. package/dist/client/error-format.d.ts.map +1 -1
  78. package/dist/client/error-format.js +53 -5
  79. package/dist/client/error-format.js.map +1 -1
  80. package/dist/client/index.d.ts +3 -1
  81. package/dist/client/index.d.ts.map +1 -1
  82. package/dist/client/index.js +3 -1
  83. package/dist/client/index.js.map +1 -1
  84. package/dist/client/notifications/NotificationsBell.d.ts.map +1 -1
  85. package/dist/client/notifications/NotificationsBell.js +28 -1
  86. package/dist/client/notifications/NotificationsBell.js.map +1 -1
  87. package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
  88. package/dist/client/onboarding/OnboardingPanel.js +88 -6
  89. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  90. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  91. package/dist/client/settings/SettingsPanel.js +145 -9
  92. package/dist/client/settings/SettingsPanel.js.map +1 -1
  93. package/dist/client/settings/useBuilderStatus.d.ts +13 -0
  94. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  95. package/dist/client/settings/useBuilderStatus.js +50 -9
  96. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  97. package/dist/client/sse-event-processor.d.ts +3 -0
  98. package/dist/client/sse-event-processor.d.ts.map +1 -1
  99. package/dist/client/sse-event-processor.js +88 -7
  100. package/dist/client/sse-event-processor.js.map +1 -1
  101. package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
  102. package/dist/client/tools/ToolsListPage.js +16 -1
  103. package/dist/client/tools/ToolsListPage.js.map +1 -1
  104. package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -1
  105. package/dist/client/tools/ToolsSidebarSection.js +63 -8
  106. package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
  107. package/dist/client/tools/tool-order.d.ts +7 -0
  108. package/dist/client/tools/tool-order.d.ts.map +1 -0
  109. package/dist/client/tools/tool-order.js +47 -0
  110. package/dist/client/tools/tool-order.js.map +1 -0
  111. package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
  112. package/dist/client/transcription/BuilderTranscriptionCta.js +71 -6
  113. package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
  114. package/dist/client/use-send-to-agent-chat.d.ts.map +1 -1
  115. package/dist/client/use-send-to-agent-chat.js +11 -3
  116. package/dist/client/use-send-to-agent-chat.js.map +1 -1
  117. package/dist/client/useProductionAgent.d.ts.map +1 -1
  118. package/dist/client/useProductionAgent.js +1 -1
  119. package/dist/client/useProductionAgent.js.map +1 -1
  120. package/dist/db/client.d.ts.map +1 -1
  121. package/dist/db/client.js +5 -1
  122. package/dist/db/client.js.map +1 -1
  123. package/dist/deploy/build.d.ts +1 -0
  124. package/dist/deploy/build.d.ts.map +1 -1
  125. package/dist/deploy/build.js +4 -1
  126. package/dist/deploy/build.js.map +1 -1
  127. package/dist/oauth-tokens/index.d.ts +1 -1
  128. package/dist/oauth-tokens/index.d.ts.map +1 -1
  129. package/dist/oauth-tokens/index.js +1 -1
  130. package/dist/oauth-tokens/index.js.map +1 -1
  131. package/dist/oauth-tokens/store.d.ts.map +1 -1
  132. package/dist/oauth-tokens/store.js +6 -0
  133. package/dist/oauth-tokens/store.js.map +1 -1
  134. package/dist/observability/store.d.ts.map +1 -1
  135. package/dist/observability/store.js +19 -19
  136. package/dist/observability/store.js.map +1 -1
  137. package/dist/onboarding/default-steps.d.ts.map +1 -1
  138. package/dist/onboarding/default-steps.js +95 -61
  139. package/dist/onboarding/default-steps.js.map +1 -1
  140. package/dist/onboarding/plugin.d.ts.map +1 -1
  141. package/dist/onboarding/plugin.js +17 -8
  142. package/dist/onboarding/plugin.js.map +1 -1
  143. package/dist/org/migrations.js +2 -2
  144. package/dist/org/migrations.js.map +1 -1
  145. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  146. package/dist/scripts/agent-engines/list-agent-engines.js +2 -3
  147. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  148. package/dist/scripts/db/exec.d.ts +2 -1
  149. package/dist/scripts/db/exec.d.ts.map +1 -1
  150. package/dist/scripts/db/exec.js +264 -61
  151. package/dist/scripts/db/exec.js.map +1 -1
  152. package/dist/scripts/db/schema.d.ts.map +1 -1
  153. package/dist/scripts/db/schema.js +16 -4
  154. package/dist/scripts/db/schema.js.map +1 -1
  155. package/dist/scripts/dev/index.d.ts.map +1 -1
  156. package/dist/scripts/dev/index.js +36 -11
  157. package/dist/scripts/dev/index.js.map +1 -1
  158. package/dist/scripts/manage-agent-loop-settings.d.ts +7 -0
  159. package/dist/scripts/manage-agent-loop-settings.d.ts.map +1 -0
  160. package/dist/scripts/manage-agent-loop-settings.js +63 -0
  161. package/dist/scripts/manage-agent-loop-settings.js.map +1 -0
  162. package/dist/scripts/runner.d.ts.map +1 -1
  163. package/dist/scripts/runner.js +11 -0
  164. package/dist/scripts/runner.js.map +1 -1
  165. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  166. package/dist/server/agent-chat-plugin.js +60 -18
  167. package/dist/server/agent-chat-plugin.js.map +1 -1
  168. package/dist/server/app-url.d.ts +5 -4
  169. package/dist/server/app-url.d.ts.map +1 -1
  170. package/dist/server/app-url.js +8 -4
  171. package/dist/server/app-url.js.map +1 -1
  172. package/dist/server/auth.d.ts +8 -0
  173. package/dist/server/auth.d.ts.map +1 -1
  174. package/dist/server/auth.js +82 -29
  175. package/dist/server/auth.js.map +1 -1
  176. package/dist/server/better-auth-instance.d.ts.map +1 -1
  177. package/dist/server/better-auth-instance.js +16 -5
  178. package/dist/server/better-auth-instance.js.map +1 -1
  179. package/dist/server/builder-browser.d.ts +12 -0
  180. package/dist/server/builder-browser.d.ts.map +1 -1
  181. package/dist/server/builder-browser.js +36 -4
  182. package/dist/server/builder-browser.js.map +1 -1
  183. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  184. package/dist/server/core-routes-plugin.js +350 -53
  185. package/dist/server/core-routes-plugin.js.map +1 -1
  186. package/dist/server/credential-provider.d.ts +21 -3
  187. package/dist/server/credential-provider.d.ts.map +1 -1
  188. package/dist/server/credential-provider.js +51 -21
  189. package/dist/server/credential-provider.js.map +1 -1
  190. package/dist/server/google-oauth.d.ts +3 -0
  191. package/dist/server/google-oauth.d.ts.map +1 -1
  192. package/dist/server/google-oauth.js +27 -3
  193. package/dist/server/google-oauth.js.map +1 -1
  194. package/dist/server/index.d.ts +4 -3
  195. package/dist/server/index.d.ts.map +1 -1
  196. package/dist/server/index.js +4 -3
  197. package/dist/server/index.js.map +1 -1
  198. package/dist/server/onboarding-html.js +2 -2
  199. package/dist/server/onboarding-html.js.map +1 -1
  200. package/dist/server/schema-prompt.d.ts.map +1 -1
  201. package/dist/server/schema-prompt.js +2 -1
  202. package/dist/server/schema-prompt.js.map +1 -1
  203. package/dist/server/security-headers.d.ts +3 -0
  204. package/dist/server/security-headers.d.ts.map +1 -1
  205. package/dist/server/security-headers.js +7 -1
  206. package/dist/server/security-headers.js.map +1 -1
  207. package/dist/server/ssr-handler.d.ts.map +1 -1
  208. package/dist/server/ssr-handler.js +31 -6
  209. package/dist/server/ssr-handler.js.map +1 -1
  210. package/dist/templates/default/_gitignore +5 -1
  211. package/dist/templates/default/app/root.tsx +1 -0
  212. package/dist/templates/default/public/favicon.svg +3 -3
  213. package/dist/templates/default/public/icon-180.svg +3 -3
  214. package/dist/templates/default/public/icon-192.svg +3 -3
  215. package/dist/templates/default/public/icon-512.svg +3 -3
  216. package/dist/templates/workspace-core/AGENTS.md +23 -7
  217. package/dist/templates/workspace-core/package.json +2 -1
  218. package/dist/templates/workspace-core/src/credentials.ts +22 -11
  219. package/dist/templates/workspace-root/.env.example +7 -0
  220. package/dist/templates/workspace-root/README.md +6 -3
  221. package/dist/templates/workspace-root/_gitignore +3 -0
  222. package/dist/templates/workspace-root/package.json +3 -1
  223. package/dist/templates/workspace-root/scripts/workspace-dev.ts +375 -0
  224. package/dist/tools/actions.d.ts.map +1 -1
  225. package/dist/tools/actions.js +2 -0
  226. package/dist/tools/actions.js.map +1 -1
  227. package/dist/tools/html-shell.d.ts.map +1 -1
  228. package/dist/tools/html-shell.js +13 -1
  229. package/dist/tools/html-shell.js.map +1 -1
  230. package/dist/tools/store.d.ts.map +1 -1
  231. package/dist/tools/store.js +10 -10
  232. package/dist/tools/store.js.map +1 -1
  233. package/dist/tracking/providers.d.ts +1 -0
  234. package/dist/tracking/providers.d.ts.map +1 -1
  235. package/dist/tracking/providers.js +72 -0
  236. package/dist/tracking/providers.js.map +1 -1
  237. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  238. package/dist/vite/action-types-plugin.js +106 -9
  239. package/dist/vite/action-types-plugin.js.map +1 -1
  240. package/dist/vite/client.d.ts.map +1 -1
  241. package/dist/vite/client.js +62 -1
  242. package/dist/vite/client.js.map +1 -1
  243. package/docs/content/authentication.md +17 -13
  244. package/docs/content/deployment.md +11 -11
  245. package/docs/content/mcp-clients.md +2 -2
  246. package/docs/content/onboarding.md +32 -30
  247. package/docs/content/security.md +1 -1
  248. package/docs/content/tools.md +4 -0
  249. package/package.json +2 -2
  250. package/src/templates/default/_gitignore +5 -1
  251. package/src/templates/default/app/root.tsx +1 -0
  252. package/src/templates/default/public/favicon.svg +3 -3
  253. package/src/templates/default/public/icon-180.svg +3 -3
  254. package/src/templates/default/public/icon-192.svg +3 -3
  255. package/src/templates/default/public/icon-512.svg +3 -3
  256. package/src/templates/workspace-core/AGENTS.md +23 -7
  257. package/src/templates/workspace-core/package.json +2 -1
  258. package/src/templates/workspace-core/src/credentials.ts +22 -11
  259. package/src/templates/workspace-root/.env.example +7 -0
  260. package/src/templates/workspace-root/README.md +6 -3
  261. package/src/templates/workspace-root/_gitignore +3 -0
  262. package/src/templates/workspace-root/package.json +3 -1
  263. package/src/templates/workspace-root/scripts/workspace-dev.ts +375 -0
@@ -182,7 +182,7 @@ Workspace-scope secret writes still require org owner/admin role regardless of t
182
182
  - [ ] `A2A_SECRET` set on every app that calls or receives A2A traffic
183
183
  - [ ] `SECRETS_ENCRYPTION_KEY` set (or rely on the `BETTER_AUTH_SECRET` fallback)
184
184
  - [ ] `AUTH_MODE` is **not** set to `local`
185
- - [ ] `AUTH_SKIP_EMAIL_VERIFICATION` is **not** set (or set only on QA preview deploys)
185
+ - [ ] `AUTH_SKIP_EMAIL_VERIFICATION` is **not** set in production (or set only on QA preview deploys)
186
186
 
187
187
  ### Webhook secrets (set the ones for integrations you use)
188
188
 
@@ -52,6 +52,10 @@ Tools are fully capable despite being lightweight. They can:
52
52
 
53
53
  All of this works out of the box. No configuration, no new files, no schema changes.
54
54
 
55
+ ## Layout defaults {#layout}
56
+
57
+ Tools render with modest canvas padding by default so simple widgets and dashboards do not hug the iframe edge. For full-bleed experiences such as maps, canvases, or custom editors, set `data-tool-layout="full-bleed"` or `data-tool-padding="none"` on the outermost element.
58
+
55
59
  ## Persistent storage {#persistent-storage}
56
60
 
57
61
  Every tool has access to a built-in key-value store. Data is automatically scoped per tool and per user — your data stays yours.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.7.19",
3
+ "version": "0.7.21",
4
4
  "type": "module",
5
5
  "description": "Framework for agent-native application development — where AI agents and UI share state via files",
6
6
  "license": "MIT",
@@ -89,7 +89,7 @@
89
89
  "build": "tsc && node scripts/finalize-build.mjs",
90
90
  "dev": "tsc --watch",
91
91
  "typecheck": "tsc --noEmit",
92
- "test": "vitest --run",
92
+ "test": "vitest --run src",
93
93
  "prepack": "cp ../../README.md ./README.md",
94
94
  "prepublishOnly": "npm run build",
95
95
  "release": "npm version patch && npm publish --access public"
@@ -1,5 +1,6 @@
1
1
  # React Router generated types
2
2
  .react-router/
3
+ .generated/
3
4
  .agent-native/
4
5
 
5
6
  # Logs
@@ -30,9 +31,12 @@ dist-ssr
30
31
  !.env.example
31
32
 
32
33
  # Data
34
+ data/*.db
35
+ data/*.db-shm
36
+ data/*.db-wal
33
37
  data/uploads/
34
38
  data/settings.json
35
- data/.sessions.json
39
+ data/.sessions.json
36
40
 
37
41
  # Learnings (personal preferences and memory — use learnings.defaults.md for tracked defaults)
38
42
  learnings.md
@@ -37,6 +37,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
37
37
  content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
38
38
  />
39
39
  <link rel="manifest" href="/manifest.json" />
40
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
40
41
  <meta name="theme-color" content="#111111" />
41
42
  <meta name="mobile-web-app-capable" content="yes" />
42
43
  <meta
@@ -1,6 +1,6 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" fill="none">
2
- <rect x="100" y="100" width="824" height="824" rx="185" fill="#000000"/>
3
- <g transform="translate(198.5 330.5) scale(5.5)">
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 600 600" fill="none">
2
+ <rect width="600" height="600" fill="#000000"/>
3
+ <g transform="translate(92 179) scale(3.6491228070175437)">
4
4
  <path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
5
5
  <path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
6
6
  <defs>
@@ -1,6 +1,6 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="180" height="180" viewBox="0 0 1024 1024" fill="none">
2
- <rect x="100" y="100" width="824" height="824" rx="185" fill="#000000"/>
3
- <g transform="translate(198.5 330.5) scale(5.5)">
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="180" height="180" viewBox="0 0 600 600" fill="none">
2
+ <rect width="600" height="600" fill="#000000"/>
3
+ <g transform="translate(92 179) scale(3.6491228070175437)">
4
4
  <path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
5
5
  <path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
6
6
  <defs>
@@ -1,6 +1,6 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 1024 1024" fill="none">
2
- <rect x="100" y="100" width="824" height="824" rx="185" fill="#000000"/>
3
- <g transform="translate(198.5 330.5) scale(5.5)">
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 600 600" fill="none">
2
+ <rect width="600" height="600" fill="#000000"/>
3
+ <g transform="translate(92 179) scale(3.6491228070175437)">
4
4
  <path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
5
5
  <path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
6
6
  <defs>
@@ -1,6 +1,6 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 1024 1024" fill="none">
2
- <rect x="100" y="100" width="824" height="824" rx="185" fill="#000000"/>
3
- <g transform="translate(198.5 330.5) scale(5.5)">
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 600 600" fill="none">
2
+ <rect width="600" height="600" fill="#000000"/>
3
+ <g transform="translate(92 179) scale(3.6491228070175437)">
4
4
  <path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
5
5
  <path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
6
6
  <defs>
@@ -17,11 +17,12 @@ business context without you having to repeat it per app.
17
17
  workspace share `DATABASE_URL` by default, so a record created by one
18
18
  app can be read by another as long as it respects the `owner_email` and
19
19
  `org_id` scoping conventions.
20
- - **All API secrets come from the central credential store.** Never
21
- hardcode a token. Call `resolveCompanyCredential("KEY")` from
22
- `@{{APP_NAME}}/core-module/credentials` it pulls from env first and
23
- falls back to the shared settings table so one rotation updates every
24
- app.
20
+ - **All API secrets come from scoped credential storage.** Never hardcode a
21
+ token or read `process.env` for user/org credentials in production. Call
22
+ `resolveCompanyCredential("KEY", { userEmail, orgId })` from
23
+ `@{{APP_NAME}}/core-module/credentials`, or omit the context only when the
24
+ current request/action already has agent-native request context. The helper
25
+ reads per-user credentials first and org-shared credentials second.
25
26
  - **UI chrome comes from the workspace core.** Wrap every screen in
26
27
  `<AuthenticatedLayout>` from `@{{APP_NAME}}/core-module/client`. Don't
27
28
  re-implement the brand header, sidebar, or org switcher per app.
@@ -46,10 +47,25 @@ Example rules:
46
47
  ## How to add a new app
47
48
 
48
49
  ```bash
49
- cd apps
50
- pnpm exec agent-native create <app-name>
50
+ pnpm exec agent-native create <app-name> --template=starter
51
51
  ```
52
52
 
53
+ Run this from the workspace root. The CLI detects the workspace and creates
54
+ `apps/<app-name>` with the workspace core module already connected. Use a
55
+ different template when useful, for example `--template=analytics` or
56
+ `--template=forms`.
57
+
58
+ The workspace dev command is a gateway:
59
+
60
+ ```bash
61
+ pnpm dev
62
+ ```
63
+
64
+ It opens Dispatch at `/dispatch`, serves every app at `/<app-name>`, and
65
+ auto-detects newly-created app directories. After creating an app, do not
66
+ restart the dev server unless the gateway reports an error; wait for it to
67
+ start the new app process, then open `/<app-name>`.
68
+
53
69
  The new app will automatically inherit:
54
70
 
55
71
  1. The workspace auth plugin (Better Auth + company SSO)
@@ -40,7 +40,8 @@
40
40
  "prepare": "tsc"
41
41
  },
42
42
  "dependencies": {
43
- "@agent-native/core": "latest"
43
+ "@agent-native/core": "latest",
44
+ "zod": "^4.3.6"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@types/node": "^24.2.1",
@@ -12,17 +12,18 @@
12
12
  * DATABASE_URL by default, so storing a credential once makes it
13
13
  * available everywhere.
14
14
  *
15
- * Once @agent-native/core publishes the upcoming 2-arg signature you can
16
- * extend this to take a `{ userEmail, orgId }` context and pass it through
17
- * for per-user / per-org scoping. The current shape works against both
18
- * versions — the published 1-arg signature ignores the second argument.
15
+ * A request/action context is required so credentials stay scoped to the
16
+ * correct user and organization. This helper can read that context
17
+ * automatically inside agent-native actions; otherwise pass it explicitly.
19
18
  */
20
19
  import { resolveCredential } from "@agent-native/core/credentials";
20
+ import {
21
+ getRequestOrgId,
22
+ getRequestUserEmail,
23
+ } from "@agent-native/core/server";
21
24
 
22
25
  /**
23
- * Optional context for scoping a credential lookup to a specific user or
24
- * org. Forward-compatible with the upcoming 2-arg @agent-native/core
25
- * signature; ignored by the current 1-arg published one.
26
+ * Optional context for scoping a credential lookup to a specific user or org.
26
27
  */
27
28
  export interface CompanyCredentialContext {
28
29
  userEmail?: string;
@@ -31,7 +32,7 @@ export interface CompanyCredentialContext {
31
32
 
32
33
  type ResolveCredentialFn = (
33
34
  key: string,
34
- ctx?: CompanyCredentialContext,
35
+ ctx: CompanyCredentialContext,
35
36
  ) => Promise<string | undefined>;
36
37
 
37
38
  /**
@@ -39,10 +40,10 @@ type ResolveCredentialFn = (
39
40
  * directly — it keeps your keys organized under a workspace namespace and
40
41
  * makes "where does this secret come from" greppable.
41
42
  *
42
- * Example:
43
+ * Inside an agent-native action:
43
44
  * const slackToken = await resolveCompanyCredential("SLACK_BOT_TOKEN");
44
45
  *
45
- * With per-user scoping (after upgrading @agent-native/core):
46
+ * Outside request context:
46
47
  * const slackToken = await resolveCompanyCredential("SLACK_BOT_TOKEN", {
47
48
  * userEmail: session.email,
48
49
  * orgId: session.orgId ?? null,
@@ -52,5 +53,15 @@ export async function resolveCompanyCredential(
52
53
  key: string,
53
54
  ctx?: CompanyCredentialContext,
54
55
  ): Promise<string | undefined> {
55
- return await (resolveCredential as ResolveCredentialFn)(key, ctx);
56
+ const effectiveCtx: CompanyCredentialContext = ctx?.userEmail
57
+ ? ctx
58
+ : {
59
+ userEmail: getRequestUserEmail() ?? undefined,
60
+ orgId: getRequestOrgId(),
61
+ };
62
+ if (!effectiveCtx.userEmail) return undefined;
63
+ return await (resolveCredential as ResolveCredentialFn)(key, {
64
+ userEmail: effectiveCtx.userEmail,
65
+ orgId: effectiveCtx.orgId ?? null,
66
+ });
56
67
  }
@@ -22,6 +22,13 @@ OPENAI_API_KEY=
22
22
  BUILDER_PRIVATE_KEY=
23
23
  BUILDER_PUBLIC_KEY=
24
24
 
25
+ # Builder app creation / branching. In local dev these can live in .env.
26
+ # In production, configure deploy env vars instead; production app creation
27
+ # never writes credentials or project IDs to the filesystem.
28
+ ENABLE_BUILDER=
29
+ DISPATCH_BUILDER_PROJECT_ID=
30
+ BUILDER_BRANCH_PROJECT_ID=
31
+
25
32
  # A2A shared secret — required for cross-app JWT verification. Every app
26
33
  # in the workspace should use the same value. Generate with:
27
34
  # openssl rand -hex 32
@@ -40,14 +40,17 @@ the workspace core package (`@{{APP_NAME}}/core-module`).
40
40
  ```bash
41
41
  pnpm install
42
42
  cp .env.example .env # fill in DATABASE_URL, BETTER_AUTH_SECRET, ANTHROPIC_API_KEY
43
- pnpm dev # starts the example app
43
+ pnpm dev # starts the workspace gateway and opens Dispatch
44
44
  ```
45
45
 
46
+ The dev gateway serves Dispatch at `/dispatch` and every app at its own path
47
+ such as `/starter`. It watches `apps/`, so newly-created apps are detected and
48
+ started without restarting `pnpm dev`.
49
+
46
50
  ## Adding a new app
47
51
 
48
52
  ```bash
49
- cd apps
50
- pnpm exec agent-native create crm
53
+ pnpm exec agent-native create crm --template=starter
51
54
  ```
52
55
 
53
56
  The CLI detects the workspace root and scaffolds a minimal app that already
@@ -19,5 +19,8 @@ packages/*/.env
19
19
  .pnpm-store/
20
20
  pnpm-debug.log*
21
21
 
22
+ # Local workspace data
23
+ data/
24
+
22
25
  # Personal agent memory — per-developer, not shared
23
26
  learnings.md
@@ -3,7 +3,7 @@
3
3
  "private": true,
4
4
  "version": "0.0.0",
5
5
  "scripts": {
6
- "dev": "pnpm --filter example dev",
6
+ "dev": "tsx scripts/workspace-dev.ts",
7
7
  "build": "pnpm -r build",
8
8
  "typecheck": "pnpm -r typecheck"
9
9
  },
@@ -11,7 +11,9 @@
11
11
  "workspaceCore": "@{{APP_NAME}}/core-module"
12
12
  },
13
13
  "devDependencies": {
14
+ "@types/node": "^24.2.1",
14
15
  "prettier": "^3.6.2",
16
+ "tsx": "catalog:",
15
17
  "typescript": "^6.0.3"
16
18
  },
17
19
  "packageManager": "pnpm@10.14.0"
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env tsx
2
+ import { spawn, type ChildProcess } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import http from "node:http";
5
+ import net from "node:net";
6
+ import path from "node:path";
7
+
8
+ interface WorkspaceApp {
9
+ id: string;
10
+ name: string;
11
+ dir: string;
12
+ port: number;
13
+ process?: ChildProcess;
14
+ }
15
+
16
+ const root = process.cwd();
17
+ const appsDir = path.join(root, "apps");
18
+ fs.mkdirSync(path.join(root, "data"), { recursive: true });
19
+ const gatewayHost = process.env.WORKSPACE_HOST || "127.0.0.1";
20
+ const requestedPort = Number(
21
+ process.env.WORKSPACE_PORT || process.env.PORT || 8080,
22
+ );
23
+ const appPortStart = Number(process.env.WORKSPACE_APP_PORT_START || 8100);
24
+ let gatewayUrl = `http://${gatewayHost}:${requestedPort}`;
25
+
26
+ function readJson(file: string): any {
27
+ try {
28
+ return JSON.parse(fs.readFileSync(file, "utf8"));
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ function discoverApps(): WorkspaceApp[] {
35
+ if (!fs.existsSync(appsDir)) return [];
36
+ return fs
37
+ .readdirSync(appsDir, { withFileTypes: true })
38
+ .filter((entry) => entry.isDirectory())
39
+ .map((entry) => {
40
+ const dir = path.join(appsDir, entry.name);
41
+ const pkg = readJson(path.join(dir, "package.json"));
42
+ if (!pkg) return null;
43
+ return {
44
+ id: entry.name,
45
+ name: pkg.displayName || pkg.name || entry.name,
46
+ dir,
47
+ port: appPortStart,
48
+ } satisfies WorkspaceApp;
49
+ })
50
+ .filter((app): app is WorkspaceApp => !!app)
51
+ .sort((a, b) => {
52
+ if (a.id === "dispatch") return -1;
53
+ if (b.id === "dispatch") return 1;
54
+ return a.id.localeCompare(b.id);
55
+ })
56
+ .map((app, index) => ({ ...app, port: appPortStart + index }));
57
+ }
58
+
59
+ const apps = discoverApps();
60
+ if (apps.length === 0) {
61
+ console.error("[workspace] No apps found under ./apps");
62
+ process.exit(1);
63
+ }
64
+
65
+ const appById = new Map(apps.map((app) => [app.id, app]));
66
+ const defaultApp =
67
+ process.env.WORKSPACE_DEFAULT_APP &&
68
+ appById.has(process.env.WORKSPACE_DEFAULT_APP)
69
+ ? process.env.WORKSPACE_DEFAULT_APP
70
+ : appById.has("dispatch")
71
+ ? "dispatch"
72
+ : apps[0].id;
73
+
74
+ function syncApps(): void {
75
+ const discovered = discoverApps();
76
+ for (const app of discovered) {
77
+ if (appById.has(app.id)) continue;
78
+ const usedPorts = new Set(apps.map((existing) => existing.port));
79
+ let port = appPortStart;
80
+ while (usedPorts.has(port)) port++;
81
+ const next = { ...app, port };
82
+ apps.push(next);
83
+ apps.sort((a, b) => {
84
+ if (a.id === "dispatch") return -1;
85
+ if (b.id === "dispatch") return 1;
86
+ return a.id.localeCompare(b.id);
87
+ });
88
+ appById.set(next.id, next);
89
+ console.log(`[workspace] Detected new app: /${next.id}`);
90
+ startApp(next);
91
+ }
92
+ }
93
+
94
+ let syncTimer: NodeJS.Timeout | undefined;
95
+ function scheduleSync(): void {
96
+ if (syncTimer) clearTimeout(syncTimer);
97
+ syncTimer = setTimeout(syncApps, 400);
98
+ }
99
+
100
+ function firstPathSegment(url: string | undefined): string | null {
101
+ if (!url) return null;
102
+ try {
103
+ const parsed = new URL(url, "http://workspace.local");
104
+ const [segment] = parsed.pathname.split("/").filter(Boolean);
105
+ return segment || null;
106
+ } catch {
107
+ return null;
108
+ }
109
+ }
110
+
111
+ function appForRequest(req: http.IncomingMessage): WorkspaceApp | null {
112
+ const direct = firstPathSegment(req.url);
113
+ if (direct && appById.has(direct)) return appById.get(direct) ?? null;
114
+ const referer = req.headers.referer;
115
+ const fromReferer =
116
+ typeof referer === "string" ? firstPathSegment(referer) : null;
117
+ return fromReferer && appById.has(fromReferer)
118
+ ? (appById.get(fromReferer) ?? null)
119
+ : null;
120
+ }
121
+
122
+ function startApp(app: WorkspaceApp): void {
123
+ const basePath = `/${app.id}`;
124
+ const child = spawn(
125
+ "pnpm",
126
+ [
127
+ "--dir",
128
+ app.dir,
129
+ "exec",
130
+ "vite",
131
+ "--host",
132
+ "127.0.0.1",
133
+ "--port",
134
+ String(app.port),
135
+ "--strictPort",
136
+ ],
137
+ {
138
+ cwd: root,
139
+ stdio: ["ignore", "pipe", "pipe"],
140
+ env: {
141
+ ...process.env,
142
+ APP_NAME: app.id,
143
+ APP_BASE_PATH: basePath,
144
+ VITE_APP_BASE_PATH: basePath,
145
+ PORT: String(app.port),
146
+ WORKSPACE_GATEWAY_URL: gatewayUrl,
147
+ },
148
+ },
149
+ );
150
+ app.process = child;
151
+
152
+ const prefix = `[${app.id}]`;
153
+ child.stdout?.on("data", (chunk) => {
154
+ process.stdout.write(
155
+ String(chunk)
156
+ .split(/\r?\n/)
157
+ .filter(Boolean)
158
+ .map((line) => `${prefix} ${line}`)
159
+ .join("\n") + "\n",
160
+ );
161
+ });
162
+ child.stderr?.on("data", (chunk) => {
163
+ process.stderr.write(
164
+ String(chunk)
165
+ .split(/\r?\n/)
166
+ .filter(Boolean)
167
+ .map((line) => `${prefix} ${line}`)
168
+ .join("\n") + "\n",
169
+ );
170
+ });
171
+ child.on("exit", (code) => {
172
+ if (code === 0 || shuttingDown) return;
173
+ console.error(`${prefix} exited with code ${code}`);
174
+ });
175
+ }
176
+
177
+ function renderIndex(): string {
178
+ return `<!doctype html>
179
+ <html>
180
+ <head>
181
+ <meta charset="utf-8" />
182
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
183
+ <title>Agent-Native Workspace</title>
184
+ <style>
185
+ body { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 0; padding: 32px; background: #fafafa; color: #171717; }
186
+ main { max-width: 760px; margin: 0 auto; }
187
+ a { color: inherit; text-decoration: none; }
188
+ .grid { display: grid; gap: 12px; margin-top: 20px; }
189
+ .card { display: flex; justify-content: space-between; border: 1px solid #d4d4d4; border-radius: 8px; padding: 14px 16px; background: white; }
190
+ .muted { color: #737373; }
191
+ </style>
192
+ </head>
193
+ <body>
194
+ <main>
195
+ <h1>Agent-Native Workspace</h1>
196
+ <p class="muted">Open an app below. Dispatch is the workspace control plane.</p>
197
+ <div class="grid">
198
+ ${apps
199
+ .map(
200
+ (app) =>
201
+ `<a class="card" href="/${app.id}"><strong>${app.name}</strong><span class="muted">/${app.id}</span></a>`,
202
+ )
203
+ .join("")}
204
+ </div>
205
+ </main>
206
+ </body>
207
+ </html>`;
208
+ }
209
+
210
+ function proxyHttp(
211
+ app: WorkspaceApp,
212
+ req: http.IncomingMessage,
213
+ res: http.ServerResponse,
214
+ ): void {
215
+ const headers = { ...req.headers, host: `127.0.0.1:${app.port}` };
216
+ const proxyReq = http.request(
217
+ {
218
+ hostname: "127.0.0.1",
219
+ port: app.port,
220
+ method: req.method,
221
+ path: req.url,
222
+ headers,
223
+ },
224
+ (proxyRes) => {
225
+ res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
226
+ proxyRes.pipe(res);
227
+ },
228
+ );
229
+
230
+ proxyReq.on("error", (err) => {
231
+ res.writeHead(502, { "content-type": "text/plain" });
232
+ res.end(`App "${app.id}" is not ready yet: ${err.message}`);
233
+ });
234
+
235
+ req.pipe(proxyReq);
236
+ }
237
+
238
+ function proxyUpgrade(
239
+ app: WorkspaceApp,
240
+ req: http.IncomingMessage,
241
+ socket: net.Socket,
242
+ head: Buffer,
243
+ ): void {
244
+ const target = net.connect(app.port, "127.0.0.1", () => {
245
+ const headers = Object.entries({
246
+ ...req.headers,
247
+ host: `127.0.0.1:${app.port}`,
248
+ })
249
+ .flatMap(([key, value]) =>
250
+ Array.isArray(value)
251
+ ? value.map((item) => `${key}: ${item}`)
252
+ : [`${key}: ${value ?? ""}`],
253
+ )
254
+ .join("\r\n");
255
+ target.write(
256
+ `${req.method} ${req.url} HTTP/${req.httpVersion}\r\n${headers}\r\n\r\n`,
257
+ );
258
+ if (head.length) target.write(head);
259
+ socket.pipe(target).pipe(socket);
260
+ });
261
+
262
+ target.on("error", () => socket.destroy());
263
+ }
264
+
265
+ let shuttingDown = false;
266
+ let workspaceStarted = false;
267
+
268
+ function startWorkspaceProcesses(): void {
269
+ if (workspaceStarted) return;
270
+ workspaceStarted = true;
271
+ for (const app of apps) startApp(app);
272
+ try {
273
+ fs.watch(appsDir, { recursive: true }, scheduleSync);
274
+ } catch {
275
+ // Some platforms do not support recursive directory watches.
276
+ }
277
+ setInterval(syncApps, 2_000).unref();
278
+ }
279
+
280
+ function openBrowser(url: string): void {
281
+ if (process.env.WORKSPACE_NO_OPEN === "1") return;
282
+ const command =
283
+ process.platform === "darwin"
284
+ ? "open"
285
+ : process.platform === "win32"
286
+ ? "cmd"
287
+ : "xdg-open";
288
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
289
+ const child = spawn(command, args, {
290
+ stdio: "ignore",
291
+ detached: true,
292
+ });
293
+ child.unref();
294
+ }
295
+
296
+ const server = http.createServer((req, res) => {
297
+ if (req.url === "/" || req.url === "/index.html") {
298
+ res.writeHead(302, { location: `/${defaultApp}` });
299
+ res.end();
300
+ return;
301
+ }
302
+
303
+ if (req.url === "/_workspace/apps") {
304
+ res.writeHead(200, { "content-type": "application/json" });
305
+ res.end(
306
+ JSON.stringify(
307
+ apps.map((app) => ({
308
+ id: app.id,
309
+ name: app.name,
310
+ path: `/${app.id}`,
311
+ port: app.port,
312
+ })),
313
+ ),
314
+ );
315
+ return;
316
+ }
317
+
318
+ const app = appForRequest(req);
319
+ if (!app) {
320
+ res.writeHead(404, { "content-type": "text/html" });
321
+ res.end(renderIndex());
322
+ return;
323
+ }
324
+ proxyHttp(app, req, res);
325
+ });
326
+
327
+ server.on("upgrade", (req, socket, head) => {
328
+ const app = appForRequest(req);
329
+ if (!app) {
330
+ socket.destroy();
331
+ return;
332
+ }
333
+ proxyUpgrade(app, req, socket, head);
334
+ });
335
+
336
+ function listen(port: number, attempts = 20): void {
337
+ server.once("error", (err: NodeJS.ErrnoException) => {
338
+ if (err.code === "EADDRINUSE" && attempts > 0) {
339
+ listen(port + 1, attempts - 1);
340
+ return;
341
+ }
342
+ console.error(`[workspace] Could not start gateway: ${err.message}`);
343
+ process.exit(1);
344
+ });
345
+ server.listen(port, gatewayHost, () => {
346
+ const address = server.address();
347
+ const actualPort =
348
+ typeof address === "object" && address ? address.port : port;
349
+ gatewayUrl = `http://${gatewayHost}:${actualPort}`;
350
+ console.log(`[workspace] Gateway: http://${gatewayHost}:${actualPort}`);
351
+ console.log(
352
+ `[workspace] Default: http://${gatewayHost}:${actualPort}/${defaultApp}`,
353
+ );
354
+ for (const app of apps) {
355
+ console.log(`[workspace] ${app.id}: /${app.id} -> 127.0.0.1:${app.port}`);
356
+ }
357
+ startWorkspaceProcesses();
358
+ openBrowser(`http://${gatewayHost}:${actualPort}/${defaultApp}`);
359
+ });
360
+ }
361
+
362
+ function shutdown(): void {
363
+ if (shuttingDown) return;
364
+ shuttingDown = true;
365
+ server.close();
366
+ for (const app of apps) {
367
+ app.process?.kill("SIGTERM");
368
+ }
369
+ setTimeout(() => process.exit(0), 300).unref();
370
+ }
371
+
372
+ process.on("SIGINT", shutdown);
373
+ process.on("SIGTERM", shutdown);
374
+
375
+ listen(requestedPort);