@agent-native/core 0.154.3 → 0.154.5

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 (64) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/content/.agents/skills/content/SKILL.md +2 -2
  3. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +20 -4
  4. package/corpus/templates/content/actions/_blocks-field-identity.ts +464 -0
  5. package/corpus/templates/content/actions/_database-row-mutation.ts +1490 -0
  6. package/corpus/templates/content/actions/_database-utils.ts +55 -3
  7. package/corpus/templates/content/actions/_property-utils.ts +141 -31
  8. package/corpus/templates/content/actions/add-database-item.ts +62 -240
  9. package/corpus/templates/content/actions/configure-document-property.ts +170 -1
  10. package/corpus/templates/content/actions/delete-document-property.ts +56 -17
  11. package/corpus/templates/content/actions/delete-document.ts +3 -0
  12. package/corpus/templates/content/actions/edit-document.ts +18 -1
  13. package/corpus/templates/content/actions/export-document.ts +41 -0
  14. package/corpus/templates/content/actions/remove-database-items.ts +10 -0
  15. package/corpus/templates/content/actions/set-document-property.ts +140 -3
  16. package/corpus/templates/content/actions/update-database-item.ts +62 -0
  17. package/corpus/templates/content/actions/update-document.ts +24 -0
  18. package/corpus/templates/content/actions/upsert-database-item-by-key.ts +43 -796
  19. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +99 -11
  20. package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +6 -0
  21. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +10 -8
  22. package/corpus/templates/content/app/hooks/use-content-database.ts +33 -32
  23. package/corpus/templates/content/app/hooks/use-document-properties.ts +4 -1
  24. package/corpus/templates/content/app/i18n-data.ts +20 -0
  25. package/corpus/templates/content/changelog/2026-08-10-database-blocks-fields-now-keep-logical-block-identity-throu.md +6 -0
  26. package/corpus/templates/content/changelog/2026-08-10-database-row-actions-now-validate-exact-schemas-and-safe-ret.md +6 -0
  27. package/corpus/templates/content/changelog/2026-08-13-blocks-field-conflicts-now-keep-the-newer-saved-version-visi.md +6 -0
  28. package/corpus/templates/content/docs/product/capabilities/content.object.block.md +5 -4
  29. package/corpus/templates/content/docs/product/capabilities/content.object.blocks-field.md +9 -4
  30. package/corpus/templates/content/docs/product/encyclopedia.md +4 -4
  31. package/corpus/templates/content/parity/matrix.md +1 -1
  32. package/corpus/templates/content/parity/matrix.ts +2 -0
  33. package/corpus/templates/content/server/db/schema.ts +100 -3
  34. package/corpus/templates/content/server/plugins/db.ts +85 -0
  35. package/corpus/templates/content/shared/api.ts +78 -2
  36. package/corpus/templates/content/shared/blocks-field-identity.ts +554 -0
  37. package/corpus/templates/content/shared/document-export.ts +33 -1
  38. package/dist/action.d.ts +22 -0
  39. package/dist/action.js +27 -0
  40. package/dist/collab/awareness.d.ts +2 -2
  41. package/dist/collab/routes.d.ts +1 -1
  42. package/dist/extensions/url-safety.d.ts +7 -2
  43. package/dist/extensions/url-safety.js +27 -12
  44. package/dist/index.browser.d.ts +1 -1
  45. package/dist/index.browser.js +1 -1
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +1 -1
  48. package/dist/mcp-client/oauth-client.d.ts +16 -1
  49. package/dist/mcp-client/oauth-client.js +177 -83
  50. package/dist/mcp-client/remote-store.js +46 -20
  51. package/dist/notifications/routes.d.ts +1 -1
  52. package/dist/oauth-tokens/index.d.ts +2 -1
  53. package/dist/oauth-tokens/index.js +2 -1
  54. package/dist/oauth-tokens/lifecycle.d.ts +87 -0
  55. package/dist/oauth-tokens/lifecycle.js +434 -0
  56. package/dist/oauth-tokens/migrations.js +10 -0
  57. package/dist/oauth-tokens/store.d.ts +22 -0
  58. package/dist/oauth-tokens/store.js +141 -18
  59. package/dist/observability/routes.d.ts +2 -2
  60. package/dist/progress/routes.d.ts +1 -1
  61. package/dist/secrets/routes.d.ts +6 -6
  62. package/dist/server/action-routes.js +10 -2
  63. package/dist/server/realtime-token.d.ts +1 -1
  64. package/package.json +2 -1
