@athenaintel/react 0.10.41 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -42,8 +42,8 @@ var __privateWrapper = (obj, member, setter, getter) => ({
42
42
  var _focused, _cleanup, _setup, _a2, _provider, _providerCalled, _b, _online, _cleanup2, _setup2, _c, _gcTimeout, _d, _initialState, _revertState, _cache, _client, _retryer, _defaultOptions, _abortSignalConsumed, _Query_instances, isInitialPausedFetch_fn, dispatch_fn, _e, _client2, _currentQuery, _currentQueryInitialState, _currentResult, _currentResultState, _currentResultOptions, _currentThenable, _selectError, _selectFn, _selectResult, _lastQueryWithDefinedData, _staleTimeoutId, _refetchIntervalId, _currentRefetchInterval, _trackedProps, _QueryObserver_instances, executeFetch_fn, updateStaleTimeout_fn, computeRefetchInterval_fn, updateRefetchInterval_fn, updateTimers_fn, clearStaleTimeout_fn, clearRefetchInterval_fn, updateQuery_fn, notify_fn, _f, _client3, _observers, _mutationCache, _retryer2, _Mutation_instances, dispatch_fn2, _g, _mutations, _scopes, _mutationId, _h, _queries, _i, _queryCache, _mutationCache2, _defaultOptions2, _queryDefaults, _mutationDefaults, _mountCount, _unsubscribeFocus, _unsubscribeOnline, _j;
43
43
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
44
44
  const jsxRuntime = require("react/jsx-runtime");
45
- const react$1 = require("@assistant-ui/react");
46
45
  const React = require("react");
46
+ const react$1 = require("@assistant-ui/react");
47
47
  const AthenaAuthContext = require("./AthenaAuthContext-B3AwLA5Z.cjs");
48
48
  const localStorage$1 = require("@assistant-ui/react-statewire/local-storage");
49
49
  const tap = require("@assistant-ui/tap");
@@ -52,6 +52,7 @@ require("@assistant-ui/core");
52
52
  const react$2 = require("@assistant-ui/core/react");
53
53
  const reactLanggraph = require("@assistant-ui/react-langgraph");
54
54
  const reactStatewire = require("@assistant-ui/react-statewire");
55
+ const store = require("@assistant-ui/store");
55
56
  const ReactDOM = require("react-dom");
56
57
  function _interopNamespaceDefault(e) {
57
58
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -71,231 +72,1134 @@ function _interopNamespaceDefault(e) {
71
72
  }
72
73
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
73
74
  const ReactDOM__namespace = /* @__PURE__ */ _interopNamespaceDefault(ReactDOM);
74
- const ATHENA_ENVIRONMENT_URLS = {
75
- production: {
76
- apiUrl: "https://iris.prd.athenaintel.com/api/chat",
77
- backendUrl: "https://api.athenaintel.com/api/assistant-ui",
78
- appUrl: "https://app.athenaintel.com"
79
- },
80
- staging: {
81
- apiUrl: "https://iris.stg.athenaintel.com/api/chat",
82
- backendUrl: "https://staging-api.athenaintel.com/api/assistant-ui",
83
- appUrl: "https://staging-app.athenaintel.com"
84
- }
75
+ const version$1 = "0.11.1";
76
+ const packageJson = {
77
+ version: version$1
85
78
  };
86
- const DEFAULT_ATHENA_ENVIRONMENT = "production";
87
- const DEFAULT_API_URL = ATHENA_ENVIRONMENT_URLS.production.apiUrl;
88
- const DEFAULT_BACKEND_URL = ATHENA_ENVIRONMENT_URLS.production.backendUrl;
89
- const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;
90
- const DEFAULT_STATEWIRE_SYNC_URL = "https://iris.prd.athenaintel.com/v2/threads";
91
- const STATEWIRE_SYNC_PATHNAME = "/v2/threads";
92
- function deriveStatewireSyncUrl({
93
- apiUrl
94
- }) {
95
- if (!apiUrl) return null;
96
- try {
97
- const parsedUrl = new URL(apiUrl);
98
- return `${parsedUrl.origin}${STATEWIRE_SYNC_PATHNAME}`;
99
- } catch {
100
- return null;
79
+ const ATHENA_REACT_SDK_VERSION = packageJson.version;
80
+ const DEFAULT_CAPTURE_GATE_CONFIG = {
81
+ globalMaxPerWindow: 50,
82
+ globalWindowMs: 1e4,
83
+ criticalReservePerWindow: 15,
84
+ defaultEventMaxPerWindow: 12,
85
+ defaultEventWindowMs: 6e4,
86
+ defaultDedupeWindowMs: 5e3,
87
+ flushIntervalMs: 3e4,
88
+ flushCoalesceThreshold: 20,
89
+ eventOverrides: {
90
+ memory_profile_sample: { maxPerWindow: 2, windowMs: 3e4, dedupeWindowMs: 15e3 },
91
+ long_task_detected: { maxPerWindow: 3, windowMs: 6e4, dedupeWindowMs: 1e4 },
92
+ collab_connection_status_changed: { maxPerWindow: 6, windowMs: 6e4 },
93
+ collab_first_remote_update: { maxPerWindow: 3, windowMs: 6e4 },
94
+ collab_first_local_update: { maxPerWindow: 3, windowMs: 6e4 },
95
+ collab_render_time_sync_fix: { maxPerWindow: 3, windowMs: 6e4 },
96
+ collab_visibility_recovery: { maxPerWindow: 4, windowMs: 6e4 },
97
+ langgraph_message_validation_error: {
98
+ maxPerWindow: 8,
99
+ windowMs: 6e4,
100
+ dedupeWindowMs: 15e3
101
+ },
102
+ langgraph_messages_validation_error: {
103
+ maxPerWindow: 5,
104
+ windowMs: 6e4,
105
+ dedupeWindowMs: 15e3
106
+ },
107
+ langgraph_interrupt_validation_error: {
108
+ maxPerWindow: 5,
109
+ windowMs: 6e4,
110
+ dedupeWindowMs: 15e3
111
+ },
112
+ langgraph_state_validation_error: {
113
+ maxPerWindow: 5,
114
+ windowMs: 6e4,
115
+ dedupeWindowMs: 15e3
116
+ },
117
+ graphql_error: { maxPerWindow: 12, windowMs: 6e4, dedupeWindowMs: 1e4 },
118
+ chat_error: { maxPerWindow: 8, windowMs: 6e4, dedupeWindowMs: 5e3 },
119
+ memory_critical_threshold: { maxPerWindow: 3, windowMs: 3e5 },
120
+ memory_warning_threshold: { maxPerWindow: 3, windowMs: 3e5 },
121
+ pptx_slide_changed: { maxPerWindow: 8, windowMs: 3e4 }
101
122
  }
123
+ };
124
+ const CRITICAL_EVENT_PATTERNS = [
125
+ /(?:^|_)failed$/,
126
+ /(?:^|_)error(?:_|$)/,
127
+ /crash/,
128
+ /^oom_/,
129
+ /aw_snap/,
130
+ /chunk_load/,
131
+ /global_error/,
132
+ /\$exception$/,
133
+ /^404_error$/,
134
+ /^500_error$/,
135
+ /_boundary_/
136
+ ];
137
+ const BYPASS_OPTION_KEY = "__posthog_gate_bypass";
138
+ function isCriticalPosthogEvent(event) {
139
+ return CRITICAL_EVENT_PATTERNS.some((pattern) => pattern.test(event));
102
140
  }
103
- function resolveStatewireSyncUrl({
104
- statewireSyncUrl,
105
- apiUrl
106
- }) {
107
- if (statewireSyncUrl) return statewireSyncUrl;
108
- const derived = deriveStatewireSyncUrl({ apiUrl });
109
- if (derived) return derived;
110
- throw new Error(
111
- `[AthenaSDK] transport 'statewire' could not derive a sync URL from apiUrl (${JSON.stringify(
112
- apiUrl ?? null
113
- )}). Pass an explicit statewireSyncUrl (the …/v2/threads mount). Credentials are never sent to a default host.`
114
- );
141
+ function shouldBypassCaptureGate(options) {
142
+ if (!options || typeof options !== "object") return false;
143
+ return options[BYPASS_OPTION_KEY] === true;
115
144
  }
116
- const MARATHON_APP_PORT = "8082";
117
- const SPACES_PATHNAME = "dashboard/spaces/";
118
- const normalizeBaseUrl = (url) => url.replace(/\/+$/, "");
119
- const createUrlWithFallback = (url, fallbackUrl) => {
120
- try {
121
- return new URL(`${normalizeBaseUrl(url)}/`);
122
- } catch {
123
- return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);
124
- }
125
- };
126
- const getHostname$1 = (value) => {
127
- if (!value) return null;
128
- try {
129
- return new URL(value).hostname;
130
- } catch {
131
- return null;
145
+ function withCaptureGateBypass(options) {
146
+ return {
147
+ ...options ?? {},
148
+ [BYPASS_OPTION_KEY]: true
149
+ };
150
+ }
151
+ function pruneWindow(window2, now, windowMs) {
152
+ const cutoff = now - windowMs;
153
+ while (window2.timestamps.length > 0 && window2.timestamps[0] < cutoff) {
154
+ window2.timestamps.shift();
132
155
  }
133
- };
134
- const deriveWorkspaceAppUrl = ({
135
- sourceUrl,
136
- sourcePort
137
- }) => {
138
- var _a3;
139
- try {
140
- const parsedUrl = new URL(sourceUrl);
141
- const match2 = parsedUrl.hostname.match(
142
- new RegExp(`^(?<prefix>.+)--${sourcePort}\\.(?<domain>app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$`)
143
- );
144
- if (!((_a3 = match2 == null ? void 0 : match2.groups) == null ? void 0 : _a3.prefix) || !match2.groups.domain) {
145
- return null;
146
- }
147
- return `${parsedUrl.protocol}//${match2.groups.prefix}--${MARATHON_APP_PORT}.${match2.groups.domain}`;
148
- } catch {
149
- return null;
156
+ }
157
+ function countInWindow(window2, now, windowMs) {
158
+ pruneWindow(window2, now, windowMs);
159
+ return window2.timestamps.length;
160
+ }
161
+ function recordInWindow(window2, now) {
162
+ window2.timestamps.push(now);
163
+ }
164
+ function stableStringify(value) {
165
+ if (value == null) return String(value);
166
+ if (typeof value !== "object") return String(value);
167
+ if (Array.isArray(value)) {
168
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
150
169
  }
151
- };
152
- const deriveWorkspaceApiUrl = (backendUrl) => {
153
- var _a3;
154
- try {
155
- const parsedUrl = new URL(backendUrl);
156
- const match2 = parsedUrl.hostname.match(
157
- /^(?<prefix>.+)--8000\.(?<domain>app\.athenaintel\.com|staging-app\.athenaintel\.com)$/
158
- );
159
- if (!((_a3 = match2 == null ? void 0 : match2.groups) == null ? void 0 : _a3.prefix) || !match2.groups.domain) {
160
- return null;
170
+ const record2 = value;
171
+ const keys2 = Object.keys(record2).sort();
172
+ return `{${keys2.map((key) => `${key}:${stableStringify(record2[key])}`).join(",")}}`;
173
+ }
174
+ function fingerprintProperties(properties) {
175
+ if (!properties) return "";
176
+ const keys2 = [
177
+ "error",
178
+ "error_message",
179
+ "message",
180
+ "reason",
181
+ "status",
182
+ "code",
183
+ "asset_id",
184
+ "doc_id",
185
+ "thread_id",
186
+ "session_id",
187
+ "operation",
188
+ "path",
189
+ "url",
190
+ "chunk_identifier",
191
+ "validation_path"
192
+ ];
193
+ const subset = {};
194
+ for (const key of keys2) {
195
+ if (key in properties) {
196
+ subset[key] = properties[key];
161
197
  }
162
- return `${parsedUrl.protocol}//${match2.groups.prefix}--8787.${match2.groups.domain}/api/chat`;
163
- } catch {
164
- return null;
165
198
  }
166
- };
167
- function getAthenaEnvironmentUrls({
168
- environment = DEFAULT_ATHENA_ENVIRONMENT
169
- } = {}) {
170
- return ATHENA_ENVIRONMENT_URLS[environment];
199
+ return stableStringify(subset);
171
200
  }
172
- function deriveAthenaApiUrl({
173
- backendUrl
174
- }) {
175
- if (!backendUrl) return null;
176
- const workspaceApiUrl = deriveWorkspaceApiUrl(backendUrl);
177
- if (workspaceApiUrl) return workspaceApiUrl;
178
- try {
179
- const parsedUrl = new URL(backendUrl);
180
- if (parsedUrl.hostname === "api.athenaintel.com") {
181
- return ATHENA_ENVIRONMENT_URLS.production.apiUrl;
201
+ function buildCaptureFingerprint(event, properties) {
202
+ return `${event}|${fingerprintProperties(properties)}`;
203
+ }
204
+ function buildCoalescedCaptureProperties(payload) {
205
+ const windowMs = payload.lastAt - payload.firstAt;
206
+ return {
207
+ ...payload.properties,
208
+ coalesced: true,
209
+ coalesced_count: payload.coalescedCount,
210
+ coalesced_first_at: new Date(payload.firstAt).toISOString(),
211
+ coalesced_last_at: new Date(payload.lastAt).toISOString(),
212
+ coalesced_window_ms: windowMs,
213
+ coalesced_suppression_reason: payload.suppressionReason
214
+ };
215
+ }
216
+ class PosthogCaptureGate {
217
+ constructor(config2 = DEFAULT_CAPTURE_GATE_CONFIG) {
218
+ __publicField(this, "config");
219
+ __publicField(this, "globalWindow", { timestamps: [] });
220
+ __publicField(this, "eventWindows", /* @__PURE__ */ new Map());
221
+ __publicField(this, "activeDedupe", /* @__PURE__ */ new Map());
222
+ __publicField(this, "coalesceBuckets", /* @__PURE__ */ new Map());
223
+ __publicField(this, "emittedTotal", 0);
224
+ __publicField(this, "coalescedTotal", 0);
225
+ __publicField(this, "flushedCoalescedTotal", 0);
226
+ this.config = config2;
227
+ }
228
+ resetForTests() {
229
+ this.globalWindow.timestamps.length = 0;
230
+ this.eventWindows.clear();
231
+ this.activeDedupe.clear();
232
+ this.coalesceBuckets.clear();
233
+ this.emittedTotal = 0;
234
+ this.coalescedTotal = 0;
235
+ this.flushedCoalescedTotal = 0;
236
+ }
237
+ getSnapshot() {
238
+ const coalescedPendingByEvent = {};
239
+ let coalescedPending = 0;
240
+ for (const bucket of this.coalesceBuckets.values()) {
241
+ coalescedPending += bucket.count;
242
+ coalescedPendingByEvent[bucket.event] = (coalescedPendingByEvent[bucket.event] ?? 0) + bucket.count;
182
243
  }
183
- if (parsedUrl.hostname === "staging-api.athenaintel.com") {
184
- return ATHENA_ENVIRONMENT_URLS.staging.apiUrl;
244
+ return {
245
+ coalescedPending,
246
+ coalescedPendingByEvent,
247
+ emittedTotal: this.emittedTotal,
248
+ coalescedTotal: this.coalescedTotal,
249
+ flushedCoalescedTotal: this.flushedCoalescedTotal
250
+ };
251
+ }
252
+ evaluateCapture(event, properties, options, now = Date.now()) {
253
+ if (shouldBypassCaptureGate(options)) {
254
+ return { action: "emit" };
185
255
  }
186
- if (parsedUrl.hostname === "localhost" || parsedUrl.hostname === "127.0.0.1") {
187
- parsedUrl.port = "8787";
188
- parsedUrl.pathname = "/api/chat";
189
- parsedUrl.search = "";
190
- parsedUrl.hash = "";
191
- return parsedUrl.toString();
256
+ const fingerprint = buildCaptureFingerprint(event, properties);
257
+ const bucketKey = fingerprint;
258
+ const override = this.config.eventOverrides[event];
259
+ const dedupeWindowMs = (override == null ? void 0 : override.dedupeWindowMs) ?? this.config.defaultDedupeWindowMs;
260
+ const lastDedupe = this.activeDedupe.get(event);
261
+ if (lastDedupe && lastDedupe.fingerprint === fingerprint && now - lastDedupe.at < dedupeWindowMs) {
262
+ return this.recordCoalesce(event, bucketKey, fingerprint, properties, "dedupe", now);
192
263
  }
193
- } catch {
194
- return null;
195
- }
196
- return null;
197
- }
198
- function deriveAthenaAppUrl({
199
- apiUrl,
200
- backendUrl
201
- }) {
202
- if (backendUrl) {
203
- const workspaceAppUrl = deriveWorkspaceAppUrl({
204
- sourceUrl: backendUrl,
205
- sourcePort: "8000"
206
- });
207
- if (workspaceAppUrl) return workspaceAppUrl;
208
- const backendHostname = getHostname$1(backendUrl);
209
- if (backendHostname === "api.athenaintel.com") {
210
- return ATHENA_ENVIRONMENT_URLS.production.appUrl;
264
+ const critical = isCriticalPosthogEvent(event);
265
+ const globalCount = countInWindow(this.globalWindow, now, this.config.globalWindowMs);
266
+ const normalBudget = Math.max(
267
+ 0,
268
+ this.config.globalMaxPerWindow - this.config.criticalReservePerWindow
269
+ );
270
+ if (critical) {
271
+ if (globalCount >= this.config.globalMaxPerWindow) {
272
+ return this.recordCoalesce(event, bucketKey, fingerprint, properties, "global_budget", now);
273
+ }
274
+ } else if (globalCount >= normalBudget) {
275
+ return this.recordCoalesce(event, bucketKey, fingerprint, properties, "global_budget", now);
211
276
  }
212
- if (backendHostname === "staging-api.athenaintel.com") {
213
- return ATHENA_ENVIRONMENT_URLS.staging.appUrl;
277
+ const eventWindow = this.eventWindows.get(event) ?? { timestamps: [] };
278
+ this.eventWindows.set(event, eventWindow);
279
+ const eventMax = (override == null ? void 0 : override.maxPerWindow) ?? this.config.defaultEventMaxPerWindow;
280
+ const eventWindowMs = (override == null ? void 0 : override.windowMs) ?? this.config.defaultEventWindowMs;
281
+ const eventCount = countInWindow(eventWindow, now, eventWindowMs);
282
+ if (eventCount >= eventMax) {
283
+ return this.recordCoalesce(event, bucketKey, fingerprint, properties, "event_budget", now);
214
284
  }
285
+ recordInWindow(this.globalWindow, now);
286
+ recordInWindow(eventWindow, now);
287
+ this.activeDedupe.set(event, { fingerprint, bucketKey, at: now });
288
+ this.emittedTotal += 1;
289
+ return { action: "emit" };
215
290
  }
216
- if (apiUrl) {
217
- const workspaceAppUrl = deriveWorkspaceAppUrl({
218
- sourceUrl: apiUrl,
219
- sourcePort: "8787"
220
- });
221
- if (workspaceAppUrl) return workspaceAppUrl;
222
- const apiHostname = getHostname$1(apiUrl);
223
- if (apiHostname === "sync.athenaintel.com" || apiHostname === "iris.prd.athenaintel.com") {
224
- return ATHENA_ENVIRONMENT_URLS.production.appUrl;
291
+ /** Returns payloads ready to flush (does not mutate buckets until caller confirms). */
292
+ collectFlushCandidates(now = Date.now()) {
293
+ const payloads = [];
294
+ for (const bucket of this.coalesceBuckets.values()) {
295
+ const shouldFlushBySize = bucket.count >= this.config.flushCoalesceThreshold;
296
+ const shouldFlushByAge = now - bucket.firstAt >= this.config.flushIntervalMs;
297
+ if (!shouldFlushBySize && !shouldFlushByAge) {
298
+ continue;
299
+ }
300
+ payloads.push({
301
+ bucketKey: bucket.bucketKey,
302
+ event: bucket.event,
303
+ properties: bucket.sampleProperties,
304
+ coalescedCount: bucket.count,
305
+ firstAt: bucket.firstAt,
306
+ lastAt: bucket.lastAt,
307
+ suppressionReason: bucket.suppressionReason
308
+ });
225
309
  }
226
- if (apiHostname === "staging-sync.athenaintel.com" || apiHostname === "iris.stg.athenaintel.com") {
227
- return ATHENA_ENVIRONMENT_URLS.staging.appUrl;
310
+ return payloads;
311
+ }
312
+ /** Remove flushed bucket keys after successful emit. */
313
+ acknowledgeFlushed(payloads) {
314
+ for (const payload of payloads) {
315
+ this.coalesceBuckets.delete(payload.bucketKey);
316
+ this.flushedCoalescedTotal += payload.coalescedCount;
228
317
  }
229
318
  }
230
- return null;
231
- }
232
- function createAthenaSpacesUrl({
233
- appUrl = DEFAULT_APP_URL,
234
- assetIds,
235
- sessionId
236
- }) {
237
- const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));
238
- if (assetIds) {
239
- const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds]).map((assetId) => assetId.trim()).filter((assetId) => assetId.length > 0);
240
- if (normalizedAssetIds.length > 0) {
241
- url.searchParams.set("asset_ids", normalizedAssetIds.join(","));
319
+ /** After coalescing, flush immediately if the bucket crossed the early threshold. */
320
+ takeBucketIfReady(bucketKey, now = Date.now()) {
321
+ const bucket = this.coalesceBuckets.get(bucketKey);
322
+ if (!bucket) return null;
323
+ const shouldFlushBySize = bucket.count >= this.config.flushCoalesceThreshold;
324
+ const shouldFlushByAge = now - bucket.firstAt >= this.config.flushIntervalMs;
325
+ if (!shouldFlushBySize && !shouldFlushByAge) {
326
+ return null;
242
327
  }
328
+ this.coalesceBuckets.delete(bucketKey);
329
+ this.flushedCoalescedTotal += bucket.count;
330
+ return {
331
+ bucketKey: bucket.bucketKey,
332
+ event: bucket.event,
333
+ properties: bucket.sampleProperties,
334
+ coalescedCount: bucket.count,
335
+ firstAt: bucket.firstAt,
336
+ lastAt: bucket.lastAt,
337
+ suppressionReason: bucket.suppressionReason
338
+ };
243
339
  }
244
- if (sessionId) {
245
- url.searchParams.set("session_id", sessionId);
340
+ /** Force-flush every pending bucket (e.g. page hide). */
341
+ collectAllPending() {
342
+ return [...this.coalesceBuckets.values()].map((bucket) => ({
343
+ bucketKey: bucket.bucketKey,
344
+ event: bucket.event,
345
+ properties: bucket.sampleProperties,
346
+ coalescedCount: bucket.count,
347
+ firstAt: bucket.firstAt,
348
+ lastAt: bucket.lastAt,
349
+ suppressionReason: bucket.suppressionReason
350
+ }));
246
351
  }
247
- return url.toString();
248
- }
249
- function isTrustedOrigin({
250
- origin,
251
- trustedOrigins
252
- }) {
253
- try {
254
- const normalizedOrigin = new URL(origin).origin;
255
- if (trustedOrigins.includes(normalizedOrigin)) {
256
- return true;
352
+ recordCoalesce(event, bucketKey, fingerprint, properties, reason, now) {
353
+ const existing = this.coalesceBuckets.get(bucketKey);
354
+ if (existing) {
355
+ existing.count += 1;
356
+ existing.lastAt = now;
357
+ existing.sampleProperties = { ...existing.sampleProperties, ...properties ?? {} };
358
+ } else {
359
+ this.coalesceBuckets.set(bucketKey, {
360
+ event,
361
+ bucketKey,
362
+ fingerprint,
363
+ count: 1,
364
+ firstAt: now,
365
+ lastAt: now,
366
+ suppressionReason: reason,
367
+ sampleProperties: { ...properties ?? {} }
368
+ });
257
369
  }
258
- const { hostname } = new URL(normalizedOrigin);
259
- return hostname === "athenaintel.com" || hostname.endsWith(".athenaintel.com") || hostname === "localhost";
260
- } catch {
261
- return false;
370
+ this.coalescedTotal += 1;
371
+ this.activeDedupe.set(event, { fingerprint, bucketKey, at: now });
372
+ return { action: "coalesce", reason, bucketKey };
262
373
  }
263
374
  }
264
- const BRIDGE_TIMEOUT_MS = 2e3;
265
- const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
266
- const TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1e3;
267
- const TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1e3;
268
- const DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1e3;
269
- const EMPTY_TRUSTED_ORIGINS = [];
270
- const normalizeOrigin = (value) => {
271
- try {
272
- return new URL(value).origin;
273
- } catch {
274
- return null;
375
+ const gate = new PosthogCaptureGate(DEFAULT_CAPTURE_GATE_CONFIG);
376
+ let wrappedClient = null;
377
+ let originalCaptureRef = null;
378
+ let flushTimer = null;
379
+ let lifecycleHooksInstalled = false;
380
+ function getPosthogCaptureGate() {
381
+ return gate;
382
+ }
383
+ function emitThroughOriginal(event, properties, options) {
384
+ if (!originalCaptureRef) return void 0;
385
+ return originalCaptureRef(event, properties, withCaptureGateBypass(options));
386
+ }
387
+ function flushCoalescedPayload(payload) {
388
+ emitThroughOriginal(payload.event, buildCoalescedCaptureProperties(payload));
389
+ }
390
+ function flushCoalescedCaptures(options) {
391
+ const payloads = (options == null ? void 0 : options.force) ? gate.collectAllPending() : gate.collectFlushCandidates();
392
+ if (payloads.length === 0) return;
393
+ for (const payload of payloads) {
394
+ flushCoalescedPayload(payload);
275
395
  }
276
- };
277
- const getTokenRefreshDelay = (expiresAt, nowMs) => {
278
- if (typeof expiresAt !== "string") {
279
- return DEFAULT_TOKEN_REFRESH_MS;
396
+ gate.acknowledgeFlushed(payloads);
397
+ }
398
+ function installLifecycleFlushHooks() {
399
+ if (lifecycleHooksInstalled || typeof window === "undefined") return;
400
+ lifecycleHooksInstalled = true;
401
+ const flushOnHide = () => {
402
+ flushCoalescedCaptures({ force: true });
403
+ };
404
+ window.addEventListener("pagehide", flushOnHide);
405
+ document.addEventListener("visibilitychange", () => {
406
+ if (document.visibilityState === "hidden") {
407
+ flushOnHide();
408
+ }
409
+ });
410
+ if (!flushTimer) {
411
+ flushTimer = setInterval(() => {
412
+ flushCoalescedCaptures();
413
+ }, DEFAULT_CAPTURE_GATE_CONFIG.flushIntervalMs);
280
414
  }
281
- const expiresAtMs = Date.parse(expiresAt);
282
- if (!Number.isFinite(expiresAtMs)) {
283
- return DEFAULT_TOKEN_REFRESH_MS;
415
+ }
416
+ function wrapPosthogClient(client) {
417
+ if (wrappedClient === client) {
418
+ return client;
284
419
  }
285
- return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);
286
- };
287
- const getAuthRetryDelay = (retryAttempt) => {
288
- const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;
289
- return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);
290
- };
291
- function useParentBridge({
292
- trustedOrigins = EMPTY_TRUSTED_ORIGINS
293
- } = {}) {
294
- const isInIframe = typeof window !== "undefined" && window.parent !== window;
295
- const runtimeTrustedOrigins = React.useMemo(() => {
296
- const origins = /* @__PURE__ */ new Set();
297
- if (typeof window !== "undefined") {
298
- origins.add(window.location.origin);
420
+ originalCaptureRef = client.capture.bind(client);
421
+ const originalCaptureException = typeof client.captureException === "function" ? client.captureException.bind(client) : null;
422
+ installLifecycleFlushHooks();
423
+ client.capture = ((event, properties, options) => {
424
+ const outcome = gate.evaluateCapture(event, properties, options);
425
+ if (outcome.action === "coalesce") {
426
+ const readyPayload = gate.takeBucketIfReady(outcome.bucketKey);
427
+ if (readyPayload) {
428
+ flushCoalescedPayload(readyPayload);
429
+ }
430
+ return void 0;
431
+ }
432
+ return emitThroughOriginal(event, properties, options);
433
+ });
434
+ if (originalCaptureException) {
435
+ client.captureException = ((error2, additionalProperties) => {
436
+ const mergedProperties = {
437
+ ...additionalProperties ?? {},
438
+ $exception_message: error2 instanceof Error ? error2.message : String(error2),
439
+ $exception_type: error2 instanceof Error ? error2.name : typeof error2
440
+ };
441
+ const outcome = gate.evaluateCapture("$exception", mergedProperties);
442
+ if (outcome.action === "coalesce") {
443
+ const readyPayload = gate.takeBucketIfReady(outcome.bucketKey);
444
+ if (readyPayload) {
445
+ flushCoalescedPayload(readyPayload);
446
+ }
447
+ return void 0;
448
+ }
449
+ return originalCaptureException(error2, additionalProperties);
450
+ });
451
+ }
452
+ wrappedClient = client;
453
+ return client;
454
+ }
455
+ function capturePosthogEvent(client, event, properties, options) {
456
+ if (!client || typeof client.capture !== "function") return;
457
+ client.capture(event, properties, options);
458
+ }
459
+ function capturePosthogException(client, error2, properties, options) {
460
+ if (!client) return;
461
+ if (typeof client.captureException === "function") {
462
+ client.captureException(error2, properties);
463
+ return;
464
+ }
465
+ capturePosthogEvent(
466
+ client,
467
+ "$exception",
468
+ {
469
+ ...properties ?? {},
470
+ $exception_message: error2.message,
471
+ $exception_type: error2.name,
472
+ $exception_stack: error2.stack
473
+ },
474
+ options
475
+ );
476
+ }
477
+ let posthogInstance = null;
478
+ let initPromise = null;
479
+ const DEFAULT_HOST = "https://us.i.posthog.com";
480
+ async function initializePostHog(apiKey, host, debug) {
481
+ if (posthogInstance) return posthogInstance;
482
+ if (typeof window === "undefined") return null;
483
+ try {
484
+ const posthog = (await import("posthog-js")).default;
485
+ posthog.init(apiKey, {
486
+ api_host: host,
487
+ autocapture: true,
488
+ capture_pageview: false,
489
+ session_recording: {
490
+ recordCrossOriginIframes: true,
491
+ maskAllInputs: true
492
+ },
493
+ loaded: (ph) => {
494
+ wrapPosthogClient(ph);
495
+ if (debug) {
496
+ ph.debug(true);
497
+ }
498
+ }
499
+ });
500
+ wrapPosthogClient(posthog);
501
+ posthogInstance = posthog;
502
+ return posthog;
503
+ } catch {
504
+ initPromise = null;
505
+ return null;
506
+ }
507
+ }
508
+ function getPostHogInstance() {
509
+ return posthogInstance;
510
+ }
511
+ function PostHogProvider({
512
+ children,
513
+ config: config2
514
+ }) {
515
+ const apiKey = (config2 == null ? void 0 : config2.apiKey) ?? "";
516
+ const host = (config2 == null ? void 0 : config2.host) ?? DEFAULT_HOST;
517
+ const debug = (config2 == null ? void 0 : config2.debug) ?? false;
518
+ React.useEffect(() => {
519
+ if (!apiKey || typeof window === "undefined") return;
520
+ if (!initPromise) {
521
+ initPromise = initializePostHog(apiKey, host, debug);
522
+ }
523
+ }, [apiKey, host, debug]);
524
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
525
+ }
526
+ const ATHENA_SDK_ERROR_CODES = {
527
+ /** No token and no API key resolved before a request had to be sent. */
528
+ auth_missing: "auth_missing",
529
+ /** The server refused the credential (401 / statewire `unauthorized` fin). */
530
+ auth_rejected: "auth_rejected",
531
+ /** `/_athena/auth` or the iframe bridge did not answer within the timeout. */
532
+ auth_bridge_timeout: "auth_bridge_timeout",
533
+ /** `/_athena/auth` answered with a non-404 error. */
534
+ auth_bridge_failed: "auth_bridge_failed",
535
+ /** The statewire sync URL could not be derived from the configuration. */
536
+ config_invalid: "config_invalid",
537
+ /** A prop was set that the active transport ignores (e.g. `agent` on statewire). */
538
+ config_ignored: "config_ignored",
539
+ /** The thread list request failed. */
540
+ threads_list_failed: "threads_list_failed",
541
+ /** Loading a thread's history failed. */
542
+ thread_load_failed: "thread_load_failed",
543
+ /** The statewire host rejected a command (structured `{status, detail}`). */
544
+ statewire_rejected: "statewire_rejected",
545
+ /** The statewire connection is gone and will not reconnect on its own. */
546
+ statewire_gone: "statewire_gone",
547
+ /** The legacy `/api/chat` stream failed. */
548
+ stream_failed: "stream_failed",
549
+ /** The selected collab agent / channel was refused by the backend. */
550
+ collab_agent_rejected: "collab_agent_rejected",
551
+ /** Anything else. */
552
+ unknown: "unknown"
553
+ };
554
+ class AthenaSdkError extends Error {
555
+ constructor(options) {
556
+ super(options.message, options.cause === void 0 ? void 0 : { cause: options.cause });
557
+ __publicField(this, "code");
558
+ __publicField(this, "hint");
559
+ __publicField(this, "status");
560
+ __publicField(this, "detail");
561
+ __publicField(this, "context");
562
+ this.name = "AthenaSdkError";
563
+ this.code = options.code;
564
+ this.hint = options.hint;
565
+ this.status = options.status;
566
+ this.detail = options.detail;
567
+ this.context = options.context ?? {};
568
+ }
569
+ /** A log/console-friendly one-liner: `[code] message — hint`. */
570
+ describe() {
571
+ return `[${this.code}] ${this.message}${this.hint ? ` — ${this.hint}` : ""}`;
572
+ }
573
+ toJSON() {
574
+ return {
575
+ name: this.name,
576
+ code: this.code,
577
+ message: this.message,
578
+ hint: this.hint,
579
+ status: this.status,
580
+ detail: this.detail,
581
+ context: this.context
582
+ };
583
+ }
584
+ }
585
+ function isAthenaSdkError(value) {
586
+ return value instanceof AthenaSdkError;
587
+ }
588
+ function toAthenaSdkError(value, fallback) {
589
+ if (isAthenaSdkError(value)) return value;
590
+ const message = value instanceof Error && value.message ? value.message : fallback.message;
591
+ return new AthenaSdkError({ ...fallback, message, cause: value });
592
+ }
593
+ function readRejectionDetail(detail) {
594
+ if (typeof detail === "string") return { code: null, message: detail || null };
595
+ if (!detail || typeof detail !== "object") return { code: null, message: null };
596
+ const record2 = detail;
597
+ return {
598
+ code: typeof record2.code === "string" ? record2.code : null,
599
+ message: typeof record2.message === "string" ? record2.message : null
600
+ };
601
+ }
602
+ const ATHENA_DIAGNOSTIC_LEVELS = ["debug", "info", "warn", "error"];
603
+ const LEVEL_RANK = {
604
+ debug: 0,
605
+ info: 1,
606
+ warn: 2,
607
+ error: 3
608
+ };
609
+ const DEFAULT_CONFIG = {
610
+ console: false,
611
+ posthog: false,
612
+ bufferSize: 300
613
+ };
614
+ const CONSOLE_PREFIX = "[AthenaSDK]";
615
+ const LOCAL_STORAGE_DEBUG_KEY = "athena:debug";
616
+ const GLOBAL_KEY = "__ATHENA_SDK__";
617
+ const REDACT_KEYS = /token|secret|api[-_]?key|authorization|cookie|password/i;
618
+ function redact(data) {
619
+ if (!data) return void 0;
620
+ const out = {};
621
+ for (const [key, value] of Object.entries(data)) {
622
+ if (REDACT_KEYS.test(key)) {
623
+ out[key] = typeof value === "string" && value.length > 0 ? `<redacted:${value.length}>` : "<redacted>";
624
+ } else {
625
+ out[key] = value;
626
+ }
627
+ }
628
+ return out;
629
+ }
630
+ function nowMs() {
631
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
632
+ }
633
+ function readLocalDebugLevel() {
634
+ try {
635
+ if (typeof localStorage === "undefined") return null;
636
+ const raw = localStorage.getItem(LOCAL_STORAGE_DEBUG_KEY);
637
+ if (!raw) return null;
638
+ if (ATHENA_DIAGNOSTIC_LEVELS.includes(raw)) {
639
+ return raw;
640
+ }
641
+ return raw === "1" || raw === "true" ? "debug" : null;
642
+ } catch {
643
+ return null;
644
+ }
645
+ }
646
+ class AthenaDiagnosticsBus {
647
+ constructor() {
648
+ __publicField(this, "config", { ...DEFAULT_CONFIG });
649
+ __publicField(this, "events", []);
650
+ __publicField(this, "listeners", /* @__PURE__ */ new Set());
651
+ __publicField(this, "snapshotListeners", /* @__PURE__ */ new Set());
652
+ __publicField(this, "seq", 0);
653
+ __publicField(this, "gauges", {});
654
+ __publicField(this, "timings", {});
655
+ __publicField(this, "spans", /* @__PURE__ */ new Map());
656
+ __publicField(this, "cachedSnapshot", null);
657
+ }
658
+ configure(partial2) {
659
+ this.config = { ...this.config, ...partial2 };
660
+ if (this.events.length > this.config.bufferSize) {
661
+ this.events = this.events.slice(-this.config.bufferSize);
662
+ }
663
+ this.invalidate();
664
+ }
665
+ getConfig() {
666
+ return this.config;
667
+ }
668
+ /** Effective console level: the configured one, or `localStorage['athena:debug']`. */
669
+ consoleLevel() {
670
+ if (this.config.console !== false) return this.config.console;
671
+ return readLocalDebugLevel() ?? false;
672
+ }
673
+ emit(name, options = {}) {
674
+ const level = options.level ?? (options.error !== void 0 ? "error" : "info");
675
+ const error2 = options.error === void 0 ? void 0 : toAthenaSdkError(options.error, {
676
+ code: "unknown",
677
+ message: `Unexpected error during ${name}`
678
+ });
679
+ const event = {
680
+ seq: ++this.seq,
681
+ t: Math.round(nowMs() * 10) / 10,
682
+ at: (/* @__PURE__ */ new Date()).toISOString(),
683
+ name,
684
+ level,
685
+ ...options.durationMs !== void 0 && { durationMs: Math.round(options.durationMs) },
686
+ ...options.data && { data: redact(options.data) },
687
+ ...error2 && { error: error2 }
688
+ };
689
+ this.events.push(event);
690
+ if (this.events.length > this.config.bufferSize) {
691
+ this.events.splice(0, this.events.length - this.config.bufferSize);
692
+ }
693
+ if (event.durationMs !== void 0) {
694
+ this.timings[name] = event.durationMs;
695
+ }
696
+ this.invalidate();
697
+ this.echoToConsole(event);
698
+ this.forwardToPostHog(event);
699
+ for (const listener of this.listeners) {
700
+ try {
701
+ listener(event);
702
+ } catch (listenerError) {
703
+ console.error(`${CONSOLE_PREFIX} diagnostics listener threw`, listenerError);
704
+ }
705
+ }
706
+ if (this.config.onEvent) {
707
+ try {
708
+ this.config.onEvent(event);
709
+ } catch (callbackError) {
710
+ console.error(`${CONSOLE_PREFIX} onDiagnostic callback threw`, callbackError);
711
+ }
712
+ }
713
+ if (error2 && this.config.onError) {
714
+ try {
715
+ this.config.onError(error2, event);
716
+ } catch (callbackError) {
717
+ console.error(`${CONSOLE_PREFIX} onError callback threw`, callbackError);
718
+ }
719
+ }
720
+ for (const notify of this.snapshotListeners) notify();
721
+ return event;
722
+ }
723
+ /** Record an error-level event. Returns the normalized `AthenaSdkError`. */
724
+ error(name, error2, fallback, data) {
725
+ const normalized = toAthenaSdkError(error2, fallback);
726
+ this.emit(name, { level: "error", error: normalized, data });
727
+ return normalized;
728
+ }
729
+ /**
730
+ * Start a named span. Returns a function that ends it and emits `doneName`
731
+ * with `durationMs` (or `errorName` when passed an error). Also records a
732
+ * `performance.measure` so the span shows in the browser's Performance panel.
733
+ */
734
+ span(spanName, startName, data) {
735
+ const startedAt = nowMs();
736
+ this.spans.set(spanName, startedAt);
737
+ this.mark(`${spanName}:start`);
738
+ this.emit(startName, { level: "debug", data });
739
+ return (doneName, doneOptions = {}) => {
740
+ const durationMs = nowMs() - startedAt;
741
+ this.spans.delete(spanName);
742
+ this.mark(`${spanName}:end`);
743
+ this.measure(spanName);
744
+ this.emit(doneName, {
745
+ level: doneOptions.error !== void 0 ? "error" : "info",
746
+ durationMs,
747
+ data: doneOptions.data,
748
+ error: doneOptions.error
749
+ });
750
+ return durationMs;
751
+ };
752
+ }
753
+ /** Set a gauge (latest-value metric): connection status, transport, auth mode. */
754
+ gauge(name, value) {
755
+ if (this.gauges[name] === value) return;
756
+ this.gauges[name] = value;
757
+ this.invalidate();
758
+ for (const notify of this.snapshotListeners) notify();
759
+ }
760
+ subscribe(listener) {
761
+ this.listeners.add(listener);
762
+ return () => {
763
+ this.listeners.delete(listener);
764
+ };
765
+ }
766
+ /**
767
+ * Additive consumer registration: every registered consumer receives every
768
+ * event (and every error), unlike the single `onEvent`/`onError` config
769
+ * slots which are last-writer-wins. `AthenaProvider` registers through this
770
+ * so two mounted providers never clobber each other's callbacks.
771
+ */
772
+ addConsumer(consumer) {
773
+ return this.subscribe((event) => {
774
+ var _a3, _b2;
775
+ try {
776
+ (_a3 = consumer.onEvent) == null ? void 0 : _a3.call(consumer, event);
777
+ } catch (callbackError) {
778
+ console.error(`${CONSOLE_PREFIX} onDiagnostic consumer threw`, callbackError);
779
+ }
780
+ if (event.error) {
781
+ try {
782
+ (_b2 = consumer.onError) == null ? void 0 : _b2.call(consumer, event.error, event);
783
+ } catch (callbackError) {
784
+ console.error(`${CONSOLE_PREFIX} onError consumer threw`, callbackError);
785
+ }
786
+ }
787
+ });
788
+ }
789
+ /** Subscribe to snapshot changes (for `useSyncExternalStore`). */
790
+ subscribeSnapshot(listener) {
791
+ this.snapshotListeners.add(listener);
792
+ return () => {
793
+ this.snapshotListeners.delete(listener);
794
+ };
795
+ }
796
+ snapshot() {
797
+ if (!this.cachedSnapshot) {
798
+ const { onEvent: _onEvent, onError: _onError, ...config2 } = this.config;
799
+ this.cachedSnapshot = {
800
+ sdkVersion: ATHENA_REACT_SDK_VERSION,
801
+ config: config2,
802
+ events: [...this.events],
803
+ gauges: { ...this.gauges },
804
+ timings: { ...this.timings }
805
+ };
806
+ }
807
+ return this.cachedSnapshot;
808
+ }
809
+ /** Copy-paste-able JSON of the current snapshot (errors serialized). */
810
+ export() {
811
+ return JSON.stringify(
812
+ this.snapshot(),
813
+ (_key, value) => isAthenaSdkError(value) ? value.toJSON() : value,
814
+ 2
815
+ );
816
+ }
817
+ clear() {
818
+ this.events = [];
819
+ this.timings = {};
820
+ this.invalidate();
821
+ for (const notify of this.snapshotListeners) notify();
822
+ }
823
+ invalidate() {
824
+ this.cachedSnapshot = null;
825
+ }
826
+ mark(name) {
827
+ try {
828
+ if (typeof performance !== "undefined" && typeof performance.mark === "function") {
829
+ performance.mark(`athena-sdk:${name}`);
830
+ }
831
+ } catch {
832
+ }
833
+ }
834
+ measure(spanName) {
835
+ try {
836
+ if (typeof performance !== "undefined" && typeof performance.measure === "function") {
837
+ performance.measure(
838
+ `athena-sdk:${spanName}`,
839
+ `athena-sdk:${spanName}:start`,
840
+ `athena-sdk:${spanName}:end`
841
+ );
842
+ }
843
+ } catch {
844
+ }
845
+ }
846
+ echoToConsole(event) {
847
+ const level = this.consoleLevel();
848
+ if (level === false) {
849
+ if (event.level === "error" && event.error) {
850
+ console.error(`${CONSOLE_PREFIX} ${event.name}: ${event.error.describe()}`, event.data ?? "");
851
+ }
852
+ return;
853
+ }
854
+ if (LEVEL_RANK[event.level] < LEVEL_RANK[level]) return;
855
+ const suffix = event.durationMs !== void 0 ? ` (${event.durationMs}ms)` : "";
856
+ const line = `${CONSOLE_PREFIX} ${event.name}${suffix}`;
857
+ const payload = event.error ? { ...event.data, error: event.error.describe() } : event.data;
858
+ const method = event.level === "error" ? console.error : event.level === "warn" ? console.warn : event.level === "debug" ? console.debug : console.info;
859
+ method(line, payload ?? "");
860
+ }
861
+ forwardToPostHog(event) {
862
+ if (!this.config.posthog) return;
863
+ if (event.durationMs === void 0 && event.level !== "error") return;
864
+ try {
865
+ const client = getPostHogInstance();
866
+ capturePosthogEvent(client, `athena_sdk_${event.name.replace(/\./g, "_")}`, {
867
+ sdk_version: ATHENA_REACT_SDK_VERSION,
868
+ level: event.level,
869
+ ...event.durationMs !== void 0 && { duration_ms: event.durationMs },
870
+ ...event.error && {
871
+ error_code: event.error.code,
872
+ error_message: event.error.message,
873
+ error_status: event.error.status
874
+ },
875
+ ...event.data
876
+ });
877
+ } catch {
878
+ }
879
+ }
880
+ }
881
+ const athenaDiagnostics = new AthenaDiagnosticsBus();
882
+ function installAthenaDiagnosticsGlobal() {
883
+ if (typeof window === "undefined") return;
884
+ const globalWindow = window;
885
+ const existing = globalWindow[GLOBAL_KEY] ?? {};
886
+ globalWindow[GLOBAL_KEY] = {
887
+ ...existing,
888
+ version: ATHENA_REACT_SDK_VERSION,
889
+ diagnostics: {
890
+ snapshot: () => athenaDiagnostics.snapshot(),
891
+ export: () => athenaDiagnostics.export(),
892
+ clear: () => athenaDiagnostics.clear(),
893
+ enableConsole: (level = "debug") => {
894
+ try {
895
+ localStorage.setItem(LOCAL_STORAGE_DEBUG_KEY, level);
896
+ } catch {
897
+ }
898
+ athenaDiagnostics.configure({ console: level });
899
+ },
900
+ disableConsole: () => {
901
+ try {
902
+ localStorage.removeItem(LOCAL_STORAGE_DEBUG_KEY);
903
+ } catch {
904
+ }
905
+ athenaDiagnostics.configure({ console: false });
906
+ }
907
+ }
908
+ };
909
+ }
910
+ function debugPropToDiagnosticsConfig(debug) {
911
+ if (debug === void 0) return {};
912
+ if (debug === true) return { console: "debug" };
913
+ if (debug === false) return { console: false };
914
+ if (typeof debug === "string") return { console: debug };
915
+ return debug;
916
+ }
917
+ function useAthenaDiagnostics() {
918
+ const snapshot = React.useSyncExternalStore(
919
+ athenaDiagnostics.subscribeSnapshot.bind(athenaDiagnostics),
920
+ athenaDiagnostics.snapshot.bind(athenaDiagnostics),
921
+ athenaDiagnostics.snapshot.bind(athenaDiagnostics)
922
+ );
923
+ const clear = React.useCallback(() => athenaDiagnostics.clear(), []);
924
+ const exportJson = React.useCallback(() => athenaDiagnostics.export(), []);
925
+ return { ...snapshot, clear, export: exportJson };
926
+ }
927
+ const DEEP_AGENT_RUNTIME_MARKER = "athena_deep_agent";
928
+ const STATEWIRE_START_CHANNEL = "web";
929
+ const STATEWIRE_RUN_LABEL = "Athena SDK";
930
+ const DEFAULT_STATEWIRE_MODEL = "claude-sonnet-5";
931
+ const COLLAB_AGENT_REF_PREFIX = "collab_agent:";
932
+ function parseCollabAgentRef(agent2) {
933
+ if (typeof agent2 !== "string" || !agent2.startsWith(COLLAB_AGENT_REF_PREFIX)) return null;
934
+ const assetId = agent2.slice(COLLAB_AGENT_REF_PREFIX.length).trim();
935
+ return assetId || null;
936
+ }
937
+ function buildStatewireRunConfig(options = {}) {
938
+ var _a3, _b2, _c2, _d2, _e2;
939
+ const collabAgentId = parseCollabAgentRef(options.agent);
940
+ const collabChannelId = collabAgentId && ((_a3 = options.channel) == null ? void 0 : _a3.trim()) ? options.channel.trim() : void 0;
941
+ const model = options.model ?? (collabAgentId ? void 0 : DEFAULT_STATEWIRE_MODEL);
942
+ const panelModel = model ?? DEFAULT_STATEWIRE_MODEL;
943
+ const enabledTools = Array.from(
944
+ /* @__PURE__ */ new Set([...options.tools ?? [], ...options.frontendToolIds ?? []])
945
+ );
946
+ const systemPrompt = ((_b2 = options.systemPrompt) == null ? void 0 : _b2.trim()) ?? "";
947
+ return {
948
+ custom: {
949
+ runtime: DEEP_AGENT_RUNTIME_MARKER,
950
+ // With a collab channel selected, omit the SDK's own 'web' stamp: the
951
+ // request's explicit start_channel would win over the backend overlay's
952
+ // kind-mapped provenance (a custom SMS channel must stamp 'sms').
953
+ ...collabChannelId ? {} : { start_channel: STATEWIRE_START_CHANNEL },
954
+ ...model ? { model } : {},
955
+ // An empty list is behavior-identical to an absent key for the parser
956
+ // (both coerce to []), but the collab-agent overlay merges request keys
957
+ // OVER the agent definition — so an explicit empty list would erase the
958
+ // agent's configured tools. Omit it when empty.
959
+ ...enabledTools.length > 0 ? { enabled_tools: enabledTools } : {},
960
+ panel_config: {
961
+ model: panelModel,
962
+ enabled_tools: enabledTools,
963
+ system_prompt: systemPrompt,
964
+ run_label: STATEWIRE_RUN_LABEL
965
+ },
966
+ ...collabAgentId ? { collab_agent_id: collabAgentId } : {},
967
+ ...collabChannelId ? { collab_channel_id: collabChannelId } : {},
968
+ ...systemPrompt ? { system_prompt: systemPrompt } : {},
969
+ ...((_c2 = options.workbench) == null ? void 0 : _c2.length) ? { workbench: options.workbench } : {},
970
+ ...((_d2 = options.knowledgeBase) == null ? void 0 : _d2.length) ? { knowledge_base: options.knowledgeBase } : {},
971
+ ...options.customToolConfigs ? { custom_tool_configs: options.customToolConfigs } : {},
972
+ ...options.appId ? { app_id: options.appId } : {},
973
+ ...((_e2 = options.clientTools) == null ? void 0 : _e2.length) ? { client_tools: options.clientTools } : {},
974
+ ...options.extraRunConfig ?? {}
975
+ }
976
+ };
977
+ }
978
+ const ATHENA_ENVIRONMENT_URLS = {
979
+ production: {
980
+ apiUrl: "https://iris.prd.athenaintel.com/api/chat",
981
+ backendUrl: "https://api.athenaintel.com/api/assistant-ui",
982
+ appUrl: "https://app.athenaintel.com"
983
+ },
984
+ staging: {
985
+ apiUrl: "https://iris.stg.athenaintel.com/api/chat",
986
+ backendUrl: "https://staging-api.athenaintel.com/api/assistant-ui",
987
+ appUrl: "https://staging-app.athenaintel.com"
988
+ }
989
+ };
990
+ const DEFAULT_ATHENA_ENVIRONMENT = "production";
991
+ const DEFAULT_API_URL = ATHENA_ENVIRONMENT_URLS.production.apiUrl;
992
+ const DEFAULT_BACKEND_URL = ATHENA_ENVIRONMENT_URLS.production.backendUrl;
993
+ const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;
994
+ const DEFAULT_STATEWIRE_SYNC_URL = "https://iris.prd.athenaintel.com/v2/threads";
995
+ const STATEWIRE_SYNC_PATHNAME = "/v2/threads";
996
+ function deriveStatewireSyncUrl({
997
+ apiUrl
998
+ }) {
999
+ if (!apiUrl) return null;
1000
+ try {
1001
+ const parsedUrl = new URL(apiUrl);
1002
+ return `${parsedUrl.origin}${STATEWIRE_SYNC_PATHNAME}`;
1003
+ } catch {
1004
+ return null;
1005
+ }
1006
+ }
1007
+ function resolveStatewireSyncUrl({
1008
+ statewireSyncUrl,
1009
+ apiUrl
1010
+ }) {
1011
+ if (statewireSyncUrl) return statewireSyncUrl;
1012
+ const derived = deriveStatewireSyncUrl({ apiUrl });
1013
+ if (derived) return derived;
1014
+ throw new Error(
1015
+ `[AthenaSDK] transport 'statewire' could not derive a sync URL from apiUrl (${JSON.stringify(
1016
+ apiUrl ?? null
1017
+ )}). Pass an explicit statewireSyncUrl (the …/v2/threads mount). Credentials are never sent to a default host.`
1018
+ );
1019
+ }
1020
+ const MARATHON_APP_PORT = "8082";
1021
+ const SPACES_PATHNAME = "dashboard/spaces/";
1022
+ const normalizeBaseUrl = (url) => url.replace(/\/+$/, "");
1023
+ const createUrlWithFallback = (url, fallbackUrl) => {
1024
+ try {
1025
+ return new URL(`${normalizeBaseUrl(url)}/`);
1026
+ } catch {
1027
+ return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);
1028
+ }
1029
+ };
1030
+ const getHostname$1 = (value) => {
1031
+ if (!value) return null;
1032
+ try {
1033
+ return new URL(value).hostname;
1034
+ } catch {
1035
+ return null;
1036
+ }
1037
+ };
1038
+ const deriveWorkspaceAppUrl = ({
1039
+ sourceUrl,
1040
+ sourcePort
1041
+ }) => {
1042
+ var _a3;
1043
+ try {
1044
+ const parsedUrl = new URL(sourceUrl);
1045
+ const match2 = parsedUrl.hostname.match(
1046
+ new RegExp(`^(?<prefix>.+)--${sourcePort}\\.(?<domain>app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$`)
1047
+ );
1048
+ if (!((_a3 = match2 == null ? void 0 : match2.groups) == null ? void 0 : _a3.prefix) || !match2.groups.domain) {
1049
+ return null;
1050
+ }
1051
+ return `${parsedUrl.protocol}//${match2.groups.prefix}--${MARATHON_APP_PORT}.${match2.groups.domain}`;
1052
+ } catch {
1053
+ return null;
1054
+ }
1055
+ };
1056
+ const deriveWorkspaceApiUrl = (backendUrl) => {
1057
+ var _a3;
1058
+ try {
1059
+ const parsedUrl = new URL(backendUrl);
1060
+ const match2 = parsedUrl.hostname.match(
1061
+ /^(?<prefix>.+)--8000\.(?<domain>app\.athenaintel\.com|staging-app\.athenaintel\.com)$/
1062
+ );
1063
+ if (!((_a3 = match2 == null ? void 0 : match2.groups) == null ? void 0 : _a3.prefix) || !match2.groups.domain) {
1064
+ return null;
1065
+ }
1066
+ return `${parsedUrl.protocol}//${match2.groups.prefix}--8787.${match2.groups.domain}/api/chat`;
1067
+ } catch {
1068
+ return null;
1069
+ }
1070
+ };
1071
+ function getAthenaEnvironmentUrls({
1072
+ environment = DEFAULT_ATHENA_ENVIRONMENT
1073
+ } = {}) {
1074
+ return ATHENA_ENVIRONMENT_URLS[environment];
1075
+ }
1076
+ function deriveAthenaApiUrl({
1077
+ backendUrl
1078
+ }) {
1079
+ if (!backendUrl) return null;
1080
+ const workspaceApiUrl = deriveWorkspaceApiUrl(backendUrl);
1081
+ if (workspaceApiUrl) return workspaceApiUrl;
1082
+ try {
1083
+ const parsedUrl = new URL(backendUrl);
1084
+ if (parsedUrl.hostname === "api.athenaintel.com") {
1085
+ return ATHENA_ENVIRONMENT_URLS.production.apiUrl;
1086
+ }
1087
+ if (parsedUrl.hostname === "staging-api.athenaintel.com") {
1088
+ return ATHENA_ENVIRONMENT_URLS.staging.apiUrl;
1089
+ }
1090
+ if (parsedUrl.hostname === "localhost" || parsedUrl.hostname === "127.0.0.1") {
1091
+ parsedUrl.port = "8787";
1092
+ parsedUrl.pathname = "/api/chat";
1093
+ parsedUrl.search = "";
1094
+ parsedUrl.hash = "";
1095
+ return parsedUrl.toString();
1096
+ }
1097
+ } catch {
1098
+ return null;
1099
+ }
1100
+ return null;
1101
+ }
1102
+ function deriveAthenaAppUrl({
1103
+ apiUrl,
1104
+ backendUrl
1105
+ }) {
1106
+ if (backendUrl) {
1107
+ const workspaceAppUrl = deriveWorkspaceAppUrl({
1108
+ sourceUrl: backendUrl,
1109
+ sourcePort: "8000"
1110
+ });
1111
+ if (workspaceAppUrl) return workspaceAppUrl;
1112
+ const backendHostname = getHostname$1(backendUrl);
1113
+ if (backendHostname === "api.athenaintel.com") {
1114
+ return ATHENA_ENVIRONMENT_URLS.production.appUrl;
1115
+ }
1116
+ if (backendHostname === "staging-api.athenaintel.com") {
1117
+ return ATHENA_ENVIRONMENT_URLS.staging.appUrl;
1118
+ }
1119
+ }
1120
+ if (apiUrl) {
1121
+ const workspaceAppUrl = deriveWorkspaceAppUrl({
1122
+ sourceUrl: apiUrl,
1123
+ sourcePort: "8787"
1124
+ });
1125
+ if (workspaceAppUrl) return workspaceAppUrl;
1126
+ const apiHostname = getHostname$1(apiUrl);
1127
+ if (apiHostname === "sync.athenaintel.com" || apiHostname === "iris.prd.athenaintel.com") {
1128
+ return ATHENA_ENVIRONMENT_URLS.production.appUrl;
1129
+ }
1130
+ if (apiHostname === "staging-sync.athenaintel.com" || apiHostname === "iris.stg.athenaintel.com") {
1131
+ return ATHENA_ENVIRONMENT_URLS.staging.appUrl;
1132
+ }
1133
+ }
1134
+ return null;
1135
+ }
1136
+ function createAthenaSpacesUrl({
1137
+ appUrl = DEFAULT_APP_URL,
1138
+ assetIds,
1139
+ sessionId
1140
+ }) {
1141
+ const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));
1142
+ if (assetIds) {
1143
+ const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds]).map((assetId) => assetId.trim()).filter((assetId) => assetId.length > 0);
1144
+ if (normalizedAssetIds.length > 0) {
1145
+ url.searchParams.set("asset_ids", normalizedAssetIds.join(","));
1146
+ }
1147
+ }
1148
+ if (sessionId) {
1149
+ url.searchParams.set("session_id", sessionId);
1150
+ }
1151
+ return url.toString();
1152
+ }
1153
+ function isTrustedOrigin({
1154
+ origin,
1155
+ trustedOrigins
1156
+ }) {
1157
+ try {
1158
+ const normalizedOrigin = new URL(origin).origin;
1159
+ if (trustedOrigins.includes(normalizedOrigin)) {
1160
+ return true;
1161
+ }
1162
+ const { hostname } = new URL(normalizedOrigin);
1163
+ return hostname === "athenaintel.com" || hostname.endsWith(".athenaintel.com") || hostname === "localhost";
1164
+ } catch {
1165
+ return false;
1166
+ }
1167
+ }
1168
+ const BRIDGE_TIMEOUT_MS = 2e3;
1169
+ const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
1170
+ const TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1e3;
1171
+ const TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1e3;
1172
+ const DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1e3;
1173
+ const EMPTY_TRUSTED_ORIGINS = [];
1174
+ const normalizeOrigin = (value) => {
1175
+ try {
1176
+ return new URL(value).origin;
1177
+ } catch {
1178
+ return null;
1179
+ }
1180
+ };
1181
+ const getTokenRefreshDelay = (expiresAt, nowMs2) => {
1182
+ if (typeof expiresAt !== "string") {
1183
+ return DEFAULT_TOKEN_REFRESH_MS;
1184
+ }
1185
+ const expiresAtMs = Date.parse(expiresAt);
1186
+ if (!Number.isFinite(expiresAtMs)) {
1187
+ return DEFAULT_TOKEN_REFRESH_MS;
1188
+ }
1189
+ return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs2 - TOKEN_REFRESH_BUFFER_MS);
1190
+ };
1191
+ const getAuthRetryDelay = (retryAttempt) => {
1192
+ const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;
1193
+ return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);
1194
+ };
1195
+ function useParentBridge({
1196
+ trustedOrigins = EMPTY_TRUSTED_ORIGINS
1197
+ } = {}) {
1198
+ const isInIframe = typeof window !== "undefined" && window.parent !== window;
1199
+ const runtimeTrustedOrigins = React.useMemo(() => {
1200
+ const origins = /* @__PURE__ */ new Set();
1201
+ if (typeof window !== "undefined") {
1202
+ origins.add(window.location.origin);
299
1203
  }
300
1204
  for (const trustedOrigin of trustedOrigins) {
301
1205
  const normalizedOrigin = normalizeOrigin(trustedOrigin);
@@ -316,6 +1220,15 @@ function useParentBridge({
316
1220
  const configReceived = React.useRef(false);
317
1221
  React.useEffect(() => {
318
1222
  if (!isInIframe) return;
1223
+ const endBridgeSpan = athenaDiagnostics.span("auth.bridge", "auth.bridge.start", {
1224
+ mode: "iframe"
1225
+ });
1226
+ let spanEnded = false;
1227
+ const endOnce = (name, options) => {
1228
+ if (spanEnded) return 0;
1229
+ spanEnded = true;
1230
+ return endBridgeSpan(name, options);
1231
+ };
319
1232
  const handler = (event) => {
320
1233
  if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {
321
1234
  return;
@@ -334,6 +1247,11 @@ function useParentBridge({
334
1247
  }));
335
1248
  }
336
1249
  if (event.data.type === "athena-auth" && typeof event.data.token === "string") {
1250
+ endOnce("auth.bridge.ready", { data: { mode: "iframe" } });
1251
+ athenaDiagnostics.emit("auth.token.acquired", {
1252
+ level: "debug",
1253
+ data: { source: "iframe-postmessage" }
1254
+ });
337
1255
  setState((prev) => ({
338
1256
  ...prev,
339
1257
  token: event.data.token,
@@ -347,6 +1265,13 @@ function useParentBridge({
347
1265
  readySignalSent.current = true;
348
1266
  }
349
1267
  const timer = setTimeout(() => {
1268
+ endOnce("auth.bridge.timeout", {
1269
+ data: {
1270
+ mode: "iframe",
1271
+ timeout_ms: BRIDGE_TIMEOUT_MS,
1272
+ config_received: configReceived.current
1273
+ }
1274
+ });
350
1275
  setState((prev) => prev.ready ? prev : { ...prev, ready: true });
351
1276
  }, BRIDGE_TIMEOUT_MS);
352
1277
  return () => {
@@ -399,9 +1324,20 @@ function useParentBridge({
399
1324
  }) => {
400
1325
  controller == null ? void 0 : controller.abort();
401
1326
  controller = new AbortController();
1327
+ const isInitial = markReadyOnFailure;
1328
+ const rawEndSpan = isInitial ? athenaDiagnostics.span("auth.bridge", "auth.bridge.start", { mode: "marathon" }) : null;
1329
+ let spanEnded = false;
1330
+ const endBridgeSpan = rawEndSpan ? (name, options) => {
1331
+ if (spanEnded) return 0;
1332
+ spanEnded = true;
1333
+ return rawEndSpan(name, options);
1334
+ } : null;
402
1335
  clearRequestTimer();
403
1336
  requestTimer = setTimeout(() => {
404
1337
  controller == null ? void 0 : controller.abort();
1338
+ endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.timeout", {
1339
+ data: { mode: "marathon", timeout_ms: BRIDGE_TIMEOUT_MS }
1340
+ });
405
1341
  if (markReadyOnFailure) {
406
1342
  markReady();
407
1343
  }
@@ -415,17 +1351,43 @@ function useParentBridge({
415
1351
  if (cancelled) return;
416
1352
  if (!resp.ok) {
417
1353
  if (resp.status === 404) {
1354
+ endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.absent", {
1355
+ data: { mode: "marathon", status: resp.status }
1356
+ });
418
1357
  markReady();
419
- } else if (markReadyOnFailure) {
420
- markReady();
421
- scheduleRetry();
422
1358
  } else {
1359
+ const failure = {
1360
+ level: "warn",
1361
+ data: { mode: "marathon", status: resp.status }
1362
+ };
1363
+ if (endBridgeSpan) {
1364
+ endBridgeSpan("auth.bridge.error", failure);
1365
+ } else {
1366
+ athenaDiagnostics.emit("auth.token.refresh_failed", failure);
1367
+ }
1368
+ if (markReadyOnFailure) {
1369
+ markReady();
1370
+ }
423
1371
  scheduleRetry();
424
1372
  }
425
1373
  return;
426
1374
  }
427
1375
  const data = await resp.json();
428
1376
  if (cancelled) return;
1377
+ if (endBridgeSpan) {
1378
+ endBridgeSpan("auth.bridge.ready", {
1379
+ data: { mode: "marathon", has_token: typeof data.token === "string" }
1380
+ });
1381
+ athenaDiagnostics.emit("auth.token.acquired", {
1382
+ level: "debug",
1383
+ data: { source: "marathon", expires_at: data.expires_at }
1384
+ });
1385
+ } else {
1386
+ athenaDiagnostics.emit("auth.token.refreshed", {
1387
+ level: "debug",
1388
+ data: { source: "marathon", expires_at: data.expires_at }
1389
+ });
1390
+ }
429
1391
  setState({
430
1392
  token: typeof data.token === "string" ? data.token : null,
431
1393
  apiUrl: typeof data.apiUrl === "string" ? data.apiUrl : null,
@@ -434,7 +1396,15 @@ function useParentBridge({
434
1396
  ready: true
435
1397
  });
436
1398
  scheduleRefresh(data.expires_at);
437
- } catch {
1399
+ } catch (error2) {
1400
+ if (!cancelled && !(error2 instanceof DOMException && error2.name === "AbortError")) {
1401
+ const failure = { level: "warn", data: { mode: "marathon" }, error: error2 };
1402
+ if (endBridgeSpan) {
1403
+ endBridgeSpan("auth.bridge.error", failure);
1404
+ } else {
1405
+ athenaDiagnostics.emit("auth.token.refresh_failed", failure);
1406
+ }
1407
+ }
438
1408
  if (markReadyOnFailure) {
439
1409
  markReady();
440
1410
  }
@@ -9880,24 +10850,42 @@ function getAthenaApiBaseUrl(backendUrl) {
9880
10850
  return stripped;
9881
10851
  }
9882
10852
  async function listThreads(backendUrl, auth, opts = {}) {
10853
+ var _a3;
9883
10854
  const base2 = getAthenaApiBaseUrl(backendUrl);
9884
- const res = await fetch(`${base2}/api/conversations/threads/list`, {
9885
- method: "POST",
9886
- headers: { "Content-Type": "application/json", ...getAuthHeaders(auth) },
9887
- body: JSON.stringify({
9888
- limit: opts.limit ?? 50,
9889
- offset: opts.offset ?? 0,
9890
- // Default to the full recent-conversations set unless a caller
9891
- // explicitly asks to hide triggered/background sessions.
9892
- exclude_triggered: opts.exclude_triggered ?? false,
9893
- ...opts.app_id ? { app_id: opts.app_id } : {},
9894
- ...opts.start_channel ? { start_channel: opts.start_channel } : {}
9895
- })
10855
+ const endSpan = athenaDiagnostics.span("threads.list", "threads.list.start", {
10856
+ auth_mode: getAuthMode(auth),
10857
+ app_id: opts.app_id
9896
10858
  });
9897
- if (!res.ok) {
9898
- throw new Error(`[AthenaSDK] Failed to list threads: ${res.status}`);
10859
+ try {
10860
+ const res = await fetch(`${base2}/api/conversations/threads/list`, {
10861
+ method: "POST",
10862
+ headers: { "Content-Type": "application/json", ...getAuthHeaders(auth) },
10863
+ body: JSON.stringify({
10864
+ limit: opts.limit ?? 50,
10865
+ offset: opts.offset ?? 0,
10866
+ // Default to the full recent-conversations set unless a caller
10867
+ // explicitly asks to hide triggered/background sessions.
10868
+ exclude_triggered: opts.exclude_triggered ?? false,
10869
+ ...opts.app_id ? { app_id: opts.app_id } : {},
10870
+ ...opts.start_channel ? { start_channel: opts.start_channel } : {}
10871
+ })
10872
+ });
10873
+ if (!res.ok) {
10874
+ throw new AthenaSdkError({
10875
+ code: res.status === 401 || res.status === 403 ? "auth_rejected" : "threads_list_failed",
10876
+ message: `Failed to list threads: ${res.status}`,
10877
+ status: res.status,
10878
+ hint: res.status === 401 || res.status === 403 ? "The credential was refused. Check the token/API key and that the user belongs to the workspace." : "Check backendUrl points at the Agora API base.",
10879
+ context: { auth_mode: getAuthMode(auth) }
10880
+ });
10881
+ }
10882
+ const data = await res.json();
10883
+ endSpan("threads.list.done", { data: { count: ((_a3 = data.threads) == null ? void 0 : _a3.length) ?? 0 } });
10884
+ return data;
10885
+ } catch (error2) {
10886
+ endSpan("threads.list.error", { error: error2 });
10887
+ throw error2;
9899
10888
  }
9900
- return res.json();
9901
10889
  }
9902
10890
  async function setThreadReadState(backendUrl, auth, opts) {
9903
10891
  const base2 = getAthenaApiBaseUrl(backendUrl);
@@ -9958,31 +10946,49 @@ function getResponseErrorBody(response) {
9958
10946
  return response.text().catch(() => "");
9959
10947
  }
9960
10948
  async function getThreadState(backendUrl, auth, threadId) {
10949
+ var _a3, _b2, _c2, _d2;
9961
10950
  const base2 = getAthenaApiBaseUrl(backendUrl);
9962
10951
  const authMode = getAuthMode(auth);
9963
10952
  const endpoint = `${base2}/api/conversations/threads/get`;
9964
- console.info("[AthenaSDK] Loading thread state from conversations API:", {
9965
- threadId,
9966
- endpoint,
9967
- authMode,
9968
- hasToken: Boolean(auth.token),
9969
- hasApiKey: Boolean(auth.apiKey)
9970
- });
9971
- const res = await fetch(endpoint, {
9972
- method: "POST",
9973
- headers: { "Content-Type": "application/json", ...getAuthHeaders(auth) },
9974
- body: JSON.stringify({
9975
- thread_id: threadId,
9976
- skip_cache: true
9977
- })
10953
+ const endSpan = athenaDiagnostics.span("threads.history", "threads.history.start", {
10954
+ thread_id: threadId,
10955
+ auth_mode: authMode
9978
10956
  });
10957
+ let res;
10958
+ try {
10959
+ res = await fetch(endpoint, {
10960
+ method: "POST",
10961
+ headers: { "Content-Type": "application/json", ...getAuthHeaders(auth) },
10962
+ body: JSON.stringify({
10963
+ thread_id: threadId,
10964
+ skip_cache: true
10965
+ })
10966
+ });
10967
+ } catch (error2) {
10968
+ endSpan("threads.history.error", { error: error2, data: { thread_id: threadId } });
10969
+ throw error2;
10970
+ }
9979
10971
  if (!res.ok) {
9980
10972
  const body = await getResponseErrorBody(res);
9981
- throw new Error(
9982
- `[AthenaSDK] Failed to get thread state: ${res.status} (${authMode})${body ? `: ${body.slice(0, 300)}` : ""}`
9983
- );
10973
+ const error2 = new AthenaSdkError({
10974
+ code: res.status === 401 || res.status === 403 ? "auth_rejected" : "thread_load_failed",
10975
+ message: `Failed to get thread state: ${res.status} (${authMode})${body ? `: ${body.slice(0, 300)}` : ""}`,
10976
+ status: res.status,
10977
+ hint: "A brand-new thread may not be persisted yet; anything else means the credential or backendUrl is wrong.",
10978
+ context: { thread_id: threadId, auth_mode: authMode }
10979
+ });
10980
+ endSpan("threads.history.error", { error: error2, data: { thread_id: threadId } });
10981
+ throw error2;
9984
10982
  }
9985
10983
  const data = await res.json();
10984
+ const channelMessages = (_b2 = (_a3 = data.thread) == null ? void 0 : _a3.channel_values) == null ? void 0 : _b2.messages;
10985
+ endSpan("threads.history.done", {
10986
+ data: {
10987
+ thread_id: threadId,
10988
+ found: data.thread_found !== false,
10989
+ message_count: Array.isArray(channelMessages) ? channelMessages.length : ((_d2 = (_c2 = data.thread) == null ? void 0 : _c2.messages) == null ? void 0 : _d2.length) ?? 0
10990
+ }
10991
+ });
9986
10992
  const thread = data.thread;
9987
10993
  if (data.thread_found === false || !thread) {
9988
10994
  return {
@@ -10264,17 +11270,35 @@ const useAthenaRuntime = (config2) => {
10264
11270
  };
10265
11271
  },
10266
11272
  onResponse: () => {
11273
+ athenaDiagnostics.emit("stream.connected", {
11274
+ level: "debug",
11275
+ data: { transport: "legacy", thread_id: threadIdRef.current }
11276
+ });
10267
11277
  if (IS_DEV) {
10268
11278
  console.log("[AthenaSDK] Stream connected");
10269
11279
  }
10270
11280
  },
10271
11281
  onFinish: () => {
11282
+ athenaDiagnostics.emit("run.done", {
11283
+ level: "debug",
11284
+ data: { transport: "legacy", thread_id: threadIdRef.current }
11285
+ });
10272
11286
  if (IS_DEV) {
10273
11287
  console.log("[AthenaSDK] Stream completed");
10274
11288
  }
10275
11289
  },
10276
11290
  onError: (error2, { commands: commands2, updateState }) => {
10277
11291
  var _a3;
11292
+ athenaDiagnostics.error(
11293
+ "stream.error",
11294
+ error2,
11295
+ {
11296
+ code: /\b401\b|unauthori[sz]ed/i.test(error2.message) ? "auth_rejected" : "stream_failed",
11297
+ message: error2.message || "The chat stream failed",
11298
+ hint: "Check the Iris apiUrl and the credential; the raw error is in `cause`."
11299
+ },
11300
+ { transport: "legacy", thread_id: threadIdRef.current }
11301
+ );
10278
11302
  const pendingCommands = commandsToMessages(commands2);
10279
11303
  const isInvalidStringLength = error2 instanceof RangeError && /Invalid string length/i.test(error2.message);
10280
11304
  const userErrorMessage = isInvalidStringLength ? "The response was too large to process. Try reducing the amount of content in a single request or starting a new chat." : error2.message;
@@ -10396,33 +11420,21 @@ const useAthenaRuntime = (config2) => {
10396
11420
  if (isExistingThread && !hasResumedRef.current) {
10397
11421
  hasResumedRef.current = true;
10398
11422
  (async () => {
10399
- var _a3, _b2;
11423
+ var _a3;
10400
11424
  const currentToken = getTokenRef.current ? await getTokenRef.current() ?? tokenRef.current : tokenRef.current;
10401
11425
  const auth = { apiKey: apiKeyRef.current, token: currentToken };
10402
- console.log("[AthenaSDK] Loading existing thread state:", {
10403
- threadId,
10404
- backendUrl
10405
- });
10406
11426
  try {
10407
11427
  const state = await getThreadState(backendUrl, auth, threadId);
10408
- const messageCount = ((_a3 = state == null ? void 0 : state.messages) == null ? void 0 : _a3.length) ?? 0;
10409
- console.log("[AthenaSDK] Got thread state:", {
10410
- threadId,
10411
- messageCount,
10412
- hasMessages: messageCount > 0
10413
- });
10414
11428
  runtime.thread.importExternalState({
10415
11429
  ...state
10416
11430
  });
10417
- console.log(
10418
- "[AthenaSDK] importExternalState completed, runtime messages:",
10419
- runtime.thread.getState().messages.length
10420
- );
10421
11431
  } catch (err) {
10422
- console.warn(
10423
- "[AthenaSDK] Failed to load thread state (may be a new streaming thread):",
10424
- err
10425
- );
11432
+ if (IS_DEV) {
11433
+ console.warn(
11434
+ "[AthenaSDK] Failed to load thread state (may be a new streaming thread):",
11435
+ err
11436
+ );
11437
+ }
10426
11438
  }
10427
11439
  try {
10428
11440
  const statusRes = await fetch(resolvedStatusApiUrl, {
@@ -10434,7 +11446,7 @@ const useAthenaRuntime = (config2) => {
10434
11446
  const status = await statusRes.json();
10435
11447
  if (status.isRunning) {
10436
11448
  try {
10437
- const lastMessageId = ((_b2 = runtime.thread.getState().messages.at(-1)) == null ? void 0 : _b2.id) ?? null;
11449
+ const lastMessageId = ((_a3 = runtime.thread.getState().messages.at(-1)) == null ? void 0 : _a3.id) ?? null;
10438
11450
  runtime.thread.resumeRun({ parentId: lastMessageId });
10439
11451
  } catch (resumeErr) {
10440
11452
  if (IS_DEV) {
@@ -10582,6 +11594,7 @@ function projectDeepAgentConnection(connection) {
10582
11594
  return {
10583
11595
  status: connection.status,
10584
11596
  cause: connection.cause,
11597
+ attempt: connection.attempt,
10585
11598
  ...message !== void 0 && { message }
10586
11599
  };
10587
11600
  }
@@ -10757,6 +11770,22 @@ function liftAopSourceIntoMetadata(message) {
10757
11770
  }
10758
11771
  };
10759
11772
  }
11773
+ function liftBranchIntoMetadata(message) {
11774
+ if (message.type !== "human" && message.type !== "ai") return message;
11775
+ const branch = message.branch;
11776
+ if (typeof branch !== "object" || branch === null || Array.isArray(branch)) return message;
11777
+ const kwargs = message.additional_kwargs;
11778
+ const existingMetadata = kwargs == null ? void 0 : kwargs.metadata;
11779
+ const metadata = typeof existingMetadata === "object" && existingMetadata !== null ? existingMetadata : {};
11780
+ if (typeof metadata.branch === "object" && metadata.branch !== null) return message;
11781
+ return {
11782
+ ...message,
11783
+ additional_kwargs: {
11784
+ ...kwargs,
11785
+ metadata: { ...metadata, branch }
11786
+ }
11787
+ };
11788
+ }
10760
11789
  const TOOL_MESSAGE_INTERNAL_KWARGS = /* @__PURE__ */ new Set([
10761
11790
  "reasoning",
10762
11791
  "tool_outputs",
@@ -10809,11 +11838,13 @@ function isConvertibleChatMessage(message) {
10809
11838
  }
10810
11839
  function sanitizeLangChainMessages(messages) {
10811
11840
  return messages.filter((message) => isConvertibleChatMessage(message) && !isMessageHiddenFromFrontend(message)).map(
10812
- (message) => liftToolMessageMetadataIntoArtifact(
10813
- liftAopSourceIntoMetadata(
10814
- liftCompactionBoundaryIntoMetadata(
10815
- liftConversationStatsIntoMetadata(
10816
- liftModelIntoMetadata(liftRunMarkerIntoMetadata(message))
11841
+ (message) => liftBranchIntoMetadata(
11842
+ liftToolMessageMetadataIntoArtifact(
11843
+ liftAopSourceIntoMetadata(
11844
+ liftCompactionBoundaryIntoMetadata(
11845
+ liftConversationStatsIntoMetadata(
11846
+ liftModelIntoMetadata(liftRunMarkerIntoMetadata(message))
11847
+ )
10817
11848
  )
10818
11849
  )
10819
11850
  )
@@ -10846,14 +11877,15 @@ function registerDeepAgentRunConfig(aui, getRunConfig) {
10846
11877
  })
10847
11878
  });
10848
11879
  }
10849
- function deepAgentThreadStatus(state) {
10850
- var _a3;
10851
- const runs = state == null ? void 0 : state.runs;
10852
- if (runs === void 0) return "loading";
10853
- return ((_a3 = runs[0]) == null ? void 0 : _a3.status) ?? "ready";
10854
- }
10855
11880
  function useDeepAgentThreadStatus() {
10856
- return reactStatewire.useStatewireState(deepAgentThreadStatus);
11881
+ return store.useAuiState((s) => {
11882
+ var _a3;
11883
+ const status = (_a3 = s.thread.extras) == null ? void 0 : _a3.status;
11884
+ if (status === void 0) {
11885
+ throw new Error("useDeepAgentThreadStatus requires a statewire thread");
11886
+ }
11887
+ return status;
11888
+ });
10857
11889
  }
10858
11890
  function shouldNotifyRunningChange(previous, next) {
10859
11891
  if (previous === null) return next.isRunning;
@@ -11038,6 +12070,9 @@ function useDeepAgentThread({
11038
12070
  return statewireTransport(
11039
12071
  {
11040
12072
  url: `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(threadId)}`,
12073
+ // A never-started chat has no server session yet: the transport
12074
+ // makes no network calls until the first command send.
12075
+ ...preloadRef.current.preload && { isNew: true },
11041
12076
  headers: (ctx) => headersRef.current(ctx),
11042
12077
  ...sessionStore && { sessionStore }
11043
12078
  },
@@ -11074,7 +12109,7 @@ function useDeepAgentThread({
11074
12109
  }
11075
12110
  const connection = projectDeepAgentConnection(meta.connection);
11076
12111
  const previous = lastConnectionRef.current;
11077
- if ((previous == null ? void 0 : previous.threadId) !== attachId || previous.connection.status !== connection.status || previous.connection.cause !== connection.cause || previous.connection.message !== connection.message) {
12112
+ if ((previous == null ? void 0 : previous.threadId) !== attachId || previous.connection.status !== connection.status || previous.connection.cause !== connection.cause || previous.connection.message !== connection.message || previous.connection.attempt !== connection.attempt) {
11078
12113
  const update = { threadId: attachId, connection };
11079
12114
  lastConnectionRef.current = update;
11080
12115
  queueMicrotask(() => {
@@ -11146,46 +12181,30 @@ function toJSONSchema(schema2) {
11146
12181
  if (isStandardSchema(schema2)) throw new Error("Could not convert schema to JSON Schema. The schema implements Standard Schema but does not support JSON Schema conversion. If you are using Zod, please upgrade to Zod v4 (npm install zod@latest). Alternatively, pass a plain JSON Schema object instead.");
11147
12182
  return schema2;
11148
12183
  }
11149
- function defaultToolFilter(_name, tool) {
11150
- return !tool.disabled && tool.type !== "backend" && (tool.type !== "frontend" || tool.execute !== void 0);
11151
- }
11152
- function toolHasUploadableParameters(tool) {
11153
- var _a3;
11154
- return tool.parameters !== void 0 && !((_a3 = tool.unstable_backendDefault) == null ? void 0 : _a3.parameters);
11155
- }
11156
- function toToolsJSONSchema(tools, options = {}) {
11157
- if (!tools) return {};
11158
- const filter = options.filter ?? defaultToolFilter;
11159
- return Object.fromEntries(Object.entries(tools).filter(([name, tool]) => filter(name, tool)).filter((entry) => toolHasUploadableParameters(entry[1])).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, tool]) => [name, {
11160
- ...tool.description && { description: tool.description },
11161
- parameters: toJSONSchema(tool.parameters),
11162
- ...tool.providerOptions && { providerOptions: tool.providerOptions }
11163
- }]));
11164
- }
11165
12184
  function hasCallableExecute(entry) {
11166
12185
  return !!entry && typeof entry === "object" && typeof entry.execute === "function";
11167
12186
  }
11168
12187
  function collectStatewireClientTools(toolkit) {
11169
12188
  const executable = Object.entries(toolkit).filter(([, entry]) => hasCallableExecute(entry));
11170
12189
  if (executable.length === 0) return [];
11171
- let schemas;
11172
- try {
11173
- schemas = toToolsJSONSchema(toolkit, {
11174
- filter: (name) => executable.some(([toolName]) => toolName === name)
11175
- });
11176
- } catch (error2) {
11177
- console.warn("[AthenaSDK] failed to convert frontend tool schemas for statewire:", error2);
11178
- return [];
11179
- }
11180
12190
  return executable.flatMap(([name, entry]) => {
11181
- const schema2 = schemas[name];
11182
- if (!schema2) return [];
12191
+ if (entry.parameters === void 0) return [];
12192
+ let parameters;
12193
+ try {
12194
+ parameters = toJSONSchema(entry.parameters);
12195
+ } catch (error2) {
12196
+ console.warn(
12197
+ `[AthenaSDK] failed to convert frontend tool schema for statewire (${name}):`,
12198
+ error2
12199
+ );
12200
+ return [];
12201
+ }
11183
12202
  const execute = entry.execute;
11184
12203
  return [
11185
12204
  {
11186
12205
  name,
11187
- description: schema2.description,
11188
- parameters: schema2.parameters ?? { type: "object" },
12206
+ description: entry.description,
12207
+ parameters,
11189
12208
  handler: async (args, { toolCallId }) => {
11190
12209
  const controller = new AbortController();
11191
12210
  const output = await execute(args, {
@@ -11209,39 +12228,6 @@ function statewireClientToolWireEntries(tools) {
11209
12228
  requires_write: false
11210
12229
  }));
11211
12230
  }
11212
- const DEEP_AGENT_RUNTIME_MARKER = "athena_deep_agent";
11213
- const STATEWIRE_START_CHANNEL = "web";
11214
- const STATEWIRE_RUN_LABEL = "Athena SDK";
11215
- const DEFAULT_STATEWIRE_MODEL = "claude-sonnet-5";
11216
- function buildStatewireRunConfig(options = {}) {
11217
- var _a3, _b2, _c2, _d2;
11218
- const model = options.model ?? DEFAULT_STATEWIRE_MODEL;
11219
- const enabledTools = Array.from(
11220
- /* @__PURE__ */ new Set([...options.tools ?? [], ...options.frontendToolIds ?? []])
11221
- );
11222
- const systemPrompt = ((_a3 = options.systemPrompt) == null ? void 0 : _a3.trim()) ?? "";
11223
- return {
11224
- custom: {
11225
- runtime: DEEP_AGENT_RUNTIME_MARKER,
11226
- start_channel: STATEWIRE_START_CHANNEL,
11227
- model,
11228
- enabled_tools: enabledTools,
11229
- panel_config: {
11230
- model,
11231
- enabled_tools: enabledTools,
11232
- system_prompt: systemPrompt,
11233
- run_label: STATEWIRE_RUN_LABEL
11234
- },
11235
- ...systemPrompt ? { system_prompt: systemPrompt } : {},
11236
- ...((_b2 = options.workbench) == null ? void 0 : _b2.length) ? { workbench: options.workbench } : {},
11237
- ...((_c2 = options.knowledgeBase) == null ? void 0 : _c2.length) ? { knowledge_base: options.knowledgeBase } : {},
11238
- ...options.customToolConfigs ? { custom_tool_configs: options.customToolConfigs } : {},
11239
- ...options.appId ? { app_id: options.appId } : {},
11240
- ...((_d2 = options.clientTools) == null ? void 0 : _d2.length) ? { client_tools: options.clientTools } : {},
11241
- ...options.extraRunConfig ?? {}
11242
- }
11243
- };
11244
- }
11245
12231
  const EMPTY_TOOLKIT = {};
11246
12232
  const EMPTY_TOOL_IDS = [];
11247
12233
  function useStatewireClientTools(frontendToolkit) {
@@ -11259,6 +12245,8 @@ function useAthenaStatewireRuntime(config2) {
11259
12245
  token,
11260
12246
  getToken,
11261
12247
  model,
12248
+ agent: agent2,
12249
+ channel,
11262
12250
  tools = EMPTY_TOOL_IDS,
11263
12251
  frontendToolIds = EMPTY_TOOL_IDS,
11264
12252
  frontendToolkit = EMPTY_TOOLKIT,
@@ -11286,6 +12274,28 @@ function useAthenaStatewireRuntime(config2) {
11286
12274
  const clientToolsRef = React.useRef(clientTools);
11287
12275
  clientToolsRef.current = clientTools;
11288
12276
  const [canPersistSession] = React.useState(() => typeof localStorage !== "undefined");
12277
+ React.useEffect(() => {
12278
+ if (agent2 && !parseCollabAgentRef(agent2)) {
12279
+ athenaDiagnostics.emit("sdk.config.warning", {
12280
+ level: "warn",
12281
+ data: {
12282
+ prop: "agent",
12283
+ value: agent2,
12284
+ reason: "the statewire transport always runs the Athena deep agent; only 'collab_agent:<asset_id>' refs apply. Use transport='legacy' for named agents."
12285
+ }
12286
+ });
12287
+ }
12288
+ }, [agent2]);
12289
+ const attachSpanRef = React.useRef(null);
12290
+ const attachThreadRef = React.useRef(null);
12291
+ if (attachThreadRef.current !== threadId) {
12292
+ attachThreadRef.current = threadId;
12293
+ attachSpanRef.current = athenaDiagnostics.span(
12294
+ `statewire.attach:${threadId}`,
12295
+ "statewire.attach.start",
12296
+ { thread_id: threadId, is_new_chat: isNewChat }
12297
+ );
12298
+ }
11289
12299
  const thread = useDeepAgentThread({
11290
12300
  threadId,
11291
12301
  baseUrl: syncUrl,
@@ -11315,11 +12325,47 @@ function useAthenaStatewireRuntime(config2) {
11315
12325
  }
11316
12326
  },
11317
12327
  capabilities: { edit: true, reload: true, continue: true },
11318
- onConnectionChange,
12328
+ onStateChange: () => {
12329
+ const endSpan = attachSpanRef.current;
12330
+ if (endSpan) {
12331
+ attachSpanRef.current = null;
12332
+ endSpan("statewire.snapshot", { data: { thread_id: threadId } });
12333
+ }
12334
+ },
12335
+ onConnectionChange: (connection) => {
12336
+ athenaDiagnostics.gauge("statewire.connection", (connection == null ? void 0 : connection.status) ?? null);
12337
+ if ((connection == null ? void 0 : connection.status) === "gone") {
12338
+ athenaDiagnostics.emit("statewire.connection", {
12339
+ level: "warn",
12340
+ data: { status: connection.status, message: connection.message }
12341
+ });
12342
+ }
12343
+ onConnectionChange == null ? void 0 : onConnectionChange(connection);
12344
+ },
11319
12345
  onRawConnectionChange,
11320
- onLegacyReadOnly,
12346
+ onLegacyReadOnly: (legacyReadOnly) => {
12347
+ if (legacyReadOnly) {
12348
+ athenaDiagnostics.emit("statewire.read_only", {
12349
+ level: "warn",
12350
+ data: { thread_id: threadId }
12351
+ });
12352
+ }
12353
+ onLegacyReadOnly == null ? void 0 : onLegacyReadOnly(legacyReadOnly);
12354
+ },
11321
12355
  onRunningChange,
11322
12356
  onError: (error2) => {
12357
+ if (!isStatewireChannelTeardown(error2)) {
12358
+ athenaDiagnostics.error(
12359
+ "statewire.error",
12360
+ error2,
12361
+ {
12362
+ code: "statewire_rejected",
12363
+ message: "The statewire host rejected a command",
12364
+ hint: "Inspect error.detail — agora rejections carry a structured {code, message}."
12365
+ },
12366
+ { thread_id: threadId }
12367
+ );
12368
+ }
11323
12369
  if (onErrorRef.current) {
11324
12370
  onErrorRef.current(error2);
11325
12371
  return;
@@ -11335,6 +12381,8 @@ function useAthenaStatewireRuntime(config2) {
11335
12381
  const aui = react$1.useAui({ thread, tools: auiTools });
11336
12382
  const runConfigInputsRef = React.useRef({
11337
12383
  model,
12384
+ agent: agent2,
12385
+ channel,
11338
12386
  tools,
11339
12387
  frontendToolIds,
11340
12388
  workbench,
@@ -11346,6 +12394,8 @@ function useAthenaStatewireRuntime(config2) {
11346
12394
  });
11347
12395
  runConfigInputsRef.current = {
11348
12396
  model,
12397
+ agent: agent2,
12398
+ channel,
11349
12399
  tools,
11350
12400
  frontendToolIds,
11351
12401
  workbench,
@@ -15914,11 +16964,11 @@ const pickNumber = (...candidates) => {
15914
16964
  return void 0;
15915
16965
  };
15916
16966
  const autoOpen = (assetId, options = {}) => {
15917
- const store = useAssetPanelStore.getState();
15918
- if (!store.markAutoOpened(assetId)) return;
15919
- const existing = store.tabs.find((tab) => tab.id === assetId);
16967
+ const store2 = useAssetPanelStore.getState();
16968
+ if (!store2.markAutoOpened(assetId)) return;
16969
+ const existing = store2.tabs.find((tab) => tab.id === assetId);
15920
16970
  const keepCurrentSlide = options.preserveExistingSlide && existing;
15921
- store.openAsset(assetId, {
16971
+ store2.openAsset(assetId, {
15922
16972
  type: options.type ?? "unknown",
15923
16973
  ...keepCurrentSlide || options.slideNumber === void 0 ? {} : { slideNumber: options.slideNumber }
15924
16974
  });
@@ -19013,779 +20063,333 @@ function useActiveThreadStateHydration({
19013
20063
  threadId,
19014
20064
  apiKey,
19015
20065
  token
19016
- });
19017
- if (hydratedKeysRef.current.has(hydrationKey)) {
19018
- return;
19019
- }
19020
- let cancelled = false;
19021
- (async () => {
19022
- var _a3, _b2;
19023
- try {
19024
- const state = await getThreadState(backendUrl, { apiKey, token }, threadId);
19025
- if (cancelled || activeThreadIdRef.current !== threadId) {
19026
- return;
19027
- }
19028
- if (aui.thread.getState().messages.length > 0) {
19029
- return;
19030
- }
19031
- (_b2 = (_a3 = aui.thread).importExternalState) == null ? void 0 : _b2.call(_a3, state);
19032
- hydratedKeysRef.current.add(hydrationKey);
19033
- } catch (error2) {
19034
- console.warn("[AthenaSDK] Failed to hydrate active thread state:", error2);
19035
- }
19036
- })();
19037
- return () => {
19038
- cancelled = true;
19039
- };
19040
- }, [apiKey, aui, backendUrl, hasThread, isRunning, messageCount, threadId, token]);
19041
- }
19042
- const LOCAL_ID_PREFIX = "__LOCALID_";
19043
- const isLocalPlaceholder = (id) => !!id && id.startsWith(LOCAL_ID_PREFIX);
19044
- const selectActiveThreadRemoteId = (state) => {
19045
- const { mainThreadId, threadItems } = state;
19046
- if (!mainThreadId) {
19047
- return null;
19048
- }
19049
- const item = threadItems.find((t) => t.id === mainThreadId);
19050
- const remoteId = item ? item.remoteId ?? null : mainThreadId;
19051
- return isLocalPlaceholder(remoteId) ? null : remoteId;
19052
- };
19053
- function useAthenaThreadManager() {
19054
- const statewireList = useAthenaStatewireThreadList();
19055
- const statewireIsThreadLoading = react$1.useAuiState((s) => {
19056
- var _a3;
19057
- return ((_a3 = s.optional.thread) == null ? void 0 : _a3.isLoading) ?? false;
19058
- });
19059
- const statewireManager = React.useMemo(() => {
19060
- if (!statewireList) return null;
19061
- return {
19062
- activeThreadId: statewireList.isNewChat ? null : statewireList.activeThreadId,
19063
- isListLoading: statewireList.isLoading,
19064
- isThreadLoading: statewireIsThreadLoading,
19065
- switchToThread: async (threadId) => statewireList.selectThread(threadId),
19066
- switchToNewThread: async () => statewireList.newThread()
19067
- };
19068
- }, [statewireList, statewireIsThreadLoading]);
19069
- const aui = react$1.useAui();
19070
- const hasThreads = aui.threads.source !== null;
19071
- const activeThreadId = react$1.useAuiState((s) => {
19072
- const threads = s.optional.threads;
19073
- return threads ? selectActiveThreadRemoteId(threads) : null;
19074
- });
19075
- const isThreadLoading = react$1.useAuiState((s) => {
19076
- var _a3;
19077
- return ((_a3 = s.optional.thread) == null ? void 0 : _a3.isLoading) ?? false;
19078
- });
19079
- const isListLoading = react$1.useAuiState((s) => {
19080
- var _a3;
19081
- return (_a3 = s.optional.threads) == null ? void 0 : _a3.isLoading;
19082
- });
19083
- const auiRef = React.useRef(aui);
19084
- auiRef.current = aui;
19085
- const switchToThread = React.useCallback(
19086
- async (id) => auiRef.current.threads.switchToThread(id),
19087
- []
19088
- );
19089
- const switchToNewThread = React.useCallback(
19090
- async () => auiRef.current.threads.switchToNewThread(),
19091
- []
19092
- );
19093
- const legacyManager = React.useMemo(() => {
19094
- if (!hasThreads || isListLoading == null) {
19095
- return null;
19096
- }
19097
- return {
19098
- activeThreadId,
19099
- isListLoading,
19100
- isThreadLoading,
19101
- switchToThread,
19102
- switchToNewThread
19103
- };
19104
- }, [hasThreads, activeThreadId, isListLoading, isThreadLoading, switchToThread, switchToNewThread]);
19105
- return statewireManager ?? legacyManager;
19106
- }
19107
- const POLL_DELAY_MS = 5e3;
19108
- const POLL_INTERVAL_MS = 1e3;
19109
- const POLL_MAX_DURATION_MS = 6e4;
19110
- function useThreadTitlePolling(refresh) {
19111
- const threadKey = react$1.useAuiState(
19112
- (s) => {
19113
- var _a3, _b2;
19114
- return ((_a3 = s.optional.threadListItem) == null ? void 0 : _a3.remoteId) ?? ((_b2 = s.optional.threadListItem) == null ? void 0 : _b2.id) ?? null;
19115
- }
19116
- );
19117
- const hasMessages = react$1.useAuiState((s) => {
19118
- var _a3;
19119
- return (((_a3 = s.optional.thread) == null ? void 0 : _a3.messages.length) ?? 0) > 0;
19120
- });
19121
- const currentTitle = react$1.useAuiState((s) => {
19122
- var _a3;
19123
- return ((_a3 = s.optional.threadListItem) == null ? void 0 : _a3.title) ?? "";
19124
- });
19125
- const trimmedTitle = currentTitle.trim();
19126
- const hasTitle = trimmedTitle.length > 0 && !isSerializedContentPartsTitle(trimmedTitle);
19127
- const polledThreadsRef = React.useRef(/* @__PURE__ */ new Set());
19128
- const refreshRef = React.useRef(refresh);
19129
- refreshRef.current = refresh;
19130
- React.useEffect(() => {
19131
- if (!threadKey || hasTitle || !hasMessages) {
19132
- return;
19133
- }
19134
- if (polledThreadsRef.current.has(threadKey)) {
19135
- return;
19136
- }
19137
- polledThreadsRef.current.add(threadKey);
19138
- let stopped = false;
19139
- let intervalId = null;
19140
- let maxTimeoutId = null;
19141
- const stop = () => {
19142
- stopped = true;
19143
- if (intervalId !== null) {
19144
- clearInterval(intervalId);
19145
- intervalId = null;
19146
- }
19147
- if (maxTimeoutId !== null) {
19148
- clearTimeout(maxTimeoutId);
19149
- maxTimeoutId = null;
19150
- }
19151
- };
19152
- const startTimeoutId = setTimeout(() => {
19153
- if (stopped) return;
19154
- refreshRef.current();
19155
- intervalId = setInterval(() => {
19156
- refreshRef.current();
19157
- }, POLL_INTERVAL_MS);
19158
- maxTimeoutId = setTimeout(stop, POLL_MAX_DURATION_MS - POLL_DELAY_MS);
19159
- }, POLL_DELAY_MS);
19160
- return () => {
19161
- clearTimeout(startTimeoutId);
19162
- stop();
19163
- };
19164
- }, [threadKey, hasTitle, hasMessages]);
19165
- }
19166
- const THEME_TO_CSS = {
19167
- primary: "--primary",
19168
- primaryForeground: "--primary-foreground",
19169
- background: "--background",
19170
- foreground: "--foreground",
19171
- muted: "--muted",
19172
- mutedForeground: "--muted-foreground",
19173
- accent: "--accent",
19174
- accentForeground: "--accent-foreground",
19175
- secondary: "--secondary",
19176
- secondaryForeground: "--secondary-foreground",
19177
- card: "--card",
19178
- cardForeground: "--card-foreground",
19179
- popover: "--popover",
19180
- popoverForeground: "--popover-foreground",
19181
- destructive: "--destructive",
19182
- border: "--border",
19183
- input: "--input",
19184
- ring: "--ring",
19185
- radius: "--radius",
19186
- fontFamily: "--font-family",
19187
- // Extended SDK-specific variables
19188
- sidebarBackground: "--sidebar-background",
19189
- sidebarBorder: "--sidebar-border",
19190
- sidebarWidth: "--sidebar-width",
19191
- userBubble: "--user-bubble",
19192
- userBubbleForeground: "--user-bubble-foreground",
19193
- userBubbleRadius: "--user-bubble-radius",
19194
- assistantForeground: "--assistant-foreground",
19195
- assistantBubble: "--assistant-bubble",
19196
- composerBorder: "--composer-border",
19197
- composerRadius: "--composer-radius",
19198
- threadMaxWidth: "--thread-max-width"
19199
- };
19200
- function themeToStyleVars(theme) {
19201
- const vars = {};
19202
- for (const [key, value] of Object.entries(theme)) {
19203
- if (value != null && THEME_TO_CSS[key]) {
19204
- vars[THEME_TO_CSS[key]] = value;
19205
- }
19206
- }
19207
- return vars;
19208
- }
19209
- const themes = {
19210
- /** Default light theme. Neutral grays with blue accent. */
19211
- light: {
19212
- background: "oklch(0.99 0 0)",
19213
- foreground: "oklch(0.13 0 0)",
19214
- primary: "oklch(0.55 0.2 250)",
19215
- primaryForeground: "oklch(1 0 0)",
19216
- secondary: "oklch(0.96 0.005 250)",
19217
- secondaryForeground: "oklch(0.13 0 0)",
19218
- muted: "oklch(0.96 0.005 250)",
19219
- mutedForeground: "oklch(0.5 0.02 250)",
19220
- accent: "oklch(0.94 0.01 250)",
19221
- accentForeground: "oklch(0.13 0 0)",
19222
- card: "oklch(0.99 0 0)",
19223
- cardForeground: "oklch(0.13 0 0)",
19224
- popover: "oklch(0.99 0 0)",
19225
- popoverForeground: "oklch(0.13 0 0)",
19226
- destructive: "oklch(0.55 0.22 27)",
19227
- border: "oklch(0.91 0.005 250)",
19228
- input: "oklch(0.91 0.005 250)",
19229
- ring: "oklch(0.55 0.2 250)",
19230
- radius: "0.625rem"
19231
- },
19232
- /** Dark theme. Deep gray background with lighter text. */
19233
- dark: {
19234
- background: "oklch(0.15 0 0)",
19235
- foreground: "oklch(0.95 0 0)",
19236
- primary: "oklch(0.7 0.15 250)",
19237
- primaryForeground: "oklch(0.13 0 0)",
19238
- secondary: "oklch(0.22 0 0)",
19239
- secondaryForeground: "oklch(0.95 0 0)",
19240
- muted: "oklch(0.22 0 0)",
19241
- mutedForeground: "oklch(0.65 0 0)",
19242
- accent: "oklch(0.25 0 0)",
19243
- accentForeground: "oklch(0.95 0 0)",
19244
- card: "oklch(0.18 0 0)",
19245
- cardForeground: "oklch(0.95 0 0)",
19246
- popover: "oklch(0.18 0 0)",
19247
- popoverForeground: "oklch(0.95 0 0)",
19248
- destructive: "oklch(0.65 0.2 25)",
19249
- border: "oklch(1 0 0 / 10%)",
19250
- input: "oklch(1 0 0 / 15%)",
19251
- ring: "oklch(0.7 0.15 250)",
19252
- radius: "0.625rem"
19253
- },
19254
- /** Midnight theme. Deep navy with soft blue tones. */
19255
- midnight: {
19256
- background: "oklch(0.16 0.02 260)",
19257
- foreground: "oklch(0.92 0.01 250)",
19258
- primary: "oklch(0.68 0.16 250)",
19259
- primaryForeground: "oklch(0.98 0 0)",
19260
- secondary: "oklch(0.22 0.02 260)",
19261
- secondaryForeground: "oklch(0.92 0.01 250)",
19262
- muted: "oklch(0.22 0.02 260)",
19263
- mutedForeground: "oklch(0.6 0.04 250)",
19264
- accent: "oklch(0.26 0.03 260)",
19265
- accentForeground: "oklch(0.92 0.01 250)",
19266
- card: "oklch(0.19 0.02 260)",
19267
- cardForeground: "oklch(0.92 0.01 250)",
19268
- popover: "oklch(0.19 0.02 260)",
19269
- popoverForeground: "oklch(0.92 0.01 250)",
19270
- destructive: "oklch(0.65 0.2 25)",
19271
- border: "oklch(0.3 0.03 260)",
19272
- input: "oklch(0.3 0.03 260)",
19273
- ring: "oklch(0.68 0.16 250)",
19274
- radius: "0.625rem"
19275
- },
19276
- /** Warm earthy theme. Brown and sand tones. */
19277
- warm: {
19278
- background: "oklch(0.98 0.005 80)",
19279
- foreground: "oklch(0.2 0.02 50)",
19280
- primary: "oklch(0.55 0.12 50)",
19281
- primaryForeground: "oklch(0.98 0 0)",
19282
- secondary: "oklch(0.94 0.01 80)",
19283
- secondaryForeground: "oklch(0.2 0.02 50)",
19284
- muted: "oklch(0.95 0.01 80)",
19285
- mutedForeground: "oklch(0.5 0.03 50)",
19286
- accent: "oklch(0.92 0.015 80)",
19287
- accentForeground: "oklch(0.2 0.02 50)",
19288
- card: "oklch(0.98 0.005 80)",
19289
- cardForeground: "oklch(0.2 0.02 50)",
19290
- popover: "oklch(0.98 0.005 80)",
19291
- popoverForeground: "oklch(0.2 0.02 50)",
19292
- destructive: "oklch(0.55 0.2 25)",
19293
- border: "oklch(0.9 0.01 80)",
19294
- input: "oklch(0.9 0.01 80)",
19295
- ring: "oklch(0.55 0.12 50)",
19296
- radius: "0.75rem"
19297
- },
19298
- /** Purple creative theme. Vibrant purple accent. */
19299
- purple: {
19300
- background: "oklch(0.99 0.003 310)",
19301
- foreground: "oklch(0.15 0.02 310)",
19302
- primary: "oklch(0.55 0.22 310)",
19303
- primaryForeground: "oklch(1 0 0)",
19304
- secondary: "oklch(0.96 0.01 310)",
19305
- secondaryForeground: "oklch(0.15 0.02 310)",
19306
- muted: "oklch(0.96 0.01 310)",
19307
- mutedForeground: "oklch(0.5 0.04 310)",
19308
- accent: "oklch(0.94 0.015 310)",
19309
- accentForeground: "oklch(0.15 0.02 310)",
19310
- card: "oklch(0.99 0.003 310)",
19311
- cardForeground: "oklch(0.15 0.02 310)",
19312
- popover: "oklch(0.99 0.003 310)",
19313
- popoverForeground: "oklch(0.15 0.02 310)",
19314
- destructive: "oklch(0.55 0.22 27)",
19315
- border: "oklch(0.92 0.005 310)",
19316
- input: "oklch(0.92 0.005 310)",
19317
- ring: "oklch(0.55 0.22 310)",
19318
- radius: "0.75rem"
19319
- },
19320
- /** Green nature theme. Fresh green tones. */
19321
- green: {
19322
- background: "oklch(0.99 0.003 150)",
19323
- foreground: "oklch(0.15 0.02 150)",
19324
- primary: "oklch(0.55 0.18 155)",
19325
- primaryForeground: "oklch(1 0 0)",
19326
- secondary: "oklch(0.96 0.01 150)",
19327
- secondaryForeground: "oklch(0.15 0.02 150)",
19328
- muted: "oklch(0.96 0.01 150)",
19329
- mutedForeground: "oklch(0.5 0.03 150)",
19330
- accent: "oklch(0.94 0.015 150)",
19331
- accentForeground: "oklch(0.15 0.02 150)",
19332
- card: "oklch(0.99 0.003 150)",
19333
- cardForeground: "oklch(0.15 0.02 150)",
19334
- popover: "oklch(0.99 0.003 150)",
19335
- popoverForeground: "oklch(0.15 0.02 150)",
19336
- destructive: "oklch(0.55 0.22 27)",
19337
- border: "oklch(0.92 0.008 150)",
19338
- input: "oklch(0.92 0.008 150)",
19339
- ring: "oklch(0.55 0.18 155)",
19340
- radius: "0.625rem"
19341
- }
19342
- };
19343
- const DEFAULT_CAPTURE_GATE_CONFIG = {
19344
- globalMaxPerWindow: 50,
19345
- globalWindowMs: 1e4,
19346
- criticalReservePerWindow: 15,
19347
- defaultEventMaxPerWindow: 12,
19348
- defaultEventWindowMs: 6e4,
19349
- defaultDedupeWindowMs: 5e3,
19350
- flushIntervalMs: 3e4,
19351
- flushCoalesceThreshold: 20,
19352
- eventOverrides: {
19353
- memory_profile_sample: { maxPerWindow: 2, windowMs: 3e4, dedupeWindowMs: 15e3 },
19354
- long_task_detected: { maxPerWindow: 3, windowMs: 6e4, dedupeWindowMs: 1e4 },
19355
- collab_connection_status_changed: { maxPerWindow: 6, windowMs: 6e4 },
19356
- collab_first_remote_update: { maxPerWindow: 3, windowMs: 6e4 },
19357
- collab_first_local_update: { maxPerWindow: 3, windowMs: 6e4 },
19358
- collab_render_time_sync_fix: { maxPerWindow: 3, windowMs: 6e4 },
19359
- collab_visibility_recovery: { maxPerWindow: 4, windowMs: 6e4 },
19360
- langgraph_message_validation_error: {
19361
- maxPerWindow: 8,
19362
- windowMs: 6e4,
19363
- dedupeWindowMs: 15e3
19364
- },
19365
- langgraph_messages_validation_error: {
19366
- maxPerWindow: 5,
19367
- windowMs: 6e4,
19368
- dedupeWindowMs: 15e3
19369
- },
19370
- langgraph_interrupt_validation_error: {
19371
- maxPerWindow: 5,
19372
- windowMs: 6e4,
19373
- dedupeWindowMs: 15e3
19374
- },
19375
- langgraph_state_validation_error: {
19376
- maxPerWindow: 5,
19377
- windowMs: 6e4,
19378
- dedupeWindowMs: 15e3
19379
- },
19380
- graphql_error: { maxPerWindow: 12, windowMs: 6e4, dedupeWindowMs: 1e4 },
19381
- chat_error: { maxPerWindow: 8, windowMs: 6e4, dedupeWindowMs: 5e3 },
19382
- memory_critical_threshold: { maxPerWindow: 3, windowMs: 3e5 },
19383
- memory_warning_threshold: { maxPerWindow: 3, windowMs: 3e5 },
19384
- pptx_slide_changed: { maxPerWindow: 8, windowMs: 3e4 }
19385
- }
19386
- };
19387
- const CRITICAL_EVENT_PATTERNS = [
19388
- /(?:^|_)failed$/,
19389
- /(?:^|_)error(?:_|$)/,
19390
- /crash/,
19391
- /^oom_/,
19392
- /aw_snap/,
19393
- /chunk_load/,
19394
- /global_error/,
19395
- /\$exception$/,
19396
- /^404_error$/,
19397
- /^500_error$/,
19398
- /_boundary_/
19399
- ];
19400
- const BYPASS_OPTION_KEY = "__posthog_gate_bypass";
19401
- function isCriticalPosthogEvent(event) {
19402
- return CRITICAL_EVENT_PATTERNS.some((pattern) => pattern.test(event));
19403
- }
19404
- function shouldBypassCaptureGate(options) {
19405
- if (!options || typeof options !== "object") return false;
19406
- return options[BYPASS_OPTION_KEY] === true;
19407
- }
19408
- function withCaptureGateBypass(options) {
19409
- return {
19410
- ...options ?? {},
19411
- [BYPASS_OPTION_KEY]: true
19412
- };
19413
- }
19414
- function pruneWindow(window2, now, windowMs) {
19415
- const cutoff = now - windowMs;
19416
- while (window2.timestamps.length > 0 && window2.timestamps[0] < cutoff) {
19417
- window2.timestamps.shift();
19418
- }
19419
- }
19420
- function countInWindow(window2, now, windowMs) {
19421
- pruneWindow(window2, now, windowMs);
19422
- return window2.timestamps.length;
19423
- }
19424
- function recordInWindow(window2, now) {
19425
- window2.timestamps.push(now);
19426
- }
19427
- function stableStringify(value) {
19428
- if (value == null) return String(value);
19429
- if (typeof value !== "object") return String(value);
19430
- if (Array.isArray(value)) {
19431
- return `[${value.map((item) => stableStringify(item)).join(",")}]`;
19432
- }
19433
- const record2 = value;
19434
- const keys2 = Object.keys(record2).sort();
19435
- return `{${keys2.map((key) => `${key}:${stableStringify(record2[key])}`).join(",")}}`;
19436
- }
19437
- function fingerprintProperties(properties) {
19438
- if (!properties) return "";
19439
- const keys2 = [
19440
- "error",
19441
- "error_message",
19442
- "message",
19443
- "reason",
19444
- "status",
19445
- "code",
19446
- "asset_id",
19447
- "doc_id",
19448
- "thread_id",
19449
- "session_id",
19450
- "operation",
19451
- "path",
19452
- "url",
19453
- "chunk_identifier",
19454
- "validation_path"
19455
- ];
19456
- const subset = {};
19457
- for (const key of keys2) {
19458
- if (key in properties) {
19459
- subset[key] = properties[key];
19460
- }
19461
- }
19462
- return stableStringify(subset);
19463
- }
19464
- function buildCaptureFingerprint(event, properties) {
19465
- return `${event}|${fingerprintProperties(properties)}`;
19466
- }
19467
- function buildCoalescedCaptureProperties(payload) {
19468
- const windowMs = payload.lastAt - payload.firstAt;
19469
- return {
19470
- ...payload.properties,
19471
- coalesced: true,
19472
- coalesced_count: payload.coalescedCount,
19473
- coalesced_first_at: new Date(payload.firstAt).toISOString(),
19474
- coalesced_last_at: new Date(payload.lastAt).toISOString(),
19475
- coalesced_window_ms: windowMs,
19476
- coalesced_suppression_reason: payload.suppressionReason
19477
- };
19478
- }
19479
- class PosthogCaptureGate {
19480
- constructor(config2 = DEFAULT_CAPTURE_GATE_CONFIG) {
19481
- __publicField(this, "config");
19482
- __publicField(this, "globalWindow", { timestamps: [] });
19483
- __publicField(this, "eventWindows", /* @__PURE__ */ new Map());
19484
- __publicField(this, "activeDedupe", /* @__PURE__ */ new Map());
19485
- __publicField(this, "coalesceBuckets", /* @__PURE__ */ new Map());
19486
- __publicField(this, "emittedTotal", 0);
19487
- __publicField(this, "coalescedTotal", 0);
19488
- __publicField(this, "flushedCoalescedTotal", 0);
19489
- this.config = config2;
19490
- }
19491
- resetForTests() {
19492
- this.globalWindow.timestamps.length = 0;
19493
- this.eventWindows.clear();
19494
- this.activeDedupe.clear();
19495
- this.coalesceBuckets.clear();
19496
- this.emittedTotal = 0;
19497
- this.coalescedTotal = 0;
19498
- this.flushedCoalescedTotal = 0;
19499
- }
19500
- getSnapshot() {
19501
- const coalescedPendingByEvent = {};
19502
- let coalescedPending = 0;
19503
- for (const bucket of this.coalesceBuckets.values()) {
19504
- coalescedPending += bucket.count;
19505
- coalescedPendingByEvent[bucket.event] = (coalescedPendingByEvent[bucket.event] ?? 0) + bucket.count;
19506
- }
19507
- return {
19508
- coalescedPending,
19509
- coalescedPendingByEvent,
19510
- emittedTotal: this.emittedTotal,
19511
- coalescedTotal: this.coalescedTotal,
19512
- flushedCoalescedTotal: this.flushedCoalescedTotal
19513
- };
19514
- }
19515
- evaluateCapture(event, properties, options, now = Date.now()) {
19516
- if (shouldBypassCaptureGate(options)) {
19517
- return { action: "emit" };
19518
- }
19519
- const fingerprint = buildCaptureFingerprint(event, properties);
19520
- const bucketKey = fingerprint;
19521
- const override = this.config.eventOverrides[event];
19522
- const dedupeWindowMs = (override == null ? void 0 : override.dedupeWindowMs) ?? this.config.defaultDedupeWindowMs;
19523
- const lastDedupe = this.activeDedupe.get(event);
19524
- if (lastDedupe && lastDedupe.fingerprint === fingerprint && now - lastDedupe.at < dedupeWindowMs) {
19525
- return this.recordCoalesce(event, bucketKey, fingerprint, properties, "dedupe", now);
19526
- }
19527
- const critical = isCriticalPosthogEvent(event);
19528
- const globalCount = countInWindow(this.globalWindow, now, this.config.globalWindowMs);
19529
- const normalBudget = Math.max(
19530
- 0,
19531
- this.config.globalMaxPerWindow - this.config.criticalReservePerWindow
19532
- );
19533
- if (critical) {
19534
- if (globalCount >= this.config.globalMaxPerWindow) {
19535
- return this.recordCoalesce(event, bucketKey, fingerprint, properties, "global_budget", now);
19536
- }
19537
- } else if (globalCount >= normalBudget) {
19538
- return this.recordCoalesce(event, bucketKey, fingerprint, properties, "global_budget", now);
19539
- }
19540
- const eventWindow = this.eventWindows.get(event) ?? { timestamps: [] };
19541
- this.eventWindows.set(event, eventWindow);
19542
- const eventMax = (override == null ? void 0 : override.maxPerWindow) ?? this.config.defaultEventMaxPerWindow;
19543
- const eventWindowMs = (override == null ? void 0 : override.windowMs) ?? this.config.defaultEventWindowMs;
19544
- const eventCount = countInWindow(eventWindow, now, eventWindowMs);
19545
- if (eventCount >= eventMax) {
19546
- return this.recordCoalesce(event, bucketKey, fingerprint, properties, "event_budget", now);
19547
- }
19548
- recordInWindow(this.globalWindow, now);
19549
- recordInWindow(eventWindow, now);
19550
- this.activeDedupe.set(event, { fingerprint, bucketKey, at: now });
19551
- this.emittedTotal += 1;
19552
- return { action: "emit" };
19553
- }
19554
- /** Returns payloads ready to flush (does not mutate buckets until caller confirms). */
19555
- collectFlushCandidates(now = Date.now()) {
19556
- const payloads = [];
19557
- for (const bucket of this.coalesceBuckets.values()) {
19558
- const shouldFlushBySize = bucket.count >= this.config.flushCoalesceThreshold;
19559
- const shouldFlushByAge = now - bucket.firstAt >= this.config.flushIntervalMs;
19560
- if (!shouldFlushBySize && !shouldFlushByAge) {
19561
- continue;
19562
- }
19563
- payloads.push({
19564
- bucketKey: bucket.bucketKey,
19565
- event: bucket.event,
19566
- properties: bucket.sampleProperties,
19567
- coalescedCount: bucket.count,
19568
- firstAt: bucket.firstAt,
19569
- lastAt: bucket.lastAt,
19570
- suppressionReason: bucket.suppressionReason
19571
- });
19572
- }
19573
- return payloads;
19574
- }
19575
- /** Remove flushed bucket keys after successful emit. */
19576
- acknowledgeFlushed(payloads) {
19577
- for (const payload of payloads) {
19578
- this.coalesceBuckets.delete(payload.bucketKey);
19579
- this.flushedCoalescedTotal += payload.coalescedCount;
19580
- }
19581
- }
19582
- /** After coalescing, flush immediately if the bucket crossed the early threshold. */
19583
- takeBucketIfReady(bucketKey, now = Date.now()) {
19584
- const bucket = this.coalesceBuckets.get(bucketKey);
19585
- if (!bucket) return null;
19586
- const shouldFlushBySize = bucket.count >= this.config.flushCoalesceThreshold;
19587
- const shouldFlushByAge = now - bucket.firstAt >= this.config.flushIntervalMs;
19588
- if (!shouldFlushBySize && !shouldFlushByAge) {
19589
- return null;
19590
- }
19591
- this.coalesceBuckets.delete(bucketKey);
19592
- this.flushedCoalescedTotal += bucket.count;
19593
- return {
19594
- bucketKey: bucket.bucketKey,
19595
- event: bucket.event,
19596
- properties: bucket.sampleProperties,
19597
- coalescedCount: bucket.count,
19598
- firstAt: bucket.firstAt,
19599
- lastAt: bucket.lastAt,
19600
- suppressionReason: bucket.suppressionReason
19601
- };
19602
- }
19603
- /** Force-flush every pending bucket (e.g. page hide). */
19604
- collectAllPending() {
19605
- return [...this.coalesceBuckets.values()].map((bucket) => ({
19606
- bucketKey: bucket.bucketKey,
19607
- event: bucket.event,
19608
- properties: bucket.sampleProperties,
19609
- coalescedCount: bucket.count,
19610
- firstAt: bucket.firstAt,
19611
- lastAt: bucket.lastAt,
19612
- suppressionReason: bucket.suppressionReason
19613
- }));
19614
- }
19615
- recordCoalesce(event, bucketKey, fingerprint, properties, reason, now) {
19616
- const existing = this.coalesceBuckets.get(bucketKey);
19617
- if (existing) {
19618
- existing.count += 1;
19619
- existing.lastAt = now;
19620
- existing.sampleProperties = { ...existing.sampleProperties, ...properties ?? {} };
19621
- } else {
19622
- this.coalesceBuckets.set(bucketKey, {
19623
- event,
19624
- bucketKey,
19625
- fingerprint,
19626
- count: 1,
19627
- firstAt: now,
19628
- lastAt: now,
19629
- suppressionReason: reason,
19630
- sampleProperties: { ...properties ?? {} }
19631
- });
19632
- }
19633
- this.coalescedTotal += 1;
19634
- this.activeDedupe.set(event, { fingerprint, bucketKey, at: now });
19635
- return { action: "coalesce", reason, bucketKey };
19636
- }
19637
- }
19638
- const gate = new PosthogCaptureGate(DEFAULT_CAPTURE_GATE_CONFIG);
19639
- let wrappedClient = null;
19640
- let originalCaptureRef = null;
19641
- let flushTimer = null;
19642
- let lifecycleHooksInstalled = false;
19643
- function getPosthogCaptureGate() {
19644
- return gate;
19645
- }
19646
- function emitThroughOriginal(event, properties, options) {
19647
- if (!originalCaptureRef) return void 0;
19648
- return originalCaptureRef(event, properties, withCaptureGateBypass(options));
19649
- }
19650
- function flushCoalescedPayload(payload) {
19651
- emitThroughOriginal(payload.event, buildCoalescedCaptureProperties(payload));
19652
- }
19653
- function flushCoalescedCaptures(options) {
19654
- const payloads = (options == null ? void 0 : options.force) ? gate.collectAllPending() : gate.collectFlushCandidates();
19655
- if (payloads.length === 0) return;
19656
- for (const payload of payloads) {
19657
- flushCoalescedPayload(payload);
19658
- }
19659
- gate.acknowledgeFlushed(payloads);
19660
- }
19661
- function installLifecycleFlushHooks() {
19662
- if (lifecycleHooksInstalled || typeof window === "undefined") return;
19663
- lifecycleHooksInstalled = true;
19664
- const flushOnHide = () => {
19665
- flushCoalescedCaptures({ force: true });
19666
- };
19667
- window.addEventListener("pagehide", flushOnHide);
19668
- document.addEventListener("visibilitychange", () => {
19669
- if (document.visibilityState === "hidden") {
19670
- flushOnHide();
19671
- }
19672
- });
19673
- if (!flushTimer) {
19674
- flushTimer = setInterval(() => {
19675
- flushCoalescedCaptures();
19676
- }, DEFAULT_CAPTURE_GATE_CONFIG.flushIntervalMs);
19677
- }
19678
- }
19679
- function wrapPosthogClient(client) {
19680
- if (wrappedClient === client) {
19681
- return client;
19682
- }
19683
- originalCaptureRef = client.capture.bind(client);
19684
- const originalCaptureException = typeof client.captureException === "function" ? client.captureException.bind(client) : null;
19685
- installLifecycleFlushHooks();
19686
- client.capture = ((event, properties, options) => {
19687
- const outcome = gate.evaluateCapture(event, properties, options);
19688
- if (outcome.action === "coalesce") {
19689
- const readyPayload = gate.takeBucketIfReady(outcome.bucketKey);
19690
- if (readyPayload) {
19691
- flushCoalescedPayload(readyPayload);
19692
- }
19693
- return void 0;
20066
+ });
20067
+ if (hydratedKeysRef.current.has(hydrationKey)) {
20068
+ return;
19694
20069
  }
19695
- return emitThroughOriginal(event, properties, options);
19696
- });
19697
- if (originalCaptureException) {
19698
- client.captureException = ((error2, additionalProperties) => {
19699
- const mergedProperties = {
19700
- ...additionalProperties ?? {},
19701
- $exception_message: error2 instanceof Error ? error2.message : String(error2),
19702
- $exception_type: error2 instanceof Error ? error2.name : typeof error2
19703
- };
19704
- const outcome = gate.evaluateCapture("$exception", mergedProperties);
19705
- if (outcome.action === "coalesce") {
19706
- const readyPayload = gate.takeBucketIfReady(outcome.bucketKey);
19707
- if (readyPayload) {
19708
- flushCoalescedPayload(readyPayload);
20070
+ let cancelled = false;
20071
+ (async () => {
20072
+ var _a3, _b2;
20073
+ try {
20074
+ const state = await getThreadState(backendUrl, { apiKey, token }, threadId);
20075
+ if (cancelled || activeThreadIdRef.current !== threadId) {
20076
+ return;
19709
20077
  }
19710
- return void 0;
20078
+ if (aui.thread.getState().messages.length > 0) {
20079
+ return;
20080
+ }
20081
+ (_b2 = (_a3 = aui.thread).importExternalState) == null ? void 0 : _b2.call(_a3, state);
20082
+ hydratedKeysRef.current.add(hydrationKey);
20083
+ } catch (error2) {
20084
+ console.warn("[AthenaSDK] Failed to hydrate active thread state:", error2);
19711
20085
  }
19712
- return originalCaptureException(error2, additionalProperties);
19713
- });
19714
- }
19715
- wrappedClient = client;
19716
- return client;
19717
- }
19718
- function capturePosthogEvent(client, event, properties, options) {
19719
- if (!client || typeof client.capture !== "function") return;
19720
- client.capture(event, properties, options);
20086
+ })();
20087
+ return () => {
20088
+ cancelled = true;
20089
+ };
20090
+ }, [apiKey, aui, backendUrl, hasThread, isRunning, messageCount, threadId, token]);
19721
20091
  }
19722
- function capturePosthogException(client, error2, properties, options) {
19723
- if (!client) return;
19724
- if (typeof client.captureException === "function") {
19725
- client.captureException(error2, properties);
19726
- return;
20092
+ const LOCAL_ID_PREFIX = "__LOCALID_";
20093
+ const isLocalPlaceholder = (id) => !!id && id.startsWith(LOCAL_ID_PREFIX);
20094
+ const selectActiveThreadRemoteId = (state) => {
20095
+ const { mainThreadId, threadItems } = state;
20096
+ if (!mainThreadId) {
20097
+ return null;
19727
20098
  }
19728
- capturePosthogEvent(
19729
- client,
19730
- "$exception",
19731
- {
19732
- ...properties ?? {},
19733
- $exception_message: error2.message,
19734
- $exception_type: error2.name,
19735
- $exception_stack: error2.stack
19736
- },
19737
- options
20099
+ const item = threadItems.find((t) => t.id === mainThreadId);
20100
+ const remoteId = item ? item.remoteId ?? null : mainThreadId;
20101
+ return isLocalPlaceholder(remoteId) ? null : remoteId;
20102
+ };
20103
+ function useAthenaThreadManager() {
20104
+ const statewireList = useAthenaStatewireThreadList();
20105
+ const statewireIsThreadLoading = react$1.useAuiState((s) => {
20106
+ var _a3;
20107
+ return ((_a3 = s.optional.thread) == null ? void 0 : _a3.isLoading) ?? false;
20108
+ });
20109
+ const statewireManager = React.useMemo(() => {
20110
+ if (!statewireList) return null;
20111
+ return {
20112
+ activeThreadId: statewireList.isNewChat ? null : statewireList.activeThreadId,
20113
+ isListLoading: statewireList.isLoading,
20114
+ isThreadLoading: statewireIsThreadLoading,
20115
+ switchToThread: async (threadId) => statewireList.selectThread(threadId),
20116
+ switchToNewThread: async () => statewireList.newThread()
20117
+ };
20118
+ }, [statewireList, statewireIsThreadLoading]);
20119
+ const aui = react$1.useAui();
20120
+ const hasThreads = aui.threads.source !== null;
20121
+ const activeThreadId = react$1.useAuiState((s) => {
20122
+ const threads = s.optional.threads;
20123
+ return threads ? selectActiveThreadRemoteId(threads) : null;
20124
+ });
20125
+ const isThreadLoading = react$1.useAuiState((s) => {
20126
+ var _a3;
20127
+ return ((_a3 = s.optional.thread) == null ? void 0 : _a3.isLoading) ?? false;
20128
+ });
20129
+ const isListLoading = react$1.useAuiState((s) => {
20130
+ var _a3;
20131
+ return (_a3 = s.optional.threads) == null ? void 0 : _a3.isLoading;
20132
+ });
20133
+ const auiRef = React.useRef(aui);
20134
+ auiRef.current = aui;
20135
+ const switchToThread = React.useCallback(
20136
+ async (id) => auiRef.current.threads.switchToThread(id),
20137
+ []
20138
+ );
20139
+ const switchToNewThread = React.useCallback(
20140
+ async () => auiRef.current.threads.switchToNewThread(),
20141
+ []
19738
20142
  );
20143
+ const legacyManager = React.useMemo(() => {
20144
+ if (!hasThreads || isListLoading == null) {
20145
+ return null;
20146
+ }
20147
+ return {
20148
+ activeThreadId,
20149
+ isListLoading,
20150
+ isThreadLoading,
20151
+ switchToThread,
20152
+ switchToNewThread
20153
+ };
20154
+ }, [hasThreads, activeThreadId, isListLoading, isThreadLoading, switchToThread, switchToNewThread]);
20155
+ return statewireManager ?? legacyManager;
19739
20156
  }
19740
- let posthogInstance = null;
19741
- let initPromise = null;
19742
- const DEFAULT_HOST = "https://us.i.posthog.com";
19743
- async function initializePostHog(apiKey, host, debug) {
19744
- if (posthogInstance) return posthogInstance;
19745
- if (typeof window === "undefined") return null;
19746
- try {
19747
- const posthog = (await import("posthog-js")).default;
19748
- posthog.init(apiKey, {
19749
- api_host: host,
19750
- autocapture: true,
19751
- capture_pageview: false,
19752
- session_recording: {
19753
- recordCrossOriginIframes: true,
19754
- maskAllInputs: true
19755
- },
19756
- loaded: (ph) => {
19757
- wrapPosthogClient(ph);
19758
- if (debug) {
19759
- ph.debug(true);
19760
- }
20157
+ const POLL_DELAY_MS = 5e3;
20158
+ const POLL_INTERVAL_MS = 1e3;
20159
+ const POLL_MAX_DURATION_MS = 6e4;
20160
+ function useThreadTitlePolling(refresh) {
20161
+ const threadKey = react$1.useAuiState(
20162
+ (s) => {
20163
+ var _a3, _b2;
20164
+ return ((_a3 = s.optional.threadListItem) == null ? void 0 : _a3.remoteId) ?? ((_b2 = s.optional.threadListItem) == null ? void 0 : _b2.id) ?? null;
20165
+ }
20166
+ );
20167
+ const hasMessages = react$1.useAuiState((s) => {
20168
+ var _a3;
20169
+ return (((_a3 = s.optional.thread) == null ? void 0 : _a3.messages.length) ?? 0) > 0;
20170
+ });
20171
+ const currentTitle = react$1.useAuiState((s) => {
20172
+ var _a3;
20173
+ return ((_a3 = s.optional.threadListItem) == null ? void 0 : _a3.title) ?? "";
20174
+ });
20175
+ const trimmedTitle = currentTitle.trim();
20176
+ const hasTitle = trimmedTitle.length > 0 && !isSerializedContentPartsTitle(trimmedTitle);
20177
+ const polledThreadsRef = React.useRef(/* @__PURE__ */ new Set());
20178
+ const refreshRef = React.useRef(refresh);
20179
+ refreshRef.current = refresh;
20180
+ React.useEffect(() => {
20181
+ if (!threadKey || hasTitle || !hasMessages) {
20182
+ return;
20183
+ }
20184
+ if (polledThreadsRef.current.has(threadKey)) {
20185
+ return;
20186
+ }
20187
+ polledThreadsRef.current.add(threadKey);
20188
+ let stopped = false;
20189
+ let intervalId = null;
20190
+ let maxTimeoutId = null;
20191
+ const stop = () => {
20192
+ stopped = true;
20193
+ if (intervalId !== null) {
20194
+ clearInterval(intervalId);
20195
+ intervalId = null;
19761
20196
  }
19762
- });
19763
- wrapPosthogClient(posthog);
19764
- posthogInstance = posthog;
19765
- return posthog;
19766
- } catch {
19767
- initPromise = null;
19768
- return null;
19769
- }
19770
- }
19771
- function getPostHogInstance() {
19772
- return posthogInstance;
20197
+ if (maxTimeoutId !== null) {
20198
+ clearTimeout(maxTimeoutId);
20199
+ maxTimeoutId = null;
20200
+ }
20201
+ };
20202
+ const startTimeoutId = setTimeout(() => {
20203
+ if (stopped) return;
20204
+ refreshRef.current();
20205
+ intervalId = setInterval(() => {
20206
+ refreshRef.current();
20207
+ }, POLL_INTERVAL_MS);
20208
+ maxTimeoutId = setTimeout(stop, POLL_MAX_DURATION_MS - POLL_DELAY_MS);
20209
+ }, POLL_DELAY_MS);
20210
+ return () => {
20211
+ clearTimeout(startTimeoutId);
20212
+ stop();
20213
+ };
20214
+ }, [threadKey, hasTitle, hasMessages]);
19773
20215
  }
19774
- function PostHogProvider({
19775
- children,
19776
- config: config2
19777
- }) {
19778
- const apiKey = (config2 == null ? void 0 : config2.apiKey) ?? "";
19779
- const host = (config2 == null ? void 0 : config2.host) ?? DEFAULT_HOST;
19780
- const debug = (config2 == null ? void 0 : config2.debug) ?? false;
19781
- React.useEffect(() => {
19782
- if (!apiKey || typeof window === "undefined") return;
19783
- if (!initPromise) {
19784
- initPromise = initializePostHog(apiKey, host, debug);
20216
+ const THEME_TO_CSS = {
20217
+ primary: "--primary",
20218
+ primaryForeground: "--primary-foreground",
20219
+ background: "--background",
20220
+ foreground: "--foreground",
20221
+ muted: "--muted",
20222
+ mutedForeground: "--muted-foreground",
20223
+ accent: "--accent",
20224
+ accentForeground: "--accent-foreground",
20225
+ secondary: "--secondary",
20226
+ secondaryForeground: "--secondary-foreground",
20227
+ card: "--card",
20228
+ cardForeground: "--card-foreground",
20229
+ popover: "--popover",
20230
+ popoverForeground: "--popover-foreground",
20231
+ destructive: "--destructive",
20232
+ border: "--border",
20233
+ input: "--input",
20234
+ ring: "--ring",
20235
+ radius: "--radius",
20236
+ fontFamily: "--font-family",
20237
+ // Extended SDK-specific variables
20238
+ sidebarBackground: "--sidebar-background",
20239
+ sidebarBorder: "--sidebar-border",
20240
+ sidebarWidth: "--sidebar-width",
20241
+ userBubble: "--user-bubble",
20242
+ userBubbleForeground: "--user-bubble-foreground",
20243
+ userBubbleRadius: "--user-bubble-radius",
20244
+ assistantForeground: "--assistant-foreground",
20245
+ assistantBubble: "--assistant-bubble",
20246
+ composerBorder: "--composer-border",
20247
+ composerRadius: "--composer-radius",
20248
+ threadMaxWidth: "--thread-max-width"
20249
+ };
20250
+ function themeToStyleVars(theme) {
20251
+ const vars = {};
20252
+ for (const [key, value] of Object.entries(theme)) {
20253
+ if (value != null && THEME_TO_CSS[key]) {
20254
+ vars[THEME_TO_CSS[key]] = value;
19785
20255
  }
19786
- }, [apiKey, host, debug]);
19787
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
20256
+ }
20257
+ return vars;
19788
20258
  }
20259
+ const themes = {
20260
+ /** Default light theme. Neutral grays with blue accent. */
20261
+ light: {
20262
+ background: "oklch(0.99 0 0)",
20263
+ foreground: "oklch(0.13 0 0)",
20264
+ primary: "oklch(0.55 0.2 250)",
20265
+ primaryForeground: "oklch(1 0 0)",
20266
+ secondary: "oklch(0.96 0.005 250)",
20267
+ secondaryForeground: "oklch(0.13 0 0)",
20268
+ muted: "oklch(0.96 0.005 250)",
20269
+ mutedForeground: "oklch(0.5 0.02 250)",
20270
+ accent: "oklch(0.94 0.01 250)",
20271
+ accentForeground: "oklch(0.13 0 0)",
20272
+ card: "oklch(0.99 0 0)",
20273
+ cardForeground: "oklch(0.13 0 0)",
20274
+ popover: "oklch(0.99 0 0)",
20275
+ popoverForeground: "oklch(0.13 0 0)",
20276
+ destructive: "oklch(0.55 0.22 27)",
20277
+ border: "oklch(0.91 0.005 250)",
20278
+ input: "oklch(0.91 0.005 250)",
20279
+ ring: "oklch(0.55 0.2 250)",
20280
+ radius: "0.625rem"
20281
+ },
20282
+ /** Dark theme. Deep gray background with lighter text. */
20283
+ dark: {
20284
+ background: "oklch(0.15 0 0)",
20285
+ foreground: "oklch(0.95 0 0)",
20286
+ primary: "oklch(0.7 0.15 250)",
20287
+ primaryForeground: "oklch(0.13 0 0)",
20288
+ secondary: "oklch(0.22 0 0)",
20289
+ secondaryForeground: "oklch(0.95 0 0)",
20290
+ muted: "oklch(0.22 0 0)",
20291
+ mutedForeground: "oklch(0.65 0 0)",
20292
+ accent: "oklch(0.25 0 0)",
20293
+ accentForeground: "oklch(0.95 0 0)",
20294
+ card: "oklch(0.18 0 0)",
20295
+ cardForeground: "oklch(0.95 0 0)",
20296
+ popover: "oklch(0.18 0 0)",
20297
+ popoverForeground: "oklch(0.95 0 0)",
20298
+ destructive: "oklch(0.65 0.2 25)",
20299
+ border: "oklch(1 0 0 / 10%)",
20300
+ input: "oklch(1 0 0 / 15%)",
20301
+ ring: "oklch(0.7 0.15 250)",
20302
+ radius: "0.625rem"
20303
+ },
20304
+ /** Midnight theme. Deep navy with soft blue tones. */
20305
+ midnight: {
20306
+ background: "oklch(0.16 0.02 260)",
20307
+ foreground: "oklch(0.92 0.01 250)",
20308
+ primary: "oklch(0.68 0.16 250)",
20309
+ primaryForeground: "oklch(0.98 0 0)",
20310
+ secondary: "oklch(0.22 0.02 260)",
20311
+ secondaryForeground: "oklch(0.92 0.01 250)",
20312
+ muted: "oklch(0.22 0.02 260)",
20313
+ mutedForeground: "oklch(0.6 0.04 250)",
20314
+ accent: "oklch(0.26 0.03 260)",
20315
+ accentForeground: "oklch(0.92 0.01 250)",
20316
+ card: "oklch(0.19 0.02 260)",
20317
+ cardForeground: "oklch(0.92 0.01 250)",
20318
+ popover: "oklch(0.19 0.02 260)",
20319
+ popoverForeground: "oklch(0.92 0.01 250)",
20320
+ destructive: "oklch(0.65 0.2 25)",
20321
+ border: "oklch(0.3 0.03 260)",
20322
+ input: "oklch(0.3 0.03 260)",
20323
+ ring: "oklch(0.68 0.16 250)",
20324
+ radius: "0.625rem"
20325
+ },
20326
+ /** Warm earthy theme. Brown and sand tones. */
20327
+ warm: {
20328
+ background: "oklch(0.98 0.005 80)",
20329
+ foreground: "oklch(0.2 0.02 50)",
20330
+ primary: "oklch(0.55 0.12 50)",
20331
+ primaryForeground: "oklch(0.98 0 0)",
20332
+ secondary: "oklch(0.94 0.01 80)",
20333
+ secondaryForeground: "oklch(0.2 0.02 50)",
20334
+ muted: "oklch(0.95 0.01 80)",
20335
+ mutedForeground: "oklch(0.5 0.03 50)",
20336
+ accent: "oklch(0.92 0.015 80)",
20337
+ accentForeground: "oklch(0.2 0.02 50)",
20338
+ card: "oklch(0.98 0.005 80)",
20339
+ cardForeground: "oklch(0.2 0.02 50)",
20340
+ popover: "oklch(0.98 0.005 80)",
20341
+ popoverForeground: "oklch(0.2 0.02 50)",
20342
+ destructive: "oklch(0.55 0.2 25)",
20343
+ border: "oklch(0.9 0.01 80)",
20344
+ input: "oklch(0.9 0.01 80)",
20345
+ ring: "oklch(0.55 0.12 50)",
20346
+ radius: "0.75rem"
20347
+ },
20348
+ /** Purple creative theme. Vibrant purple accent. */
20349
+ purple: {
20350
+ background: "oklch(0.99 0.003 310)",
20351
+ foreground: "oklch(0.15 0.02 310)",
20352
+ primary: "oklch(0.55 0.22 310)",
20353
+ primaryForeground: "oklch(1 0 0)",
20354
+ secondary: "oklch(0.96 0.01 310)",
20355
+ secondaryForeground: "oklch(0.15 0.02 310)",
20356
+ muted: "oklch(0.96 0.01 310)",
20357
+ mutedForeground: "oklch(0.5 0.04 310)",
20358
+ accent: "oklch(0.94 0.015 310)",
20359
+ accentForeground: "oklch(0.15 0.02 310)",
20360
+ card: "oklch(0.99 0.003 310)",
20361
+ cardForeground: "oklch(0.15 0.02 310)",
20362
+ popover: "oklch(0.99 0.003 310)",
20363
+ popoverForeground: "oklch(0.15 0.02 310)",
20364
+ destructive: "oklch(0.55 0.22 27)",
20365
+ border: "oklch(0.92 0.005 310)",
20366
+ input: "oklch(0.92 0.005 310)",
20367
+ ring: "oklch(0.55 0.22 310)",
20368
+ radius: "0.75rem"
20369
+ },
20370
+ /** Green nature theme. Fresh green tones. */
20371
+ green: {
20372
+ background: "oklch(0.99 0.003 150)",
20373
+ foreground: "oklch(0.15 0.02 150)",
20374
+ primary: "oklch(0.55 0.18 155)",
20375
+ primaryForeground: "oklch(1 0 0)",
20376
+ secondary: "oklch(0.96 0.01 150)",
20377
+ secondaryForeground: "oklch(0.15 0.02 150)",
20378
+ muted: "oklch(0.96 0.01 150)",
20379
+ mutedForeground: "oklch(0.5 0.03 150)",
20380
+ accent: "oklch(0.94 0.015 150)",
20381
+ accentForeground: "oklch(0.15 0.02 150)",
20382
+ card: "oklch(0.99 0.003 150)",
20383
+ cardForeground: "oklch(0.15 0.02 150)",
20384
+ popover: "oklch(0.99 0.003 150)",
20385
+ popoverForeground: "oklch(0.15 0.02 150)",
20386
+ destructive: "oklch(0.55 0.22 27)",
20387
+ border: "oklch(0.92 0.008 150)",
20388
+ input: "oklch(0.92 0.008 150)",
20389
+ ring: "oklch(0.55 0.18 155)",
20390
+ radius: "0.625rem"
20391
+ }
20392
+ };
19789
20393
  const resolveTokenOverride = ({
19790
20394
  config: config2,
19791
20395
  token
@@ -19915,6 +20519,8 @@ function AthenaStatewireStandalone({
19915
20519
  token,
19916
20520
  getToken,
19917
20521
  model,
20522
+ agent: agent2,
20523
+ channel,
19918
20524
  tools,
19919
20525
  frontendToolIds,
19920
20526
  frontendTools,
@@ -19946,6 +20552,8 @@ function AthenaStatewireStandalone({
19946
20552
  token,
19947
20553
  getToken,
19948
20554
  model,
20555
+ agent: agent2,
20556
+ channel,
19949
20557
  tools,
19950
20558
  frontendToolIds,
19951
20559
  frontendToolkit: frontendTools,
@@ -20221,6 +20829,7 @@ function AthenaProvider({
20221
20829
  apiKey,
20222
20830
  token: tokenProp,
20223
20831
  agent: agent2,
20832
+ channel,
20224
20833
  model,
20225
20834
  tools = [],
20226
20835
  frontendTools = {},
@@ -20243,8 +20852,32 @@ function AthenaProvider({
20243
20852
  theme,
20244
20853
  linkClicks,
20245
20854
  citationLinks,
20246
- posthog: posthogProp
20855
+ posthog: posthogProp,
20856
+ debug,
20857
+ onDiagnostic,
20858
+ onError
20247
20859
  }) {
20860
+ const onDiagnosticRef = React.useRef(onDiagnostic);
20861
+ onDiagnosticRef.current = onDiagnostic;
20862
+ const onErrorRef = React.useRef(onError);
20863
+ onErrorRef.current = onError;
20864
+ React.useEffect(() => {
20865
+ installAthenaDiagnosticsGlobal();
20866
+ return athenaDiagnostics.addConsumer({
20867
+ onEvent: (event) => {
20868
+ var _a3;
20869
+ return (_a3 = onDiagnosticRef.current) == null ? void 0 : _a3.call(onDiagnosticRef, event);
20870
+ },
20871
+ onError: (error2, event) => {
20872
+ var _a3;
20873
+ return (_a3 = onErrorRef.current) == null ? void 0 : _a3.call(onErrorRef, error2, event);
20874
+ }
20875
+ });
20876
+ }, []);
20877
+ const debugKey = JSON.stringify(debugPropToDiagnosticsConfig(debug));
20878
+ React.useEffect(() => {
20879
+ athenaDiagnostics.configure(debugPropToDiagnosticsConfig(debug));
20880
+ }, [debugKey]);
20248
20881
  const frontendToolNames = React.useMemo(() => Object.keys(frontendTools), [frontendTools]);
20249
20882
  const effectiveFrontendTools = React.useMemo(
20250
20883
  () => disableAutoOpen ? frontendTools : { ...DEFAULT_AUTO_OPEN_TOOLS, ...frontendTools },
@@ -20271,10 +20904,31 @@ function AthenaProvider({
20271
20904
  transport: configuredTransport
20272
20905
  });
20273
20906
  React.useEffect(() => {
20907
+ athenaDiagnostics.gauge("sdk.transport", effectiveTransport);
20908
+ athenaDiagnostics.emit("sdk.transport", {
20909
+ level: "debug",
20910
+ data: { transport: effectiveTransport, thread_list: enableThreadList }
20911
+ });
20274
20912
  if (transportFallbackReason) {
20913
+ athenaDiagnostics.emit("sdk.config.warning", {
20914
+ level: "warn",
20915
+ data: { prop: "transport", reason: transportFallbackReason }
20916
+ });
20275
20917
  console.warn(`[AthenaSDK] ${transportFallbackReason}`);
20276
20918
  }
20277
- }, [transportFallbackReason]);
20919
+ }, [effectiveTransport, enableThreadList, transportFallbackReason]);
20920
+ React.useEffect(() => {
20921
+ if (channel && effectiveTransport !== "statewire") {
20922
+ athenaDiagnostics.emit("sdk.config.warning", {
20923
+ level: "warn",
20924
+ data: {
20925
+ prop: "channel",
20926
+ value: channel,
20927
+ reason: "the 'channel' prop requires transport='statewire'; the legacy transport ignores it."
20928
+ }
20929
+ });
20930
+ }
20931
+ }, [channel, effectiveTransport]);
20278
20932
  const bridge = useParentBridge({
20279
20933
  trustedOrigins: configuredTrustedParentOrigins
20280
20934
  });
@@ -20301,6 +20955,8 @@ function AthenaProvider({
20301
20955
  token: effectiveToken,
20302
20956
  getToken: configuredGetToken,
20303
20957
  model,
20958
+ agent: agent2,
20959
+ channel,
20304
20960
  tools,
20305
20961
  frontendToolIds: frontendToolNames,
20306
20962
  frontendTools: effectiveFrontendTools,
@@ -54652,13 +55308,13 @@ function getEmptyMessage(scope, query) {
54652
55308
  function MentionIcon({ item }) {
54653
55309
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(MentionIconGlyph, { icon: item.fallbackIcon }) });
54654
55310
  }
54655
- const MentionSuggestionList = React.forwardRef(({ store, query, command: command2, closeMenu }, ref) => {
55311
+ const MentionSuggestionList = React.forwardRef(({ store: store2, query, command: command2, closeMenu }, ref) => {
54656
55312
  const [selectedIndex, setSelectedIndex] = React.useState(0);
54657
55313
  const [currentScope, setCurrentScope] = React.useState("root");
54658
55314
  const scrollContainerRef = React.useRef(null);
54659
- const hasGlobalSearch = store.state.registry.entries.has(GLOBAL_SEARCH_SCOPE$1);
55315
+ const hasGlobalSearch = store2.state.registry.entries.has(GLOBAL_SEARCH_SCOPE$1);
54660
55316
  const effectiveScope = currentScope === "root" && query.trim() && hasGlobalSearch ? GLOBAL_SEARCH_SCOPE$1 : currentScope;
54661
- const { items, isFetching } = useStore(store, (state) => ({
55317
+ const { items, isFetching } = useStore(store2, (state) => ({
54662
55318
  items: getFilteredItems(state.cache, effectiveScope, query),
54663
55319
  isFetching: false
54664
55320
  }));
@@ -54777,7 +55433,7 @@ const MentionSuggestionList = React.forwardRef(({ store, query, command: command
54777
55433
  ] });
54778
55434
  });
54779
55435
  MentionSuggestionList.displayName = "MentionSuggestionList";
54780
- const MentionSuggestionPopup = React.forwardRef(({ store, query, command: command2, clientRect: clientRect2, closeMenu, isHidden: isHidden3 = false }, ref) => {
55436
+ const MentionSuggestionPopup = React.forwardRef(({ store: store2, query, command: command2, clientRect: clientRect2, closeMenu, isHidden: isHidden3 = false }, ref) => {
54781
55437
  const listRef = React.useRef(null);
54782
55438
  const virtualRef = React.useRef({
54783
55439
  getBoundingClientRect: () => clientRect2 ? clientRect2() : new DOMRect()
@@ -54824,7 +55480,7 @@ const MentionSuggestionPopup = React.forwardRef(({ store, query, command: comman
54824
55480
  MentionSuggestionList,
54825
55481
  {
54826
55482
  ref: listRef,
54827
- store,
55483
+ store: store2,
54828
55484
  query,
54829
55485
  command: command2,
54830
55486
  closeMenu
@@ -55051,9 +55707,9 @@ function useMentionSuggestions(tools, options = {}) {
55051
55707
  populateMentionCache({ cache, tools, rootCategories });
55052
55708
  storeRef.current = new Store({ registry: registry2, cache });
55053
55709
  }
55054
- const store = storeRef.current;
55710
+ const store2 = storeRef.current;
55055
55711
  React.useEffect(() => {
55056
- store.setState((prev) => {
55712
+ store2.setState((prev) => {
55057
55713
  const newCache = createItemCache();
55058
55714
  if (rootCategories) {
55059
55715
  registerStaticSource(prev.registry, "toolkits");
@@ -55063,8 +55719,8 @@ function useMentionSuggestions(tools, options = {}) {
55063
55719
  populateMentionCache({ cache: newCache, tools, rootCategories });
55064
55720
  return { ...prev, cache: newCache };
55065
55721
  });
55066
- }, [tools, store, rootCategories, rootCategoriesKey]);
55067
- return store;
55722
+ }, [tools, store2, rootCategories, rootCategoriesKey]);
55723
+ return store2;
55068
55724
  }
55069
55725
  const AttachmentCtx = React.createContext(null);
55070
55726
  function AttachmentProvider({ children }) {
@@ -55385,13 +56041,13 @@ const TiptapComposer = ({ tools = [], rootCategories }) => {
55385
56041
  });
55386
56042
  if (fullMessage) {
55387
56043
  aui.composer.setText(fullMessage);
55388
- aui.composer.send();
56044
+ aui.composer.send({ steer: false });
55389
56045
  clearAttachments();
55390
56046
  clearQuote();
55391
56047
  }
55392
56048
  } else {
55393
56049
  aui.composer.setText(markdown);
55394
- aui.composer.send();
56050
+ aui.composer.send({ steer: false });
55395
56051
  }
55396
56052
  editor2.commands.clearContent();
55397
56053
  }, [aui, clearAttachments, clearQuote, appUrl]);
@@ -56028,6 +56684,10 @@ const TOOL_META = {
56028
56684
  // Presentations
56029
56685
  create_powerpoint_deck: { displayName: "Building presentation", icon: Monitor },
56030
56686
  execute_presentation_code: { displayName: "Generating slides", icon: Monitor },
56687
+ execute_word_document_code: { displayName: "Editing Word document", icon: FileText },
56688
+ execute_spreadsheet_code: { displayName: "Editing spreadsheet", icon: ChartColumn },
56689
+ execute_spreadsheet_commands: { displayName: "Editing spreadsheet", icon: ChartColumn },
56690
+ unified_email_list_accounts: { displayName: "Checking email accounts", icon: Mail },
56031
56691
  // Code & Data
56032
56692
  run_python_code: { displayName: "Running analysis", icon: Code },
56033
56693
  run_sql_query_tool: { displayName: "Running SQL query", icon: Database, describer: (a) => a.sql_query ?? a.query ?? a.sql ? `"${truncateLine(a.sql_query ?? a.query ?? a.sql)}"` : "" },
@@ -56075,14 +56735,14 @@ function extractResultMessage(result) {
56075
56735
  return null;
56076
56736
  }
56077
56737
  function isResultSuccess(result) {
56078
- if (typeof result === "string") {
56079
- const parsed = tryParseJson$1(result);
56080
- if (parsed) return parsed.success === true;
56081
- }
56082
- if (typeof result === "object" && result !== null) {
56083
- return result.success === true;
56738
+ const obj = typeof result === "string" ? tryParseJson$1(result) : typeof result === "object" && result !== null ? result : null;
56739
+ if (obj) {
56740
+ if (obj.success === false || obj.ok === false) return false;
56741
+ if (typeof obj.error === "string" && obj.error.length > 0) return false;
56742
+ return true;
56084
56743
  }
56085
- return false;
56744
+ if (typeof result === "string") return !/^\s*error\b/i.test(result);
56745
+ return true;
56086
56746
  }
56087
56747
  function extractAssetId$1(result) {
56088
56748
  if (!result) return null;
@@ -56247,7 +56907,7 @@ function ToolFallbackTrigger({
56247
56907
  "span",
56248
56908
  {
56249
56909
  className: cn(
56250
- "relative inline-block leading-tight",
56910
+ "relative inline-block text-[13px] font-medium leading-tight",
56251
56911
  isCancelled && "text-muted-foreground line-through"
56252
56912
  ),
56253
56913
  children: [
@@ -57232,17 +57892,103 @@ BrowseToolUI.displayName = "BrowseToolUI";
57232
57892
  function parseEmailResults(data) {
57233
57893
  const results = data.limited_results ?? data.results ?? data.emails ?? data.messages;
57234
57894
  if (!Array.isArray(results)) return [];
57235
- return results.slice(0, 10).map((r2) => {
57236
- const item = r2;
57237
- return {
57238
- subject: item.subject ?? item.title,
57239
- from: item.from ?? item.sender ?? item.from_email,
57240
- date: item.date ?? item.received_at ?? item.sent_at,
57241
- snippet: item.snippet ?? item.preview ?? item.body_preview,
57242
- isDraft: Boolean(item.is_draft)
57243
- };
57895
+ return results.slice(0, 50).flatMap((r2) => {
57896
+ const item = asRecord(r2);
57897
+ if (!item) return [];
57898
+ return [
57899
+ {
57900
+ subject: asString(item.subject) ?? asString(item.title),
57901
+ from: asString(item.from) ?? asString(item.sender) ?? asString(item.from_email),
57902
+ date: asString(item.date) ?? asString(item.received_at) ?? asString(item.sent_at),
57903
+ snippet: asString(item.snippet) ?? asString(item.preview) ?? asString(item.body_preview),
57904
+ isDraft: item.is_draft === true
57905
+ }
57906
+ ];
57244
57907
  });
57245
57908
  }
57909
+ function formatEmailDate(raw) {
57910
+ if (!raw) return void 0;
57911
+ const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
57912
+ const date2 = dateOnly ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3])) : new Date(raw);
57913
+ if (Number.isNaN(date2.getTime())) return raw;
57914
+ const now = /* @__PURE__ */ new Date();
57915
+ if (!dateOnly && date2.toDateString() === now.toDateString()) {
57916
+ return date2.toLocaleTimeString(void 0, {
57917
+ hour: "numeric",
57918
+ minute: "2-digit"
57919
+ });
57920
+ }
57921
+ const sameYear = date2.getFullYear() === now.getFullYear();
57922
+ return date2.toLocaleDateString(void 0, {
57923
+ month: "short",
57924
+ day: "numeric",
57925
+ ...sameYear ? {} : { year: "numeric" }
57926
+ });
57927
+ }
57928
+ const EMAIL_QUERY_OPERATOR = /(?:^|\s)(received|after|before|from|to|subject|has|in|is):(?:"([^"]+)"|(\S+))/gi;
57929
+ function humanEmailDate(raw) {
57930
+ return formatEmailDate(raw.replace(/\//g, "-")) ?? raw;
57931
+ }
57932
+ function humanizeEmailQuery(query) {
57933
+ if (!query.trim()) return "";
57934
+ const phrases = [];
57935
+ let freeText = query;
57936
+ for (const match2 of query.matchAll(EMAIL_QUERY_OPERATOR)) {
57937
+ const [token, op, quoted, bare] = match2;
57938
+ const value = quoted ?? bare ?? "";
57939
+ freeText = freeText.replace(token, " ");
57940
+ switch (op.toLowerCase()) {
57941
+ case "received": {
57942
+ const [start, end] = value.split("..");
57943
+ phrases.push(
57944
+ end ? `${humanEmailDate(start)} – ${humanEmailDate(end)}` : humanEmailDate(start)
57945
+ );
57946
+ break;
57947
+ }
57948
+ case "after":
57949
+ phrases.push(`after ${humanEmailDate(value)}`);
57950
+ break;
57951
+ case "before":
57952
+ phrases.push(`before ${humanEmailDate(value)}`);
57953
+ break;
57954
+ case "from":
57955
+ phrases.push(`from ${value}`);
57956
+ break;
57957
+ case "to":
57958
+ phrases.push(`to ${value}`);
57959
+ break;
57960
+ case "subject":
57961
+ phrases.push(`subject “${value}”`);
57962
+ break;
57963
+ case "has":
57964
+ phrases.push(value === "attachment" ? "with attachments" : `has ${value}`);
57965
+ break;
57966
+ case "in":
57967
+ phrases.push(value === "drafts" ? "drafts only" : `in ${value}`);
57968
+ break;
57969
+ case "is":
57970
+ phrases.push(value);
57971
+ break;
57972
+ }
57973
+ }
57974
+ const remainder = freeText.trim().replace(/\s+/g, " ");
57975
+ if (remainder) phrases.push(`“${remainder}”`);
57976
+ return phrases.join(" · ");
57977
+ }
57978
+ const INLINE_EMAIL_ROWS = 3;
57979
+ function EmailResultRow({ email: email2 }) {
57980
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 py-2 first:pt-0 last:pb-0", children: [
57981
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
57982
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-baseline gap-1.5 text-[12px] font-medium text-foreground", children: [
57983
+ email2.isDraft && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded bg-amber-100 px-1 py-px text-[9px] font-semibold uppercase tracking-wide text-amber-700", children: "Draft" }),
57984
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: email2.subject || "No subject" })
57985
+ ] }),
57986
+ email2.date && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: formatEmailDate(email2.date) })
57987
+ ] }),
57988
+ email2.from && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: email2.from }),
57989
+ email2.snippet && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground/80", children: truncate(email2.snippet, 150) })
57990
+ ] });
57991
+ }
57246
57992
  const EmailSearchToolUIImpl = ({
57247
57993
  toolName,
57248
57994
  args,
@@ -57253,35 +57999,96 @@ const EmailSearchToolUIImpl = ({
57253
57999
  const query = (typedArgs == null ? void 0 : typedArgs.query) ?? (typedArgs == null ? void 0 : typedArgs.search_query) ?? "";
57254
58000
  const data = React.useMemo(() => normalizeResult(result), [result]);
57255
58001
  const emails = React.useMemo(() => data ? parseEmailResults(data) : [], [data]);
58002
+ const provider = typeof (data == null ? void 0 : data.provider) === "string" ? data.provider : void 0;
58003
+ const totalCount = typeof (data == null ? void 0 : data.count) === "number" ? data.count : emails.length;
57256
58004
  const isRunning = (status == null ? void 0 : status.type) === "running";
57257
58005
  const isComplete = (status == null ? void 0 : status.type) === "complete";
57258
58006
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
58007
+ const readableQuery = React.useMemo(() => humanizeEmailQuery(query), [query]);
58008
+ const inlineEmails = emails.slice(0, INLINE_EMAIL_ROWS);
58009
+ const overflowEmails = emails.slice(INLINE_EMAIL_ROWS);
58010
+ const countLabel = emails.length > 0 && totalCount > emails.length ? `${emails.length} of ${totalCount}` : `${totalCount}`;
57259
58011
  return /* @__PURE__ */ jsxRuntime.jsx(
57260
58012
  ToolCard,
57261
58013
  {
57262
58014
  icon: Mail,
57263
58015
  status: (status == null ? void 0 : status.type) ?? "complete",
57264
- title: isRunning ? "Searching emails..." : "Email search",
57265
- subtitle: query ? truncate(query, 80) : void 0,
58016
+ title: isRunning ? "Searching email…" : "Email search",
58017
+ subtitle: readableQuery ? truncate(readableQuery, 90) : void 0,
57266
58018
  toolName,
57267
58019
  args: typedArgs,
57268
58020
  result,
57269
- badge: isComplete && emails.length > 0 ? `${emails.length} emails` : void 0,
58021
+ badge: isComplete ? totalCount > 0 ? `${countLabel} email${totalCount === 1 ? "" : "s"}${provider ? ` · ${provider}` : ""}` : "No matches" : void 0,
58022
+ error: errorMsg,
58023
+ children: isComplete && emails.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/40 px-4 py-2.5", children: [
58024
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: inlineEmails.map((email2, i) => /* @__PURE__ */ jsxRuntime.jsx(EmailResultRow, { email: email2 }, i)) }),
58025
+ overflowEmails.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
58026
+ ExpandableSection,
58027
+ {
58028
+ label: `Show ${overflowEmails.length} more`,
58029
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: overflowEmails.map((email2, i) => /* @__PURE__ */ jsxRuntime.jsx(EmailResultRow, { email: email2 }, i)) })
58030
+ }
58031
+ )
58032
+ ] })
58033
+ }
58034
+ );
58035
+ };
58036
+ const EmailSearchToolUI = React.memo(
58037
+ EmailSearchToolUIImpl
58038
+ );
58039
+ EmailSearchToolUI.displayName = "EmailSearchToolUI";
58040
+ function parseEmailAccounts(data) {
58041
+ const accounts = data.accounts ?? data.results;
58042
+ if (!Array.isArray(accounts)) return [];
58043
+ return accounts.slice(0, 10).flatMap((raw) => {
58044
+ const item = asRecord(raw);
58045
+ if (!item) return [];
58046
+ const defaultFlag = typeof item.is_default === "boolean" ? item.is_default : item.default;
58047
+ return [
58048
+ {
58049
+ address: asString(item.email_address) ?? asString(item.email) ?? asString(item.address),
58050
+ provider: asString(item.provider) ?? asString(item.provider_name),
58051
+ name: asString(item.account_name) ?? asString(item.name),
58052
+ isDefault: defaultFlag === true
58053
+ }
58054
+ ];
58055
+ });
58056
+ }
58057
+ const EmailAccountsToolUIImpl = ({
58058
+ toolName,
58059
+ args,
58060
+ result,
58061
+ status
58062
+ }) => {
58063
+ const data = React.useMemo(() => normalizeResult(result), [result]);
58064
+ const accounts = React.useMemo(
58065
+ () => data ? parseEmailAccounts(data) : [],
58066
+ [data]
58067
+ );
58068
+ const isRunning = (status == null ? void 0 : status.type) === "running";
58069
+ const isComplete = (status == null ? void 0 : status.type) === "complete";
58070
+ const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
58071
+ return /* @__PURE__ */ jsxRuntime.jsx(
58072
+ ToolCard,
58073
+ {
58074
+ icon: Mail,
58075
+ status: (status == null ? void 0 : status.type) ?? "complete",
58076
+ title: isRunning ? "Checking connected email accounts…" : "Email accounts",
58077
+ toolName,
58078
+ args,
58079
+ result,
58080
+ badge: isComplete && accounts.length > 0 ? `${accounts.length} connected` : void 0,
57270
58081
  error: errorMsg,
57271
- children: isComplete && emails.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ExpandableSection, { label: "Show email results", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: emails.map((email2, i) => /* @__PURE__ */ jsxRuntime.jsxs(
58082
+ children: isComplete && accounts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/40 px-4 py-2.5", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: accounts.map((account, i) => /* @__PURE__ */ jsxRuntime.jsxs(
57272
58083
  "div",
57273
58084
  {
57274
- className: cn("flex flex-col gap-0.5 py-2", i === 0 && "pt-0"),
58085
+ className: "flex items-center justify-between gap-2 py-1.5 first:pt-0 last:pb-0",
57275
58086
  children: [
57276
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
57277
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-baseline gap-1.5 text-[12px] font-medium text-foreground", children: [
57278
- email2.isDraft && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded bg-amber-100 px-1 py-px text-[9px] font-semibold uppercase tracking-wide text-amber-700", children: "Draft" }),
57279
- email2.subject || "No subject"
57280
- ] }),
57281
- email2.date && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: email2.date })
57282
- ] }),
57283
- email2.from && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground", children: email2.from }),
57284
- email2.snippet && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground/80", children: truncate(email2.snippet, 150) })
58087
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-[12px] text-foreground", children: account.address || account.name || "Unknown account" }),
58088
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex shrink-0 items-center gap-1.5", children: [
58089
+ account.isDefault && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-emerald-50 px-1.5 py-px text-[9px] font-semibold uppercase tracking-wide text-emerald-700", children: "Default" }),
58090
+ account.provider && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-md bg-muted/60 px-1.5 py-0.5 text-[10px] text-muted-foreground", children: account.provider })
58091
+ ] })
57285
58092
  ]
57286
58093
  },
57287
58094
  i
@@ -57289,10 +58096,10 @@ const EmailSearchToolUIImpl = ({
57289
58096
  }
57290
58097
  );
57291
58098
  };
57292
- const EmailSearchToolUI = React.memo(
57293
- EmailSearchToolUIImpl
58099
+ const EmailAccountsToolUI = React.memo(
58100
+ EmailAccountsToolUIImpl
57294
58101
  );
57295
- EmailSearchToolUI.displayName = "EmailSearchToolUI";
58102
+ EmailAccountsToolUI.displayName = "EmailAccountsToolUI";
57296
58103
  function extractAssetId(result) {
57297
58104
  const data = normalizeResult(result);
57298
58105
  if (!data) return null;
@@ -57329,6 +58136,9 @@ function asRecord(value) {
57329
58136
  }
57330
58137
  return value;
57331
58138
  }
58139
+ function asString(value) {
58140
+ return typeof value === "string" ? value : void 0;
58141
+ }
57332
58142
  const PRESENTATION_CODE_SLIDE_NUMBER_KEYS = [
57333
58143
  "slideNumber",
57334
58144
  "slide_number",
@@ -57425,22 +58235,51 @@ function CreateAssetToolUIImpl({
57425
58235
  args: typedArgs,
57426
58236
  result,
57427
58237
  error: errorMsg,
57428
- children: assetId && isComplete && !isCancelled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/40 px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
58238
+ children: assetId && isComplete && !isCancelled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/40 px-4 py-2.5", children: /* @__PURE__ */ jsxRuntime.jsxs(
57429
58239
  "button",
57430
58240
  {
57431
58241
  type: "button",
57432
58242
  onClick: handleOpen,
57433
- className: "flex items-center gap-1.5 rounded-md border border-border/60 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
58243
+ className: "group flex w-full items-center gap-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-2.5 text-left transition-colors hover:border-border hover:bg-muted/40",
57434
58244
  children: [
57435
- /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { className: "size-3" }),
57436
- "Open ",
57437
- assetType === "unknown" ? "asset" : assetType
58245
+ /* @__PURE__ */ jsxRuntime.jsx(
58246
+ "div",
58247
+ {
58248
+ className: cn(
58249
+ "flex size-10 shrink-0 items-center justify-center rounded-lg",
58250
+ ASSET_TILE_STYLE[assetType] ?? ASSET_TILE_STYLE.unknown
58251
+ ),
58252
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "size-5" })
58253
+ }
58254
+ ),
58255
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
58256
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[13px] font-medium text-foreground", children: createdName || name || "Untitled" }),
58257
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] text-muted-foreground", children: [
58258
+ ASSET_TILE_LABEL[assetType] ?? "Asset",
58259
+ " · Click to open"
58260
+ ] })
58261
+ ] }),
58262
+ /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { className: "size-3.5 shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100" })
57438
58263
  ]
57439
58264
  }
57440
58265
  ) })
57441
58266
  }
57442
58267
  );
57443
58268
  }
58269
+ const ASSET_TILE_STYLE = {
58270
+ document: "bg-blue-500/10 text-blue-600",
58271
+ spreadsheet: "bg-emerald-500/10 text-emerald-600",
58272
+ presentation: "bg-orange-500/10 text-orange-600",
58273
+ notebook: "bg-violet-500/10 text-violet-600",
58274
+ unknown: "bg-muted text-muted-foreground"
58275
+ };
58276
+ const ASSET_TILE_LABEL = {
58277
+ document: "Document",
58278
+ spreadsheet: "Spreadsheet",
58279
+ presentation: "Presentation",
58280
+ notebook: "Notebook",
58281
+ unknown: "Asset"
58282
+ };
57444
58283
  const CreateDocumentToolUIImpl = (props) => /* @__PURE__ */ jsxRuntime.jsx(
57445
58284
  CreateAssetToolUIImpl,
57446
58285
  {
@@ -57489,10 +58328,10 @@ function openStudioAsset({
57489
58328
  slideNumber,
57490
58329
  preserveExistingSlide
57491
58330
  }) {
57492
- const store = useAssetPanelStore.getState();
57493
- const existing = store.tabs.find((tab) => tab.id === assetId);
58331
+ const store2 = useAssetPanelStore.getState();
58332
+ const existing = store2.tabs.find((tab) => tab.id === assetId);
57494
58333
  const shouldKeepCurrentSlide = preserveExistingSlide && existing;
57495
- store.openAsset(assetId, {
58334
+ store2.openAsset(assetId, {
57496
58335
  type: assetType,
57497
58336
  ...!shouldKeepCurrentSlide && slideNumber !== void 0 ? { slideNumber } : {}
57498
58337
  });
@@ -59863,6 +60702,8 @@ const TOOL_UI_REGISTRY = {
59863
60702
  open_browser: OpenBrowserToolUI,
59864
60703
  search_email: EmailSearchToolUI,
59865
60704
  unified_email_search: EmailSearchToolUI,
60705
+ unified_email_list_accounts: EmailAccountsToolUI,
60706
+ email_list_accounts: EmailAccountsToolUI,
59866
60707
  create_email_draft: CreateEmailDraftToolUI,
59867
60708
  unified_email_create_draft: CreateEmailDraftToolUI,
59868
60709
  create_new_document: CreateDocumentToolUI,
@@ -60621,7 +61462,7 @@ function useSendMessage() {
60621
61462
  shouldReplace || !currentText ? text2 : `${currentText}
60622
61463
  ${text2}`
60623
61464
  );
60624
- await aui.composer.send();
61465
+ await aui.composer.send({ steer: false });
60625
61466
  },
60626
61467
  [aui]
60627
61468
  );
@@ -61221,13 +62062,13 @@ const ComposerSendWithQuote = () => {
61221
62062
  });
61222
62063
  if (!fullMessage) return;
61223
62064
  aui.composer.setText(fullMessage);
61224
- aui.composer.send();
62065
+ aui.composer.send({ steer: false });
61225
62066
  clearQuote();
61226
62067
  clearAttachments();
61227
62068
  } else {
61228
62069
  if (!userText) return;
61229
62070
  aui.composer.setText(userText);
61230
- aui.composer.send();
62071
+ aui.composer.send({ steer: false });
61231
62072
  }
61232
62073
  editor == null ? void 0 : editor.clear();
61233
62074
  }, [aui, quote, attachments, hasExtras, isUploading, isThreadRunning, clearQuote, clearAttachments, editorRef, appUrl]);
@@ -62457,6 +63298,8 @@ function useComposerAttachment() {
62457
63298
  }, [aui]);
62458
63299
  return { addFile, addContent, clear };
62459
63300
  }
63301
+ exports.ATHENA_REACT_SDK_VERSION = ATHENA_REACT_SDK_VERSION;
63302
+ exports.ATHENA_SDK_ERROR_CODES = ATHENA_SDK_ERROR_CODES;
62460
63303
  exports.ATHENA_TRANSPORTS = ATHENA_TRANSPORTS;
62461
63304
  exports.AppendDocumentToolUI = AppendDocumentToolUI;
62462
63305
  exports.AssetPanel = AssetPanel;
@@ -62467,10 +63310,12 @@ exports.AthenaChat = AthenaChat;
62467
63310
  exports.AthenaLayout = AthenaLayout;
62468
63311
  exports.AthenaProvider = AthenaProvider;
62469
63312
  exports.AthenaReasoningPart = AthenaReasoningPart;
63313
+ exports.AthenaSdkError = AthenaSdkError;
62470
63314
  exports.AthenaThreadIdContext = AthenaThreadIdContext;
62471
63315
  exports.AthenaUserMessage = AthenaUserMessage;
62472
63316
  exports.BrowseToolUI = BrowseToolUI;
62473
63317
  exports.Button = Button;
63318
+ exports.COLLAB_AGENT_REF_PREFIX = COLLAB_AGENT_REF_PREFIX;
62474
63319
  exports.Collapsible = Collapsible;
62475
63320
  exports.CollapsibleContent = CollapsibleContent;
62476
63321
  exports.CollapsibleTrigger = CollapsibleTrigger;
@@ -62530,6 +63375,7 @@ exports.WebSearchToolUI = WebSearchToolUI;
62530
63375
  exports.allowsMidRunSend = allowsMidRunSend;
62531
63376
  exports.archiveThread = archiveThread;
62532
63377
  exports.asHitlApproval = asHitlApproval;
63378
+ exports.athenaDiagnostics = athenaDiagnostics;
62533
63379
  exports.athenaStatewireErrorNotice = athenaStatewireErrorNotice;
62534
63380
  exports.autoCloseInFlightSubgraphMessages = autoCloseInFlightSubgraphMessages;
62535
63381
  exports.buildCoalescedCaptureProperties = buildCoalescedCaptureProperties;
@@ -62553,13 +63399,17 @@ exports.getPostHogInstance = getPostHogInstance;
62553
63399
  exports.getPosthogCaptureGate = getPosthogCaptureGate;
62554
63400
  exports.getThreadState = getThreadState;
62555
63401
  exports.hasStatewireThreadExtras = hasStatewireThreadExtras;
63402
+ exports.installAthenaDiagnosticsGlobal = installAthenaDiagnosticsGlobal;
62556
63403
  exports.isAthenaCitationUrl = isAthenaCitationUrl;
63404
+ exports.isAthenaSdkError = isAthenaSdkError;
62557
63405
  exports.isCriticalPosthogEvent = isCriticalPosthogEvent;
62558
63406
  exports.isPendingInterrupt = isPendingInterrupt;
62559
63407
  exports.listThreads = listThreads;
62560
63408
  exports.normalizeResult = normalizeResult;
62561
63409
  exports.parseAthenaCitationLink = parseAthenaCitationLink;
63410
+ exports.parseCollabAgentRef = parseCollabAgentRef;
62562
63411
  exports.readInterruptMessage = readInterruptMessage;
63412
+ exports.readRejectionDetail = readRejectionDetail;
62563
63413
  exports.resetAssetAutoOpen = resetAssetAutoOpen;
62564
63414
  exports.resolveAthenaTransport = resolveAthenaTransport;
62565
63415
  exports.resolveStatewireSyncUrl = resolveStatewireSyncUrl;
@@ -62567,6 +63417,7 @@ exports.setThreadReadState = setThreadReadState;
62567
63417
  exports.statewireClientToolWireEntries = statewireClientToolWireEntries;
62568
63418
  exports.themeToStyleVars = themeToStyleVars;
62569
63419
  exports.themes = themes;
63420
+ exports.toAthenaSdkError = toAthenaSdkError;
62570
63421
  exports.truncate = truncate;
62571
63422
  exports.tryParseJson = tryParseJson$2;
62572
63423
  exports.useAppendToComposer = useAppendToComposer;
@@ -62574,6 +63425,7 @@ exports.useAssetEmbed = useAssetEmbed;
62574
63425
  exports.useAssetPanelStore = useAssetPanelStore;
62575
63426
  exports.useAthenaCitationLinkHandler = useAthenaCitationLinkHandler;
62576
63427
  exports.useAthenaConfig = useAthenaConfig;
63428
+ exports.useAthenaDiagnostics = useAthenaDiagnostics;
62577
63429
  exports.useAthenaLinkClickHandler = useAthenaLinkClickHandler;
62578
63430
  exports.useAthenaRuntime = useAthenaRuntime;
62579
63431
  exports.useAthenaStatewireLifecycle = useAthenaStatewireLifecycle;