@agent-native/core 0.154.1 → 0.154.3

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 (31) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/changelog/2026-08-12-feature-flags-can-be-managed-across-apps-whose-workspace-rec.md +6 -0
  3. package/corpus/templates/analytics/changelog/2026-08-12-kept-feature-flag-details-readable-by-stacking-rollout-contr.md +6 -0
  4. package/corpus/templates/analytics/scripts/preview-dashboard-report.ts +16 -5
  5. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +98 -31
  6. package/corpus/templates/dispatch/changelog/2026-08-12-dispatch-now-appears-in-authorized-workspace-app-directories.md +6 -0
  7. package/corpus/templates/dispatch/server/lib/org-apps-directory.ts +3 -4
  8. package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +15 -3
  9. package/dist/a2a-claims.d.ts +1 -0
  10. package/dist/a2a-claims.js +3 -1
  11. package/dist/cli/create.js +3 -0
  12. package/dist/client/feature-flags/FeatureFlagsPanel.js +2 -2
  13. package/dist/collab/routes.d.ts +1 -1
  14. package/dist/feature-flags/a2a-action-route.js +10 -1
  15. package/dist/feature-flags/actions/set-feature-flag.d.ts +2 -1
  16. package/dist/feature-flags/actions/set-feature-flag.js +9 -2
  17. package/dist/mcp/org-directory.d.ts +3 -0
  18. package/dist/mcp/org-directory.js +15 -5
  19. package/dist/notifications/routes.d.ts +1 -1
  20. package/dist/observability/routes.d.ts +5 -5
  21. package/dist/progress/routes.d.ts +1 -1
  22. package/dist/resources/handlers.d.ts +1 -1
  23. package/dist/secrets/routes.d.ts +9 -9
  24. package/dist/server/action-routes.js +10 -2
  25. package/dist/server/agent-discovery.d.ts +6 -2
  26. package/dist/server/agent-discovery.js +16 -9
  27. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  28. package/dist/server/identity-sso-store.d.ts +5 -4
  29. package/dist/server/identity-sso-store.js +5 -4
  30. package/dist/server/realtime-token.d.ts +1 -1
  31. package/package.json +3 -3
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: 8205
34
+ - template files: 8208
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-12
4
+ ---
5
+
6
+ Feature flags can be managed across apps whose workspace records use different local IDs
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-12
4
+ ---
5
+
6
+ Kept feature flag details readable by stacking rollout controls at narrow widths.
@@ -19,7 +19,8 @@
19
19
  */
20
20
 
21
21
  import { mkdir, writeFile } from "node:fs/promises";
22
- import { join } from "node:path";
22
+ import path, { join } from "node:path";
23
+ import { pathToFileURL } from "node:url";
23
24
 
24
25
  import { runWithRequestContext } from "@agent-native/core/server/request-context";
25
26
  import { eq } from "drizzle-orm";
@@ -215,7 +216,17 @@ async function renderPreview(
215
216
  if (rendered.degradedPanelIds.length) process.exitCode = 1;
216
217
  }
217
218
 
218
- main().catch((err) => {
219
- console.error("[preview] failed:", err);
220
- process.exit(1);
221
- });
219
+ function isDirectRun(): boolean {
220
+ const entrypoint = process.argv[1];
221
+ return Boolean(
222
+ entrypoint &&
223
+ import.meta.url === pathToFileURL(path.resolve(entrypoint)).href,
224
+ );
225
+ }
226
+
227
+ if (isDirectRun()) {
228
+ main().catch((err) => {
229
+ console.error("[preview] failed:", err);
230
+ process.exit(1);
231
+ });
232
+ }
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
 
3
3
  import { signA2AToken } from "@agent-native/core/a2a";
4
4
  import { fetchOrgApps, type OrgApp } from "@agent-native/core/mcp";
5
+ import { getOrgDomain } from "@agent-native/core/org";
5
6
 
6
7
  import type { AnalyticsAdminContext } from "./db-admin-connections.js";
7
8
 
@@ -31,11 +32,11 @@ export interface WorkspaceFeatureFlagsResult {
31
32
  }
32
33
 
33
34
  export interface WorkspaceFeatureFlagMutationResult {
34
- contractVersion: 1;
35
+ contractVersion: 2;
35
36
  status: "ready";
36
37
  key: string;
37
38
  rules: Record<string, unknown>;
38
- scope: { orgId: string | null };
39
+ scope: { orgId: string | null; orgDomain: string | null };
39
40
  }
40
41
 
