@agent-native/core 0.201.1 → 0.202.0-nightly-20261006033736

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 (98) hide show
  1. package/dist/automation/index.d.ts +1 -1
  2. package/dist/client/analytics/index.d.ts +1 -1
  3. package/dist/client/analytics/index.js +1 -1
  4. package/dist/client/analytics.d.ts +23 -3
  5. package/dist/client/analytics.js +189 -46
  6. package/dist/client/client-status-requests.d.ts +4 -1
  7. package/dist/client/client-status-requests.js +21 -5
  8. package/dist/client/embed-auth.d.ts +6 -0
  9. package/dist/client/embed-auth.js +13 -0
  10. package/dist/client/session-replay.js +48 -22
  11. package/dist/client/sharing/index.d.ts +2 -0
  12. package/dist/client/sharing/index.js +2 -0
  13. package/dist/client/sharing/useAccessRequestReview.d.ts +80 -0
  14. package/dist/client/sharing/useAccessRequestReview.js +100 -0
  15. package/dist/client/sharing/useResourceAccessGate.d.ts +57 -0
  16. package/dist/client/sharing/useResourceAccessGate.js +76 -0
  17. package/dist/client/use-agent-engine-configured.d.ts +2 -0
  18. package/dist/client/use-agent-engine-configured.js +4 -1
  19. package/dist/email-catalog/registry.d.ts +1 -1
  20. package/dist/email-catalog/registry.js +1 -1
  21. package/dist/email-catalog/system-emails.d.ts +2 -2
  22. package/dist/email-catalog/system-emails.js +47 -2
  23. package/dist/email-catalog/templates.d.ts +5 -1
  24. package/dist/email-catalog/templates.js +11 -1
  25. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  26. package/dist/framework-tools.js +6 -0
  27. package/dist/identity/rekey.js +9 -0
  28. package/dist/jobs/run-history.d.ts +27 -8
  29. package/dist/jobs/run-history.js +17 -10
  30. package/dist/jobs/scheduler.js +39 -10
  31. package/dist/mcp/build-server.d.ts +1 -0
  32. package/dist/mcp/build-server.js +30 -7
  33. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  34. package/dist/notifications/routes.d.ts +2 -2
  35. package/dist/observability/routes.d.ts +7 -7
  36. package/dist/org/index.d.ts +1 -1
  37. package/dist/progress/routes.d.ts +1 -1
  38. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  39. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  40. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  41. package/dist/review/suggestions/actions.js +14 -35
  42. package/dist/review/suggestions/store.d.ts +0 -1
  43. package/dist/review/suggestions/store.js +0 -7
  44. package/dist/secrets/routes.d.ts +11 -11
  45. package/dist/server/action-discovery.js +24 -0
  46. package/dist/server/agent-access.js +7 -3
  47. package/dist/server/agent-chat/mcp-options.d.ts +1 -0
  48. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  49. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  50. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  51. package/dist/server/attribution.d.ts +33 -1
  52. package/dist/server/attribution.js +93 -21
  53. package/dist/server/auth.js +35 -11
  54. package/dist/server/email-templates.d.ts +34 -0
  55. package/dist/server/email-templates.js +40 -0
  56. package/dist/server/magic-link-attribution.js +2 -1
  57. package/dist/server/onboarding-html.js +35 -67
  58. package/dist/server/release-schema.js +4 -0
  59. package/dist/server/short-lived-token.d.ts +7 -0
  60. package/dist/server/short-lived-token.js +59 -0
  61. package/dist/shared/analytics-anonymous-id.d.ts +1 -0
  62. package/dist/shared/analytics-anonymous-id.js +1 -1
  63. package/dist/shared/attribution-source.d.ts +30 -0
  64. package/dist/shared/attribution-source.js +65 -0
  65. package/dist/sharing/access-request-store.d.ts +96 -0
  66. package/dist/sharing/access-request-store.js +213 -0
  67. package/dist/sharing/access-requests.d.ts +113 -0
  68. package/dist/sharing/access-requests.js +490 -0
  69. package/dist/sharing/access.d.ts +36 -2
  70. package/dist/sharing/access.js +74 -12
  71. package/dist/sharing/actions/approve-resource-access-request.d.ts +10 -0
  72. package/dist/sharing/actions/approve-resource-access-request.js +15 -0
  73. package/dist/sharing/actions/decline-resource-access-request.d.ts +7 -0
  74. package/dist/sharing/actions/decline-resource-access-request.js +14 -0
  75. package/dist/sharing/actions/get-resource-access-request.d.ts +4 -0
  76. package/dist/sharing/actions/get-resource-access-request.js +13 -0
  77. package/dist/sharing/actions/get-resource-access-status.d.ts +5 -0
  78. package/dist/sharing/actions/get-resource-access-status.js +19 -0
  79. package/dist/sharing/actions/list-resource-access-requests.d.ts +5 -0
  80. package/dist/sharing/actions/list-resource-access-requests.js +16 -0
  81. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  82. package/dist/sharing/actions/request-resource-access.d.ts +6 -0
  83. package/dist/sharing/actions/request-resource-access.js +17 -0
  84. package/dist/sharing/actions/share-resource.d.ts +1 -1
  85. package/dist/sharing/actions/share-resource.js +18 -144
  86. package/dist/sharing/grant.d.ts +49 -0
  87. package/dist/sharing/grant.js +195 -0
  88. package/dist/sharing/index.d.ts +2 -1
  89. package/dist/sharing/index.js +2 -1
  90. package/dist/sharing/registry.d.ts +36 -0
  91. package/dist/templates/chat/server/plugins/auth.ts +2 -2
  92. package/dist/templates/default/app/routes/access-requests.$requestId.tsx +7 -0
  93. package/dist/templates/default/server/plugins/auth.ts +2 -2
  94. package/dist/vite/action-types-plugin.js +24 -0
  95. package/package.json +11 -3
  96. package/src/templates/chat/server/plugins/auth.ts +2 -2
  97. package/src/templates/default/app/routes/access-requests.$requestId.tsx +7 -0
  98. package/src/templates/default/server/plugins/auth.ts +2 -2
