@asiyst/sdk 0.1.6 → 0.1.9

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/index.d.cts CHANGED
@@ -69,7 +69,7 @@ type AsiystEventName = keyof AsiystEventMap;
69
69
 
70
70
  type AnchorPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
71
71
  type AssistantMode = "guided" | "assist";
72
- type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
72
+ type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open" | "close" | "filter" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
73
73
  type HighlightStyle = "spotlight" | "outline" | "glow" | "pointer" | "pulse" | "dim";
74
74
  type ElementKind = "button" | "link" | "input" | "select" | "textarea" | "form" | "navigation" | "heading" | "menu" | "dialog" | "tab" | "card" | "search" | "section" | "other";
75
75
  interface TargetRef {
@@ -82,6 +82,8 @@ type TargetInput = string | TargetRef;
82
82
  interface InitOptions {
83
83
  projectId: string;
84
84
  publicKey: string;
85
+ avatarId?: string;
86
+ position?: AnchorPosition;
85
87
  apiBaseUrl?: string;
86
88
  mode?: AssistantMode;
87
89
  allowedActions?: ActionKind[];
@@ -102,6 +104,28 @@ interface BehaviorConfig {
102
104
  voiceSpeed?: number;
103
105
  speechBubble?: boolean;
104
106
  }
107
+ interface AvatarRuntimeRule {
108
+ action?: string;
109
+ allowed?: boolean;
110
+ target?: string;
111
+ route?: string;
112
+ routes?: string[];
113
+ domain?: string;
114
+ domains?: string[];
115
+ enabled?: boolean;
116
+ sourceId?: string;
117
+ dataSourceId?: string;
118
+ }
119
+ interface AvatarDataSource {
120
+ id: string;
121
+ name?: string;
122
+ type?: string;
123
+ enabled?: boolean;
124
+ url?: string;
125
+ method?: string;
126
+ headers?: Record<string, string>;
127
+ credentials?: string;
128
+ }
105
129
  interface ProjectConfig {
106
130
  schemaVersion: number;
107
131
  version: number;
@@ -116,6 +140,11 @@ interface ProjectConfig {
116
140
  behavior: BehaviorConfig;
117
141
  mode: AssistantMode;
118
142
  allowedActions: ActionKind[];
143
+ allowedDomains: string[];
144
+ allowedRoutes: string[];
145
+ blockedRoutes: string[];
146
+ rules: AvatarRuntimeRule[];
147
+ dataSources: AvatarDataSource[];
119
148
  elementSelectors: Record<string, string>;
120
149
  }
121
150
  interface Rect {
@@ -210,7 +239,7 @@ declare const Asiyst: {
210
239
  off<K extends keyof AsiystEventMap>(event: K, handler: (payload: AsiystEventMap[K]) => void): void;
211
240
  };
212
241
 
213
- declare const SDK_VERSION = "0.1.5";
242
+ declare const SDK_VERSION: string;
214
243
 
215
244
  declare class AsiystError extends Error {
216
245
  readonly code: string;
@@ -263,9 +292,13 @@ declare function normalizeProjectConfig(raw: unknown): ProjectConfig;
263
292
  declare function validateInitOptions(options: {
264
293
  projectId?: unknown;
265
294
  publicKey?: unknown;
295
+ avatarId?: unknown;
296
+ position?: unknown;
266
297
  }): {
267
298
  projectId: string;
268
299
  publicKey: string;
300
+ avatarId?: string;
301
+ position?: AnchorPosition;
269
302
  };
270
303
 
271
304
  declare function isSafeSelector(selector: string): boolean;
@@ -299,4 +332,18 @@ declare function anchorToViewport(position: "bottom-right" | "bottom-left" | "to
299
332
  y: number;
300
333
  };
301
334
 
302
- export { type ActionKind, ActionNotAllowedError, Asiyst, AsiystError, type AsiystEventMap, type AsiystEventName, type AssistantMode, AuthenticationError, ConfigurationError, type HighlightStyle, type InitOptions, InitializationError, type MappedElement, NetworkError, type ProjectConfig, SDK_VERSION, type TargetInput, TargetNotFoundError, type TargetRef, type TaskDefinition, TaskExecutionError, TaskStatus, type TaskStep, type WebsiteMapSnapshot, type WorkflowDefinition, anchorToViewport, canTransition, computeAvatarDestination, fallbackConfig, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions };
335
+ declare function validateWebsiteDomain(config: ProjectConfig, currentUrl: string): {
336
+ allowed: boolean;
337
+ reason?: string;
338
+ };
339
+ declare function canNavigateTo(config: ProjectConfig, targetUrl: string, currentUrl: string): {
340
+ allowed: boolean;
341
+ reason?: string;
342
+ };
343
+ declare function evaluateActionPermission(action: ActionKind, config: ProjectConfig, currentUrl: string, target?: string): {
344
+ allowed: boolean;
345
+ reason?: string;
346
+ };
347
+ declare function isActionAllowed(action: ActionKind, config: ProjectConfig, source: "developer" | "cloud" | "local", currentUrl?: string, target?: string): boolean;
348
+
349
+ export { type ActionKind, ActionNotAllowedError, Asiyst, AsiystError, type AsiystEventMap, type AsiystEventName, type AssistantMode, AuthenticationError, ConfigurationError, type HighlightStyle, type InitOptions, InitializationError, type MappedElement, NetworkError, type ProjectConfig, SDK_VERSION, type TargetInput, TargetNotFoundError, type TargetRef, type TaskDefinition, TaskExecutionError, TaskStatus, type TaskStep, type WebsiteMapSnapshot, type WorkflowDefinition, anchorToViewport, canNavigateTo, canTransition, computeAvatarDestination, evaluateActionPermission, fallbackConfig, isActionAllowed, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions, validateWebsiteDomain };
package/dist/index.d.ts CHANGED
@@ -69,7 +69,7 @@ type AsiystEventName = keyof AsiystEventMap;
69
69
 
70
70
  type AnchorPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
71
71
  type AssistantMode = "guided" | "assist";
72
- type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
72
+ type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open" | "close" | "filter" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
73
73
  type HighlightStyle = "spotlight" | "outline" | "glow" | "pointer" | "pulse" | "dim";
74
74
  type ElementKind = "button" | "link" | "input" | "select" | "textarea" | "form" | "navigation" | "heading" | "menu" | "dialog" | "tab" | "card" | "search" | "section" | "other";
75
75
  interface TargetRef {
@@ -82,6 +82,8 @@ type TargetInput = string | TargetRef;
82
82
  interface InitOptions {
83
83
  projectId: string;
84
84
  publicKey: string;
85
+ avatarId?: string;
86
+ position?: AnchorPosition;
85
87
  apiBaseUrl?: string;
86
88
  mode?: AssistantMode;
87
89
  allowedActions?: ActionKind[];
@@ -102,6 +104,28 @@ interface BehaviorConfig {
102
104
  voiceSpeed?: number;
103
105
  speechBubble?: boolean;
104
106
  }
107
+ interface AvatarRuntimeRule {
108
+ action?: string;
109
+ allowed?: boolean;
110
+ target?: string;
111
+ route?: string;
112
+ routes?: string[];
113
+ domain?: string;
114
+ domains?: string[];
115
+ enabled?: boolean;
116
+ sourceId?: string;
117
+ dataSourceId?: string;
118
+ }
119
+ interface AvatarDataSource {
120
+ id: string;
121
+ name?: string;
122
+ type?: string;
123
+ enabled?: boolean;
124
+ url?: string;
125
+ method?: string;
126
+ headers?: Record<string, string>;
127
+ credentials?: string;
128
+ }
105
129
  interface ProjectConfig {
106
130
  schemaVersion: number;
107
131
  version: number;
@@ -116,6 +140,11 @@ interface ProjectConfig {
116
140
  behavior: BehaviorConfig;
117
141
  mode: AssistantMode;
118
142
  allowedActions: ActionKind[];
143
+ allowedDomains: string[];
144
+ allowedRoutes: string[];
145
+ blockedRoutes: string[];
146
+ rules: AvatarRuntimeRule[];
147
+ dataSources: AvatarDataSource[];
119
148
  elementSelectors: Record<string, string>;
120
149
  }
121
150
  interface Rect {
@@ -210,7 +239,7 @@ declare const Asiyst: {
210
239
  off<K extends keyof AsiystEventMap>(event: K, handler: (payload: AsiystEventMap[K]) => void): void;
211
240
  };
212
241
 
213
- declare const SDK_VERSION = "0.1.5";
242
+ declare const SDK_VERSION: string;
214
243
 
215
244
  declare class AsiystError extends Error {
216
245
  readonly code: string;
@@ -263,9 +292,13 @@ declare function normalizeProjectConfig(raw: unknown): ProjectConfig;
263
292
  declare function validateInitOptions(options: {
264
293
  projectId?: unknown;
265
294
  publicKey?: unknown;
295
+ avatarId?: unknown;
296
+ position?: unknown;
266
297
  }): {
267
298
  projectId: string;
268
299
  publicKey: string;
300
+ avatarId?: string;
301
+ position?: AnchorPosition;
269
302
  };
270
303
 
271
304
  declare function isSafeSelector(selector: string): boolean;
@@ -299,4 +332,18 @@ declare function anchorToViewport(position: "bottom-right" | "bottom-left" | "to
299
332
  y: number;
300
333
  };
301
334
 
302
- export { type ActionKind, ActionNotAllowedError, Asiyst, AsiystError, type AsiystEventMap, type AsiystEventName, type AssistantMode, AuthenticationError, ConfigurationError, type HighlightStyle, type InitOptions, InitializationError, type MappedElement, NetworkError, type ProjectConfig, SDK_VERSION, type TargetInput, TargetNotFoundError, type TargetRef, type TaskDefinition, TaskExecutionError, TaskStatus, type TaskStep, type WebsiteMapSnapshot, type WorkflowDefinition, anchorToViewport, canTransition, computeAvatarDestination, fallbackConfig, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions };
335
+ declare function validateWebsiteDomain(config: ProjectConfig, currentUrl: string): {
336
+ allowed: boolean;
337
+ reason?: string;
338
+ };
339
+ declare function canNavigateTo(config: ProjectConfig, targetUrl: string, currentUrl: string): {
340
+ allowed: boolean;
341
+ reason?: string;
342
+ };
343
+ declare function evaluateActionPermission(action: ActionKind, config: ProjectConfig, currentUrl: string, target?: string): {
344
+ allowed: boolean;
345
+ reason?: string;
346
+ };
347
+ declare function isActionAllowed(action: ActionKind, config: ProjectConfig, source: "developer" | "cloud" | "local", currentUrl?: string, target?: string): boolean;
348
+
349
+ export { type ActionKind, ActionNotAllowedError, Asiyst, AsiystError, type AsiystEventMap, type AsiystEventName, type AssistantMode, AuthenticationError, ConfigurationError, type HighlightStyle, type InitOptions, InitializationError, type MappedElement, NetworkError, type ProjectConfig, SDK_VERSION, type TargetInput, TargetNotFoundError, type TargetRef, type TaskDefinition, TaskExecutionError, TaskStatus, type TaskStep, type WebsiteMapSnapshot, type WorkflowDefinition, anchorToViewport, canNavigateTo, canTransition, computeAvatarDestination, evaluateActionPermission, fallbackConfig, isActionAllowed, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions, validateWebsiteDomain };
package/dist/index.js CHANGED
@@ -57,6 +57,9 @@ var ALL_ACTION_KINDS = [
57
57
  "scroll",
58
58
  "type",
59
59
  "select",
60
+ "open",
61
+ "close",
62
+ "filter",
60
63
  "open-menu",
61
64
  "open-modal",
62
65
  "search",
@@ -66,8 +69,8 @@ var ALL_ACTION_KINDS = [
66
69
  ];
67
70
 
68
71
  // src/core/constants.ts
69
- var SDK_VERSION = "0.1.5";
70
- var DEFAULT_API_BASE_URL = "https://asiyst.com/api/v1";
72
+ var SDK_VERSION = "0.1.9" ;
73
+ var DEFAULT_API_BASE_URL = "https://nqhxpgsjofzqudyqkqib.supabase.co/functions/v1/api";
71
74
  var CONFIG_SCHEMA_VERSION = 1;
72
75
  var HOST_ELEMENT_ID = "asiyst-host";
73
76
  var DATA_ATTR = "data-asiyst";
@@ -122,6 +125,19 @@ var ANCHORS = [
122
125
  "top-right",
123
126
  "top-left"
124
127
  ];
128
+ var PUBLIC_IDENTIFIER_PATTERN = /^(?=.{1,128}$)[A-Za-z0-9_][A-Za-z0-9_-]*$/;
129
+ function isValidPublicIdentifier(value) {
130
+ if (typeof value !== "string") return false;
131
+ const trimmed = value.trim();
132
+ if (!trimmed || trimmed.length > 128) return false;
133
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(trimmed)) {
134
+ return false;
135
+ }
136
+ return PUBLIC_IDENTIFIER_PATTERN.test(trimmed);
137
+ }
138
+ function isValidProjectId(value) {
139
+ return isValidPublicIdentifier(value);
140
+ }
125
141
  function isRecord(value) {
126
142
  return typeof value === "object" && value !== null && !Array.isArray(value);
127
143
  }
@@ -150,6 +166,88 @@ function parseAllowedActions(value, fallback) {
150
166
  }
151
167
  return next.length > 0 ? next : [...fallback];
152
168
  }
169
+ function parseStringList(value) {
170
+ if (typeof value === "string") {
171
+ return value.split(",").map((item) => sanitizeText(item.trim(), 200)).filter(Boolean);
172
+ }
173
+ if (!Array.isArray(value)) {
174
+ return [];
175
+ }
176
+ return value.filter((item) => typeof item === "string").map((item) => sanitizeText(item.trim(), 200)).filter(Boolean);
177
+ }
178
+ function parseRule(value) {
179
+ if (!isRecord(value)) return null;
180
+ const action = typeof value.action === "string" ? sanitizeText(value.action, 64) : void 0;
181
+ if (action && !ALL_ACTION_KINDS.includes(action)) {
182
+ return null;
183
+ }
184
+ const routeList = parseStringList(value.routes ?? value.route);
185
+ const domainList = parseStringList(value.domains ?? value.domain);
186
+ const hasAllowedValue = typeof value.allowed === "boolean";
187
+ const hasAnySemanticField = hasAllowedValue || typeof value.target === "string" || typeof value.route === "string" || routeList.length > 0 || typeof value.domain === "string" || domainList.length > 0 || typeof value.enabled === "boolean" || typeof value.sourceId === "string" || typeof value.dataSourceId === "string";
188
+ if (!hasAnySemanticField) {
189
+ return null;
190
+ }
191
+ const rule = {
192
+ action,
193
+ allowed: typeof value.allowed === "boolean" ? value.allowed : void 0,
194
+ target: typeof value.target === "string" ? sanitizeText(value.target, 200) : void 0,
195
+ route: typeof value.route === "string" ? sanitizeText(value.route, 200) : void 0,
196
+ routes: routeList,
197
+ domain: typeof value.domain === "string" ? sanitizeText(value.domain, 200) : void 0,
198
+ domains: domainList,
199
+ enabled: typeof value.enabled === "boolean" ? value.enabled : void 0,
200
+ sourceId: typeof value.sourceId === "string" ? sanitizeText(value.sourceId, 128) : void 0,
201
+ dataSourceId: typeof value.dataSourceId === "string" ? sanitizeText(value.dataSourceId, 128) : void 0
202
+ };
203
+ if (typeof value.allowed !== "undefined" && typeof value.allowed !== "boolean") {
204
+ return null;
205
+ }
206
+ return rule;
207
+ }
208
+ function parseRules(value) {
209
+ if (!Array.isArray(value)) {
210
+ return [];
211
+ }
212
+ const rules = [];
213
+ for (const entry of value) {
214
+ const rule = parseRule(entry);
215
+ if (rule) {
216
+ rules.push(rule);
217
+ }
218
+ }
219
+ return rules;
220
+ }
221
+ function parseDataSources(value) {
222
+ if (!Array.isArray(value)) {
223
+ return [];
224
+ }
225
+ const next = [];
226
+ for (const entry of value) {
227
+ if (!isRecord(entry)) continue;
228
+ const id = typeof entry.id === "string" ? sanitizeText(entry.id, 128) : "";
229
+ if (!id) continue;
230
+ next.push({
231
+ id,
232
+ name: typeof entry.name === "string" ? sanitizeText(entry.name, 128) : void 0,
233
+ type: typeof entry.type === "string" ? sanitizeText(entry.type, 64) : void 0,
234
+ enabled: typeof entry.enabled === "boolean" ? entry.enabled : void 0,
235
+ url: typeof entry.url === "string" ? sanitizeText(entry.url, 400) : void 0,
236
+ method: typeof entry.method === "string" ? sanitizeText(entry.method, 20) : void 0,
237
+ headers: isRecord(entry.headers) ? (() => {
238
+ const safeHeaders = {};
239
+ for (const [key, value2] of Object.entries(entry.headers)) {
240
+ if (typeof value2 === "string") {
241
+ safeHeaders[sanitizeText(key, 64)] = sanitizeText(value2, 200);
242
+ }
243
+ }
244
+ return safeHeaders;
245
+ })() : void 0,
246
+ credentials: typeof entry.credentials === "string" ? "[redacted]" : void 0
247
+ });
248
+ }
249
+ return next;
250
+ }
153
251
  function parseTheme(value) {
154
252
  if (!isRecord(value)) {
155
253
  return {};
@@ -220,6 +318,11 @@ function fallbackConfig() {
220
318
  },
221
319
  mode: "guided",
222
320
  allowedActions: ["navigate", "highlight", "scroll", "wait", "explain", "complete"],
321
+ allowedDomains: [],
322
+ allowedRoutes: [],
323
+ blockedRoutes: [],
324
+ rules: [],
325
+ dataSources: [],
223
326
  elementSelectors: {}
224
327
  };
225
328
  }
@@ -243,22 +346,41 @@ function normalizeProjectConfig(raw) {
243
346
  behavior: { ...base.behavior, ...parseBehavior(raw.behavior) },
244
347
  mode: raw.mode === "assist" ? "assist" : "guided",
245
348
  allowedActions: parseAllowedActions(raw.allowedActions, base.allowedActions),
349
+ allowedDomains: parseStringList(raw.allowedDomains ?? raw.allowed_domains),
350
+ allowedRoutes: parseStringList(raw.allowedRoutes ?? raw.allowed_routes),
351
+ blockedRoutes: parseStringList(raw.blockedRoutes ?? raw.blocked_routes),
352
+ rules: parseRules(raw.rules ?? raw.avatarRules),
353
+ dataSources: parseDataSources(raw.dataSources ?? raw.enabledDataSources),
246
354
  elementSelectors: parseElementSelectors(raw.elementSelectors)
247
355
  };
248
356
  }
249
357
  function validateInitOptions(options) {
250
- if (typeof options.projectId !== "string" || !options.projectId.trim()) {
251
- throw new ConfigurationError("projectId is required");
358
+ const projectIdValue = typeof options.projectId === "string" ? options.projectId.trim() : "";
359
+ if (!projectIdValue) {
360
+ throw new ConfigurationError("Asiyst SDK: projectId is required.");
361
+ }
362
+ if (!isValidProjectId(projectIdValue)) {
363
+ throw new ConfigurationError("Asiyst SDK: projectId is invalid. Use a public Project ID.");
252
364
  }
253
365
  if (typeof options.publicKey !== "string" || !options.publicKey.trim()) {
254
366
  throw new ConfigurationError("publicKey is required");
255
367
  }
256
- if (options.projectId.length > 128 || options.publicKey.length > 256) {
368
+ if (projectIdValue.length > 128 || options.publicKey.trim().length > 256) {
257
369
  throw new ConfigurationError("project credentials exceed allowed length");
258
370
  }
371
+ const avatarId = typeof options.avatarId === "string" ? options.avatarId.trim() : "";
372
+ if (options.avatarId !== void 0 && (!avatarId || avatarId.length > 128 || !/^[A-Za-z0-9_-]+$/.test(avatarId))) {
373
+ throw new ConfigurationError("Asiyst SDK: avatarId is invalid.");
374
+ }
375
+ const position = options.position === void 0 ? void 0 : ANCHORS.includes(options.position) ? options.position : void 0;
376
+ if (options.position !== void 0 && !position) {
377
+ throw new ConfigurationError("Asiyst SDK: position is invalid.");
378
+ }
259
379
  return {
260
- projectId: options.projectId.trim(),
261
- publicKey: options.publicKey.trim()
380
+ projectId: projectIdValue,
381
+ publicKey: options.publicKey.trim(),
382
+ avatarId: avatarId || void 0,
383
+ position
262
384
  };
263
385
  }
264
386
 
@@ -309,6 +431,7 @@ var HttpTransport = class {
309
431
  "Content-Type": "application/json",
310
432
  "X-Asiyst-Project-Id": this.options.projectId,
311
433
  "X-Asiyst-Public-Key": this.options.publicKey,
434
+ ...this.options.avatarId ? { "X-Asiyst-Avatar-Id": this.options.avatarId } : {},
312
435
  "X-Asiyst-SDK-Version": SDK_VERSION
313
436
  },
314
437
  body: req.body === void 0 ? void 0 : JSON.stringify(req.body),
@@ -538,6 +661,7 @@ var ConfigManager = class {
538
661
  return {
539
662
  ...base,
540
663
  mode: this.options.mode ?? base.mode,
664
+ position: this.options.position ?? base.position,
541
665
  allowedActions: this.options.allowedActions ?? base.allowedActions
542
666
  };
543
667
  }
@@ -1635,14 +1759,110 @@ var AvatarController = class {
1635
1759
  };
1636
1760
 
1637
1761
  // src/interaction/permissions.ts
1638
- function isActionAllowed(action, config, source) {
1762
+ function normalizeHost(host) {
1763
+ return host.replace(/^https?:\/\//i, "").replace(/\/$/, "").toLowerCase();
1764
+ }
1765
+ function matchesRoutePattern(pathname, pattern) {
1766
+ const candidate = pathname;
1767
+ const normalized = pattern.trim();
1768
+ if (!normalized) {
1769
+ return false;
1770
+ }
1771
+ const escaped = normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
1772
+ return new RegExp(`^${escaped}$`, "i").test(candidate);
1773
+ }
1774
+ function isLikelyJavaScriptUrl(value) {
1775
+ const lower = value.trim().toLowerCase();
1776
+ return lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.includes("eval(") || lower.includes("new function") || lower.includes("document.cookie");
1777
+ }
1778
+ function validateWebsiteDomain(config, currentUrl) {
1779
+ const allowedDomains = config.allowedDomains.map((domain) => normalizeHost(domain));
1780
+ if (allowedDomains.length === 0) {
1781
+ return { allowed: true };
1782
+ }
1783
+ try {
1784
+ const parsed = new URL(currentUrl);
1785
+ const host = normalizeHost(parsed.host);
1786
+ const allowed = allowedDomains.some((domain) => host === domain || host.endsWith(`.${domain}`));
1787
+ if (!allowed) {
1788
+ return { allowed: false, reason: "DOMAIN_NOT_AUTHORIZED" };
1789
+ }
1790
+ return { allowed: true };
1791
+ } catch {
1792
+ return { allowed: false, reason: "DOMAIN_NOT_AUTHORIZED" };
1793
+ }
1794
+ }
1795
+ function canNavigateTo(config, targetUrl, currentUrl) {
1796
+ if (!targetUrl || typeof targetUrl !== "string") {
1797
+ return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
1798
+ }
1799
+ if (isLikelyJavaScriptUrl(targetUrl)) {
1800
+ return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
1801
+ }
1802
+ let target;
1803
+ try {
1804
+ target = new URL(targetUrl, currentUrl);
1805
+ } catch {
1806
+ return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
1807
+ }
1808
+ if (!["http:", "https:"].includes(target.protocol)) {
1809
+ return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
1810
+ }
1811
+ const domainCheck = validateWebsiteDomain(config, currentUrl);
1812
+ if (!domainCheck.allowed) {
1813
+ return domainCheck;
1814
+ }
1815
+ const host = normalizeHost(target.host);
1816
+ const allowedDomains = config.allowedDomains.map((domain) => normalizeHost(domain));
1817
+ if (allowedDomains.length > 0 && !allowedDomains.some((domain) => host === domain || host.endsWith(`.${domain}`))) {
1818
+ return { allowed: false, reason: "DOMAIN_NOT_ALLOWED" };
1819
+ }
1820
+ const pathname = target.pathname || "/";
1821
+ if (config.blockedRoutes.some((route) => matchesRoutePattern(pathname, route))) {
1822
+ return { allowed: false, reason: "ROUTE_BLOCKED" };
1823
+ }
1824
+ if (config.allowedRoutes.length > 0 && !config.allowedRoutes.some((route) => matchesRoutePattern(pathname, route))) {
1825
+ return { allowed: false, reason: "ROUTE_NOT_PERMITTED" };
1826
+ }
1827
+ return { allowed: true };
1828
+ }
1829
+ function evaluateActionPermission(action, config, currentUrl, target) {
1830
+ const explicitRule = config.rules.find((rule) => rule.action === action && rule.enabled !== false);
1831
+ if (explicitRule && typeof explicitRule.allowed === "boolean") {
1832
+ if (!explicitRule.allowed) {
1833
+ return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
1834
+ }
1835
+ }
1836
+ if (!config.allowedActions.includes(action)) {
1837
+ return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
1838
+ }
1839
+ if (action === "navigate") {
1840
+ if (!target) {
1841
+ return { allowed: true };
1842
+ }
1843
+ return canNavigateTo(config, target, currentUrl);
1844
+ }
1845
+ const allowedDomains = config.allowedDomains;
1846
+ if (allowedDomains.length > 0) {
1847
+ const domainCheck = validateWebsiteDomain(config, currentUrl);
1848
+ if (!domainCheck.allowed) {
1849
+ return domainCheck;
1850
+ }
1851
+ }
1852
+ return { allowed: true };
1853
+ }
1854
+ function isActionAllowed(action, config, source, currentUrl, target) {
1639
1855
  if (source === "developer") {
1640
1856
  return true;
1641
1857
  }
1642
- return config.allowedActions.includes(action);
1858
+ if (currentUrl) {
1859
+ return evaluateActionPermission(action, config, currentUrl, target).allowed;
1860
+ }
1861
+ return config.allowedActions.includes(action) && !config.rules.some((rule) => rule.action === action && rule.allowed === false);
1643
1862
  }
1644
- function assertActionAllowed(action, config, source) {
1645
- if (!isActionAllowed(action, config, source)) {
1863
+ function assertActionAllowed(action, config, source, currentUrl, target) {
1864
+ const allowed = isActionAllowed(action, config, source, currentUrl, target);
1865
+ if (!allowed) {
1646
1866
  throw new ActionNotAllowedError(`Action "${action}" is not permitted for this project`);
1647
1867
  }
1648
1868
  }
@@ -1672,7 +1892,12 @@ var InteractionEngine = class {
1672
1892
  }
1673
1893
  async execute(step, source) {
1674
1894
  const config = this.getConfig();
1675
- assertActionAllowed(step.action, config, source);
1895
+ const currentUrl = this.win.location.href;
1896
+ const target = step.url ?? (typeof step.target === "string" ? step.target : void 0);
1897
+ const allowed = this.evaluatePermission(step.action, config, currentUrl, target, source);
1898
+ if (!allowed.ok) {
1899
+ return { ok: false, waitedForUser: false, allowed: false, reason: allowed.reason ?? "ACTION_NOT_PERMITTED" };
1900
+ }
1676
1901
  const wait = shouldWaitForUser(step.action, config.mode, step.waitForUser);
1677
1902
  switch (step.action) {
1678
1903
  case "explain":
@@ -1703,6 +1928,14 @@ var InteractionEngine = class {
1703
1928
  throw new TaskExecutionError("Unsupported action");
1704
1929
  }
1705
1930
  }
1931
+ evaluatePermission(action, config, currentUrl, target, source) {
1932
+ try {
1933
+ assertActionAllowed(action, config, source, currentUrl, target);
1934
+ return { ok: true };
1935
+ } catch {
1936
+ return { ok: false, reason: "ACTION_NOT_PERMITTED" };
1937
+ }
1938
+ }
1706
1939
  async highlight(step, source, wait) {
1707
1940
  if (!step.target) {
1708
1941
  throw new TaskExecutionError("Highlight requires a target");
@@ -1886,6 +2119,11 @@ var TaskEngine = class {
1886
2119
  }
1887
2120
  this.move(TaskStatus.ActionStarted);
1888
2121
  const result = await this.interaction.execute(step, source);
2122
+ if (!result.ok) {
2123
+ this.events.emit("asiyst:task:failed", { taskId: task.id, reason: result.reason ?? "ACTION_NOT_PERMITTED" });
2124
+ this.analytics.track("task_failed", { taskId: task.id });
2125
+ return this.finish(TaskStatus.Failed, result.reason ?? "ACTION_NOT_PERMITTED");
2126
+ }
1889
2127
  if (result.waitedForUser && result.elementId) {
1890
2128
  this.move(TaskStatus.WaitingForUser);
1891
2129
  const clicked = await this.interaction.waitForElementClick(result.elementId, step.timeoutMs ?? 3e4);
@@ -2131,7 +2369,8 @@ var AsiystRuntime = class {
2131
2369
  const transport = new HttpTransport({
2132
2370
  apiBaseUrl: options.apiBaseUrl ?? DEFAULT_API_BASE_URL,
2133
2371
  projectId: options.projectId,
2134
- publicKey: options.publicKey
2372
+ publicKey: options.publicKey,
2373
+ avatarId: options.avatarId
2135
2374
  });
2136
2375
  this.cloud = new CloudClient(transport, options.projectId);
2137
2376
  this.config = new ConfigManager(options, this.cloud, this.events);
@@ -2188,6 +2427,15 @@ var AsiystRuntime = class {
2188
2427
  async start() {
2189
2428
  this.events.emit("asiyst:initialized", { projectId: this.options.projectId });
2190
2429
  const cfg = await isolateAsync(() => this.config.refresh(), this.config.get());
2430
+ const domainCheck = validateWebsiteDomain(cfg, window.location.href);
2431
+ if (!domainCheck.allowed) {
2432
+ this.connectionStatus = "disconnected";
2433
+ this.events.emit("asiyst:error", {
2434
+ code: "domain_not_authorized",
2435
+ message: "This website is not authorized for the current Asiyst project."
2436
+ });
2437
+ return;
2438
+ }
2191
2439
  this.avatar.applyConfig(cfg);
2192
2440
  this.avatar.show();
2193
2441
  try {
@@ -2382,6 +2630,6 @@ var Asiyst = {
2382
2630
  }
2383
2631
  };
2384
2632
 
2385
- export { ActionNotAllowedError, Asiyst, AsiystError, AuthenticationError, ConfigurationError, InitializationError, NetworkError, SDK_VERSION, TargetNotFoundError, TaskExecutionError, TaskStatus, anchorToViewport, canTransition, computeAvatarDestination, fallbackConfig, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions };
2633
+ export { ActionNotAllowedError, Asiyst, AsiystError, AuthenticationError, ConfigurationError, InitializationError, NetworkError, SDK_VERSION, TargetNotFoundError, TaskExecutionError, TaskStatus, anchorToViewport, canNavigateTo, canTransition, computeAvatarDestination, evaluateActionPermission, fallbackConfig, isActionAllowed, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions, validateWebsiteDomain };
2386
2634
  //# sourceMappingURL=index.js.map
2387
2635
  //# sourceMappingURL=index.js.map