@ateam-ai-mobile/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +389 -0
- package/dist/index.d.ts +389 -0
- package/dist/index.js +1081 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1027 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +27 -0
- package/src/api/client.ts +105 -0
- package/src/api/mcp.ts +104 -0
- package/src/chat/store.ts +938 -0
- package/src/chat/types.ts +76 -0
- package/src/config.ts +118 -0
- package/src/index.ts +72 -0
- package/src/storage/types.ts +29 -0
- package/src/theme/tokens.ts +105 -0
- package/src/utils/emitter.ts +74 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1027 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
var _config = null;
|
|
3
|
+
function initAdas(config) {
|
|
4
|
+
_config = config;
|
|
5
|
+
}
|
|
6
|
+
function isAdasInitialized() {
|
|
7
|
+
return _config !== null;
|
|
8
|
+
}
|
|
9
|
+
function getConfig() {
|
|
10
|
+
if (!_config) {
|
|
11
|
+
throw new Error("[adas] SDK not initialized \u2014 call initAdas(config) before using any ADAS API");
|
|
12
|
+
}
|
|
13
|
+
return _config;
|
|
14
|
+
}
|
|
15
|
+
function getCoreApiUrl() {
|
|
16
|
+
return getConfig().coreUrl.replace(/\/$/, "");
|
|
17
|
+
}
|
|
18
|
+
function getCoreWsUrl() {
|
|
19
|
+
return getCoreApiUrl().replace(/^http/, "ws");
|
|
20
|
+
}
|
|
21
|
+
function getVoiceApiUrl() {
|
|
22
|
+
const url = getConfig().voiceUrl;
|
|
23
|
+
if (!url) throw new Error("[adas] voiceUrl not configured");
|
|
24
|
+
return url.replace(/\/$/, "");
|
|
25
|
+
}
|
|
26
|
+
function getTenant() {
|
|
27
|
+
return getConfig().tenant || "";
|
|
28
|
+
}
|
|
29
|
+
function getUser() {
|
|
30
|
+
return getConfig().getUser?.() || null;
|
|
31
|
+
}
|
|
32
|
+
function getToken() {
|
|
33
|
+
return getConfig().tokenProvider.getToken();
|
|
34
|
+
}
|
|
35
|
+
async function refreshToken() {
|
|
36
|
+
return getConfig().tokenProvider.refresh();
|
|
37
|
+
}
|
|
38
|
+
function notifyAuthFailure() {
|
|
39
|
+
getConfig().tokenProvider.onAuthFailure?.();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/api/client.ts
|
|
43
|
+
async function apiFetch(path, options = {}) {
|
|
44
|
+
const { baseUrl = "core", ...fetchOptions } = options;
|
|
45
|
+
const base = baseUrl === "voice" ? getVoiceApiUrl() : getCoreApiUrl();
|
|
46
|
+
const url = `${base}${path}`;
|
|
47
|
+
const token = getToken();
|
|
48
|
+
const headers = {
|
|
49
|
+
...fetchOptions.headers
|
|
50
|
+
};
|
|
51
|
+
if (token && !headers["Authorization"]) {
|
|
52
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
53
|
+
}
|
|
54
|
+
if (!headers["Content-Type"] && fetchOptions.body && typeof fetchOptions.body === "string") {
|
|
55
|
+
headers["Content-Type"] = "application/json";
|
|
56
|
+
}
|
|
57
|
+
const res = await fetch(url, { ...fetchOptions, headers });
|
|
58
|
+
if (res.status === 401 && token) {
|
|
59
|
+
console.warn(`[adas] 401 on ${path} \u2014 attempting refresh`);
|
|
60
|
+
const outcome = await refreshToken();
|
|
61
|
+
if (outcome.ok === "ok") {
|
|
62
|
+
const newToken = getToken();
|
|
63
|
+
headers["Authorization"] = `Bearer ${newToken}`;
|
|
64
|
+
const retryRes = await fetch(url, { ...fetchOptions, headers });
|
|
65
|
+
if (retryRes.ok) {
|
|
66
|
+
return retryRes.json();
|
|
67
|
+
}
|
|
68
|
+
if (retryRes.status === 401 || retryRes.status === 403) {
|
|
69
|
+
console.warn(`[adas] retry after refresh still ${retryRes.status} on ${path} \u2014 auth failure`);
|
|
70
|
+
notifyAuthFailure();
|
|
71
|
+
throw new Error("Session expired");
|
|
72
|
+
}
|
|
73
|
+
const text = await retryRes.text().catch(() => "");
|
|
74
|
+
throw new Error(`API ${retryRes.status}: ${text}`);
|
|
75
|
+
}
|
|
76
|
+
if (outcome.ok === "auth_failed") {
|
|
77
|
+
console.warn(`[adas] refresh auth_failed on ${path} (${outcome.reason}) \u2014 auth failure`);
|
|
78
|
+
notifyAuthFailure();
|
|
79
|
+
throw new Error("Session expired");
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`API 401: refresh transient (${outcome.reason})`);
|
|
82
|
+
}
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
const text = await res.text();
|
|
85
|
+
throw new Error(`API ${res.status}: ${text}`);
|
|
86
|
+
}
|
|
87
|
+
return res.json();
|
|
88
|
+
}
|
|
89
|
+
async function apiFetchRaw(path, options = {}) {
|
|
90
|
+
const { baseUrl = "core", ...fetchOptions } = options;
|
|
91
|
+
const base = baseUrl === "voice" ? getVoiceApiUrl() : getCoreApiUrl();
|
|
92
|
+
const url = `${base}${path}`;
|
|
93
|
+
const token = getToken();
|
|
94
|
+
const headers = {
|
|
95
|
+
...fetchOptions.headers
|
|
96
|
+
};
|
|
97
|
+
if (token && !headers["Authorization"]) {
|
|
98
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
99
|
+
}
|
|
100
|
+
return fetch(url, { ...fetchOptions, headers });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/api/mcp.ts
|
|
104
|
+
async function connectorCall(connectorId, tool, args = {}) {
|
|
105
|
+
try {
|
|
106
|
+
const res = await apiFetchRaw(
|
|
107
|
+
`/api/connectors/${encodeURIComponent(connectorId)}/call`,
|
|
108
|
+
{
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/json" },
|
|
111
|
+
body: JSON.stringify({ tool, args })
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
const text = await res.text();
|
|
116
|
+
return { ok: false, result: null, error: `HTTP ${res.status}: ${text}` };
|
|
117
|
+
}
|
|
118
|
+
const data = await res.json();
|
|
119
|
+
if (data.ok !== void 0) return data;
|
|
120
|
+
return { ok: true, result: data };
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return { ok: false, result: null, error: err instanceof Error ? err.message : String(err) };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function mcpFetch(body) {
|
|
126
|
+
const res = await apiFetchRaw("/mcp", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "Content-Type": "application/json" },
|
|
129
|
+
body: typeof body === "string" ? body : JSON.stringify(body)
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const text = await res.text();
|
|
133
|
+
throw new Error(`MCP fetch failed: HTTP ${res.status}: ${text}`);
|
|
134
|
+
}
|
|
135
|
+
return res.json();
|
|
136
|
+
}
|
|
137
|
+
async function mcpToolCall(toolName, args = {}) {
|
|
138
|
+
return mcpFetch({
|
|
139
|
+
jsonrpc: "2.0",
|
|
140
|
+
id: Date.now(),
|
|
141
|
+
method: "tools/call",
|
|
142
|
+
params: { name: toolName, arguments: args }
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async function postCommandResult(correlationId, result, error) {
|
|
146
|
+
await apiFetchRaw("/api/plugin-command-result", {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: { "Content-Type": "application/json" },
|
|
149
|
+
body: JSON.stringify({ correlationId, result, error })
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function unwrapMcpResult(result) {
|
|
153
|
+
if (!result) return result;
|
|
154
|
+
if (!result.content) return result;
|
|
155
|
+
const textItem = Array.isArray(result.content) ? result.content.find((c) => c.type === "text") : null;
|
|
156
|
+
if (!textItem?.text) return result;
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(textItem.text);
|
|
159
|
+
} catch {
|
|
160
|
+
return textItem.text;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/chat/store.ts
|
|
165
|
+
import { create } from "zustand";
|
|
166
|
+
|
|
167
|
+
// src/utils/emitter.ts
|
|
168
|
+
var RECENT_TTL_MS = 1e4;
|
|
169
|
+
var RECENT_MAX = 20;
|
|
170
|
+
var PluginCommandEmitter = class {
|
|
171
|
+
constructor() {
|
|
172
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
173
|
+
// Ring buffer of plugin commands seen recently. Lets a plugin that mounts
|
|
174
|
+
// AFTER its first command was emitted (e.g. a surface mounted by the
|
|
175
|
+
// dispatcher in response to focus-plugin) still pick that command up via
|
|
176
|
+
// `drainPluginCommands(pluginId)`. Self-cleans on TTL + size cap.
|
|
177
|
+
this.recent = [];
|
|
178
|
+
}
|
|
179
|
+
on(event, fn) {
|
|
180
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
181
|
+
this.listeners.get(event).add(fn);
|
|
182
|
+
return () => this.off(event, fn);
|
|
183
|
+
}
|
|
184
|
+
off(event, fn) {
|
|
185
|
+
this.listeners.get(event)?.delete(fn);
|
|
186
|
+
}
|
|
187
|
+
emit(event, data) {
|
|
188
|
+
if (event === "plugin-command" && data && data.pluginId && data.command && data.command !== "__focus") {
|
|
189
|
+
this.recent.push({
|
|
190
|
+
pluginId: data.pluginId,
|
|
191
|
+
command: data.command,
|
|
192
|
+
args: data.args,
|
|
193
|
+
correlationId: data.correlationId,
|
|
194
|
+
_bufferedAt: Date.now()
|
|
195
|
+
});
|
|
196
|
+
if (this.recent.length > RECENT_MAX) this.recent.shift();
|
|
197
|
+
}
|
|
198
|
+
this.listeners.get(event)?.forEach((fn) => {
|
|
199
|
+
try {
|
|
200
|
+
fn(data);
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/** Pop and return any plugin-commands targeted at `pluginId` that were
|
|
206
|
+
* emitted within the last RECENT_TTL_MS. Used by the native bridge on
|
|
207
|
+
* mount so the surface-dispatcher path doesn't drop the first command. */
|
|
208
|
+
drainPluginCommands(pluginId) {
|
|
209
|
+
const now = Date.now();
|
|
210
|
+
const out = [];
|
|
211
|
+
const remaining = [];
|
|
212
|
+
for (const cmd of this.recent) {
|
|
213
|
+
if (now - cmd._bufferedAt > RECENT_TTL_MS) continue;
|
|
214
|
+
if (cmd.pluginId === pluginId) out.push(cmd);
|
|
215
|
+
else remaining.push(cmd);
|
|
216
|
+
}
|
|
217
|
+
this.recent = remaining;
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var pluginCommandEmitter = new PluginCommandEmitter();
|
|
222
|
+
|
|
223
|
+
// src/chat/store.ts
|
|
224
|
+
function coreHeaders() {
|
|
225
|
+
const token = getToken();
|
|
226
|
+
const headers = {
|
|
227
|
+
"Content-Type": "application/json"
|
|
228
|
+
};
|
|
229
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
230
|
+
return headers;
|
|
231
|
+
}
|
|
232
|
+
function isTerminalStatus(status) {
|
|
233
|
+
const s = typeof status === "string" ? status.toLowerCase() : "";
|
|
234
|
+
return s === "done" || s === "error" || s === "failed" || s === "completed";
|
|
235
|
+
}
|
|
236
|
+
function normalizeJobStatus(snap) {
|
|
237
|
+
const status = typeof snap?.status === "string" ? snap.status.toLowerCase() : "";
|
|
238
|
+
if (status === "done" || status === "completed") return "done";
|
|
239
|
+
if (status === "error" || status === "failed") return "error";
|
|
240
|
+
if (status === "paused" || status === "waiting") return "paused";
|
|
241
|
+
if (status === "unknown") return "unknown";
|
|
242
|
+
return "running";
|
|
243
|
+
}
|
|
244
|
+
function extractPendingQuestion(snap) {
|
|
245
|
+
return snap?.pendingQuestion || snap?.state?.pendingQuestion || snap?.state?.waiting || null;
|
|
246
|
+
}
|
|
247
|
+
function extractActorId(snap) {
|
|
248
|
+
const actorId = snap?.actorId || snap?.actor?.actorId || snap?.actor?.id || snap?.state?.actorId || snap?.state?.actor?.actorId || snap?.state?.actor?.id;
|
|
249
|
+
return typeof actorId === "string" && actorId.length > 0 ? actorId : void 0;
|
|
250
|
+
}
|
|
251
|
+
function extractLiveProgress(snap) {
|
|
252
|
+
const lp = snap?.liveProgress || snap?.state?.__live_progress;
|
|
253
|
+
const latest = Array.isArray(lp) ? lp[lp.length - 1] : lp;
|
|
254
|
+
if (!latest?.message) return void 0;
|
|
255
|
+
return {
|
|
256
|
+
message: String(latest.message || ""),
|
|
257
|
+
...latest.step != null ? { step: latest.step } : {},
|
|
258
|
+
...latest.total != null ? { total: latest.total } : {}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function extractCurrentTool(state) {
|
|
262
|
+
const exec = state?.steps?.__exec;
|
|
263
|
+
if (!exec || typeof exec !== "object") return void 0;
|
|
264
|
+
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];
|
|
265
|
+
if (!latest) return void 0;
|
|
266
|
+
const steps = exec[latest.k];
|
|
267
|
+
if (!Array.isArray(steps) || !steps.length) return void 0;
|
|
268
|
+
const last = steps[steps.length - 1];
|
|
269
|
+
return last?.tool || last?.phase;
|
|
270
|
+
}
|
|
271
|
+
function snapshotToProgress(snap, rootJobId) {
|
|
272
|
+
const pq = extractPendingQuestion(snap);
|
|
273
|
+
const progress = {
|
|
274
|
+
jobId: rootJobId,
|
|
275
|
+
actorId: extractActorId(snap),
|
|
276
|
+
status: pq ? "paused" : normalizeJobStatus(snap),
|
|
277
|
+
subStatus: snap?.subStatus || snap?.phase || snap?.state?.visual?.phase,
|
|
278
|
+
phase: snap?.phase || snap?.state?.visual?.phase,
|
|
279
|
+
iteration: snap?.iteration || snap?.state?.plannerIter,
|
|
280
|
+
skillSlug: snap?.skillSlug || snap?.activeSkill,
|
|
281
|
+
currentTool: snap?.currentTool || snap?.lastTool || extractCurrentTool(snap?.state)
|
|
282
|
+
};
|
|
283
|
+
const liveProgress = extractLiveProgress(snap);
|
|
284
|
+
if (liveProgress) progress.liveProgress = liveProgress;
|
|
285
|
+
if (pq) {
|
|
286
|
+
progress.pendingQuestion = {
|
|
287
|
+
inputId: pq.correlation_id || pq.correlationId || pq.inputId || "",
|
|
288
|
+
question: pq.prompt_message || pq.question || "The assistant needs your input",
|
|
289
|
+
inputType: pq.inputType || "text",
|
|
290
|
+
options: pq.options || pq.requested_fields,
|
|
291
|
+
placeholder: pq.placeholder
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return progress;
|
|
295
|
+
}
|
|
296
|
+
function isTerminalSnapshot(snap) {
|
|
297
|
+
if (extractPendingQuestion(snap)) return false;
|
|
298
|
+
return snap?.done === true || isTerminalStatus(snap?.status);
|
|
299
|
+
}
|
|
300
|
+
var TABLE_BLOCK_OPEN = "\uE100";
|
|
301
|
+
var TABLE_BLOCK_CLOSE = "\uE101";
|
|
302
|
+
function renderTableShape(content) {
|
|
303
|
+
if (!content || typeof content !== "object") return null;
|
|
304
|
+
const cols = Array.isArray(content.columns) ? content.columns : null;
|
|
305
|
+
const rows = Array.isArray(content.rows) ? content.rows : null;
|
|
306
|
+
if (!cols || !rows) return null;
|
|
307
|
+
const payload = encodeURIComponent(JSON.stringify({ columns: cols, rows }));
|
|
308
|
+
return `${TABLE_BLOCK_OPEN}${payload}${TABLE_BLOCK_CLOSE}`;
|
|
309
|
+
}
|
|
310
|
+
function blocksToText(blocks) {
|
|
311
|
+
if (!blocks || blocks.length === 0) return "";
|
|
312
|
+
return blocks.map((b) => {
|
|
313
|
+
if (b.kind === "text" || b.kind === "markdown" || b.kind === "description") {
|
|
314
|
+
return typeof b.content === "string" ? b.content : "";
|
|
315
|
+
}
|
|
316
|
+
if (b.kind === "code") {
|
|
317
|
+
const code = typeof b.content === "object" ? b.content.content : b.content;
|
|
318
|
+
return `\`\`\`
|
|
319
|
+
${code}
|
|
320
|
+
\`\`\``;
|
|
321
|
+
}
|
|
322
|
+
if (b.kind === "table" && b.content && typeof b.content === "object") {
|
|
323
|
+
const md = renderTableShape(b.content);
|
|
324
|
+
if (md) return md;
|
|
325
|
+
}
|
|
326
|
+
if (b.kind === "json") {
|
|
327
|
+
if (b.content && typeof b.content === "object" && "pendingQuestion" in b.content) return "";
|
|
328
|
+
const md = renderTableShape(b.content);
|
|
329
|
+
if (md) return md;
|
|
330
|
+
return "```json\n" + JSON.stringify(b.content, null, 2) + "\n```";
|
|
331
|
+
}
|
|
332
|
+
if (b.kind === "html") {
|
|
333
|
+
return typeof b.content === "string" ? b.content : "";
|
|
334
|
+
}
|
|
335
|
+
return typeof b.content === "string" ? b.content : JSON.stringify(b.content);
|
|
336
|
+
}).filter(Boolean).join("\n\n");
|
|
337
|
+
}
|
|
338
|
+
function blocksToAttachments(blocks) {
|
|
339
|
+
if (!blocks || blocks.length === 0) return void 0;
|
|
340
|
+
const atts = [];
|
|
341
|
+
for (const b of blocks) {
|
|
342
|
+
if ((b.kind === "image" || b.kind === "file") && (b.url || b.hash)) {
|
|
343
|
+
atts.push({
|
|
344
|
+
mimeType: b.mimeType || (b.kind === "image" ? "image/jpeg" : "application/octet-stream"),
|
|
345
|
+
name: b.name || "attachment",
|
|
346
|
+
previewUri: b.url ? `${getCoreApiUrl()}${b.url}` : void 0
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return atts.length > 0 ? atts : void 0;
|
|
351
|
+
}
|
|
352
|
+
var useChatStore = create((set, get) => ({
|
|
353
|
+
messages: [],
|
|
354
|
+
loading: false,
|
|
355
|
+
error: null,
|
|
356
|
+
activeJobId: null,
|
|
357
|
+
conversationLoaded: false,
|
|
358
|
+
// -----------------------------------------------------------------------
|
|
359
|
+
// Load conversation history from Core
|
|
360
|
+
// GET /api/conversation → array of turns [{user, assistant, jobId, ...}]
|
|
361
|
+
// -----------------------------------------------------------------------
|
|
362
|
+
loadConversation: async () => {
|
|
363
|
+
try {
|
|
364
|
+
const attachmentCache = getConfig().attachmentCache;
|
|
365
|
+
await attachmentCache?.ready();
|
|
366
|
+
const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=20`, {
|
|
367
|
+
headers: coreHeaders()
|
|
368
|
+
});
|
|
369
|
+
if (!res.ok) {
|
|
370
|
+
console.warn("[adas-chat] Failed to load conversation:", res.status);
|
|
371
|
+
set({ conversationLoaded: true });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const data = await res.json();
|
|
375
|
+
if (!data.ok || !data.items) {
|
|
376
|
+
set({ conversationLoaded: true });
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const items = [...data.items].reverse();
|
|
380
|
+
const persistedMessages = [];
|
|
381
|
+
const seenJobIds = /* @__PURE__ */ new Set();
|
|
382
|
+
for (const turn of items) {
|
|
383
|
+
if (turn.jobId && seenJobIds.has(turn.jobId)) continue;
|
|
384
|
+
if (turn.jobId) seenJobIds.add(turn.jobId);
|
|
385
|
+
const userText = blocksToText(turn.user?.blocks);
|
|
386
|
+
let userAtts = blocksToAttachments(turn.user?.blocks);
|
|
387
|
+
if (!userAtts && turn.jobId) {
|
|
388
|
+
userAtts = attachmentCache?.get(turn.jobId);
|
|
389
|
+
}
|
|
390
|
+
const turnChainId = typeof turn.chainId === "string" ? turn.chainId : void 0;
|
|
391
|
+
if (userText || userAtts) {
|
|
392
|
+
persistedMessages.push({
|
|
393
|
+
id: `user-${turn.jobId}`,
|
|
394
|
+
role: "user",
|
|
395
|
+
content: userText || "",
|
|
396
|
+
timestamp: new Date(turn.startedAt || Date.now()),
|
|
397
|
+
skillSlug: turn.skillSlug,
|
|
398
|
+
...userAtts ? { attachments: userAtts } : {},
|
|
399
|
+
...turnChainId ? { chainId: turnChainId } : {}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
const assistantText = blocksToText(turn.assistant?.blocks);
|
|
403
|
+
const assistantAtts = blocksToAttachments(turn.assistant?.blocks);
|
|
404
|
+
const assistantActions = Array.isArray(turn.assistant?.actions) ? turn.assistant.actions : void 0;
|
|
405
|
+
const turnKind = typeof turn.kind === "string" ? turn.kind : void 0;
|
|
406
|
+
if (assistantText || assistantAtts || assistantActions?.length) {
|
|
407
|
+
persistedMessages.push({
|
|
408
|
+
id: `bot-${turn.jobId}`,
|
|
409
|
+
role: "assistant",
|
|
410
|
+
content: assistantText || "",
|
|
411
|
+
timestamp: new Date(turn.finishedAt || turn.startedAt || Date.now()),
|
|
412
|
+
skillSlug: turn.skillSlug,
|
|
413
|
+
...assistantAtts ? { attachments: assistantAtts } : {},
|
|
414
|
+
...assistantActions?.length ? { actions: assistantActions } : {},
|
|
415
|
+
...turnChainId ? { chainId: turnChainId } : {},
|
|
416
|
+
...turnKind ? { kind: turnKind } : {}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const current = get().messages;
|
|
421
|
+
const keptPlaceholders = [];
|
|
422
|
+
for (const m of current) {
|
|
423
|
+
if (!m.id.startsWith("job-") || !m.jobProgress || !m.chainId) continue;
|
|
424
|
+
const matchIdx = persistedMessages.findIndex(
|
|
425
|
+
(pm) => pm.role === "assistant" && pm.chainId === m.chainId
|
|
426
|
+
);
|
|
427
|
+
if (matchIdx >= 0) {
|
|
428
|
+
const st = m.jobProgress.status;
|
|
429
|
+
if (st === "running" || st === "paused") {
|
|
430
|
+
persistedMessages[matchIdx] = { ...persistedMessages[matchIdx], jobProgress: m.jobProgress };
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
keptPlaceholders.push(m);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const isLocalUserId = (id) => /^user-\d+$/.test(id);
|
|
437
|
+
const keptUserBubbles = current.filter((m) => {
|
|
438
|
+
if (m.role !== "user" || !isLocalUserId(m.id)) return false;
|
|
439
|
+
if (!m.chainId) return true;
|
|
440
|
+
return !persistedMessages.some(
|
|
441
|
+
(pm) => pm.role === "user" && pm.chainId === m.chainId
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
const finalMessages = [...persistedMessages, ...keptUserBubbles, ...keptPlaceholders];
|
|
445
|
+
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
446
|
+
let firstRunning = keptPlaceholders.find((m) => m.jobProgress?.status === "running");
|
|
447
|
+
try {
|
|
448
|
+
const chainRes = await fetch(`${getCoreApiUrl()}/api/chain/current`, {
|
|
449
|
+
headers: coreHeaders()
|
|
450
|
+
});
|
|
451
|
+
if (chainRes.ok) {
|
|
452
|
+
const chainBody = await chainRes.json();
|
|
453
|
+
const slim = chainBody?.data;
|
|
454
|
+
if (slim?.id) {
|
|
455
|
+
const chainId = String(slim.id);
|
|
456
|
+
const alreadyTracked = finalMessages.some(
|
|
457
|
+
(m) => m.role === "assistant" && m.jobProgress?.jobId === chainId
|
|
458
|
+
);
|
|
459
|
+
if (!alreadyTracked) {
|
|
460
|
+
const progress = snapshotToProgress(slim, chainId);
|
|
461
|
+
const chainCreatedAt = typeof slim.createdAt === "number" ? slim.createdAt : typeof slim.lastUpdate === "number" ? slim.lastUpdate : Date.now();
|
|
462
|
+
const chainLastUpdate = typeof slim.lastUpdate === "number" ? slim.lastUpdate : chainCreatedAt;
|
|
463
|
+
const jobProgress = {
|
|
464
|
+
jobId: chainId,
|
|
465
|
+
actorId: extractActorId(slim),
|
|
466
|
+
status: "running",
|
|
467
|
+
chainStartedAt: chainCreatedAt,
|
|
468
|
+
...progress
|
|
469
|
+
};
|
|
470
|
+
const goal = typeof slim.goal === "string" ? slim.goal : "";
|
|
471
|
+
const userExists = finalMessages.some(
|
|
472
|
+
(m) => m.role === "user" && (m.chainId === chainId || goal && m.content === goal)
|
|
473
|
+
);
|
|
474
|
+
if (goal && !userExists) {
|
|
475
|
+
finalMessages.push({
|
|
476
|
+
id: `user-${chainId}`,
|
|
477
|
+
role: "user",
|
|
478
|
+
content: goal,
|
|
479
|
+
timestamp: new Date(chainCreatedAt),
|
|
480
|
+
chainId
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
let matchIdx = -1;
|
|
484
|
+
for (let i = finalMessages.length - 1; i >= 0; i--) {
|
|
485
|
+
const m = finalMessages[i];
|
|
486
|
+
if (m.role === "assistant" && m.chainId === chainId) {
|
|
487
|
+
matchIdx = i;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (matchIdx >= 0) {
|
|
492
|
+
finalMessages[matchIdx] = { ...finalMessages[matchIdx], jobProgress };
|
|
493
|
+
if (jobProgress.status === "running") firstRunning = finalMessages[matchIdx];
|
|
494
|
+
} else {
|
|
495
|
+
const reattachBubble = {
|
|
496
|
+
id: `job-${chainId}`,
|
|
497
|
+
role: "assistant",
|
|
498
|
+
content: "Working on it...",
|
|
499
|
+
timestamp: new Date(chainLastUpdate),
|
|
500
|
+
jobProgress,
|
|
501
|
+
chainId
|
|
502
|
+
};
|
|
503
|
+
finalMessages.push(reattachBubble);
|
|
504
|
+
if (jobProgress.status === "running") firstRunning = reattachBubble;
|
|
505
|
+
}
|
|
506
|
+
finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
507
|
+
streamJob(chainId, getTenant(), getToken());
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
set({
|
|
514
|
+
messages: finalMessages,
|
|
515
|
+
conversationLoaded: true,
|
|
516
|
+
error: null,
|
|
517
|
+
activeJobId: firstRunning?.jobProgress?.jobId || null,
|
|
518
|
+
loading: !!firstRunning
|
|
519
|
+
});
|
|
520
|
+
} catch (err) {
|
|
521
|
+
console.warn("[adas-chat] Error loading conversation:", err.message);
|
|
522
|
+
set({ conversationLoaded: true });
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
// -----------------------------------------------------------------------
|
|
526
|
+
// Send message via Core API
|
|
527
|
+
// POST /api/chat { goal } → { jobId } → stream → fetch turn result
|
|
528
|
+
// -----------------------------------------------------------------------
|
|
529
|
+
sendMessage: async (text, skillSlug, attachments) => {
|
|
530
|
+
const userMsg = {
|
|
531
|
+
id: `user-${Date.now()}`,
|
|
532
|
+
role: "user",
|
|
533
|
+
content: text,
|
|
534
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
535
|
+
...attachments?.length ? {
|
|
536
|
+
attachments: attachments.map((a) => ({
|
|
537
|
+
mimeType: a.mimeType,
|
|
538
|
+
name: a.name,
|
|
539
|
+
previewUri: a.mimeType.startsWith("image/") ? `data:${a.mimeType};base64,${a.data}` : void 0
|
|
540
|
+
}))
|
|
541
|
+
} : {}
|
|
542
|
+
};
|
|
543
|
+
set((s) => ({ messages: [...s.messages, userMsg], loading: true, error: null }));
|
|
544
|
+
try {
|
|
545
|
+
const actor = getUser();
|
|
546
|
+
const body = {
|
|
547
|
+
goal: text,
|
|
548
|
+
// Opt in to the Chain Additions router (attach/cancel/new while a
|
|
549
|
+
// chain runs). Backend ignores it where not applicable — safe to send.
|
|
550
|
+
chainRouting: true,
|
|
551
|
+
triggerContext: {
|
|
552
|
+
channel: "mobile",
|
|
553
|
+
replyContext: { chatId: actor?.actorId || "unknown" }
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
if (skillSlug) body.skillSlug = skillSlug;
|
|
557
|
+
if (attachments?.length) body.attachments = attachments;
|
|
558
|
+
const res = await fetch(`${getCoreApiUrl()}/api/chat`, {
|
|
559
|
+
method: "POST",
|
|
560
|
+
headers: coreHeaders(),
|
|
561
|
+
body: JSON.stringify(body)
|
|
562
|
+
});
|
|
563
|
+
if (!res.ok) {
|
|
564
|
+
let errMsg = `Server error (${res.status})`;
|
|
565
|
+
try {
|
|
566
|
+
const text2 = await res.text();
|
|
567
|
+
if (text2.startsWith("<") || text2.startsWith("<!")) {
|
|
568
|
+
errMsg = res.status === 502 ? "Server is temporarily unavailable. Please try again." : `Server error (${res.status})`;
|
|
569
|
+
} else {
|
|
570
|
+
const parsed = JSON.parse(text2);
|
|
571
|
+
errMsg = parsed.error || errMsg;
|
|
572
|
+
}
|
|
573
|
+
} catch {
|
|
574
|
+
}
|
|
575
|
+
throw new Error(errMsg);
|
|
576
|
+
}
|
|
577
|
+
const data = await res.json();
|
|
578
|
+
if (!data.ok) {
|
|
579
|
+
throw new Error(data.error || "Failed to send message");
|
|
580
|
+
}
|
|
581
|
+
if (data.mode === "chain_addition" || data.mode === "chain_cancel") {
|
|
582
|
+
const ack = data.mode === "chain_addition" ? data.ack || "Got it \u2014 added to the running task." : "Stopping the task\u2026";
|
|
583
|
+
set((s) => ({
|
|
584
|
+
messages: s.messages.map((m) => m.id === userMsg.id && data.chainId ? { ...m, chainId: data.chainId } : m).concat({
|
|
585
|
+
id: `ack-${Date.now()}`,
|
|
586
|
+
role: "assistant",
|
|
587
|
+
content: ack,
|
|
588
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
589
|
+
...data.chainId ? { chainId: data.chainId } : {}
|
|
590
|
+
})
|
|
591
|
+
}));
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const jobId = data.id || data.jobId;
|
|
595
|
+
if (!jobId) {
|
|
596
|
+
throw new Error("No job ID returned");
|
|
597
|
+
}
|
|
598
|
+
if (attachments?.length) {
|
|
599
|
+
await getConfig().attachmentCache?.save(jobId, attachments);
|
|
600
|
+
}
|
|
601
|
+
const progressMsg = {
|
|
602
|
+
id: `job-${jobId}`,
|
|
603
|
+
role: "assistant",
|
|
604
|
+
content: "Working on it...",
|
|
605
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
606
|
+
jobProgress: {
|
|
607
|
+
jobId,
|
|
608
|
+
actorId: actor?.actorId,
|
|
609
|
+
status: "running",
|
|
610
|
+
subStatus: "Starting...",
|
|
611
|
+
chainStartedAt: Date.now()
|
|
612
|
+
},
|
|
613
|
+
chainId: jobId
|
|
614
|
+
};
|
|
615
|
+
set((s) => ({
|
|
616
|
+
messages: s.messages.map(
|
|
617
|
+
(m) => m.id === userMsg.id ? { ...m, chainId: jobId } : m
|
|
618
|
+
).concat(progressMsg),
|
|
619
|
+
activeJobId: jobId
|
|
620
|
+
}));
|
|
621
|
+
streamJob(jobId, getTenant(), getToken());
|
|
622
|
+
} catch (err) {
|
|
623
|
+
set((s) => ({
|
|
624
|
+
messages: [...s.messages, {
|
|
625
|
+
id: `error-${Date.now()}`,
|
|
626
|
+
role: "system",
|
|
627
|
+
content: `Error: ${err.message}`,
|
|
628
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
629
|
+
}],
|
|
630
|
+
loading: false
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
// -----------------------------------------------------------------------
|
|
635
|
+
// Respond to sys.askUser paused question
|
|
636
|
+
// -----------------------------------------------------------------------
|
|
637
|
+
respondToQuestion: async (jobId, inputId, answer) => {
|
|
638
|
+
try {
|
|
639
|
+
const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/respond`, {
|
|
640
|
+
method: "POST",
|
|
641
|
+
headers: coreHeaders(),
|
|
642
|
+
body: JSON.stringify({ inputId, answer })
|
|
643
|
+
});
|
|
644
|
+
const data = await res.json();
|
|
645
|
+
if (!data.ok) {
|
|
646
|
+
console.warn("[adas-chat] Failed to respond to question:", data.error);
|
|
647
|
+
}
|
|
648
|
+
} catch (err) {
|
|
649
|
+
console.warn("[adas-chat] Error responding to question:", err.message);
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
// -----------------------------------------------------------------------
|
|
653
|
+
// Update job progress (called by the stream handler)
|
|
654
|
+
// -----------------------------------------------------------------------
|
|
655
|
+
updateJobProgress: (jobId, progress) => {
|
|
656
|
+
set((s) => ({
|
|
657
|
+
messages: s.messages.map((m) => {
|
|
658
|
+
if (m.jobProgress?.jobId === jobId) {
|
|
659
|
+
const updated = { ...m.jobProgress, ...progress };
|
|
660
|
+
if (progress.status && progress.status !== "paused") {
|
|
661
|
+
updated.pendingQuestion = void 0;
|
|
662
|
+
}
|
|
663
|
+
const isPersisted = m.id.startsWith("bot-");
|
|
664
|
+
const content = isPersisted ? m.content : progress.resultMessage || progress.pendingQuestion?.question || m.content;
|
|
665
|
+
const actions = isPersisted ? m.actions : progress.resultActions?.length ? progress.resultActions : m.actions;
|
|
666
|
+
return {
|
|
667
|
+
...m,
|
|
668
|
+
jobProgress: updated,
|
|
669
|
+
content,
|
|
670
|
+
...actions ? { actions } : {}
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return m;
|
|
674
|
+
}),
|
|
675
|
+
loading: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? false : s.loading,
|
|
676
|
+
activeJobId: progress.status === "done" || progress.status === "error" || progress.status === "paused" ? null : s.activeJobId
|
|
677
|
+
}));
|
|
678
|
+
},
|
|
679
|
+
clearChat: () => {
|
|
680
|
+
set({
|
|
681
|
+
messages: [],
|
|
682
|
+
loading: false,
|
|
683
|
+
error: null,
|
|
684
|
+
activeJobId: null,
|
|
685
|
+
conversationLoaded: false
|
|
686
|
+
});
|
|
687
|
+
},
|
|
688
|
+
newChat: () => {
|
|
689
|
+
set({
|
|
690
|
+
messages: [],
|
|
691
|
+
loading: false,
|
|
692
|
+
error: null,
|
|
693
|
+
activeJobId: null,
|
|
694
|
+
conversationLoaded: true
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}));
|
|
698
|
+
var _processedPluginCmds = /* @__PURE__ */ new Set();
|
|
699
|
+
function dispatchPluginCommands(cmds) {
|
|
700
|
+
for (const cmd of cmds) {
|
|
701
|
+
const corrId = cmd?.correlationId || cmd?.correlation_id;
|
|
702
|
+
if (corrId && _processedPluginCmds.has(corrId)) continue;
|
|
703
|
+
if (corrId) _processedPluginCmds.add(corrId);
|
|
704
|
+
pluginCommandEmitter.emit("plugin-command", cmd);
|
|
705
|
+
const pluginId = cmd?.pluginId || cmd?.plugin_id || cmd?.args?.plugin_id;
|
|
706
|
+
if (pluginId) {
|
|
707
|
+
const props = cmd?.props ?? cmd?.args?.props ?? void 0;
|
|
708
|
+
pluginCommandEmitter.emit("focus-plugin", { pluginId, props });
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
var _ws = null;
|
|
713
|
+
var _wsReady = false;
|
|
714
|
+
var _currentJobId = null;
|
|
715
|
+
function ensureWs(tenant, token) {
|
|
716
|
+
if (_ws && _wsReady) return _ws;
|
|
717
|
+
const wsUrl = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(token || "")}&tenant=${encodeURIComponent(tenant)}`;
|
|
718
|
+
const ws = new WebSocket(wsUrl);
|
|
719
|
+
_ws = ws;
|
|
720
|
+
_wsReady = false;
|
|
721
|
+
ws.onopen = () => {
|
|
722
|
+
_wsReady = true;
|
|
723
|
+
if (_currentJobId) {
|
|
724
|
+
ws.send(JSON.stringify({ type: "subscribe", jobId: _currentJobId }));
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
ws.onmessage = (ev) => {
|
|
728
|
+
try {
|
|
729
|
+
const msg = JSON.parse(typeof ev.data === "string" ? ev.data : "");
|
|
730
|
+
const { event, data } = msg;
|
|
731
|
+
const { updateJobProgress } = useChatStore.getState();
|
|
732
|
+
const jobId = data && (data.id || data.chainId) || _currentJobId;
|
|
733
|
+
if (event === "plugin_command") {
|
|
734
|
+
dispatchPluginCommands([data]);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (event === "plugin-command-ack" || event === "pong") return;
|
|
738
|
+
if (!jobId) return;
|
|
739
|
+
switch (event) {
|
|
740
|
+
case "init":
|
|
741
|
+
case "job":
|
|
742
|
+
case "update": {
|
|
743
|
+
const snap = data;
|
|
744
|
+
const progress = snapshotToProgress(snap, jobId);
|
|
745
|
+
if (isTerminalSnapshot(snap)) {
|
|
746
|
+
if (snap.status === "done" && (snap.isHandoff || snap.state?.handoffTransfer)) {
|
|
747
|
+
updateJobProgress(jobId, { status: "running" });
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
_currentJobId = null;
|
|
751
|
+
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
752
|
+
updateJobProgress(jobId, progress);
|
|
753
|
+
} else {
|
|
754
|
+
fetchTurnResult(jobId, snap);
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
updateJobProgress(jobId, progress);
|
|
759
|
+
break;
|
|
760
|
+
}
|
|
761
|
+
case "chain_final":
|
|
762
|
+
_currentJobId = null;
|
|
763
|
+
fetchTurnResult(jobId, data);
|
|
764
|
+
break;
|
|
765
|
+
case "final":
|
|
766
|
+
_currentJobId = null;
|
|
767
|
+
fetchTurnResult(jobId, data);
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
} catch (err) {
|
|
771
|
+
console.warn("[adas-chat] WS parse error:", err);
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
ws.onerror = () => {
|
|
775
|
+
_ws = null;
|
|
776
|
+
_wsReady = false;
|
|
777
|
+
if (_currentJobId) {
|
|
778
|
+
const jid = _currentJobId;
|
|
779
|
+
_currentJobId = null;
|
|
780
|
+
pollJob(jid, token);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
ws.onclose = () => {
|
|
784
|
+
_ws = null;
|
|
785
|
+
_wsReady = false;
|
|
786
|
+
if (_currentJobId) {
|
|
787
|
+
const jid = _currentJobId;
|
|
788
|
+
_currentJobId = null;
|
|
789
|
+
pollJob(jid, token);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
return ws;
|
|
793
|
+
}
|
|
794
|
+
function streamJob(jobId, tenant, token) {
|
|
795
|
+
_currentJobId = jobId;
|
|
796
|
+
const ws = ensureWs(tenant, token);
|
|
797
|
+
if (_wsReady) {
|
|
798
|
+
ws.send(JSON.stringify({ type: "subscribe", jobId }));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function closeChatStream() {
|
|
802
|
+
_currentJobId = null;
|
|
803
|
+
if (_ws) {
|
|
804
|
+
try {
|
|
805
|
+
_ws.close();
|
|
806
|
+
} catch {
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
_ws = null;
|
|
810
|
+
_wsReady = false;
|
|
811
|
+
}
|
|
812
|
+
async function fetchTurnResult(jobId, jobSnap) {
|
|
813
|
+
if (_processedPluginCmds.size > 200) _processedPluginCmds.clear();
|
|
814
|
+
const { updateJobProgress } = useChatStore.getState();
|
|
815
|
+
const status = normalizeJobStatus(jobSnap) === "error" ? "error" : "done";
|
|
816
|
+
if (jobSnap.isHandoff || jobSnap.state?.handoffTransfer || jobSnap.result?.contentType === "handoff") {
|
|
817
|
+
try {
|
|
818
|
+
for (let i = 0; i < 40; i++) {
|
|
819
|
+
const statusRes = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {
|
|
820
|
+
headers: coreHeaders()
|
|
821
|
+
});
|
|
822
|
+
if (statusRes.ok) {
|
|
823
|
+
const statusData = await statusRes.json();
|
|
824
|
+
if (statusData.done && !statusData.isHandoff) {
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
updateJobProgress(jobId, { status: "running" });
|
|
828
|
+
}
|
|
829
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
830
|
+
}
|
|
831
|
+
} catch {
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
835
|
+
try {
|
|
836
|
+
const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=1`, {
|
|
837
|
+
headers: coreHeaders()
|
|
838
|
+
});
|
|
839
|
+
if (res.ok) {
|
|
840
|
+
const data = await res.json();
|
|
841
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
842
|
+
const turn = items[0];
|
|
843
|
+
if (turn?.assistant || turn?.turn?.assistant) {
|
|
844
|
+
const assistant = turn.assistant || turn.turn?.assistant;
|
|
845
|
+
const text = blocksToText(assistant.blocks);
|
|
846
|
+
const actions = Array.isArray(assistant.actions) && assistant.actions.length ? assistant.actions : void 0;
|
|
847
|
+
if (text) {
|
|
848
|
+
updateJobProgress(jobId, {
|
|
849
|
+
status,
|
|
850
|
+
resultMessage: text,
|
|
851
|
+
...actions ? { resultActions: actions } : {}
|
|
852
|
+
});
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
} catch {
|
|
858
|
+
}
|
|
859
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
860
|
+
}
|
|
861
|
+
const fallback = jobSnap.result?.message || jobSnap.state?.finalReply || (status === "error" ? "Something went wrong. Please try again." : "Done.");
|
|
862
|
+
updateJobProgress(jobId, { status, resultMessage: fallback });
|
|
863
|
+
}
|
|
864
|
+
async function pollJob(jobId, token) {
|
|
865
|
+
const { updateJobProgress } = useChatStore.getState();
|
|
866
|
+
for (let i = 0; i < 120; i++) {
|
|
867
|
+
try {
|
|
868
|
+
const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {
|
|
869
|
+
headers: {
|
|
870
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
if (!res.ok) {
|
|
874
|
+
if (res.status === 404) return;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
const snap = await res.json();
|
|
878
|
+
const progress = snapshotToProgress(snap, jobId);
|
|
879
|
+
if (progress.status === "paused" && progress.pendingQuestion) {
|
|
880
|
+
updateJobProgress(jobId, progress);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
updateJobProgress(jobId, progress);
|
|
884
|
+
if (isTerminalSnapshot(snap)) {
|
|
885
|
+
fetchTurnResult(jobId, snap);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
} catch {
|
|
889
|
+
}
|
|
890
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
891
|
+
}
|
|
892
|
+
updateJobProgress(jobId, {
|
|
893
|
+
status: "error",
|
|
894
|
+
resultMessage: "Job timed out. Please try again."
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// src/theme/tokens.ts
|
|
899
|
+
var colors = {
|
|
900
|
+
dark: {
|
|
901
|
+
bgPrimary: "#0a0e14",
|
|
902
|
+
bgSecondary: "#111820",
|
|
903
|
+
bgTertiary: "#1a2230",
|
|
904
|
+
bgInput: "#0d1219",
|
|
905
|
+
bgHover: "#1e2a3a",
|
|
906
|
+
border: "rgba(255, 255, 255, 0.08)",
|
|
907
|
+
borderHover: "rgba(255, 255, 255, 0.15)",
|
|
908
|
+
textPrimary: "#e8ecf1",
|
|
909
|
+
textSecondary: "#8b95a5",
|
|
910
|
+
textMuted: "#5a6577",
|
|
911
|
+
accent: "#2563eb",
|
|
912
|
+
accentHover: "#3b82f6",
|
|
913
|
+
accentSoft: "rgba(37, 99, 235, 0.15)",
|
|
914
|
+
green: "#16a34a",
|
|
915
|
+
greenSoft: "rgba(22, 163, 74, 0.15)",
|
|
916
|
+
red: "#dc2626",
|
|
917
|
+
redSoft: "rgba(220, 38, 38, 0.15)",
|
|
918
|
+
amber: "#f59e0b",
|
|
919
|
+
amberSoft: "rgba(245, 158, 11, 0.15)",
|
|
920
|
+
purple: "#8b5cf6",
|
|
921
|
+
purpleSoft: "rgba(139, 92, 246, 0.15)",
|
|
922
|
+
// Message-specific
|
|
923
|
+
userBubble: "#2563eb",
|
|
924
|
+
botBubble: "#1a2230",
|
|
925
|
+
botBubbleBorder: "rgba(255, 255, 255, 0.08)"
|
|
926
|
+
},
|
|
927
|
+
light: {
|
|
928
|
+
bgPrimary: "#ffffff",
|
|
929
|
+
bgSecondary: "#f8f9fa",
|
|
930
|
+
bgTertiary: "#f1f3f5",
|
|
931
|
+
bgInput: "#ffffff",
|
|
932
|
+
bgHover: "#e9ecef",
|
|
933
|
+
border: "rgba(0, 0, 0, 0.08)",
|
|
934
|
+
borderHover: "rgba(0, 0, 0, 0.15)",
|
|
935
|
+
textPrimary: "#1a1a2e",
|
|
936
|
+
textSecondary: "#6b7280",
|
|
937
|
+
textMuted: "#9ca3af",
|
|
938
|
+
accent: "#2563eb",
|
|
939
|
+
accentHover: "#1d4ed8",
|
|
940
|
+
accentSoft: "rgba(37, 99, 235, 0.1)",
|
|
941
|
+
green: "#16a34a",
|
|
942
|
+
greenSoft: "rgba(22, 163, 74, 0.1)",
|
|
943
|
+
red: "#dc2626",
|
|
944
|
+
redSoft: "rgba(220, 38, 38, 0.1)",
|
|
945
|
+
amber: "#f59e0b",
|
|
946
|
+
amberSoft: "rgba(245, 158, 11, 0.1)",
|
|
947
|
+
purple: "#8b5cf6",
|
|
948
|
+
purpleSoft: "rgba(139, 92, 246, 0.1)",
|
|
949
|
+
userBubble: "#2563eb",
|
|
950
|
+
botBubble: "#f1f3f5",
|
|
951
|
+
botBubbleBorder: "rgba(0, 0, 0, 0.08)"
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
var ada = {
|
|
955
|
+
paper: "#FAF6EE",
|
|
956
|
+
paperSoft: "#F2EADC",
|
|
957
|
+
paperDeep: "#EBE0CC",
|
|
958
|
+
ink: "#1a1410",
|
|
959
|
+
inkSoft: "#5a4d3f",
|
|
960
|
+
inkMuted: "#9a8870",
|
|
961
|
+
border: "rgba(26, 20, 16, 0.08)",
|
|
962
|
+
borderStrong: "rgba(26, 20, 16, 0.16)",
|
|
963
|
+
orange: "#FF7A28",
|
|
964
|
+
orangeDeep: "#B8410A",
|
|
965
|
+
orangeSoft: "rgba(255, 122, 40, 0.14)",
|
|
966
|
+
userBubble: "#1a1410",
|
|
967
|
+
userBubbleText: "#FAF6EE",
|
|
968
|
+
botBubble: "#F2EADC",
|
|
969
|
+
botBubbleBorder: "rgba(26, 20, 16, 0.06)",
|
|
970
|
+
botBubbleText: "#1a1410"
|
|
971
|
+
};
|
|
972
|
+
var spacing = {
|
|
973
|
+
xs: 4,
|
|
974
|
+
sm: 8,
|
|
975
|
+
md: 12,
|
|
976
|
+
lg: 16,
|
|
977
|
+
xl: 20,
|
|
978
|
+
xxl: 24,
|
|
979
|
+
xxxl: 32
|
|
980
|
+
};
|
|
981
|
+
var fontSize = {
|
|
982
|
+
xs: 11,
|
|
983
|
+
sm: 13,
|
|
984
|
+
md: 15,
|
|
985
|
+
lg: 17,
|
|
986
|
+
xl: 20,
|
|
987
|
+
xxl: 24,
|
|
988
|
+
title: 28
|
|
989
|
+
};
|
|
990
|
+
var borderRadius = {
|
|
991
|
+
sm: 8,
|
|
992
|
+
md: 12,
|
|
993
|
+
lg: 16,
|
|
994
|
+
xl: 20,
|
|
995
|
+
full: 9999
|
|
996
|
+
};
|
|
997
|
+
export {
|
|
998
|
+
PluginCommandEmitter,
|
|
999
|
+
TABLE_BLOCK_CLOSE,
|
|
1000
|
+
TABLE_BLOCK_OPEN,
|
|
1001
|
+
ada,
|
|
1002
|
+
apiFetch,
|
|
1003
|
+
apiFetchRaw,
|
|
1004
|
+
blocksToText,
|
|
1005
|
+
borderRadius,
|
|
1006
|
+
closeChatStream,
|
|
1007
|
+
colors,
|
|
1008
|
+
connectorCall,
|
|
1009
|
+
fontSize,
|
|
1010
|
+
getConfig,
|
|
1011
|
+
getCoreApiUrl,
|
|
1012
|
+
getCoreWsUrl,
|
|
1013
|
+
getTenant,
|
|
1014
|
+
getToken,
|
|
1015
|
+
getUser,
|
|
1016
|
+
getVoiceApiUrl,
|
|
1017
|
+
initAdas,
|
|
1018
|
+
isAdasInitialized,
|
|
1019
|
+
mcpFetch,
|
|
1020
|
+
mcpToolCall,
|
|
1021
|
+
pluginCommandEmitter,
|
|
1022
|
+
postCommandResult,
|
|
1023
|
+
spacing,
|
|
1024
|
+
unwrapMcpResult,
|
|
1025
|
+
useChatStore
|
|
1026
|
+
};
|
|
1027
|
+
//# sourceMappingURL=index.mjs.map
|