@ateam-ai-mobile/core 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +386 -2
- package/dist/index.d.ts +386 -2
- package/dist/index.js +1819 -1333
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1792 -1332
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/acs/ws.ts +46 -2
- package/src/api/tenants.ts +17 -1
- package/src/authSession.ts +241 -0
- package/src/chat/store.ts +35 -0
- package/src/consumerAuth.ts +216 -0
- package/src/deepLinks.ts +38 -0
- package/src/gate.ts +67 -0
- package/src/index.ts +68 -0
- package/src/lifecycle.ts +43 -0
- package/src/pushRegistration.ts +71 -0
- package/src/session.ts +62 -0
- package/src/workspace.ts +127 -0
package/dist/index.js
CHANGED
|
@@ -28,23 +28,34 @@ __export(index_exports, {
|
|
|
28
28
|
apiFetchRaw: () => apiFetchRaw,
|
|
29
29
|
blocksToText: () => blocksToText,
|
|
30
30
|
borderRadius: () => borderRadius,
|
|
31
|
+
capabilityEnabled: () => capabilityEnabled,
|
|
31
32
|
closeChatStream: () => closeChatStream,
|
|
32
33
|
colors: () => colors,
|
|
33
34
|
connectAcsWs: () => connectAcsWs,
|
|
34
35
|
connectorCall: () => connectorCall,
|
|
36
|
+
createAuthSession: () => createAuthSession,
|
|
37
|
+
createPushRegistration: () => createPushRegistration,
|
|
35
38
|
deleteArtifact: () => deleteArtifact,
|
|
36
39
|
disconnectAcsWs: () => disconnectAcsWs,
|
|
40
|
+
exchangeHandoff: () => exchangeHandoff,
|
|
41
|
+
fetchConsumerConfig: () => fetchConsumerConfig,
|
|
37
42
|
fontSize: () => fontSize,
|
|
38
43
|
getConfig: () => getConfig,
|
|
39
44
|
getConsumerAuthApiUrl: () => getConsumerAuthApiUrl,
|
|
40
45
|
getCoreApiUrl: () => getCoreApiUrl,
|
|
41
46
|
getCoreWsUrl: () => getCoreWsUrl,
|
|
47
|
+
getSession: () => getSession,
|
|
42
48
|
getTenant: () => getTenant,
|
|
43
49
|
getToken: () => getToken,
|
|
44
50
|
getUser: () => getUser,
|
|
45
51
|
getVoiceApiUrl: () => getVoiceApiUrl,
|
|
46
52
|
initAdas: () => initAdas,
|
|
47
53
|
isAdasInitialized: () => isAdasInitialized,
|
|
54
|
+
isDevHost: () => isDevHost,
|
|
55
|
+
joinWithApple: () => joinWithApple,
|
|
56
|
+
joinWithGoogle: () => joinWithGoogle,
|
|
57
|
+
linkChannelStart: () => linkChannelStart,
|
|
58
|
+
linkChannelVerify: () => linkChannelVerify,
|
|
48
59
|
listHomeActions: () => listHomeActions,
|
|
49
60
|
listMadeForYou: () => listMadeForYou,
|
|
50
61
|
listMyTenants: () => listMyTenants,
|
|
@@ -52,22 +63,37 @@ __export(index_exports, {
|
|
|
52
63
|
mcpFetch: () => mcpFetch,
|
|
53
64
|
mcpToolCall: () => mcpToolCall,
|
|
54
65
|
notifyAcsWsForeground: () => notifyAcsWsForeground,
|
|
66
|
+
notifyAppForeground: () => notifyAppForeground,
|
|
67
|
+
onAppForeground: () => onAppForeground,
|
|
68
|
+
openJoinWithApple: () => openJoinWithApple,
|
|
69
|
+
openJoinWithGoogle: () => openJoinWithGoogle,
|
|
70
|
+
parseDeepLinkParam: () => parseDeepLinkParam,
|
|
71
|
+
parseHandoffCode: () => parseHandoffCode,
|
|
72
|
+
parseInviteCode: () => parseInviteCode,
|
|
55
73
|
patchArtifact: () => patchArtifact,
|
|
56
74
|
patchHomeAction: () => patchHomeAction,
|
|
57
75
|
pluginCommandEmitter: () => pluginCommandEmitter,
|
|
58
76
|
postCommandResult: () => postCommandResult,
|
|
77
|
+
previewInvite: () => previewInvite,
|
|
59
78
|
recordEvent: () => recordEvent,
|
|
60
79
|
refreshToken: () => refreshToken,
|
|
80
|
+
resolveAdasPhase: () => resolveAdasPhase,
|
|
61
81
|
setAcsCacheStorage: () => setAcsCacheStorage,
|
|
62
82
|
setAcsReachabilityReporter: () => setAcsReachabilityReporter,
|
|
83
|
+
setClientDeclarationProvider: () => setClientDeclarationProvider,
|
|
63
84
|
setHomeActionOrder: () => setHomeActionOrder,
|
|
64
85
|
setOrder: () => setOrder,
|
|
86
|
+
signInWithApple: () => signInWithApple,
|
|
87
|
+
signInWithGoogle: () => signInWithGoogle,
|
|
65
88
|
spacing: () => spacing,
|
|
66
89
|
switchTenant: () => switchTenant,
|
|
67
90
|
unwrapMcpResult: () => unwrapMcpResult,
|
|
91
|
+
useCapability: () => useCapability,
|
|
68
92
|
useChatStore: () => useChatStore,
|
|
69
93
|
useHomeActionsStore: () => useHomeActionsStore,
|
|
70
|
-
useWidgetsStore: () => useWidgetsStore
|
|
94
|
+
useWidgetsStore: () => useWidgetsStore,
|
|
95
|
+
useWorkspaceStore: () => useWorkspaceStore,
|
|
96
|
+
userFromJwt: () => userFromJwt
|
|
71
97
|
});
|
|
72
98
|
module.exports = __toCommonJS(index_exports);
|
|
73
99
|
|
|
@@ -243,1476 +269,1910 @@ function unwrapMcpResult(result) {
|
|
|
243
269
|
}
|
|
244
270
|
}
|
|
245
271
|
|
|
246
|
-
// src/
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
constructor() {
|
|
254
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
255
|
-
// Ring buffer of plugin commands seen recently. Lets a plugin that mounts
|
|
256
|
-
// AFTER its first command was emitted (e.g. a surface mounted by the
|
|
257
|
-
// dispatcher in response to focus-plugin) still pick that command up via
|
|
258
|
-
// `drainPluginCommands(pluginId)`. Self-cleans on TTL + size cap.
|
|
259
|
-
this.recent = [];
|
|
272
|
+
// src/session.ts
|
|
273
|
+
function hostOf(u) {
|
|
274
|
+
if (!u) return "";
|
|
275
|
+
try {
|
|
276
|
+
return new URL(u).host;
|
|
277
|
+
} catch {
|
|
278
|
+
return u;
|
|
260
279
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
280
|
+
}
|
|
281
|
+
function isDevHost(u) {
|
|
282
|
+
const h = hostOf(u);
|
|
283
|
+
return !!h && (h.includes("dev-") || /(^|\.)dev\./.test(h));
|
|
284
|
+
}
|
|
285
|
+
function getSession() {
|
|
286
|
+
let consumerUrl = "";
|
|
287
|
+
try {
|
|
288
|
+
consumerUrl = getConsumerAuthApiUrl();
|
|
289
|
+
} catch {
|
|
265
290
|
}
|
|
266
|
-
|
|
267
|
-
|
|
291
|
+
let coreUrl = "";
|
|
292
|
+
try {
|
|
293
|
+
coreUrl = getCoreApiUrl();
|
|
294
|
+
} catch {
|
|
268
295
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
296
|
+
const tenant = getTenant() || null;
|
|
297
|
+
const user = getUser();
|
|
298
|
+
return {
|
|
299
|
+
tenant,
|
|
300
|
+
tenantName: tenant || "\u2014",
|
|
301
|
+
env: isDevHost(consumerUrl) || isDevHost(coreUrl) ? "dev" : "prod",
|
|
302
|
+
host: hostOf(consumerUrl || coreUrl),
|
|
303
|
+
actorId: user?.actorId ?? null,
|
|
304
|
+
email: user?.email ?? null
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/authSession.ts
|
|
309
|
+
function decodeJwtPayload(token) {
|
|
310
|
+
try {
|
|
311
|
+
const segs = token.split(".");
|
|
312
|
+
if (segs.length !== 3) return null;
|
|
313
|
+
const b64 = segs[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
314
|
+
const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
315
|
+
const json = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
|
|
316
|
+
return JSON.parse(json);
|
|
317
|
+
} catch {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function userFromJwt(token) {
|
|
322
|
+
const claims = decodeJwtPayload(token);
|
|
323
|
+
if (!claims?.sub) return null;
|
|
324
|
+
return {
|
|
325
|
+
actorId: String(claims.sub),
|
|
326
|
+
email: claims.email,
|
|
327
|
+
displayName: claims.name || claims.email,
|
|
328
|
+
tenantRole: claims.tenantRole ?? null,
|
|
329
|
+
serviceRole: claims.serviceRole ?? null,
|
|
330
|
+
platformRoles: Array.isArray(claims.platformRoles) ? claims.platformRoles : [],
|
|
331
|
+
roles: Array.isArray(claims.roles) ? claims.roles : []
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
async function attemptRefresh(expiredToken) {
|
|
335
|
+
let res;
|
|
336
|
+
try {
|
|
337
|
+
res = await fetch(`${getCoreApiUrl()}/api/auth/refresh`, {
|
|
338
|
+
method: "POST",
|
|
339
|
+
headers: {
|
|
340
|
+
"Authorization": `Bearer ${expiredToken}`,
|
|
341
|
+
"Content-Type": "application/json"
|
|
284
342
|
}
|
|
285
343
|
});
|
|
344
|
+
} catch (e) {
|
|
345
|
+
console.warn("[adas/session] refresh fetch threw (network):", e?.message || e);
|
|
346
|
+
return { ok: "transient", reason: `network: ${e?.message || "unknown"}` };
|
|
286
347
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
348
|
+
if (res.status === 401 || res.status === 403) {
|
|
349
|
+
console.warn(`[adas/session] refresh rejected with ${res.status} \u2014 auth genuinely broken`);
|
|
350
|
+
return { ok: "auth_failed", status: res.status, reason: `core ${res.status}` };
|
|
351
|
+
}
|
|
352
|
+
if (!res.ok) {
|
|
353
|
+
console.warn(`[adas/session] refresh server error ${res.status} \u2014 treating as transient`);
|
|
354
|
+
return { ok: "transient", status: res.status, reason: `core ${res.status}` };
|
|
355
|
+
}
|
|
356
|
+
const body = await res.json().catch(() => null);
|
|
357
|
+
if (typeof body?.token === "string") return { ok: "ok", token: body.token };
|
|
358
|
+
console.warn("[adas/session] refresh returned 2xx but no token \u2014 treating as auth_failed");
|
|
359
|
+
return { ok: "auth_failed", status: res.status, reason: "malformed refresh response" };
|
|
360
|
+
}
|
|
361
|
+
function createAuthSession(deps) {
|
|
362
|
+
let state = { token: null, user: null, status: "loading" };
|
|
363
|
+
const subs = /* @__PURE__ */ new Set();
|
|
364
|
+
const set = (patch) => {
|
|
365
|
+
state = { ...state, ...patch };
|
|
366
|
+
for (const cb of subs) cb(state);
|
|
367
|
+
};
|
|
368
|
+
async function adopt(token) {
|
|
369
|
+
await deps.persistence.saveToken(token);
|
|
370
|
+
const user = userFromJwt(token);
|
|
371
|
+
set({ token, user, status: "authenticated" });
|
|
372
|
+
await deps.onToken?.(token, user);
|
|
373
|
+
}
|
|
374
|
+
async function refresh() {
|
|
375
|
+
const current = state.token;
|
|
376
|
+
if (!current) return { ok: "auth_failed", status: 0, reason: "no token in store" };
|
|
377
|
+
const outcome = await attemptRefresh(current);
|
|
378
|
+
if (outcome.ok === "ok") {
|
|
379
|
+
await adopt(outcome.token);
|
|
380
|
+
return { ok: "ok", status: 200 };
|
|
298
381
|
}
|
|
299
|
-
|
|
300
|
-
return
|
|
382
|
+
if (outcome.ok === "auth_failed") return { ok: "auth_failed", status: outcome.status, reason: outcome.reason };
|
|
383
|
+
return { ok: "transient", status: outcome.status ?? 0, reason: outcome.reason };
|
|
301
384
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
385
|
+
async function logout() {
|
|
386
|
+
await deps.onLogout?.();
|
|
387
|
+
await deps.persistence.clearToken();
|
|
388
|
+
set({ token: null, user: null, status: "signed_out" });
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
getToken: () => state.token,
|
|
392
|
+
getUser: () => state.user,
|
|
393
|
+
getState: () => state,
|
|
394
|
+
subscribe(cb) {
|
|
395
|
+
subs.add(cb);
|
|
396
|
+
return () => {
|
|
397
|
+
subs.delete(cb);
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
async restore() {
|
|
401
|
+
const token = await deps.persistence.loadToken();
|
|
402
|
+
if (!token) {
|
|
403
|
+
set({ status: "signed_out" });
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const user = userFromJwt(token);
|
|
407
|
+
if (!user) {
|
|
408
|
+
await deps.persistence.clearToken();
|
|
409
|
+
set({ status: "signed_out" });
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const claims = decodeJwtPayload(token);
|
|
413
|
+
const exp = typeof claims?.exp === "number" ? claims.exp * 1e3 : 0;
|
|
414
|
+
if (exp && Date.now() >= exp) {
|
|
415
|
+
const outcome = await attemptRefresh(token);
|
|
416
|
+
if (outcome.ok === "ok") {
|
|
417
|
+
await adopt(outcome.token);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (outcome.ok === "auth_failed") {
|
|
421
|
+
await deps.persistence.clearToken();
|
|
422
|
+
set({ token: null, user: null, status: "signed_out" });
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
set({ token, user, status: "authenticated" });
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
set({ token, user, status: "authenticated" });
|
|
429
|
+
},
|
|
430
|
+
async login(token) {
|
|
431
|
+
const user = userFromJwt(token);
|
|
432
|
+
if (!user) throw new Error("Invalid JWT \u2014 could not decode subject");
|
|
433
|
+
await adopt(token);
|
|
434
|
+
},
|
|
435
|
+
logout,
|
|
436
|
+
refresh
|
|
310
437
|
};
|
|
311
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
312
|
-
return headers;
|
|
313
438
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
439
|
+
|
|
440
|
+
// src/pushRegistration.ts
|
|
441
|
+
function createPushRegistration(backend) {
|
|
442
|
+
let token = null;
|
|
443
|
+
return {
|
|
444
|
+
getToken: () => token,
|
|
445
|
+
async setToken(next) {
|
|
446
|
+
if (!next) {
|
|
447
|
+
token = null;
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (next === token) return;
|
|
451
|
+
token = next;
|
|
452
|
+
try {
|
|
453
|
+
await backend.registerDevice(next);
|
|
454
|
+
} catch (e) {
|
|
455
|
+
console.warn("[adas/push] registerDevice failed:", e?.message || e);
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
async unregister() {
|
|
459
|
+
const t = token;
|
|
460
|
+
token = null;
|
|
461
|
+
if (t) {
|
|
462
|
+
backend.unregisterDevice(t).catch(
|
|
463
|
+
(e) => console.warn("[adas/push] unregisterDevice failed:", e?.message || e)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
317
468
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
469
|
+
|
|
470
|
+
// src/acs/widgetsStore.ts
|
|
471
|
+
var import_zustand = require("zustand");
|
|
472
|
+
|
|
473
|
+
// src/acs/providers.ts
|
|
474
|
+
var _reachability = null;
|
|
475
|
+
function setAcsReachabilityReporter(r) {
|
|
476
|
+
_reachability = r;
|
|
325
477
|
}
|
|
326
|
-
function
|
|
327
|
-
|
|
478
|
+
function reportAcsServerUp() {
|
|
479
|
+
try {
|
|
480
|
+
_reachability?.up();
|
|
481
|
+
} catch {
|
|
482
|
+
}
|
|
328
483
|
}
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
|
|
484
|
+
function reportAcsServerDown() {
|
|
485
|
+
try {
|
|
486
|
+
_reachability?.down();
|
|
487
|
+
} catch {
|
|
488
|
+
}
|
|
332
489
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (!latest?.message) return void 0;
|
|
337
|
-
return {
|
|
338
|
-
message: String(latest.message || ""),
|
|
339
|
-
...latest.step != null ? { step: latest.step } : {},
|
|
340
|
-
...latest.total != null ? { total: latest.total } : {}
|
|
341
|
-
};
|
|
490
|
+
var _kv = null;
|
|
491
|
+
function setAcsCacheStorage(kv) {
|
|
492
|
+
_kv = kv;
|
|
342
493
|
}
|
|
343
|
-
function
|
|
344
|
-
|
|
345
|
-
if (!exec || typeof exec !== "object") return void 0;
|
|
346
|
-
const latest = Object.keys(exec).map((k) => ({ k, n: parseInt(k.replace("iter_", ""), 10) })).filter(({ n }) => !isNaN(n)).sort((a, b) => b.n - a.n)[0];
|
|
347
|
-
if (!latest) return void 0;
|
|
348
|
-
const steps = exec[latest.k];
|
|
349
|
-
if (!Array.isArray(steps) || !steps.length) return void 0;
|
|
350
|
-
const last = steps[steps.length - 1];
|
|
351
|
-
return last?.tool || last?.phase;
|
|
494
|
+
function getAcsCacheStorage() {
|
|
495
|
+
return _kv;
|
|
352
496
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
inputId: pq.correlation_id || pq.correlationId || pq.inputId || "",
|
|
372
|
-
question: pq.prompt_message || pq.question || "The assistant needs your input",
|
|
373
|
-
inputType: pq.inputType || "text",
|
|
374
|
-
options: pq.options || pq.requested_fields,
|
|
375
|
-
placeholder: pq.placeholder
|
|
376
|
-
};
|
|
497
|
+
|
|
498
|
+
// src/acs/api.ts
|
|
499
|
+
async function authedFetch(path, init) {
|
|
500
|
+
const token = getToken();
|
|
501
|
+
try {
|
|
502
|
+
const res = await fetch(`${getCoreApiUrl()}${path}`, {
|
|
503
|
+
...init,
|
|
504
|
+
headers: {
|
|
505
|
+
"Content-Type": "application/json",
|
|
506
|
+
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
507
|
+
...init?.headers || {}
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
reportAcsServerUp();
|
|
511
|
+
return res;
|
|
512
|
+
} catch (e) {
|
|
513
|
+
reportAcsServerDown();
|
|
514
|
+
throw e;
|
|
377
515
|
}
|
|
378
|
-
return progress;
|
|
379
|
-
}
|
|
380
|
-
function isTerminalSnapshot(snap) {
|
|
381
|
-
if (extractPendingQuestion(snap)) return false;
|
|
382
|
-
return snap?.done === true || isTerminalStatus(snap?.status);
|
|
383
516
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
517
|
+
async function listSurface(surface, context) {
|
|
518
|
+
try {
|
|
519
|
+
const res = await authedFetch("/api/acs/surface", {
|
|
520
|
+
method: "POST",
|
|
521
|
+
body: JSON.stringify({ surface, ...context ? { context } : {} })
|
|
522
|
+
});
|
|
523
|
+
if (!res.ok) {
|
|
524
|
+
console.warn(`[acs] /surface ${surface} returned ${res.status}`);
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
const data = await res.json();
|
|
528
|
+
return Array.isArray(data?.items) ? data.items : [];
|
|
529
|
+
} catch (e) {
|
|
530
|
+
console.warn("[acs] /surface failed:", e?.message || e);
|
|
531
|
+
return [];
|
|
532
|
+
}
|
|
393
533
|
}
|
|
394
|
-
function
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (
|
|
398
|
-
|
|
534
|
+
async function listMadeForYou() {
|
|
535
|
+
try {
|
|
536
|
+
const res = await authedFetch("/api/acs/made-for-you", { method: "GET" });
|
|
537
|
+
if (!res.ok) {
|
|
538
|
+
console.warn(`[acs] /made-for-you returned ${res.status}`);
|
|
539
|
+
return [];
|
|
399
540
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
541
|
+
const data = await res.json();
|
|
542
|
+
return Array.isArray(data?.artifacts) ? data.artifacts : [];
|
|
543
|
+
} catch (e) {
|
|
544
|
+
console.warn("[acs] /made-for-you failed:", e?.message || e);
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async function deleteArtifact(artifactId, reason = "user_long_press_delete") {
|
|
549
|
+
try {
|
|
550
|
+
const res = await authedFetch(
|
|
551
|
+
`/api/acs/artifacts/${encodeURIComponent(artifactId)}`,
|
|
552
|
+
{ method: "DELETE", body: JSON.stringify({ reason }) }
|
|
553
|
+
);
|
|
554
|
+
const data = await res.json().catch(() => ({}));
|
|
555
|
+
if (res.ok) {
|
|
556
|
+
return { ok: true, already: !!data?.already, status: res.status };
|
|
405
557
|
}
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
if (md) return md;
|
|
558
|
+
if (res.status === 404) {
|
|
559
|
+
return { ok: true, notFound: true, status: 404 };
|
|
409
560
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
561
|
+
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
562
|
+
} catch (e) {
|
|
563
|
+
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async function patchArtifact(artifactId, body, reason) {
|
|
567
|
+
try {
|
|
568
|
+
const res = await authedFetch(
|
|
569
|
+
`/api/acs/artifacts/${encodeURIComponent(artifactId)}`,
|
|
570
|
+
{ method: "PATCH", body: JSON.stringify({ ...body, reason }) }
|
|
571
|
+
);
|
|
572
|
+
const data = await res.json().catch(() => ({}));
|
|
573
|
+
if (res.ok) {
|
|
574
|
+
return {
|
|
575
|
+
ok: true,
|
|
576
|
+
already: !!data?.already,
|
|
577
|
+
artifact: data?.artifact || void 0,
|
|
578
|
+
status: res.status
|
|
579
|
+
};
|
|
415
580
|
}
|
|
416
|
-
if (
|
|
417
|
-
return
|
|
581
|
+
if (res.status === 404) {
|
|
582
|
+
return { ok: true, notFound: true, status: 404 };
|
|
418
583
|
}
|
|
419
|
-
return
|
|
420
|
-
|
|
584
|
+
return {
|
|
585
|
+
ok: false,
|
|
586
|
+
status: res.status,
|
|
587
|
+
error: data?.error || `HTTP ${res.status}`
|
|
588
|
+
};
|
|
589
|
+
} catch (e) {
|
|
590
|
+
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
591
|
+
}
|
|
421
592
|
}
|
|
422
|
-
function
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
593
|
+
async function setOrder(pinned) {
|
|
594
|
+
try {
|
|
595
|
+
const res = await authedFetch("/api/acs/order", {
|
|
596
|
+
method: "PUT",
|
|
597
|
+
body: JSON.stringify({ pinned })
|
|
598
|
+
});
|
|
599
|
+
if (res.ok) return { ok: true, status: res.status };
|
|
600
|
+
const data = await res.json().catch(() => ({}));
|
|
601
|
+
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
602
|
+
} catch (e) {
|
|
603
|
+
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function recordEvent(artifactId, event, surface) {
|
|
607
|
+
try {
|
|
608
|
+
await authedFetch("/api/acs/event", {
|
|
609
|
+
method: "POST",
|
|
610
|
+
body: JSON.stringify({ artifactId, event, ...surface ? { surface } : {} })
|
|
611
|
+
});
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
async function listHomeActions() {
|
|
616
|
+
try {
|
|
617
|
+
const res = await authedFetch("/api/acs/home-actions", { method: "GET" });
|
|
618
|
+
if (!res.ok) {
|
|
619
|
+
console.warn(`[acs] /home-actions returned ${res.status}`);
|
|
620
|
+
return [];
|
|
432
621
|
}
|
|
622
|
+
const data = await res.json();
|
|
623
|
+
return Array.isArray(data?.actions) ? data.actions : [];
|
|
624
|
+
} catch (e) {
|
|
625
|
+
console.warn("[acs] /home-actions failed:", e?.message || e);
|
|
626
|
+
return [];
|
|
433
627
|
}
|
|
434
|
-
return atts.length > 0 ? atts : void 0;
|
|
435
628
|
}
|
|
436
|
-
|
|
437
|
-
|
|
629
|
+
async function patchHomeAction(actionId, body, reason) {
|
|
630
|
+
try {
|
|
631
|
+
const res = await authedFetch(
|
|
632
|
+
`/api/acs/home-actions/${encodeURIComponent(actionId)}`,
|
|
633
|
+
{ method: "PATCH", body: JSON.stringify({ ...body, reason }) }
|
|
634
|
+
);
|
|
635
|
+
const data = await res.json().catch(() => ({}));
|
|
636
|
+
if (res.ok) return { ok: true, action: data?.action || void 0, status: res.status };
|
|
637
|
+
if (res.status === 404) return { ok: true, status: 404 };
|
|
638
|
+
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
639
|
+
} catch (e) {
|
|
640
|
+
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function setHomeActionOrder(order) {
|
|
644
|
+
try {
|
|
645
|
+
const res = await authedFetch("/api/acs/home-actions/order", {
|
|
646
|
+
method: "PUT",
|
|
647
|
+
body: JSON.stringify({ order })
|
|
648
|
+
});
|
|
649
|
+
if (res.ok) return { ok: true, status: res.status };
|
|
650
|
+
const data = await res.json().catch(() => ({}));
|
|
651
|
+
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
652
|
+
} catch (e) {
|
|
653
|
+
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/acs/widgetsStore.ts
|
|
658
|
+
var useWidgetsStore = (0, import_zustand.create)((set, get) => ({
|
|
659
|
+
tiles: [],
|
|
660
|
+
hydrated: false,
|
|
438
661
|
loading: false,
|
|
439
662
|
error: null,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
663
|
+
hydrate: async () => {
|
|
664
|
+
if (get().hydrated || get().loading) return;
|
|
665
|
+
await get().refresh();
|
|
666
|
+
},
|
|
667
|
+
refresh: async () => {
|
|
668
|
+
if (get().loading) return;
|
|
669
|
+
set({ loading: true, error: null });
|
|
447
670
|
try {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
671
|
+
const tiles = await listSurface("home");
|
|
672
|
+
set({ tiles, loading: false, hydrated: true });
|
|
673
|
+
} catch (e) {
|
|
674
|
+
set({ loading: false, hydrated: true, error: e?.message || "Failed to load widgets" });
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
clear: () => set({ tiles: [], hydrated: false, loading: false, error: null }),
|
|
678
|
+
remove: async (artifactId, reason = "user_long_press_delete") => {
|
|
679
|
+
const before = get().tiles;
|
|
680
|
+
set({ tiles: before.filter((t) => t.id !== artifactId) });
|
|
681
|
+
const result = await deleteArtifact(artifactId, reason);
|
|
682
|
+
if (result.ok) {
|
|
683
|
+
return { ok: true };
|
|
684
|
+
}
|
|
685
|
+
set({ tiles: before });
|
|
686
|
+
return { ok: false, error: result.error || `HTTP ${result.status}` };
|
|
687
|
+
},
|
|
688
|
+
hide: async (artifactId) => {
|
|
689
|
+
const before = get().tiles;
|
|
690
|
+
set({
|
|
691
|
+
tiles: before.map(
|
|
692
|
+
(t) => t.id === artifactId ? { ...t, visibility: "hidden" } : t
|
|
693
|
+
)
|
|
694
|
+
});
|
|
695
|
+
const r = await patchArtifact(artifactId, { visibility: "hidden" }, "user_long_press_hide");
|
|
696
|
+
if (r.ok) {
|
|
697
|
+
if (r.artifact) {
|
|
698
|
+
set({
|
|
699
|
+
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
700
|
+
});
|
|
462
701
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
skillSlug: turn.skillSlug,
|
|
482
|
-
...userAtts ? { attachments: userAtts } : {},
|
|
483
|
-
...turnChainId ? { chainId: turnChainId } : {}
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
const assistantText = blocksToText(turn.assistant?.blocks);
|
|
487
|
-
const assistantAtts = blocksToAttachments(turn.assistant?.blocks);
|
|
488
|
-
const assistantActions = Array.isArray(turn.assistant?.actions) ? turn.assistant.actions : void 0;
|
|
489
|
-
const turnKind = typeof turn.kind === "string" ? turn.kind : void 0;
|
|
490
|
-
if (assistantText || assistantAtts || assistantActions?.length) {
|
|
491
|
-
persistedMessages.push({
|
|
492
|
-
id: `bot-${turn.jobId}`,
|
|
493
|
-
role: "assistant",
|
|
494
|
-
content: assistantText || "",
|
|
495
|
-
timestamp: new Date(turn.finishedAt || turn.startedAt || Date.now()),
|
|
496
|
-
skillSlug: turn.skillSlug,
|
|
497
|
-
...assistantAtts ? { attachments: assistantAtts } : {},
|
|
498
|
-
...assistantActions?.length ? { actions: assistantActions } : {},
|
|
499
|
-
...turnChainId ? { chainId: turnChainId } : {},
|
|
500
|
-
...turnKind ? { kind: turnKind } : {}
|
|
501
|
-
});
|
|
502
|
-
}
|
|
702
|
+
return { ok: true };
|
|
703
|
+
}
|
|
704
|
+
set({ tiles: before });
|
|
705
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
706
|
+
},
|
|
707
|
+
unhide: async (artifactId) => {
|
|
708
|
+
const before = get().tiles;
|
|
709
|
+
set({
|
|
710
|
+
tiles: before.map(
|
|
711
|
+
(t) => t.id === artifactId ? { ...t, visibility: "pinned" } : t
|
|
712
|
+
)
|
|
713
|
+
});
|
|
714
|
+
const r = await patchArtifact(artifactId, { visibility: "pinned" }, "user_long_press_unhide");
|
|
715
|
+
if (r.ok) {
|
|
716
|
+
if (r.artifact) {
|
|
717
|
+
set({
|
|
718
|
+
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
719
|
+
});
|
|
503
720
|
}
|
|
504
|
-
|
|
505
|
-
const keptPlaceholders = [];
|
|
506
|
-
for (const m of current) {
|
|
507
|
-
if (!m.id.startsWith("job-") || !m.jobProgress || !m.chainId) continue;
|
|
508
|
-
const matchIdx = persistedMessages.findIndex(
|
|
509
|
-
(pm) => pm.role === "assistant" && pm.chainId === m.chainId
|
|
510
|
-
);
|
|
511
|
-
if (matchIdx >= 0) {
|
|
512
|
-
const st = m.jobProgress.status;
|
|
513
|
-
if (st === "running" || st === "paused") {
|
|
514
|
-
persistedMessages[matchIdx] = { ...persistedMessages[matchIdx], jobProgress: m.jobProgress };
|
|
515
|
-
}
|
|
516
|
-
} else {
|
|
517
|
-
keptPlaceholders.push(m);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
const isLocalUserId = (id) => /^user-\d+$/.test(id);
|
|
521
|
-
const keptUserBubbles = current.filter((m) => {
|
|
522
|
-
if (m.role !== "user" || !isLocalUserId(m.id)) return false;
|
|
523
|
-
if (!m.chainId) return true;
|
|
524
|
-
return !persistedMessages.some(
|
|
525
|
-
(pm) => pm.role === "user" && pm.chainId === m.chainId
|
|
526
|
-
);
|
|
527
|
-
});
|
|
528
|
-
const finalMessages = [...persistedMessages, ...keptUserBubbles, ...keptPlaceholders];
|
|
529
|
-
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
530
|
-
let firstRunning = keptPlaceholders.find((m) => m.jobProgress?.status === "running");
|
|
531
|
-
try {
|
|
532
|
-
let activeChains = [];
|
|
533
|
-
const chainsRes = await fetch(`${getCoreApiUrl()}/api/chains/active`, {
|
|
534
|
-
headers: coreHeaders()
|
|
535
|
-
});
|
|
536
|
-
if (chainsRes.ok) {
|
|
537
|
-
const body = await chainsRes.json();
|
|
538
|
-
activeChains = Array.isArray(body?.chains) ? body.chains : [];
|
|
539
|
-
} else {
|
|
540
|
-
const legacy = await fetch(`${getCoreApiUrl()}/api/chain/current`, {
|
|
541
|
-
headers: coreHeaders()
|
|
542
|
-
});
|
|
543
|
-
if (legacy.ok) {
|
|
544
|
-
const lb = await legacy.json();
|
|
545
|
-
if (lb?.data?.id) activeChains = [lb.data];
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
for (const slim of activeChains) {
|
|
549
|
-
if (!slim?.id) continue;
|
|
550
|
-
const chainId = String(slim.id);
|
|
551
|
-
const alreadyTracked = finalMessages.some(
|
|
552
|
-
(m) => m.role === "assistant" && m.jobProgress?.jobId === chainId
|
|
553
|
-
);
|
|
554
|
-
if (alreadyTracked) continue;
|
|
555
|
-
const progress = snapshotToProgress(
|
|
556
|
-
{ ...slim, status: slim.chainStatus || slim.status },
|
|
557
|
-
chainId
|
|
558
|
-
);
|
|
559
|
-
const chainCreatedAt = typeof slim.createdAt === "number" ? slim.createdAt : typeof slim.lastUpdate === "number" ? slim.lastUpdate : Date.now();
|
|
560
|
-
const chainLastUpdate = typeof slim.lastUpdate === "number" ? slim.lastUpdate : chainCreatedAt;
|
|
561
|
-
const jobProgress = {
|
|
562
|
-
jobId: chainId,
|
|
563
|
-
actorId: extractActorId(slim),
|
|
564
|
-
status: "running",
|
|
565
|
-
chainStartedAt: chainCreatedAt,
|
|
566
|
-
...progress
|
|
567
|
-
};
|
|
568
|
-
const goal = typeof slim.goal === "string" ? slim.goal : "";
|
|
569
|
-
const userExists = finalMessages.some(
|
|
570
|
-
(m) => m.role === "user" && (m.chainId === chainId || goal && m.content === goal)
|
|
571
|
-
);
|
|
572
|
-
if (goal && !userExists) {
|
|
573
|
-
finalMessages.push({
|
|
574
|
-
id: `user-${chainId}`,
|
|
575
|
-
role: "user",
|
|
576
|
-
content: goal,
|
|
577
|
-
timestamp: new Date(chainCreatedAt),
|
|
578
|
-
chainId
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
let matchIdx = -1;
|
|
582
|
-
for (let i = finalMessages.length - 1; i >= 0; i--) {
|
|
583
|
-
const m = finalMessages[i];
|
|
584
|
-
if (m.role === "assistant" && m.chainId === chainId) {
|
|
585
|
-
matchIdx = i;
|
|
586
|
-
break;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
if (matchIdx >= 0) {
|
|
590
|
-
finalMessages[matchIdx] = { ...finalMessages[matchIdx], jobProgress };
|
|
591
|
-
if (jobProgress.status === "running" && !firstRunning) firstRunning = finalMessages[matchIdx];
|
|
592
|
-
} else {
|
|
593
|
-
const reattachBubble = {
|
|
594
|
-
id: `job-${chainId}`,
|
|
595
|
-
role: "assistant",
|
|
596
|
-
content: "Working on it...",
|
|
597
|
-
timestamp: new Date(chainLastUpdate),
|
|
598
|
-
jobProgress,
|
|
599
|
-
chainId
|
|
600
|
-
};
|
|
601
|
-
finalMessages.push(reattachBubble);
|
|
602
|
-
if (jobProgress.status === "running" && !firstRunning) firstRunning = reattachBubble;
|
|
603
|
-
}
|
|
604
|
-
streamJob(chainId, getTenant(), getToken());
|
|
605
|
-
}
|
|
606
|
-
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
607
|
-
} catch {
|
|
608
|
-
}
|
|
609
|
-
set({
|
|
610
|
-
messages: finalMessages,
|
|
611
|
-
conversationLoaded: true,
|
|
612
|
-
error: null,
|
|
613
|
-
activeJobId: firstRunning?.jobProgress?.jobId || null,
|
|
614
|
-
loading: !!firstRunning
|
|
615
|
-
});
|
|
616
|
-
} catch (err) {
|
|
617
|
-
console.warn("[adas-chat] Error loading conversation:", err.message);
|
|
618
|
-
set({ conversationLoaded: true });
|
|
619
|
-
}
|
|
620
|
-
},
|
|
621
|
-
// -----------------------------------------------------------------------
|
|
622
|
-
// Send message via Core API
|
|
623
|
-
// POST /api/chat { goal } → { jobId } → stream → fetch turn result
|
|
624
|
-
// -----------------------------------------------------------------------
|
|
625
|
-
sendMessage: async (text, skillSlug, attachments) => {
|
|
626
|
-
const userMsg = {
|
|
627
|
-
id: `user-${Date.now()}`,
|
|
628
|
-
role: "user",
|
|
629
|
-
content: text,
|
|
630
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
631
|
-
...attachments?.length ? {
|
|
632
|
-
attachments: attachments.map((a) => ({
|
|
633
|
-
mimeType: a.mimeType,
|
|
634
|
-
name: a.name,
|
|
635
|
-
previewUri: a.mimeType.startsWith("image/") ? `data:${a.mimeType};base64,${a.data}` : void 0
|
|
636
|
-
}))
|
|
637
|
-
} : {}
|
|
638
|
-
};
|
|
639
|
-
set((s) => ({ messages: [...s.messages, userMsg], loading: true, error: null }));
|
|
640
|
-
try {
|
|
641
|
-
const actor = getUser();
|
|
642
|
-
const body = {
|
|
643
|
-
goal: text,
|
|
644
|
-
// Opt in to the Chain Additions router (attach/cancel/new while a
|
|
645
|
-
// chain runs). Backend ignores it where not applicable — safe to send.
|
|
646
|
-
chainRouting: true,
|
|
647
|
-
triggerContext: {
|
|
648
|
-
channel: "mobile",
|
|
649
|
-
replyContext: { chatId: actor?.actorId || "unknown" }
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
if (skillSlug) body.skillSlug = skillSlug;
|
|
653
|
-
if (attachments?.length) body.attachments = attachments;
|
|
654
|
-
const res = await fetch(`${getCoreApiUrl()}/api/chat`, {
|
|
655
|
-
method: "POST",
|
|
656
|
-
headers: coreHeaders(),
|
|
657
|
-
body: JSON.stringify(body)
|
|
658
|
-
});
|
|
659
|
-
if (!res.ok) {
|
|
660
|
-
let errMsg = `Server error (${res.status})`;
|
|
661
|
-
try {
|
|
662
|
-
const text2 = await res.text();
|
|
663
|
-
if (text2.startsWith("<") || text2.startsWith("<!")) {
|
|
664
|
-
errMsg = res.status === 502 ? "Server is temporarily unavailable. Please try again." : `Server error (${res.status})`;
|
|
665
|
-
} else {
|
|
666
|
-
const parsed = JSON.parse(text2);
|
|
667
|
-
errMsg = parsed.error || errMsg;
|
|
668
|
-
}
|
|
669
|
-
} catch {
|
|
670
|
-
}
|
|
671
|
-
throw new Error(errMsg);
|
|
672
|
-
}
|
|
673
|
-
const data = await res.json();
|
|
674
|
-
if (!data.ok) {
|
|
675
|
-
throw new Error(data.error || "Failed to send message");
|
|
676
|
-
}
|
|
677
|
-
if (data.mode === "chain_addition" || data.mode === "chain_cancel") {
|
|
678
|
-
const ack = data.mode === "chain_addition" ? data.ack || "Got it \u2014 added to the running task." : "Stopping the task\u2026";
|
|
679
|
-
set((s) => ({
|
|
680
|
-
messages: s.messages.map((m) => m.id === userMsg.id && data.chainId ? { ...m, chainId: data.chainId } : m).concat({
|
|
681
|
-
id: `ack-${Date.now()}`,
|
|
682
|
-
role: "assistant",
|
|
683
|
-
content: ack,
|
|
684
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
685
|
-
...data.chainId ? { chainId: data.chainId } : {}
|
|
686
|
-
})
|
|
687
|
-
}));
|
|
688
|
-
return;
|
|
689
|
-
}
|
|
690
|
-
const jobId = data.id || data.jobId;
|
|
691
|
-
if (!jobId) {
|
|
692
|
-
throw new Error("No job ID returned");
|
|
693
|
-
}
|
|
694
|
-
if (attachments?.length) {
|
|
695
|
-
await getConfig().attachmentCache?.save(jobId, attachments);
|
|
696
|
-
}
|
|
697
|
-
const progressMsg = {
|
|
698
|
-
id: `job-${jobId}`,
|
|
699
|
-
role: "assistant",
|
|
700
|
-
content: "Working on it...",
|
|
701
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
702
|
-
jobProgress: {
|
|
703
|
-
jobId,
|
|
704
|
-
actorId: actor?.actorId,
|
|
705
|
-
status: "running",
|
|
706
|
-
subStatus: "Starting...",
|
|
707
|
-
chainStartedAt: Date.now()
|
|
708
|
-
},
|
|
709
|
-
chainId: jobId
|
|
710
|
-
};
|
|
711
|
-
set((s) => ({
|
|
712
|
-
messages: s.messages.map(
|
|
713
|
-
(m) => m.id === userMsg.id ? { ...m, chainId: jobId } : m
|
|
714
|
-
).concat(progressMsg),
|
|
715
|
-
activeJobId: jobId
|
|
716
|
-
}));
|
|
717
|
-
streamJob(jobId, getTenant(), getToken());
|
|
718
|
-
} catch (err) {
|
|
719
|
-
set((s) => ({
|
|
720
|
-
messages: [...s.messages, {
|
|
721
|
-
id: `error-${Date.now()}`,
|
|
722
|
-
role: "system",
|
|
723
|
-
content: `Error: ${err.message}`,
|
|
724
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
725
|
-
}],
|
|
726
|
-
loading: false
|
|
727
|
-
}));
|
|
721
|
+
return { ok: true };
|
|
728
722
|
}
|
|
723
|
+
set({ tiles: before });
|
|
724
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
729
725
|
},
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
726
|
+
rename: async (artifactId, name) => {
|
|
727
|
+
const trimmed = name.trim();
|
|
728
|
+
if (!trimmed) return { ok: false, error: "Name cannot be empty" };
|
|
729
|
+
if (trimmed.length > 60) return { ok: false, error: "Name must be 60 characters or less" };
|
|
730
|
+
const before = get().tiles;
|
|
731
|
+
set({
|
|
732
|
+
tiles: before.map((t) => t.id === artifactId ? { ...t, name: trimmed } : t)
|
|
733
|
+
});
|
|
734
|
+
const r = await patchArtifact(artifactId, { name: trimmed }, "user_long_press_rename");
|
|
735
|
+
if (r.ok) {
|
|
736
|
+
if (r.artifact) {
|
|
737
|
+
set({
|
|
738
|
+
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
739
|
+
});
|
|
743
740
|
}
|
|
744
|
-
|
|
745
|
-
console.warn("[adas-chat] Error responding to question:", err.message);
|
|
741
|
+
return { ok: true };
|
|
746
742
|
}
|
|
743
|
+
set({ tiles: before });
|
|
744
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
747
745
|
},
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
updateJobProgress: (jobId, progress) => {
|
|
752
|
-
set((s) => ({
|
|
753
|
-
messages: s.messages.map((m) => {
|
|
754
|
-
if (m.jobProgress?.jobId === jobId) {
|
|
755
|
-
const updated = { ...m.jobProgress, ...progress };
|
|
756
|
-
if (progress.status && progress.status !== "paused") {
|
|
757
|
-
updated.pendingQuestion = void 0;
|
|
758
|
-
}
|
|
759
|
-
const isPersisted = m.id.startsWith("bot-");
|
|
760
|
-
const content = isPersisted ? m.content : progress.resultMessage || progress.pendingQuestion?.question || m.content;
|
|
761
|
-
const actions = isPersisted ? m.actions : progress.resultActions?.length ? progress.resultActions : m.actions;
|
|
762
|
-
return {
|
|
763
|
-
...m,
|
|
764
|
-
jobProgress: updated,
|
|
765
|
-
content,
|
|
766
|
-
...actions ? { actions } : {}
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
return m;
|
|
770
|
-
}),
|
|
771
|
-
loading: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? false : s.loading,
|
|
772
|
-
activeJobId: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? null : s.activeJobId
|
|
773
|
-
}));
|
|
774
|
-
},
|
|
775
|
-
clearChat: () => {
|
|
746
|
+
reorder: async (pinnedIds) => {
|
|
747
|
+
const before = get().tiles;
|
|
748
|
+
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
776
749
|
set({
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
conversationLoaded: false
|
|
750
|
+
tiles: before.map((t) => {
|
|
751
|
+
const o = idToOrder.get(t.id);
|
|
752
|
+
return o == null ? t : { ...t, pinOrder: o };
|
|
753
|
+
})
|
|
782
754
|
});
|
|
755
|
+
const r = await setOrder(pinnedIds);
|
|
756
|
+
if (r.ok) return { ok: true };
|
|
757
|
+
set({ tiles: before });
|
|
758
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
783
759
|
},
|
|
784
|
-
|
|
760
|
+
applyServerUpdate: (artifact) => {
|
|
785
761
|
set({
|
|
786
|
-
|
|
787
|
-
loading: false,
|
|
788
|
-
error: null,
|
|
789
|
-
activeJobId: null,
|
|
790
|
-
conversationLoaded: true
|
|
762
|
+
tiles: get().tiles.map((t) => t.id === artifact.id ? { ...t, ...artifact } : t)
|
|
791
763
|
});
|
|
764
|
+
},
|
|
765
|
+
applyServerRetire: (artifactId) => {
|
|
766
|
+
set({ tiles: get().tiles.filter((t) => t.id !== artifactId) });
|
|
767
|
+
},
|
|
768
|
+
applyServerOrder: (pinnedIds) => {
|
|
769
|
+
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
770
|
+
set({
|
|
771
|
+
tiles: get().tiles.map((t) => {
|
|
772
|
+
const o = idToOrder.get(t.id);
|
|
773
|
+
return o == null ? t : { ...t, pinOrder: o };
|
|
774
|
+
})
|
|
775
|
+
});
|
|
776
|
+
},
|
|
777
|
+
track: (artifactId, event) => {
|
|
778
|
+
recordEvent(artifactId, event, "home");
|
|
792
779
|
}
|
|
793
|
-
}));
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const pluginId = cmd?.pluginId || cmd?.plugin_id || cmd?.args?.plugin_id;
|
|
802
|
-
if (pluginId) {
|
|
803
|
-
const props = cmd?.props ?? cmd?.args?.props ?? void 0;
|
|
804
|
-
pluginCommandEmitter.emit("focus-plugin", { pluginId, props });
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
var _ws = null;
|
|
809
|
-
var _wsReady = false;
|
|
810
|
-
var _currentJobId = null;
|
|
811
|
-
function ensureWs(tenant, token) {
|
|
812
|
-
if (_ws && _wsReady) return _ws;
|
|
813
|
-
const wsUrl = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(token || "")}&tenant=${encodeURIComponent(tenant)}`;
|
|
814
|
-
const ws = new WebSocket(wsUrl);
|
|
815
|
-
_ws = ws;
|
|
816
|
-
_wsReady = false;
|
|
817
|
-
ws.onopen = () => {
|
|
818
|
-
_wsReady = true;
|
|
819
|
-
if (_currentJobId) {
|
|
820
|
-
ws.send(JSON.stringify({ type: "subscribe", jobId: _currentJobId }));
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
ws.onmessage = (ev) => {
|
|
824
|
-
try {
|
|
825
|
-
const msg = JSON.parse(typeof ev.data === "string" ? ev.data : "");
|
|
826
|
-
const { event, data } = msg;
|
|
827
|
-
const { updateJobProgress } = useChatStore.getState();
|
|
828
|
-
const jobId = data && (data.id || data.chainId) || _currentJobId;
|
|
829
|
-
if (event === "plugin_command") {
|
|
830
|
-
dispatchPluginCommands([data]);
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
833
|
-
if (event === "plugin-command-ack" || event === "pong") return;
|
|
834
|
-
if (!jobId) return;
|
|
835
|
-
switch (event) {
|
|
836
|
-
case "init":
|
|
837
|
-
case "job":
|
|
838
|
-
case "update": {
|
|
839
|
-
const snap = data;
|
|
840
|
-
const progress = snapshotToProgress(snap, jobId);
|
|
841
|
-
if (isTerminalSnapshot(snap)) {
|
|
842
|
-
if (snap.status === "done" && (snap.isHandoff || snap.state?.handoffTransfer)) {
|
|
843
|
-
updateJobProgress(jobId, { status: "running" });
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
_currentJobId = null;
|
|
847
|
-
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
848
|
-
updateJobProgress(jobId, progress);
|
|
849
|
-
} else {
|
|
850
|
-
fetchTurnResult(jobId, snap);
|
|
851
|
-
}
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
updateJobProgress(jobId, progress);
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
857
|
-
case "chain_final":
|
|
858
|
-
_currentJobId = null;
|
|
859
|
-
fetchTurnResult(jobId, data);
|
|
860
|
-
break;
|
|
861
|
-
case "final":
|
|
862
|
-
_currentJobId = null;
|
|
863
|
-
fetchTurnResult(jobId, data);
|
|
864
|
-
break;
|
|
865
|
-
}
|
|
866
|
-
} catch (err) {
|
|
867
|
-
console.warn("[adas-chat] WS parse error:", err);
|
|
868
|
-
}
|
|
869
|
-
};
|
|
870
|
-
ws.onerror = () => {
|
|
871
|
-
_ws = null;
|
|
872
|
-
_wsReady = false;
|
|
873
|
-
if (_currentJobId) {
|
|
874
|
-
const jid = _currentJobId;
|
|
875
|
-
_currentJobId = null;
|
|
876
|
-
pollJob(jid, token);
|
|
877
|
-
}
|
|
878
|
-
};
|
|
879
|
-
ws.onclose = () => {
|
|
880
|
-
_ws = null;
|
|
881
|
-
_wsReady = false;
|
|
882
|
-
if (_currentJobId) {
|
|
883
|
-
const jid = _currentJobId;
|
|
884
|
-
_currentJobId = null;
|
|
885
|
-
pollJob(jid, token);
|
|
886
|
-
}
|
|
887
|
-
};
|
|
888
|
-
return ws;
|
|
889
|
-
}
|
|
890
|
-
function streamJob(jobId, tenant, token) {
|
|
891
|
-
_currentJobId = jobId;
|
|
892
|
-
const ws = ensureWs(tenant, token);
|
|
893
|
-
if (_wsReady) {
|
|
894
|
-
ws.send(JSON.stringify({ type: "subscribe", jobId }));
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
function closeChatStream() {
|
|
898
|
-
_currentJobId = null;
|
|
899
|
-
if (_ws) {
|
|
900
|
-
try {
|
|
901
|
-
_ws.close();
|
|
902
|
-
} catch {
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
_ws = null;
|
|
906
|
-
_wsReady = false;
|
|
907
|
-
}
|
|
908
|
-
async function fetchTurnResult(jobId, jobSnap) {
|
|
909
|
-
if (_processedPluginCmds.size > 200) _processedPluginCmds.clear();
|
|
910
|
-
const { updateJobProgress } = useChatStore.getState();
|
|
911
|
-
const status = normalizeJobStatus(jobSnap) === "error" ? "error" : "done";
|
|
912
|
-
if (jobSnap.isHandoff || jobSnap.state?.handoffTransfer || jobSnap.result?.contentType === "handoff") {
|
|
913
|
-
try {
|
|
914
|
-
for (let i = 0; i < 40; i++) {
|
|
915
|
-
const statusRes = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {
|
|
916
|
-
headers: coreHeaders()
|
|
917
|
-
});
|
|
918
|
-
if (statusRes.ok) {
|
|
919
|
-
const statusData = await statusRes.json();
|
|
920
|
-
if (statusData.done && !statusData.isHandoff) {
|
|
921
|
-
break;
|
|
922
|
-
}
|
|
923
|
-
updateJobProgress(jobId, { status: "running" });
|
|
924
|
-
}
|
|
925
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
926
|
-
}
|
|
927
|
-
} catch {
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
for (let attempt = 0; attempt < 5; attempt++) {
|
|
931
|
-
try {
|
|
932
|
-
const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=1`, {
|
|
933
|
-
headers: coreHeaders()
|
|
934
|
-
});
|
|
935
|
-
if (res.ok) {
|
|
936
|
-
const data = await res.json();
|
|
937
|
-
const items = Array.isArray(data.items) ? data.items : [];
|
|
938
|
-
const turn = items[0];
|
|
939
|
-
if (turn?.assistant || turn?.turn?.assistant) {
|
|
940
|
-
const assistant = turn.assistant || turn.turn?.assistant;
|
|
941
|
-
const text = blocksToText(assistant.blocks);
|
|
942
|
-
const actions = Array.isArray(assistant.actions) && assistant.actions.length ? assistant.actions : void 0;
|
|
943
|
-
if (text) {
|
|
944
|
-
updateJobProgress(jobId, {
|
|
945
|
-
status,
|
|
946
|
-
resultMessage: text,
|
|
947
|
-
...actions ? { resultActions: actions } : {}
|
|
948
|
-
});
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
} catch {
|
|
954
|
-
}
|
|
955
|
-
await new Promise((r) => setTimeout(r, 800));
|
|
956
|
-
}
|
|
957
|
-
const fallback = jobSnap.result?.message || jobSnap.state?.finalReply || (status === "error" ? "Something went wrong. Please try again." : "Done.");
|
|
958
|
-
updateJobProgress(jobId, { status, resultMessage: fallback });
|
|
780
|
+
}));
|
|
781
|
+
|
|
782
|
+
// src/acs/homeActionsStore.ts
|
|
783
|
+
var import_zustand2 = require("zustand");
|
|
784
|
+
var CACHE_KEY = "ateam.homeActions.v1";
|
|
785
|
+
function persist(actions) {
|
|
786
|
+
getAcsCacheStorage()?.setItem(CACHE_KEY, JSON.stringify(actions)).catch(() => {
|
|
787
|
+
});
|
|
959
788
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
789
|
+
function sortByPin(a, b) {
|
|
790
|
+
if (!!a.pinned !== !!b.pinned) return a.pinned ? -1 : 1;
|
|
791
|
+
const ao = typeof a.pinOrder === "number" ? a.pinOrder : Number.MAX_SAFE_INTEGER;
|
|
792
|
+
const bo = typeof b.pinOrder === "number" ? b.pinOrder : Number.MAX_SAFE_INTEGER;
|
|
793
|
+
return ao - bo;
|
|
794
|
+
}
|
|
795
|
+
var useHomeActionsStore = (0, import_zustand2.create)((set, get) => ({
|
|
796
|
+
actions: [],
|
|
797
|
+
hydrated: false,
|
|
798
|
+
loading: false,
|
|
799
|
+
hydrate: async () => {
|
|
800
|
+
if (get().hydrated || get().loading) return;
|
|
963
801
|
try {
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
802
|
+
const cached = await getAcsCacheStorage()?.getItem(CACHE_KEY);
|
|
803
|
+
if (cached) {
|
|
804
|
+
const parsed = JSON.parse(cached);
|
|
805
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
806
|
+
set({ actions: parsed, hydrated: true });
|
|
967
807
|
}
|
|
968
|
-
});
|
|
969
|
-
if (!res.ok) {
|
|
970
|
-
if (res.status === 404) return;
|
|
971
|
-
continue;
|
|
972
|
-
}
|
|
973
|
-
const snap = await res.json();
|
|
974
|
-
const progress = snapshotToProgress(snap, jobId);
|
|
975
|
-
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
976
|
-
updateJobProgress(jobId, progress);
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
updateJobProgress(jobId, progress);
|
|
980
|
-
if (isTerminalSnapshot(snap)) {
|
|
981
|
-
fetchTurnResult(jobId, snap);
|
|
982
|
-
return;
|
|
983
808
|
}
|
|
984
809
|
} catch {
|
|
985
810
|
}
|
|
986
|
-
await
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
811
|
+
await get().refresh();
|
|
812
|
+
},
|
|
813
|
+
refresh: async () => {
|
|
814
|
+
if (get().loading) return;
|
|
815
|
+
set({ loading: true });
|
|
816
|
+
const actions = await listHomeActions();
|
|
817
|
+
if (actions.length > 0) {
|
|
818
|
+
set({ actions, loading: false, hydrated: true });
|
|
819
|
+
persist(actions);
|
|
820
|
+
} else {
|
|
821
|
+
set({ loading: false, hydrated: true });
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
// clear() wipes in-memory state on sign-out AND drops the disk cache — a
|
|
825
|
+
// different user must not see the prior user's cards.
|
|
826
|
+
clear: () => {
|
|
827
|
+
set({ actions: [], hydrated: false, loading: false });
|
|
828
|
+
getAcsCacheStorage()?.setItem(CACHE_KEY, "[]").catch(() => {
|
|
1001
829
|
});
|
|
1002
|
-
const data = await res.json().catch(() => null);
|
|
1003
|
-
if (!res.ok || !data?.ok || !Array.isArray(data.tenants)) return [];
|
|
1004
|
-
return data.tenants.map((t) => ({
|
|
1005
|
-
id: t.id,
|
|
1006
|
-
name: t.name || t.id,
|
|
1007
|
-
role: t.role ?? null
|
|
1008
|
-
}));
|
|
1009
|
-
} catch {
|
|
1010
|
-
return [];
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
async function switchTenant(targetTenantId) {
|
|
1014
|
-
const token = getToken();
|
|
1015
|
-
if (!token) throw new Error("[adas] switchTenant: not signed in");
|
|
1016
|
-
const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/switch-tenant`, {
|
|
1017
|
-
method: "POST",
|
|
1018
|
-
headers: {
|
|
1019
|
-
"Content-Type": "application/json",
|
|
1020
|
-
Authorization: `Bearer ${token}`
|
|
1021
|
-
},
|
|
1022
|
-
body: JSON.stringify({ target_tenant: targetTenantId })
|
|
1023
|
-
});
|
|
1024
|
-
const data = await res.json().catch(() => null);
|
|
1025
|
-
if (!res.ok || !data?.ok || !data.jwt) {
|
|
1026
|
-
throw new Error(data?.error || `switchTenant failed (${res.status})`);
|
|
1027
|
-
}
|
|
1028
|
-
const session = {
|
|
1029
|
-
jwt: data.jwt,
|
|
1030
|
-
tenant: data.tenant,
|
|
1031
|
-
actor: data.actor
|
|
1032
|
-
};
|
|
1033
|
-
const onSession = getConfig().onSession;
|
|
1034
|
-
if (!onSession) {
|
|
1035
|
-
throw new Error("[adas] switchTenant: config.onSession is required to persist the new session");
|
|
1036
|
-
}
|
|
1037
|
-
await onSession(session);
|
|
1038
|
-
closeChatStream();
|
|
1039
|
-
useChatStore.getState().clearChat();
|
|
1040
|
-
return session;
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
// src/theme/tokens.ts
|
|
1044
|
-
var colors = {
|
|
1045
|
-
dark: {
|
|
1046
|
-
bgPrimary: "#0a0e14",
|
|
1047
|
-
bgSecondary: "#111820",
|
|
1048
|
-
bgTertiary: "#1a2230",
|
|
1049
|
-
bgInput: "#0d1219",
|
|
1050
|
-
bgHover: "#1e2a3a",
|
|
1051
|
-
border: "rgba(255, 255, 255, 0.08)",
|
|
1052
|
-
borderHover: "rgba(255, 255, 255, 0.15)",
|
|
1053
|
-
textPrimary: "#e8ecf1",
|
|
1054
|
-
textSecondary: "#8b95a5",
|
|
1055
|
-
textMuted: "#5a6577",
|
|
1056
|
-
accent: "#2563eb",
|
|
1057
|
-
accentHover: "#3b82f6",
|
|
1058
|
-
accentSoft: "rgba(37, 99, 235, 0.15)",
|
|
1059
|
-
green: "#16a34a",
|
|
1060
|
-
greenSoft: "rgba(22, 163, 74, 0.15)",
|
|
1061
|
-
red: "#dc2626",
|
|
1062
|
-
redSoft: "rgba(220, 38, 38, 0.15)",
|
|
1063
|
-
amber: "#f59e0b",
|
|
1064
|
-
amberSoft: "rgba(245, 158, 11, 0.15)",
|
|
1065
|
-
purple: "#8b5cf6",
|
|
1066
|
-
purpleSoft: "rgba(139, 92, 246, 0.15)",
|
|
1067
|
-
// Message-specific
|
|
1068
|
-
userBubble: "#2563eb",
|
|
1069
|
-
botBubble: "#1a2230",
|
|
1070
|
-
botBubbleBorder: "rgba(255, 255, 255, 0.08)"
|
|
1071
830
|
},
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
md: 12,
|
|
1138
|
-
lg: 16,
|
|
1139
|
-
xl: 20,
|
|
1140
|
-
full: 9999
|
|
1141
|
-
};
|
|
831
|
+
dismiss: async (actionId) => {
|
|
832
|
+
const before = get().actions;
|
|
833
|
+
const next = before.filter((a) => a.actionId !== actionId);
|
|
834
|
+
set({ actions: next });
|
|
835
|
+
persist(next);
|
|
836
|
+
const r = await patchHomeAction(actionId, { dismissed: true }, "user_dismiss");
|
|
837
|
+
if (r.ok) return { ok: true };
|
|
838
|
+
set({ actions: before });
|
|
839
|
+
persist(before);
|
|
840
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
841
|
+
},
|
|
842
|
+
setPinned: async (actionId, pinned) => {
|
|
843
|
+
const before = get().actions;
|
|
844
|
+
const next = before.map((a) => a.actionId === actionId ? { ...a, pinned } : a).sort(sortByPin);
|
|
845
|
+
set({ actions: next });
|
|
846
|
+
persist(next);
|
|
847
|
+
const r = await patchHomeAction(actionId, { pinned }, pinned ? "user_pin" : "user_unpin");
|
|
848
|
+
if (r.ok) {
|
|
849
|
+
if (r.action) {
|
|
850
|
+
const merged = get().actions.map((a) => a.actionId === actionId ? { ...a, ...r.action } : a).sort(sortByPin);
|
|
851
|
+
set({ actions: merged });
|
|
852
|
+
persist(merged);
|
|
853
|
+
}
|
|
854
|
+
return { ok: true };
|
|
855
|
+
}
|
|
856
|
+
set({ actions: before });
|
|
857
|
+
persist(before);
|
|
858
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
859
|
+
},
|
|
860
|
+
reorder: async (orderedIds) => {
|
|
861
|
+
const before = get().actions;
|
|
862
|
+
const idx = new Map(orderedIds.map((id, i) => [id, i]));
|
|
863
|
+
const next = before.map((a) => {
|
|
864
|
+
const o = idx.get(a.actionId);
|
|
865
|
+
return o == null ? a : { ...a, pinOrder: o };
|
|
866
|
+
}).sort(sortByPin);
|
|
867
|
+
set({ actions: next });
|
|
868
|
+
persist(next);
|
|
869
|
+
const r = await setHomeActionOrder(orderedIds);
|
|
870
|
+
if (r.ok) return { ok: true };
|
|
871
|
+
set({ actions: before });
|
|
872
|
+
persist(before);
|
|
873
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
874
|
+
},
|
|
875
|
+
applyServerUpsert: (action) => {
|
|
876
|
+
const rest = get().actions.filter((a) => a.actionId !== action.actionId);
|
|
877
|
+
const next = [...rest, action].sort(sortByPin);
|
|
878
|
+
set({ actions: next });
|
|
879
|
+
persist(next);
|
|
880
|
+
},
|
|
881
|
+
applyServerRemove: (actionId) => {
|
|
882
|
+
const next = get().actions.filter((a) => a.actionId !== actionId);
|
|
883
|
+
set({ actions: next });
|
|
884
|
+
persist(next);
|
|
885
|
+
},
|
|
886
|
+
applyServerOrder: (orderedIds) => {
|
|
887
|
+
const idx = new Map(orderedIds.map((id, i) => [id, i]));
|
|
888
|
+
const next = get().actions.map((a) => {
|
|
889
|
+
const o = idx.get(a.actionId);
|
|
890
|
+
return o == null ? a : { ...a, pinOrder: o };
|
|
891
|
+
}).sort(sortByPin);
|
|
892
|
+
set({ actions: next });
|
|
893
|
+
persist(next);
|
|
894
|
+
}
|
|
895
|
+
}));
|
|
1142
896
|
|
|
1143
|
-
// src/acs/
|
|
1144
|
-
var
|
|
1145
|
-
|
|
1146
|
-
|
|
897
|
+
// src/acs/ws.ts
|
|
898
|
+
var _ws = null;
|
|
899
|
+
var _wantOpen = false;
|
|
900
|
+
var _retryAttempt = 0;
|
|
901
|
+
var _retryTimer = null;
|
|
902
|
+
var _token = null;
|
|
903
|
+
var _tenant = null;
|
|
904
|
+
var TOPIC = "acs.artifacts";
|
|
905
|
+
var TOPIC_HOME = "acs.home-actions";
|
|
906
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
907
|
+
var TERMINAL_CLOSE_CODES = /* @__PURE__ */ new Set([4003]);
|
|
908
|
+
var _terminated = false;
|
|
909
|
+
function backoffMs(attempt) {
|
|
910
|
+
const base = Math.min(MAX_BACKOFF_MS, 500 * Math.pow(2, attempt));
|
|
911
|
+
const jitter = Math.random() * 250;
|
|
912
|
+
return base + jitter;
|
|
1147
913
|
}
|
|
1148
|
-
function
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
914
|
+
function send(obj) {
|
|
915
|
+
if (_ws && _ws.readyState === 1) {
|
|
916
|
+
try {
|
|
917
|
+
_ws.send(JSON.stringify(obj));
|
|
918
|
+
} catch (e) {
|
|
919
|
+
console.warn("[acsWs] send failed", e);
|
|
920
|
+
}
|
|
1152
921
|
}
|
|
1153
922
|
}
|
|
1154
|
-
function
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
923
|
+
function handleEvent(msg) {
|
|
924
|
+
if (msg?.event === "subscribed") {
|
|
925
|
+
console.log("[acsWs] subscribed to", msg?.data?.topic);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (msg?.topic === TOPIC_HOME) {
|
|
929
|
+
const home = useHomeActionsStore.getState();
|
|
930
|
+
switch (msg.event) {
|
|
931
|
+
case "updated":
|
|
932
|
+
if (msg.action && typeof msg.action.actionId === "string") home.applyServerUpsert(msg.action);
|
|
933
|
+
break;
|
|
934
|
+
case "removed":
|
|
935
|
+
if (typeof msg.actionId === "string") home.applyServerRemove(msg.actionId);
|
|
936
|
+
break;
|
|
937
|
+
case "order":
|
|
938
|
+
if (Array.isArray(msg.order)) home.applyServerOrder(msg.order.filter((id) => typeof id === "string"));
|
|
939
|
+
break;
|
|
940
|
+
case "refreshed":
|
|
941
|
+
home.refresh();
|
|
942
|
+
break;
|
|
943
|
+
default:
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (msg?.topic !== TOPIC) return;
|
|
949
|
+
const store = useWidgetsStore.getState();
|
|
950
|
+
switch (msg.event) {
|
|
951
|
+
case "updated":
|
|
952
|
+
if (msg.artifact && typeof msg.artifact.id === "string") {
|
|
953
|
+
store.applyServerUpdate(msg.artifact);
|
|
954
|
+
}
|
|
955
|
+
break;
|
|
956
|
+
case "retired":
|
|
957
|
+
if (typeof msg.artifactId === "string") {
|
|
958
|
+
store.applyServerRetire(msg.artifactId);
|
|
959
|
+
}
|
|
960
|
+
break;
|
|
961
|
+
case "order":
|
|
962
|
+
if (Array.isArray(msg.pinned)) {
|
|
963
|
+
store.applyServerOrder(msg.pinned.filter((id) => typeof id === "string"));
|
|
964
|
+
}
|
|
965
|
+
break;
|
|
966
|
+
case "created":
|
|
967
|
+
if (msg.artifact && typeof msg.artifact.id === "string") {
|
|
968
|
+
store.applyServerUpdate(msg.artifact);
|
|
969
|
+
}
|
|
970
|
+
break;
|
|
971
|
+
default:
|
|
972
|
+
break;
|
|
1158
973
|
}
|
|
1159
974
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
975
|
+
function openSocket() {
|
|
976
|
+
if (_ws) return;
|
|
977
|
+
if (!_token || !_tenant) return;
|
|
978
|
+
const url = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(_token)}&tenant=${encodeURIComponent(_tenant)}`;
|
|
979
|
+
console.log(`[acsWs] opening`);
|
|
980
|
+
const ws = new WebSocket(url);
|
|
981
|
+
_ws = ws;
|
|
982
|
+
const isCurrent = () => ws === _ws;
|
|
983
|
+
ws.onopen = () => {
|
|
984
|
+
if (!isCurrent()) return;
|
|
985
|
+
_retryAttempt = 0;
|
|
986
|
+
send({ type: "subscribe", topic: TOPIC });
|
|
987
|
+
send({ type: "subscribe", topic: TOPIC_HOME });
|
|
988
|
+
};
|
|
989
|
+
ws.onmessage = (e) => {
|
|
990
|
+
if (!isCurrent()) return;
|
|
991
|
+
try {
|
|
992
|
+
const msg = JSON.parse(typeof e.data === "string" ? e.data : "");
|
|
993
|
+
handleEvent(msg);
|
|
994
|
+
} catch (err) {
|
|
995
|
+
console.warn("[acsWs] parse failed", err);
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
ws.onerror = (e) => {
|
|
999
|
+
if (!isCurrent()) return;
|
|
1000
|
+
console.warn("[acsWs] error", e?.message || e);
|
|
1001
|
+
};
|
|
1002
|
+
ws.onclose = (ev) => {
|
|
1003
|
+
if (!isCurrent()) return;
|
|
1004
|
+
_ws = null;
|
|
1005
|
+
if (!_wantOpen) return;
|
|
1006
|
+
const code = typeof ev?.code === "number" ? ev.code : 0;
|
|
1007
|
+
if (TERMINAL_CLOSE_CODES.has(code)) {
|
|
1008
|
+
_terminated = true;
|
|
1009
|
+
if (_retryTimer) {
|
|
1010
|
+
clearTimeout(_retryTimer);
|
|
1011
|
+
_retryTimer = null;
|
|
1012
|
+
}
|
|
1013
|
+
console.warn(`[acsWs] server refused permanently (close ${code}); stopping reconnects until re-auth/tenant change`);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const delay = backoffMs(_retryAttempt++);
|
|
1017
|
+
console.log(`[acsWs] closed (${code}); reconnecting in ${Math.round(delay)}ms (attempt ${_retryAttempt})`);
|
|
1018
|
+
if (_retryTimer) clearTimeout(_retryTimer);
|
|
1019
|
+
_retryTimer = setTimeout(openSocket, delay);
|
|
1020
|
+
};
|
|
1163
1021
|
}
|
|
1164
|
-
function
|
|
1165
|
-
return
|
|
1022
|
+
function notifyAcsWsForeground() {
|
|
1023
|
+
if (_terminated) return;
|
|
1024
|
+
if (_wantOpen && !_ws) {
|
|
1025
|
+
if (_retryTimer) {
|
|
1026
|
+
clearTimeout(_retryTimer);
|
|
1027
|
+
_retryTimer = null;
|
|
1028
|
+
}
|
|
1029
|
+
_retryAttempt = 0;
|
|
1030
|
+
openSocket();
|
|
1031
|
+
}
|
|
1166
1032
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
1177
|
-
...init?.headers || {}
|
|
1178
|
-
}
|
|
1179
|
-
});
|
|
1180
|
-
reportAcsServerUp();
|
|
1181
|
-
return res;
|
|
1182
|
-
} catch (e) {
|
|
1183
|
-
reportAcsServerDown();
|
|
1184
|
-
throw e;
|
|
1033
|
+
function connectAcsWs(token, tenant) {
|
|
1034
|
+
_terminated = false;
|
|
1035
|
+
if (_ws && _wantOpen && _token === token && _tenant === tenant) return;
|
|
1036
|
+
if (_ws) {
|
|
1037
|
+
try {
|
|
1038
|
+
_ws.close();
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
_ws = null;
|
|
1185
1042
|
}
|
|
1043
|
+
_token = token;
|
|
1044
|
+
_tenant = tenant;
|
|
1045
|
+
_wantOpen = true;
|
|
1046
|
+
openSocket();
|
|
1186
1047
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1048
|
+
function disconnectAcsWs() {
|
|
1049
|
+
_wantOpen = false;
|
|
1050
|
+
_terminated = false;
|
|
1051
|
+
_token = null;
|
|
1052
|
+
_tenant = null;
|
|
1053
|
+
if (_retryTimer) {
|
|
1054
|
+
clearTimeout(_retryTimer);
|
|
1055
|
+
_retryTimer = null;
|
|
1056
|
+
}
|
|
1057
|
+
if (_ws) {
|
|
1058
|
+
try {
|
|
1059
|
+
_ws.close();
|
|
1060
|
+
} catch {
|
|
1196
1061
|
}
|
|
1197
|
-
|
|
1198
|
-
return Array.isArray(data?.items) ? data.items : [];
|
|
1199
|
-
} catch (e) {
|
|
1200
|
-
console.warn("[acs] /surface failed:", e?.message || e);
|
|
1201
|
-
return [];
|
|
1062
|
+
_ws = null;
|
|
1202
1063
|
}
|
|
1203
1064
|
}
|
|
1204
|
-
|
|
1065
|
+
|
|
1066
|
+
// src/lifecycle.ts
|
|
1067
|
+
var foregroundCbs = /* @__PURE__ */ new Set();
|
|
1068
|
+
function onAppForeground(cb) {
|
|
1069
|
+
foregroundCbs.add(cb);
|
|
1070
|
+
return () => {
|
|
1071
|
+
foregroundCbs.delete(cb);
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
function notifyAppForeground() {
|
|
1205
1075
|
try {
|
|
1206
|
-
|
|
1207
|
-
if (!res.ok) {
|
|
1208
|
-
console.warn(`[acs] /made-for-you returned ${res.status}`);
|
|
1209
|
-
return [];
|
|
1210
|
-
}
|
|
1211
|
-
const data = await res.json();
|
|
1212
|
-
return Array.isArray(data?.artifacts) ? data.artifacts : [];
|
|
1076
|
+
notifyAcsWsForeground();
|
|
1213
1077
|
} catch (e) {
|
|
1214
|
-
console.warn("[
|
|
1215
|
-
|
|
1078
|
+
console.warn("[adas/lifecycle] acs foreground reconnect failed:", e?.message || e);
|
|
1079
|
+
}
|
|
1080
|
+
for (const cb of foregroundCbs) {
|
|
1081
|
+
try {
|
|
1082
|
+
cb();
|
|
1083
|
+
} catch (e) {
|
|
1084
|
+
console.warn("[adas/lifecycle] foreground callback failed:", e?.message || e);
|
|
1085
|
+
}
|
|
1216
1086
|
}
|
|
1217
1087
|
}
|
|
1218
|
-
|
|
1088
|
+
|
|
1089
|
+
// src/gate.ts
|
|
1090
|
+
function resolveAdasPhase(f) {
|
|
1091
|
+
if (!f.configHydrated || f.authLoading || f.capabilitiesLoading || f.onboarded === void 0) {
|
|
1092
|
+
return "loading";
|
|
1093
|
+
}
|
|
1094
|
+
if (!f.onboarded) return "onboarding";
|
|
1095
|
+
if (!f.isAuthenticated) {
|
|
1096
|
+
return f.handoffPending ? "handoff" : f.invitePending ? "invite" : "login";
|
|
1097
|
+
}
|
|
1098
|
+
if (f.requireTenantPick && !f.tenantConfirmed) return "tenant";
|
|
1099
|
+
if (!f.hasCompletedSetup) return "capabilities";
|
|
1100
|
+
return "app";
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// src/consumerAuth.ts
|
|
1104
|
+
function cleanErrorMessage(status, rawText, fallback) {
|
|
1105
|
+
const trimmed = (rawText || "").trim();
|
|
1106
|
+
if (trimmed.startsWith("<")) {
|
|
1107
|
+
return `${fallback} (${status}) \u2014 backend unreachable`;
|
|
1108
|
+
}
|
|
1109
|
+
if (!trimmed) return `${fallback} (${status})`;
|
|
1110
|
+
const short = trimmed.length > 200 ? `${trimmed.slice(0, 200)}\u2026` : trimmed;
|
|
1111
|
+
return `${fallback} (${status}): ${short}`;
|
|
1112
|
+
}
|
|
1113
|
+
async function postJson(path, body, jwt) {
|
|
1114
|
+
const res = await fetch(`${getConsumerAuthApiUrl()}${path}`, {
|
|
1115
|
+
method: "POST",
|
|
1116
|
+
headers: {
|
|
1117
|
+
"Content-Type": "application/json",
|
|
1118
|
+
...jwt ? { Authorization: `Bearer ${jwt}` } : {}
|
|
1119
|
+
},
|
|
1120
|
+
body: JSON.stringify(body)
|
|
1121
|
+
});
|
|
1122
|
+
const text = await res.text();
|
|
1123
|
+
let parsed = null;
|
|
1219
1124
|
try {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
return { ok: true, already: !!data?.already, status: res.status };
|
|
1227
|
-
}
|
|
1228
|
-
if (res.status === 404) {
|
|
1229
|
-
return { ok: true, notFound: true, status: 404 };
|
|
1230
|
-
}
|
|
1231
|
-
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
1232
|
-
} catch (e) {
|
|
1233
|
-
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
1125
|
+
parsed = JSON.parse(text);
|
|
1126
|
+
} catch {
|
|
1127
|
+
}
|
|
1128
|
+
if (!res.ok || parsed?.ok === false) {
|
|
1129
|
+
if (parsed?.error) throw new Error(parsed.error);
|
|
1130
|
+
throw new Error(cleanErrorMessage(res.status, text, `consumer-auth ${path} failed`));
|
|
1234
1131
|
}
|
|
1132
|
+
return parsed;
|
|
1235
1133
|
}
|
|
1236
|
-
async function
|
|
1134
|
+
async function fetchConsumerConfig() {
|
|
1135
|
+
const url = `${getConsumerAuthApiUrl()}/platform/consumer/config?tenant=${encodeURIComponent(getTenant())}`;
|
|
1136
|
+
const res = await fetch(url);
|
|
1137
|
+
const text = await res.text();
|
|
1138
|
+
let data = null;
|
|
1237
1139
|
try {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
return {
|
|
1245
|
-
ok: true,
|
|
1246
|
-
already: !!data?.already,
|
|
1247
|
-
artifact: data?.artifact || void 0,
|
|
1248
|
-
status: res.status
|
|
1249
|
-
};
|
|
1250
|
-
}
|
|
1251
|
-
if (res.status === 404) {
|
|
1252
|
-
return { ok: true, notFound: true, status: 404 };
|
|
1253
|
-
}
|
|
1254
|
-
return {
|
|
1255
|
-
ok: false,
|
|
1256
|
-
status: res.status,
|
|
1257
|
-
error: data?.error || `HTTP ${res.status}`
|
|
1258
|
-
};
|
|
1259
|
-
} catch (e) {
|
|
1260
|
-
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
1140
|
+
data = JSON.parse(text);
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
if (!res.ok || !data?.ok) {
|
|
1144
|
+
if (data?.error) throw new Error(data.error);
|
|
1145
|
+
throw new Error(cleanErrorMessage(res.status, text, "Failed to load sign-in config"));
|
|
1261
1146
|
}
|
|
1147
|
+
return data;
|
|
1262
1148
|
}
|
|
1263
|
-
async function
|
|
1149
|
+
async function signInWithGoogle(idToken) {
|
|
1150
|
+
return postJson("/platform/consumer/oauth/google", {
|
|
1151
|
+
tenant: getTenant(),
|
|
1152
|
+
id_token: idToken
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
async function signInWithApple(idToken) {
|
|
1156
|
+
return postJson("/platform/consumer/oauth/apple", {
|
|
1157
|
+
tenant: getTenant(),
|
|
1158
|
+
id_token: idToken
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
async function exchangeHandoff(code) {
|
|
1162
|
+
return postJson("/platform/consumer/handoff/exchange", { code });
|
|
1163
|
+
}
|
|
1164
|
+
async function previewInvite(code) {
|
|
1165
|
+
const url = `${getConsumerAuthApiUrl()}/platform/consumer/invite/${encodeURIComponent(code)}`;
|
|
1166
|
+
const res = await fetch(url);
|
|
1167
|
+
const text = await res.text();
|
|
1168
|
+
let data = null;
|
|
1264
1169
|
try {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
body: JSON.stringify({ pinned })
|
|
1268
|
-
});
|
|
1269
|
-
if (res.ok) return { ok: true, status: res.status };
|
|
1270
|
-
const data = await res.json().catch(() => ({}));
|
|
1271
|
-
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
1272
|
-
} catch (e) {
|
|
1273
|
-
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
1170
|
+
data = JSON.parse(text);
|
|
1171
|
+
} catch {
|
|
1274
1172
|
}
|
|
1173
|
+
if (res.status === 404) throw new Error("This invite is invalid or has expired.");
|
|
1174
|
+
if (!res.ok || !data?.ok) {
|
|
1175
|
+
if (data?.error) throw new Error(data.error);
|
|
1176
|
+
throw new Error(cleanErrorMessage(res.status, text, "Could not load invite"));
|
|
1177
|
+
}
|
|
1178
|
+
return data;
|
|
1275
1179
|
}
|
|
1276
|
-
async function
|
|
1180
|
+
async function joinWithGoogle(invite, idToken) {
|
|
1181
|
+
return postJson("/platform/consumer/oauth/google", { invite, id_token: idToken });
|
|
1182
|
+
}
|
|
1183
|
+
async function joinWithApple(invite, idToken) {
|
|
1184
|
+
return postJson("/platform/consumer/oauth/apple", { invite, id_token: idToken });
|
|
1185
|
+
}
|
|
1186
|
+
async function openJoinWithGoogle(tenant, idToken) {
|
|
1187
|
+
return postJson("/platform/consumer/oauth/google", { tenant, id_token: idToken });
|
|
1188
|
+
}
|
|
1189
|
+
async function openJoinWithApple(tenant, idToken) {
|
|
1190
|
+
return postJson("/platform/consumer/oauth/apple", { tenant, id_token: idToken });
|
|
1191
|
+
}
|
|
1192
|
+
async function linkChannelStart(jwt, channel, handle) {
|
|
1193
|
+
return postJson("/platform/consumer/link-channel/start", { channel, handle }, jwt);
|
|
1194
|
+
}
|
|
1195
|
+
async function linkChannelVerify(jwt, challengeId, code) {
|
|
1196
|
+
return postJson("/platform/consumer/link-channel/verify", { challenge_id: challengeId, code }, jwt);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/deepLinks.ts
|
|
1200
|
+
function parseDeepLinkParam(url, param) {
|
|
1201
|
+
if (!url) return null;
|
|
1202
|
+
const re = new RegExp(`[?&]${param}=([^&#\\s]+)`, "i");
|
|
1203
|
+
const m = url.match(re);
|
|
1204
|
+
if (!m) return null;
|
|
1277
1205
|
try {
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
body: JSON.stringify({ artifactId, event, ...surface ? { surface } : {} })
|
|
1281
|
-
});
|
|
1206
|
+
const v = decodeURIComponent(m[1]);
|
|
1207
|
+
return v.length > 0 ? v : null;
|
|
1282
1208
|
} catch {
|
|
1209
|
+
return m[1].length > 0 ? m[1] : null;
|
|
1283
1210
|
}
|
|
1284
1211
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1212
|
+
function parseInviteCode(url) {
|
|
1213
|
+
return parseDeepLinkParam(url, "invite");
|
|
1214
|
+
}
|
|
1215
|
+
function parseHandoffCode(url) {
|
|
1216
|
+
return parseDeepLinkParam(url, "handoff");
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// src/workspace.ts
|
|
1220
|
+
var import_zustand3 = require("zustand");
|
|
1221
|
+
function normalize(w) {
|
|
1222
|
+
if (!w || typeof w !== "object") return null;
|
|
1223
|
+
const state = w.state === "active" || w.state === "expired" || w.state === "suspended" ? w.state : "unknown";
|
|
1224
|
+
return {
|
|
1225
|
+
state,
|
|
1226
|
+
reason: typeof w.reason === "string" ? w.reason : null,
|
|
1227
|
+
trialEndsAt: typeof w.trialEndsAt === "string" ? w.trialEndsAt : null,
|
|
1228
|
+
expiresInDays: typeof w.expiresInDays === "number" ? w.expiresInDays : null,
|
|
1229
|
+
capabilities: w.capabilities && typeof w.capabilities === "object" ? w.capabilities : {},
|
|
1230
|
+
message: typeof w.message === "string" && w.message.trim() ? w.message : null
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
var _gen = 0;
|
|
1234
|
+
var useWorkspaceStore = (0, import_zustand3.create)((set) => ({
|
|
1235
|
+
snapshot: null,
|
|
1236
|
+
loaded: false,
|
|
1237
|
+
refresh: async () => {
|
|
1238
|
+
const gen = _gen;
|
|
1239
|
+
try {
|
|
1240
|
+
const res = await apiFetch("/api/session");
|
|
1241
|
+
if (gen !== _gen) return;
|
|
1242
|
+
set({ snapshot: normalize(res?.workspace), loaded: true });
|
|
1243
|
+
} catch {
|
|
1244
|
+
if (gen !== _gen) return;
|
|
1245
|
+
set({ loaded: true });
|
|
1291
1246
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
return [];
|
|
1247
|
+
},
|
|
1248
|
+
clear: () => {
|
|
1249
|
+
_gen++;
|
|
1250
|
+
set({ snapshot: null, loaded: false });
|
|
1297
1251
|
}
|
|
1252
|
+
}));
|
|
1253
|
+
function capabilityEnabled(name) {
|
|
1254
|
+
const { snapshot } = useWorkspaceStore.getState();
|
|
1255
|
+
if (!snapshot) return true;
|
|
1256
|
+
return snapshot.capabilities?.[name] === true;
|
|
1257
|
+
}
|
|
1258
|
+
function useCapability(name) {
|
|
1259
|
+
return useWorkspaceStore((s) => {
|
|
1260
|
+
if (!s.snapshot) return true;
|
|
1261
|
+
return s.snapshot.capabilities?.[name] === true;
|
|
1262
|
+
});
|
|
1298
1263
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1264
|
+
|
|
1265
|
+
// src/chat/store.ts
|
|
1266
|
+
var import_zustand4 = require("zustand");
|
|
1267
|
+
|
|
1268
|
+
// src/utils/emitter.ts
|
|
1269
|
+
var RECENT_TTL_MS = 1e4;
|
|
1270
|
+
var RECENT_MAX = 20;
|
|
1271
|
+
var PluginCommandEmitter = class {
|
|
1272
|
+
constructor() {
|
|
1273
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
1274
|
+
// Ring buffer of plugin commands seen recently. Lets a plugin that mounts
|
|
1275
|
+
// AFTER its first command was emitted (e.g. a surface mounted by the
|
|
1276
|
+
// dispatcher in response to focus-plugin) still pick that command up via
|
|
1277
|
+
// `drainPluginCommands(pluginId)`. Self-cleans on TTL + size cap.
|
|
1278
|
+
this.recent = [];
|
|
1279
|
+
}
|
|
1280
|
+
on(event, fn) {
|
|
1281
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
1282
|
+
this.listeners.get(event).add(fn);
|
|
1283
|
+
return () => this.off(event, fn);
|
|
1284
|
+
}
|
|
1285
|
+
off(event, fn) {
|
|
1286
|
+
this.listeners.get(event)?.delete(fn);
|
|
1287
|
+
}
|
|
1288
|
+
emit(event, data) {
|
|
1289
|
+
if (event === "plugin-command" && data && data.pluginId && data.command && data.command !== "__focus") {
|
|
1290
|
+
this.recent.push({
|
|
1291
|
+
pluginId: data.pluginId,
|
|
1292
|
+
command: data.command,
|
|
1293
|
+
args: data.args,
|
|
1294
|
+
correlationId: data.correlationId,
|
|
1295
|
+
_bufferedAt: Date.now()
|
|
1296
|
+
});
|
|
1297
|
+
if (this.recent.length > RECENT_MAX) this.recent.shift();
|
|
1298
|
+
}
|
|
1299
|
+
this.listeners.get(event)?.forEach((fn) => {
|
|
1300
|
+
try {
|
|
1301
|
+
fn(data);
|
|
1302
|
+
} catch {
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
/** Pop and return any plugin-commands targeted at `pluginId` that were
|
|
1307
|
+
* emitted within the last RECENT_TTL_MS. Used by the native bridge on
|
|
1308
|
+
* mount so the surface-dispatcher path doesn't drop the first command. */
|
|
1309
|
+
drainPluginCommands(pluginId) {
|
|
1310
|
+
const now = Date.now();
|
|
1311
|
+
const out = [];
|
|
1312
|
+
const remaining = [];
|
|
1313
|
+
for (const cmd of this.recent) {
|
|
1314
|
+
if (now - cmd._bufferedAt > RECENT_TTL_MS) continue;
|
|
1315
|
+
if (cmd.pluginId === pluginId) out.push(cmd);
|
|
1316
|
+
else remaining.push(cmd);
|
|
1317
|
+
}
|
|
1318
|
+
this.recent = remaining;
|
|
1319
|
+
return out;
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
var pluginCommandEmitter = new PluginCommandEmitter();
|
|
1323
|
+
|
|
1324
|
+
// src/chat/store.ts
|
|
1325
|
+
function coreHeaders() {
|
|
1326
|
+
const token = getToken();
|
|
1327
|
+
const headers = {
|
|
1328
|
+
"Content-Type": "application/json"
|
|
1329
|
+
};
|
|
1330
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
1331
|
+
return headers;
|
|
1332
|
+
}
|
|
1333
|
+
function isTerminalStatus(status) {
|
|
1334
|
+
const s = typeof status === "string" ? status.toLowerCase() : "";
|
|
1335
|
+
return s === "done" || s === "error" || s === "failed" || s === "completed";
|
|
1336
|
+
}
|
|
1337
|
+
function normalizeJobStatus(snap) {
|
|
1338
|
+
const status = typeof snap?.status === "string" ? snap.status.toLowerCase() : "";
|
|
1339
|
+
if (status === "done" || status === "completed") return "done";
|
|
1340
|
+
if (status === "error" || status === "failed") return "error";
|
|
1341
|
+
if (status === "paused" || status === "waiting") return "paused";
|
|
1342
|
+
if (status === "unknown") return "unknown";
|
|
1343
|
+
return "running";
|
|
1344
|
+
}
|
|
1345
|
+
function extractPendingQuestion(snap) {
|
|
1346
|
+
return snap?.pendingQuestion || snap?.state?.pendingQuestion || snap?.state?.waiting || null;
|
|
1347
|
+
}
|
|
1348
|
+
function extractActorId(snap) {
|
|
1349
|
+
const actorId = snap?.actorId || snap?.actor?.actorId || snap?.actor?.id || snap?.state?.actorId || snap?.state?.actor?.actorId || snap?.state?.actor?.id;
|
|
1350
|
+
return typeof actorId === "string" && actorId.length > 0 ? actorId : void 0;
|
|
1351
|
+
}
|
|
1352
|
+
function extractLiveProgress(snap) {
|
|
1353
|
+
const lp = snap?.liveProgress || snap?.state?.__live_progress;
|
|
1354
|
+
const latest = Array.isArray(lp) ? lp[lp.length - 1] : lp;
|
|
1355
|
+
if (!latest?.message) return void 0;
|
|
1356
|
+
return {
|
|
1357
|
+
message: String(latest.message || ""),
|
|
1358
|
+
...latest.step != null ? { step: latest.step } : {},
|
|
1359
|
+
...latest.total != null ? { total: latest.total } : {}
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
function extractCurrentTool(state) {
|
|
1363
|
+
const exec = state?.steps?.__exec;
|
|
1364
|
+
if (!exec || typeof exec !== "object") return void 0;
|
|
1365
|
+
const latest = Object.keys(exec).map((k) => ({ k, n: parseInt(k.replace("iter_", ""), 10) })).filter(({ n }) => !isNaN(n)).sort((a, b) => b.n - a.n)[0];
|
|
1366
|
+
if (!latest) return void 0;
|
|
1367
|
+
const steps = exec[latest.k];
|
|
1368
|
+
if (!Array.isArray(steps) || !steps.length) return void 0;
|
|
1369
|
+
const last = steps[steps.length - 1];
|
|
1370
|
+
return last?.tool || last?.phase;
|
|
1371
|
+
}
|
|
1372
|
+
function snapshotToProgress(snap, rootJobId) {
|
|
1373
|
+
const pq = extractPendingQuestion(snap);
|
|
1374
|
+
const progress = {
|
|
1375
|
+
jobId: rootJobId,
|
|
1376
|
+
actorId: extractActorId(snap),
|
|
1377
|
+
status: pq ? "paused" : normalizeJobStatus(snap),
|
|
1378
|
+
subStatus: snap?.subStatus || snap?.phase || snap?.state?.visual?.phase,
|
|
1379
|
+
phase: snap?.phase || snap?.state?.visual?.phase,
|
|
1380
|
+
iteration: snap?.iteration || snap?.state?.plannerIter,
|
|
1381
|
+
skillSlug: snap?.skillSlug || snap?.activeSkill,
|
|
1382
|
+
currentTool: snap?.currentTool || snap?.lastTool || extractCurrentTool(snap?.state)
|
|
1383
|
+
};
|
|
1384
|
+
const liveProgress = extractLiveProgress(snap);
|
|
1385
|
+
if (liveProgress) progress.liveProgress = liveProgress;
|
|
1386
|
+
if (typeof snap?.humanStatus === "string" && snap.humanStatus) progress.humanStatus = snap.humanStatus;
|
|
1387
|
+
if (snap?.humanLabels && typeof snap.humanLabels === "object") progress.humanLabels = snap.humanLabels;
|
|
1388
|
+
if (pq) {
|
|
1389
|
+
progress.pendingQuestion = {
|
|
1390
|
+
inputId: pq.correlation_id || pq.correlationId || pq.inputId || "",
|
|
1391
|
+
question: pq.prompt_message || pq.question || "The assistant needs your input",
|
|
1392
|
+
inputType: pq.inputType || "text",
|
|
1393
|
+
options: pq.options || pq.requested_fields,
|
|
1394
|
+
placeholder: pq.placeholder
|
|
1395
|
+
};
|
|
1311
1396
|
}
|
|
1397
|
+
return progress;
|
|
1398
|
+
}
|
|
1399
|
+
function isTerminalSnapshot(snap) {
|
|
1400
|
+
if (extractPendingQuestion(snap)) return false;
|
|
1401
|
+
return snap?.done === true || isTerminalStatus(snap?.status);
|
|
1402
|
+
}
|
|
1403
|
+
var TABLE_BLOCK_OPEN = "\uE100";
|
|
1404
|
+
var TABLE_BLOCK_CLOSE = "\uE101";
|
|
1405
|
+
function renderTableShape(content) {
|
|
1406
|
+
if (!content || typeof content !== "object") return null;
|
|
1407
|
+
const cols = Array.isArray(content.columns) ? content.columns : null;
|
|
1408
|
+
const rows = Array.isArray(content.rows) ? content.rows : null;
|
|
1409
|
+
if (!cols || !rows) return null;
|
|
1410
|
+
const payload = encodeURIComponent(JSON.stringify({ columns: cols, rows }));
|
|
1411
|
+
return `${TABLE_BLOCK_OPEN}${payload}${TABLE_BLOCK_CLOSE}`;
|
|
1412
|
+
}
|
|
1413
|
+
function blocksToText(blocks) {
|
|
1414
|
+
if (!blocks || blocks.length === 0) return "";
|
|
1415
|
+
const hasTextBlock = blocks.some(
|
|
1416
|
+
(b) => b.kind === "text" || b.kind === "markdown" || b.kind === "description"
|
|
1417
|
+
);
|
|
1418
|
+
return blocks.map((b) => {
|
|
1419
|
+
if ((b.kind === "json" || b.kind === "code") && hasTextBlock) return "";
|
|
1420
|
+
if (b.kind === "text" || b.kind === "markdown" || b.kind === "description") {
|
|
1421
|
+
return typeof b.content === "string" ? b.content : "";
|
|
1422
|
+
}
|
|
1423
|
+
if (b.kind === "code") {
|
|
1424
|
+
const code = typeof b.content === "object" ? b.content.content : b.content;
|
|
1425
|
+
return `\`\`\`
|
|
1426
|
+
${code}
|
|
1427
|
+
\`\`\``;
|
|
1428
|
+
}
|
|
1429
|
+
if (b.kind === "table" && b.content && typeof b.content === "object") {
|
|
1430
|
+
const md = renderTableShape(b.content);
|
|
1431
|
+
if (md) return md;
|
|
1432
|
+
}
|
|
1433
|
+
if (b.kind === "json") {
|
|
1434
|
+
if (b.content && typeof b.content === "object" && "pendingQuestion" in b.content) return "";
|
|
1435
|
+
const md = renderTableShape(b.content);
|
|
1436
|
+
if (md) return md;
|
|
1437
|
+
return "```json\n" + JSON.stringify(b.content, null, 2) + "\n```";
|
|
1438
|
+
}
|
|
1439
|
+
if (b.kind === "html") {
|
|
1440
|
+
return typeof b.content === "string" ? b.content : "";
|
|
1441
|
+
}
|
|
1442
|
+
return typeof b.content === "string" ? b.content : JSON.stringify(b.content);
|
|
1443
|
+
}).filter(Boolean).join("\n\n");
|
|
1312
1444
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1445
|
+
function blocksToAttachments(blocks) {
|
|
1446
|
+
if (!blocks || blocks.length === 0) return void 0;
|
|
1447
|
+
const atts = [];
|
|
1448
|
+
for (const b of blocks) {
|
|
1449
|
+
if ((b.kind === "image" || b.kind === "file") && (b.url || b.hash)) {
|
|
1450
|
+
atts.push({
|
|
1451
|
+
mimeType: b.mimeType || (b.kind === "image" ? "image/jpeg" : "application/octet-stream"),
|
|
1452
|
+
name: b.name || "attachment",
|
|
1453
|
+
previewUri: b.url ? `${getCoreApiUrl()}${b.url}` : void 0
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1324
1456
|
}
|
|
1457
|
+
return atts.length > 0 ? atts : void 0;
|
|
1325
1458
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
var import_zustand2 = require("zustand");
|
|
1329
|
-
var useWidgetsStore = (0, import_zustand2.create)((set, get) => ({
|
|
1330
|
-
tiles: [],
|
|
1331
|
-
hydrated: false,
|
|
1459
|
+
var useChatStore = (0, import_zustand4.create)((set, get) => ({
|
|
1460
|
+
messages: [],
|
|
1332
1461
|
loading: false,
|
|
1333
1462
|
error: null,
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1463
|
+
activeJobId: null,
|
|
1464
|
+
conversationLoaded: false,
|
|
1465
|
+
// -----------------------------------------------------------------------
|
|
1466
|
+
// Load conversation history from Core
|
|
1467
|
+
// GET /api/conversation → array of turns [{user, assistant, jobId, ...}]
|
|
1468
|
+
// -----------------------------------------------------------------------
|
|
1469
|
+
loadConversation: async () => {
|
|
1341
1470
|
try {
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
set({ tiles: before.filter((t) => t.id !== artifactId) });
|
|
1352
|
-
const result = await deleteArtifact(artifactId, reason);
|
|
1353
|
-
if (result.ok) {
|
|
1354
|
-
return { ok: true };
|
|
1355
|
-
}
|
|
1356
|
-
set({ tiles: before });
|
|
1357
|
-
return { ok: false, error: result.error || `HTTP ${result.status}` };
|
|
1358
|
-
},
|
|
1359
|
-
hide: async (artifactId) => {
|
|
1360
|
-
const before = get().tiles;
|
|
1361
|
-
set({
|
|
1362
|
-
tiles: before.map(
|
|
1363
|
-
(t) => t.id === artifactId ? { ...t, visibility: "hidden" } : t
|
|
1364
|
-
)
|
|
1365
|
-
});
|
|
1366
|
-
const r = await patchArtifact(artifactId, { visibility: "hidden" }, "user_long_press_hide");
|
|
1367
|
-
if (r.ok) {
|
|
1368
|
-
if (r.artifact) {
|
|
1369
|
-
set({
|
|
1370
|
-
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
1371
|
-
});
|
|
1471
|
+
const attachmentCache = getConfig().attachmentCache;
|
|
1472
|
+
await attachmentCache?.ready();
|
|
1473
|
+
const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=20`, {
|
|
1474
|
+
headers: coreHeaders()
|
|
1475
|
+
});
|
|
1476
|
+
if (!res.ok) {
|
|
1477
|
+
console.warn("[adas-chat] Failed to load conversation:", res.status);
|
|
1478
|
+
set({ conversationLoaded: true });
|
|
1479
|
+
return;
|
|
1372
1480
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
},
|
|
1378
|
-
unhide: async (artifactId) => {
|
|
1379
|
-
const before = get().tiles;
|
|
1380
|
-
set({
|
|
1381
|
-
tiles: before.map(
|
|
1382
|
-
(t) => t.id === artifactId ? { ...t, visibility: "pinned" } : t
|
|
1383
|
-
)
|
|
1384
|
-
});
|
|
1385
|
-
const r = await patchArtifact(artifactId, { visibility: "pinned" }, "user_long_press_unhide");
|
|
1386
|
-
if (r.ok) {
|
|
1387
|
-
if (r.artifact) {
|
|
1388
|
-
set({
|
|
1389
|
-
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
1390
|
-
});
|
|
1481
|
+
const data = await res.json();
|
|
1482
|
+
if (!data.ok || !data.items) {
|
|
1483
|
+
set({ conversationLoaded: true });
|
|
1484
|
+
return;
|
|
1391
1485
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1486
|
+
const items = [...data.items].reverse();
|
|
1487
|
+
const persistedMessages = [];
|
|
1488
|
+
const seenJobIds = /* @__PURE__ */ new Set();
|
|
1489
|
+
for (const turn of items) {
|
|
1490
|
+
if (turn.jobId && seenJobIds.has(turn.jobId)) continue;
|
|
1491
|
+
if (turn.jobId) seenJobIds.add(turn.jobId);
|
|
1492
|
+
const userText = blocksToText(turn.user?.blocks);
|
|
1493
|
+
let userAtts = blocksToAttachments(turn.user?.blocks);
|
|
1494
|
+
if (!userAtts && turn.jobId) {
|
|
1495
|
+
userAtts = attachmentCache?.get(turn.jobId);
|
|
1496
|
+
}
|
|
1497
|
+
const turnChainId = typeof turn.chainId === "string" ? turn.chainId : void 0;
|
|
1498
|
+
if (userText || userAtts) {
|
|
1499
|
+
persistedMessages.push({
|
|
1500
|
+
id: `user-${turn.jobId}`,
|
|
1501
|
+
role: "user",
|
|
1502
|
+
content: userText || "",
|
|
1503
|
+
timestamp: new Date(turn.startedAt || Date.now()),
|
|
1504
|
+
skillSlug: turn.skillSlug,
|
|
1505
|
+
...userAtts ? { attachments: userAtts } : {},
|
|
1506
|
+
...turnChainId ? { chainId: turnChainId } : {}
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
const assistantText = blocksToText(turn.assistant?.blocks);
|
|
1510
|
+
const assistantAtts = blocksToAttachments(turn.assistant?.blocks);
|
|
1511
|
+
const assistantActions = Array.isArray(turn.assistant?.actions) ? turn.assistant.actions : void 0;
|
|
1512
|
+
const turnKind = typeof turn.kind === "string" ? turn.kind : void 0;
|
|
1513
|
+
if (assistantText || assistantAtts || assistantActions?.length) {
|
|
1514
|
+
persistedMessages.push({
|
|
1515
|
+
id: `bot-${turn.jobId}`,
|
|
1516
|
+
role: "assistant",
|
|
1517
|
+
content: assistantText || "",
|
|
1518
|
+
timestamp: new Date(turn.finishedAt || turn.startedAt || Date.now()),
|
|
1519
|
+
skillSlug: turn.skillSlug,
|
|
1520
|
+
...assistantAtts ? { attachments: assistantAtts } : {},
|
|
1521
|
+
...assistantActions?.length ? { actions: assistantActions } : {},
|
|
1522
|
+
...turnChainId ? { chainId: turnChainId } : {},
|
|
1523
|
+
...turnKind ? { kind: turnKind } : {}
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
const current = get().messages;
|
|
1528
|
+
const keptPlaceholders = [];
|
|
1529
|
+
for (const m of current) {
|
|
1530
|
+
if (!m.id.startsWith("job-") || !m.jobProgress || !m.chainId) continue;
|
|
1531
|
+
const matchIdx = persistedMessages.findIndex(
|
|
1532
|
+
(pm) => pm.role === "assistant" && pm.chainId === m.chainId
|
|
1533
|
+
);
|
|
1534
|
+
if (matchIdx >= 0) {
|
|
1535
|
+
const st = m.jobProgress.status;
|
|
1536
|
+
if (st === "running" || st === "paused") {
|
|
1537
|
+
persistedMessages[matchIdx] = { ...persistedMessages[matchIdx], jobProgress: m.jobProgress };
|
|
1538
|
+
}
|
|
1539
|
+
} else {
|
|
1540
|
+
keptPlaceholders.push(m);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
const isLocalUserId = (id) => /^user-\d+$/.test(id);
|
|
1544
|
+
const keptUserBubbles = current.filter((m) => {
|
|
1545
|
+
if (m.role !== "user" || !isLocalUserId(m.id)) return false;
|
|
1546
|
+
if (!m.chainId) return true;
|
|
1547
|
+
return !persistedMessages.some(
|
|
1548
|
+
(pm) => pm.role === "user" && pm.chainId === m.chainId
|
|
1549
|
+
);
|
|
1550
|
+
});
|
|
1551
|
+
const finalMessages = [...persistedMessages, ...keptUserBubbles, ...keptPlaceholders];
|
|
1552
|
+
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
1553
|
+
let firstRunning = keptPlaceholders.find((m) => m.jobProgress?.status === "running");
|
|
1554
|
+
try {
|
|
1555
|
+
let activeChains = [];
|
|
1556
|
+
const chainsRes = await fetch(`${getCoreApiUrl()}/api/chains/active`, {
|
|
1557
|
+
headers: coreHeaders()
|
|
1410
1558
|
});
|
|
1559
|
+
if (chainsRes.ok) {
|
|
1560
|
+
const body = await chainsRes.json();
|
|
1561
|
+
activeChains = Array.isArray(body?.chains) ? body.chains : [];
|
|
1562
|
+
} else {
|
|
1563
|
+
const legacy = await fetch(`${getCoreApiUrl()}/api/chain/current`, {
|
|
1564
|
+
headers: coreHeaders()
|
|
1565
|
+
});
|
|
1566
|
+
if (legacy.ok) {
|
|
1567
|
+
const lb = await legacy.json();
|
|
1568
|
+
if (lb?.data?.id) activeChains = [lb.data];
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
for (const slim of activeChains) {
|
|
1572
|
+
if (!slim?.id) continue;
|
|
1573
|
+
const chainId = String(slim.id);
|
|
1574
|
+
const alreadyTracked = finalMessages.some(
|
|
1575
|
+
(m) => m.role === "assistant" && m.jobProgress?.jobId === chainId
|
|
1576
|
+
);
|
|
1577
|
+
if (alreadyTracked) continue;
|
|
1578
|
+
const progress = snapshotToProgress(
|
|
1579
|
+
{ ...slim, status: slim.chainStatus || slim.status },
|
|
1580
|
+
chainId
|
|
1581
|
+
);
|
|
1582
|
+
const chainCreatedAt = typeof slim.createdAt === "number" ? slim.createdAt : typeof slim.lastUpdate === "number" ? slim.lastUpdate : Date.now();
|
|
1583
|
+
const chainLastUpdate = typeof slim.lastUpdate === "number" ? slim.lastUpdate : chainCreatedAt;
|
|
1584
|
+
const jobProgress = {
|
|
1585
|
+
jobId: chainId,
|
|
1586
|
+
actorId: extractActorId(slim),
|
|
1587
|
+
status: "running",
|
|
1588
|
+
chainStartedAt: chainCreatedAt,
|
|
1589
|
+
...progress
|
|
1590
|
+
};
|
|
1591
|
+
const goal = typeof slim.goal === "string" ? slim.goal : "";
|
|
1592
|
+
const userExists = finalMessages.some(
|
|
1593
|
+
(m) => m.role === "user" && (m.chainId === chainId || goal && m.content === goal)
|
|
1594
|
+
);
|
|
1595
|
+
if (goal && !userExists) {
|
|
1596
|
+
finalMessages.push({
|
|
1597
|
+
id: `user-${chainId}`,
|
|
1598
|
+
role: "user",
|
|
1599
|
+
content: goal,
|
|
1600
|
+
timestamp: new Date(chainCreatedAt),
|
|
1601
|
+
chainId
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
let matchIdx = -1;
|
|
1605
|
+
for (let i = finalMessages.length - 1; i >= 0; i--) {
|
|
1606
|
+
const m = finalMessages[i];
|
|
1607
|
+
if (m.role === "assistant" && m.chainId === chainId) {
|
|
1608
|
+
matchIdx = i;
|
|
1609
|
+
break;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
if (matchIdx >= 0) {
|
|
1613
|
+
finalMessages[matchIdx] = { ...finalMessages[matchIdx], jobProgress };
|
|
1614
|
+
if (jobProgress.status === "running" && !firstRunning) firstRunning = finalMessages[matchIdx];
|
|
1615
|
+
} else {
|
|
1616
|
+
const reattachBubble = {
|
|
1617
|
+
id: `job-${chainId}`,
|
|
1618
|
+
role: "assistant",
|
|
1619
|
+
content: "Working on it...",
|
|
1620
|
+
timestamp: new Date(chainLastUpdate),
|
|
1621
|
+
jobProgress,
|
|
1622
|
+
chainId
|
|
1623
|
+
};
|
|
1624
|
+
finalMessages.push(reattachBubble);
|
|
1625
|
+
if (jobProgress.status === "running" && !firstRunning) firstRunning = reattachBubble;
|
|
1626
|
+
}
|
|
1627
|
+
streamJob(chainId, getTenant(), getToken());
|
|
1628
|
+
}
|
|
1629
|
+
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
1630
|
+
} catch {
|
|
1411
1631
|
}
|
|
1412
|
-
|
|
1632
|
+
set({
|
|
1633
|
+
messages: finalMessages,
|
|
1634
|
+
conversationLoaded: true,
|
|
1635
|
+
error: null,
|
|
1636
|
+
activeJobId: firstRunning?.jobProgress?.jobId || null,
|
|
1637
|
+
loading: !!firstRunning
|
|
1638
|
+
});
|
|
1639
|
+
} catch (err) {
|
|
1640
|
+
console.warn("[adas-chat] Error loading conversation:", err.message);
|
|
1641
|
+
set({ conversationLoaded: true });
|
|
1413
1642
|
}
|
|
1414
|
-
set({ tiles: before });
|
|
1415
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1416
|
-
},
|
|
1417
|
-
reorder: async (pinnedIds) => {
|
|
1418
|
-
const before = get().tiles;
|
|
1419
|
-
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
1420
|
-
set({
|
|
1421
|
-
tiles: before.map((t) => {
|
|
1422
|
-
const o = idToOrder.get(t.id);
|
|
1423
|
-
return o == null ? t : { ...t, pinOrder: o };
|
|
1424
|
-
})
|
|
1425
|
-
});
|
|
1426
|
-
const r = await setOrder(pinnedIds);
|
|
1427
|
-
if (r.ok) return { ok: true };
|
|
1428
|
-
set({ tiles: before });
|
|
1429
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1430
|
-
},
|
|
1431
|
-
applyServerUpdate: (artifact) => {
|
|
1432
|
-
set({
|
|
1433
|
-
tiles: get().tiles.map((t) => t.id === artifact.id ? { ...t, ...artifact } : t)
|
|
1434
|
-
});
|
|
1435
|
-
},
|
|
1436
|
-
applyServerRetire: (artifactId) => {
|
|
1437
|
-
set({ tiles: get().tiles.filter((t) => t.id !== artifactId) });
|
|
1438
|
-
},
|
|
1439
|
-
applyServerOrder: (pinnedIds) => {
|
|
1440
|
-
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
1441
|
-
set({
|
|
1442
|
-
tiles: get().tiles.map((t) => {
|
|
1443
|
-
const o = idToOrder.get(t.id);
|
|
1444
|
-
return o == null ? t : { ...t, pinOrder: o };
|
|
1445
|
-
})
|
|
1446
|
-
});
|
|
1447
1643
|
},
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
actions: [],
|
|
1468
|
-
hydrated: false,
|
|
1469
|
-
loading: false,
|
|
1470
|
-
hydrate: async () => {
|
|
1471
|
-
if (get().hydrated || get().loading) return;
|
|
1644
|
+
// -----------------------------------------------------------------------
|
|
1645
|
+
// Send message via Core API
|
|
1646
|
+
// POST /api/chat { goal } → { jobId } → stream → fetch turn result
|
|
1647
|
+
// -----------------------------------------------------------------------
|
|
1648
|
+
sendMessage: async (text, skillSlug, attachments) => {
|
|
1649
|
+
const userMsg = {
|
|
1650
|
+
id: `user-${Date.now()}`,
|
|
1651
|
+
role: "user",
|
|
1652
|
+
content: text,
|
|
1653
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1654
|
+
...attachments?.length ? {
|
|
1655
|
+
attachments: attachments.map((a) => ({
|
|
1656
|
+
mimeType: a.mimeType,
|
|
1657
|
+
name: a.name,
|
|
1658
|
+
previewUri: a.mimeType.startsWith("image/") ? `data:${a.mimeType};base64,${a.data}` : void 0
|
|
1659
|
+
}))
|
|
1660
|
+
} : {}
|
|
1661
|
+
};
|
|
1662
|
+
set((s) => ({ messages: [...s.messages, userMsg], loading: true, error: null }));
|
|
1472
1663
|
try {
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1664
|
+
const actor = getUser();
|
|
1665
|
+
const body = {
|
|
1666
|
+
goal: text,
|
|
1667
|
+
// Opt in to the Chain Additions router (attach/cancel/new while a
|
|
1668
|
+
// chain runs). Backend ignores it where not applicable — safe to send.
|
|
1669
|
+
chainRouting: true,
|
|
1670
|
+
triggerContext: {
|
|
1671
|
+
channel: "mobile",
|
|
1672
|
+
replyContext: { chatId: actor?.actorId || "unknown" }
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
if (skillSlug) body.skillSlug = skillSlug;
|
|
1676
|
+
if (attachments?.length) body.attachments = attachments;
|
|
1677
|
+
const res = await fetch(`${getCoreApiUrl()}/api/chat`, {
|
|
1678
|
+
method: "POST",
|
|
1679
|
+
headers: coreHeaders(),
|
|
1680
|
+
body: JSON.stringify(body)
|
|
1681
|
+
});
|
|
1682
|
+
if (!res.ok) {
|
|
1683
|
+
let errMsg = `Server error (${res.status})`;
|
|
1684
|
+
try {
|
|
1685
|
+
const text2 = await res.text();
|
|
1686
|
+
if (text2.startsWith("<") || text2.startsWith("<!")) {
|
|
1687
|
+
errMsg = res.status === 502 ? "Server is temporarily unavailable. Please try again." : `Server error (${res.status})`;
|
|
1688
|
+
} else {
|
|
1689
|
+
const parsed = JSON.parse(text2);
|
|
1690
|
+
errMsg = parsed.error || errMsg;
|
|
1691
|
+
}
|
|
1692
|
+
} catch {
|
|
1478
1693
|
}
|
|
1694
|
+
throw new Error(errMsg);
|
|
1479
1695
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1696
|
+
const data = await res.json();
|
|
1697
|
+
if (!data.ok) {
|
|
1698
|
+
throw new Error(data.error || "Failed to send message");
|
|
1699
|
+
}
|
|
1700
|
+
if (data.mode === "chain_addition" || data.mode === "chain_cancel") {
|
|
1701
|
+
const ack = data.mode === "chain_addition" ? data.ack || "Got it \u2014 added to the running task." : "Stopping the task\u2026";
|
|
1702
|
+
set((s) => ({
|
|
1703
|
+
messages: s.messages.map((m) => m.id === userMsg.id && data.chainId ? { ...m, chainId: data.chainId } : m).concat({
|
|
1704
|
+
id: `ack-${Date.now()}`,
|
|
1705
|
+
role: "assistant",
|
|
1706
|
+
content: ack,
|
|
1707
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1708
|
+
...data.chainId ? { chainId: data.chainId } : {}
|
|
1709
|
+
})
|
|
1710
|
+
}));
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
const jobId = data.id || data.jobId;
|
|
1714
|
+
if (!jobId) {
|
|
1715
|
+
throw new Error("No job ID returned");
|
|
1716
|
+
}
|
|
1717
|
+
if (attachments?.length) {
|
|
1718
|
+
await getConfig().attachmentCache?.save(jobId, attachments);
|
|
1719
|
+
}
|
|
1720
|
+
const progressMsg = {
|
|
1721
|
+
id: `job-${jobId}`,
|
|
1722
|
+
role: "assistant",
|
|
1723
|
+
content: "Working on it...",
|
|
1724
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1725
|
+
jobProgress: {
|
|
1726
|
+
jobId,
|
|
1727
|
+
actorId: actor?.actorId,
|
|
1728
|
+
status: "running",
|
|
1729
|
+
subStatus: "Starting...",
|
|
1730
|
+
chainStartedAt: Date.now()
|
|
1731
|
+
},
|
|
1732
|
+
chainId: jobId
|
|
1733
|
+
};
|
|
1734
|
+
set((s) => ({
|
|
1735
|
+
messages: s.messages.map(
|
|
1736
|
+
(m) => m.id === userMsg.id ? { ...m, chainId: jobId } : m
|
|
1737
|
+
).concat(progressMsg),
|
|
1738
|
+
activeJobId: jobId
|
|
1739
|
+
}));
|
|
1740
|
+
streamJob(jobId, getTenant(), getToken());
|
|
1741
|
+
} catch (err) {
|
|
1742
|
+
set((s) => ({
|
|
1743
|
+
messages: [...s.messages, {
|
|
1744
|
+
id: `error-${Date.now()}`,
|
|
1745
|
+
role: "system",
|
|
1746
|
+
content: `Error: ${err.message}`,
|
|
1747
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1748
|
+
}],
|
|
1749
|
+
loading: false
|
|
1750
|
+
}));
|
|
1493
1751
|
}
|
|
1494
1752
|
},
|
|
1495
|
-
//
|
|
1496
|
-
//
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
if (r.ok) return { ok: true };
|
|
1509
|
-
set({ actions: before });
|
|
1510
|
-
persist(before);
|
|
1511
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1512
|
-
},
|
|
1513
|
-
setPinned: async (actionId, pinned) => {
|
|
1514
|
-
const before = get().actions;
|
|
1515
|
-
const next = before.map((a) => a.actionId === actionId ? { ...a, pinned } : a).sort(sortByPin);
|
|
1516
|
-
set({ actions: next });
|
|
1517
|
-
persist(next);
|
|
1518
|
-
const r = await patchHomeAction(actionId, { pinned }, pinned ? "user_pin" : "user_unpin");
|
|
1519
|
-
if (r.ok) {
|
|
1520
|
-
if (r.action) {
|
|
1521
|
-
const merged = get().actions.map((a) => a.actionId === actionId ? { ...a, ...r.action } : a).sort(sortByPin);
|
|
1522
|
-
set({ actions: merged });
|
|
1523
|
-
persist(merged);
|
|
1753
|
+
// -----------------------------------------------------------------------
|
|
1754
|
+
// Respond to sys.askUser paused question
|
|
1755
|
+
// -----------------------------------------------------------------------
|
|
1756
|
+
respondToQuestion: async (jobId, inputId, answer) => {
|
|
1757
|
+
try {
|
|
1758
|
+
const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/respond`, {
|
|
1759
|
+
method: "POST",
|
|
1760
|
+
headers: coreHeaders(),
|
|
1761
|
+
body: JSON.stringify({ inputId, answer })
|
|
1762
|
+
});
|
|
1763
|
+
const data = await res.json();
|
|
1764
|
+
if (!data.ok) {
|
|
1765
|
+
console.warn("[adas-chat] Failed to respond to question:", data.error);
|
|
1524
1766
|
}
|
|
1525
|
-
|
|
1767
|
+
} catch (err) {
|
|
1768
|
+
console.warn("[adas-chat] Error responding to question:", err.message);
|
|
1526
1769
|
}
|
|
1527
|
-
set({ actions: before });
|
|
1528
|
-
persist(before);
|
|
1529
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1530
|
-
},
|
|
1531
|
-
reorder: async (orderedIds) => {
|
|
1532
|
-
const before = get().actions;
|
|
1533
|
-
const idx = new Map(orderedIds.map((id, i) => [id, i]));
|
|
1534
|
-
const next = before.map((a) => {
|
|
1535
|
-
const o = idx.get(a.actionId);
|
|
1536
|
-
return o == null ? a : { ...a, pinOrder: o };
|
|
1537
|
-
}).sort(sortByPin);
|
|
1538
|
-
set({ actions: next });
|
|
1539
|
-
persist(next);
|
|
1540
|
-
const r = await setHomeActionOrder(orderedIds);
|
|
1541
|
-
if (r.ok) return { ok: true };
|
|
1542
|
-
set({ actions: before });
|
|
1543
|
-
persist(before);
|
|
1544
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1545
1770
|
},
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1771
|
+
// -----------------------------------------------------------------------
|
|
1772
|
+
// Update job progress (called by the stream handler)
|
|
1773
|
+
// -----------------------------------------------------------------------
|
|
1774
|
+
updateJobProgress: (jobId, progress) => {
|
|
1775
|
+
set((s) => ({
|
|
1776
|
+
messages: s.messages.map((m) => {
|
|
1777
|
+
if (m.jobProgress?.jobId === jobId) {
|
|
1778
|
+
const updated = { ...m.jobProgress, ...progress };
|
|
1779
|
+
if (progress.status && progress.status !== "paused") {
|
|
1780
|
+
updated.pendingQuestion = void 0;
|
|
1781
|
+
}
|
|
1782
|
+
const isPersisted = m.id.startsWith("bot-");
|
|
1783
|
+
const content = isPersisted ? m.content : progress.resultMessage || progress.pendingQuestion?.question || m.content;
|
|
1784
|
+
const actions = isPersisted ? m.actions : progress.resultActions?.length ? progress.resultActions : m.actions;
|
|
1785
|
+
return {
|
|
1786
|
+
...m,
|
|
1787
|
+
jobProgress: updated,
|
|
1788
|
+
content,
|
|
1789
|
+
...actions ? { actions } : {}
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
return m;
|
|
1793
|
+
}),
|
|
1794
|
+
loading: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? false : s.loading,
|
|
1795
|
+
activeJobId: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? null : s.activeJobId
|
|
1796
|
+
}));
|
|
1551
1797
|
},
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1798
|
+
clearChat: () => {
|
|
1799
|
+
set({
|
|
1800
|
+
messages: [],
|
|
1801
|
+
loading: false,
|
|
1802
|
+
error: null,
|
|
1803
|
+
activeJobId: null,
|
|
1804
|
+
conversationLoaded: false
|
|
1805
|
+
});
|
|
1556
1806
|
},
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1807
|
+
newChat: () => {
|
|
1808
|
+
set({
|
|
1809
|
+
messages: [],
|
|
1810
|
+
loading: false,
|
|
1811
|
+
error: null,
|
|
1812
|
+
activeJobId: null,
|
|
1813
|
+
conversationLoaded: true
|
|
1814
|
+
});
|
|
1565
1815
|
}
|
|
1566
1816
|
}));
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
function backoffMs(attempt) {
|
|
1579
|
-
const base = Math.min(MAX_BACKOFF_MS, 500 * Math.pow(2, attempt));
|
|
1580
|
-
const jitter = Math.random() * 250;
|
|
1581
|
-
return base + jitter;
|
|
1582
|
-
}
|
|
1583
|
-
function send(obj) {
|
|
1584
|
-
if (_ws2 && _ws2.readyState === 1) {
|
|
1585
|
-
try {
|
|
1586
|
-
_ws2.send(JSON.stringify(obj));
|
|
1587
|
-
} catch (e) {
|
|
1588
|
-
console.warn("[acsWs] send failed", e);
|
|
1817
|
+
var _processedPluginCmds = /* @__PURE__ */ new Set();
|
|
1818
|
+
function dispatchPluginCommands(cmds) {
|
|
1819
|
+
for (const cmd of cmds) {
|
|
1820
|
+
const corrId = cmd?.correlationId || cmd?.correlation_id;
|
|
1821
|
+
if (corrId && _processedPluginCmds.has(corrId)) continue;
|
|
1822
|
+
if (corrId) _processedPluginCmds.add(corrId);
|
|
1823
|
+
pluginCommandEmitter.emit("plugin-command", cmd);
|
|
1824
|
+
const pluginId = cmd?.pluginId || cmd?.plugin_id || cmd?.args?.plugin_id;
|
|
1825
|
+
if (pluginId) {
|
|
1826
|
+
const props = cmd?.props ?? cmd?.args?.props ?? void 0;
|
|
1827
|
+
pluginCommandEmitter.emit("focus-plugin", { pluginId, props });
|
|
1589
1828
|
}
|
|
1590
1829
|
}
|
|
1591
1830
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if (msg?.topic === TOPIC_HOME) {
|
|
1598
|
-
const home = useHomeActionsStore.getState();
|
|
1599
|
-
switch (msg.event) {
|
|
1600
|
-
case "updated":
|
|
1601
|
-
if (msg.action && typeof msg.action.actionId === "string") home.applyServerUpsert(msg.action);
|
|
1602
|
-
break;
|
|
1603
|
-
case "removed":
|
|
1604
|
-
if (typeof msg.actionId === "string") home.applyServerRemove(msg.actionId);
|
|
1605
|
-
break;
|
|
1606
|
-
case "order":
|
|
1607
|
-
if (Array.isArray(msg.order)) home.applyServerOrder(msg.order.filter((id) => typeof id === "string"));
|
|
1608
|
-
break;
|
|
1609
|
-
case "refreshed":
|
|
1610
|
-
home.refresh();
|
|
1611
|
-
break;
|
|
1612
|
-
default:
|
|
1613
|
-
break;
|
|
1614
|
-
}
|
|
1615
|
-
return;
|
|
1616
|
-
}
|
|
1617
|
-
if (msg?.topic !== TOPIC) return;
|
|
1618
|
-
const store = useWidgetsStore.getState();
|
|
1619
|
-
switch (msg.event) {
|
|
1620
|
-
case "updated":
|
|
1621
|
-
if (msg.artifact && typeof msg.artifact.id === "string") {
|
|
1622
|
-
store.applyServerUpdate(msg.artifact);
|
|
1623
|
-
}
|
|
1624
|
-
break;
|
|
1625
|
-
case "retired":
|
|
1626
|
-
if (typeof msg.artifactId === "string") {
|
|
1627
|
-
store.applyServerRetire(msg.artifactId);
|
|
1628
|
-
}
|
|
1629
|
-
break;
|
|
1630
|
-
case "order":
|
|
1631
|
-
if (Array.isArray(msg.pinned)) {
|
|
1632
|
-
store.applyServerOrder(msg.pinned.filter((id) => typeof id === "string"));
|
|
1633
|
-
}
|
|
1634
|
-
break;
|
|
1635
|
-
case "created":
|
|
1636
|
-
if (msg.artifact && typeof msg.artifact.id === "string") {
|
|
1637
|
-
store.applyServerUpdate(msg.artifact);
|
|
1638
|
-
}
|
|
1639
|
-
break;
|
|
1640
|
-
default:
|
|
1641
|
-
break;
|
|
1642
|
-
}
|
|
1831
|
+
var _ws2 = null;
|
|
1832
|
+
var _wsReady = false;
|
|
1833
|
+
var _clientDeclarationProvider = null;
|
|
1834
|
+
function setClientDeclarationProvider(fn) {
|
|
1835
|
+
_clientDeclarationProvider = fn;
|
|
1643
1836
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
if (
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
const ws = new WebSocket(url);
|
|
1837
|
+
var _currentJobId = null;
|
|
1838
|
+
function ensureWs(tenant, token) {
|
|
1839
|
+
if (_ws2 && _wsReady) return _ws2;
|
|
1840
|
+
const wsUrl = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(token || "")}&tenant=${encodeURIComponent(tenant)}`;
|
|
1841
|
+
const ws = new WebSocket(wsUrl);
|
|
1650
1842
|
_ws2 = ws;
|
|
1843
|
+
_wsReady = false;
|
|
1651
1844
|
ws.onopen = () => {
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1845
|
+
_wsReady = true;
|
|
1846
|
+
try {
|
|
1847
|
+
const declared = _clientDeclarationProvider?.();
|
|
1848
|
+
if (declared) ws.send(JSON.stringify({ type: "declare_client", ...declared }));
|
|
1849
|
+
} catch {
|
|
1850
|
+
}
|
|
1851
|
+
if (_currentJobId) {
|
|
1852
|
+
ws.send(JSON.stringify({ type: "subscribe", jobId: _currentJobId }));
|
|
1853
|
+
}
|
|
1655
1854
|
};
|
|
1656
|
-
ws.onmessage = (
|
|
1855
|
+
ws.onmessage = (ev) => {
|
|
1657
1856
|
try {
|
|
1658
|
-
const msg = JSON.parse(typeof
|
|
1659
|
-
|
|
1857
|
+
const msg = JSON.parse(typeof ev.data === "string" ? ev.data : "");
|
|
1858
|
+
const { event, data } = msg;
|
|
1859
|
+
const { updateJobProgress } = useChatStore.getState();
|
|
1860
|
+
const jobId = data && (data.id || data.chainId) || _currentJobId;
|
|
1861
|
+
if (event === "plugin_command") {
|
|
1862
|
+
dispatchPluginCommands([data]);
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
if (event === "plugin-command-ack" || event === "pong") return;
|
|
1866
|
+
if (!jobId) return;
|
|
1867
|
+
switch (event) {
|
|
1868
|
+
case "init":
|
|
1869
|
+
case "job":
|
|
1870
|
+
case "update": {
|
|
1871
|
+
const snap = data;
|
|
1872
|
+
const progress = snapshotToProgress(snap, jobId);
|
|
1873
|
+
if (isTerminalSnapshot(snap)) {
|
|
1874
|
+
if (snap.status === "done" && (snap.isHandoff || snap.state?.handoffTransfer)) {
|
|
1875
|
+
updateJobProgress(jobId, { status: "running" });
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
_currentJobId = null;
|
|
1879
|
+
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
1880
|
+
updateJobProgress(jobId, progress);
|
|
1881
|
+
} else {
|
|
1882
|
+
fetchTurnResult(jobId, snap);
|
|
1883
|
+
}
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
updateJobProgress(jobId, progress);
|
|
1887
|
+
break;
|
|
1888
|
+
}
|
|
1889
|
+
case "chain_final":
|
|
1890
|
+
_currentJobId = null;
|
|
1891
|
+
fetchTurnResult(jobId, data);
|
|
1892
|
+
break;
|
|
1893
|
+
case "final":
|
|
1894
|
+
_currentJobId = null;
|
|
1895
|
+
fetchTurnResult(jobId, data);
|
|
1896
|
+
break;
|
|
1897
|
+
}
|
|
1660
1898
|
} catch (err) {
|
|
1661
|
-
console.warn("[
|
|
1899
|
+
console.warn("[adas-chat] WS parse error:", err);
|
|
1662
1900
|
}
|
|
1663
1901
|
};
|
|
1664
|
-
ws.onerror = (
|
|
1665
|
-
|
|
1902
|
+
ws.onerror = () => {
|
|
1903
|
+
_ws2 = null;
|
|
1904
|
+
_wsReady = false;
|
|
1905
|
+
if (_currentJobId) {
|
|
1906
|
+
const jid = _currentJobId;
|
|
1907
|
+
_currentJobId = null;
|
|
1908
|
+
pollJob(jid, token);
|
|
1909
|
+
}
|
|
1666
1910
|
};
|
|
1667
1911
|
ws.onclose = () => {
|
|
1668
1912
|
_ws2 = null;
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
openSocket();
|
|
1913
|
+
_wsReady = false;
|
|
1914
|
+
if (_currentJobId) {
|
|
1915
|
+
const jid = _currentJobId;
|
|
1916
|
+
_currentJobId = null;
|
|
1917
|
+
pollJob(jid, token);
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
return ws;
|
|
1921
|
+
}
|
|
1922
|
+
function streamJob(jobId, tenant, token) {
|
|
1923
|
+
_currentJobId = jobId;
|
|
1924
|
+
const ws = ensureWs(tenant, token);
|
|
1925
|
+
if (_wsReady) {
|
|
1926
|
+
ws.send(JSON.stringify({ type: "subscribe", jobId }));
|
|
1684
1927
|
}
|
|
1685
1928
|
}
|
|
1686
|
-
function
|
|
1687
|
-
|
|
1929
|
+
function closeChatStream() {
|
|
1930
|
+
_currentJobId = null;
|
|
1688
1931
|
if (_ws2) {
|
|
1689
1932
|
try {
|
|
1690
1933
|
_ws2.close();
|
|
1691
1934
|
} catch {
|
|
1692
1935
|
}
|
|
1693
|
-
_ws2 = null;
|
|
1694
1936
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
_wantOpen = true;
|
|
1698
|
-
openSocket();
|
|
1937
|
+
_ws2 = null;
|
|
1938
|
+
_wsReady = false;
|
|
1699
1939
|
}
|
|
1700
|
-
function
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
if (
|
|
1705
|
-
|
|
1706
|
-
|
|
1940
|
+
async function fetchTurnResult(jobId, jobSnap) {
|
|
1941
|
+
if (_processedPluginCmds.size > 200) _processedPluginCmds.clear();
|
|
1942
|
+
const { updateJobProgress } = useChatStore.getState();
|
|
1943
|
+
const status = normalizeJobStatus(jobSnap) === "error" ? "error" : "done";
|
|
1944
|
+
if (jobSnap.isHandoff || jobSnap.state?.handoffTransfer || jobSnap.result?.contentType === "handoff") {
|
|
1945
|
+
try {
|
|
1946
|
+
for (let i = 0; i < 40; i++) {
|
|
1947
|
+
const statusRes = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {
|
|
1948
|
+
headers: coreHeaders()
|
|
1949
|
+
});
|
|
1950
|
+
if (statusRes.ok) {
|
|
1951
|
+
const statusData = await statusRes.json();
|
|
1952
|
+
if (statusData.done && !statusData.isHandoff) {
|
|
1953
|
+
break;
|
|
1954
|
+
}
|
|
1955
|
+
updateJobProgress(jobId, { status: "running" });
|
|
1956
|
+
}
|
|
1957
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
1958
|
+
}
|
|
1959
|
+
} catch {
|
|
1960
|
+
}
|
|
1707
1961
|
}
|
|
1708
|
-
|
|
1962
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
1709
1963
|
try {
|
|
1710
|
-
|
|
1964
|
+
const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=1`, {
|
|
1965
|
+
headers: coreHeaders()
|
|
1966
|
+
});
|
|
1967
|
+
if (res.ok) {
|
|
1968
|
+
const data = await res.json();
|
|
1969
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
1970
|
+
const turn = items[0];
|
|
1971
|
+
if (turn?.assistant || turn?.turn?.assistant) {
|
|
1972
|
+
const assistant = turn.assistant || turn.turn?.assistant;
|
|
1973
|
+
const text = blocksToText(assistant.blocks);
|
|
1974
|
+
const actions = Array.isArray(assistant.actions) && assistant.actions.length ? assistant.actions : void 0;
|
|
1975
|
+
if (text) {
|
|
1976
|
+
updateJobProgress(jobId, {
|
|
1977
|
+
status,
|
|
1978
|
+
resultMessage: text,
|
|
1979
|
+
...actions ? { resultActions: actions } : {}
|
|
1980
|
+
});
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1711
1985
|
} catch {
|
|
1712
1986
|
}
|
|
1713
|
-
|
|
1987
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
1988
|
+
}
|
|
1989
|
+
const fallback = jobSnap.result?.message || jobSnap.state?.finalReply || (status === "error" ? "Something went wrong. Please try again." : "Done.");
|
|
1990
|
+
updateJobProgress(jobId, { status, resultMessage: fallback });
|
|
1991
|
+
}
|
|
1992
|
+
async function pollJob(jobId, token) {
|
|
1993
|
+
const { updateJobProgress } = useChatStore.getState();
|
|
1994
|
+
for (let i = 0; i < 120; i++) {
|
|
1995
|
+
try {
|
|
1996
|
+
const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {
|
|
1997
|
+
headers: {
|
|
1998
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
if (!res.ok) {
|
|
2002
|
+
if (res.status === 404) return;
|
|
2003
|
+
continue;
|
|
2004
|
+
}
|
|
2005
|
+
const snap = await res.json();
|
|
2006
|
+
const progress = snapshotToProgress(snap, jobId);
|
|
2007
|
+
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
2008
|
+
updateJobProgress(jobId, progress);
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
updateJobProgress(jobId, progress);
|
|
2012
|
+
if (isTerminalSnapshot(snap)) {
|
|
2013
|
+
fetchTurnResult(jobId, snap);
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
} catch {
|
|
2017
|
+
}
|
|
2018
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
2019
|
+
}
|
|
2020
|
+
updateJobProgress(jobId, {
|
|
2021
|
+
status: "error",
|
|
2022
|
+
resultMessage: "Job timed out. Please try again."
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/api/tenants.ts
|
|
2027
|
+
async function listMyTenants() {
|
|
2028
|
+
const token = getToken();
|
|
2029
|
+
if (!token) return [];
|
|
2030
|
+
try {
|
|
2031
|
+
const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/my-tenants`, {
|
|
2032
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2033
|
+
});
|
|
2034
|
+
const data = await res.json().catch(() => null);
|
|
2035
|
+
if (!res.ok || !data?.ok || !Array.isArray(data.tenants)) return [];
|
|
2036
|
+
return data.tenants.map((t) => ({
|
|
2037
|
+
id: t.id,
|
|
2038
|
+
// Fall back to the id rather than to an empty string: a nameless row in
|
|
2039
|
+
// a team switcher is unpickable.
|
|
2040
|
+
name: t.name || t.id,
|
|
2041
|
+
role: t.role ?? null
|
|
2042
|
+
}));
|
|
2043
|
+
} catch {
|
|
2044
|
+
return [];
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
async function switchTenant(targetTenantId) {
|
|
2048
|
+
const token = getToken();
|
|
2049
|
+
if (!token) throw new Error("[adas] switchTenant: not signed in");
|
|
2050
|
+
const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/switch-tenant`, {
|
|
2051
|
+
method: "POST",
|
|
2052
|
+
headers: {
|
|
2053
|
+
"Content-Type": "application/json",
|
|
2054
|
+
Authorization: `Bearer ${token}`
|
|
2055
|
+
},
|
|
2056
|
+
body: JSON.stringify({ target_tenant: targetTenantId })
|
|
2057
|
+
});
|
|
2058
|
+
const data = await res.json().catch(() => null);
|
|
2059
|
+
if (!res.ok || !data?.ok || !data.jwt) {
|
|
2060
|
+
throw new Error(data?.error || `switchTenant failed (${res.status})`);
|
|
2061
|
+
}
|
|
2062
|
+
const session = {
|
|
2063
|
+
jwt: data.jwt,
|
|
2064
|
+
tenant: data.tenant,
|
|
2065
|
+
actor: data.actor
|
|
2066
|
+
};
|
|
2067
|
+
const onSession = getConfig().onSession;
|
|
2068
|
+
if (!onSession) {
|
|
2069
|
+
throw new Error("[adas] switchTenant: config.onSession is required to persist the new session");
|
|
1714
2070
|
}
|
|
2071
|
+
await onSession(session);
|
|
2072
|
+
closeChatStream();
|
|
2073
|
+
useChatStore.getState().clearChat();
|
|
2074
|
+
return session;
|
|
1715
2075
|
}
|
|
2076
|
+
|
|
2077
|
+
// src/theme/tokens.ts
|
|
2078
|
+
var colors = {
|
|
2079
|
+
dark: {
|
|
2080
|
+
bgPrimary: "#0a0e14",
|
|
2081
|
+
bgSecondary: "#111820",
|
|
2082
|
+
bgTertiary: "#1a2230",
|
|
2083
|
+
bgInput: "#0d1219",
|
|
2084
|
+
bgHover: "#1e2a3a",
|
|
2085
|
+
border: "rgba(255, 255, 255, 0.08)",
|
|
2086
|
+
borderHover: "rgba(255, 255, 255, 0.15)",
|
|
2087
|
+
textPrimary: "#e8ecf1",
|
|
2088
|
+
textSecondary: "#8b95a5",
|
|
2089
|
+
textMuted: "#5a6577",
|
|
2090
|
+
accent: "#2563eb",
|
|
2091
|
+
accentHover: "#3b82f6",
|
|
2092
|
+
accentSoft: "rgba(37, 99, 235, 0.15)",
|
|
2093
|
+
green: "#16a34a",
|
|
2094
|
+
greenSoft: "rgba(22, 163, 74, 0.15)",
|
|
2095
|
+
red: "#dc2626",
|
|
2096
|
+
redSoft: "rgba(220, 38, 38, 0.15)",
|
|
2097
|
+
amber: "#f59e0b",
|
|
2098
|
+
amberSoft: "rgba(245, 158, 11, 0.15)",
|
|
2099
|
+
purple: "#8b5cf6",
|
|
2100
|
+
purpleSoft: "rgba(139, 92, 246, 0.15)",
|
|
2101
|
+
// Message-specific
|
|
2102
|
+
userBubble: "#2563eb",
|
|
2103
|
+
botBubble: "#1a2230",
|
|
2104
|
+
botBubbleBorder: "rgba(255, 255, 255, 0.08)"
|
|
2105
|
+
},
|
|
2106
|
+
light: {
|
|
2107
|
+
bgPrimary: "#ffffff",
|
|
2108
|
+
bgSecondary: "#f8f9fa",
|
|
2109
|
+
bgTertiary: "#f1f3f5",
|
|
2110
|
+
bgInput: "#ffffff",
|
|
2111
|
+
bgHover: "#e9ecef",
|
|
2112
|
+
border: "rgba(0, 0, 0, 0.08)",
|
|
2113
|
+
borderHover: "rgba(0, 0, 0, 0.15)",
|
|
2114
|
+
textPrimary: "#1a1a2e",
|
|
2115
|
+
textSecondary: "#6b7280",
|
|
2116
|
+
textMuted: "#9ca3af",
|
|
2117
|
+
accent: "#2563eb",
|
|
2118
|
+
accentHover: "#1d4ed8",
|
|
2119
|
+
accentSoft: "rgba(37, 99, 235, 0.1)",
|
|
2120
|
+
green: "#16a34a",
|
|
2121
|
+
greenSoft: "rgba(22, 163, 74, 0.1)",
|
|
2122
|
+
red: "#dc2626",
|
|
2123
|
+
redSoft: "rgba(220, 38, 38, 0.1)",
|
|
2124
|
+
amber: "#f59e0b",
|
|
2125
|
+
amberSoft: "rgba(245, 158, 11, 0.1)",
|
|
2126
|
+
purple: "#8b5cf6",
|
|
2127
|
+
purpleSoft: "rgba(139, 92, 246, 0.1)",
|
|
2128
|
+
userBubble: "#2563eb",
|
|
2129
|
+
botBubble: "#f1f3f5",
|
|
2130
|
+
botBubbleBorder: "rgba(0, 0, 0, 0.08)"
|
|
2131
|
+
}
|
|
2132
|
+
};
|
|
2133
|
+
var ada = {
|
|
2134
|
+
paper: "#FAF6EE",
|
|
2135
|
+
paperSoft: "#F2EADC",
|
|
2136
|
+
paperDeep: "#EBE0CC",
|
|
2137
|
+
ink: "#1a1410",
|
|
2138
|
+
inkSoft: "#5a4d3f",
|
|
2139
|
+
inkMuted: "#9a8870",
|
|
2140
|
+
border: "rgba(26, 20, 16, 0.08)",
|
|
2141
|
+
borderStrong: "rgba(26, 20, 16, 0.16)",
|
|
2142
|
+
orange: "#FF7A28",
|
|
2143
|
+
orangeDeep: "#B8410A",
|
|
2144
|
+
orangeSoft: "rgba(255, 122, 40, 0.14)",
|
|
2145
|
+
userBubble: "#1a1410",
|
|
2146
|
+
userBubbleText: "#FAF6EE",
|
|
2147
|
+
botBubble: "#F2EADC",
|
|
2148
|
+
botBubbleBorder: "rgba(26, 20, 16, 0.06)",
|
|
2149
|
+
botBubbleText: "#1a1410"
|
|
2150
|
+
};
|
|
2151
|
+
var spacing = {
|
|
2152
|
+
xs: 4,
|
|
2153
|
+
sm: 8,
|
|
2154
|
+
md: 12,
|
|
2155
|
+
lg: 16,
|
|
2156
|
+
xl: 20,
|
|
2157
|
+
xxl: 24,
|
|
2158
|
+
xxxl: 32
|
|
2159
|
+
};
|
|
2160
|
+
var fontSize = {
|
|
2161
|
+
xs: 11,
|
|
2162
|
+
sm: 13,
|
|
2163
|
+
md: 15,
|
|
2164
|
+
lg: 17,
|
|
2165
|
+
xl: 20,
|
|
2166
|
+
xxl: 24,
|
|
2167
|
+
title: 28
|
|
2168
|
+
};
|
|
2169
|
+
var borderRadius = {
|
|
2170
|
+
sm: 8,
|
|
2171
|
+
md: 12,
|
|
2172
|
+
lg: 16,
|
|
2173
|
+
xl: 20,
|
|
2174
|
+
full: 9999
|
|
2175
|
+
};
|
|
1716
2176
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1717
2177
|
0 && (module.exports = {
|
|
1718
2178
|
PluginCommandEmitter,
|
|
@@ -1723,23 +2183,34 @@ function disconnectAcsWs() {
|
|
|
1723
2183
|
apiFetchRaw,
|
|
1724
2184
|
blocksToText,
|
|
1725
2185
|
borderRadius,
|
|
2186
|
+
capabilityEnabled,
|
|
1726
2187
|
closeChatStream,
|
|
1727
2188
|
colors,
|
|
1728
2189
|
connectAcsWs,
|
|
1729
2190
|
connectorCall,
|
|
2191
|
+
createAuthSession,
|
|
2192
|
+
createPushRegistration,
|
|
1730
2193
|
deleteArtifact,
|
|
1731
2194
|
disconnectAcsWs,
|
|
2195
|
+
exchangeHandoff,
|
|
2196
|
+
fetchConsumerConfig,
|
|
1732
2197
|
fontSize,
|
|
1733
2198
|
getConfig,
|
|
1734
2199
|
getConsumerAuthApiUrl,
|
|
1735
2200
|
getCoreApiUrl,
|
|
1736
2201
|
getCoreWsUrl,
|
|
2202
|
+
getSession,
|
|
1737
2203
|
getTenant,
|
|
1738
2204
|
getToken,
|
|
1739
2205
|
getUser,
|
|
1740
2206
|
getVoiceApiUrl,
|
|
1741
2207
|
initAdas,
|
|
1742
2208
|
isAdasInitialized,
|
|
2209
|
+
isDevHost,
|
|
2210
|
+
joinWithApple,
|
|
2211
|
+
joinWithGoogle,
|
|
2212
|
+
linkChannelStart,
|
|
2213
|
+
linkChannelVerify,
|
|
1743
2214
|
listHomeActions,
|
|
1744
2215
|
listMadeForYou,
|
|
1745
2216
|
listMyTenants,
|
|
@@ -1747,21 +2218,36 @@ function disconnectAcsWs() {
|
|
|
1747
2218
|
mcpFetch,
|
|
1748
2219
|
mcpToolCall,
|
|
1749
2220
|
notifyAcsWsForeground,
|
|
2221
|
+
notifyAppForeground,
|
|
2222
|
+
onAppForeground,
|
|
2223
|
+
openJoinWithApple,
|
|
2224
|
+
openJoinWithGoogle,
|
|
2225
|
+
parseDeepLinkParam,
|
|
2226
|
+
parseHandoffCode,
|
|
2227
|
+
parseInviteCode,
|
|
1750
2228
|
patchArtifact,
|
|
1751
2229
|
patchHomeAction,
|
|
1752
2230
|
pluginCommandEmitter,
|
|
1753
2231
|
postCommandResult,
|
|
2232
|
+
previewInvite,
|
|
1754
2233
|
recordEvent,
|
|
1755
2234
|
refreshToken,
|
|
2235
|
+
resolveAdasPhase,
|
|
1756
2236
|
setAcsCacheStorage,
|
|
1757
2237
|
setAcsReachabilityReporter,
|
|
2238
|
+
setClientDeclarationProvider,
|
|
1758
2239
|
setHomeActionOrder,
|
|
1759
2240
|
setOrder,
|
|
2241
|
+
signInWithApple,
|
|
2242
|
+
signInWithGoogle,
|
|
1760
2243
|
spacing,
|
|
1761
2244
|
switchTenant,
|
|
1762
2245
|
unwrapMcpResult,
|
|
2246
|
+
useCapability,
|
|
1763
2247
|
useChatStore,
|
|
1764
2248
|
useHomeActionsStore,
|
|
1765
|
-
useWidgetsStore
|
|
2249
|
+
useWidgetsStore,
|
|
2250
|
+
useWorkspaceStore,
|
|
2251
|
+
userFromJwt
|
|
1766
2252
|
});
|
|
1767
2253
|
//# sourceMappingURL=index.js.map
|