@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.
- package/dist/a2a/anthropic-managed-agents.js +1 -0
- package/dist/a2a/caller-auth.js +7 -2
- package/dist/a2a/client.d.ts +1 -0
- package/dist/a2a/client.js +27 -0
- package/dist/a2a/handlers.js +15 -3
- package/dist/a2a/organization-identity.d.ts +4 -1
- package/dist/a2a/organization-identity.js +3 -1
- package/dist/a2a/server.d.ts +6 -5
- package/dist/a2a/server.js +79 -6
- package/dist/a2a-claims.d.ts +1 -0
- package/dist/a2a-claims.js +1 -0
- package/dist/agent/actions/manage-provider-key-policy.js +4 -2
- package/dist/agent/app-model-defaults.js +2 -10
- package/dist/agent/loop-settings.js +2 -10
- package/dist/appearance/actions/get-theme-family.d.ts +8 -0
- package/dist/appearance/actions/get-theme-family.js +38 -0
- package/dist/appearance/actions/set-theme-family.d.ts +7 -0
- package/dist/appearance/actions/set-theme-family.js +33 -0
- package/dist/appearance/theme-family.d.ts +13 -0
- package/dist/appearance/theme-family.js +38 -0
- package/dist/audit/actions/export-audit-events.js +1 -0
- package/dist/audit/attribution.d.ts +5 -4
- package/dist/audit/attribution.js +2 -1
- package/dist/audit/index.d.ts +1 -1
- package/dist/audit/ocsf.js +1 -0
- package/dist/audit/read-scope.js +2 -12
- package/dist/audit/record.js +1 -0
- package/dist/audit/store.js +12 -3
- package/dist/audit/types.d.ts +4 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/automations/service.js +3 -10
- package/dist/client/org/hooks.d.ts +3 -0
- package/dist/client/org/hooks.js +12 -0
- package/dist/client/theme-family.d.ts +35 -0
- package/dist/client/theme-family.js +223 -0
- package/dist/client/theme.d.ts +1 -1
- package/dist/client/theme.js +5 -2
- package/dist/client/ui/index.d.ts +1 -0
- package/dist/client/ui/index.js +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +1 -1
- package/dist/connections/catalog.js +7 -1
- package/dist/credentials/index.js +18 -2
- package/dist/data-widgets/index.d.ts +339 -4
- package/dist/data-widgets/index.js +150 -7
- package/dist/db/schema-snapshot.generated.js +5 -2
- package/dist/feature-flags/a2a-action-route.js +1 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/storage-settings.js +2 -9
- package/dist/framework-tools.js +2 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/jobs/tools.js +2 -12
- package/dist/mcp/actions/service-token-access.js +15 -11
- package/dist/mcp/credential-issuance.js +9 -2
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/org/actions.d.ts +1 -1
- package/dist/org/actions.js +3 -7
- package/dist/org/app-roles.js +10 -1
- package/dist/org/context.d.ts +1 -0
- package/dist/org/context.js +122 -24
- package/dist/org/federation.d.ts +1 -1
- package/dist/org/federation.js +2 -2
- package/dist/org/handlers.d.ts +4 -0
- package/dist/org/handlers.js +140 -44
- package/dist/org/index.d.ts +2 -1
- package/dist/org/membership.d.ts +6 -4
- package/dist/org/membership.js +7 -5
- package/dist/org/migrations.js +6 -0
- package/dist/org/plugin.d.ts +1 -0
- package/dist/org/plugin.js +9 -1
- package/dist/org/private-icon-handlers.js +4 -10
- package/dist/org/types.d.ts +2 -0
- package/dist/org/workspace-app-access.js +62 -14
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/action-discovery.js +8 -0
- package/dist/server/action-routes.d.ts +1 -0
- package/dist/server/action-routes.js +13 -1
- package/dist/server/agent-chat/context-tools.js +43 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/org-admin.js +3 -2
- package/dist/server/personal-provider-key-holders.d.ts +1 -1
- package/dist/server/personal-provider-key-holders.js +7 -2
- package/dist/server/personal-provider-key-policy.d.ts +2 -0
- package/dist/server/personal-provider-key-policy.js +21 -5
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.js +7 -0
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +16 -13
- package/dist/triggers/actions/manage-automation.d.ts +4 -4
- package/dist/triggers/routes.js +2 -9
- package/dist/usage/alerts-store.js +2 -9
- package/dist/usage/metrics-store.js +2 -9
- package/dist/workspace-connections/actions/list-workspace-user-groups.d.ts +1 -1
- package/dist/workspace-connections/groups.js +2 -11
- package/package.json +3 -3
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +16 -13
package/dist/audit/ocsf.js
CHANGED
|
@@ -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
|
{
|
package/dist/audit/read-scope.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/audit/record.js
CHANGED
|
@@ -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 &&
|
package/dist/audit/store.js
CHANGED
|
@@ -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
|
}
|
package/dist/audit/types.d.ts
CHANGED
|
@@ -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
|
|
65
|
-
|
|
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;
|
package/dist/client/org/hooks.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/client/theme.d.ts
CHANGED
|
@@ -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;
|
package/dist/client/theme.js
CHANGED
|
@@ -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
|
|
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";
|
package/dist/client/ui/index.js
CHANGED
|
@@ -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
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -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;
|
|
@@ -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: [
|
|
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
|
|
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 =
|
|
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. ` +
|