@agent-native/core 0.0.0-beta-20260820150521 → 0.0.0-beta-20260820155159

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.
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 8484
34
+ - template files: 8485
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-20
4
+ ---
5
+
6
+ Dispatch chat now loads its OpenRouter provider reliably in production.
@@ -19,9 +19,11 @@
19
19
  "@agent-native/toolkit": "workspace:*",
20
20
  "@fontsource-variable/inter": "^5.2.8",
21
21
  "@libsql/client": "^0.15.8",
22
+ "@openrouter/ai-sdk-provider": "^2.9.1",
22
23
  "@radix-ui/react-tooltip": "^1.2.7",
23
24
  "@tabler/icons-react": "catalog:",
24
25
  "@tanstack/react-query": "^5.99.2",
26
+ "ai": "6.0.258",
25
27
  "h3": "catalog:",
26
28
  "isbot": "^5",
27
29
  "next-themes": "^0.4.6",
@@ -12,6 +12,8 @@ NPM_CONFIG_PRODUCTION = "false"
12
12
 
13
13
  [context.production.environment]
14
14
  AGENT_NATIVE_HOSTED_HARNESS = "true"
15
+ AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
+ AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
15
17
  # Macros owns its schema at release time (`migrate:production` above), so
16
18
  # production request functions must not probe the database for migrations.
17
19
  # Preview functions still run request-path migrations because previews do not
@@ -239,6 +239,15 @@ function compareMcpUrl(value) {
239
239
  return value.trim().replace(/\/+$/, "");
240
240
  }
241
241
  }
242
+ function startMcpOAuthReconnect(server) {
243
+ const returnUrl = `${window.location.pathname}${window.location.search}${window.location.hash}`;
244
+ const params = new URLSearchParams({
245
+ serverId: server.id,
246
+ scope: server.scope,
247
+ return: returnUrl,
248
+ });
249
+ window.location.assign(agentNativePath(`/_agent-native/mcp/servers/oauth/start?${params}`));
250
+ }
242
251
  function McpServerStatus({ server, onReconnect, reconnecting = false, reconnectError, }) {
243
252
  const t = useT();
244
253
  if (server.status.state === "connected") {
@@ -362,7 +371,9 @@ function McpIntegrationsSection({ query }) {
362
371
  return (_jsxs("div", { className: "flex items-start gap-3 py-3.5", children: [_jsx("span", { className: "mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-lg border border-border/70 bg-background text-muted-foreground", children: _jsx(IconServer, { className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground", children: server.name }), _jsx("span", { className: "text-[11px] text-muted-foreground", children: server.scope === "user"
363
372
  ? t("mcpIntegrations.personal")
364
373
  : t("mcpIntegrations.sharedWithWorkspace") })] }), _jsx(McpServerStatus, { server: server, onReconnect: server.status.state === "error"
365
- ? () => void reconnect(server)
374
+ ? () => server.authMode === "oauth"
375
+ ? startMcpOAuthReconnect(server)
376
+ : void reconnect(server)
366
377
  : undefined, reconnecting: reconnectingKey === key, reconnectError: reconnectError?.key === key
367
378
  ? reconnectError.message
368
379
  : undefined })] }), canRemove && (_jsx("button", { type: "button", onClick: () => void removeServer(server), disabled: deleteServer.isPending, className: "shrink-0 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent disabled:opacity-50", children: deleteTarget === key ? "Confirm" : "Remove" }))] }, key));
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  states: {
66
67
  clientId: number;
67
68
  state: string;
68
69
  }[];
69
- error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
+ error?: undefined;
80
81
  users: {
81
82
  clientId: number;
82
83
  lastSeen: number;
83
84
  }[];