@@ -1,4 +1,26 @@
1
1
  export declare function getOAuthTokens(provider: string, accountId: string, owner?: string): Promise<Record<string, unknown> | null>;
2
+ export interface OAuthTokenSnapshot {
3
+ tokens: Record<string, unknown>;
4
+ owner: string | null;
5
+ revision: number;
6
+ legacyRevision: number;
7
+ storageVersion: string;
8
+ }
9
+ /**
10
+ * Read one credential bundle together with the row revision used by
11
+ * refresh/revocation compare-and-swap writes.
12
+ */
13
+ export declare function getOAuthTokenSnapshot(provider: string, accountId: string, owner: string): Promise<OAuthTokenSnapshot | null>;
14
+ /** Read a legacy user-owned row without treating organization ids as case-insensitive. */
15
+ export declare function getOAuthTokenSnapshotForUserOwner(provider: string, accountId: string, owner: string): Promise<OAuthTokenSnapshot | null>;
16
+ /**
17
+ * Replace an existing credential bundle only when the caller still owns the
18
+ * revision it read. This prevents a slow refresh/revoke flow from overwriting
19
+ * a newer authorization completed in another process.
20
+ */
21
+ export declare function replaceOAuthTokensIfRevision(provider: string, accountId: string, owner: string, expectedRevision: number, expectedLegacyRevision: number, expectedStorageVersion: string, tokens: Record<string, unknown>): Promise<boolean>;
22
+ /** Delete only the exact credential revision the caller inspected. */
23
+ export declare function deleteOAuthTokensIfRevision(provider: string, accountId: string, owner: string, expectedRevision: number, expectedLegacyRevision: number, expectedStorageVersion: string): Promise<boolean>;
2
24
  /**
3
25
  * Thrown when an OAuth save would re-bind an `(provider, account_id)` row
4
26
  * to a different owner than already holds it. Callers should catch this and
@@ -42,6 +42,15 @@ function parseStoredTokens(stored) {
42
42
  function oauthTokensTable() {
43
43
  return isPostgres() ? "public.oauth_tokens" : "oauth_tokens";
44
44
  }
45
+ function isDuplicateColumnError(err) {
46
+ const code = String(err?.code ?? "");
47
+ const message = String(err?.message ?? err)
48
+ .toLowerCase()
49
+ .trim();
50
+ return (code === "42701" ||
51
+ message.includes("duplicate column") ||
52
+ message.includes("already exists"));
53
+ }
45
54
  async function ensureTable() {
46
55
  if (!_initPromise) {
47
56
  _initPromise = (async () => {
@@ -54,6 +63,7 @@ async function ensureTable() {
54
63
  owner TEXT,
55
64
  tokens TEXT NOT NULL,
56
65
  updated_at ${intType()} NOT NULL,
66
+ revision ${intType()} NOT NULL,
57
67
  PRIMARY KEY (provider, account_id)
58
68
  )
59
69
  `;
@@ -76,13 +86,11 @@ async function ensureTable() {
76
86
  await ensureColumnExists("oauth_tokens", "owner", `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS owner TEXT`);
77
87
  // Migration: add display_name column
78
88
  await ensureColumnExists("oauth_tokens", "display_name", `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS display_name TEXT`);
89
+ await ensureColumnExists("oauth_tokens", "revision", `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS revision BIGINT`);
79
90
  // Backfill: set owner = account_id for existing rows without an owner
80
91
  await client.execute(`UPDATE ${table} SET owner = account_id WHERE owner IS NULL`);
81
- // Older deployments have a 32-bit `updated_at`; on Postgres the
82
- // `Date.now()` written on every token save overflows int4. Widen in place
83
- // (no-op once done / on fresh DBs). Unqualified name — the helper scopes
84
- // to the `public` schema.
85
- await widenIntColumnsToBigInt("oauth_tokens", ["updated_at"]);
92
+ await client.execute(`UPDATE ${table} SET revision = updated_at WHERE revision IS NULL`);
93
+ await widenIntColumnsToBigInt("oauth_tokens", ["updated_at"], client);
86
94
  return;
87
95
  }
88
96
  // SQLite (local dev): no lock problem — keep the original behaviour.
@@ -91,23 +99,28 @@ async function ensureTable() {
91
99
  try {
92
100
  await client.execute(`ALTER TABLE ${table} ADD COLUMN owner TEXT`);
93
101
  }
94
- catch {
95
- // Column already exists
102
+ catch (err) {
103
+ if (!isDuplicateColumnError(err))
104
+ throw err;
96
105
  }
97
106
  // Migration: add display_name column
98
107
  try {
99
108
  await client.execute(`ALTER TABLE ${table} ADD COLUMN display_name TEXT`);
100
109
  }
101
- catch {
102
- // Column already exists
110
+ catch (err) {
111
+ if (!isDuplicateColumnError(err))
112
+ throw err;
113
+ }
114
+ try {
115
+ await client.execute(`ALTER TABLE ${table} ADD COLUMN revision INTEGER`);
116
+ }
117
+ catch (err) {
118
+ if (!isDuplicateColumnError(err))
119
+ throw err;
103
120
  }
104
121
  // Backfill: set owner = account_id for existing rows without an owner
105
122
  await client.execute(`UPDATE ${table} SET owner = account_id WHERE owner IS NULL`);
106
- // Older deployments have a 32-bit `updated_at`; on Postgres the
107
- // `Date.now()` written on every token save overflows int4. Widen in place
108
- // (no-op once done / on fresh DBs). Unqualified name — the helper scopes
109
- // to the `public` schema.
110
- await widenIntColumnsToBigInt("oauth_tokens", ["updated_at"]);
123
+ await client.execute(`UPDATE ${table} SET revision = updated_at WHERE revision IS NULL`);
111
124
  })().catch((err) => {
112
125
  // Retry init on the next call after a failed startup.
113
126
  _initPromise = undefined;
@@ -130,6 +143,91 @@ export async function getOAuthTokens(provider, accountId, owner) {
130
143
  return null;
131
144
  return parseStoredTokens(rows[0].tokens);
132
145
  }
146
+ /**
147
+ * Read one credential bundle together with the row revision used by
148
+ * refresh/revocation compare-and-swap writes.
149
+ */
150
+ export async function getOAuthTokenSnapshot(provider, accountId, owner) {
151
+ await ensureTable();
152
+ const client = getDbExec();
153
+ const table = oauthTokensTable();
154
+ const { rows } = await client.execute({
155
+ sql: `SELECT owner, tokens, revision, updated_at FROM ${table} WHERE provider = ? AND account_id = ? AND owner = ?`,
156
+ args: [provider, accountId, owner],
157
+ });
158
+ if (rows.length === 0)
159
+ return null;
160
+ return {
161
+ tokens: parseStoredTokens(rows[0].tokens),
162
+ owner: rows[0].owner ?? null,
163
+ revision: Number(rows[0].revision ?? 0),
164
+ legacyRevision: Number(rows[0].updated_at),
165
+ storageVersion: rows[0].tokens,
166
+ };
167
+ }
168
+ /** Read a legacy user-owned row without treating organization ids as case-insensitive. */
169
+ export async function getOAuthTokenSnapshotForUserOwner(provider, accountId, owner) {
170
+ await ensureTable();
171
+ const client = getDbExec();
172
+ const table = oauthTokensTable();
173
+ const { rows } = await client.execute({
174
+ sql: `SELECT owner, tokens, revision, updated_at FROM ${table} WHERE provider = ? AND account_id = ? AND LOWER(owner) = LOWER(?) AND LOWER(owner) LIKE 'user:%'`,
175
+ args: [provider, accountId, owner],
176
+ });
177
+ if (rows.length === 0)
178
+ return null;
179
+ return {
180
+ tokens: parseStoredTokens(rows[0].tokens),
181
+ owner: rows[0].owner ?? null,
182
+ revision: Number(rows[0].revision ?? 0),
183
+ legacyRevision: Number(rows[0].updated_at),
184
+ storageVersion: rows[0].tokens,
185
+ };
186
+ }
187
+ /**
188
+ * Replace an existing credential bundle only when the caller still owns the
189
+ * revision it read. This prevents a slow refresh/revoke flow from overwriting
190
+ * a newer authorization completed in another process.
191
+ */
192
+ export async function replaceOAuthTokensIfRevision(provider, accountId, owner, expectedRevision, expectedLegacyRevision, expectedStorageVersion, tokens) {
193
+ await ensureTable();
194
+ const client = getDbExec();
195
+ const table = oauthTokensTable();
196
+ const nextRevision = Math.max(Date.now(), expectedRevision + 1);
197
+ const result = await client.execute({
198
+ sql: `UPDATE ${table} SET tokens = ?, revision = ?, updated_at = ? WHERE provider = ? AND account_id = ? AND owner = ? AND COALESCE(revision, 0) = ? AND updated_at = ? AND tokens = ?`,
199
+ args: [
200
+ serializeTokens(tokens),
201
+ nextRevision,
202
+ Math.floor(Date.now() / 1_000),
203
+ provider,
204
+ accountId,
205
+ owner,
206
+ expectedRevision,
207
+ expectedLegacyRevision,
208
+ expectedStorageVersion,
209
+ ],
210
+ });
211
+ return result.rowsAffected === 1;
212
+ }
213
+ /** Delete only the exact credential revision the caller inspected. */
214
+ export async function deleteOAuthTokensIfRevision(provider, accountId, owner, expectedRevision, expectedLegacyRevision, expectedStorageVersion) {
215
+ await ensureTable();
216
+ const client = getDbExec();
217
+ const table = oauthTokensTable();
218
+ const result = await client.execute({
219
+ sql: `DELETE FROM ${table} WHERE provider = ? AND account_id = ? AND owner = ? AND COALESCE(revision, 0) = ? AND updated_at = ? AND tokens = ?`,
220
+ args: [
221
+ provider,
222
+ accountId,
223
+ owner,
224
+ expectedRevision,
225
+ expectedLegacyRevision,
226
+ expectedStorageVersion,
227
+ ],
228
+ });
229
+ return result.rowsAffected === 1;
230
+ }
133
231
  /**
134
232
  * Thrown when an OAuth save would re-bind an `(provider, account_id)` row
135
233
  * to a different owner than already holds it. Callers should catch this and
@@ -154,6 +252,12 @@ export class OAuthAccountOwnedByOtherUserError extends Error {
154
252
  this.attemptedOwner = opts.attemptedOwner;
155
253
  }
156
254
  }
255
+ function ownersRepresentSameUser(existingOwner, attemptedOwner) {
256
+ return (existingOwner === attemptedOwner ||
257
+ (existingOwner.startsWith("user:") &&
258
+ attemptedOwner.startsWith("user:") &&
259
+ existingOwner.toLowerCase() === attemptedOwner.toLowerCase()));
260
+ }
157
261
  /**
158
262
  * Save OAuth tokens. The `owner` parameter specifies which user owns this
159
263
  * account — defaults to `accountId` (the account itself is the owner).
@@ -195,7 +299,9 @@ export async function saveOAuthTokens(provider, accountId, tokens, owner) {
195
299
  if (existingOwner)
196
300
  resolvedOwner = existingOwner;
197
301
  }
198
- else if (existingOwner && owner && existingOwner !== owner) {
302
+ else if (existingOwner &&
303
+ owner &&
304
+ !ownersRepresentSameUser(existingOwner, owner)) {
199
305
  // Refuse to silently re-bind an account from one user to another.
200
306
  // This is the case the docstring promised but the previous
201
307
  // implementation didn't enforce — `ON CONFLICT DO UPDATE SET
@@ -212,19 +318,36 @@ export async function saveOAuthTokens(provider, accountId, tokens, owner) {
212
318
  ...(existingTokens ?? {}),
213
319
  ...cleanedIncomingTokens,
214
320
  };
215
- await client.execute({
321
+ const result = await client.execute({
216
322
  sql: isPostgres()
217
- ? `INSERT INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=EXCLUDED.owner, display_name=COALESCE(EXCLUDED.display_name, ${table}.display_name), tokens=EXCLUDED.tokens, updated_at=EXCLUDED.updated_at`
218
- : `INSERT OR REPLACE INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,
323
+ ? `INSERT INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at, revision) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=EXCLUDED.owner, display_name=COALESCE(EXCLUDED.display_name, ${table}.display_name), tokens=EXCLUDED.tokens, updated_at=EXCLUDED.updated_at, revision=GREATEST(COALESCE(${table}.revision, 0) + 1, EXCLUDED.revision) WHERE ${table}.owner = EXCLUDED.owner OR (LOWER(${table}.owner) = LOWER(EXCLUDED.owner) AND LOWER(${table}.owner) LIKE 'user:%' AND LOWER(EXCLUDED.owner) LIKE 'user:%')`
324
+ : `INSERT INTO ${table} (provider, account_id, owner, display_name, tokens, updated_at, revision) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (provider, account_id) DO UPDATE SET owner=excluded.owner, display_name=COALESCE(excluded.display_name, ${table}.display_name), tokens=excluded.tokens, updated_at=excluded.updated_at, revision=MAX(COALESCE(${table}.revision, 0) + 1, excluded.revision) WHERE ${table}.owner = excluded.owner OR (LOWER(${table}.owner) = LOWER(excluded.owner) AND LOWER(${table}.owner) LIKE 'user:%' AND LOWER(excluded.owner) LIKE 'user:%')`,
219
325
  args: [
220
326
  provider,
221
327
  accountId,
222
328
  resolvedOwner,
223
329
  existingDisplayName,
224
330
  serializeTokens(tokensToStore),
331
+ Math.floor(Date.now() / 1_000),
225
332
  Date.now(),
226
333
  ],
227
334
  });
335
+ if (result.rowsAffected === 1)
336
+ return;
337
+ const { rows: conflict } = await client.execute({
338
+ sql: `SELECT owner FROM ${table} WHERE provider = ? AND account_id = ?`,
339
+ args: [provider, accountId],
340
+ });
341
+ const conflictOwner = conflict[0]?.owner ?? "";
342
+ if (conflictOwner && !ownersRepresentSameUser(conflictOwner, resolvedOwner)) {
343
+ throw new OAuthAccountOwnedByOtherUserError({
344
+ provider,
345
+ accountId,
346
+ existingOwner: conflictOwner,
347
+ attemptedOwner: resolvedOwner,
348
+ });
349
+ }
350
+ throw new Error(`OAuth account ${provider}:${accountId} was not saved.`);
228
351
  }
229
352
  export async function deleteOAuthTokens(provider, accountId, owner) {
230
353
  await ensureTable();
@@ -41,14 +41,14 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
44
  ok?: undefined;
45
+ error?: undefined;
46
46
  summary: import("./types.js").TraceSummary;
47
47
  spans: import("./types.js").TraceSpan[];
48
48
  id?: undefined;
49
49
  } | {
50
- error?: undefined;
51
50
  ok?: undefined;
51
+ error?: undefined;
52
52
  summary?: undefined;
53
53
  spans?: undefined;
54
54
  id: string;
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- ok: boolean;
19
18
  error?: undefined;
19
+ ok: boolean;
20
20
  }>>;
@@ -37,17 +37,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
37
37
  ok?: undefined;
38
38
  status?: undefined;
39
39
  } | {
40
- error?: undefined;
41
40
  ok: boolean;
42
41
  status: string;
42
+ error?: undefined;
43
43
  } | {
44
44
  ok?: undefined;
45
45
  error: string;
46
46
  removed?: undefined;
47
47
  } | {
48
- error?: undefined;
49
48
  ok: boolean;
50
49
  removed: boolean;
50
+ error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
@@ -58,13 +58,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
58
58
  error: string;
59
59
  note?: undefined;
60
60
  } | {
61
- error?: undefined;
62
61
  ok: boolean;
63
62
  note?: undefined;
64
- } | {
65
63
  error?: undefined;
64
+ } | {
66
65
  ok: boolean;
67
66
  note: string;
67
+ error?: undefined;
68
68
  } | {
69
69
  note?: undefined;
70
70
  ok: boolean;
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
95
95
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
96
96
  error: string;
97
97
  } | {
98
- error?: undefined;
99
98
  ok: boolean;
100
99
  key: string;
101
- } | {
102
100
  error?: undefined;
101
+ } | {
103
102
  ok: boolean;
104
103
  removed: boolean;
104
+ error?: undefined;
105
105
  }>>;
@@ -1,6 +1,6 @@
1
1
  import { createError, defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getQuery, getHeader, getRequestURL, } from "h3";
2
2
  import { verifyA2ATokenWithClaims } from "../a2a-claims.js";
3
- import { isAgentActionStopError } from "../action.js";
3
+ import { isActionContractError, isAgentActionStopError } from "../action.js";
4
4
  import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
5
5
  import { resolveOrgByDomain, resolveOrgIdForEmail } from "../org/context.js";
6
6
  import { readBody } from "../server/h3-helpers.js";
@@ -507,7 +507,15 @@ export function mountActionRoutes(nitroApp, actions, options) {
507
507
  isAgentActionStopError(err) ||
508
508
  (explicitStatus !== undefined && explicitStatus < 500);
509
509
  if (isUserFacing) {
510
- return { error: msg };
510
+ return isActionContractError(err)
511
+ ? {
512
+ error: msg,
513
+ errorCode: err.errorCode,
514
+ ...(err.details === undefined
515
+ ? {}
516
+ : { details: err.details }),
517
+ }
518
+ : { error: msg };
511
519
  }
512
520
  const requestId = getHttpRequestTelemetryId(event);
513
521
  const captureId = captureError(err, {
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
26
26
  expiresAt?: undefined;
27
27
  ttlSeconds?: undefined;
28
28
  } | {
29
+ error?: undefined;
29
30
  token: string;
30
31
  expiresAt: string;
31
32
  ttlSeconds: number;
32
- error?: undefined;
33
33
  }>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.154.3",
3
+ "version": "0.154.5",
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": {
@@ -418,6 +418,7 @@
418
418
  "turndown": "7.2.4",
419
419
  "tw-animate-css": "1.4.0",
420
420
  "tweetnacl": "^1.0.3",
421
+ "undici": "^7.28.0",
421
422
  "y-protocols": "^1.0.7",
422
423
  "yjs": "^13.6.31",
423
424
  "zod": "^4.3.6",