@agent-native/core 0.162.0 → 0.163.1

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 (132) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +27 -2
  3. package/corpus/templates/analytics/server/plugins/collab.ts +1 -0
  4. package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +27 -2
  5. package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +27 -2
  6. package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +27 -2
  7. package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
  8. package/corpus/templates/clips/app/lib/camera-composite.ts +15 -1
  9. package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-crashes-when-a-camera-bubble-starts-at-a-tin.md +6 -0
  10. package/corpus/templates/content/.agents/skills/sharing/SKILL.md +27 -2
  11. package/corpus/templates/content/actions/create-content-database.ts +1 -1
  12. package/corpus/templates/content/actions/create-document.ts +1 -1
  13. package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +27 -2
  14. package/corpus/templates/design/.agents/skills/sharing/SKILL.md +27 -2
  15. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +6 -1
  16. package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +27 -2
  17. package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +27 -2
  18. package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +27 -2
  19. package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +27 -2
  20. package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +27 -2
  21. package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +27 -2
  22. package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +27 -2
  23. package/corpus/templates/slides/DEVELOPING.md +17 -0
  24. package/corpus/templates/slides/actions/get-deck-access-status.ts +67 -0
  25. package/corpus/templates/slides/actions/request-deck-access.ts +569 -0
  26. package/corpus/templates/slides/agent-native.json +3 -0
  27. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +3 -3
  28. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +3 -3
  29. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -1
  30. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
  31. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +1 -1
  32. package/corpus/templates/slides/app/components/layout/Header.tsx +2 -0
  33. package/corpus/templates/slides/app/hooks/use-deck-access.ts +51 -0
  34. package/corpus/templates/slides/app/i18n/en-US.ts +12 -0
  35. package/corpus/templates/slides/app/lib/deck-editor-loading.ts +7 -0
  36. package/corpus/templates/slides/app/pages/DeckEditor.tsx +173 -33
  37. package/corpus/templates/slides/changelog/2026-08-18-private-deck-links-now-explain-access-and-let-viewers-notify.md +6 -0
  38. package/corpus/templates/slides/server/db/schema.ts +10 -0
  39. package/corpus/templates/slides/server/handlers/request-auth-context.ts +1 -0
  40. package/corpus/templates/slides/server/plugins/db.ts +17 -0
  41. package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +27 -2
  42. package/dist/app-config/schema.d.ts +1 -0
  43. package/dist/app-config/workspace.d.ts +1 -0
  44. package/dist/app-config/workspace.js +8 -0
  45. package/dist/chat-threads/schema.d.ts +2 -2
  46. package/dist/client/analytics.d.ts +1 -0
  47. package/dist/client/analytics.js +19 -5
  48. package/dist/client/org/TeamPage.js +15 -2
  49. package/dist/client/org/hooks.d.ts +2 -0
  50. package/dist/client/org/hooks.js +12 -0
  51. package/dist/client/org/index.d.ts +2 -2
  52. package/dist/client/org/index.js +1 -1
  53. package/dist/client/sharing/ShareButton.js +43 -18
  54. package/dist/client/sharing/useShareButtonController.d.ts +12 -2
  55. package/dist/client/sharing/useShareButtonController.js +38 -8
  56. package/dist/collab/awareness.d.ts +2 -2
  57. package/dist/collab/routes.d.ts +1 -1
  58. package/dist/collab/storage.d.ts +2 -0
  59. package/dist/collab/storage.js +7 -0
  60. package/dist/config.js +11 -1
  61. package/dist/dashboard-storage/schema.d.ts +2 -2
  62. package/dist/data-programs/schema.d.ts +2 -2
  63. package/dist/deploy/build.d.ts +1 -1
  64. package/dist/deploy/build.js +89 -12
  65. package/dist/extensions/schema.d.ts +2 -2
  66. package/dist/extensions/store.js +3 -0
  67. package/dist/localization/core-messages/ar-SA.js +1 -0
  68. package/dist/localization/core-messages/de-DE.js +1 -0
  69. package/dist/localization/core-messages/en-US.d.ts +1 -0
  70. package/dist/localization/core-messages/en-US.js +1 -0
  71. package/dist/localization/core-messages/es-ES.js +1 -0
  72. package/dist/localization/core-messages/fr-FR.js +1 -0
  73. package/dist/localization/core-messages/hi-IN.js +1 -0
  74. package/dist/localization/core-messages/ja-JP.js +1 -0
  75. package/dist/localization/core-messages/ko-KR.js +1 -0
  76. package/dist/localization/core-messages/pt-BR.js +1 -0
  77. package/dist/localization/core-messages/zh-CN.js +1 -0
  78. package/dist/localization/core-messages/zh-TW.js +1 -0
  79. package/dist/localization/core-messages.d.ts +1 -0
  80. package/dist/localization/default-messages.d.ts +4 -0
  81. package/dist/localization/default-messages.js +4 -0
  82. package/dist/observability/routes.d.ts +1 -1
  83. package/dist/observability/tracking-identity.js +2 -0
  84. package/dist/org/handlers.d.ts +6 -1
  85. package/dist/org/handlers.js +43 -0
  86. package/dist/org/index.d.ts +3 -3
  87. package/dist/org/index.js +2 -2
  88. package/dist/org/migrations.js +33 -0
  89. package/dist/org/plugin.js +10 -1
  90. package/dist/org/schema.d.ts +315 -0
  91. package/dist/org/schema.js +20 -1
  92. package/dist/org/types.d.ts +3 -0
  93. package/dist/org/workspace-app-access.d.ts +10 -0
  94. package/dist/org/workspace-app-access.js +179 -0
  95. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  96. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  97. package/dist/secrets/routes.d.ts +9 -9
  98. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  99. package/dist/server/auth.js +14 -1
  100. package/dist/server/collab-plugin.d.ts +6 -0
  101. package/dist/server/collab-plugin.js +27 -6
  102. package/dist/server/deploy-environment.d.ts +1 -1
  103. package/dist/server/deploy-environment.js +40 -2
  104. package/dist/server/poll.js +6 -0
  105. package/dist/server/realtime-token.d.ts +1 -1
  106. package/dist/server/sentry-config.js +8 -4
  107. package/dist/server/sentry.js +4 -0
  108. package/dist/sharing/access.d.ts +2 -2
  109. package/dist/sharing/access.js +56 -3
  110. package/dist/sharing/actions/list-resource-shares.d.ts +2 -0
  111. package/dist/sharing/actions/list-resource-shares.js +26 -1
  112. package/dist/sharing/actions/share-resource.d.ts +1 -1
  113. package/dist/sharing/actions/share-resource.js +23 -4
  114. package/dist/sharing/actions/unshare-resource.d.ts +1 -1
  115. package/dist/sharing/actions/unshare-resource.js +1 -1
  116. package/dist/sharing/registry.d.ts +12 -0
  117. package/dist/sharing/schema.d.ts +4 -4
  118. package/dist/sharing/schema.js +2 -2
  119. package/dist/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
  120. package/dist/templates/default/.agents/skills/sharing/SKILL.md +27 -2
  121. package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +27 -2
  122. package/dist/tracking/registry.js +8 -6
  123. package/dist/vite/client.js +5 -0
  124. package/dist/workspace-connections/actions/list-workspace-user-groups.d.ts +9 -1
  125. package/dist/workspace-connections/actions/list-workspace-user-groups.js +9 -4
  126. package/dist/workspace-connections/groups.d.ts +2 -0
  127. package/dist/workspace-connections/groups.js +16 -5
  128. package/docs/content/deployment.mdx +19 -5
  129. package/package.json +1 -1
  130. package/src/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
  131. package/src/templates/default/.agents/skills/sharing/SKILL.md +27 -2
  132. package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +27 -2
