@agent-native/core 0.212.1 → 0.214.0

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 (107) hide show
  1. package/dist/a2a/anthropic-managed-agents.js +1 -0
  2. package/dist/a2a/caller-auth.js +7 -2
  3. package/dist/a2a/client.d.ts +1 -0
  4. package/dist/a2a/client.js +27 -0
  5. package/dist/a2a/handlers.js +15 -3
  6. package/dist/a2a/organization-identity.d.ts +4 -1
  7. package/dist/a2a/organization-identity.js +3 -1
  8. package/dist/a2a/server.d.ts +6 -5
  9. package/dist/a2a/server.js +79 -6
  10. package/dist/a2a-claims.d.ts +1 -0
  11. package/dist/a2a-claims.js +1 -0
  12. package/dist/agent/actions/manage-provider-key-policy.js +4 -2
  13. package/dist/agent/app-model-defaults.js +2 -10
  14. package/dist/agent/loop-settings.js +2 -10
  15. package/dist/appearance/actions/get-theme-family.d.ts +8 -0
  16. package/dist/appearance/actions/get-theme-family.js +38 -0
  17. package/dist/appearance/actions/set-theme-family.d.ts +7 -0
  18. package/dist/appearance/actions/set-theme-family.js +33 -0
  19. package/dist/appearance/theme-family.d.ts +13 -0
  20. package/dist/appearance/theme-family.js +38 -0
  21. package/dist/audit/actions/export-audit-events.js +1 -0
  22. package/dist/audit/attribution.d.ts +5 -4
  23. package/dist/audit/attribution.js +2 -1
  24. package/dist/audit/index.d.ts +1 -1
  25. package/dist/audit/ocsf.js +1 -0
  26. package/dist/audit/read-scope.js +2 -12
  27. package/dist/audit/record.js +1 -0
  28. package/dist/audit/store.js +12 -3
  29. package/dist/audit/types.d.ts +4 -0
  30. package/dist/automation/index.d.ts +1 -1
  31. package/dist/automations/service.js +3 -10
  32. package/dist/client/org/hooks.d.ts +3 -0
  33. package/dist/client/org/hooks.js +12 -0
  34. package/dist/client/theme-family.d.ts +35 -0
  35. package/dist/client/theme-family.js +223 -0
  36. package/dist/client/theme.d.ts +1 -1
  37. package/dist/client/theme.js +5 -2
  38. package/dist/client/ui/index.d.ts +1 -0
  39. package/dist/client/ui/index.js +1 -0
  40. package/dist/collab/awareness.d.ts +2 -2
  41. package/dist/collab/routes.d.ts +2 -2
  42. package/dist/collab/struct-routes.d.ts +1 -1
  43. package/dist/connections/catalog.d.ts +1 -1
  44. package/dist/connections/catalog.js +7 -1
  45. package/dist/credentials/index.js +18 -2
  46. package/dist/data-widgets/index.d.ts +339 -4
  47. package/dist/data-widgets/index.js +150 -7
  48. package/dist/db/schema-snapshot.generated.js +5 -2
  49. package/dist/feature-flags/a2a-action-route.js +1 -0
  50. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  51. package/dist/file-upload/storage-settings.js +2 -9
  52. package/dist/framework-tools.js +2 -0
  53. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  54. package/dist/jobs/tools.js +2 -12
  55. package/dist/mcp/actions/service-token-access.js +15 -11
  56. package/dist/mcp/credential-issuance.js +9 -2
  57. package/dist/notifications/routes.d.ts +3 -3
  58. package/dist/observability/routes.d.ts +1 -1
  59. package/dist/org/actions.d.ts +1 -1
  60. package/dist/org/actions.js +3 -7
  61. package/dist/org/app-roles.js +10 -1
  62. package/dist/org/context.d.ts +1 -0
  63. package/dist/org/context.js +122 -24
  64. package/dist/org/federation.d.ts +1 -1
  65. package/dist/org/federation.js +2 -2
  66. package/dist/org/handlers.d.ts +4 -0
  67. package/dist/org/handlers.js +140 -44
  68. package/dist/org/index.d.ts +2 -1
  69. package/dist/org/membership.d.ts +6 -4
  70. package/dist/org/membership.js +7 -5
  71. package/dist/org/migrations.js +6 -0
  72. package/dist/org/plugin.d.ts +1 -0
  73. package/dist/org/plugin.js +9 -1
  74. package/dist/org/private-icon-handlers.js +4 -10
  75. package/dist/org/types.d.ts +2 -0
  76. package/dist/org/workspace-app-access.js +62 -14
  77. package/dist/progress/routes.d.ts +1 -1
  78. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  79. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  80. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  81. package/dist/secrets/routes.d.ts +6 -6
  82. package/dist/server/action-discovery.js +8 -0
  83. package/dist/server/action-routes.d.ts +1 -0
  84. package/dist/server/action-routes.js +13 -1
  85. package/dist/server/agent-chat/context-tools.js +43 -1
  86. package/dist/server/agent-chat/prompt-resources.js +1 -1
  87. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  88. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  89. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  90. package/dist/server/index.d.ts +1 -0
  91. package/dist/server/index.js +1 -0
  92. package/dist/server/org-admin.js +3 -2
  93. package/dist/server/personal-provider-key-holders.d.ts +1 -1
  94. package/dist/server/personal-provider-key-holders.js +7 -2
  95. package/dist/server/personal-provider-key-policy.d.ts +2 -0
  96. package/dist/server/personal-provider-key-policy.js +21 -5
  97. package/dist/server/request-context.d.ts +2 -0
  98. package/dist/server/request-context.js +7 -0
  99. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +16 -13
  100. package/dist/triggers/actions/manage-automation.d.ts +4 -4
  101. package/dist/triggers/routes.js +2 -9
  102. package/dist/usage/alerts-store.js +2 -9
  103. package/dist/usage/metrics-store.js +2 -9
  104. package/dist/workspace-connections/actions/list-workspace-user-groups.d.ts +1 -1
  105. package/dist/workspace-connections/groups.js +2 -11
  106. package/package.json +3 -3
  107. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +16 -13
