@agent-native/core 0.209.1-nightly-20261015153252 → 0.209.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 (37) hide show
  1. package/dist/a2a/index.d.ts +1 -1
  2. package/dist/a2a/index.js +1 -1
  3. package/dist/a2a/server.d.ts +10 -0
  4. package/dist/a2a/server.js +96 -0
  5. package/dist/agent/production-agent.d.ts +2 -0
  6. package/dist/agent/production-agent.js +43 -6
  7. package/dist/agent/submission-deadline.d.ts +1 -0
  8. package/dist/agent/submission-deadline.js +27 -0
  9. package/dist/agent/thread-data-builder.d.ts +2 -1
  10. package/dist/agent/thread-data-builder.js +37 -6
  11. package/dist/authorization/check-action.js +4 -3
  12. package/dist/chat-threads/store.d.ts +1 -0
  13. package/dist/chat-threads/store.js +34 -2
  14. package/dist/client/agent-chat.d.ts +3 -0
  15. package/dist/client/agent-chat.js +9 -0
  16. package/dist/client/chat/agentkit-agent-native.js +126 -3
  17. package/dist/client/error-format.d.ts +2 -0
  18. package/dist/client/error-format.js +10 -0
  19. package/dist/collab/routes.d.ts +1 -1
  20. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  21. package/dist/org/index.d.ts +1 -1
  22. package/dist/org/index.js +1 -1
  23. package/dist/org/workspace-app-access-outcome.d.ts +45 -0
  24. package/dist/org/workspace-app-access-outcome.js +80 -0
  25. package/dist/org/workspace-app-access-request.d.ts +2 -2
  26. package/dist/org/workspace-app-access-request.js +2 -2
  27. package/dist/org/workspace-app-access.d.ts +8 -3
  28. package/dist/org/workspace-app-access.js +122 -30
  29. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  30. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  31. package/dist/server/action-routes.js +10 -7
  32. package/dist/server/agent-chat-plugin.js +28 -2
  33. package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
  34. package/dist/server/auth.js +3 -3
  35. package/dist/server/realtime-token.d.ts +1 -1
  36. package/dist/triggers/actions/manage-automation.d.ts +3 -3
  37. package/package.json +4 -4
@@ -9,11 +9,33 @@ import { isHostedWorkspaceRuntime, resolveVercelDeploymentProtectionHeaders, } f
9
9
  import { workspaceUserGroupsIncludeUser } from "../workspace-connections/groups.js";
10
10
  import { isMissingOrganizationTableError } from "./membership.js";
11
11
  import { getCachedWorkspaceAccess, invalidateMemberOrgCaches, rememberWorkspaceAccess, workspaceAccessGenerationNow, } from "./request-org-cache.js";
12
+ import { describeFetchFailure, isWorkspaceAppAccessUnavailable, registryErrorDetail, workspaceAppAccessUnavailable, WORKSPACE_APP_ACCESS_UNAVAILABLE, } from "./workspace-app-access-outcome.js";
13
+ export { WORKSPACE_APP_ACCESS_UNAVAILABLE, WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE, isWorkspaceAppAccessUnavailable, workspaceAppAccessUnavailableBody, } from "./workspace-app-access-outcome.js";
12
14
  const WORKSPACE_APPS_ACTION_PATH = "/_agent-native/actions/list-workspace-apps";
13
15
  const WORKSPACE_APP_CLAIM_ACTION_PATH = "/_agent-native/actions/claim-workspace-app-organization";
14
16
  const WORKSPACE_APP_ACCESS_TIMEOUT_MS = 10_000;