@@ -42,7 +42,7 @@ export declare function ownableColumns(): {
42
42
  * Columns:
43
43
  * - `id` — share row id (generated by the share action)
44
44
  * - `resourceId` — id of the parent resource
45
- * - `principalType` — `'user' | 'org'`
45
+ * - `principalType` — `'user' | 'group' | 'org'`
46
46
  * - `principalId` — email (for user) or org id (for org)
47
47
  * - `role` — `'viewer' | 'commenter' | 'editor' | 'admin'`
48
48
  * - `createdBy` — email of the user who created the share
@@ -100,14 +100,14 @@ export declare function createSharesTable(tableName: string): import("drizzle-or
100
100
  tableName: string;
101
101
  dataType: "string";
102
102
  columnType: "SQLiteText";
103
- data: "org" | "user";
103
+ data: "group" | "org" | "user";
104
104
  driverParam: string;
105
105
  notNull: true;
106
106
  hasDefault: false;
107
107
  isPrimaryKey: false;
108
108
  isAutoincrement: false;
109
109
  hasRuntimeDefault: false;
110
- enumValues: ["user", "org"];
110
+ enumValues: ["user", "group", "org"];
111
111
  baseColumn: never;
112
112
  identity: undefined;
113
113
  generated: undefined;
@@ -195,7 +195,7 @@ export declare function createSharesTable(tableName: string): import("drizzle-or
195
195
  }>;
196
196
  export type Visibility = "private" | "org" | "public";
197
197
  export type ShareRole = "viewer" | "commenter" | "editor" | "admin";
198
- export type PrincipalType = "user" | "org";
198
+ export type PrincipalType = "user" | "group" | "org";
199
199
  /** Role precedence — higher number = stronger capability. */
200
200
  export declare const ROLE_RANK: Record<ShareRole | "owner", number>;
201
201
  export declare function roleSatisfies(actual: ShareRole | "owner", minimum: ShareRole | "owner"): boolean;
@@ -49,7 +49,7 @@ export function ownableColumns() {
49
49
  * Columns:
50
50
  * - `id` — share row id (generated by the share action)
51
51
  * - `resourceId` — id of the parent resource
52
- * - `principalType` — `'user' | 'org'`
52
+ * - `principalType` — `'user' | 'group' | 'org'`
53
53
  * - `principalId` — email (for user) or org id (for org)
54
54
  * - `role` — `'viewer' | 'commenter' | 'editor' | 'admin'`
55
55
  * - `createdBy` — email of the user who created the share
@@ -65,7 +65,7 @@ export function createSharesTable(tableName) {
65
65
  id: text("id").primaryKey(),
66
66
  resourceId: text("resource_id").notNull(),
67
67
  principalType: text("principal_type", {
68
- enum: ["user", "org"],
68
+ enum: ["user", "group", "org"],
69
69
  }).notNull(),
70
70
  principalId: text("principal_id").notNull(),
71
71
  role: text("role", {
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
18
18
 
19
19
  This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
20
20
 
21
+ Workspace apps are the intentional organization-scoped exception: their default
22
+ visibility is `org`, configurable by an organization owner/admin. The creator is
23
+ stored as the app owner, and the creator or an organization owner/admin can
24
+ change the app's visibility or manage its share grants.
25
+
21
26
  ## Concepts
22
27
 
23
28
  ### Three visibility levels
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
26
31
  - **`org`** — owner + explicit grants + anyone in the same org (read-only).
27
32
  - **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
28
33
 
29
- Visibility is coarse. Explicit share grants are fine-grained (per user or per org).
34
+ Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
35
+ organization group, or per org).
30
36
 
31
37
  ### Roles on a share grant
32
38
 
@@ -101,6 +107,7 @@ registerShareableResource({
101
107
  // ...
102
108
  allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
103
109
  requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
110
+ supportsGroupShares: true, // enable reusable organization groups for this resource
104
111
  });
105
112
  ```
106
113
 
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
111
118
 
112
119
  Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
113
120
 
121
+ ### Reusable organization groups
122
+
123
+ The workspace organization model stores reusable groups in
124
+ `workspace_user_groups`. Organization owners/admins manage the groups and their
125
+ members through the existing group actions. Set `supportsGroupShares: true` on a
126
+ resource registration to enable `principalType: "group"` in the generic share
127
+ actions and the shared popover's autocomplete. Group ids are always checked
128
+ against the resource's organization, and membership is evaluated at access time
129
+ so group changes apply to existing shares.
130
+
131
+ Workspace apps opt into this flag and use the organization default visibility
132
+ setting. The app creator is the durable `owner_email`, while organization
133
+ owners/admins receive admin access through the registration's
134
+ `canManageAccess` callback.
135
+
114
136
  ## Filter list/read queries
115
137
 
116
138
  ```ts
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
197
219
  - Add `ShareAgentsSection` only when the resource has a real agent-readable
198
220
  link or prompt. Keep it collapsed by default and supply domain-specific
199
221
  content through the shared section shell.
222
+ - When `supportsGroupShares` is enabled, keep the existing people input as the
223
+ only entry point and include organization groups in its autocomplete. Do not
224
+ add a separate group-management surface to each resource.
200
225
  - App-specific tabs or controls may remain when they represent a real domain
201
226
  action (for example, an embed-code editor), but they should retain the same
202
227
  trigger, copy-row, access, and spacing language.
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
211
236
 
212
237
  | Action | Args | Purpose |
213
238
  | -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
214
- | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
239
+ | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
215
240
  | `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
216
241
  | `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
217
242
  | `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
18
18
 
19
19
  This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
20
20
 
21
+ Workspace apps are the intentional organization-scoped exception: their default
22
+ visibility is `org`, configurable by an organization owner/admin. The creator is
23
+ stored as the app owner, and the creator or an organization owner/admin can
24
+ change the app's visibility or manage its share grants.
25
+
21
26
  ## Concepts
22
27
 
23
28
  ### Three visibility levels
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
26
31
  - **`org`** — owner + explicit grants + anyone in the same org (read-only).
27
32
  - **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
28
33
 
29
- Visibility is coarse. Explicit share grants are fine-grained (per user or per org).
34
+ Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
35
+ organization group, or per org).
30
36
 
31
37
  ### Roles on a share grant
32
38
 
@@ -101,6 +107,7 @@ registerShareableResource({
101
107
  // ...
102
108
  allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
103
109
  requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
110
+ supportsGroupShares: true, // enable reusable organization groups for this resource
104
111
  });
105
112
  ```
106
113
 
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
111
118
 
112
119
  Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
113
120
 
121
+ ### Reusable organization groups
122
+
123
+ The workspace organization model stores reusable groups in
124
+ `workspace_user_groups`. Organization owners/admins manage the groups and their
125
+ members through the existing group actions. Set `supportsGroupShares: true` on a
126
+ resource registration to enable `principalType: "group"` in the generic share
127
+ actions and the shared popover's autocomplete. Group ids are always checked
128
+ against the resource's organization, and membership is evaluated at access time
129
+ so group changes apply to existing shares.
130
+
131
+ Workspace apps opt into this flag and use the organization default visibility
132
+ setting. The app creator is the durable `owner_email`, while organization
133
+ owners/admins receive admin access through the registration's
134
+ `canManageAccess` callback.
135
+
114
136
  ## Filter list/read queries
115
137
 
116
138
  ```ts
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
197
219
  - Add `ShareAgentsSection` only when the resource has a real agent-readable
198
220
  link or prompt. Keep it collapsed by default and supply domain-specific
199
221
  content through the shared section shell.
222
+ - When `supportsGroupShares` is enabled, keep the existing people input as the
223
+ only entry point and include organization groups in its autocomplete. Do not
224
+ add a separate group-management surface to each resource.
200
225
  - App-specific tabs or controls may remain when they represent a real domain
201
226
  action (for example, an embed-code editor), but they should retain the same
202
227
  trigger, copy-row, access, and spacing language.
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
211
236
 
212
237
  | Action | Args | Purpose |
213
238
  | -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
214
- | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
239
+ | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
215
240
  | `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
216
241
  | `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
217
242
  | `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
18
18
 
19
19
  This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
20
20
 
21
+ Workspace apps are the intentional organization-scoped exception: their default
22
+ visibility is `org`, configurable by an organization owner/admin. The creator is
23
+ stored as the app owner, and the creator or an organization owner/admin can
24
+ change the app's visibility or manage its share grants.
25
+
21
26
  ## Concepts
22
27
 
23
28
  ### Three visibility levels
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
26
31
  - **`org`** — owner + explicit grants + anyone in the same org (read-only).
27
32
  - **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
28
33
 
29
- Visibility is coarse. Explicit share grants are fine-grained (per user or per org).
34
+ Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
35
+ organization group, or per org).
30
36
 
31
37
  ### Roles on a share grant
32
38
 
@@ -101,6 +107,7 @@ registerShareableResource({
101
107
  // ...
102
108
  allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
103
109
  requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
110
+ supportsGroupShares: true, // enable reusable organization groups for this resource
104
111
  });
105
112
  ```
106
113
 
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
111
118
 
112
119
  Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
113
120
 
121
+ ### Reusable organization groups
122
+
123
+ The workspace organization model stores reusable groups in
124
+ `workspace_user_groups`. Organization owners/admins manage the groups and their
125
+ members through the existing group actions. Set `supportsGroupShares: true` on a
126
+ resource registration to enable `principalType: "group"` in the generic share
127
+ actions and the shared popover's autocomplete. Group ids are always checked
128
+ against the resource's organization, and membership is evaluated at access time
129
+ so group changes apply to existing shares.
130
+
131
+ Workspace apps opt into this flag and use the organization default visibility
132
+ setting. The app creator is the durable `owner_email`, while organization
133
+ owners/admins receive admin access through the registration's
134
+ `canManageAccess` callback.
135
+
114
136
  ## Filter list/read queries
115
137
 
116
138
  ```ts
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
197
219
  - Add `ShareAgentsSection` only when the resource has a real agent-readable
198
220
  link or prompt. Keep it collapsed by default and supply domain-specific
199
221
  content through the shared section shell.
222
+ - When `supportsGroupShares` is enabled, keep the existing people input as the
223
+ only entry point and include organization groups in its autocomplete. Do not
224
+ add a separate group-management surface to each resource.
200
225
  - App-specific tabs or controls may remain when they represent a real domain
201
226
  action (for example, an embed-code editor), but they should retain the same
202
227
  trigger, copy-row, access, and spacing language.
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
211
236
 
212
237
  | Action | Args | Purpose |
213
238
  | -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
214
- | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
239
+ | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
215
240
  | `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
216
241
  | `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
217
242
  | `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
@@ -1,3 +1,4 @@
1
+ import { resolveDeployEnvironment } from "../server/deploy-environment.js";
1
2
  import { getRequestContext } from "../server/request-context.js";
2
3
  import { ANALYTICS_CLIENT_PLATFORM_PROPERTY } from "../shared/analytics-platform.js";
3
4
  const REGISTRY_KEY = Symbol.for("@agent-native/core/tracking.registry");
@@ -46,12 +47,13 @@ function resolveTrackingSource(source) {
46
47
  export function track(name, properties, source) {
47
48
  const { userId, anonymousId, sessionId } = resolveTrackingSource(source);
48
49
  const clientPlatform = getRequestContext()?.clientPlatform;
49
- const trackedProperties = clientPlatform
50
- ? {
51
- ...(properties ?? {}),
52
- [ANALYTICS_CLIENT_PLATFORM_PROPERTY]: clientPlatform,
53
- }
54
- : properties;
50
+ const trackedProperties = {
51
+ ...(properties ?? {}),
52
+ deployment_environment: resolveDeployEnvironment(),
53
+ ...(clientPlatform
54
+ ? { [ANALYTICS_CLIENT_PLATFORM_PROPERTY]: clientPlatform }
55
+ : {}),
56
+ };
55
57
  const event = {
56
58
  name,
57
59
  properties: trackedProperties,
@@ -2913,6 +2913,11 @@ function createAgentNativeConfig(options = {}, command, userConfig = {}, mode =
2913
2913
  // netlify.toml environment is available to the build but not injected
2914
2914
  // into deployed Functions, so embed the decision in the server bundle.
2915
2915
  "process.env.AGENT_NATIVE_RELEASE_MIGRATIONS": JSON.stringify(process.env.AGENT_NATIVE_RELEASE_MIGRATIONS?.trim() || ""),
2916
+ ...(resolvedAppConfig.deployment?.environment
2917
+ ? {
2918
+ "process.env.AGENT_NATIVE_DEPLOYMENT_ENVIRONMENT": JSON.stringify(resolvedAppConfig.deployment.environment),
2919
+ }
2920
+ : {}),
2916
2921
  __AGENT_NATIVE_BUILD_GTM_CONTAINER_ID__: JSON.stringify(process.env.GTM_CONTAINER_ID?.trim() || ""),
2917
2922
  "process.env.AGENT_NATIVE_BUILD_GTM_CONTAINER_ID": JSON.stringify(process.env.GTM_CONTAINER_ID?.trim() || ""),
2918
2923
  // Framework route warmup controls how SSR `.data` routes are fetched:
@@ -1,2 +1,10 @@
1
- declare const _default: import("../../action.js").ActionDefinition<Record<string, never>, import("../groups.js").WorkspaceUserGroup[]>;
1
+ declare const _default: import("../../action.js").ActionDefinition<Record<string, never>, {
2
+ id: string;
3
+ orgId: string;
4
+ name: string;
5
+ createdByEmail: string;
6
+ createdAt: string;
7
+ updatedAt: string;
8
+ memberEmails: any[];
9
+ }[]>;
2
10
  export default _default;
@@ -1,13 +1,18 @@
1
1
  import { z } from "zod";
2
2
  import { defineAction } from "../../action.js";
3
- import { assertWorkspaceUserGroupManager, listWorkspaceUserGroupsForOrg, } from "../groups.js";
3
+ import { listWorkspaceUserGroupsForOrg, workspaceUserGroupRole, } from "../groups.js";
4
4
  export default defineAction({
5
- description: "List reusable workspace user groups. Group management is restricted to workspace owners and admins.",
5
+ description: "List reusable workspace user groups. Workspace members can see group names for sharing; only owners and admins receive member details and can manage groups.",
6
6
  schema: z.object({}),
7
7
  http: { method: "GET" },
8
8
  readOnly: true,
9
9
  run: async (_args, ctx) => {
10
- await assertWorkspaceUserGroupManager(ctx?.orgId, ctx?.userEmail);
11
- return listWorkspaceUserGroupsForOrg(ctx?.orgId ?? "");
10
+ const role = await workspaceUserGroupRole(ctx?.orgId, ctx?.userEmail);
11
+ if (!role)
12
+ throw new Error("Workspace membership is required.");
13
+ const groups = await listWorkspaceUserGroupsForOrg(ctx?.orgId ?? "");
14
+ if (role === "owner" || role === "admin")
15
+ return groups;
16
+ return groups.map((group) => ({ ...group, memberEmails: [] }));
12
17
  },
13
18
  });
@@ -20,12 +20,14 @@ export interface UpdateWorkspaceUserGroupMembersInput {
20
20
  operation: "add" | "remove";
21
21
  orgId?: string | null;
22
22
  }
23
+ export declare function workspaceUserGroupsTable(): string;
23
24
  export declare function normalizeWorkspaceUserGroupIds(value: unknown): string[];
24
25
  export declare function ensureWorkspaceUserGroupsTable(): Promise<void>;
25
26
  export declare function listWorkspaceUserGroupsForOrg(orgId: string, groupIds?: string[]): Promise<WorkspaceUserGroup[]>;
26
27
  export declare function listWorkspaceUserGroups(): Promise<WorkspaceUserGroup[]>;
27
28
  export declare function assertWorkspaceUserGroupIds(groupIds: string[] | undefined, orgId: string | null | undefined): Promise<string[] | undefined>;
28
29
  export declare function assertWorkspaceUserGroupManager(orgId: string | null | undefined, userEmail: string | undefined): Promise<void>;
30
+ export declare function workspaceUserGroupRole(orgId: string | null | undefined, userEmail: string | undefined): Promise<"owner" | "admin" | "member" | null>;
29
31
  export declare function upsertWorkspaceUserGroup(input: UpsertWorkspaceUserGroupInput): Promise<WorkspaceUserGroup>;
30
32
  export declare function updateWorkspaceUserGroupMembers(input: UpdateWorkspaceUserGroupMembersInput): Promise<WorkspaceUserGroup>;
31
33
  export declare function deleteWorkspaceUserGroup(id: string, orgId?: string | null): Promise<boolean>;
@@ -3,7 +3,7 @@ import { getDbExec, intType, isPostgres, retryOnDdlRace, safeJsonParse, } from "
3
3
  import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
4
4
  import { isOrgMember } from "../org/membership.js";
5
5
  import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
6
- function workspaceUserGroupsTable() {
6
+ export function workspaceUserGroupsTable() {
7
7
  return isPostgres()
8
8
  ? "public.workspace_user_groups"
9
9
  : "workspace_user_groups";
@@ -171,19 +171,25 @@ export async function assertWorkspaceUserGroupIds(groupIds, orgId) {
171
171
  return normalized;
172
172
  }
173
173
  export async function assertWorkspaceUserGroupManager(orgId, userEmail) {
174
+ const role = await workspaceUserGroupRole(orgId, userEmail);
175
+ if (role === "owner" || role === "admin")
176
+ return;
177
+ throw new Error("Only workspace admins can manage user groups.");
178
+ }
179
+ export async function workspaceUserGroupRole(orgId, userEmail) {
174
180
  const normalizedOrgId = orgId?.trim();
175
181
  const normalizedEmail = userEmail?.trim().toLowerCase();
176
182
  if (!normalizedOrgId || !normalizedEmail) {
177
- throw new Error("Only workspace admins can manage user groups.");
183
+ return null;
178
184
  }
179
185
  const { rows } = await getDbExec().execute({
180
186
  sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,
181
187
  args: [normalizedOrgId, normalizedEmail],
182
188
  });
183
189
  const role = String(rows[0]?.role ?? "");
184
- if (role !== "owner" && role !== "admin") {
185
- throw new Error("Only workspace admins can manage user groups.");
186
- }
190
+ return role === "owner" || role === "admin" || role === "member"
191
+ ? role
192
+ : null;
187
193
  }
188
194
  export async function upsertWorkspaceUserGroup(input) {
189
195
  const requestScope = requireWorkspaceUserGroupScope();
@@ -283,6 +289,11 @@ export async function workspaceUserGroupsIncludeUser(orgId, groupIds, userEmail)
283
289
  if (!normalizedOrgId || !normalizedEmail || normalizedIds.length === 0) {
284
290
  return false;
285
291
  }
292
+ // Group rows can outlive roster membership. A stale email in the JSON
293
+ // member list must not keep granting access after that person leaves the
294
+ // organization.
295
+ if (!(await isOrgMember(normalizedOrgId, normalizedEmail)))
296
+ return false;
286
297
  const groups = await listWorkspaceUserGroupsForOrg(normalizedOrgId, normalizedIds);
287
298
  return groups.some((group) => group.memberEmails.includes(normalizedEmail));
288
299
  }
@@ -26,11 +26,11 @@ pnpm install
26
26
 
27
27
  The install step creates the lockfile that the production Dockerfile below copies into its build stage.
28
28
 
29
- ### 2. Add a local Compose file
29
+ ### 2. Add the local Docker files
30
30
 
31
- Create `docker-compose.yml` in the app root. It uses the [Dockerfile](#docker) in the next section and starts Postgres only on the private Compose network:
31
+ Create `Dockerfile`, `.dockerignore`, and `docker-compose.yml` in the app root. `docker compose up --build` needs the Dockerfile, and `.dockerignore` keeps host-only files such as `node_modules` out of the Linux build context. Compose starts Postgres only on the private Compose network.
32
32
 
33
- You can also download the [Dockerfile](https://www.agent-native.com/examples/self-hosted-chat/Dockerfile), [Compose file](https://www.agent-native.com/examples/self-hosted-chat/docker-compose.yml), and [environment template](https://www.agent-native.com/examples/self-hosted-chat/env.example) as a starter fixture.
33
+ You can also download the [Dockerfile](https://www.agent-native.com/examples/self-hosted-chat/Dockerfile), [.dockerignore](https://www.agent-native.com/examples/self-hosted-chat/.dockerignore), [Compose file](https://www.agent-native.com/examples/self-hosted-chat/docker-compose.yml), and [environment template](https://www.agent-native.com/examples/self-hosted-chat/env.example) as a starter fixture. The [Dockerfile](#docker) below matches the fixture.
34
34
 
35
35
  ```yaml filename="docker-compose.yml" maxLines=40
36
36
  services:
@@ -53,7 +53,7 @@ services:
53
53
  POSTGRES_USER: agent_native
54
54
  POSTGRES_HOST_AUTH_METHOD: trust
55
55
  volumes:
56
- - postgres-data:/var/lib/postgresql
56
+ - postgres-data-v18:/var/lib/postgresql
57
57
  healthcheck:
58
58
  test: ["CMD-SHELL", "pg_isready -U agent_native -d agent_native"]
59
59
  interval: 5s
@@ -61,9 +61,11 @@ services:
61
61
  retries: 10
62
62
 
63
63
  volumes:
64
- postgres-data:
64
+ postgres-data-v18:
65
65
  ```
66
66
 
67
+ PostgreSQL 18 uses `/var/lib/postgresql` as its volume mount path, unlike PostgreSQL 17 and earlier. The versioned volume name keeps this fresh fixture separate from older local stacks. If you are upgrading an existing PostgreSQL 17 or earlier stack, back up and migrate its data before changing the image or mount path. Do not point this fixture at the old volume without a migration.
68
+
67
69
  ### 3. Run it
68
70
 
69
71
  ```bash
@@ -322,6 +324,18 @@ EXPOSE 3000
322
324
  CMD ["node", ".output/server/index.mjs"]
323
325
  ```
324
326
 
327
+ Create `.dockerignore` alongside the Dockerfile:
328
+
329
+ ```text filename=".dockerignore"
330
+ node_modules
331
+ .output
332
+ data
333
+ .env
334
+ .env.*
335
+ !.env.example
336
+ .git
337
+ ```
338
+
325
339
  ### Self-hosted workspace with Docker Compose {#workspace-docker-compose}
326
340
 
327
341
  The Dockerfile above is for one standalone app. A workspace is not one Nitro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.162.0",
3
+ "version": "0.163.1",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
18
18
 
19
19
  This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
20
20
 
21
+ Workspace apps are the intentional organization-scoped exception: their default
22
+ visibility is `org`, configurable by an organization owner/admin. The creator is
23
+ stored as the app owner, and the creator or an organization owner/admin can
24
+ change the app's visibility or manage its share grants.
25
+
21
26
  ## Concepts
22
27
 
23
28
  ### Three visibility levels
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
26
31
  - **`org`** — owner + explicit grants + anyone in the same org (read-only).
27
32
  - **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
28
33
 
29
- Visibility is coarse. Explicit share grants are fine-grained (per user or per org).
34
+ Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
35
+ organization group, or per org).
30
36
 
31
37
  ### Roles on a share grant
32
38
 
@@ -101,6 +107,7 @@ registerShareableResource({
101
107
  // ...
102
108
  allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
103
109
  requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
110
+ supportsGroupShares: true, // enable reusable organization groups for this resource
104
111
  });
105
112
  ```
106
113
 
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
111
118
 
112
119
  Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
113
120
 
121
+ ### Reusable organization groups
122
+
123
+ The workspace organization model stores reusable groups in
124
+ `workspace_user_groups`. Organization owners/admins manage the groups and their
125
+ members through the existing group actions. Set `supportsGroupShares: true` on a
126
+ resource registration to enable `principalType: "group"` in the generic share
127
+ actions and the shared popover's autocomplete. Group ids are always checked
128
+ against the resource's organization, and membership is evaluated at access time
129
+ so group changes apply to existing shares.
130
+
131
+ Workspace apps opt into this flag and use the organization default visibility
132
+ setting. The app creator is the durable `owner_email`, while organization
133
+ owners/admins receive admin access through the registration's
134
+ `canManageAccess` callback.
135
+
114
136
  ## Filter list/read queries
115
137
 
116
138
  ```ts
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
197
219
  - Add `ShareAgentsSection` only when the resource has a real agent-readable
198
220
  link or prompt. Keep it collapsed by default and supply domain-specific
199
221
  content through the shared section shell.
222
+ - When `supportsGroupShares` is enabled, keep the existing people input as the
223
+ only entry point and include organization groups in its autocomplete. Do not
224
+ add a separate group-management surface to each resource.
200
225
  - App-specific tabs or controls may remain when they represent a real domain
201
226
  action (for example, an embed-code editor), but they should retain the same
202
227
  trigger, copy-row, access, and spacing language.
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
211
236
 
212
237
  | Action | Args | Purpose |
213
238
  | -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
214
- | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
239
+ | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
215
240
  | `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
216
241
  | `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
217
242
  | `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
18
18
 
19
19
  This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
20
20
 
21
+ Workspace apps are the intentional organization-scoped exception: their default
22
+ visibility is `org`, configurable by an organization owner/admin. The creator is
23
+ stored as the app owner, and the creator or an organization owner/admin can
24
+ change the app's visibility or manage its share grants.
25
+
21
26
  ## Concepts
22
27
 
23
28
  ### Three visibility levels
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
26
31
  - **`org`** — owner + explicit grants + anyone in the same org (read-only).
27
32
  - **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
28
33
 
29
- Visibility is coarse. Explicit share grants are fine-grained (per user or per org).
34
+ Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
35
+ organization group, or per org).
30
36
 
31
37
  ### Roles on a share grant
32
38
 
@@ -101,6 +107,7 @@ registerShareableResource({
101
107
  // ...
102
108
  allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
103
109
  requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
110
+ supportsGroupShares: true, // enable reusable organization groups for this resource
104
111
  });
105
112
  ```
106
113
 
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
111
118
 
112
119
  Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
113
120
 
121
+ ### Reusable organization groups
122
+
123
+ The workspace organization model stores reusable groups in
124
+ `workspace_user_groups`. Organization owners/admins manage the groups and their
125
+ members through the existing group actions. Set `supportsGroupShares: true` on a
126
+ resource registration to enable `principalType: "group"` in the generic share
127
+ actions and the shared popover's autocomplete. Group ids are always checked
128
+ against the resource's organization, and membership is evaluated at access time
129
+ so group changes apply to existing shares.
130
+
131
+ Workspace apps opt into this flag and use the organization default visibility
132
+ setting. The app creator is the durable `owner_email`, while organization
133
+ owners/admins receive admin access through the registration's
134
+ `canManageAccess` callback.
135
+
114
136
  ## Filter list/read queries
115
137
 
116
138
  ```ts
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
197
219
  - Add `ShareAgentsSection` only when the resource has a real agent-readable
198
220
  link or prompt. Keep it collapsed by default and supply domain-specific
199
221
  content through the shared section shell.
222
+ - When `supportsGroupShares` is enabled, keep the existing people input as the
223
+ only entry point and include organization groups in its autocomplete. Do not
224
+ add a separate group-management surface to each resource.
200
225
  - App-specific tabs or controls may remain when they represent a real domain
201
226
  action (for example, an embed-code editor), but they should retain the same
202
227
  trigger, copy-row, access, and spacing language.
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
211
236
 
212
237
  | Action | Args | Purpose |
213
238
  | -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
214
- | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
239
+ | `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
215
240
  | `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
216
241
  | `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
217
242
  | `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |