@ateam-ai-mobile/core 0.1.6 → 0.1.8
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 +365 -2
- package/dist/index.d.ts +365 -2
- package/dist/index.js +1772 -1330
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1747 -1329
- 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 +26 -0
- package/src/consumerAuth.ts +216 -0
- package/src/deepLinks.ts +38 -0
- package/src/gate.ts +67 -0
- package/src/index.ts +66 -0
- package/src/lifecycle.ts +43 -0
- package/src/pushRegistration.ts +71 -0
- package/src/workspace.ts +127 -0
package/dist/index.js
CHANGED
|
@@ -28,12 +28,17 @@ __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,
|
|
@@ -47,6 +52,10 @@ __export(index_exports, {
|
|
|
47
52
|
initAdas: () => initAdas,
|
|
48
53
|
isAdasInitialized: () => isAdasInitialized,
|
|
49
54
|
isDevHost: () => isDevHost,
|
|
55
|
+
joinWithApple: () => joinWithApple,
|
|
56
|
+
joinWithGoogle: () => joinWithGoogle,
|
|
57
|
+
linkChannelStart: () => linkChannelStart,
|
|
58
|
+
linkChannelVerify: () => linkChannelVerify,
|
|
50
59
|
listHomeActions: () => listHomeActions,
|
|
51
60
|
listMadeForYou: () => listMadeForYou,
|
|
52
61
|
listMyTenants: () => listMyTenants,
|
|
@@ -54,22 +63,37 @@ __export(index_exports, {
|
|
|
54
63
|
mcpFetch: () => mcpFetch,
|
|
55
64
|
mcpToolCall: () => mcpToolCall,
|
|
56
65
|
notifyAcsWsForeground: () => notifyAcsWsForeground,
|
|
66
|
+
notifyAppForeground: () => notifyAppForeground,
|
|
67
|
+
onAppForeground: () => onAppForeground,
|
|
68
|
+
openJoinWithApple: () => openJoinWithApple,
|
|
69
|
+
openJoinWithGoogle: () => openJoinWithGoogle,
|
|
70
|
+
parseDeepLinkParam: () => parseDeepLinkParam,
|
|
71
|
+
parseHandoffCode: () => parseHandoffCode,
|
|
72
|
+
parseInviteCode: () => parseInviteCode,
|
|
57
73
|
patchArtifact: () => patchArtifact,
|
|
58
74
|
patchHomeAction: () => patchHomeAction,
|
|
59
75
|
pluginCommandEmitter: () => pluginCommandEmitter,
|
|
60
76
|
postCommandResult: () => postCommandResult,
|
|
77
|
+
previewInvite: () => previewInvite,
|
|
61
78
|
recordEvent: () => recordEvent,
|
|
62
79
|
refreshToken: () => refreshToken,
|
|
80
|
+
resolveAdasPhase: () => resolveAdasPhase,
|
|
63
81
|
setAcsCacheStorage: () => setAcsCacheStorage,
|
|
64
82
|
setAcsReachabilityReporter: () => setAcsReachabilityReporter,
|
|
83
|
+
setClientDeclarationProvider: () => setClientDeclarationProvider,
|
|
65
84
|
setHomeActionOrder: () => setHomeActionOrder,
|
|
66
85
|
setOrder: () => setOrder,
|
|
86
|
+
signInWithApple: () => signInWithApple,
|
|
87
|
+
signInWithGoogle: () => signInWithGoogle,
|
|
67
88
|
spacing: () => spacing,
|
|
68
89
|
switchTenant: () => switchTenant,
|
|
69
90
|
unwrapMcpResult: () => unwrapMcpResult,
|
|
91
|
+
useCapability: () => useCapability,
|
|
70
92
|
useChatStore: () => useChatStore,
|
|
71
93
|
useHomeActionsStore: () => useHomeActionsStore,
|
|
72
|
-
useWidgetsStore: () => useWidgetsStore
|
|
94
|
+
useWidgetsStore: () => useWidgetsStore,
|
|
95
|
+
useWorkspaceStore: () => useWorkspaceStore,
|
|
96
|
+
userFromJwt: () => userFromJwt
|
|
73
97
|
});
|
|
74
98
|
module.exports = __toCommonJS(index_exports);
|
|
75
99
|
|
|
@@ -281,1480 +305,1874 @@ function getSession() {
|
|
|
281
305
|
};
|
|
282
306
|
}
|
|
283
307
|
|
|
284
|
-
// src/
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
// dispatcher in response to focus-plugin) still pick that command up via
|
|
296
|
-
// `drainPluginCommands(pluginId)`. Self-cleans on TTL + size cap.
|
|
297
|
-
this.recent = [];
|
|
298
|
-
}
|
|
299
|
-
on(event, fn) {
|
|
300
|
-
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
301
|
-
this.listeners.get(event).add(fn);
|
|
302
|
-
return () => this.off(event, fn);
|
|
303
|
-
}
|
|
304
|
-
off(event, fn) {
|
|
305
|
-
this.listeners.get(event)?.delete(fn);
|
|
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;
|
|
306
319
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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"
|
|
322
342
|
}
|
|
323
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"}` };
|
|
324
347
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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 };
|
|
336
381
|
}
|
|
337
|
-
|
|
338
|
-
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 };
|
|
339
384
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
|
348
437
|
};
|
|
349
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
350
|
-
return headers;
|
|
351
|
-
}
|
|
352
|
-
function isTerminalStatus(status) {
|
|
353
|
-
const s = typeof status === "string" ? status.toLowerCase() : "";
|
|
354
|
-
return s === "done" || s === "error" || s === "failed" || s === "completed";
|
|
355
|
-
}
|
|
356
|
-
function normalizeJobStatus(snap) {
|
|
357
|
-
const status = typeof snap?.status === "string" ? snap.status.toLowerCase() : "";
|
|
358
|
-
if (status === "done" || status === "completed") return "done";
|
|
359
|
-
if (status === "error" || status === "failed") return "error";
|
|
360
|
-
if (status === "paused" || status === "waiting") return "paused";
|
|
361
|
-
if (status === "unknown") return "unknown";
|
|
362
|
-
return "running";
|
|
363
|
-
}
|
|
364
|
-
function extractPendingQuestion(snap) {
|
|
365
|
-
return snap?.pendingQuestion || snap?.state?.pendingQuestion || snap?.state?.waiting || null;
|
|
366
|
-
}
|
|
367
|
-
function extractActorId(snap) {
|
|
368
|
-
const actorId = snap?.actorId || snap?.actor?.actorId || snap?.actor?.id || snap?.state?.actorId || snap?.state?.actor?.actorId || snap?.state?.actor?.id;
|
|
369
|
-
return typeof actorId === "string" && actorId.length > 0 ? actorId : void 0;
|
|
370
438
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
439
|
+
|
|
440
|
+
// src/pushRegistration.ts
|
|
441
|
+
function createPushRegistration(backend) {
|
|
442
|
+
let token = null;
|
|
375
443
|
return {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
+
}
|
|
379
467
|
};
|
|
380
468
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return last?.tool || last?.phase;
|
|
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;
|
|
390
477
|
}
|
|
391
|
-
function
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
actorId: extractActorId(snap),
|
|
396
|
-
status: pq ? "paused" : normalizeJobStatus(snap),
|
|
397
|
-
subStatus: snap?.subStatus || snap?.phase || snap?.state?.visual?.phase,
|
|
398
|
-
phase: snap?.phase || snap?.state?.visual?.phase,
|
|
399
|
-
iteration: snap?.iteration || snap?.state?.plannerIter,
|
|
400
|
-
skillSlug: snap?.skillSlug || snap?.activeSkill,
|
|
401
|
-
currentTool: snap?.currentTool || snap?.lastTool || extractCurrentTool(snap?.state)
|
|
402
|
-
};
|
|
403
|
-
const liveProgress = extractLiveProgress(snap);
|
|
404
|
-
if (liveProgress) progress.liveProgress = liveProgress;
|
|
405
|
-
if (typeof snap?.humanStatus === "string" && snap.humanStatus) progress.humanStatus = snap.humanStatus;
|
|
406
|
-
if (snap?.humanLabels && typeof snap.humanLabels === "object") progress.humanLabels = snap.humanLabels;
|
|
407
|
-
if (pq) {
|
|
408
|
-
progress.pendingQuestion = {
|
|
409
|
-
inputId: pq.correlation_id || pq.correlationId || pq.inputId || "",
|
|
410
|
-
question: pq.prompt_message || pq.question || "The assistant needs your input",
|
|
411
|
-
inputType: pq.inputType || "text",
|
|
412
|
-
options: pq.options || pq.requested_fields,
|
|
413
|
-
placeholder: pq.placeholder
|
|
414
|
-
};
|
|
478
|
+
function reportAcsServerUp() {
|
|
479
|
+
try {
|
|
480
|
+
_reachability?.up();
|
|
481
|
+
} catch {
|
|
415
482
|
}
|
|
416
|
-
return progress;
|
|
417
483
|
}
|
|
418
|
-
function
|
|
419
|
-
|
|
420
|
-
|
|
484
|
+
function reportAcsServerDown() {
|
|
485
|
+
try {
|
|
486
|
+
_reachability?.down();
|
|
487
|
+
} catch {
|
|
488
|
+
}
|
|
421
489
|
}
|
|
422
|
-
var
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (!content || typeof content !== "object") return null;
|
|
426
|
-
const cols = Array.isArray(content.columns) ? content.columns : null;
|
|
427
|
-
const rows = Array.isArray(content.rows) ? content.rows : null;
|
|
428
|
-
if (!cols || !rows) return null;
|
|
429
|
-
const payload = encodeURIComponent(JSON.stringify({ columns: cols, rows }));
|
|
430
|
-
return `${TABLE_BLOCK_OPEN}${payload}${TABLE_BLOCK_CLOSE}`;
|
|
490
|
+
var _kv = null;
|
|
491
|
+
function setAcsCacheStorage(kv) {
|
|
492
|
+
_kv = kv;
|
|
431
493
|
}
|
|
432
|
-
function
|
|
433
|
-
|
|
434
|
-
const hasTextBlock = blocks.some(
|
|
435
|
-
(b) => b.kind === "text" || b.kind === "markdown" || b.kind === "description"
|
|
436
|
-
);
|
|
437
|
-
return blocks.map((b) => {
|
|
438
|
-
if ((b.kind === "json" || b.kind === "code") && hasTextBlock) return "";
|
|
439
|
-
if (b.kind === "text" || b.kind === "markdown" || b.kind === "description") {
|
|
440
|
-
return typeof b.content === "string" ? b.content : "";
|
|
441
|
-
}
|
|
442
|
-
if (b.kind === "code") {
|
|
443
|
-
const code = typeof b.content === "object" ? b.content.content : b.content;
|
|
444
|
-
return `\`\`\`
|
|
445
|
-
${code}
|
|
446
|
-
\`\`\``;
|
|
447
|
-
}
|
|
448
|
-
if (b.kind === "table" && b.content && typeof b.content === "object") {
|
|
449
|
-
const md = renderTableShape(b.content);
|
|
450
|
-
if (md) return md;
|
|
451
|
-
}
|
|
452
|
-
if (b.kind === "json") {
|
|
453
|
-
if (b.content && typeof b.content === "object" && "pendingQuestion" in b.content) return "";
|
|
454
|
-
const md = renderTableShape(b.content);
|
|
455
|
-
if (md) return md;
|
|
456
|
-
return "```json\n" + JSON.stringify(b.content, null, 2) + "\n```";
|
|
457
|
-
}
|
|
458
|
-
if (b.kind === "html") {
|
|
459
|
-
return typeof b.content === "string" ? b.content : "";
|
|
460
|
-
}
|
|
461
|
-
return typeof b.content === "string" ? b.content : JSON.stringify(b.content);
|
|
462
|
-
}).filter(Boolean).join("\n\n");
|
|
494
|
+
function getAcsCacheStorage() {
|
|
495
|
+
return _kv;
|
|
463
496
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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;
|
|
475
515
|
}
|
|
476
|
-
return atts.length > 0 ? atts : void 0;
|
|
477
516
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
+
}
|
|
533
|
+
}
|
|
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 [];
|
|
540
|
+
}
|
|
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 };
|
|
557
|
+
}
|
|
558
|
+
if (res.status === 404) {
|
|
559
|
+
return { ok: true, notFound: true, status: 404 };
|
|
560
|
+
}
|
|
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
|
+
};
|
|
580
|
+
}
|
|
581
|
+
if (res.status === 404) {
|
|
582
|
+
return { ok: true, notFound: true, status: 404 };
|
|
583
|
+
}
|
|
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
|
+
}
|
|
592
|
+
}
|
|
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 [];
|
|
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 [];
|
|
627
|
+
}
|
|
628
|
+
}
|
|
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,
|
|
661
|
+
loading: false,
|
|
481
662
|
error: null,
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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 });
|
|
489
670
|
try {
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
});
|
|
495
|
-
if (!res.ok) {
|
|
496
|
-
console.warn("[adas-chat] Failed to load conversation:", res.status);
|
|
497
|
-
set({ conversationLoaded: true });
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
const data = await res.json();
|
|
501
|
-
if (!data.ok || !data.items) {
|
|
502
|
-
set({ conversationLoaded: true });
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const items = [...data.items].reverse();
|
|
506
|
-
const persistedMessages = [];
|
|
507
|
-
const seenJobIds = /* @__PURE__ */ new Set();
|
|
508
|
-
for (const turn of items) {
|
|
509
|
-
if (turn.jobId && seenJobIds.has(turn.jobId)) continue;
|
|
510
|
-
if (turn.jobId) seenJobIds.add(turn.jobId);
|
|
511
|
-
const userText = blocksToText(turn.user?.blocks);
|
|
512
|
-
let userAtts = blocksToAttachments(turn.user?.blocks);
|
|
513
|
-
if (!userAtts && turn.jobId) {
|
|
514
|
-
userAtts = attachmentCache?.get(turn.jobId);
|
|
515
|
-
}
|
|
516
|
-
const turnChainId = typeof turn.chainId === "string" ? turn.chainId : void 0;
|
|
517
|
-
if (userText || userAtts) {
|
|
518
|
-
persistedMessages.push({
|
|
519
|
-
id: `user-${turn.jobId}`,
|
|
520
|
-
role: "user",
|
|
521
|
-
content: userText || "",
|
|
522
|
-
timestamp: new Date(turn.startedAt || Date.now()),
|
|
523
|
-
skillSlug: turn.skillSlug,
|
|
524
|
-
...userAtts ? { attachments: userAtts } : {},
|
|
525
|
-
...turnChainId ? { chainId: turnChainId } : {}
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
const assistantText = blocksToText(turn.assistant?.blocks);
|
|
529
|
-
const assistantAtts = blocksToAttachments(turn.assistant?.blocks);
|
|
530
|
-
const assistantActions = Array.isArray(turn.assistant?.actions) ? turn.assistant.actions : void 0;
|
|
531
|
-
const turnKind = typeof turn.kind === "string" ? turn.kind : void 0;
|
|
532
|
-
if (assistantText || assistantAtts || assistantActions?.length) {
|
|
533
|
-
persistedMessages.push({
|
|
534
|
-
id: `bot-${turn.jobId}`,
|
|
535
|
-
role: "assistant",
|
|
536
|
-
content: assistantText || "",
|
|
537
|
-
timestamp: new Date(turn.finishedAt || turn.startedAt || Date.now()),
|
|
538
|
-
skillSlug: turn.skillSlug,
|
|
539
|
-
...assistantAtts ? { attachments: assistantAtts } : {},
|
|
540
|
-
...assistantActions?.length ? { actions: assistantActions } : {},
|
|
541
|
-
...turnChainId ? { chainId: turnChainId } : {},
|
|
542
|
-
...turnKind ? { kind: turnKind } : {}
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
const current = get().messages;
|
|
547
|
-
const keptPlaceholders = [];
|
|
548
|
-
for (const m of current) {
|
|
549
|
-
if (!m.id.startsWith("job-") || !m.jobProgress || !m.chainId) continue;
|
|
550
|
-
const matchIdx = persistedMessages.findIndex(
|
|
551
|
-
(pm) => pm.role === "assistant" && pm.chainId === m.chainId
|
|
552
|
-
);
|
|
553
|
-
if (matchIdx >= 0) {
|
|
554
|
-
const st = m.jobProgress.status;
|
|
555
|
-
if (st === "running" || st === "paused") {
|
|
556
|
-
persistedMessages[matchIdx] = { ...persistedMessages[matchIdx], jobProgress: m.jobProgress };
|
|
557
|
-
}
|
|
558
|
-
} else {
|
|
559
|
-
keptPlaceholders.push(m);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
const isLocalUserId = (id) => /^user-\d+$/.test(id);
|
|
563
|
-
const keptUserBubbles = current.filter((m) => {
|
|
564
|
-
if (m.role !== "user" || !isLocalUserId(m.id)) return false;
|
|
565
|
-
if (!m.chainId) return true;
|
|
566
|
-
return !persistedMessages.some(
|
|
567
|
-
(pm) => pm.role === "user" && pm.chainId === m.chainId
|
|
568
|
-
);
|
|
569
|
-
});
|
|
570
|
-
const finalMessages = [...persistedMessages, ...keptUserBubbles, ...keptPlaceholders];
|
|
571
|
-
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
572
|
-
let firstRunning = keptPlaceholders.find((m) => m.jobProgress?.status === "running");
|
|
573
|
-
try {
|
|
574
|
-
let activeChains = [];
|
|
575
|
-
const chainsRes = await fetch(`${getCoreApiUrl()}/api/chains/active`, {
|
|
576
|
-
headers: coreHeaders()
|
|
577
|
-
});
|
|
578
|
-
if (chainsRes.ok) {
|
|
579
|
-
const body = await chainsRes.json();
|
|
580
|
-
activeChains = Array.isArray(body?.chains) ? body.chains : [];
|
|
581
|
-
} else {
|
|
582
|
-
const legacy = await fetch(`${getCoreApiUrl()}/api/chain/current`, {
|
|
583
|
-
headers: coreHeaders()
|
|
584
|
-
});
|
|
585
|
-
if (legacy.ok) {
|
|
586
|
-
const lb = await legacy.json();
|
|
587
|
-
if (lb?.data?.id) activeChains = [lb.data];
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
for (const slim of activeChains) {
|
|
591
|
-
if (!slim?.id) continue;
|
|
592
|
-
const chainId = String(slim.id);
|
|
593
|
-
const alreadyTracked = finalMessages.some(
|
|
594
|
-
(m) => m.role === "assistant" && m.jobProgress?.jobId === chainId
|
|
595
|
-
);
|
|
596
|
-
if (alreadyTracked) continue;
|
|
597
|
-
const progress = snapshotToProgress(
|
|
598
|
-
{ ...slim, status: slim.chainStatus || slim.status },
|
|
599
|
-
chainId
|
|
600
|
-
);
|
|
601
|
-
const chainCreatedAt = typeof slim.createdAt === "number" ? slim.createdAt : typeof slim.lastUpdate === "number" ? slim.lastUpdate : Date.now();
|
|
602
|
-
const chainLastUpdate = typeof slim.lastUpdate === "number" ? slim.lastUpdate : chainCreatedAt;
|
|
603
|
-
const jobProgress = {
|
|
604
|
-
jobId: chainId,
|
|
605
|
-
actorId: extractActorId(slim),
|
|
606
|
-
status: "running",
|
|
607
|
-
chainStartedAt: chainCreatedAt,
|
|
608
|
-
...progress
|
|
609
|
-
};
|
|
610
|
-
const goal = typeof slim.goal === "string" ? slim.goal : "";
|
|
611
|
-
const userExists = finalMessages.some(
|
|
612
|
-
(m) => m.role === "user" && (m.chainId === chainId || goal && m.content === goal)
|
|
613
|
-
);
|
|
614
|
-
if (goal && !userExists) {
|
|
615
|
-
finalMessages.push({
|
|
616
|
-
id: `user-${chainId}`,
|
|
617
|
-
role: "user",
|
|
618
|
-
content: goal,
|
|
619
|
-
timestamp: new Date(chainCreatedAt),
|
|
620
|
-
chainId
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
let matchIdx = -1;
|
|
624
|
-
for (let i = finalMessages.length - 1; i >= 0; i--) {
|
|
625
|
-
const m = finalMessages[i];
|
|
626
|
-
if (m.role === "assistant" && m.chainId === chainId) {
|
|
627
|
-
matchIdx = i;
|
|
628
|
-
break;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
if (matchIdx >= 0) {
|
|
632
|
-
finalMessages[matchIdx] = { ...finalMessages[matchIdx], jobProgress };
|
|
633
|
-
if (jobProgress.status === "running" && !firstRunning) firstRunning = finalMessages[matchIdx];
|
|
634
|
-
} else {
|
|
635
|
-
const reattachBubble = {
|
|
636
|
-
id: `job-${chainId}`,
|
|
637
|
-
role: "assistant",
|
|
638
|
-
content: "Working on it...",
|
|
639
|
-
timestamp: new Date(chainLastUpdate),
|
|
640
|
-
jobProgress,
|
|
641
|
-
chainId
|
|
642
|
-
};
|
|
643
|
-
finalMessages.push(reattachBubble);
|
|
644
|
-
if (jobProgress.status === "running" && !firstRunning) firstRunning = reattachBubble;
|
|
645
|
-
}
|
|
646
|
-
streamJob(chainId, getTenant(), getToken());
|
|
647
|
-
}
|
|
648
|
-
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
649
|
-
} catch {
|
|
650
|
-
}
|
|
651
|
-
set({
|
|
652
|
-
messages: finalMessages,
|
|
653
|
-
conversationLoaded: true,
|
|
654
|
-
error: null,
|
|
655
|
-
activeJobId: firstRunning?.jobProgress?.jobId || null,
|
|
656
|
-
loading: !!firstRunning
|
|
657
|
-
});
|
|
658
|
-
} catch (err) {
|
|
659
|
-
console.warn("[adas-chat] Error loading conversation:", err.message);
|
|
660
|
-
set({ conversationLoaded: true });
|
|
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" });
|
|
661
675
|
}
|
|
662
676
|
},
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
};
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
triggerContext: {
|
|
690
|
-
channel: "mobile",
|
|
691
|
-
replyContext: { chatId: actor?.actorId || "unknown" }
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
if (skillSlug) body.skillSlug = skillSlug;
|
|
695
|
-
if (attachments?.length) body.attachments = attachments;
|
|
696
|
-
const res = await fetch(`${getCoreApiUrl()}/api/chat`, {
|
|
697
|
-
method: "POST",
|
|
698
|
-
headers: coreHeaders(),
|
|
699
|
-
body: JSON.stringify(body)
|
|
700
|
-
});
|
|
701
|
-
if (!res.ok) {
|
|
702
|
-
let errMsg = `Server error (${res.status})`;
|
|
703
|
-
try {
|
|
704
|
-
const text2 = await res.text();
|
|
705
|
-
if (text2.startsWith("<") || text2.startsWith("<!")) {
|
|
706
|
-
errMsg = res.status === 502 ? "Server is temporarily unavailable. Please try again." : `Server error (${res.status})`;
|
|
707
|
-
} else {
|
|
708
|
-
const parsed = JSON.parse(text2);
|
|
709
|
-
errMsg = parsed.error || errMsg;
|
|
710
|
-
}
|
|
711
|
-
} catch {
|
|
712
|
-
}
|
|
713
|
-
throw new Error(errMsg);
|
|
714
|
-
}
|
|
715
|
-
const data = await res.json();
|
|
716
|
-
if (!data.ok) {
|
|
717
|
-
throw new Error(data.error || "Failed to send message");
|
|
718
|
-
}
|
|
719
|
-
if (data.mode === "chain_addition" || data.mode === "chain_cancel") {
|
|
720
|
-
const ack = data.mode === "chain_addition" ? data.ack || "Got it \u2014 added to the running task." : "Stopping the task\u2026";
|
|
721
|
-
set((s) => ({
|
|
722
|
-
messages: s.messages.map((m) => m.id === userMsg.id && data.chainId ? { ...m, chainId: data.chainId } : m).concat({
|
|
723
|
-
id: `ack-${Date.now()}`,
|
|
724
|
-
role: "assistant",
|
|
725
|
-
content: ack,
|
|
726
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
727
|
-
...data.chainId ? { chainId: data.chainId } : {}
|
|
728
|
-
})
|
|
729
|
-
}));
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
const jobId = data.id || data.jobId;
|
|
733
|
-
if (!jobId) {
|
|
734
|
-
throw new Error("No job ID returned");
|
|
735
|
-
}
|
|
736
|
-
if (attachments?.length) {
|
|
737
|
-
await getConfig().attachmentCache?.save(jobId, attachments);
|
|
738
|
-
}
|
|
739
|
-
const progressMsg = {
|
|
740
|
-
id: `job-${jobId}`,
|
|
741
|
-
role: "assistant",
|
|
742
|
-
content: "Working on it...",
|
|
743
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
744
|
-
jobProgress: {
|
|
745
|
-
jobId,
|
|
746
|
-
actorId: actor?.actorId,
|
|
747
|
-
status: "running",
|
|
748
|
-
subStatus: "Starting...",
|
|
749
|
-
chainStartedAt: Date.now()
|
|
750
|
-
},
|
|
751
|
-
chainId: jobId
|
|
752
|
-
};
|
|
753
|
-
set((s) => ({
|
|
754
|
-
messages: s.messages.map(
|
|
755
|
-
(m) => m.id === userMsg.id ? { ...m, chainId: jobId } : m
|
|
756
|
-
).concat(progressMsg),
|
|
757
|
-
activeJobId: jobId
|
|
758
|
-
}));
|
|
759
|
-
streamJob(jobId, getTenant(), getToken());
|
|
760
|
-
} catch (err) {
|
|
761
|
-
set((s) => ({
|
|
762
|
-
messages: [...s.messages, {
|
|
763
|
-
id: `error-${Date.now()}`,
|
|
764
|
-
role: "system",
|
|
765
|
-
content: `Error: ${err.message}`,
|
|
766
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
767
|
-
}],
|
|
768
|
-
loading: false
|
|
769
|
-
}));
|
|
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
|
+
});
|
|
701
|
+
}
|
|
702
|
+
return { ok: true };
|
|
770
703
|
}
|
|
704
|
+
set({ tiles: before });
|
|
705
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
771
706
|
},
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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
|
+
});
|
|
785
720
|
}
|
|
786
|
-
|
|
787
|
-
console.warn("[adas-chat] Error responding to question:", err.message);
|
|
721
|
+
return { ok: true };
|
|
788
722
|
}
|
|
723
|
+
set({ tiles: before });
|
|
724
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
789
725
|
},
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
return m;
|
|
812
|
-
}),
|
|
813
|
-
loading: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? false : s.loading,
|
|
814
|
-
activeJobId: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? null : s.activeJobId
|
|
815
|
-
}));
|
|
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
|
+
});
|
|
740
|
+
}
|
|
741
|
+
return { ok: true };
|
|
742
|
+
}
|
|
743
|
+
set({ tiles: before });
|
|
744
|
+
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
816
745
|
},
|
|
817
|
-
|
|
746
|
+
reorder: async (pinnedIds) => {
|
|
747
|
+
const before = get().tiles;
|
|
748
|
+
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
818
749
|
set({
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
conversationLoaded: false
|
|
750
|
+
tiles: before.map((t) => {
|
|
751
|
+
const o = idToOrder.get(t.id);
|
|
752
|
+
return o == null ? t : { ...t, pinOrder: o };
|
|
753
|
+
})
|
|
824
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}` };
|
|
825
759
|
},
|
|
826
|
-
|
|
760
|
+
applyServerUpdate: (artifact) => {
|
|
827
761
|
set({
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
762
|
+
tiles: get().tiles.map((t) => t.id === artifact.id ? { ...t, ...artifact } : t)
|
|
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");
|
|
779
|
+
}
|
|
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
|
+
});
|
|
788
|
+
}
|
|
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;
|
|
801
|
+
try {
|
|
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 });
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
} catch {
|
|
810
|
+
}
|
|
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(() => {
|
|
833
829
|
});
|
|
830
|
+
},
|
|
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
|
+
}));
|
|
896
|
+
|
|
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;
|
|
913
|
+
}
|
|
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
|
+
}
|
|
834
921
|
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
922
|
+
}
|
|
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;
|
|
847
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;
|
|
848
973
|
}
|
|
849
974
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
const
|
|
856
|
-
const ws = new WebSocket(wsUrl);
|
|
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);
|
|
857
981
|
_ws = ws;
|
|
858
|
-
|
|
982
|
+
const isCurrent = () => ws === _ws;
|
|
859
983
|
ws.onopen = () => {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
}
|
|
984
|
+
if (!isCurrent()) return;
|
|
985
|
+
_retryAttempt = 0;
|
|
986
|
+
send({ type: "subscribe", topic: TOPIC });
|
|
987
|
+
send({ type: "subscribe", topic: TOPIC_HOME });
|
|
864
988
|
};
|
|
865
|
-
ws.onmessage = (
|
|
989
|
+
ws.onmessage = (e) => {
|
|
990
|
+
if (!isCurrent()) return;
|
|
866
991
|
try {
|
|
867
|
-
const msg = JSON.parse(typeof
|
|
868
|
-
|
|
869
|
-
const { updateJobProgress } = useChatStore.getState();
|
|
870
|
-
const jobId = data && (data.id || data.chainId) || _currentJobId;
|
|
871
|
-
if (event === "plugin_command") {
|
|
872
|
-
dispatchPluginCommands([data]);
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
875
|
-
if (event === "plugin-command-ack" || event === "pong") return;
|
|
876
|
-
if (!jobId) return;
|
|
877
|
-
switch (event) {
|
|
878
|
-
case "init":
|
|
879
|
-
case "job":
|
|
880
|
-
case "update": {
|
|
881
|
-
const snap = data;
|
|
882
|
-
const progress = snapshotToProgress(snap, jobId);
|
|
883
|
-
if (isTerminalSnapshot(snap)) {
|
|
884
|
-
if (snap.status === "done" && (snap.isHandoff || snap.state?.handoffTransfer)) {
|
|
885
|
-
updateJobProgress(jobId, { status: "running" });
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
_currentJobId = null;
|
|
889
|
-
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
890
|
-
updateJobProgress(jobId, progress);
|
|
891
|
-
} else {
|
|
892
|
-
fetchTurnResult(jobId, snap);
|
|
893
|
-
}
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
updateJobProgress(jobId, progress);
|
|
897
|
-
break;
|
|
898
|
-
}
|
|
899
|
-
case "chain_final":
|
|
900
|
-
_currentJobId = null;
|
|
901
|
-
fetchTurnResult(jobId, data);
|
|
902
|
-
break;
|
|
903
|
-
case "final":
|
|
904
|
-
_currentJobId = null;
|
|
905
|
-
fetchTurnResult(jobId, data);
|
|
906
|
-
break;
|
|
907
|
-
}
|
|
992
|
+
const msg = JSON.parse(typeof e.data === "string" ? e.data : "");
|
|
993
|
+
handleEvent(msg);
|
|
908
994
|
} catch (err) {
|
|
909
|
-
console.warn("[
|
|
995
|
+
console.warn("[acsWs] parse failed", err);
|
|
910
996
|
}
|
|
911
997
|
};
|
|
912
|
-
ws.onerror = () => {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
if (_currentJobId) {
|
|
916
|
-
const jid = _currentJobId;
|
|
917
|
-
_currentJobId = null;
|
|
918
|
-
pollJob(jid, token);
|
|
919
|
-
}
|
|
998
|
+
ws.onerror = (e) => {
|
|
999
|
+
if (!isCurrent()) return;
|
|
1000
|
+
console.warn("[acsWs] error", e?.message || e);
|
|
920
1001
|
};
|
|
921
|
-
ws.onclose = () => {
|
|
1002
|
+
ws.onclose = (ev) => {
|
|
1003
|
+
if (!isCurrent()) return;
|
|
922
1004
|
_ws = null;
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
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;
|
|
928
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);
|
|
929
1020
|
};
|
|
930
|
-
return ws;
|
|
931
1021
|
}
|
|
932
|
-
function
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
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();
|
|
937
1031
|
}
|
|
938
1032
|
}
|
|
939
|
-
function
|
|
940
|
-
|
|
1033
|
+
function connectAcsWs(token, tenant) {
|
|
1034
|
+
_terminated = false;
|
|
1035
|
+
if (_ws && _wantOpen && _token === token && _tenant === tenant) return;
|
|
941
1036
|
if (_ws) {
|
|
942
1037
|
try {
|
|
943
1038
|
_ws.close();
|
|
944
1039
|
} catch {
|
|
945
1040
|
}
|
|
1041
|
+
_ws = null;
|
|
946
1042
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
if (_processedPluginCmds.size > 200) _processedPluginCmds.clear();
|
|
952
|
-
const { updateJobProgress } = useChatStore.getState();
|
|
953
|
-
const status = normalizeJobStatus(jobSnap) === "error" ? "error" : "done";
|
|
954
|
-
if (jobSnap.isHandoff || jobSnap.state?.handoffTransfer || jobSnap.result?.contentType === "handoff") {
|
|
955
|
-
try {
|
|
956
|
-
for (let i = 0; i < 40; i++) {
|
|
957
|
-
const statusRes = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {
|
|
958
|
-
headers: coreHeaders()
|
|
959
|
-
});
|
|
960
|
-
if (statusRes.ok) {
|
|
961
|
-
const statusData = await statusRes.json();
|
|
962
|
-
if (statusData.done && !statusData.isHandoff) {
|
|
963
|
-
break;
|
|
964
|
-
}
|
|
965
|
-
updateJobProgress(jobId, { status: "running" });
|
|
966
|
-
}
|
|
967
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
968
|
-
}
|
|
969
|
-
} catch {
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
for (let attempt = 0; attempt < 5; attempt++) {
|
|
973
|
-
try {
|
|
974
|
-
const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=1`, {
|
|
975
|
-
headers: coreHeaders()
|
|
976
|
-
});
|
|
977
|
-
if (res.ok) {
|
|
978
|
-
const data = await res.json();
|
|
979
|
-
const items = Array.isArray(data.items) ? data.items : [];
|
|
980
|
-
const turn = items[0];
|
|
981
|
-
if (turn?.assistant || turn?.turn?.assistant) {
|
|
982
|
-
const assistant = turn.assistant || turn.turn?.assistant;
|
|
983
|
-
const text = blocksToText(assistant.blocks);
|
|
984
|
-
const actions = Array.isArray(assistant.actions) && assistant.actions.length ? assistant.actions : void 0;
|
|
985
|
-
if (text) {
|
|
986
|
-
updateJobProgress(jobId, {
|
|
987
|
-
status,
|
|
988
|
-
resultMessage: text,
|
|
989
|
-
...actions ? { resultActions: actions } : {}
|
|
990
|
-
});
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
} catch {
|
|
996
|
-
}
|
|
997
|
-
await new Promise((r) => setTimeout(r, 800));
|
|
998
|
-
}
|
|
999
|
-
const fallback = jobSnap.result?.message || jobSnap.state?.finalReply || (status === "error" ? "Something went wrong. Please try again." : "Done.");
|
|
1000
|
-
updateJobProgress(jobId, { status, resultMessage: fallback });
|
|
1043
|
+
_token = token;
|
|
1044
|
+
_tenant = tenant;
|
|
1045
|
+
_wantOpen = true;
|
|
1046
|
+
openSocket();
|
|
1001
1047
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
const snap = await res.json();
|
|
1016
|
-
const progress = snapshotToProgress(snap, jobId);
|
|
1017
|
-
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
1018
|
-
updateJobProgress(jobId, progress);
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
updateJobProgress(jobId, progress);
|
|
1022
|
-
if (isTerminalSnapshot(snap)) {
|
|
1023
|
-
fetchTurnResult(jobId, snap);
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
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();
|
|
1026
1060
|
} catch {
|
|
1027
1061
|
}
|
|
1028
|
-
|
|
1062
|
+
_ws = null;
|
|
1029
1063
|
}
|
|
1030
|
-
updateJobProgress(jobId, {
|
|
1031
|
-
status: "error",
|
|
1032
|
-
resultMessage: "Job timed out. Please try again."
|
|
1033
|
-
});
|
|
1034
1064
|
}
|
|
1035
1065
|
|
|
1036
|
-
// src/
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
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() {
|
|
1040
1075
|
try {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
}
|
|
1051
|
-
} catch {
|
|
1052
|
-
return [];
|
|
1076
|
+
notifyAcsWsForeground();
|
|
1077
|
+
} catch (e) {
|
|
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
|
+
}
|
|
1053
1086
|
}
|
|
1054
1087
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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}`, {
|
|
1059
1115
|
method: "POST",
|
|
1060
1116
|
headers: {
|
|
1061
1117
|
"Content-Type": "application/json",
|
|
1062
|
-
Authorization: `Bearer ${
|
|
1118
|
+
...jwt ? { Authorization: `Bearer ${jwt}` } : {}
|
|
1063
1119
|
},
|
|
1064
|
-
body: JSON.stringify(
|
|
1120
|
+
body: JSON.stringify(body)
|
|
1065
1121
|
});
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1122
|
+
const text = await res.text();
|
|
1123
|
+
let parsed = null;
|
|
1124
|
+
try {
|
|
1125
|
+
parsed = JSON.parse(text);
|
|
1126
|
+
} catch {
|
|
1069
1127
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
actor: data.actor
|
|
1074
|
-
};
|
|
1075
|
-
const onSession = getConfig().onSession;
|
|
1076
|
-
if (!onSession) {
|
|
1077
|
-
throw new Error("[adas] switchTenant: config.onSession is required to persist the new session");
|
|
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`));
|
|
1078
1131
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1132
|
+
return parsed;
|
|
1133
|
+
}
|
|
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;
|
|
1139
|
+
try {
|
|
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"));
|
|
1146
|
+
}
|
|
1147
|
+
return data;
|
|
1148
|
+
}
|
|
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;
|
|
1169
|
+
try {
|
|
1170
|
+
data = JSON.parse(text);
|
|
1171
|
+
} catch {
|
|
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;
|
|
1179
|
+
}
|
|
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);
|
|
1083
1197
|
}
|
|
1084
1198
|
|
|
1085
|
-
// src/
|
|
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
|
-
|
|
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;
|
|
1205
|
+
try {
|
|
1206
|
+
const v = decodeURIComponent(m[1]);
|
|
1207
|
+
return v.length > 0 ? v : null;
|
|
1208
|
+
} catch {
|
|
1209
|
+
return m[1].length > 0 ? m[1] : null;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
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 });
|
|
1246
|
+
}
|
|
1113
1247
|
},
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1248
|
+
clear: () => {
|
|
1249
|
+
_gen++;
|
|
1250
|
+
set({ snapshot: null, loaded: false });
|
|
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
|
+
});
|
|
1263
|
+
}
|
|
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;
|
|
1139
1320
|
}
|
|
1140
1321
|
};
|
|
1141
|
-
var
|
|
1142
|
-
paper: "#FAF6EE",
|
|
1143
|
-
paperSoft: "#F2EADC",
|
|
1144
|
-
paperDeep: "#EBE0CC",
|
|
1145
|
-
ink: "#1a1410",
|
|
1146
|
-
inkSoft: "#5a4d3f",
|
|
1147
|
-
inkMuted: "#9a8870",
|
|
1148
|
-
border: "rgba(26, 20, 16, 0.08)",
|
|
1149
|
-
borderStrong: "rgba(26, 20, 16, 0.16)",
|
|
1150
|
-
orange: "#FF7A28",
|
|
1151
|
-
orangeDeep: "#B8410A",
|
|
1152
|
-
orangeSoft: "rgba(255, 122, 40, 0.14)",
|
|
1153
|
-
userBubble: "#1a1410",
|
|
1154
|
-
userBubbleText: "#FAF6EE",
|
|
1155
|
-
botBubble: "#F2EADC",
|
|
1156
|
-
botBubbleBorder: "rgba(26, 20, 16, 0.06)",
|
|
1157
|
-
botBubbleText: "#1a1410"
|
|
1158
|
-
};
|
|
1159
|
-
var spacing = {
|
|
1160
|
-
xs: 4,
|
|
1161
|
-
sm: 8,
|
|
1162
|
-
md: 12,
|
|
1163
|
-
lg: 16,
|
|
1164
|
-
xl: 20,
|
|
1165
|
-
xxl: 24,
|
|
1166
|
-
xxxl: 32
|
|
1167
|
-
};
|
|
1168
|
-
var fontSize = {
|
|
1169
|
-
xs: 11,
|
|
1170
|
-
sm: 13,
|
|
1171
|
-
md: 15,
|
|
1172
|
-
lg: 17,
|
|
1173
|
-
xl: 20,
|
|
1174
|
-
xxl: 24,
|
|
1175
|
-
title: 28
|
|
1176
|
-
};
|
|
1177
|
-
var borderRadius = {
|
|
1178
|
-
sm: 8,
|
|
1179
|
-
md: 12,
|
|
1180
|
-
lg: 16,
|
|
1181
|
-
xl: 20,
|
|
1182
|
-
full: 9999
|
|
1183
|
-
};
|
|
1322
|
+
var pluginCommandEmitter = new PluginCommandEmitter();
|
|
1184
1323
|
|
|
1185
|
-
// src/
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
} catch {
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
function reportAcsServerDown() {
|
|
1197
|
-
try {
|
|
1198
|
-
_reachability?.down();
|
|
1199
|
-
} catch {
|
|
1200
|
-
}
|
|
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;
|
|
1201
1332
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1333
|
+
function isTerminalStatus(status) {
|
|
1334
|
+
const s = typeof status === "string" ? status.toLowerCase() : "";
|
|
1335
|
+
return s === "done" || s === "error" || s === "failed" || s === "completed";
|
|
1205
1336
|
}
|
|
1206
|
-
function
|
|
1207
|
-
|
|
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";
|
|
1208
1344
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
async function authedFetch(path, init) {
|
|
1212
|
-
const token = getToken();
|
|
1213
|
-
try {
|
|
1214
|
-
const res = await fetch(`${getCoreApiUrl()}${path}`, {
|
|
1215
|
-
...init,
|
|
1216
|
-
headers: {
|
|
1217
|
-
"Content-Type": "application/json",
|
|
1218
|
-
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
1219
|
-
...init?.headers || {}
|
|
1220
|
-
}
|
|
1221
|
-
});
|
|
1222
|
-
reportAcsServerUp();
|
|
1223
|
-
return res;
|
|
1224
|
-
} catch (e) {
|
|
1225
|
-
reportAcsServerDown();
|
|
1226
|
-
throw e;
|
|
1227
|
-
}
|
|
1345
|
+
function extractPendingQuestion(snap) {
|
|
1346
|
+
return snap?.pendingQuestion || snap?.state?.pendingQuestion || snap?.state?.waiting || null;
|
|
1228
1347
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
method: "POST",
|
|
1233
|
-
body: JSON.stringify({ surface, ...context ? { context } : {} })
|
|
1234
|
-
});
|
|
1235
|
-
if (!res.ok) {
|
|
1236
|
-
console.warn(`[acs] /surface ${surface} returned ${res.status}`);
|
|
1237
|
-
return [];
|
|
1238
|
-
}
|
|
1239
|
-
const data = await res.json();
|
|
1240
|
-
return Array.isArray(data?.items) ? data.items : [];
|
|
1241
|
-
} catch (e) {
|
|
1242
|
-
console.warn("[acs] /surface failed:", e?.message || e);
|
|
1243
|
-
return [];
|
|
1244
|
-
}
|
|
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;
|
|
1245
1351
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
} catch (e) {
|
|
1256
|
-
console.warn("[acs] /made-for-you failed:", e?.message || e);
|
|
1257
|
-
return [];
|
|
1258
|
-
}
|
|
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
|
+
};
|
|
1259
1361
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
}
|
|
1270
|
-
if (res.status === 404) {
|
|
1271
|
-
return { ok: true, notFound: true, status: 404 };
|
|
1272
|
-
}
|
|
1273
|
-
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
1274
|
-
} catch (e) {
|
|
1275
|
-
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
1276
|
-
}
|
|
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;
|
|
1277
1371
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
)
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
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
|
|
1300
1395
|
};
|
|
1301
|
-
} catch (e) {
|
|
1302
|
-
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
1303
1396
|
}
|
|
1397
|
+
return progress;
|
|
1304
1398
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
method: "PUT",
|
|
1309
|
-
body: JSON.stringify({ pinned })
|
|
1310
|
-
});
|
|
1311
|
-
if (res.ok) return { ok: true, status: res.status };
|
|
1312
|
-
const data = await res.json().catch(() => ({}));
|
|
1313
|
-
return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };
|
|
1314
|
-
} catch (e) {
|
|
1315
|
-
return { ok: false, status: 0, error: e?.message || "network error" };
|
|
1316
|
-
}
|
|
1399
|
+
function isTerminalSnapshot(snap) {
|
|
1400
|
+
if (extractPendingQuestion(snap)) return false;
|
|
1401
|
+
return snap?.done === true || isTerminalStatus(snap?.status);
|
|
1317
1402
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
}
|
|
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}`;
|
|
1326
1412
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
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 : "";
|
|
1333
1422
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
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");
|
|
1354
1444
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
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
|
+
}
|
|
1366
1456
|
}
|
|
1457
|
+
return atts.length > 0 ? atts : void 0;
|
|
1367
1458
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
var import_zustand2 = require("zustand");
|
|
1371
|
-
var useWidgetsStore = (0, import_zustand2.create)((set, get) => ({
|
|
1372
|
-
tiles: [],
|
|
1373
|
-
hydrated: false,
|
|
1459
|
+
var useChatStore = (0, import_zustand4.create)((set, get) => ({
|
|
1460
|
+
messages: [],
|
|
1374
1461
|
loading: false,
|
|
1375
1462
|
error: null,
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
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 () => {
|
|
1383
1470
|
try {
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
set({ tiles: before.filter((t) => t.id !== artifactId) });
|
|
1394
|
-
const result = await deleteArtifact(artifactId, reason);
|
|
1395
|
-
if (result.ok) {
|
|
1396
|
-
return { ok: true };
|
|
1397
|
-
}
|
|
1398
|
-
set({ tiles: before });
|
|
1399
|
-
return { ok: false, error: result.error || `HTTP ${result.status}` };
|
|
1400
|
-
},
|
|
1401
|
-
hide: async (artifactId) => {
|
|
1402
|
-
const before = get().tiles;
|
|
1403
|
-
set({
|
|
1404
|
-
tiles: before.map(
|
|
1405
|
-
(t) => t.id === artifactId ? { ...t, visibility: "hidden" } : t
|
|
1406
|
-
)
|
|
1407
|
-
});
|
|
1408
|
-
const r = await patchArtifact(artifactId, { visibility: "hidden" }, "user_long_press_hide");
|
|
1409
|
-
if (r.ok) {
|
|
1410
|
-
if (r.artifact) {
|
|
1411
|
-
set({
|
|
1412
|
-
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
1413
|
-
});
|
|
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;
|
|
1414
1480
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
},
|
|
1420
|
-
unhide: async (artifactId) => {
|
|
1421
|
-
const before = get().tiles;
|
|
1422
|
-
set({
|
|
1423
|
-
tiles: before.map(
|
|
1424
|
-
(t) => t.id === artifactId ? { ...t, visibility: "pinned" } : t
|
|
1425
|
-
)
|
|
1426
|
-
});
|
|
1427
|
-
const r = await patchArtifact(artifactId, { visibility: "pinned" }, "user_long_press_unhide");
|
|
1428
|
-
if (r.ok) {
|
|
1429
|
-
if (r.artifact) {
|
|
1430
|
-
set({
|
|
1431
|
-
tiles: get().tiles.map((t) => t.id === artifactId ? { ...t, ...r.artifact } : t)
|
|
1432
|
-
});
|
|
1481
|
+
const data = await res.json();
|
|
1482
|
+
if (!data.ok || !data.items) {
|
|
1483
|
+
set({ conversationLoaded: true });
|
|
1484
|
+
return;
|
|
1433
1485
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
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()
|
|
1452
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 {
|
|
1453
1631
|
}
|
|
1454
|
-
|
|
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 });
|
|
1455
1642
|
}
|
|
1456
|
-
set({ tiles: before });
|
|
1457
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1458
|
-
},
|
|
1459
|
-
reorder: async (pinnedIds) => {
|
|
1460
|
-
const before = get().tiles;
|
|
1461
|
-
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
1462
|
-
set({
|
|
1463
|
-
tiles: before.map((t) => {
|
|
1464
|
-
const o = idToOrder.get(t.id);
|
|
1465
|
-
return o == null ? t : { ...t, pinOrder: o };
|
|
1466
|
-
})
|
|
1467
|
-
});
|
|
1468
|
-
const r = await setOrder(pinnedIds);
|
|
1469
|
-
if (r.ok) return { ok: true };
|
|
1470
|
-
set({ tiles: before });
|
|
1471
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1472
|
-
},
|
|
1473
|
-
applyServerUpdate: (artifact) => {
|
|
1474
|
-
set({
|
|
1475
|
-
tiles: get().tiles.map((t) => t.id === artifact.id ? { ...t, ...artifact } : t)
|
|
1476
|
-
});
|
|
1477
|
-
},
|
|
1478
|
-
applyServerRetire: (artifactId) => {
|
|
1479
|
-
set({ tiles: get().tiles.filter((t) => t.id !== artifactId) });
|
|
1480
|
-
},
|
|
1481
|
-
applyServerOrder: (pinnedIds) => {
|
|
1482
|
-
const idToOrder = new Map(pinnedIds.map((id, i) => [id, i]));
|
|
1483
|
-
set({
|
|
1484
|
-
tiles: get().tiles.map((t) => {
|
|
1485
|
-
const o = idToOrder.get(t.id);
|
|
1486
|
-
return o == null ? t : { ...t, pinOrder: o };
|
|
1487
|
-
})
|
|
1488
|
-
});
|
|
1489
1643
|
},
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
actions: [],
|
|
1510
|
-
hydrated: false,
|
|
1511
|
-
loading: false,
|
|
1512
|
-
hydrate: async () => {
|
|
1513
|
-
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 }));
|
|
1514
1663
|
try {
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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 {
|
|
1520
1693
|
}
|
|
1694
|
+
throw new Error(errMsg);
|
|
1521
1695
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
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
|
+
}));
|
|
1535
1751
|
}
|
|
1536
1752
|
},
|
|
1537
|
-
//
|
|
1538
|
-
//
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
if (r.ok) return { ok: true };
|
|
1551
|
-
set({ actions: before });
|
|
1552
|
-
persist(before);
|
|
1553
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1554
|
-
},
|
|
1555
|
-
setPinned: async (actionId, pinned) => {
|
|
1556
|
-
const before = get().actions;
|
|
1557
|
-
const next = before.map((a) => a.actionId === actionId ? { ...a, pinned } : a).sort(sortByPin);
|
|
1558
|
-
set({ actions: next });
|
|
1559
|
-
persist(next);
|
|
1560
|
-
const r = await patchHomeAction(actionId, { pinned }, pinned ? "user_pin" : "user_unpin");
|
|
1561
|
-
if (r.ok) {
|
|
1562
|
-
if (r.action) {
|
|
1563
|
-
const merged = get().actions.map((a) => a.actionId === actionId ? { ...a, ...r.action } : a).sort(sortByPin);
|
|
1564
|
-
set({ actions: merged });
|
|
1565
|
-
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);
|
|
1566
1766
|
}
|
|
1567
|
-
|
|
1767
|
+
} catch (err) {
|
|
1768
|
+
console.warn("[adas-chat] Error responding to question:", err.message);
|
|
1568
1769
|
}
|
|
1569
|
-
set({ actions: before });
|
|
1570
|
-
persist(before);
|
|
1571
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1572
|
-
},
|
|
1573
|
-
reorder: async (orderedIds) => {
|
|
1574
|
-
const before = get().actions;
|
|
1575
|
-
const idx = new Map(orderedIds.map((id, i) => [id, i]));
|
|
1576
|
-
const next = before.map((a) => {
|
|
1577
|
-
const o = idx.get(a.actionId);
|
|
1578
|
-
return o == null ? a : { ...a, pinOrder: o };
|
|
1579
|
-
}).sort(sortByPin);
|
|
1580
|
-
set({ actions: next });
|
|
1581
|
-
persist(next);
|
|
1582
|
-
const r = await setHomeActionOrder(orderedIds);
|
|
1583
|
-
if (r.ok) return { ok: true };
|
|
1584
|
-
set({ actions: before });
|
|
1585
|
-
persist(before);
|
|
1586
|
-
return { ok: false, error: r.error || `HTTP ${r.status}` };
|
|
1587
1770
|
},
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
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
|
+
}));
|
|
1593
1797
|
},
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1798
|
+
clearChat: () => {
|
|
1799
|
+
set({
|
|
1800
|
+
messages: [],
|
|
1801
|
+
loading: false,
|
|
1802
|
+
error: null,
|
|
1803
|
+
activeJobId: null,
|
|
1804
|
+
conversationLoaded: false
|
|
1805
|
+
});
|
|
1598
1806
|
},
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1807
|
+
newChat: () => {
|
|
1808
|
+
set({
|
|
1809
|
+
messages: [],
|
|
1810
|
+
loading: false,
|
|
1811
|
+
error: null,
|
|
1812
|
+
activeJobId: null,
|
|
1813
|
+
conversationLoaded: true
|
|
1814
|
+
});
|
|
1607
1815
|
}
|
|
1608
1816
|
}));
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
function backoffMs(attempt) {
|
|
1621
|
-
const base = Math.min(MAX_BACKOFF_MS, 500 * Math.pow(2, attempt));
|
|
1622
|
-
const jitter = Math.random() * 250;
|
|
1623
|
-
return base + jitter;
|
|
1624
|
-
}
|
|
1625
|
-
function send(obj) {
|
|
1626
|
-
if (_ws2 && _ws2.readyState === 1) {
|
|
1627
|
-
try {
|
|
1628
|
-
_ws2.send(JSON.stringify(obj));
|
|
1629
|
-
} catch (e) {
|
|
1630
|
-
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 });
|
|
1631
1828
|
}
|
|
1632
1829
|
}
|
|
1633
1830
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
if (msg?.topic === TOPIC_HOME) {
|
|
1640
|
-
const home = useHomeActionsStore.getState();
|
|
1641
|
-
switch (msg.event) {
|
|
1642
|
-
case "updated":
|
|
1643
|
-
if (msg.action && typeof msg.action.actionId === "string") home.applyServerUpsert(msg.action);
|
|
1644
|
-
break;
|
|
1645
|
-
case "removed":
|
|
1646
|
-
if (typeof msg.actionId === "string") home.applyServerRemove(msg.actionId);
|
|
1647
|
-
break;
|
|
1648
|
-
case "order":
|
|
1649
|
-
if (Array.isArray(msg.order)) home.applyServerOrder(msg.order.filter((id) => typeof id === "string"));
|
|
1650
|
-
break;
|
|
1651
|
-
case "refreshed":
|
|
1652
|
-
home.refresh();
|
|
1653
|
-
break;
|
|
1654
|
-
default:
|
|
1655
|
-
break;
|
|
1656
|
-
}
|
|
1657
|
-
return;
|
|
1658
|
-
}
|
|
1659
|
-
if (msg?.topic !== TOPIC) return;
|
|
1660
|
-
const store = useWidgetsStore.getState();
|
|
1661
|
-
switch (msg.event) {
|
|
1662
|
-
case "updated":
|
|
1663
|
-
if (msg.artifact && typeof msg.artifact.id === "string") {
|
|
1664
|
-
store.applyServerUpdate(msg.artifact);
|
|
1665
|
-
}
|
|
1666
|
-
break;
|
|
1667
|
-
case "retired":
|
|
1668
|
-
if (typeof msg.artifactId === "string") {
|
|
1669
|
-
store.applyServerRetire(msg.artifactId);
|
|
1670
|
-
}
|
|
1671
|
-
break;
|
|
1672
|
-
case "order":
|
|
1673
|
-
if (Array.isArray(msg.pinned)) {
|
|
1674
|
-
store.applyServerOrder(msg.pinned.filter((id) => typeof id === "string"));
|
|
1675
|
-
}
|
|
1676
|
-
break;
|
|
1677
|
-
case "created":
|
|
1678
|
-
if (msg.artifact && typeof msg.artifact.id === "string") {
|
|
1679
|
-
store.applyServerUpdate(msg.artifact);
|
|
1680
|
-
}
|
|
1681
|
-
break;
|
|
1682
|
-
default:
|
|
1683
|
-
break;
|
|
1684
|
-
}
|
|
1831
|
+
var _ws2 = null;
|
|
1832
|
+
var _wsReady = false;
|
|
1833
|
+
var _clientDeclarationProvider = null;
|
|
1834
|
+
function setClientDeclarationProvider(fn) {
|
|
1835
|
+
_clientDeclarationProvider = fn;
|
|
1685
1836
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
if (
|
|
1689
|
-
const
|
|
1690
|
-
|
|
1691
|
-
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);
|
|
1692
1842
|
_ws2 = ws;
|
|
1843
|
+
_wsReady = false;
|
|
1693
1844
|
ws.onopen = () => {
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
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
|
+
}
|
|
1697
1854
|
};
|
|
1698
|
-
ws.onmessage = (
|
|
1855
|
+
ws.onmessage = (ev) => {
|
|
1699
1856
|
try {
|
|
1700
|
-
const msg = JSON.parse(typeof
|
|
1701
|
-
|
|
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
|
+
}
|
|
1702
1898
|
} catch (err) {
|
|
1703
|
-
console.warn("[
|
|
1899
|
+
console.warn("[adas-chat] WS parse error:", err);
|
|
1704
1900
|
}
|
|
1705
1901
|
};
|
|
1706
|
-
ws.onerror = (
|
|
1707
|
-
|
|
1902
|
+
ws.onerror = () => {
|
|
1903
|
+
_ws2 = null;
|
|
1904
|
+
_wsReady = false;
|
|
1905
|
+
if (_currentJobId) {
|
|
1906
|
+
const jid = _currentJobId;
|
|
1907
|
+
_currentJobId = null;
|
|
1908
|
+
pollJob(jid, token);
|
|
1909
|
+
}
|
|
1708
1910
|
};
|
|
1709
1911
|
ws.onclose = () => {
|
|
1710
1912
|
_ws2 = null;
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
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 }));
|
|
1726
1927
|
}
|
|
1727
1928
|
}
|
|
1728
|
-
function
|
|
1729
|
-
|
|
1929
|
+
function closeChatStream() {
|
|
1930
|
+
_currentJobId = null;
|
|
1730
1931
|
if (_ws2) {
|
|
1731
1932
|
try {
|
|
1732
1933
|
_ws2.close();
|
|
1733
1934
|
} catch {
|
|
1734
1935
|
}
|
|
1735
|
-
_ws2 = null;
|
|
1736
1936
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
_wantOpen = true;
|
|
1740
|
-
openSocket();
|
|
1937
|
+
_ws2 = null;
|
|
1938
|
+
_wsReady = false;
|
|
1741
1939
|
}
|
|
1742
|
-
function
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
if (
|
|
1747
|
-
|
|
1748
|
-
|
|
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
|
+
}
|
|
1749
1961
|
}
|
|
1750
|
-
|
|
1962
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
1751
1963
|
try {
|
|
1752
|
-
|
|
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
|
+
}
|
|
1753
1985
|
} catch {
|
|
1754
1986
|
}
|
|
1755
|
-
|
|
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");
|
|
1756
2070
|
}
|
|
2071
|
+
await onSession(session);
|
|
2072
|
+
closeChatStream();
|
|
2073
|
+
useChatStore.getState().clearChat();
|
|
2074
|
+
return session;
|
|
1757
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
|
+
};
|
|
1758
2176
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1759
2177
|
0 && (module.exports = {
|
|
1760
2178
|
PluginCommandEmitter,
|
|
@@ -1765,12 +2183,17 @@ function disconnectAcsWs() {
|
|
|
1765
2183
|
apiFetchRaw,
|
|
1766
2184
|
blocksToText,
|
|
1767
2185
|
borderRadius,
|
|
2186
|
+
capabilityEnabled,
|
|
1768
2187
|
closeChatStream,
|
|
1769
2188
|
colors,
|
|
1770
2189
|
connectAcsWs,
|
|
1771
2190
|
connectorCall,
|
|
2191
|
+
createAuthSession,
|
|
2192
|
+
createPushRegistration,
|
|
1772
2193
|
deleteArtifact,
|
|
1773
2194
|
disconnectAcsWs,
|
|
2195
|
+
exchangeHandoff,
|
|
2196
|
+
fetchConsumerConfig,
|
|
1774
2197
|
fontSize,
|
|
1775
2198
|
getConfig,
|
|
1776
2199
|
getConsumerAuthApiUrl,
|
|
@@ -1784,6 +2207,10 @@ function disconnectAcsWs() {
|
|
|
1784
2207
|
initAdas,
|
|
1785
2208
|
isAdasInitialized,
|
|
1786
2209
|
isDevHost,
|
|
2210
|
+
joinWithApple,
|
|
2211
|
+
joinWithGoogle,
|
|
2212
|
+
linkChannelStart,
|
|
2213
|
+
linkChannelVerify,
|
|
1787
2214
|
listHomeActions,
|
|
1788
2215
|
listMadeForYou,
|
|
1789
2216
|
listMyTenants,
|
|
@@ -1791,21 +2218,36 @@ function disconnectAcsWs() {
|
|
|
1791
2218
|
mcpFetch,
|
|
1792
2219
|
mcpToolCall,
|
|
1793
2220
|
notifyAcsWsForeground,
|
|
2221
|
+
notifyAppForeground,
|
|
2222
|
+
onAppForeground,
|
|
2223
|
+
openJoinWithApple,
|
|
2224
|
+
openJoinWithGoogle,
|
|
2225
|
+
parseDeepLinkParam,
|
|
2226
|
+
parseHandoffCode,
|
|
2227
|
+
parseInviteCode,
|
|
1794
2228
|
patchArtifact,
|
|
1795
2229
|
patchHomeAction,
|
|
1796
2230
|
pluginCommandEmitter,
|
|
1797
2231
|
postCommandResult,
|
|
2232
|
+
previewInvite,
|
|
1798
2233
|
recordEvent,
|
|
1799
2234
|
refreshToken,
|
|
2235
|
+
resolveAdasPhase,
|
|
1800
2236
|
setAcsCacheStorage,
|
|
1801
2237
|
setAcsReachabilityReporter,
|
|
2238
|
+
setClientDeclarationProvider,
|
|
1802
2239
|
setHomeActionOrder,
|
|
1803
2240
|
setOrder,
|
|
2241
|
+
signInWithApple,
|
|
2242
|
+
signInWithGoogle,
|
|
1804
2243
|
spacing,
|
|
1805
2244
|
switchTenant,
|
|
1806
2245
|
unwrapMcpResult,
|
|
2246
|
+
useCapability,
|
|
1807
2247
|
useChatStore,
|
|
1808
2248
|
useHomeActionsStore,
|
|
1809
|
-
useWidgetsStore
|
|
2249
|
+
useWidgetsStore,
|
|
2250
|
+
useWorkspaceStore,
|
|
2251
|
+
userFromJwt
|
|
1810
2252
|
});
|
|
1811
2253
|
//# sourceMappingURL=index.js.map
|