15
- export const WORKSPACE_APP_ACCESS_UNAVAILABLE = "unavailable";
16
- export const WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE = "Workspace app access is temporarily unavailable.";
17
+ // An unavailable registry is re-asked on every request, so one failing page
18
+ // load would otherwise log the same cause dozens of times. The key is
19
+ // categorical only: `detail` is upstream text, and a per-request id in it would
20
+ // grow this set without bound.
21
+ const reportedUnavailable = new Set();
22
+ function reportUnavailable(outcome) {
23
+ const key = [
24
+ outcome.reason,
25
+ outcome.upstreamStatus ?? "",
26
+ outcome.audience ?? "",
27
+ ].join("|");
28
+ if (reportedUnavailable.has(key))
29
+ return outcome;
30
+ reportedUnavailable.add(key);
31
+ console.error("[workspace-app-access] unavailable", {
32
+ reason: outcome.reason,
33
+ upstreamStatus: outcome.upstreamStatus,
34
+ audience: outcome.audience,
35
+ detail: outcome.detail,
36
+ });
37
+ return outcome;
38
+ }
17
39
  const inFlightWorkspaceAppRegistryReads = new Map();
18
40
  function normalizedEmail(email) {
19
41
  return email.trim().toLowerCase();
@@ -153,19 +175,26 @@ async function hostedWorkspaceAppAccess(appId, context, email) {
153
175
  if (!configuredDirectory)
154
176
  return null;
155
177
  const auth = await resolveHostedWorkspaceAppAuth(configuredDirectory, context, email);
156
- if (!auth)
157
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
178
+ if (isWorkspaceAppAccessUnavailable(auth))
179
+ return reportUnavailable(auth);
158
180
  const controller = new AbortController();
159
181
  const timeout = setTimeout(() => controller.abort(), WORKSPACE_APP_ACCESS_TIMEOUT_MS);
160
182
  try {
161
183
  const registry = await waitForRegistryResult(getHostedWorkspaceAppRegistry(auth), controller.signal);
162
- if (!registry || registry.status === "unavailable") {
163
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
184
+ if (!registry) {
185
+ return reportUnavailable(workspaceAppAccessUnavailable("registry-timeout", {
186
+ audience: auth.audience,
187
+ }));
164
188
  }
189
+ if (registry.status === "unavailable")
190
+ return reportUnavailable(registry);
165
191
  const matchingApp = registry.apps.find((app) => app.id === appId);
166
192
  if (matchingApp)
167
193
  return !workspaceAppIsDisabled(matchingApp);
168
- return await claimHostedWorkspaceApp(auth, appId, controller.signal);
194
+ const claimed = await claimHostedWorkspaceApp(auth, appId, controller.signal);
195
+ return isWorkspaceAppAccessUnavailable(claimed)
196
+ ? reportUnavailable(claimed)
197
+ : claimed;
169
198
  }
170
199
  finally {
171
200
  clearTimeout(timeout);
@@ -185,20 +214,25 @@ async function cachedOrgDomain(orgId) {
185
214
  async function resolveHostedWorkspaceAppAuth(configuredDirectory, context, email) {
186
215
  const url = workspaceAppsActionUrl(configuredDirectory);
187
216
  if (!url)
188
- return null;
217
+ return workspaceAppAccessUnavailable("directory-url-invalid");
189
218
  const orgId = context.orgId?.trim() || null;
219
+ const audience = canonicalA2AAudience(configuredDirectory);
220
+ let stage = "org-domain-lookup-failed";
190
221
  try {
191
222
  const orgDomain = orgId ? await cachedOrgDomain(orgId) : null;
192
223
  const normalizedOrgDomain = orgDomain?.trim() || undefined;
193
- if (orgId && !normalizedOrgDomain)
194
- return null;
224
+ if (orgId && !normalizedOrgDomain) {
225
+ return workspaceAppAccessUnavailable("org-domain-missing", { audience });
226
+ }
195
227
  const signingSecret = readDeployCredentialEnv("A2A_SECRET")?.trim();
196
- if (!signingSecret)
197
- return null;
228
+ if (!signingSecret) {
229
+ return workspaceAppAccessUnavailable("a2a-secret-missing", { audience });
230
+ }
231
+ stage = "token-signing-failed";
198
232
  const token = await signA2AToken(email, normalizedOrgDomain, undefined, {
199
233
  expiresIn: "1m",
200
234
  preferGlobalSecret: true,
201
- audience: canonicalA2AAudience(configuredDirectory),
235
+ audience,
202
236
  extraClaims: {
203
237
  jti: randomUUID(),
204
238
  },
@@ -216,6 +250,7 @@ async function resolveHostedWorkspaceAppAuth(configuredDirectory, context, email
216
250
  .digest("hex");
217
251
  return {
218
252
  url,
253
+ audience,
219
254
  requestKey,
220
255
  protectionHeaders,
221
256
  headers: {
@@ -227,7 +262,7 @@ async function resolveHostedWorkspaceAppAuth(configuredDirectory, context, email
227
262
  }
228
263
  catch (error) {
229
264
  console.error("[workspace-app-access] registry auth unavailable", error);
230
- return null;
265
+ return workspaceAppAccessUnavailable(stage, { audience });
231
266
  }
232
267
  }
233
268
  function waitForRegistryResult(request, signal) {
@@ -279,24 +314,50 @@ async function fetchHostedWorkspaceAppRegistry(auth) {
279
314
  : {}),
280
315
  signal: controller.signal,
281
316
  });
282
- if (!response.ok)
283
- return { status: "unavailable" };
317
+ if (!response.ok) {
318
+ return workspaceAppAccessUnavailable("registry-http-error", {
319
+ upstreamStatus: response.status,
320
+ audience: auth.audience,
321
+ detail: registryErrorDetail(
322
+ // coercion-ok: an unreadable error body only drops the optional detail; the status is already on the outcome.
323
+ await response.json().catch(() => null)),
324
+ });
325
+ }
284
326
  const apps = workspaceAppsFromResponse(
285
327
  // coercion-ok: malformed registry JSON is an authorization failure.
286
328
  await response.json().catch(() => null));
287
- return apps ? { status: "available", apps } : { status: "unavailable" };
329
+ return apps
330
+ ? { status: "available", apps }
331
+ : workspaceAppAccessUnavailable("registry-malformed-response", {
332
+ upstreamStatus: response.status,
333
+ audience: auth.audience,
334
+ });
288
335
  }
289
336
  catch (error) {
290
337
  console.error("[workspace-app-access] registry access check failed", error);
291
- return { status: "unavailable" };
338
+ return registryFetchFailure(error, auth);
292
339
  }
293
340
  finally {
294
341
  clearTimeout(timeout);
295
342
  }
296
343
  }
344
+ function registryFetchFailure(error, auth) {
345
+ const failure = describeFetchFailure(error);
346
+ return failure.timedOut
347
+ ? workspaceAppAccessUnavailable("registry-timeout", {
348
+ audience: auth.audience,
349
+ })
350
+ : workspaceAppAccessUnavailable("registry-fetch-failed", {
351
+ audience: auth.audience,
352
+ detail: failure.detail,
353
+ });
354
+ }
297
355
  async function claimHostedWorkspaceApp(auth, appId, signal) {
356
+ const timedOut = () => workspaceAppAccessUnavailable("registry-timeout", {
357
+ audience: auth.audience,
358
+ });
298
359
  if (signal.aborted)
299
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
360
+ return timedOut();
300
361
  const claimUrl = new URL(auth.url);
301
362
  claimUrl.pathname = claimUrl.pathname.replace(WORKSPACE_APPS_ACTION_PATH, WORKSPACE_APP_CLAIM_ACTION_PATH);
302
363
  claimUrl.search = "";
@@ -313,12 +374,23 @@ async function claimHostedWorkspaceApp(auth, appId, signal) {
313
374
  body: JSON.stringify({ appId }),
314
375
  signal,
315
376
  });
316
- if (!claimResponse.ok || signal.aborted) {
317
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
377
+ if (signal.aborted)
378
+ return timedOut();
379
+ if (!claimResponse.ok) {
380
+ return workspaceAppAccessUnavailable("claim-http-error", {
381
+ upstreamStatus: claimResponse.status,
382
+ audience: auth.audience,
383
+ detail: registryErrorDetail(
384
+ // coercion-ok: an unreadable error body only drops the optional detail; the status is already on the outcome.
385
+ await claimResponse.json().catch(() => null)),
386
+ });
318
387
  }
319
388
  const claim = (await claimResponse.json().catch(() => null));
320
389
  if (!claim || typeof claim.allowed !== "boolean") {
321
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
390
+ return workspaceAppAccessUnavailable("claim-malformed-response", {
391
+ upstreamStatus: claimResponse.status,
392
+ audience: auth.audience,
393
+ });
322
394
  }
323
395
  if (!claim.allowed)
324
396
  return false;
@@ -331,20 +403,32 @@ async function claimHostedWorkspaceApp(auth, appId, signal) {
331
403
  : {}),
332
404
  signal,
333
405
  });
334
- if (!refreshedResponse.ok || signal.aborted) {
335
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
406
+ if (signal.aborted)
407
+ return timedOut();
408
+ if (!refreshedResponse.ok) {
409
+ return workspaceAppAccessUnavailable("registry-http-error", {
410
+ upstreamStatus: refreshedResponse.status,
411
+ audience: auth.audience,
412
+ detail: registryErrorDetail(
413
+ // coercion-ok: an unreadable error body only drops the optional detail; the status is already on the outcome.
414
+ await refreshedResponse.json().catch(() => null)),
415
+ });
336
416
  }
337
417
  const refreshedApps = workspaceAppsFromResponse(
338
418
  // coercion-ok: malformed registry JSON is an authorization failure.
339
419
  await refreshedResponse.json().catch(() => null));
340
- if (!refreshedApps)
341
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
420
+ if (!refreshedApps) {
421
+ return workspaceAppAccessUnavailable("registry-malformed-response", {
422
+ upstreamStatus: refreshedResponse.status,
423
+ audience: auth.audience,
424
+ });
425
+ }
342
426
  const refreshedApp = refreshedApps.find((app) => app.id === appId);
343
427
  return refreshedApp ? !workspaceAppIsDisabled(refreshedApp) : false;
344
428
  }
345
429
  catch (error) {
346
430
  console.error("[workspace-app-access] registry access check failed", error);
347
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
431
+ return registryFetchFailure(error, auth);
348
432
  }
349
433
  }
350
434
  async function localOrganizationAppEnabled(appId, orgId) {
@@ -528,7 +612,18 @@ async function isDispatchWorkspaceAppAccessAllowed(context, email) {
528
612
  return false;
529
613
  }
530
614
  }
615
+ /**
616
+ * Keeps the contract this function has always had: `true`, `false`, or the
617
+ * `"unavailable"` sentinel. Use `checkWorkspaceAppAccess` to learn why a verdict
618
+ * could not be reached.
619
+ */
531
620
  export async function isWorkspaceAppAccessAllowed(appId, context) {
621
+ const check = await checkWorkspaceAppAccess(appId, context);
622
+ return isWorkspaceAppAccessUnavailable(check)
623
+ ? WORKSPACE_APP_ACCESS_UNAVAILABLE
624
+ : check;
625
+ }
626
+ export async function checkWorkspaceAppAccess(appId, context) {
532
627
  const normalizedAppId = appId.trim();
533
628
  const email = normalizedEmail(context.email);
534
629
  if (!normalizedAppId || !email) {
@@ -543,9 +638,6 @@ export async function isWorkspaceAppAccessAllowed(appId, context) {
543
638
  return false;
544
639
  }
545
640
  const hostedAccess = await hostedWorkspaceAppAccess(normalizedAppId, context, email);
546
- if (hostedAccess === WORKSPACE_APP_ACCESS_UNAVAILABLE) {
547
- return WORKSPACE_APP_ACCESS_UNAVAILABLE;
548
- }
549
641
  if (hostedAccess !== null)
550
642
  return hostedAccess;
551
643
  try {
@@ -75,10 +75,10 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- message?: undefined;
79
78
  id?: undefined;
80
79
  provider?: undefined;
81
80
  deleted?: undefined;
81
+ message?: undefined;
82
82
  providers: {
83
83
  id: string;
84
84
  label: string;
@@ -93,9 +93,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
93
93
  registered?: undefined;
94
94
  label?: undefined;
95
95
  } | {
96
- message?: undefined;
97
96
  id?: undefined;
98
97
  deleted?: undefined;
98
+ message?: undefined;
99
99
  providers?: undefined;
100
100
  count?: undefined;
101
101
  found: boolean;
@@ -103,9 +103,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
103
103
  registered?: undefined;
104
104
  label?: undefined;
105
105
  } | {
106
- message?: undefined;
107
106
  provider?: undefined;
108
107
  deleted?: undefined;
108
+ message?: undefined;
109
109
  providers?: undefined;
110
110
  count?: undefined;
111
111
  found: boolean;
@@ -113,8 +113,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
113
113
  registered?: undefined;
114
114
  label?: undefined;
115
115
  } | {
116
- message?: undefined;
117
116
  provider?: undefined;
117
+ message?: undefined;
118
118
  providers?: undefined;
119
119
  count?: undefined;
120
120
  found?: undefined;
@@ -316,10 +316,10 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
316
316
  notes?: string | undefined;
317
317
  scope?: "org" | "user" | undefined;
318
318
  }, {
319
- deleted?: undefined;
319
+ message?: undefined;
320
320
  id?: undefined;
321
+ deleted?: undefined;
321
322
  provider?: undefined;
322
- message?: undefined;
323
323
  providers: {
324
324
  id: string;
325
325
  label: string;
@@ -334,9 +334,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
334
334
  registered?: undefined;
335
335
  label?: undefined;
336
336
  } | {
337
- deleted?: undefined;
338
- id?: undefined;
339
337
  message?: undefined;
338
+ id?: undefined;
339
+ deleted?: undefined;
340
340
  providers?: undefined;
341
341
  count?: undefined;
342
342
  found: boolean;
@@ -344,9 +344,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
344
344
  registered?: undefined;
345
345
  label?: undefined;
346
346
  } | {
347
+ message?: undefined;
347
348
  deleted?: undefined;
348
349
  provider?: undefined;
349
- message?: undefined;
350
350
  providers?: undefined;
351
351
  count?: undefined;
352
352
  found: boolean;
@@ -354,8 +354,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
354
354
  registered?: undefined;
355
355
  label?: undefined;
356
356
  } | {
357
- provider?: undefined;
358
357
  message?: undefined;
358
+ provider?: undefined;
359
359
  providers?: undefined;
360
360
  count?: undefined;
361
361
  found?: undefined;
@@ -9,8 +9,8 @@ import { isTransientDatabaseError } from "../db/client.js";
9
9
  import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
10
10
  import { isFeatureFlagAdminEmail } from "../feature-flags/permissions.js";
11
11
  import { isFederationMembershipValidatedForEvent, resolveOrgIdForEmail, } from "../org/context.js";
12
+ import { isWorkspaceAppAccessUnavailable, workspaceAppAccessUnavailableBody, } from "../org/workspace-app-access-outcome.js";
12
13
  import { checkWorkspaceAppAccessForRequest } from "../org/workspace-app-access-request.js";
13
- import { WORKSPACE_APP_ACCESS_UNAVAILABLE, WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE, } from "../org/workspace-app-access.js";
14
14
  import { ACTION_BROWSER_PERSIST_ALLOW, ACTION_BROWSER_PERSIST_HEADER, } from "../shared/action-browser-persist.js";
15
15
  import { ACTION_CHANGE_MARKER_FAILED, ACTION_CHANGE_MARKER_HEADER, } from "../shared/action-change-marker-header.js";
16
16
  import { LLM_PROVIDER_MISSING_ERROR_CODE, LLM_PROVIDER_MISSING_STATUS, } from "../shared/action-error-codes.js";
@@ -345,13 +345,16 @@ async function denyWithoutWorkspaceAppAccess(event, name, ownerContext, resolved
345
345
  email: ownerContext.owner,
346
346
  orgId: workspaceOrgId,
347
347
  });
348
- if (workspaceAppAccess === WORKSPACE_APP_ACCESS_UNAVAILABLE) {
349
- return { status: 503, error: WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE };
348
+ if (isWorkspaceAppAccessUnavailable(workspaceAppAccess)) {
349
+ return {
350
+ status: 503,
351
+ body: workspaceAppAccessUnavailableBody(workspaceAppAccess),
352
+ };
350
353
  }
351
354
  if (workspaceAppAccess === false) {
352
355
  return {
353
356
  status: 403,
354
- error: "You do not have access to this workspace app.",
357
+ body: { error: "You do not have access to this workspace app." },
355
358
  };
356
359
  }
357
360
  return null;
@@ -587,7 +590,7 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
587
590
  const denied = await denyWithoutWorkspaceAppAccess(event, name, ownerContext, null, options?.resolveOrgId);
588
591
  if (denied) {
589
592
  setResponseStatus(event, denied.status);
590
- return { error: denied.error };
593
+ return denied.body;
591
594
  }
592
595
  }
593
596
  catch (error) {
@@ -601,7 +604,7 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
601
604
  const denied = await denyWithoutWorkspaceAppAccess(event, name, resolvedCaller, resolvedCaller, options?.resolveOrgId);
602
605
  if (denied) {
603
606
  setResponseStatus(event, denied.status);
604
- return { error: denied.error };
607
+ return denied.body;
605
608
  }
606
609
  }
607
610
  }
@@ -640,7 +643,7 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
640
643
  const denied = await denyWithoutWorkspaceAppAccess(event, name, ownerContext, resolvedCaller, options?.resolveOrgId);
641
644
  if (denied) {
642
645
  setResponseStatus(event, denied.status);
643
- return { error: denied.error };
646
+ return denied.body;
644
647
  }
645
648
  }
646
649
  if (!resolvedCaller &&
@@ -1,6 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import nodePath from "node:path";
3
- import { AgentProtocolValidationError, parseQueueMessageInput, } from "@agent-native/agentkit/protocol";
3
+ import { AgentProtocolValidationError, AGENT_NATIVE_SUBMISSION_DEADLINES_CAPABILITY_ID, parseQueueMessageInput, } from "@agent-native/agentkit/protocol";
4
4
  import { createError, defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getQuery, getHeader, getRequestIP, readBody as readH3Body, } from "h3";
5
5
  import { applyA2AAgentActivityEvent, buildA2AAgentActivityPart, createA2AAgentActivityState, } from "../a2a/activity.js";
6
6
  import { appendA2AArtifactLinks, buildA2ARecoverableArtifactMessage, } from "../a2a/artifact-response.js";
@@ -2968,6 +2968,11 @@ export function createAgentChatPlugin(options) {
2968
2968
  turnId: details.turnId,
2969
2969
  agentKitMessageId: details.agentKitMessageId,
2970
2970
  queuedMessageId: details.queuedMessageId,
2971
+ ...(Object.prototype.hasOwnProperty.call(details, "submissionExpiresAt")
2972
+ ? {
2973
+ submissionExpiresAt: details.submissionExpiresAt,
2974
+ }
2975
+ : {}),
2971
2976
  ...(details.failure
2972
2977
  ? { refusedRetry: details.retryContext ?? {} }
2973
2978
  : {}),
@@ -3009,7 +3014,7 @@ export function createAgentChatPlugin(options) {
3009
3014
  submissionFailure = result.status;
3010
3015
  return threadData;
3011
3016
  }
3012
- const submitted = details.failure
3017
+ const submitted = details.failure && result.status !== "already_submitted"
3013
3018
  ? foldUnstartedTurnFailure(result.repo, {
3014
3019
  runId: details.runId,
3015
3020
  threadId,
@@ -3038,6 +3043,13 @@ export function createAgentChatPlugin(options) {
3038
3043
  data: { code: "run_slot_busy", retryable: true },
3039
3044
  });
3040
3045
  }
3046
+ if (submissionFailure === "submission_expired") {
3047
+ throw createError({
3048
+ statusCode: 409,
3049
+ statusMessage: "Prompt submission expired before it could start",
3050
+ data: { code: "submission_expired", retryable: false },
3051
+ });
3052
+ }
3041
3053
  if (submissionFailure === "invalid_thread_data") {
3042
3054
  throw new TypeError("Agent chat thread data is not valid JSON.");
3043
3055
  }
@@ -6305,6 +6317,20 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
6305
6317
  // This is mounted last because h3's use() is prefix-based, meaning /_agent-native/agent-chat
6306
6318
  // also matches /_agent-native/agent-chat/threads/... — we skip sub-path requests here so the
6307
6319
  // earlier-mounted handlers (mode, save-key, files, skills, mentions, threads) handle them.
6320
+ const capabilitiesPath = `${routePath}/capabilities`;
6321
+ getH3App(nitroApp).use(capabilitiesPath, defineEventHandler((event) => {
6322
+ const requestPath = new URL(event.node?.req?.url || event.path || "", "http://localhost").pathname;
6323
+ if (requestPath !== capabilitiesPath) {
6324
+ setResponseStatus(event, 404);
6325
+ return { error: "Not found" };
6326
+ }
6327
+ if (getMethod(event) !== "GET") {
6328
+ setResponseStatus(event, 405);
6329
+ return { error: "Method not allowed" };
6330
+ }
6331
+ setResponseHeader(event, "Cache-Control", "no-store");
6332
+ return { [AGENT_NATIVE_SUBMISSION_DEADLINES_CAPABILITY_ID]: true };
6333
+ }));
6308
6334
  getH3App(nitroApp).use(routePath, withTransientDatabaseFallback(routePath, async (event) => {
6309
6335
  // Skip sub-path requests — they're handled by earlier-mounted handlers
6310
6336
  const url = event.node?.req?.url || event.path || "";
@@ -1,9 +1,9 @@
1
1
  export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
2
  error: string;
3
- ok?: undefined;
4
3
  models?: undefined;
4
+ ok?: undefined;
5
5
  } | {
6
- error?: undefined;
7
6
  ok: boolean;
8
7
  models: string[];
8
+ error?: undefined;
9
9
  }>>;
@@ -3,8 +3,8 @@ import { defineEventHandler, getMethod, getQuery, setResponseHeader, setResponse
3
3
  import { getAppConfig, resolveAppHomePath } from "../app-config/index.js";
4
4
  import { acceptPendingInvitationsForEmail } from "../org/accept-pending.js";
5
5
  import { newOrgSelection, ORG_SELECTION_COOKIE, } from "../org/request-org-cache.js";
6
+ import { isWorkspaceAppAccessUnavailable, workspaceAppAccessUnavailableBody, } from "../org/workspace-app-access-outcome.js";
6
7
  import { checkWorkspaceAppAccessForRequest } from "../org/workspace-app-access-request.js";
7
- import { WORKSPACE_APP_ACCESS_UNAVAILABLE, WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE, } from "../org/workspace-app-access.js";
8
8
  import { EMBED_SESSION_COOKIE, EMBED_START_PATH, EMBED_TARGET_HEADER, isMcpDirectoryWidgetCapabilityScope, } from "../shared/embed-auth.js";
9
9
  import { FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_MAX_AGE, } from "../shared/first-run-onboarding.js";
10
10
  import { isGoogleProfileImageUrl } from "../shared/google-profile-image.js";
@@ -3114,9 +3114,9 @@ function createAuthGuardFn(app) {
3114
3114
  email: session.email,
3115
3115
  orgId: session.orgId,
3116
3116
  });
3117
- if (workspaceAppAccess === WORKSPACE_APP_ACCESS_UNAVAILABLE) {
3117
+ if (isWorkspaceAppAccessUnavailable(workspaceAppAccess)) {
3118
3118
  setResponseStatus(event, 503);
3119
- return { error: WORKSPACE_APP_ACCESS_UNAVAILABLE_MESSAGE };
3119
+ return workspaceAppAccessUnavailableBody(workspaceAppAccess);
3120
3120
  }
3121
3121
  if (workspaceAppAccess === false) {
3122
3122
  setResponseStatus(event, 403);
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
54
54
  expiresAt?: undefined;
55
55
  ttlSeconds?: undefined;
56
56
  } | {
57
- error?: undefined;
58
57
  token: string;
59
58
  expiresAt: string;
60
59
  ttlSeconds: number;
60
+ error?: undefined;
61
61
  }>>;
@@ -14,6 +14,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
14
14
  executionCwd?: string;
15
15
  }, {
16
16
  deleted?: undefined;
17
+ enabled?: undefined;
17
18
  created: boolean;
18
19
  name: string;
19
20
  scope: "organization" | "personal";
@@ -24,25 +25,24 @@ declare const _default: import("../../action.js").ActionDefinition<{
24
25
  reasoningEffort: "auto" | "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh";
25
26
  webhookPath: string;
26
27
  nextRun: string;
27
- enabled?: undefined;
28
28
  executionHostId?: undefined;
29
29
  executionEngine?: undefined;
30
30
  executionCwd?: undefined;
31
31
  } | {
32
+ enabled?: undefined;
33
+ nextRun?: undefined;
32
34
  created?: undefined;
33
35
  scope?: undefined;
34
36
  triggerType?: undefined;
35
37
  event?: undefined;
36
38
  deleted: boolean;
37
39
  name: string;
38
- enabled?: undefined;
39
40
  schedule?: undefined;
40
41
  timezone?: undefined;
41
42
  reasoningEffort?: undefined;
42
43
  executionHostId?: undefined;
43
44
  executionEngine?: undefined;
44
45
  executionCwd?: undefined;
45
- nextRun?: undefined;
46
46
  webhookPath?: undefined;
47
47
  } | {
48
48
  deleted?: undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.209.1-nightly-20261015153252",
3
+ "version": "0.209.1",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -704,8 +704,8 @@
704
704
  "yaml": "^2.9.0",
705
705
  "yjs": "^13.6.33",
706
706
  "zod": "^4.3.6",
707
- "@agent-native/agentkit": "^0.209.1-nightly-20261015153252",
708
- "@agent-native/recap-cli": "^0.5.72"
707
+ "@agent-native/agentkit": "^0.209.1",
708
+ "@agent-native/recap-cli": "^0.5.73"
709
709
  },
710
710
  "devDependencies": {
711
711
  "@ai-sdk/anthropic": "^3.0.71",
@@ -775,7 +775,7 @@
775
775
  "xlsx": "^0.18.5"
776
776
  },
777
777
  "peerDependencies": {
778
- "@agent-native/core-corpus": "0.209.1-nightly-20261015153252",
778
+ "@agent-native/core-corpus": ">=0.196.0",
779
779
  "@ai-sdk/anthropic": ">=3",
780
780
  "@ai-sdk/cohere": ">=3",
781
781
  "@ai-sdk/google": ">=3",