@@ -177,6 +177,7 @@ export function auditEventToOcsf(event) {
177
177
  keep("reported_client_version", event.reportedClientVersion);
178
178
  keep("oauth_client_id", event.oauthClientId);
179
179
  keep("approval", event.approval);
180
+ keep("identity_assurance", event.identityAssurance);
180
181
  const resource = event.targetType || event.targetId
181
182
  ? [
182
183
  {
@@ -4,9 +4,9 @@
4
4
  * decided on the server from `org_members`, never from anything the caller
5
5
  * sends.
6
6
  */
7
- import { getDbExec } from "../db/client.js";
8
7
  import { isMissingOrganizationTableError } from "../org/membership.js";
9
8
  import { canManageOrg } from "../org/permissions.js";
9
+ import { readOrgMemberRole } from "../server/personal-provider-key-policy.js";
10
10
  export class AuditAccessError extends Error {
11
11
  statusCode = 403;
12
12
  constructor(message) {
@@ -16,17 +16,7 @@ export class AuditAccessError extends Error {
16
16
  }
17
17
  async function readOrgRole(orgId, email) {
18
18
  try {
19
- const { rows } = await getDbExec().execute({
20
- sql: `SELECT role FROM org_members
21
- WHERE org_id = ? AND LOWER(email) = ?
22
- AND federation_removal_pending_at IS NULL
23
- LIMIT 1`,
24
- args: [orgId, email.toLowerCase()],
25
- });
26
- const role = rows[0]?.role;
27
- return role === "owner" || role === "admin" || role === "member"
28
- ? role
29
- : null;
19
+ return await readOrgMemberRole(orgId, email);
30
20
  }
31
21
  catch (error) {
32
22
  // No org tables means no organization to administer. Any other failure
@@ -170,6 +170,7 @@ export async function recordActionAudit(input) {
170
170
  reportedClientVersion: reportedLabel(attribution.mcpClient?.version),
171
171
  oauthClientId: attribution.mcpClient?.oauthClientId ?? null,
172
172
  approval: approvalEvidence(attribution),
173
+ identityAssurance: attribution.identityAssurance ?? null,
173
174
  };
174
175
  if (integration) {
175
176
  if (ctx?.orgId &&
@@ -78,6 +78,12 @@ export const auditStore = defineStore({
78
78
  "ALTER TABLE agent_audit_log ADD COLUMN IF NOT EXISTS approval TEXT",
79
79
  ],
80
80
  },
81
+ {
82
+ name: "a2a-identity-assurance",
83
+ sql: [
84
+ "ALTER TABLE agent_audit_log ADD COLUMN IF NOT EXISTS identity_assurance TEXT",
85
+ ],
86
+ },
81
87
  ],
82
88
  });
83
89
  export function ensureAuditTables() {
@@ -100,8 +106,8 @@ export async function insertAuditEvent(event) {
100
106
  error_code, owner_email, visibility, run_id, task_id, parent_task_id,
101
107
  source_kind, source_platform, source_id, source_url, network_protocol,
102
108
  network_id, network_peer, app, reported_client_name,
103
- reported_client_version, oauth_client_id, approval)
104
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
109
+ reported_client_version, oauth_client_id, approval, identity_assurance)
110
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
105
111
  args: [
106
112
  event.id,
107
113
  event.createdAt,
@@ -135,6 +141,7 @@ export async function insertAuditEvent(event) {
135
141
  event.reportedClientVersion ?? null,
136
142
  event.oauthClientId ?? null,
137
143
  event.approval ?? null,
144
+ event.identityAssurance ?? null,
138
145
  ].map(storableText),
139
146
  });
140
147
  }
@@ -203,6 +210,8 @@ function mapRow(row) {
203
210
  reportedClientVersion: row.reported_client_version ?? null,
204
211
  oauthClientId: row.oauth_client_id ?? null,
205
212
  approval: (row.approval ?? null),
213
+ identityAssurance: (row.identity_assurance ??
214
+ null),
206
215
  };
207
216
  }
208
217
  function sharedVisibilities(scope) {
@@ -252,7 +261,7 @@ const LIST_COLUMNS = "id, created_at, action, caller, actor_kind, actor_email, o
252
261
  "error_code, owner_email, visibility, app, run_id, task_id, " +
253
262
  "parent_task_id, source_kind, source_platform, source_id, source_url, " +
254
263
  "network_protocol, network_id, network_peer, reported_client_name, " +
255
- "reported_client_version, oauth_client_id, approval";
264
+ "reported_client_version, oauth_client_id, approval, identity_assurance";
256
265
  function clampLimit(limit) {
257
266
  return Math.min(Math.max(1, Math.floor(limit ?? DEFAULT_LIMIT)), MAX_LIMIT);
258
267
  }
@@ -7,6 +7,8 @@ export type AuditActorKind = "agent" | "human" | "service" | "system";
7
7
  * accepted MCP elicitation response; the server cannot verify who responded.
8
8
  */
9
9
  export type AuditApproval = "agent-chat" | "a2a-review" | "mcp-client-accepted";
10
+ /** Verified identity provenance for a request authenticated through A2A. */
11
+ export type AuditIdentityAssurance = "user" | "organization" | "org-member";
10
12
  /**
11
13
  * Who can read an event besides its owner.
12
14
  * - `private` — the owner only (the default; personal content stays here).
@@ -80,6 +82,8 @@ export interface AuditEvent {
80
82
  * not verify who responded. Rows written before the column existed are null.
81
83
  */
82
84
  approval?: AuditApproval | null;
85
+ /** Verified identity provenance; null on older rows or non-A2A calls. */
86
+ identityAssurance?: AuditIdentityAssurance | null;
83
87
  }
84
88
  export interface AuditQueryFilters {
85
89
  targetType?: string;
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
139
139
  duplicate?: undefined;
140
140
  eventId?: undefined;
141
141
  } | {
142
+ error?: undefined;
142
143
  accepted: boolean;
143
144
  duplicate: boolean;
144
145
  eventId: string;
145
- error?: undefined;
146
146
  }>;
@@ -6,6 +6,7 @@ import { deleteAutomationRunsWithDb, ensureAutomationRunHistoryReady, prepareAut
6
6
  import { applyJobResourceEdit } from "../jobs/update-job-resource.js";
7
7
  import { resolveUserSchedulingTimezone } from "../localization/user-timezone.js";
8
8
  import { organizationIdFromResourceOwner, organizationResourceOwner, ensureResourceStoreReady, prepareResourceBatchAssertion, prepareResourceCreate, prepareResourceDelete, prepareResourceUpdate, resourceGetByPath, resourceList, resourcePutWithDb, resourceDelete, SHARED_OWNER, } from "../resources/store.js";
9
+ import { readOrgMemberRole } from "../server/personal-provider-key-policy.js";
9
10
  import { isReasoningEffort, } from "../shared/reasoning-effort.js";
10
11
  import { deleteAutomationWebhookToken, readAutomationWebhookPath, saveAutomationWebhookToken, } from "../triggers/webhook-store.js";
11
12
  import { createAutomationWebhookToken, automationWebhookPath, } from "../triggers/webhook.js";
@@ -61,16 +62,8 @@ function ownerForScope(actor, scope) {
61
62
  return organizationResourceOwner(actor.orgId);
62
63
  }
63
64
  async function readOrganizationMembership(orgId, email) {
64
- const result = await getDbExec().execute({
65
- sql: `SELECT role FROM org_members
66
- WHERE org_id = ? AND LOWER(email) = ?
67
- AND federation_removal_pending_at IS NULL
68
- LIMIT 1`,
69
- args: [orgId, email.toLowerCase()],
70
- });
71
- if (!result.rows.length)
72
- return null;
73
- return { role: String(result.rows[0]?.role ?? "").toLowerCase() };
65
+ const role = await readOrgMemberRole(orgId, email);
66
+ return role ? { role } : null;
74
67
  }
75
68
  async function requireOrganizationMembership(actor) {
76
69
  if (!actor.orgId) {
@@ -235,6 +235,9 @@ export declare function useSetA2ASecret(): import("@tanstack/react-query").UseMu
235
235
  a2aSecret: string;
236
236
  previousSecret: string | null;
237
237
  }, Error, string | undefined, unknown>;
238
+ export declare function useSetA2AMemberDelegation(): import("@tanstack/react-query").UseMutationResult<{
239
+ enabled: boolean;
240
+ }, Error, boolean, unknown>;
238
241
  export interface SyncA2ASecretResult {
239
242
  total: number;
240
243
  succeeded: number;
@@ -486,6 +486,18 @@ export function useSetA2ASecret() {
486
486
  },
487
487
  });
488
488
  }
489
+ export function useSetA2AMemberDelegation() {
490
+ const qc = useQueryClient();
491
+ return useMutation({
492
+ mutationFn: (enabled) => apiFetch(`${orgBasePath()}/a2a-member-delegation`, {
493
+ method: "PUT",
494
+ body: JSON.stringify({ enabled }),
495
+ }),
496
+ onSuccess: async () => {
497
+ await qc.invalidateQueries({ queryKey: ["org-me"] });
498
+ },
499
+ });
500
+ }
489
501
  export function useSyncA2ASecret() {
490
502
  return useMutation({
491
503
  mutationFn: (vars) => apiFetch(`${orgBasePath()}/a2a-secret/sync`, {
@@ -0,0 +1,35 @@
1
+ import { type ThemeFamily } from "../appearance/theme-family.js";
2
+ export { DEFAULT_THEME_FAMILY, isThemeFamily, THEME_FAMILY_APP_STATE_KEY, THEME_FAMILY_ATTRIBUTE, THEME_FAMILY_STORAGE_KEY, themeFamilyAppStateKey, themeFamilyStorageKey, type ThemeFamily, } from "../appearance/theme-family.js";
3
+ export declare const THEME_FAMILY_CHANGE_EVENT = "agent-native:theme-family-change";
4
+ export type ThemeFamilyStorageStatus = "valid" | "missing" | "invalid" | "unreadable";
5
+ export type StoredThemeFamily = {
6
+ status: "valid";
7
+ family: ThemeFamily;
8
+ } | {
9
+ status: "missing";
10
+ } | {
11
+ status: "invalid";
12
+ value: string;
13
+ } | {
14
+ status: "unreadable";
15
+ error: Error;
16
+ };
17
+ export interface ThemeFamilyApplyResult {
18
+ family: ThemeFamily;
19
+ storageStatus: "saved" | "unavailable";
20
+ }
21
+ export interface ThemeFamilyPreference {
22
+ family: ThemeFamily;
23
+ source: "default" | "user-setting";
24
+ appId: string | null;
25
+ }
26
+ export declare function readStoredThemeFamily(appId?: string | null): StoredThemeFamily;
27
+ export declare function applyThemeFamily(family: ThemeFamily, appId?: string | null): ThemeFamilyApplyResult;
28
+ export interface ThemeFamilyHook {
29
+ family: ThemeFamily;
30
+ status: "loading" | "success" | "error";
31
+ error?: Error;
32
+ storageStatus: ThemeFamilyStorageStatus;
33
+ setFamily: (family: ThemeFamily) => Promise<ThemeFamily>;
34
+ }
35
+ export declare function useThemeFamily(): ThemeFamilyHook;
@@ -0,0 +1,223 @@
1
+ import { useCallback, useEffect, useRef, useState, useSyncExternalStore, } from "react";
2
+ import { DEFAULT_THEME_FAMILY, isThemeFamily, THEME_FAMILY_ATTRIBUTE, THEME_FAMILY_STATUS_ATTRIBUTE, themeFamilyAppStateKey, themeFamilyStorageKey, } from "../appearance/theme-family.js";
3
+ import { injectedAgentNativeAppId } from "./app-config.js";
4
+ import { readClientAppState } from "./application-state.js";
5
+ import { useActionMutation, useActionQuery } from "./use-action.js";
6
+ import { useChangeVersion } from "./use-change-version.js";
7
+ export { DEFAULT_THEME_FAMILY, isThemeFamily, THEME_FAMILY_APP_STATE_KEY, THEME_FAMILY_ATTRIBUTE, THEME_FAMILY_STORAGE_KEY, themeFamilyAppStateKey, themeFamilyStorageKey, } from "../appearance/theme-family.js";
8
+ export const THEME_FAMILY_CHANGE_EVENT = "agent-native:theme-family-change";
9
+ const familySnapshots = new Map();
10
+ function resolvedAppId(appId) {
11
+ if (appId !== undefined)
12
+ return appId ?? undefined;
13
+ return injectedAgentNativeAppId() ?? undefined;
14
+ }
15
+ function scopeKey(appId) {
16
+ return themeFamilyStorageKey(resolvedAppId(appId));
17
+ }
18
+ function asError(error) {
19
+ return error instanceof Error ? error : new Error(String(error));
20
+ }
21
+ export function readStoredThemeFamily(appId) {
22
+ if (typeof window === "undefined")
23
+ return { status: "missing" };
24
+ let value;
25
+ try {
26
+ value = window.localStorage.getItem(themeFamilyStorageKey(resolvedAppId(appId)));
27
+ }
28
+ catch (error) {
29
+ return { status: "unreadable", error: asError(error) };
30
+ }
31
+ if (value === null)
32
+ return { status: "missing" };
33
+ if (isThemeFamily(value))
34
+ return { status: "valid", family: value };
35
+ return { status: "invalid", value };
36
+ }
37
+ function getFamilySnapshot(appId) {
38
+ return familySnapshots.get(scopeKey(appId)) ?? DEFAULT_THEME_FAMILY;
39
+ }
40
+ function subscribe(onChange, appId) {
41
+ if (typeof window === "undefined")
42
+ return () => { };
43
+ const refreshFromStorage = () => {
44
+ const stored = readStoredThemeFamily(appId);
45
+ const key = scopeKey(appId);
46
+ if (stored.status === "valid") {
47
+ familySnapshots.set(key, stored.family);
48
+ window.document.documentElement.setAttribute(THEME_FAMILY_ATTRIBUTE, stored.family);
49
+ window.document.documentElement.removeAttribute(THEME_FAMILY_STATUS_ATTRIBUTE);
50
+ }
51
+ else if (stored.status === "missing" || stored.status === "invalid") {
52
+ familySnapshots.set(key, DEFAULT_THEME_FAMILY);
53
+ window.document.documentElement.setAttribute(THEME_FAMILY_ATTRIBUTE, DEFAULT_THEME_FAMILY);
54
+ if (stored.status === "invalid") {
55
+ window.document.documentElement.setAttribute(THEME_FAMILY_STATUS_ATTRIBUTE, "invalid");
56
+ }
57
+ }
58
+ onChange();
59
+ };
60
+ const onStorage = (event) => {
61
+ if (event.key === null || event.key === scopeKey(appId)) {
62
+ refreshFromStorage();
63
+ }
64
+ };
65
+ const onFamilyChange = (event) => {
66
+ const storageKey = event.detail
67
+ ?.storageKey;
68
+ if (storageKey === scopeKey(appId))
69
+ refreshFromStorage();
70
+ };
71
+ window.addEventListener("storage", onStorage);
72
+ window.addEventListener(THEME_FAMILY_CHANGE_EVENT, onFamilyChange);
73
+ return () => {
74
+ window.removeEventListener("storage", onStorage);
75
+ window.removeEventListener(THEME_FAMILY_CHANGE_EVENT, onFamilyChange);
76
+ };
77
+ }
78
+ export function applyThemeFamily(family, appId) {
79
+ if (!isThemeFamily(family)) {
80
+ throw new TypeError(`Unknown theme family: ${String(family)}`);
81
+ }
82
+ const storageAppId = resolvedAppId(appId);
83
+ const key = themeFamilyStorageKey(storageAppId);
84
+ familySnapshots.set(key, family);
85
+ if (typeof window === "undefined") {
86
+ return { family, storageStatus: "unavailable" };
87
+ }
88
+ const root = window.document.documentElement;
89
+ root.setAttribute(THEME_FAMILY_ATTRIBUTE, family);
90
+ try {
91
+ window.localStorage.setItem(key, family);
92
+ root.removeAttribute(THEME_FAMILY_STATUS_ATTRIBUTE);
93
+ window.dispatchEvent(new CustomEvent(THEME_FAMILY_CHANGE_EVENT, {
94
+ detail: { storageKey: key },
95
+ }));
96
+ return { family, storageStatus: "saved" };
97
+ }
98
+ catch {
99
+ root.setAttribute(THEME_FAMILY_STATUS_ATTRIBUTE, "unreadable");
100
+ window.dispatchEvent(new CustomEvent(THEME_FAMILY_CHANGE_EVENT, {
101
+ detail: { storageKey: key },
102
+ }));
103
+ return { family, storageStatus: "unavailable" };
104
+ }
105
+ }
106
+ function storageStatusForApply(result) {
107
+ return result.storageStatus === "saved" ? "valid" : "unreadable";
108
+ }
109
+ function errorForState(value) {
110
+ return new Error(`The open app's theme-family state is invalid: ${JSON.stringify(value)}`);
111
+ }
112
+ export function useThemeFamily() {
113
+ const runtimeAppId = injectedAgentNativeAppId();
114
+ const query = useActionQuery("get-theme-family", undefined, { retry: false, queryKeyScope: ["theme-family", runtimeAppId] });
115
+ const mutation = useActionMutation("set-theme-family", { skipActionQueryInvalidation: true });
116
+ const appId = query.data === undefined ? runtimeAppId : query.data.appId;
117
+ const appStateKey = themeFamilyAppStateKey(appId ?? undefined);
118
+ const appStateVersion = useChangeVersion(`app-state:${appStateKey}`);
119
+ const [storageStatus, setStorageStatus] = useState(() => readStoredThemeFamily(appId).status);
120
+ const [mutationError, setMutationError] = useState();
121
+ const mutationPending = useRef(false);
122
+ const family = useSyncExternalStore((onChange) => subscribe(onChange, appId), () => getFamilySnapshot(appId), () => DEFAULT_THEME_FAMILY);
123
+ useEffect(() => {
124
+ if (mutationPending.current)
125
+ return;
126
+ if (query.data === undefined)
127
+ return;
128
+ if (!isThemeFamily(query.data?.family)) {
129
+ setMutationError(errorForState(query.data));
130
+ return;
131
+ }
132
+ const applied = applyThemeFamily(query.data.family, query.data.appId);
133
+ setStorageStatus(storageStatusForApply(applied));
134
+ setMutationError(undefined);
135
+ }, [mutationPending, query.data]);
136
+ useEffect(() => {
137
+ if (appStateVersion === 0)
138
+ return;
139
+ let current = true;
140
+ void readClientAppState(appStateKey)
141
+ .then((state) => {
142
+ if (!current || state === null)
143
+ return;
144
+ if (!isThemeFamily(state.family)) {
145
+ setMutationError(errorForState(state));
146
+ return;
147
+ }
148
+ const applied = applyThemeFamily(state.family, appId);
149
+ setStorageStatus(storageStatusForApply(applied));
150
+ setMutationError(undefined);
151
+ })
152
+ .catch((error) => {
153
+ if (current)
154
+ setMutationError(asError(error));
155
+ });
156
+ return () => {
157
+ current = false;
158
+ };
159
+ }, [appId, appStateKey, appStateVersion]);
160
+ const setFamily = useCallback(async (nextFamily) => {
161
+ if (!isThemeFamily(nextFamily)) {
162
+ throw new TypeError(`Unknown theme family: ${String(nextFamily)}`);
163
+ }
164
+ const previousAppId = appId;
165
+ const previousFamily = getFamilySnapshot(previousAppId);
166
+ mutationPending.current = true;
167
+ const optimistic = applyThemeFamily(nextFamily, previousAppId);
168
+ setStorageStatus(storageStatusForApply(optimistic));
169
+ setMutationError(undefined);
170
+ try {
171
+ const result = await mutation.mutateAsync({ family: nextFamily });
172
+ if (!isThemeFamily(result?.family)) {
173
+ throw errorForState(result);
174
+ }
175
+ if (result.appId !== null && typeof result.appId !== "string") {
176
+ throw errorForState(result);
177
+ }
178
+ if (result.family !== nextFamily) {
179
+ throw new Error("The saved theme family did not match the selection.");
180
+ }
181
+ mutationPending.current = false;
182
+ const applied = applyThemeFamily(result.family, result.appId);
183
+ setStorageStatus(storageStatusForApply(applied));
184
+ return result.family;
185
+ }
186
+ catch (error) {
187
+ const normalized = asError(error);
188
+ let reconciledFamily = previousFamily;
189
+ let reconciledAppId = previousAppId;
190
+ let reconciliationError;
191
+ try {
192
+ const refreshed = await query.refetch();
193
+ const data = refreshed.data;
194
+ if (refreshed.isError) {
195
+ reconciliationError = asError(refreshed.error);
196
+ }
197
+ else if (!data ||
198
+ !isThemeFamily(data.family) ||
199
+ (data.appId !== null && typeof data.appId !== "string")) {
200
+ reconciliationError = errorForState(data);
201
+ }
202
+ else {
203
+ reconciledFamily = data.family;
204
+ reconciledAppId = data.appId;
205
+ }
206
+ }
207
+ catch (refreshError) {
208
+ reconciliationError = asError(refreshError);
209
+ }
210
+ mutationPending.current = false;
211
+ const reconciled = applyThemeFamily(reconciledFamily, reconciledAppId);
212
+ setStorageStatus(storageStatusForApply(reconciled));
213
+ const failure = reconciliationError
214
+ ? new AggregateError([normalized, reconciliationError], "The theme family update failed and its saved value could not be reloaded.")
215
+ : normalized;
216
+ setMutationError(failure);
217
+ throw failure;
218
+ }
219
+ }, [appId, mutation]);
220
+ const error = mutationError ?? (query.isError ? asError(query.error) : undefined);
221
+ const status = error ? "error" : query.isSuccess ? "success" : "loading";
222
+ return { family, status, error, storageStatus, setFamily };
223
+ }
@@ -15,5 +15,5 @@ export interface NormalizedEmbeddedThemeUpdate {
15
15
  export declare function parseEmbeddedThemeUpdate(value: unknown): NormalizedEmbeddedThemeUpdate | null;
16
16
  export declare function buildEmbeddedThemeUpdate(theme: ResolvedTheme, vars?: Record<string, string>): EmbeddedThemeUpdate;
17
17
  export declare function applyEmbeddedThemeUpdate(root: HTMLElement, update: NormalizedEmbeddedThemeUpdate): void;
18
- export declare function getThemeInitScript(defaultTheme?: ThemePreference, enableSystem?: boolean): string;
18
+ export declare function getThemeInitScript(defaultTheme?: ThemePreference, enableSystem?: boolean, appId?: string): string;
19
19
  export declare const themeInitScript: string;
@@ -1,4 +1,6 @@
1
+ import { getThemeFamilyInitScript } from "../appearance/theme-family.js";
1
2
  import { THEME_VAR_NAMES } from "../extensions/theme.js";
3
+ import { injectedAgentNativeAppId } from "./app-config.js";
2
4
  import { getViteDevRecoveryScript, shouldInlineViteDevRecoveryScript, } from "./vite-dev-recovery-script.js";
3
5
  export const EMBEDDED_THEME_UPDATE_MESSAGE = "agent-native-theme-update";
4
6
  export const EMBEDDED_THEME_CHANGE_EVENT = "agent-native:theme-change";
@@ -60,10 +62,11 @@ function normalizeDefaultTheme(theme) {
60
62
  }
61
63
  return "system";
62
64
  }
63
- export function getThemeInitScript(defaultTheme = "system", enableSystem = true) {
65
+ export function getThemeInitScript(defaultTheme = "system", enableSystem = true, appId) {
64
66
  const safeDefaultTheme = normalizeDefaultTheme(defaultTheme);
65
67
  const systemEnabled = enableSystem ? "true" : "false";
66
- const themeScript = `(function(){function m(){var d={};return{get length(){return Object.keys(d).length},key:function(i){return Object.keys(d)[i]||null},getItem:function(k){k=String(k);return Object.prototype.hasOwnProperty.call(d,k)?d[k]:null},setItem:function(k,v){d[String(k)]=String(v)},removeItem:function(k){delete d[String(k)]},clear:function(){d={}}}}function s(n){try{var x=window[n],p='__an_storage_probe__';x.setItem(p,'1');x.removeItem(p)}catch(e){try{Object.defineProperty(window,n,{configurable:true,value:m()})}catch(_){}}}s('localStorage');s('sessionStorage');try{var defaultTheme=${JSON.stringify(safeDefaultTheme)};var enableSystem=${systemEnabled};var stored=window.localStorage.getItem('theme');var valid=stored==='light'||stored==='dark'||stored==='system'||stored==='auto';var mode=valid?stored:defaultTheme;if(mode==='auto')mode='system';if(!enableSystem&&mode==='system')mode=defaultTheme==='system'?'light':defaultTheme;if(!valid){window.localStorage.removeItem('theme')}else if(stored!==mode){window.localStorage.setItem('theme',mode)}var prefersDark=enableSystem&&mode==='system'&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches;var resolved=mode==='system'?(prefersDark?'dark':'light'):mode;var root=document.documentElement;root.classList.remove('light','dark');root.classList.add(resolved);root.setAttribute('data-theme',resolved);root.style.colorScheme=resolved;var appearance=window.localStorage.getItem('appearance');var appearanceValid=appearance==='warm'||appearance==='ocean'||appearance==='forest'||appearance==='rose'||appearance==='slate';if(appearanceValid){root.setAttribute('data-appearance',appearance)}else{root.removeAttribute('data-appearance');if(appearance!==null)window.localStorage.removeItem('appearance')}}catch(e){}})();`;
68
+ const themeFamilyAppId = appId ?? injectedAgentNativeAppId() ?? undefined;
69
+ const themeScript = `${getThemeFamilyInitScript(themeFamilyAppId)}\n(function(){function m(){var d={};return{get length(){return Object.keys(d).length},key:function(i){return Object.keys(d)[i]||null},getItem:function(k){k=String(k);return Object.prototype.hasOwnProperty.call(d,k)?d[k]:null},setItem:function(k,v){d[String(k)]=String(v)},removeItem:function(k){delete d[k]},clear:function(){d={}}}}function s(n){try{var x=window[n],p='__an_storage_probe__';x.setItem(p,'1');x.removeItem(p)}catch(e){try{Object.defineProperty(window,n,{configurable:true,value:m()})}catch(_){}}}s('localStorage');s('sessionStorage');try{var defaultTheme=${JSON.stringify(safeDefaultTheme)};var enableSystem=${systemEnabled};var stored=window.localStorage.getItem('theme');var valid=stored==='light'||stored==='dark'||stored==='system'||stored==='auto';var mode=valid?stored:defaultTheme;if(mode==='auto')mode='system';if(!enableSystem&&mode==='system')mode=defaultTheme==='system'?'light':defaultTheme;if(!valid){window.localStorage.removeItem('theme')}else if(stored!==mode){window.localStorage.setItem('theme',mode)}var prefersDark=enableSystem&&mode==='system'&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches;var resolved=mode==='system'?(prefersDark?'dark':'light'):mode;var root=document.documentElement;root.classList.remove('light','dark');root.classList.add(resolved);root.setAttribute('data-theme',resolved);root.style.colorScheme=resolved;var appearance=window.localStorage.getItem('appearance');var appearanceValid=appearance==='warm'||appearance==='ocean'||appearance==='forest'||appearance==='rose'||appearance==='slate';if(appearanceValid){root.setAttribute('data-appearance',appearance)}else{root.removeAttribute('data-appearance');if(appearance!==null)window.localStorage.removeItem('appearance')}}catch(e){document.documentElement.setAttribute('data-theme-status','unreadable')}})();`;
67
70
  if (!shouldInlineViteDevRecoveryScript())
68
71
  return themeScript;
69
72
  return `${themeScript}\n${getViteDevRecoveryScript()}`;
@@ -2,3 +2,4 @@ export { buildErrorReportTemplate, buildGitHubIssueUrl, type ErrorReportDebugIte
2
2
  export { getClientSurface, type ClientSurface } from "../client-surface.js";
3
3
  export { applyEmbeddedThemeUpdate, buildEmbeddedThemeUpdate, EMBEDDED_THEME_CHANGE_EVENT, EMBEDDED_THEME_UPDATE_MESSAGE, getThemeInitScript, parseEmbeddedThemeUpdate, themeInitScript, type EmbeddedThemeUpdate, type NormalizedEmbeddedThemeUpdate, type ResolvedTheme, type ThemePreference, } from "../theme.js";
4
4
  export { APPEARANCE_PRESETS, applyAppearance, getStoredAppearance, useAppearance, useAppearanceSync, type AppearancePresetId, } from "../appearance.js";
5
+ export { applyThemeFamily, readStoredThemeFamily, THEME_FAMILY_CHANGE_EVENT, useThemeFamily, type StoredThemeFamily, type ThemeFamily, type ThemeFamilyApplyResult, type ThemeFamilyHook, type ThemeFamilyStorageStatus, } from "../theme-family.js";
@@ -2,3 +2,4 @@ export { buildErrorReportTemplate, buildGitHubIssueUrl, } from "../error-reporti
2
2
  export { getClientSurface } from "../client-surface.js";
3
3
  export { applyEmbeddedThemeUpdate, buildEmbeddedThemeUpdate, EMBEDDED_THEME_CHANGE_EVENT, EMBEDDED_THEME_UPDATE_MESSAGE, getThemeInitScript, parseEmbeddedThemeUpdate, themeInitScript, } from "../theme.js";
4
4
  export { APPEARANCE_PRESETS, applyAppearance, getStoredAppearance, useAppearance, useAppearanceSync, } from "../appearance.js";
5
+ export { applyThemeFamily, readStoredThemeFamily, THEME_FAMILY_CHANGE_EVENT, useThemeFamily, } from "../theme-family.js";
@@ -38,19 +38,19 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
38
38
  error: string;
39
39
  states?: undefined;
40
40
  } | {
41
+ error?: undefined;
41
42
  states: {
42
43
  clientId: number;
43
44
  state: string;
44
45
  }[];
45
- error?: undefined;
46
46
  }>>;
47
47
  export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
48
48
  error: string;
49
49
  users?: undefined;
50
50
  } | {
51
+ error?: undefined;
51
52
  users: {
52
53
  clientId: number;
53
54
  lastSeen: number;
54
55
  }[];
55
- error?: undefined;
56
56
  }>>;
@@ -13,12 +13,12 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
13
13
  };
14
14
  }>>;
15
15
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
- error?: undefined;
20
19
  liveCopy: "derived" | "not_derived" | "unchanged";
21
20
  liveCopyReason?: string;
21
+ error?: undefined;
22
22
  ok: boolean;
23
23
  } | {
24
24
  error?: undefined;
@@ -1,6 +1,6 @@
1
1
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
- ok?: undefined;
3
2
  error: string;
3
+ ok?: undefined;
4
4
  } | {
5
5
  error?: undefined;
6
6
  ok: boolean;
@@ -65,7 +65,7 @@ export declare const WORKSPACE_CONNECTION_PROVIDERS: readonly [{
65
65
  readonly scopes: readonly ["repo", "read:org", "read:user", "user:email"];
66
66
  };
67
67
  readonly capabilities: readonly ["search", "import", "code", "docs"];
68
- readonly recommendedTemplateUses: readonly ["brain", "analytics", "dispatch", "factory"];
68
+ readonly recommendedTemplateUses: readonly ["brain", "analytics", "content", "dispatch", "factory"];
69
69
  }, {
70
70
  readonly id: "figma";
71
71
  readonly label: "Figma";
@@ -59,7 +59,13 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
59
59
  scopes: ["repo", "read:org", "read:user", "user:email"],
60
60
  },
61
61
  capabilities: ["search", "import", "code", "docs"],
62
- recommendedTemplateUses: ["brain", "analytics", "dispatch", "factory"],
62
+ recommendedTemplateUses: [
63
+ "brain",
64
+ "analytics",
65
+ "content",
66
+ "dispatch",
67
+ "factory",
68
+ ],
63
69
  }),
64
70
  defineWorkspaceConnectionProvider({
65
71
  id: "figma",
@@ -5,6 +5,7 @@ import { deleteAppSecret, readAppSecret, } from "../secrets/storage.js";
5
5
  import { assertCredentialStoreReadable } from "../server/credential-provider.js";
6
6
  import { isPersonalCredentialScope, readsOrgCredentialFirst, } from "../server/credential-read-order.js";
7
7
  import { isPersonalProviderKeyUseRestricted, isPersonalProviderPolicyKey, readOrgMemberRole, } from "../server/personal-provider-key-policy.js";
8
+ import { getRequestContext } from "../server/request-context.js";
8
9
  import { getSetting, putSetting, deleteSetting } from "../settings/store.js";
9
10
  const SETTING_PREFIX = "credential:";
10
11
  export class CredentialEndpointMismatchError extends Error {
@@ -239,7 +240,20 @@ export async function resolveCredential(key, ctx) {
239
240
  export async function describeCredentialScopeGap(keys, ctx) {
240
241
  if (!ctx?.userEmail)
241
242
  return null;
242
- const orgLookup = await resolveEffectiveOrgId(ctx);
243
+ const requestContext = getRequestContext();
244
+ const isDelegatedOrgMember = requestContext?.a2aIdentityAssurance === "org-member";
245
+ const delegatedEmail = requestContext?.userEmail?.trim().toLowerCase();
246
+ const delegatedOrgId = requestContext?.orgId?.trim();
247
+ if (isDelegatedOrgMember &&
248
+ (!delegatedEmail ||
249
+ delegatedEmail !== ctx.userEmail.trim().toLowerCase() ||
250
+ !delegatedOrgId ||
251
+ (ctx.orgId && ctx.orgId.trim() !== delegatedOrgId))) {
252
+ return null;
253
+ }
254
+ const orgLookup = isDelegatedOrgMember
255
+ ? { orgId: delegatedOrgId, lookupFailed: false }
256
+ : await resolveEffectiveOrgId(ctx);
243
257
  if (orgLookup.lookupFailed || !orgLookup.orgId)
244
258
  return null;
245
259
  const scopedCtx = { ...ctx, orgId: orgLookup.orgId };
@@ -251,7 +265,9 @@ export async function describeCredentialScopeGap(keys, ctx) {
251
265
  `integration, job, or automation — so it needs "${key}" saved with ` +
252
266
  `Workspace or Organization scope instead.`);
253
267
  }
254
- const holder = await findMemberOrgHoldingCredential(key, scopedCtx);
268
+ const holder = isDelegatedOrgMember
269
+ ? null
270
+ : await findMemberOrgHoldingCredential(key, scopedCtx);
255
271
  if (holder) {
256
272
  return (`A "${key}" key is saved in the ${holder} organization, but this ` +
257
273
  `request resolved to a different organization you also belong to. ` +