@@ -0,0 +1,213 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { getDbExec } from "../db/client.js";
3
+ import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
4
+ const TABLE = "resource_access_requests";
5
+ let _initPromise;
6
+ export async function ensureTable() {
7
+ if (!_initPromise) {
8
+ _initPromise = (async () => {
9
+ await ensureTableExists(TABLE, `CREATE TABLE IF NOT EXISTS ${TABLE} (
10
+ id TEXT PRIMARY KEY,
11
+ resource_type TEXT NOT NULL,
12
+ resource_id TEXT NOT NULL,
13
+ requester_email TEXT NOT NULL,
14
+ requester_name TEXT,
15
+ owner_email TEXT NOT NULL,
16
+ note TEXT,
17
+ state TEXT NOT NULL,
18
+ generation INTEGER NOT NULL DEFAULT 1,
19
+ requested_at BIGINT NOT NULL,
20
+ created_at BIGINT NOT NULL,
21
+ decided_by TEXT,
22
+ decided_at BIGINT,
23
+ granted_role TEXT,
24
+ delivery TEXT
25
+ )`);
26
+ await ensureIndexExists("idx_resource_access_requests_requester", `CREATE UNIQUE INDEX IF NOT EXISTS idx_resource_access_requests_requester ON ${TABLE} (resource_type, resource_id, requester_email)`);
27
+ await ensureIndexExists("idx_resource_access_requests_requester_recent", `CREATE INDEX IF NOT EXISTS idx_resource_access_requests_requester_recent ON ${TABLE} (requester_email, requested_at)`);
28
+ await ensureIndexExists("idx_resource_access_requests_owner_recent", `CREATE INDEX IF NOT EXISTS idx_resource_access_requests_owner_recent ON ${TABLE} (owner_email, requested_at)`);
29
+ })().catch((err) => {
30
+ _initPromise = undefined;
31
+ throw err;
32
+ });
33
+ }
34
+ return _initPromise;
35
+ }
36
+ function optionalString(value) {
37
+ return value == null ? null : String(value);
38
+ }
39
+ function optionalNumber(value) {
40
+ return value == null ? null : Number(value);
41
+ }
42
+ function parseRow(row) {
43
+ return {
44
+ id: String(row.id),
45
+ resourceType: String(row.resource_type),
46
+ resourceId: String(row.resource_id),
47
+ requesterEmail: String(row.requester_email),
48
+ requesterName: optionalString(row.requester_name),
49
+ ownerEmail: String(row.owner_email),
50
+ note: optionalString(row.note),
51
+ state: String(row.state),
52
+ generation: Number(row.generation),
53
+ requestedAt: Number(row.requested_at),
54
+ createdAt: Number(row.created_at),
55
+ decidedBy: optionalString(row.decided_by),
56
+ decidedAt: optionalNumber(row.decided_at),
57
+ grantedRole: optionalString(row.granted_role),
58
+ delivered: row.delivery != null,
59
+ };
60
+ }
61
+ function newRequestId() {
62
+ return randomBytes(12).toString("base64url");
63
+ }
64
+ export async function getAccessRequest(id) {
65
+ await ensureTable();
66
+ const { rows } = await getDbExec().execute({
67
+ sql: `SELECT * FROM ${TABLE} WHERE id = ? LIMIT 1`,
68
+ args: [id],
69
+ });
70
+ return rows[0] ? parseRow(rows[0]) : null;
71
+ }
72
+ export async function findAccessRequest(resourceType, resourceId, requesterEmail) {
73
+ await ensureTable();
74
+ const { rows } = await getDbExec().execute({
75
+ sql: `SELECT * FROM ${TABLE} WHERE resource_type = ? AND resource_id = ? AND requester_email = ? LIMIT 1`,
76
+ args: [resourceType, resourceId, requesterEmail],
77
+ });
78
+ return rows[0] ? parseRow(rows[0]) : null;
79
+ }
80
+ export async function listPendingAccessRequests(resourceType, resourceId, limit) {
81
+ await ensureTable();
82
+ const { rows } = await getDbExec().execute({
83
+ sql: `SELECT * FROM ${TABLE} WHERE resource_type = ? AND resource_id = ? AND state = 'pending' ORDER BY requested_at DESC LIMIT ?`,
84
+ args: [resourceType, resourceId, limit],
85
+ });
86
+ return rows.map(parseRow);
87
+ }
88
+ /**
89
+ * How many requests were asked since `since` by one requester or against one
90
+ * owner, and when the oldest of them was asked.
91
+ */
92
+ export async function countRecentAccessRequests(by, since) {
93
+ await ensureTable();
94
+ const [column, value] = "requesterEmail" in by
95
+ ? ["requester_email", by.requesterEmail]
96
+ : ["owner_email", by.ownerEmail];
97
+ const { rows } = await getDbExec().execute({
98
+ sql: `SELECT COUNT(*) AS n, MIN(requested_at) AS oldest FROM ${TABLE} WHERE ${column} = ? AND requested_at > ?`,
99
+ args: [value, since],
100
+ });
101
+ return {
102
+ count: Number(rows[0]?.n ?? 0),
103
+ oldestAt: optionalNumber(rows[0]?.oldest),
104
+ };
105
+ }
106
+ /**
107
+ * Opens a request, or reopens a closed one as a new generation. A pending
108
+ * request whose send was cut off before anyone was told is claimed again in
109
+ * its own generation, so recipients the cut-off send reached get nothing new.
110
+ * Returns `opened: false` with the existing row when a request is already
111
+ * pending, or was declined too recently to ask again, so the caller sends
112
+ * nothing.
113
+ */
114
+ export async function openAccessRequest(input) {
115
+ await ensureTable();
116
+ const { rows } = await getDbExec().execute({
117
+ sql: `INSERT INTO ${TABLE} (
118
+ id, resource_type, resource_id, requester_email, requester_name,
119
+ owner_email, note, state, generation, requested_at, created_at
120
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', 1, ?, ?)
121
+ ON CONFLICT (resource_type, resource_id, requester_email) DO UPDATE SET
122
+ requester_name = excluded.requester_name,
123
+ owner_email = excluded.owner_email,
124
+ note = excluded.note,
125
+ state = 'pending',
126
+ generation = ${TABLE}.generation + 1,
127
+ requested_at = excluded.requested_at,
128
+ decided_by = NULL,
129
+ decided_at = NULL,
130
+ granted_role = NULL,
131
+ delivery = NULL
132
+ WHERE ${TABLE}.state = 'approved'
133
+ OR (${TABLE}.state = 'declined' AND ${TABLE}.decided_at <= ?)
134
+ RETURNING *`,
135
+ args: [
136
+ newRequestId(),
137
+ input.resourceType,
138
+ input.resourceId,
139
+ input.requesterEmail,
140
+ input.requesterName,
141
+ input.ownerEmail,
142
+ input.note,
143
+ input.now,
144
+ input.now,
145
+ input.declinedCooldownStart,
146
+ ],
147
+ });
148
+ if (rows[0])
149
+ return { request: parseRow(rows[0]), opened: true };
150
+ // Moving requested_at makes the claim one ask's alone.
151
+ const { rows: unsent } = await getDbExec().execute({
152
+ sql: `UPDATE ${TABLE} SET requested_at = ?
153
+ WHERE resource_type = ? AND resource_id = ? AND requester_email = ?
154
+ AND state = 'pending' AND delivery IS NULL AND requested_at <= ?
155
+ RETURNING *`,
156
+ args: [
157
+ input.now,
158
+ input.resourceType,
159
+ input.resourceId,
160
+ input.requesterEmail,
161
+ input.sendWindowStart,
162
+ ],
163
+ });
164
+ if (unsent[0])
165
+ return { request: parseRow(unsent[0]), opened: true };
166
+ const existing = await findAccessRequest(input.resourceType, input.resourceId, input.requesterEmail);
167
+ if (!existing)
168
+ throw new Error("Access request conflict could not be read.");
169
+ return { request: existing, opened: false };
170
+ }
171
+ /**
172
+ * Withdraws a request that was just opened but reached nobody, so asking
173
+ * again isn't blocked and doesn't count toward the limits. Only the ask that
174
+ * opened or claimed it at `requestedAt` can: one another ask has claimed
175
+ * since, or already delivered, stays.
176
+ */
177
+ export async function deleteAccessRequest(id, generation, requestedAt) {
178
+ await ensureTable();
179
+ await getDbExec().execute({
180
+ sql: `DELETE FROM ${TABLE} WHERE id = ? AND generation = ? AND requested_at = ? AND state = 'pending' AND delivery IS NULL`,
181
+ args: [id, generation, requestedAt],
182
+ });
183
+ }
184
+ export async function recordAccessRequestDelivery(id, generation, delivery) {
185
+ await ensureTable();
186
+ await getDbExec().execute({
187
+ sql: `UPDATE ${TABLE} SET delivery = ? WHERE id = ? AND generation = ?`,
188
+ args: [JSON.stringify(delivery), id, generation],
189
+ });
190
+ }
191
+ /**
192
+ * Closes a pending request. Only a request still pending in the generation
193
+ * the decider saw changes, so a decision on a stale page can't close a newer
194
+ * ask and two people can't both decide one. Pass the transaction a grant runs
195
+ * in so both commit together.
196
+ */
197
+ export async function decideAccessRequest(input, exec = getDbExec()) {
198
+ await ensureTable();
199
+ const { rows } = await exec.execute({
200
+ sql: `UPDATE ${TABLE} SET state = ?, decided_by = ?, decided_at = ?, granted_role = ?
201
+ WHERE id = ? AND generation = ? AND state = 'pending'
202
+ RETURNING *`,
203
+ args: [
204
+ input.state,
205
+ input.decidedBy,
206
+ input.now,
207
+ input.grantedRole,
208
+ input.id,
209
+ input.generation,
210
+ ],
211
+ });
212
+ return rows[0] ? parseRow(rows[0]) : null;
213
+ }
@@ -0,0 +1,113 @@
1
+ import { type AccessRequestRow } from "./access-request-store.js";
2
+ import { type ResourceAccessStatus } from "./access.js";
3
+ import type { ShareRole } from "./schema.js";
4
+ export declare const ACCESS_REQUEST_NOTE_MAX_LENGTH = 500;
5
+ /** How long a declined requester waits before they can ask again. */
6
+ export declare const ACCESS_REQUEST_DECLINE_COOLDOWN_MS: number;
7
+ /** Requests one person can make in a day, across every resource. */
8
+ export declare const ACCESS_REQUESTS_PER_REQUESTER_PER_DAY = 20;
9
+ /** Requests one owner can receive in a day, across their resources. */
10
+ export declare const ACCESS_REQUESTS_PER_OWNER_PER_DAY = 50;
11
+ /**
12
+ * How long a request nobody has been told about yet reads as still sending.
13
+ * After that its send was cut off, and asking again sends it again; the
14
+ * request's idempotency keys stop anyone it already reached hearing twice.
15
+ */
16
+ export declare const ACCESS_REQUEST_SEND_WINDOW_MS: number;
17
+ /** Where someone who manages access reviews a request, relative to the app. */
18
+ export declare function accessRequestReviewPath(requestId: string): string;
19
+ /** The viewer's own request, as their access screen shows it. */
20
+ export interface ViewerAccessRequest {
21
+ state: "pending";
22
+ requestedAt: string;
23
+ }
24
+ export interface ResourceLinkStatus extends ResourceAccessStatus {
25
+ /** Whether the viewer can ask for access now. Only for `denied`. */
26
+ canRequest?: boolean;
27
+ /** The viewer's open request, if any. Only for `denied`. */
28
+ request?: ViewerAccessRequest;
29
+ }
30
+ /**
31
+ * {@link resolveAccessStatus}, plus whether a viewer who can't open the
32
+ * resource can ask for access, and their own open request if they already
33
+ * asked. Registrations without `accessRequests` never offer it.
34
+ */
35
+ export declare function resolveLinkStatus(resourceType: string, resourceId: string): Promise<ResourceLinkStatus>;
36
+ export type RequestResourceAccessResult = {
37
+ state: "allowed";
38
+ } | {
39
+ state: "requested";
40
+ request: ViewerAccessRequest;
41
+ sent: boolean;
42
+ };
43
+ /**
44
+ * Asks the owner, and anyone with admin, to give the signed-in viewer access
45
+ * to a resource they can't open. Asking again while a request is open sends
46
+ * nothing; asking again after its send was cut off sends it again. If the
47
+ * request is over a daily limit, or nobody could be told, it is withdrawn
48
+ * and this fails, so the viewer never sees "sent" for a request nobody will
49
+ * find.
50
+ */
51
+ export declare function requestResourceAccess(input: {
52
+ resourceType: string;
53
+ resourceId: string;
54
+ note?: string;
55
+ }): Promise<RequestResourceAccessResult>;
56
+ export interface AccessRequestReview {
57
+ id: string;
58
+ generation: number;
59
+ state: AccessRequestRow["state"];
60
+ requester: {
61
+ email: string;
62
+ name: string | null;
63
+ };
64
+ note: string | null;
65
+ requestedAt: string;
66
+ decidedAt: string | null;
67
+ grantedRole: ShareRole | null;
68
+ resource: {
69
+ type: string;
70
+ id: string;
71
+ label: string;
72
+ title: string;
73
+ path: string | null;
74
+ };
75
+ }
76
+ /** A request, for someone who manages access to its resource. */
77
+ export declare function getAccessRequestReview(requestId: string): Promise<AccessRequestReview>;
78
+ export interface ResourceAccessRequestList {
79
+ /** The newest pending requests. */
80
+ requests: AccessRequestReview[];
81
+ /** Older pending requests exist beyond `requests`. */
82
+ hasMore: boolean;
83
+ }
84
+ /** Pending requests for one resource, for someone who manages its access. */
85
+ export declare function listResourceAccessRequests(resourceType: string, resourceId: string): Promise<ResourceAccessRequestList>;
86
+ /**
87
+ * Whether the requester was emailed that they're in: `skipped` when the app
88
+ * has no email set up or the requester is a test identity, `failed` when
89
+ * sending failed or the email setup is broken or unreadable.
90
+ */
91
+ export type AccessGrantedEmail = "sent" | "skipped" | "failed";
92
+ /**
93
+ * Gives the requester `role`, through the same grant and sharing rules as
94
+ * Share, and closes the request in the same transaction. A stronger role the
95
+ * requester already holds is kept. The access stands even when the email
96
+ * telling them fails; `email` says so.
97
+ */
98
+ export declare function approveAccessRequest(input: {
99
+ requestId: string;
100
+ generation: number;
101
+ role: ShareRole;
102
+ }): Promise<{
103
+ state: "approved";
104
+ role: ShareRole;
105
+ email: AccessGrantedEmail;
106
+ }>;
107
+ /** Closes a request without granting anything or telling the requester. */
108
+ export declare function declineAccessRequest(input: {
109
+ requestId: string;
110
+ generation: number;
111
+ }): Promise<{
112
+ state: "declined";
113
+ }>;