@webless/agent 0.2.9 → 0.2.11
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/{chunk-7XB5OBTP.js → chunk-XAUJPXWR.js} +359 -114
- package/dist/chunk-XAUJPXWR.js.map +1 -0
- package/dist/embed.cjs +344 -96
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +46 -0
- package/dist/embed.css.map +1 -1
- package/dist/embed.js +1 -1
- package/dist/index.cjs +146 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +146 -26
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +341 -96
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +46 -0
- package/dist/react.css.map +1 -1
- package/dist/react.js +1 -1
- package/package.json +3 -2
- package/dist/chunk-7XB5OBTP.js.map +0 -1
package/dist/embed.cjs
CHANGED
|
@@ -237,6 +237,54 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
// src/runtime/client.ts
|
|
240
|
+
function isTurnBoundary(event) {
|
|
241
|
+
return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
|
|
242
|
+
}
|
|
243
|
+
function applyMessageEvent(event, rendered, handlers) {
|
|
244
|
+
const step = mapStepLabel(event);
|
|
245
|
+
if (step) handlers.onStep?.(step.label, step.detail);
|
|
246
|
+
if (event.type === "session.failed") {
|
|
247
|
+
throw new Error(event.data.message || event.data.code);
|
|
248
|
+
}
|
|
249
|
+
if (event.type === "message.completed") {
|
|
250
|
+
handlers.onComplete?.();
|
|
251
|
+
}
|
|
252
|
+
if (event.type !== "message.appended") return rendered;
|
|
253
|
+
const { messageDelta, messageSoFar } = event.data;
|
|
254
|
+
let delta = messageDelta;
|
|
255
|
+
let next = rendered;
|
|
256
|
+
if (messageSoFar.startsWith(rendered)) {
|
|
257
|
+
delta = messageSoFar.slice(rendered.length);
|
|
258
|
+
next = messageSoFar;
|
|
259
|
+
} else if (messageDelta) {
|
|
260
|
+
next += messageDelta;
|
|
261
|
+
}
|
|
262
|
+
if (delta) handlers.onDelta(delta);
|
|
263
|
+
return next;
|
|
264
|
+
}
|
|
265
|
+
function latestTurnEvents(events) {
|
|
266
|
+
let startIndex = -1;
|
|
267
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
268
|
+
if (events[index]?.type === "message.received") {
|
|
269
|
+
startIndex = index;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return startIndex >= 0 ? events.slice(startIndex) : [];
|
|
274
|
+
}
|
|
275
|
+
function renderTurn(events) {
|
|
276
|
+
let rendered = "";
|
|
277
|
+
for (const event of events) {
|
|
278
|
+
if (event.type !== "message.appended") continue;
|
|
279
|
+
const { messageDelta, messageSoFar } = event.data;
|
|
280
|
+
if (messageSoFar.startsWith(rendered)) {
|
|
281
|
+
rendered = messageSoFar;
|
|
282
|
+
} else if (messageDelta) {
|
|
283
|
+
rendered += messageDelta;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return rendered;
|
|
287
|
+
}
|
|
240
288
|
function mapStepLabel(event) {
|
|
241
289
|
if (event.type !== "step.started") return null;
|
|
242
290
|
const stepIndex = event.data.stepIndex;
|
|
@@ -272,8 +320,13 @@ var AgentSession = class {
|
|
|
272
320
|
return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
|
|
273
321
|
}
|
|
274
322
|
reset() {
|
|
275
|
-
|
|
276
|
-
|
|
323
|
+
if (this.activeResponse) {
|
|
324
|
+
void this.activeResponse.cancel().catch(() => {
|
|
325
|
+
});
|
|
326
|
+
} else {
|
|
327
|
+
void this.session?.cancel().catch(() => {
|
|
328
|
+
});
|
|
329
|
+
}
|
|
277
330
|
this.activeResponse = void 0;
|
|
278
331
|
this.session = void 0;
|
|
279
332
|
clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
@@ -298,7 +351,8 @@ var AgentSession = class {
|
|
|
298
351
|
if (this.client && this.clientHost === config.host) {
|
|
299
352
|
return this.client;
|
|
300
353
|
}
|
|
301
|
-
this.
|
|
354
|
+
this.activeResponse = void 0;
|
|
355
|
+
this.session = void 0;
|
|
302
356
|
this.client = new import_client2.Client({
|
|
303
357
|
auth: { bearer: () => this.capability.getAccessToken() },
|
|
304
358
|
host: config.host,
|
|
@@ -347,28 +401,20 @@ var AgentSession = class {
|
|
|
347
401
|
this.persistSessionCursor(session);
|
|
348
402
|
}
|
|
349
403
|
this.activeResponse = response;
|
|
404
|
+
let streamIndex = session?.state.streamIndex ?? 0;
|
|
350
405
|
let rendered = "";
|
|
351
406
|
try {
|
|
352
407
|
for await (const event of response) {
|
|
353
408
|
if (signal.aborted) break;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
rendered += messageDelta;
|
|
364
|
-
}
|
|
365
|
-
if (delta) handlers.onDelta(delta);
|
|
366
|
-
}
|
|
367
|
-
if (event.type === "message.completed") {
|
|
368
|
-
handlers.onComplete?.();
|
|
369
|
-
}
|
|
370
|
-
if (event.type === "session.failed") {
|
|
371
|
-
throw new Error(event.data.message || event.data.code);
|
|
409
|
+
rendered = applyMessageEvent(event, rendered, handlers);
|
|
410
|
+
streamIndex += 1;
|
|
411
|
+
if (session) {
|
|
412
|
+
savePersistedAgentSession(
|
|
413
|
+
this.visitorSessionId,
|
|
414
|
+
session.state.sessionId,
|
|
415
|
+
streamIndex,
|
|
416
|
+
this.storeOptions
|
|
417
|
+
);
|
|
372
418
|
}
|
|
373
419
|
}
|
|
374
420
|
} finally {
|
|
@@ -380,15 +426,83 @@ var AgentSession = class {
|
|
|
380
426
|
if (!rendered.trim() && !signal.aborted) {
|
|
381
427
|
throw new Error("Empty response from runtime");
|
|
382
428
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
429
|
+
return rendered.trim();
|
|
430
|
+
}
|
|
431
|
+
async resumeTurn(message, signal, handlers, initialText = "") {
|
|
432
|
+
const persisted = loadPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
433
|
+
if (!persisted) return null;
|
|
434
|
+
const client = this.ensureClient();
|
|
435
|
+
const attached = client.sessions.attach(persisted.sessionId, {
|
|
436
|
+
streamIndex: persisted.streamIndex
|
|
437
|
+
});
|
|
438
|
+
const snapshot = await withCapabilityRefresh(
|
|
439
|
+
this.capability,
|
|
440
|
+
() => attached.snapshot({ signal })
|
|
441
|
+
);
|
|
442
|
+
const turnEvents = latestTurnEvents(snapshot.events);
|
|
443
|
+
const received = turnEvents[0];
|
|
444
|
+
if (received?.type !== "message.received" || received.data.message !== message) {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
let rendered = renderTurn(turnEvents);
|
|
448
|
+
if (rendered.startsWith(initialText)) {
|
|
449
|
+
const missedText = rendered.slice(initialText.length);
|
|
450
|
+
if (missedText) handlers.onDelta(missedText);
|
|
451
|
+
} else if (initialText.startsWith(rendered)) {
|
|
452
|
+
rendered = initialText;
|
|
453
|
+
} else if (!initialText.startsWith(rendered)) {
|
|
454
|
+
rendered = initialText + rendered;
|
|
455
|
+
}
|
|
456
|
+
let session = client.sessions.attach(snapshot.session.sessionId, {
|
|
457
|
+
streamIndex: snapshot.session.streamIndex
|
|
458
|
+
});
|
|
459
|
+
this.session = session;
|
|
460
|
+
this.persistSessionCursor(session);
|
|
461
|
+
let snapshotBoundary;
|
|
462
|
+
for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
|
|
463
|
+
const event = turnEvents[index];
|
|
464
|
+
if (event && isTurnBoundary(event)) {
|
|
465
|
+
snapshotBoundary = event;
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (snapshotBoundary) {
|
|
470
|
+
if (snapshotBoundary.type === "session.failed") {
|
|
471
|
+
throw new Error(snapshotBoundary.data.message || snapshotBoundary.data.code);
|
|
472
|
+
}
|
|
473
|
+
handlers.onComplete?.();
|
|
474
|
+
if (!rendered.trim()) throw new Error("Empty response from runtime");
|
|
475
|
+
return rendered.trim();
|
|
476
|
+
}
|
|
477
|
+
let streamIndex = snapshot.session.streamIndex;
|
|
478
|
+
for await (const event of session.stream({ signal })) {
|
|
479
|
+
if (signal.aborted) break;
|
|
480
|
+
rendered = applyMessageEvent(event, rendered, handlers);
|
|
481
|
+
streamIndex += 1;
|
|
482
|
+
savePersistedAgentSession(
|
|
483
|
+
this.visitorSessionId,
|
|
484
|
+
session.state.sessionId,
|
|
485
|
+
streamIndex,
|
|
486
|
+
this.storeOptions
|
|
487
|
+
);
|
|
488
|
+
if (isTurnBoundary(event)) break;
|
|
489
|
+
}
|
|
490
|
+
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
491
|
+
this.session = session;
|
|
492
|
+
this.persistSessionCursor(session);
|
|
493
|
+
if (!rendered.trim() && !signal.aborted) {
|
|
494
|
+
throw new Error("Empty response from runtime");
|
|
386
495
|
}
|
|
387
496
|
return rendered.trim();
|
|
388
497
|
}
|
|
389
498
|
cancelActive() {
|
|
390
|
-
this.activeResponse
|
|
391
|
-
|
|
499
|
+
if (this.activeResponse) {
|
|
500
|
+
this.activeResponse.cancel().catch(() => {
|
|
501
|
+
});
|
|
502
|
+
} else {
|
|
503
|
+
this.session?.cancel().catch(() => {
|
|
504
|
+
});
|
|
505
|
+
}
|
|
392
506
|
}
|
|
393
507
|
};
|
|
394
508
|
function createAgentClient(options) {
|
|
@@ -422,6 +536,12 @@ function createAgentClient(options) {
|
|
|
422
536
|
sendOptions.signal ?? new AbortController().signal,
|
|
423
537
|
sendOptions.handlers
|
|
424
538
|
),
|
|
539
|
+
resumeTurn: (resumeOptions) => session.resumeTurn(
|
|
540
|
+
resumeOptions.message,
|
|
541
|
+
resumeOptions.signal ?? new AbortController().signal,
|
|
542
|
+
resumeOptions.handlers,
|
|
543
|
+
resumeOptions.initialText
|
|
544
|
+
),
|
|
425
545
|
reset: () => session.reset(),
|
|
426
546
|
cancelActive: () => session.cancelActive(),
|
|
427
547
|
getActiveSessionId: () => session.getActiveSessionId()
|
|
@@ -450,6 +570,58 @@ function formatAgentError(error) {
|
|
|
450
570
|
return "Runtime request failed";
|
|
451
571
|
}
|
|
452
572
|
|
|
573
|
+
// src/react/persisted-conversation.ts
|
|
574
|
+
var CONVERSATION_VERSION = 1;
|
|
575
|
+
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
576
|
+
return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
|
|
577
|
+
}
|
|
578
|
+
function parseMessage(value) {
|
|
579
|
+
if (typeof value !== "object" || value === null) return null;
|
|
580
|
+
const record = value;
|
|
581
|
+
if (typeof record.id !== "string" || record.role !== "agent" && record.role !== "visitor" || typeof record.text !== "string" || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt)) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
return {
|
|
585
|
+
id: record.id,
|
|
586
|
+
role: record.role,
|
|
587
|
+
text: record.text,
|
|
588
|
+
createdAt: record.createdAt
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
592
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
593
|
+
const raw = sessionStorage.getItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
594
|
+
if (!raw) return null;
|
|
595
|
+
try {
|
|
596
|
+
const value = JSON.parse(raw);
|
|
597
|
+
if (typeof value !== "object" || value === null) return null;
|
|
598
|
+
const record = value;
|
|
599
|
+
if (record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string") {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const messages = record.messages.map(parseMessage);
|
|
603
|
+
if (messages.some((message) => message === null)) return null;
|
|
604
|
+
return {
|
|
605
|
+
messages: messages.filter((message) => message !== null),
|
|
606
|
+
pending: record.pending,
|
|
607
|
+
streamingText: record.streamingText
|
|
608
|
+
};
|
|
609
|
+
} catch {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conversation) {
|
|
614
|
+
if (typeof sessionStorage === "undefined") return;
|
|
615
|
+
sessionStorage.setItem(
|
|
616
|
+
conversationKey(storageKeyPrefix, visitorSessionId),
|
|
617
|
+
JSON.stringify({ version: CONVERSATION_VERSION, ...conversation })
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
621
|
+
if (typeof sessionStorage === "undefined") return;
|
|
622
|
+
sessionStorage.removeItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
623
|
+
}
|
|
624
|
+
|
|
453
625
|
// src/react/hooks/useAgentChat.ts
|
|
454
626
|
var GREETING_MESSAGE = {
|
|
455
627
|
id: "greeting",
|
|
@@ -466,6 +638,15 @@ var INITIAL_STATE = {
|
|
|
466
638
|
streamingText: "",
|
|
467
639
|
error: null
|
|
468
640
|
};
|
|
641
|
+
function stateFromConversation(conversation) {
|
|
642
|
+
if (!conversation || conversation.messages.length === 0) return INITIAL_STATE;
|
|
643
|
+
return {
|
|
644
|
+
...INITIAL_STATE,
|
|
645
|
+
messages: conversation.messages,
|
|
646
|
+
phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
|
|
647
|
+
streamingText: conversation.streamingText
|
|
648
|
+
};
|
|
649
|
+
}
|
|
469
650
|
var STATUS_SEQUENCE = [
|
|
470
651
|
{ id: "s1", label: "Starting Eve session", ms: 400 },
|
|
471
652
|
{ id: "s2", label: "Connecting to runtime", ms: 500 }
|
|
@@ -497,8 +678,6 @@ function useAgentChat({
|
|
|
497
678
|
visitorSessionId,
|
|
498
679
|
storageKeyPrefix
|
|
499
680
|
}) {
|
|
500
|
-
const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
|
|
501
|
-
const runRef = (0, import_react.useRef)(null);
|
|
502
681
|
const resolvedStorageKeyPrefix = (0, import_react.useMemo)(
|
|
503
682
|
() => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, version, runtimeOrigin }),
|
|
504
683
|
[customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
|
|
@@ -507,6 +686,12 @@ function useAgentChat({
|
|
|
507
686
|
() => visitorSessionId?.trim() || getOrCreateVisitorSessionId({ storageKeyPrefix: resolvedStorageKeyPrefix }),
|
|
508
687
|
[resolvedStorageKeyPrefix, visitorSessionId]
|
|
509
688
|
);
|
|
689
|
+
const [state, setState] = (0, import_react.useState)(
|
|
690
|
+
() => stateFromConversation(
|
|
691
|
+
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
|
|
692
|
+
)
|
|
693
|
+
);
|
|
694
|
+
const runRef = (0, import_react.useRef)(null);
|
|
510
695
|
const clientRef = (0, import_react.useRef)(
|
|
511
696
|
createAgentClient({
|
|
512
697
|
customerId,
|
|
@@ -517,20 +702,15 @@ function useAgentChat({
|
|
|
517
702
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
518
703
|
})
|
|
519
704
|
);
|
|
520
|
-
const identityRef = (0, import_react.useRef)(null);
|
|
521
705
|
const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
|
|
706
|
+
const identityRef = (0, import_react.useRef)(identityKey);
|
|
522
707
|
(0, import_react.useEffect)(() => {
|
|
523
|
-
if (identityRef.current === null) {
|
|
524
|
-
identityRef.current = identityKey;
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
708
|
if (identityRef.current === identityKey) {
|
|
528
709
|
return;
|
|
529
710
|
}
|
|
530
711
|
identityRef.current = identityKey;
|
|
531
712
|
runRef.current?.abort();
|
|
532
713
|
runRef.current = null;
|
|
533
|
-
clientRef.current.reset();
|
|
534
714
|
clientRef.current = createAgentClient({
|
|
535
715
|
customerId,
|
|
536
716
|
indexId,
|
|
@@ -539,81 +719,81 @@ function useAgentChat({
|
|
|
539
719
|
visitorSessionId: visitorId,
|
|
540
720
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
541
721
|
});
|
|
542
|
-
setState(
|
|
722
|
+
setState(
|
|
723
|
+
stateFromConversation(
|
|
724
|
+
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
|
|
725
|
+
)
|
|
726
|
+
);
|
|
543
727
|
}, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]);
|
|
728
|
+
(0, import_react.useEffect)(() => {
|
|
729
|
+
if (!hasVisitorMessages(state.messages)) return;
|
|
730
|
+
savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
|
|
731
|
+
messages: state.messages,
|
|
732
|
+
pending: isAgentBusy(state.phase),
|
|
733
|
+
streamingText: state.streamingText
|
|
734
|
+
});
|
|
735
|
+
}, [resolvedStorageKeyPrefix, state.messages, state.phase, state.streamingText, visitorId]);
|
|
544
736
|
const reset = (0, import_react.useCallback)(() => {
|
|
545
737
|
runRef.current?.abort();
|
|
546
738
|
runRef.current = null;
|
|
547
739
|
clientRef.current.reset();
|
|
740
|
+
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
548
741
|
setState(INITIAL_STATE);
|
|
549
|
-
}, []);
|
|
550
|
-
const
|
|
551
|
-
async (
|
|
552
|
-
|
|
553
|
-
clientRef.current.cancelActive();
|
|
554
|
-
const controller = new AbortController();
|
|
555
|
-
runRef.current = controller;
|
|
742
|
+
}, [resolvedStorageKeyPrefix, visitorId]);
|
|
743
|
+
const runTurn = (0, import_react.useCallback)(
|
|
744
|
+
async (input) => {
|
|
745
|
+
const { controller, initialText = "", resume, visitorText } = input;
|
|
556
746
|
const { signal } = controller;
|
|
557
747
|
const isActiveRun = () => runRef.current === controller && !signal.aborted;
|
|
558
|
-
const visitorMessage = {
|
|
559
|
-
id: `visitor-${Date.now()}`,
|
|
560
|
-
role: "visitor",
|
|
561
|
-
text: visitorText,
|
|
562
|
-
createdAt: Date.now()
|
|
563
|
-
};
|
|
564
|
-
setState((prev) => ({
|
|
565
|
-
...prev,
|
|
566
|
-
phase: "thinking",
|
|
567
|
-
messages: [...prev.messages, visitorMessage],
|
|
568
|
-
toolSteps: [{ id: "s1", label: "Starting Eve session", state: "active" }],
|
|
569
|
-
journey: null,
|
|
570
|
-
followUps: [],
|
|
571
|
-
streamingText: "",
|
|
572
|
-
error: null
|
|
573
|
-
}));
|
|
574
748
|
try {
|
|
575
|
-
let streamStarted =
|
|
576
|
-
|
|
749
|
+
let streamStarted = Boolean(initialText);
|
|
750
|
+
let streamed = initialText;
|
|
751
|
+
const planningPromise = resume ? Promise.resolve() : runStatusSequence(signal, (step) => {
|
|
577
752
|
if (streamStarted || !isActiveRun()) return;
|
|
578
753
|
setState((prev) => ({ ...prev, phase: "running-tools", toolSteps: [step] }));
|
|
579
754
|
});
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
755
|
+
const handlers = {
|
|
756
|
+
onStep: (label, detail) => {
|
|
757
|
+
if (streamStarted || !isActiveRun()) return;
|
|
758
|
+
setState((prev) => ({
|
|
759
|
+
...prev,
|
|
760
|
+
phase: "running-tools",
|
|
761
|
+
toolSteps: [{ id: `step-${label}`, label, detail, state: "active" }]
|
|
762
|
+
}));
|
|
763
|
+
},
|
|
764
|
+
onDelta: (delta) => {
|
|
765
|
+
if (!isActiveRun()) return;
|
|
766
|
+
void planningPromise.catch(() => {
|
|
767
|
+
});
|
|
768
|
+
if (!streamStarted) {
|
|
769
|
+
streamStarted = true;
|
|
586
770
|
setState((prev) => ({
|
|
587
771
|
...prev,
|
|
588
|
-
phase: "
|
|
589
|
-
toolSteps: [
|
|
772
|
+
phase: "streaming",
|
|
773
|
+
toolSteps: [],
|
|
774
|
+
streamingText: ""
|
|
590
775
|
}));
|
|
591
|
-
},
|
|
592
|
-
onDelta: (delta) => {
|
|
593
|
-
if (!isActiveRun()) return;
|
|
594
|
-
void planningPromise.catch(() => {
|
|
595
|
-
});
|
|
596
|
-
if (!streamStarted) {
|
|
597
|
-
streamStarted = true;
|
|
598
|
-
setState((prev) => ({
|
|
599
|
-
...prev,
|
|
600
|
-
phase: "streaming",
|
|
601
|
-
toolSteps: [],
|
|
602
|
-
streamingText: ""
|
|
603
|
-
}));
|
|
604
|
-
}
|
|
605
|
-
streamed += delta;
|
|
606
|
-
setState((prev) => ({ ...prev, phase: "streaming", streamingText: streamed }));
|
|
607
|
-
},
|
|
608
|
-
onComplete: () => {
|
|
609
|
-
if (!isActiveRun()) return;
|
|
610
|
-
streamStarted = true;
|
|
611
776
|
}
|
|
777
|
+
streamed += delta;
|
|
778
|
+
setState((prev) => ({ ...prev, phase: "streaming", streamingText: streamed }));
|
|
779
|
+
},
|
|
780
|
+
onComplete: () => {
|
|
781
|
+
if (!isActiveRun()) return;
|
|
782
|
+
streamStarted = true;
|
|
612
783
|
}
|
|
613
|
-
}
|
|
784
|
+
};
|
|
785
|
+
let finalText = resume ? await clientRef.current.resumeTurn({
|
|
786
|
+
handlers,
|
|
787
|
+
initialText,
|
|
788
|
+
message: visitorText,
|
|
789
|
+
signal
|
|
790
|
+
}) : await clientRef.current.sendTurn(visitorText, { handlers, signal });
|
|
791
|
+
if (resume && finalText === null) {
|
|
792
|
+
finalText = await clientRef.current.sendTurn(visitorText, { handlers, signal });
|
|
793
|
+
}
|
|
614
794
|
await planningPromise.catch(() => {
|
|
615
795
|
});
|
|
616
|
-
if (!isActiveRun()) return;
|
|
796
|
+
if (!isActiveRun() || finalText === null) return;
|
|
617
797
|
const agentMessage = {
|
|
618
798
|
id: `agent-${Date.now()}`,
|
|
619
799
|
role: "agent",
|
|
@@ -629,6 +809,7 @@ function useAgentChat({
|
|
|
629
809
|
followUps: [],
|
|
630
810
|
journey: null
|
|
631
811
|
}));
|
|
812
|
+
runRef.current = null;
|
|
632
813
|
} catch (error) {
|
|
633
814
|
if (error instanceof DOMException && error.name === "AbortError") return;
|
|
634
815
|
if (!isActiveRun()) return;
|
|
@@ -641,14 +822,66 @@ function useAgentChat({
|
|
|
641
822
|
streamingText: "",
|
|
642
823
|
error: message
|
|
643
824
|
}));
|
|
825
|
+
runRef.current = null;
|
|
644
826
|
}
|
|
645
827
|
},
|
|
646
|
-
[
|
|
828
|
+
[]
|
|
829
|
+
);
|
|
830
|
+
const submit = (0, import_react.useCallback)(
|
|
831
|
+
async (visitorText) => {
|
|
832
|
+
if (runRef.current) {
|
|
833
|
+
runRef.current.abort();
|
|
834
|
+
clientRef.current.cancelActive();
|
|
835
|
+
}
|
|
836
|
+
const controller = new AbortController();
|
|
837
|
+
runRef.current = controller;
|
|
838
|
+
const visitorMessage = {
|
|
839
|
+
id: `visitor-${Date.now()}`,
|
|
840
|
+
role: "visitor",
|
|
841
|
+
text: visitorText,
|
|
842
|
+
createdAt: Date.now()
|
|
843
|
+
};
|
|
844
|
+
setState((prev) => ({
|
|
845
|
+
...prev,
|
|
846
|
+
phase: "thinking",
|
|
847
|
+
messages: [...prev.messages, visitorMessage],
|
|
848
|
+
toolSteps: [{ id: "s1", label: "Starting Eve session", state: "active" }],
|
|
849
|
+
journey: null,
|
|
850
|
+
followUps: [],
|
|
851
|
+
streamingText: "",
|
|
852
|
+
error: null
|
|
853
|
+
}));
|
|
854
|
+
await runTurn({ controller, resume: false, visitorText });
|
|
855
|
+
},
|
|
856
|
+
[runTurn]
|
|
647
857
|
);
|
|
858
|
+
(0, import_react.useEffect)(() => {
|
|
859
|
+
const conversation = loadPersistedAgentConversation(
|
|
860
|
+
resolvedStorageKeyPrefix,
|
|
861
|
+
visitorId
|
|
862
|
+
);
|
|
863
|
+
if (!conversation?.pending) return;
|
|
864
|
+
const visitorMessage = [...conversation.messages].reverse().find((message) => message.role === "visitor");
|
|
865
|
+
if (!visitorMessage) return;
|
|
866
|
+
const controller = new AbortController();
|
|
867
|
+
runRef.current = controller;
|
|
868
|
+
void runTurn({
|
|
869
|
+
controller,
|
|
870
|
+
initialText: conversation.streamingText,
|
|
871
|
+
resume: true,
|
|
872
|
+
visitorText: visitorMessage.text
|
|
873
|
+
});
|
|
874
|
+
return () => {
|
|
875
|
+
if (runRef.current === controller) {
|
|
876
|
+
runRef.current = null;
|
|
877
|
+
}
|
|
878
|
+
controller.abort();
|
|
879
|
+
};
|
|
880
|
+
}, [identityKey, resolvedStorageKeyPrefix, runTurn, visitorId]);
|
|
648
881
|
(0, import_react.useEffect)(() => {
|
|
649
882
|
return () => {
|
|
650
883
|
runRef.current?.abort();
|
|
651
|
-
|
|
884
|
+
runRef.current = null;
|
|
652
885
|
};
|
|
653
886
|
}, []);
|
|
654
887
|
return {
|
|
@@ -669,6 +902,9 @@ function createIdleSuggestions() {
|
|
|
669
902
|
{ id: "idle-3", label: "What should I ask you?" }
|
|
670
903
|
];
|
|
671
904
|
}
|
|
905
|
+
function isAgentBusy(phase) {
|
|
906
|
+
return phase === "thinking" || phase === "running-tools" || phase === "streaming";
|
|
907
|
+
}
|
|
672
908
|
|
|
673
909
|
// src/react/hooks/useIsMobile.ts
|
|
674
910
|
var import_react2 = require("react");
|
|
@@ -873,15 +1109,27 @@ function FollowUpChips({
|
|
|
873
1109
|
}
|
|
874
1110
|
|
|
875
1111
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
1112
|
+
var import_streamdown = require("streamdown");
|
|
1113
|
+
var import_styles = require("streamdown/styles.css");
|
|
876
1114
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
877
1115
|
function MessageBubble({ message }) {
|
|
878
1116
|
if (message.role === "visitor") {
|
|
879
1117
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "message-bubble__text", children: message.text }) });
|
|
880
1118
|
}
|
|
881
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime5.
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1120
|
+
import_streamdown.Streamdown,
|
|
1121
|
+
{
|
|
1122
|
+
animated: true,
|
|
1123
|
+
caret: "circle",
|
|
1124
|
+
className: "message-bubble__markdown",
|
|
1125
|
+
controls: false,
|
|
1126
|
+
isAnimating: message.streaming,
|
|
1127
|
+
linkSafety: { enabled: false },
|
|
1128
|
+
mode: message.streaming ? "streaming" : "static",
|
|
1129
|
+
skipHtml: true,
|
|
1130
|
+
children: message.text
|
|
1131
|
+
}
|
|
1132
|
+
) }) });
|
|
885
1133
|
}
|
|
886
1134
|
|
|
887
1135
|
// src/react/components/AgentRail/AgentRail.tsx
|