41
42
  export interface WorkspaceFeatureFlagMutationInput {
@@ -66,14 +67,14 @@ export function validateWorkspaceFeatureFlagMutation(
66
67
  body: unknown,
67
68
  expected: {
68
69
  key: string;
69
- orgId: string;
70
+ orgDomain: string;
70
71
  rules?: Record<string, unknown>;
71
72
  enabledForEmail?: string;
72
73
  },
73
74
  ): WorkspaceFeatureFlagMutationResult {
74
75
  const payload = body as Partial<WorkspaceFeatureFlagMutationResult> | null;
75
76
  const valid =
76
- payload?.contractVersion === 1 &&
77
+ payload?.contractVersion === 2 &&
77
78
  payload.status === "ready" &&
78
79
  payload.key === expected.key &&
79
80
  !!payload.rules &&
@@ -81,7 +82,7 @@ export function validateWorkspaceFeatureFlagMutation(
81
82
  !Array.isArray(payload.rules) &&
82
83
  !!payload.scope &&
83
84
  typeof payload.scope === "object" &&
84
- payload.scope.orgId === expected.orgId;
85
+ payload.scope.orgDomain === expected.orgDomain;
85
86
  if (!valid)
86
87
  throw new Error(
87
88
  "The target app returned an unsupported or unverified feature flag mutation response.",
@@ -152,13 +153,43 @@ async function delegatedToken(
152
153
  admin: AnalyticsAdminContext,
153
154
  origin: string,
154
155
  scope: "flags:read" | "flags:write",
156
+ resolvedOrgDomain?: string,
155
157
  ): Promise<string> {
156
- return signA2AToken(admin.userEmail, undefined, undefined, {
157
- expiresIn: "120s",
158
- preferGlobalSecret: true,
159
- audience: origin,
160
- extraClaims: { org_id: admin.orgId, scope, jti: randomUUID() },
161
- });
158
+ try {
159
+ const orgDomain =
160
+ resolvedOrgDomain ??
161
+ (await getOrgDomain(admin.orgId))?.trim().toLowerCase();
162
+ if (!orgDomain) throw new TargetCallFailure("token-generation");
163
+ return await signA2AToken(admin.userEmail, orgDomain, undefined, {
164
+ expiresIn: "120s",
165
+ preferGlobalSecret: true,
166
+ audience: origin,
167
+ extraClaims: { org_id: admin.orgId, scope, jti: randomUUID() },
168
+ });
169
+ } catch {
170
+ throw new TargetCallFailure("token-generation");
171
+ }
172
+ }
173
+
174
+ type TargetFailureReason = "token-generation" | "timeout" | "network";
175
+
176
+ class TargetCallFailure extends Error {
177
+ constructor(readonly reason: TargetFailureReason) {
178
+ super(`Feature flag target call failed: ${reason}`);
179
+ this.name = "TargetCallFailure";
180
+ }
181
+ }
182
+
183
+ export function classifyWorkspaceFeatureFlagTargetFailure(
184
+ error: unknown,
185
+ ): TargetFailureReason {
186
+ if (error instanceof TargetCallFailure) return error.reason;
187
+ if (
188
+ error instanceof Error &&
189
+ (error.name === "TimeoutError" || error.name === "AbortError")
190
+ )
191
+ return "timeout";
192
+ return "network";
162
193
  }
163
194
 
164
195
  async function callTarget(
@@ -166,25 +197,34 @@ async function callTarget(
166
197
  admin: AnalyticsAdminContext,
167
198
  action: "list-feature-flags" | "set-feature-flag",
168
199
  body: Record<string, unknown>,
200
+ orgDomain?: string,
169
201
  ): Promise<{ status: number; body: unknown }> {
170
202
  const origin = targetOrigin(app);
171
203
  const token = await delegatedToken(
172
204
  admin,
173
205
  origin,
174
206
  action === "list-feature-flags" ? "flags:read" : "flags:write",
207
+ orgDomain,
175
208
  );
176
- const response = await fetch(`${origin}/_agent-native/actions/${action}`, {
177
- method: action === "list-feature-flags" ? "GET" : "POST",
178
- headers: {
179
- Authorization: `Bearer ${token}`,
180
- Accept: "application/json",
181
- ...(action === "set-feature-flag"
182
- ? { "Content-Type": "application/json" }
183
- : {}),
184
- },
185
- ...(action === "set-feature-flag" ? { body: JSON.stringify(body) } : {}),
186
- signal: AbortSignal.timeout(TARGET_TIMEOUT_MS),
187
- });
209
+ let response: Response;
210
+ try {
211
+ response = await fetch(`${origin}/_agent-native/actions/${action}`, {
212
+ method: action === "list-feature-flags" ? "GET" : "POST",
213
+ headers: {
214
+ Authorization: `Bearer ${token}`,
215
+ Accept: "application/json",
216
+ ...(action === "set-feature-flag"
217
+ ? { "Content-Type": "application/json" }
218
+ : {}),
219
+ },
220
+ ...(action === "set-feature-flag" ? { body: JSON.stringify(body) } : {}),
221
+ signal: AbortSignal.timeout(TARGET_TIMEOUT_MS),
222
+ });
223
+ } catch (error) {
224
+ throw new TargetCallFailure(
225
+ classifyWorkspaceFeatureFlagTargetFailure(error),
226
+ );
227
+ }
188
228
  let parsed: unknown = null;
189
229
  try {
190
230
  parsed = await response.json();
@@ -209,7 +249,7 @@ export function classifyWorkspaceFeatureFlagList(
209
249
  if (result.status === 404 || result.status === 405)
210
250
  return { ...base, state: "unsupported" };
211
251
  if (result.status < 200 || result.status >= 300)
212
- return { ...base, state: "unknown-legacy" };
252
+ return { ...base, state: "unknown-legacy", reason: "target-execution" };
213
253
  const payload = result.body as {
214
254
  flags?: unknown;
215
255
  canManage?: unknown;
@@ -254,7 +294,11 @@ async function mapBounded<T, R>(
254
294
  export async function listWorkspaceFeatureFlags(
255
295
  admin: AnalyticsAdminContext,
256
296
  ): Promise<WorkspaceFeatureFlagsResult> {
257
- const apps = await fetchOrgApps({ serviceOrgId: admin.orgId });
297
+ const apps = await fetchOrgApps({
298
+ selfId: "analytics",
299
+ includeDirectoryApp: true,
300
+ serviceOrgId: admin.orgId,
301
+ });
258
302
  if (apps.length === 0) return { directoryStatus: "unavailable", apps: [] };
259
303
  const entries = await mapBounded(apps, async (app) => {
260
304
  try {
@@ -262,13 +306,14 @@ export async function listWorkspaceFeatureFlags(
262
306
  app,
263
307
  await callTarget(app, admin, "list-feature-flags", {}),
264
308
  );
265
- } catch {
309
+ } catch (error) {
266
310
  return {
267
311
  appId: app.id,
268
312
  appName: app.name,
269
313
  appOrigin: targetOrigin(app),
270
314
  state: "unreachable" as const,
271
315
  flags: [],
316
+ reason: classifyWorkspaceFeatureFlagTargetFailure(error),
272
317
  };
273
318
  }
274
319
  });
@@ -279,14 +324,31 @@ export async function setWorkspaceFeatureFlag(
279
324
  admin: AnalyticsAdminContext,
280
325
  input: WorkspaceFeatureFlagMutationInput,
281
326
  ): Promise<WorkspaceFeatureFlagMutationResult> {
282
- const apps = await fetchOrgApps({ serviceOrgId: admin.orgId });
327
+ const apps = await fetchOrgApps({
328
+ selfId: "analytics",
329
+ includeDirectoryApp: true,
330
+ serviceOrgId: admin.orgId,
331
+ });
283
332
  const app = apps.find((candidate) => candidate.id === input.appId);
284
333
  if (!app)
285
334
  throw new Error(
286
335
  "The requested app is not available in this organization directory.",
287
336
  );
288
337
  const targetInput = workspaceFeatureFlagTargetInput(input);
289
- const result = await callTarget(app, admin, "set-feature-flag", targetInput);
338
+ let orgDomain: string | undefined;
339
+ try {
340
+ orgDomain = (await getOrgDomain(admin.orgId))?.trim().toLowerCase();
341
+ } catch {
342
+ throw new TargetCallFailure("token-generation");
343
+ }
344
+ if (!orgDomain) throw new TargetCallFailure("token-generation");
345
+ const result = await callTarget(
346
+ app,
347
+ admin,
348
+ "set-feature-flag",
349
+ targetInput,
350
+ orgDomain,
351
+ );
290
352
  if (result.status === 401 || result.status === 403)
291
353
  throw new Error("The target app denied this delegated flag operation.");
292
354
  if (result.status === 404 || result.status === 405)
@@ -297,7 +359,7 @@ export async function setWorkspaceFeatureFlag(
297
359
  );
298
360
  return validateWorkspaceFeatureFlagMutation(result.body, {
299
361
  key: input.key,
300
- orgId: admin.orgId,
362
+ orgDomain,
301
363
  ...(input.operation === "replace-rules" && input.rules
302
364
  ? {
303
365
  rules: {
@@ -324,7 +386,11 @@ export async function getWorkspaceFlagTarget(
324
386
  admin: AnalyticsAdminContext,
325
387
  appId: string,
326
388
  ): Promise<FleetFlagApp> {
327
- const apps = await fetchOrgApps({ serviceOrgId: admin.orgId });
389
+ const apps = await fetchOrgApps({
390
+ selfId: "analytics",
391
+ includeDirectoryApp: true,
392
+ serviceOrgId: admin.orgId,
393
+ });
328
394
  const app = apps.find((candidate) => candidate.id === appId);
329
395
  if (!app)
330
396
  throw new Error(
@@ -335,13 +401,14 @@ export async function getWorkspaceFlagTarget(
335
401
  app,
336
402
  await callTarget(app, admin, "list-feature-flags", {}),
337
403
  );
338
- } catch {
404
+ } catch (error) {
339
405
  return {
340
406
  appId: app.id,
341
407
  appName: app.name,
342
408
  appOrigin: targetOrigin(app),
343
409
  state: "unreachable",
344
410
  flags: [],
411
+ reason: classifyWorkspaceFeatureFlagTargetFailure(error),
345
412
  };
346
413
  }
347
414
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-12
4
+ ---
5
+
6
+ Dispatch now appears in authorized workspace app directories for fleet-wide administration
@@ -278,14 +278,13 @@ export interface DiscoveredAppLike {
278
278
 
279
279
  /**
280
280
  * Shape the discovered-agent list into the directory response. The input is
281
- * Dispatch's EXISTING connected-apps registry — `discoverAgents("dispatch")`
281
+ * Dispatch's EXISTING connected-apps registry — `discoverAgents()`
282
282
  * from `@agent-native/core/server/agent-discovery`, the same source
283
283
  * `list-connected-agents` and the `call-agent` delegation path use. It is
284
284
  * already allow-list-respecting (hidden first-party templates are excluded
285
285
  * from `BUILTIN_AGENTS` unless `defaultAgent`), so no second filter is
286
- * needed here; we only drop entries without a usable absolute http(s) URL
287
- * and Dispatch itself (a caller does not need to discover the directory it
288
- * just called).
286
+ * needed here; we only drop entries without a usable absolute http(s) URL.
287
+ * Callers that identify themselves may also request self-filtering.
289
288
  */
290
289
  export function buildOrgAppsResponse(input: {
291
290
  org: string;
@@ -24,7 +24,7 @@
24
24
  *
25
25
  * APP-LIST SOURCE — Dispatch's existing connected-apps registry.
26
26
  * -------------------------------------------------------------
27
- * Dispatch already has a connected-apps concept: `discoverAgents("dispatch")`
27
+ * Dispatch already has a connected-apps concept: `discoverAgents()`
28
28
  * from `@agent-native/core/server/agent-discovery` (the same source
29
29
  * `list-connected-agents` and the `call-agent` delegation path use). It
30
30
  * returns the allow-listed first-party apps with their prod URLs PLUS any
@@ -143,7 +143,16 @@ const orgAppsHandler = defineEventHandler(
143
143
  const apps: DiscoveredAppLike[] = await runWithRequestContext(
144
144
  { userEmail: verified.email, orgId: localOrg.orgId },
145
145
  async () => {
146
- const discovered = await discoverAgents(SELF_APP_ID);
146
+ const includeDirectoryApp =
147
+ getRequestHeader(event, "x-agent-native-include-directory-app") ===
148
+ "1";
149
+ const discovered = await discoverAgents(
150
+ includeDirectoryApp ? undefined : SELF_APP_ID,
151
+ {
152
+ preferLocalUrls:
153
+ process.env.AGENT_NATIVE_PREFER_LOCAL_APP_URLS === "1",
154
+ },
155
+ );
147
156
  return discovered.map((a) => ({
148
157
  id: a.id,
149
158
  name: a.name,
@@ -165,7 +174,10 @@ const orgAppsHandler = defineEventHandler(
165
174
  const body = buildOrgAppsResponse({
166
175
  org: orgLabel,
167
176
  apps,
168
- selfId: SELF_APP_ID,
177
+ selfId:
178
+ getRequestHeader(event, "x-agent-native-include-directory-app") === "1"
179
+ ? undefined
180
+ : SELF_APP_ID,
169
181
  });
170
182
 
171
183
  // Short, cacheable, read-only. Private (per-org) so shared caches must
@@ -1,6 +1,7 @@
1
1
  export interface VerifiedA2AClaims {
2
2
  email: string;
3
3
  orgId: string;
4
+ orgDomain: string;
4
5
  jti: string;
5
6
  issuer?: string;
6
7
  scope: string[];
@@ -24,10 +24,12 @@ export async function verifyA2ATokenWithClaims(token, event) {
24
24
  ? raw.scope.split(/\s+/).filter(Boolean)
25
25
  : [];
26
26
  const issuer = typeof raw.iss === "string" ? raw.iss.trim() : "";
27
- return orgId && jti
27
+ const orgDomain = identity.orgDomain?.trim().toLowerCase() ?? "";
28
+ return orgId && orgDomain && jti
28
29
  ? {
29
30
  email: identity.email,
30
31
  orgId,
32
+ orgDomain,
31
33
  jti,
32
34
  ...(issuer ? { issuer } : {}),
33
35
  scope: scopes,
@@ -81,6 +81,7 @@ const FIRST_PARTY_TARBALL_SYMLINK_EXCLUDES = [
81
81
  "*/CLAUDE.md",
82
82
  "*/.claude/skills",
83
83
  ];
84
+ const TAR_LISTING_MAX_BUFFER = 100 * 1024 * 1024;
84
85
  const localPackageTarballs = new Map();
85
86
  /** VCS/editor files that don't count as "not empty" for an in-place scaffold. */
86
87
  const IN_PLACE_ALLOWLIST = new Set([
@@ -1737,6 +1738,7 @@ function archiveSymlinksForExtraction(tarPath) {
1737
1738
  const listing = execFileSync("tar", ["tvzf", tarPath], {
1738
1739
  encoding: "utf-8",
1739
1740
  stdio: ["ignore", "pipe", "pipe"],
1741
+ maxBuffer: TAR_LISTING_MAX_BUFFER,
1740
1742
  });
1741
1743
  return listing.split(/\r?\n/).flatMap((line) => {
1742
1744
  if (!line.startsWith("l"))
@@ -1866,6 +1868,7 @@ function validateCommunityArchive(tarPath) {
1866
1868
  const listing = execFileSync("tar", ["tvzf", tarPath], {
1867
1869
  encoding: "utf-8",
1868
1870
  stdio: ["ignore", "pipe", "pipe"],
1871
+ maxBuffer: TAR_LISTING_MAX_BUFFER,
1869
1872
  });
1870
1873
  assertSafeCommunityArchiveListing(listing);
1871
1874
  }
@@ -117,9 +117,9 @@ function FeatureFlagRow({ flag, onMutate, isPending, error, }) {
117
117
  const actor = formatActor(flag.rules.updatedBy);
118
118
  const when = formatWhen(flag.rules.updatedAt);
119
119
  const metadata = [actor, when].filter(Boolean).join(" · ");
120
- return (_jsxs("article", { id: `feature-flag-${flag.key}`, className: "grid gap-4 scroll-mt-24 border-b border-border py-5 last:border-b-0 md:grid-cols-[minmax(0,1fr)_auto] md:items-center", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("h3", { className: "truncate text-sm font-medium text-foreground", children: flag.displayName ?? flag.key }), flag.displayName && flag.displayName !== flag.key ? (_jsx("code", { className: "truncate text-xs text-muted-foreground", children: flag.key })) : null, _jsx(Badge, { variant: flag.rules.mode === "on" ? "secondary" : "outline", className: "ms-auto shrink-0", children: modeLabel(flag.rules, t) })] }), flag.description ? (_jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: flag.description })) : null, _jsxs("div", { className: "mt-3 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { children: [t("featureFlags.enabledFor"), ": ", audienceLabel(flag.rules, t)] }), metadata ? (_jsx("span", { children: t("featureFlags.lastChanged", { metadata }) })) : null] }), _jsx("div", { className: "mt-2 flex items-center gap-1.5 text-xs font-medium text-foreground", children: flag.enabledForCurrentUser === true ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { className: "size-4 text-emerald-500" }), t("featureFlags.youHaveAccess")] })) : flag.enabledForCurrentUser === false ? (_jsx(_Fragment, { children: t("featureFlags.youDoNotHaveAccess") })) : (_jsx(_Fragment, { children: t("featureFlags.currentUserUnknown") })) }), error ? (_jsx("p", { className: "mt-3 text-sm text-destructive", children: t("featureFlags.mutationUnverified", {
120
+ return (_jsxs("article", { id: `feature-flag-${flag.key}`, className: "grid gap-4 scroll-mt-24 border-b border-border py-5 last:border-b-0 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("h3", { className: "truncate text-sm font-medium text-foreground", children: flag.displayName ?? flag.key }), flag.displayName && flag.displayName !== flag.key ? (_jsx("code", { className: "truncate text-xs text-muted-foreground", children: flag.key })) : null, _jsx(Badge, { variant: flag.rules.mode === "on" ? "secondary" : "outline", className: "ms-auto shrink-0", children: modeLabel(flag.rules, t) })] }), flag.description ? (_jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: flag.description })) : null, _jsxs("div", { className: "mt-3 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { children: [t("featureFlags.enabledFor"), ": ", audienceLabel(flag.rules, t)] }), metadata ? (_jsx("span", { children: t("featureFlags.lastChanged", { metadata }) })) : null] }), _jsx("div", { className: "mt-2 flex items-center gap-1.5 text-xs font-medium text-foreground", children: flag.enabledForCurrentUser === true ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { className: "size-4 text-emerald-500" }), t("featureFlags.youHaveAccess")] })) : flag.enabledForCurrentUser === false ? (_jsx(_Fragment, { children: t("featureFlags.youDoNotHaveAccess") })) : (_jsx(_Fragment, { children: t("featureFlags.currentUserUnknown") })) }), error ? (_jsx("p", { className: "mt-3 text-sm text-destructive", children: t("featureFlags.mutationUnverified", {
121
121
  name: flag.displayName ?? flag.key,
122
- }) })) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 md:justify-end", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "text-xs", disabled: isPending || flag.enabledForCurrentUser === true, onClick: () => onMutate({
122
+ }) })) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 lg:justify-end", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "text-xs", disabled: isPending || flag.enabledForCurrentUser === true, onClick: () => onMutate({
123
123
  key: flag.key,
124
124
  operation: "enable-for-current-user",
125
125
  }), children: [flag.enabledForCurrentUser === true ? (_jsx(IconCheck, {})) : (_jsx(IconUserCheck, {})), flag.enabledForCurrentUser === true
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -1,6 +1,8 @@
1
1
  import { getHeader } from "h3";
2
2
  import * as jose from "jose";
3
3
  import { verifyA2ATokenWithClaims } from "../a2a-claims.js";
4
+ import { resolveOrgByDomain } from "../org/context.js";
5
+ import { consumeOneTimeJti } from "../server/identity-sso-store.js";
4
6
  const FLAG_ACTION_SCOPES = {
5
7
  "list-feature-flags": "flags:read",
6
8
  "set-feature-flag": "flags:write",
@@ -37,9 +39,16 @@ export function createFeatureFlagA2AActionRouteAuth(actionName) {
37
39
  if (!claims || !claims.scope.includes(FLAG_ACTION_SCOPES[actionName])) {
38
40
  throw new Error("Invalid feature flag delegation");
39
41
  }
42
+ const localOrg = await resolveOrgByDomain(claims.orgDomain);
43
+ if (!localOrg)
44
+ throw new Error("Invalid feature flag delegation");
45
+ if (actionName === "set-feature-flag" &&
46
+ (await consumeOneTimeJti(claims.jti))) {
47
+ throw new Error("Invalid feature flag delegation");
48
+ }
40
49
  return {
41
50
  owner: claims.email,
42
- orgId: claims.orgId,
51
+ orgId: localOrg.orgId,
43
52
  anonymous: false,
44
53
  delegationJti: claims.jti,
45
54
  delegationIssuer: claims.issuer,
@@ -14,12 +14,13 @@ declare const _default: import("../../action.js").ActionDefinition<{
14
14
  percentage?: number;
15
15
  };
16
16
  }, {
17
- contractVersion: 1;
17
+ contractVersion: 2;
18
18
  status: "ready";
19
19
  key: string;
20
20
  rules: import("../store.js").FeatureFlagRules;
21
21
  scope: {
22
22
  orgId: string;
23
+ orgDomain: string;
23
24
  };
24
25
  }>;
25
26
  export default _default;
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { defineAction } from "../../action.js";
3
+ import { getOrgDomain } from "../../org/context.js";
3
4
  import { requireFeatureFlagManager } from "../permissions.js";
4
5
  import { getFeatureFlagDefinition } from "../registry.js";
5
6
  import { defaultFeatureFlagRules, mutateFeatureFlagRules, normalizeFeatureFlagRules, } from "../store.js";
@@ -47,6 +48,12 @@ export default defineAction({
47
48
  if (!getFeatureFlagDefinition(args.key)) {
48
49
  throw new Error(`Unknown feature flag: ${args.key}`);
49
50
  }
51
+ const orgDomain = manager.orgId
52
+ ? (await getOrgDomain(manager.orgId))?.trim().toLowerCase() || null
53
+ : null;
54
+ if (ctx?.caller === "a2a" && manager.orgId && !orgDomain) {
55
+ throw new Error("Feature flag organization domain is unavailable.");
56
+ }
50
57
  const persistedRules = await mutateFeatureFlagRules(args.key, manager, (current) => {
51
58
  let rules;
52
59
  if (args.operation === "off") {
@@ -67,11 +74,11 @@ export default defineAction({
67
74
  return { ...rules, updatedAt: Date.now(), updatedBy: manager.email };
68
75
  });
69
76
  return {
70
- contractVersion: 1,
77
+ contractVersion: 2,
71
78
  status: "ready",
72
79
  key: args.key,
73
80
  rules: persistedRules,
74
- scope: { orgId: manager.orgId },
81
+ scope: { orgId: manager.orgId, orgDomain },
75
82
  };
76
83
  },
77
84
  });
@@ -71,11 +71,14 @@ export declare function resolveOrgDirectoryOrigin(env?: NodeJS.ProcessEnv): stri
71
71
  *
72
72
  * @param opts.selfId Current app id (so it's stripped from the result).
73
73
  * @param opts.selfOrigin Current app origin (so it's stripped by origin too).
74
+ * @param opts.includeDirectoryApp Request the directory authority itself. The
75
+ * server keeps legacy self-filtering unless this explicit signal is present.
74
76
  * @param opts.env Injectable env (tests). Defaults to `process.env`.
75
77
  */
76
78
  export declare function fetchOrgApps(opts?: {
77
79
  selfId?: string;
78
80
  selfOrigin?: string;
81
+ includeDirectoryApp?: boolean;
79
82
  serviceOrgId?: string;
80
83
  env?: NodeJS.ProcessEnv;
81
84
  }): Promise<OrgApp[]>;
@@ -109,18 +109,23 @@ function sameOrigin(a, b) {
109
109
  return a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
110
110
  }
111
111
  }
112
- function scopedCacheKey(origin, auth) {
112
+ function scopedCacheKey(origin, auth, includeDirectoryApp = false) {
113
113
  return [
114
114
  origin,
115
115
  `user:${auth.userEmail ?? ""}`,
116
116
  `org:${auth.orgId ?? auth.orgDomain ?? ""}`,
117
+ `include-directory:${includeDirectoryApp ? "1" : "0"}`,
117
118
  ].join("|");
118
119
  }
119
120
  function authTokenAttempts(auth) {
120
121
  return [auth.apiKey, ...(auth.apiKeyFallbacks ?? [])].filter((token) => typeof token === "string" && token.length > 0);
121
122
  }
122
- function serviceScopedCacheKey(origin, orgId) {
123
- return [origin, `service-org:${orgId}`].join("|");
123
+ function serviceScopedCacheKey(origin, orgId, includeDirectoryApp = false) {
124
+ return [
125
+ origin,
126
+ `service-org:${orgId}`,
127
+ `include-directory:${includeDirectoryApp ? "1" : "0"}`,
128
+ ].join("|");
124
129
  }
125
130
  /**
126
131
  * Fetch the org's first-party sibling apps from the org directory.
@@ -133,6 +138,8 @@ function serviceScopedCacheKey(origin, orgId) {
133
138
  *
134
139
  * @param opts.selfId Current app id (so it's stripped from the result).
135
140
  * @param opts.selfOrigin Current app origin (so it's stripped by origin too).
141
+ * @param opts.includeDirectoryApp Request the directory authority itself. The
142
+ * server keeps legacy self-filtering unless this explicit signal is present.
136
143
  * @param opts.env Injectable env (tests). Defaults to `process.env`.
137
144
  */
138
145
  export async function fetchOrgApps(opts) {
@@ -155,7 +162,7 @@ export async function fetchOrgApps(opts) {
155
162
  let ttl = EMPTY_TTL_MS;
156
163
  const serviceOrgId = opts?.serviceOrgId?.trim();
157
164
  if (serviceOrgId) {
158
- const serviceCacheKey = serviceScopedCacheKey(origin, serviceOrgId);
165
+ const serviceCacheKey = serviceScopedCacheKey(origin, serviceOrgId, opts?.includeDirectoryApp);
159
166
  const cached = cache.get(serviceCacheKey);
160
167
  if (cached && cached.expiresAt > Date.now()) {
161
168
  return stripSelf(cached.apps);
@@ -174,7 +181,7 @@ export async function fetchOrgApps(opts) {
174
181
  }
175
182
  if (!cacheKey) {
176
183
  const now = Date.now();
177
- cacheKey = scopedCacheKey(origin, auth);
184
+ cacheKey = scopedCacheKey(origin, auth, opts?.includeDirectoryApp);
178
185
  const cached = cache.get(cacheKey);
179
186
  if (cached && cached.expiresAt > now) {
180
187
  return stripSelf(cached.apps);
@@ -186,6 +193,9 @@ export async function fetchOrgApps(opts) {
186
193
  headers: {
187
194
  Authorization: `Bearer ${attempts[i]}`,
188
195
  Accept: "application/json",
196
+ ...(opts?.includeDirectoryApp
197
+ ? { "X-Agent-Native-Include-Directory-App": "1" }
198
+ : {}),
189
199
  },
190
200
  signal: AbortSignal.timeout(4000),
191
201
  });
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- ok: boolean;
32
31
  error?: undefined;
32
+ ok: boolean;
33
33
  }>>;
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
+ error?: undefined;
45
+ ok?: undefined;
44
46
  summary: import("./types.js").TraceSummary;
45
47
  spans: import("./types.js").TraceSpan[];
46
48
  id?: undefined;
49
+ } | {
47
50
  error?: undefined;
48
51
  ok?: undefined;
49
- } | {
50
52
  summary?: undefined;
51
53
  spans?: undefined;
52
54
  id: string;
53
- error?: undefined;
54
- ok?: undefined;
55
55
  } | {
56
+ ok?: undefined;
56
57
  summary?: undefined;
57
58
  spans?: undefined;
58
59
  id?: undefined;
59
60
  error: any;
60
- ok?: undefined;
61
61
  } | {
62
+ error?: undefined;
62
63
  summary?: undefined;
63
64
  spans?: undefined;
64
65
  id?: undefined;
65
66
  ok: boolean;
66
- error?: undefined;
67
67
  }>>;
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- error: string;
52
51
  ok?: undefined;
52
+ error: string;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -34,37 +34,37 @@ 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
+ error?: undefined;
40
41
  ok: boolean;
41
42
  status: string;
42
- error?: undefined;
43
43
  } | {
44
+ ok?: undefined;
44
45
  error: string;
45
46
  removed?: undefined;
46
- ok?: undefined;
47
47
  } | {
48
+ error?: undefined;
48
49
  ok: boolean;
49
50
  removed: boolean;
50
- error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
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
+ error?: undefined;
61
62
  ok: boolean;
62
63
  note?: undefined;
63
- error?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  ok: boolean;
66
67
  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;
98
99
  ok: boolean;
99
100
  key: string;
100
- error?: undefined;
101
101
  } | {
102
+ error?: undefined;
102
103
  ok: boolean;
103
104
  removed: boolean;
104
- error?: undefined;
105
105
  }>>;
@@ -2,7 +2,7 @@ import { createError, defineEventHandler, setResponseStatus, setResponseHeader,
2
2
  import { verifyA2ATokenWithClaims } from "../a2a-claims.js";
3
3
  import { isAgentActionStopError } from "../action.js";
4
4
  import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
5
- import { resolveOrgIdForEmail } from "../org/context.js";
5
+ import { resolveOrgByDomain, resolveOrgIdForEmail } from "../org/context.js";
6
6
  import { readBody } from "../server/h3-helpers.js";
7
7
  import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
8
8
  import { isMcpEmbedCorsOrigin, MCP_EMBED_CORS_ALLOW_HEADERS, shouldAllowMcpEmbedCredentials, } from "../shared/mcp-embed-headers.js";
@@ -12,6 +12,7 @@ import { captureError } from "./capture-error.js";
12
12
  import { getAllowedCorsOrigin as resolveAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
13
13
  import { resolveEmbedSessionFromRequest, resolvedEmbedCapabilityScope, } from "./embed-session.js";
14
14
  import { getHttpRequestTelemetryId } from "./http-response-telemetry.js";
15
+ import { consumeOneTimeJti } from "./identity-sso-store.js";
15
16
  function requiredClientCompatibilityVersion() {
16
17
  const configured = typeof __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__ === "string"
17
18
  ? __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__
@@ -52,9 +53,16 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
52
53
  const claims = await verifyA2ATokenWithClaims(token, event);
53
54
  if (!claims || !claims.scope.includes(required))
54
55
  throw new Error("Invalid feature flag delegation");
56
+ const localOrg = await resolveOrgByDomain(claims.orgDomain);
57
+ if (!localOrg)
58
+ throw new Error("Invalid feature flag delegation");
59
+ if (actionName === "set-feature-flag" &&
60
+ (await consumeOneTimeJti(claims.jti))) {
61
+ throw new Error("Invalid feature flag delegation");
62
+ }
55
63
  return {
56
64
  owner: claims.email,
57
- orgId: claims.orgId,
65
+ orgId: localOrg.orgId,
58
66
  anonymous: false,
59
67
  delegationJti: claims.jti,
60
68
  delegationIssuer: claims.issuer,
@@ -70,12 +70,16 @@ export declare function shouldIncludeRemoteAgentManifest(manifest: {
70
70
  /**
71
71
  * Get built-in agents (static, no DB). Used as fallback and for seeding.
72
72
  */
73
- export declare function getBuiltinAgents(selfAppId?: string): DiscoveredAgent[];
73
+ export declare function getBuiltinAgents(selfAppId?: string, options?: {
74
+ preferLocalUrls?: boolean;
75
+ }): DiscoveredAgent[];
74
76
  /**
75
77
  * Discover all agents: built-in + custom agents stored as resources.
76
78
  * Custom agents override built-in agents with the same ID.
77
79
  */
78
- export declare function discoverAgents(selfAppId?: string): Promise<DiscoveredAgent[]>;
80
+ export declare function discoverAgents(selfAppId?: string, options?: {
81
+ preferLocalUrls?: boolean;
82
+ }): Promise<DiscoveredAgent[]>;
79
83
  /**
80
84
  * Look up a single agent by ID or name (case-insensitive).
81
85
  */
@@ -191,13 +191,13 @@ export function shouldIncludeRemoteAgentManifest(manifest, selfAppId) {
191
191
  /**
192
192
  * Get built-in agents (static, no DB). Used as fallback and for seeding.
193
193
  */
194
- export function getBuiltinAgents(selfAppId) {
194
+ export function getBuiltinAgents(selfAppId, options) {
195
195
  const normalizedSelfAppId = selfAppId ? normalizeAgentId(selfAppId) : "";
196
196
  return BUILTIN_AGENTS.filter((app) => app.id !== normalizedSelfAppId && app.url).map((app) => ({
197
197
  id: app.id,
198
198
  name: app.name,
199
199
  description: app.description,
200
- url: resolveAgentUrl(app),
200
+ url: resolveAgentUrl(app, options?.preferLocalUrls),
201
201
  color: app.color,
202
202
  }));
203
203
  }
@@ -205,8 +205,8 @@ export function getBuiltinAgents(selfAppId) {
205
205
  * Discover all agents: built-in + custom agents stored as resources.
206
206
  * Custom agents override built-in agents with the same ID.
207
207
  */
208
- export async function discoverAgents(selfAppId) {
209
- const builtins = getBuiltinAgents(selfAppId);
208
+ export async function discoverAgents(selfAppId, options) {
209
+ const builtins = getBuiltinAgents(selfAppId, options);
210
210
  const agentsById = new Map();
211
211
  // Start with built-ins
212
212
  for (const agent of builtins) {
@@ -249,6 +249,11 @@ export async function discoverAgents(selfAppId) {
249
249
  url = builtin.url;
250
250
  }
251
251
  const builtin = agentsById.get(manifestId);
252
+ if (options?.preferLocalUrls && builtin) {
253
+ const isBuiltinAgent = BUILTIN_AGENTS.some((candidate) => candidate.id === manifestId);
254
+ if (isBuiltinAgent)
255
+ url = builtin.url;
256
+ }
252
257
  const isLegacyAssetsManifest = manifest.id.trim().toLowerCase() !== manifestId;
253
258
  if (isLegacyAssetsManifest && builtin?.url) {
254
259
  try {
@@ -280,7 +285,7 @@ export async function discoverAgents(selfAppId) {
280
285
  }
281
286
  // Overlay sibling workspace apps last so same-origin workspaces prefer the
282
287
  // app mounted in this workspace over the public template with the same id.
283
- for (const agent of await discoverWorkspaceAgents(selfAppId)) {
288
+ for (const agent of await discoverWorkspaceAgents(selfAppId, options)) {
284
289
  agentsById.set(agent.id, agent);
285
290
  }
286
291
  return Array.from(agentsById.values());
@@ -347,8 +352,8 @@ function isHostedRuntime() {
347
352
  function shouldUseLocalAgentUrls() {
348
353
  return !isHostedRuntime();
349
354
  }
350
- function resolveAgentUrl(app) {
351
- if (shouldUseLocalAgentUrls()) {
355
+ function resolveAgentUrl(app, preferLocalUrls = false) {
356
+ if (preferLocalUrls || shouldUseLocalAgentUrls()) {
352
357
  return app.devUrl || `http://localhost:${app.devPort}`;
353
358
  }
354
359
  return app.url;
@@ -527,7 +532,7 @@ function workspaceAppUrl(app, hostedLoopbackFallbackUrl) {
527
532
  return null;
528
533
  }
529
534
  }
530
- async function discoverWorkspaceAgents(selfAppId) {
535
+ async function discoverWorkspaceAgents(selfAppId, options) {
531
536
  const workspaceApps = loadWorkspaceAppsManifest();
532
537
  if (!workspaceApps)
533
538
  return [];
@@ -537,7 +542,9 @@ async function discoverWorkspaceAgents(selfAppId) {
537
542
  .map((app) => {
538
543
  const withOverride = applyWorkspaceAppMetadataOverride(app, metadataSettings);
539
544
  const builtin = BUILTIN_AGENTS.find((agent) => agent.id === withOverride.id);
540
- const url = workspaceAppUrl(withOverride, builtin?.url);
545
+ const url = options?.preferLocalUrls && builtin
546
+ ? resolveAgentUrl(builtin, true)
547
+ : workspaceAppUrl(withOverride, builtin?.url);
541
548
  if (!url)
542
549
  return null;
543
550
  return {
@@ -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 {};
@@ -7,9 +7,9 @@
7
7
  *
8
8
  * - `identity_sso_flow_state` binds state to the exact app, client,
9
9
  * authority, callback, and PKCE challenge. State is single-use.
10
- * - `identity_sso_jti` remains a defence-in-depth replay guard for the
11
- * server-to-server assertion. The assertion is never sent through the
12
- * browser; the one-time authorization code is the only browser credential.
10
+ * - `identity_sso_jti` is the legacy-named shared replay guard for short-lived
11
+ * server-to-server assertions, including identity SSO and privileged A2A
12
+ * mutations.
13
13
  *
14
14
  * Uses the same portable raw-SQL pattern as the other framework stores. DDL is
15
15
  * additive and lazy, and PostgreSQL creation goes through the DDL guard.
@@ -59,4 +59,5 @@ export declare function consumeSsoState(state: string, expected: SsoStateBinding
59
59
  * guarantee, and refusing a login is safer than accepting an unverifiable
60
60
  * replay boundary.
61
61
  */
62
- export declare function isJtiReplayed(jti: string | undefined): Promise<boolean>;
62
+ export declare function consumeOneTimeJti(jti: string | undefined): Promise<boolean>;
63
+ export declare const isJtiReplayed: typeof consumeOneTimeJti;
@@ -7,9 +7,9 @@
7
7
  *
8
8
  * - `identity_sso_flow_state` binds state to the exact app, client,
9
9
  * authority, callback, and PKCE challenge. State is single-use.
10
- * - `identity_sso_jti` remains a defence-in-depth replay guard for the
11
- * server-to-server assertion. The assertion is never sent through the
12
- * browser; the one-time authorization code is the only browser credential.
10
+ * - `identity_sso_jti` is the legacy-named shared replay guard for short-lived
11
+ * server-to-server assertions, including identity SSO and privileged A2A
12
+ * mutations.
13
13
  *
14
14
  * Uses the same portable raw-SQL pattern as the other framework stores. DDL is
15
15
  * additive and lazy, and PostgreSQL creation goes through the DDL guard.
@@ -321,7 +321,7 @@ export async function consumeSsoState(state, expected) {
321
321
  * guarantee, and refusing a login is safer than accepting an unverifiable
322
322
  * replay boundary.
323
323
  */
324
- export async function isJtiReplayed(jti) {
324
+ export async function consumeOneTimeJti(jti) {
325
325
  if (!jti)
326
326
  return true;
327
327
  try {
@@ -352,3 +352,4 @@ export async function isJtiReplayed(jti) {
352
352
  return true;
353
353
  }
354
354
  }
355
+ export const isJtiReplayed = consumeOneTimeJti;
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
26
26
  expiresAt?: undefined;
27
27
  ttlSeconds?: undefined;
28
28
  } | {
29
- error?: undefined;
30
29
  token: string;
31
30
  expiresAt: string;
32
31
  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.1",
3
+ "version": "0.154.3",
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": {
@@ -421,8 +421,8 @@
421
421
  "y-protocols": "^1.0.7",
422
422
  "yjs": "^13.6.31",
423
423
  "zod": "^4.3.6",
424
- "@agent-native/toolkit": "^0.14.2",
425
- "@agent-native/recap-cli": "0.5.4"
424
+ "@agent-native/recap-cli": "0.5.4",
425
+ "@agent-native/toolkit": "^0.14.2"
426
426
  },
427
427
  "devDependencies": {
428
428
  "@ai-sdk/anthropic": "^3.0.71",