84
- error?: undefined;
85
85
  }>>;
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
137
137
  inputSchema: {
138
138
  type: string;
139
139
  properties: {
140
- count?: undefined;
141
140
  query?: undefined;
142
141
  minutes?: undefined;
143
142
  limit?: undefined;
144
143
  clientHint?: undefined;
145
144
  timestamp?: undefined;
146
145
  chapterId?: undefined;
146
+ count?: undefined;
147
147
  startAt?: undefined;
148
148
  endAt?: undefined;
149
149
  reason?: undefined;
@@ -159,7 +159,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
159
159
  inputSchema: {
160
160
  type: string;
161
161
  properties: {
162
- count?: undefined;
163
162
  query: {
164
163
  type: string;
165
164
  description: string;
@@ -175,6 +174,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
175
174
  clientHint?: undefined;
176
175
  timestamp?: undefined;
177
176
  chapterId?: undefined;
177
+ count?: undefined;
178
178
  startAt?: undefined;
179
179
  endAt?: undefined;
180
180
  reason?: undefined;
@@ -190,7 +190,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
190
190
  inputSchema: {
191
191
  type: string;
192
192
  properties: {
193
- count?: undefined;
194
193
  minutes: {
195
194
  type: string;
196
195
  description: string;
@@ -200,6 +199,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
200
199
  clientHint?: undefined;
201
200
  timestamp?: undefined;
202
201
  chapterId?: undefined;
202
+ count?: undefined;
203
203
  startAt?: undefined;
204
204
  endAt?: undefined;
205
205
  reason?: undefined;
@@ -216,7 +216,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
216
216
  type: string;
217
217
  required: string[];
218
218
  properties: {
219
- count?: undefined;
220
219
  query: {
221
220
  type: string;
222
221
  description: string;
@@ -235,6 +234,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
235
234
  };
236
235
  timestamp?: undefined;
237
236
  chapterId?: undefined;
237
+ count?: undefined;
238
238
  startAt?: undefined;
239
239
  endAt?: undefined;
240
240
  reason?: undefined;
@@ -250,7 +250,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
250
250
  type: string;
251
251
  required: string[];
252
252
  properties: {
253
- count?: undefined;
254
253
  query?: undefined;
255
254
  minutes?: undefined;
256
255
  limit?: undefined;
@@ -264,6 +263,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
264
263
  description: string;
265
264
  };
266
265
  chapterId?: undefined;
266
+ count?: undefined;
267
267
  startAt?: undefined;
268
268
  endAt?: undefined;
269
269
  includeMicrophone?: undefined;
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
314
314
  type: string;
315
315
  required: string[];
316
316
  properties: {
317
- count?: undefined;
318
317
  query?: undefined;
319
318
  minutes?: undefined;
320
319
  limit?: undefined;
321
320
  clientHint?: undefined;
322
321
  timestamp?: undefined;
323
322
  chapterId?: undefined;
323
+ count?: undefined;
324
324
  startAt: {
325
325
  type: string;
326
326
  description: string;
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
349
349
  type: string;
350
350
  required: string[];
351
351
  properties: {
352
- count?: undefined;
353
352
  query?: undefined;
354
353
  minutes?: undefined;
355
354
  limit?: undefined;
356
355
  clientHint?: undefined;
357
356
  timestamp?: undefined;
358
357
  chapterId?: undefined;
358
+ count?: undefined;
359
359
  startAt?: undefined;
360
360
  endAt?: undefined;
361
361
  reason?: undefined;
@@ -16,6 +16,7 @@ export interface McpOAuthFlow {
16
16
  clientInformation: StoredOAuthClientInformation;
17
17
  discoveryState?: McpOAuthDiscoveryState;
18
18
  returnUrl?: string;
19
+ replaceServerId?: string;
19
20
  expiresAt: number;
20
21
  }
21
22
  export interface McpOAuthRoutesOptions {
@@ -8,7 +8,7 @@ import { getH3App } from "../server/framework-request-handler.js";
8
8
  import { getAppUrl, resolveOAuthRedirectUri } from "../server/google-oauth.js";
9
9
  import { runWithRequestContext } from "../server/request-context.js";
10
10
  import { finishMcpOAuthAuthorization, startMcpOAuthAuthorization, validateMcpOAuthCallbackIssuer, } from "./oauth-client.js";
11
- import { addOAuthRemoteServer, normalizeServerName, validateRemoteUrl, } from "./remote-store.js";
11
+ import { addOAuthRemoteServer, listRemoteServers, normalizeServerName, replaceOAuthRemoteServer, validateRemoteUrl, } from "./remote-store.js";
12
12
  const FLOW_COOKIE = "an_mcp_oauth_flow";
13
13
  const FLOW_TTL_SECONDS = 10 * 60;
14
14
  const FLOW_COOKIE_CHUNK_SIZE = 2_800;
@@ -69,8 +69,33 @@ async function handleMcpOAuthStart(event) {
69
69
  if (!session?.email)
70
70
  return unauthorized(event);
71
71
  const query = getQuery(event);
72
- const rawUrl = text(query.url);
73
- const rawName = text(query.name);
72
+ const reconnectServerId = text(query.serverId);
73
+ const reconnectScope = query.scope === "org" ? "org" : "user";
74
+ const reconnectOrg = reconnectScope === "org" ? await getOrgContext(event) : null;
75
+ const reconnectScopeId = reconnectScope === "user" ? session.email : (reconnectOrg?.orgId ?? "");
76
+ let reconnectServer;
77
+ if (reconnectServerId) {
78
+ if (reconnectScope === "org" &&
79
+ (!reconnectScopeId || !isOrgAdmin(reconnectOrg?.role))) {
80
+ setResponseStatus(event, reconnectScopeId ? 403 : 400);
81
+ return {
82
+ error: reconnectScopeId
83
+ ? "Only organization owners and admins can reconnect an org MCP server."
84
+ : "Join an organization before reconnecting an org MCP server.",
85
+ };
86
+ }
87
+ reconnectServer = (await listRemoteServers(reconnectScope, reconnectScopeId)).find((server) => server.id === reconnectServerId);
88
+ if (!reconnectServer) {
89
+ setResponseStatus(event, 404);
90
+ return { error: "MCP server was not found." };
91
+ }
92
+ if (!reconnectServer.oauthSecretKey) {
93
+ setResponseStatus(event, 400);
94
+ return { error: "This MCP server does not use OAuth credentials." };
95
+ }
96
+ }
97
+ const rawUrl = reconnectServer?.url ?? text(query.url);
98
+ const rawName = reconnectServer?.name ?? text(query.name);
74
99
  const returnUrl = text(query.return);
75
100
  if (!rawUrl || !rawName) {
76
101
  setResponseStatus(event, 400);
@@ -159,6 +184,7 @@ async function handleMcpOAuthStart(event) {
159
184
  ? { discoveryState: started.discoveryState }
160
185
  : {}),
161
186
  ...(safeReturnUrl ? { returnUrl: safeReturnUrl } : {}),
187
+ ...(reconnectServerId ? { replaceServerId: reconnectServerId } : {}),
162
188
  expiresAt: Date.now() + FLOW_TTL_SECONDS * 1_000,
163
189
  };
164
190
  setMcpOAuthFlowCookie(event, flow, redirectUri.startsWith("https://"));
@@ -245,12 +271,14 @@ async function handleMcpOAuthCallback(event, options) {
245
271
  authorizationCode: code,
246
272
  iss,
247
273
  }));
248
- const result = await addOAuthRemoteServer(flow.scope, flow.scopeId, {
249
- name: flow.name,
250
- url: flow.url,
251
- description: flow.description,
252
- credentials: finished.credentials,
253
- });
274
+ const result = flow.replaceServerId
275
+ ? await replaceOAuthRemoteServer(flow.scope, flow.scopeId, flow.replaceServerId, finished.credentials)
276
+ : await addOAuthRemoteServer(flow.scope, flow.scopeId, {
277
+ name: flow.name,
278
+ url: flow.url,
279
+ description: flow.description,
280
+ credentials: finished.credentials,
281
+ });
254
282
  if (!result.ok) {
255
283
  setResponseStatus(event, 400);
256
284
  return { error: result.error };
@@ -107,6 +107,18 @@ export declare function addOAuthRemoteServer(scope: RemoteMcpScope, scopeId: str
107
107
  ok: false;
108
108
  error: string;
109
109
  }>;
110
+ /**
111
+ * Replace the OAuth grant for an existing server without changing its id or
112
+ * display name. Reconnect must update the saved grant in place; registering a
113
+ * second row makes the original connection impossible to repair.
114
+ */
115
+ export declare function replaceOAuthRemoteServer(scope: RemoteMcpScope, scopeId: string, serverId: string, credentials: McpOAuthCredentialBundle): Promise<{
116
+ ok: true;
117
+ server: StoredRemoteMcpServer;
118
+ } | {
119
+ ok: false;
120
+ error: string;
121
+ }>;
110
122
  export declare function addFirstPartyRemoteServer(orgId: string, input: {
111
123
  appId: string;
112
124
  name: string;
@@ -193,6 +193,77 @@ export async function addOAuthRemoteServer(scope, scopeId, input) {
193
193
  };
194
194
  }
195
195
  }
196
+ /**
197
+ * Replace the OAuth grant for an existing server without changing its id or
198
+ * display name. Reconnect must update the saved grant in place; registering a
199
+ * second row makes the original connection impossible to repair.
200
+ */
201
+ export async function replaceOAuthRemoteServer(scope, scopeId, serverId, credentials) {
202
+ const credentialResource = validateRemoteUrl(credentials.serverUrl);
203
+ if (!credentialResource.ok || !credentialResource.url) {
204
+ return {
205
+ ok: false,
206
+ error: "MCP server URL must match the OAuth credential resource URL",
207
+ };
208
+ }
209
+ const existing = await readList(scope, scopeId);
210
+ const index = existing.findIndex((server) => server.id === serverId);
211
+ const current = index >= 0 ? existing[index] : undefined;
212
+ if (!current)
213
+ return { ok: false, error: "MCP server was not found" };
214
+ if (!current.oauthSecretKey) {
215
+ return {
216
+ ok: false,
217
+ error: "This MCP server does not use OAuth credentials",
218
+ };
219
+ }
220
+ if (current.url !== credentialResource.url.toString()) {
221
+ return {
222
+ ok: false,
223
+ error: "MCP server URL must match the saved OAuth connection",
224
+ };
225
+ }
226
+ const nextSecretKey = `mcp_oauth:${shortId()}`;
227
+ const nextCredentials = {
228
+ ...credentials,
229
+ serverUrl: credentialResource.url.toString(),
230
+ };
231
+ try {
232
+ await saveMcpOAuthCredentials({
233
+ key: nextSecretKey,
234
+ scope,
235
+ scopeId,
236
+ credentials: nextCredentials,
237
+ });
238
+ const server = {
239
+ ...current,
240
+ url: nextCredentials.serverUrl,
241
+ oauthSecretKey: nextSecretKey,
242
+ };
243
+ const next = [...existing];
244
+ next[index] = server;
245
+ await writeList(scope, scopeId, next);
246
+ await revokeMcpOAuthCredentials({
247
+ key: current.oauthSecretKey,
248
+ scope,
249
+ scopeId,
250
+ serverUrl: current.url,
251
+ }).catch(() => undefined);
252
+ return { ok: true, server };
253
+ }
254
+ catch (err) {
255
+ await revokeMcpOAuthCredentials({
256
+ key: nextSecretKey,
257
+ scope,
258
+ scopeId,
259
+ serverUrl: nextCredentials.serverUrl,
260
+ }).catch(() => undefined);
261
+ return {
262
+ ok: false,
263
+ error: `Failed to replace MCP OAuth credentials: ${err?.message ?? err}`,
264
+ };
265
+ }
266
+ }
196
267
  export async function addFirstPartyRemoteServer(orgId, input) {
197
268
  const trust = await isFirstPartyRemoteEndpointTrusted(orgId, input.appId, input.url);
198
269
  if (!trust.ok)
@@ -11,23 +11,23 @@
11
11
  * DELETE /_agent-native/notifications/:id — delete
12
12
  */
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
+ error?: undefined;
14
15
  count: number;
15
16
  updated?: undefined;
16
17
  ok?: undefined;
17
- error?: undefined;
18
18
  } | {
19
- count?: undefined;
19
+ error?: undefined;
20
20
  updated: number;
21
21
  ok?: undefined;
22
- error?: undefined;
23
- } | {
24
22
  count?: undefined;
23
+ } | {
25
24
  updated?: undefined;
26
25
  error: string;
27
26
  ok?: undefined;
28
- } | {
29
27
  count?: undefined;
28
+ } | {
29
+ error?: undefined;
30
30
  updated?: undefined;
31
31
  ok: boolean;
32
- error?: undefined;
32
+ count?: undefined;
33
33
  }>>;
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
+ error?: undefined;
44
45
  summary: import("./types.js").TraceSummary;
45
46
  spans: import("./types.js").TraceSpan[];
46
47
  id?: undefined;
47
- error?: undefined;
48
48
  ok?: undefined;
49
49
  } | {
50
+ error?: undefined;
50
51
  summary?: undefined;
51
52
  spans?: undefined;
52
53
  id: string;
53
- error?: undefined;
54
54
  ok?: undefined;
55
55
  } | {
56
56
  summary?: undefined;
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
59
59
  error: any;
60
60
  ok?: undefined;
61
61
  } | {
62
+ error?: undefined;
62
63
  summary?: undefined;
63
64
  spans?: undefined;
64
65
  id?: undefined;
65
- error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
@@ -34,16 +34,16 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
34
34
  /** POST /_agent-native/secrets/:key — write a secret. */
35
35
  export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
36
36
  error: string;
37
- status?: undefined;
38
37
  ok?: undefined;
38
+ status?: undefined;
39
39
  } | {
40
40
  ok: boolean;
41
41
  status: string;
42
42
  error?: undefined;
43
43
  } | {
44
+ ok?: undefined;
44
45
  error: string;
45
46
  removed?: undefined;
46
- ok?: undefined;
47
47
  } | {
48
48
  ok: boolean;
49
49
  removed: boolean;
@@ -54,9 +54,9 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
54
54
  * or the current stored value without changing anything.
55
55
  */
56
56
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
+ ok?: undefined;
57
58
  error: string;
58
59
  note?: undefined;
59
- ok?: undefined;
60
60
  } | {
61
61
  ok: boolean;
62
62
  note?: undefined;
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
27
27
  export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
28
28
  error: any;
29
29
  } | {
30
+ error?: undefined;
30
31
  ok: boolean;
31
32
  key: string;
32
33
  baseUrlKey?: string;
33
34
  scope: AgentEngineApiKeyScope;
34
- error?: undefined;
35
35
  }>>;
36
36
  export {};
@@ -20,6 +20,6 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- text: string;
24
23
  error?: undefined;
24
+ text: string;
25
25
  }>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.0.0-beta-20260820150521",
3
+ "version": "0.0.0-beta-20260820155159",
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": {
@@ -428,7 +428,7 @@
428
428
  "yjs": "^13.6.32",
429
429
  "zod": "^4.3.6",
430
430
  "@agent-native/recap-cli": "0.5.5",
431
- "@agent-native/toolkit": "^0.0.0-beta-20260820150521"
431
+ "@agent-native/toolkit": "^0.16.8"
432
432
  },
433
433
  "devDependencies": {
434
434
  "@ai-sdk/anthropic": "^3.0.71",