@webless/agent 0.2.10 → 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-DSNTEDXA.js → chunk-XAUJPXWR.js} +326 -93
- package/dist/chunk-XAUJPXWR.js.map +1 -0
- package/dist/embed.cjs +328 -92
- package/dist/embed.cjs.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 +325 -92
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DSNTEDXA.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -221,6 +221,54 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
// src/runtime/client.ts
|
|
224
|
+
function isTurnBoundary(event) {
|
|
225
|
+
return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
|
|
226
|
+
}
|
|
227
|
+
function applyMessageEvent(event, rendered, handlers) {
|
|
228
|
+
const step = mapStepLabel(event);
|
|
229
|
+
if (step) handlers.onStep?.(step.label, step.detail);
|
|
230
|
+
if (event.type === "session.failed") {
|
|
231
|
+
throw new Error(event.data.message || event.data.code);
|
|
232
|
+
}
|
|
233
|
+
if (event.type === "message.completed") {
|
|
234
|
+
handlers.onComplete?.();
|
|
235
|
+
}
|
|
236
|
+
if (event.type !== "message.appended") return rendered;
|
|
237
|
+
const { messageDelta, messageSoFar } = event.data;
|
|
238
|
+
let delta = messageDelta;
|
|
239
|
+
let next = rendered;
|
|
240
|
+
if (messageSoFar.startsWith(rendered)) {
|
|
241
|
+
delta = messageSoFar.slice(rendered.length);
|
|
242
|
+
next = messageSoFar;
|
|
243
|
+
} else if (messageDelta) {
|
|
244
|
+
next += messageDelta;
|
|
245
|
+
}
|
|
246
|
+
if (delta) handlers.onDelta(delta);
|
|
247
|
+
return next;
|
|
248
|
+
}
|
|
249
|
+
function latestTurnEvents(events) {
|
|
250
|
+
let startIndex = -1;
|
|
251
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
252
|
+
if (events[index]?.type === "message.received") {
|
|
253
|
+
startIndex = index;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return startIndex >= 0 ? events.slice(startIndex) : [];
|
|
258
|
+
}
|
|
259
|
+
function renderTurn(events) {
|
|
260
|
+
let rendered = "";
|
|
261
|
+
for (const event of events) {
|
|
262
|
+
if (event.type !== "message.appended") continue;
|
|
263
|
+
const { messageDelta, messageSoFar } = event.data;
|
|
264
|
+
if (messageSoFar.startsWith(rendered)) {
|
|
265
|
+
rendered = messageSoFar;
|
|
266
|
+
} else if (messageDelta) {
|
|
267
|
+
rendered += messageDelta;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return rendered;
|
|
271
|
+
}
|
|
224
272
|
function mapStepLabel(event) {
|
|
225
273
|
if (event.type !== "step.started") return null;
|
|
226
274
|
const stepIndex = event.data.stepIndex;
|
|
@@ -256,8 +304,13 @@ var AgentSession = class {
|
|
|
256
304
|
return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
|
|
257
305
|
}
|
|
258
306
|
reset() {
|
|
259
|
-
|
|
260
|
-
|
|
307
|
+
if (this.activeResponse) {
|
|
308
|
+
void this.activeResponse.cancel().catch(() => {
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
void this.session?.cancel().catch(() => {
|
|
312
|
+
});
|
|
313
|
+
}
|
|
261
314
|
this.activeResponse = void 0;
|
|
262
315
|
this.session = void 0;
|
|
263
316
|
clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
@@ -282,7 +335,8 @@ var AgentSession = class {
|
|
|
282
335
|
if (this.client && this.clientHost === config.host) {
|
|
283
336
|
return this.client;
|
|
284
337
|
}
|
|
285
|
-
this.
|
|
338
|
+
this.activeResponse = void 0;
|
|
339
|
+
this.session = void 0;
|
|
286
340
|
this.client = new import_client2.Client({
|
|
287
341
|
auth: { bearer: () => this.capability.getAccessToken() },
|
|
288
342
|
host: config.host,
|
|
@@ -331,28 +385,20 @@ var AgentSession = class {
|
|
|
331
385
|
this.persistSessionCursor(session);
|
|
332
386
|
}
|
|
333
387
|
this.activeResponse = response;
|
|
388
|
+
let streamIndex = session?.state.streamIndex ?? 0;
|
|
334
389
|
let rendered = "";
|
|
335
390
|
try {
|
|
336
391
|
for await (const event of response) {
|
|
337
392
|
if (signal.aborted) break;
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
rendered += messageDelta;
|
|
348
|
-
}
|
|
349
|
-
if (delta) handlers.onDelta(delta);
|
|
350
|
-
}
|
|
351
|
-
if (event.type === "message.completed") {
|
|
352
|
-
handlers.onComplete?.();
|
|
353
|
-
}
|
|
354
|
-
if (event.type === "session.failed") {
|
|
355
|
-
throw new Error(event.data.message || event.data.code);
|
|
393
|
+
rendered = applyMessageEvent(event, rendered, handlers);
|
|
394
|
+
streamIndex += 1;
|
|
395
|
+
if (session) {
|
|
396
|
+
savePersistedAgentSession(
|
|
397
|
+
this.visitorSessionId,
|
|
398
|
+
session.state.sessionId,
|
|
399
|
+
streamIndex,
|
|
400
|
+
this.storeOptions
|
|
401
|
+
);
|
|
356
402
|
}
|
|
357
403
|
}
|
|
358
404
|
} finally {
|
|
@@ -364,15 +410,83 @@ var AgentSession = class {
|
|
|
364
410
|
if (!rendered.trim() && !signal.aborted) {
|
|
365
411
|
throw new Error("Empty response from runtime");
|
|
366
412
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
413
|
+
return rendered.trim();
|
|
414
|
+
}
|
|
415
|
+
async resumeTurn(message, signal, handlers, initialText = "") {
|
|
416
|
+
const persisted = loadPersistedAgentSession(this.visitorSessionId, this.storeOptions);
|
|
417
|
+
if (!persisted) return null;
|
|
418
|
+
const client = this.ensureClient();
|
|
419
|
+
const attached = client.sessions.attach(persisted.sessionId, {
|
|
420
|
+
streamIndex: persisted.streamIndex
|
|
421
|
+
});
|
|
422
|
+
const snapshot = await withCapabilityRefresh(
|
|
423
|
+
this.capability,
|
|
424
|
+
() => attached.snapshot({ signal })
|
|
425
|
+
);
|
|
426
|
+
const turnEvents = latestTurnEvents(snapshot.events);
|
|
427
|
+
const received = turnEvents[0];
|
|
428
|
+
if (received?.type !== "message.received" || received.data.message !== message) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
let rendered = renderTurn(turnEvents);
|
|
432
|
+
if (rendered.startsWith(initialText)) {
|
|
433
|
+
const missedText = rendered.slice(initialText.length);
|
|
434
|
+
if (missedText) handlers.onDelta(missedText);
|
|
435
|
+
} else if (initialText.startsWith(rendered)) {
|
|
436
|
+
rendered = initialText;
|
|
437
|
+
} else if (!initialText.startsWith(rendered)) {
|
|
438
|
+
rendered = initialText + rendered;
|
|
439
|
+
}
|
|
440
|
+
let session = client.sessions.attach(snapshot.session.sessionId, {
|
|
441
|
+
streamIndex: snapshot.session.streamIndex
|
|
442
|
+
});
|
|
443
|
+
this.session = session;
|
|
444
|
+
this.persistSessionCursor(session);
|
|
445
|
+
let snapshotBoundary;
|
|
446
|
+
for (let index = turnEvents.length - 1; index >= 0; index -= 1) {
|
|
447
|
+
const event = turnEvents[index];
|
|
448
|
+
if (event && isTurnBoundary(event)) {
|
|
449
|
+
snapshotBoundary = event;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (snapshotBoundary) {
|
|
454
|
+
if (snapshotBoundary.type === "session.failed") {
|
|
455
|
+
throw new Error(snapshotBoundary.data.message || snapshotBoundary.data.code);
|
|
456
|
+
}
|
|
457
|
+
handlers.onComplete?.();
|
|
458
|
+
if (!rendered.trim()) throw new Error("Empty response from runtime");
|
|
459
|
+
return rendered.trim();
|
|
460
|
+
}
|
|
461
|
+
let streamIndex = snapshot.session.streamIndex;
|
|
462
|
+
for await (const event of session.stream({ signal })) {
|
|
463
|
+
if (signal.aborted) break;
|
|
464
|
+
rendered = applyMessageEvent(event, rendered, handlers);
|
|
465
|
+
streamIndex += 1;
|
|
466
|
+
savePersistedAgentSession(
|
|
467
|
+
this.visitorSessionId,
|
|
468
|
+
session.state.sessionId,
|
|
469
|
+
streamIndex,
|
|
470
|
+
this.storeOptions
|
|
471
|
+
);
|
|
472
|
+
if (isTurnBoundary(event)) break;
|
|
473
|
+
}
|
|
474
|
+
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
475
|
+
this.session = session;
|
|
476
|
+
this.persistSessionCursor(session);
|
|
477
|
+
if (!rendered.trim() && !signal.aborted) {
|
|
478
|
+
throw new Error("Empty response from runtime");
|
|
370
479
|
}
|
|
371
480
|
return rendered.trim();
|
|
372
481
|
}
|
|
373
482
|
cancelActive() {
|
|
374
|
-
this.activeResponse
|
|
375
|
-
|
|
483
|
+
if (this.activeResponse) {
|
|
484
|
+
this.activeResponse.cancel().catch(() => {
|
|
485
|
+
});
|
|
486
|
+
} else {
|
|
487
|
+
this.session?.cancel().catch(() => {
|
|
488
|
+
});
|
|
489
|
+
}
|
|
376
490
|
}
|
|
377
491
|
};
|
|
378
492
|
function createAgentClient(options) {
|
|
@@ -406,6 +520,12 @@ function createAgentClient(options) {
|
|
|
406
520
|
sendOptions.signal ?? new AbortController().signal,
|
|
407
521
|
sendOptions.handlers
|
|
408
522
|
),
|
|
523
|
+
resumeTurn: (resumeOptions) => session.resumeTurn(
|
|
524
|
+
resumeOptions.message,
|
|
525
|
+
resumeOptions.signal ?? new AbortController().signal,
|
|
526
|
+
resumeOptions.handlers,
|
|
527
|
+
resumeOptions.initialText
|
|
528
|
+
),
|
|
409
529
|
reset: () => session.reset(),
|
|
410
530
|
cancelActive: () => session.cancelActive(),
|
|
411
531
|
getActiveSessionId: () => session.getActiveSessionId()
|
|
@@ -434,6 +554,58 @@ function formatAgentError(error) {
|
|
|
434
554
|
return "Runtime request failed";
|
|
435
555
|
}
|
|
436
556
|
|
|
557
|
+
// src/react/persisted-conversation.ts
|
|
558
|
+
var CONVERSATION_VERSION = 1;
|
|
559
|
+
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
560
|
+
return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
|
|
561
|
+
}
|
|
562
|
+
function parseMessage(value) {
|
|
563
|
+
if (typeof value !== "object" || value === null) return null;
|
|
564
|
+
const record = value;
|
|
565
|
+
if (typeof record.id !== "string" || record.role !== "agent" && record.role !== "visitor" || typeof record.text !== "string" || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt)) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
id: record.id,
|
|
570
|
+
role: record.role,
|
|
571
|
+
text: record.text,
|
|
572
|
+
createdAt: record.createdAt
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
576
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
577
|
+
const raw = sessionStorage.getItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
578
|
+
if (!raw) return null;
|
|
579
|
+
try {
|
|
580
|
+
const value = JSON.parse(raw);
|
|
581
|
+
if (typeof value !== "object" || value === null) return null;
|
|
582
|
+
const record = value;
|
|
583
|
+
if (record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string") {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
const messages = record.messages.map(parseMessage);
|
|
587
|
+
if (messages.some((message) => message === null)) return null;
|
|
588
|
+
return {
|
|
589
|
+
messages: messages.filter((message) => message !== null),
|
|
590
|
+
pending: record.pending,
|
|
591
|
+
streamingText: record.streamingText
|
|
592
|
+
};
|
|
593
|
+
} catch {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conversation) {
|
|
598
|
+
if (typeof sessionStorage === "undefined") return;
|
|
599
|
+
sessionStorage.setItem(
|
|
600
|
+
conversationKey(storageKeyPrefix, visitorSessionId),
|
|
601
|
+
JSON.stringify({ version: CONVERSATION_VERSION, ...conversation })
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
605
|
+
if (typeof sessionStorage === "undefined") return;
|
|
606
|
+
sessionStorage.removeItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
607
|
+
}
|
|
608
|
+
|
|
437
609
|
// src/react/hooks/useAgentChat.ts
|
|
438
610
|
var GREETING_MESSAGE = {
|
|
439
611
|
id: "greeting",
|
|
@@ -450,6 +622,15 @@ var INITIAL_STATE = {
|
|
|
450
622
|
streamingText: "",
|
|
451
623
|
error: null
|
|
452
624
|
};
|
|
625
|
+
function stateFromConversation(conversation) {
|
|
626
|
+
if (!conversation || conversation.messages.length === 0) return INITIAL_STATE;
|
|
627
|
+
return {
|
|
628
|
+
...INITIAL_STATE,
|
|
629
|
+
messages: conversation.messages,
|
|
630
|
+
phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
|
|
631
|
+
streamingText: conversation.streamingText
|
|
632
|
+
};
|
|
633
|
+
}
|
|
453
634
|
var STATUS_SEQUENCE = [
|
|
454
635
|
{ id: "s1", label: "Starting Eve session", ms: 400 },
|
|
455
636
|
{ id: "s2", label: "Connecting to runtime", ms: 500 }
|
|
@@ -481,8 +662,6 @@ function useAgentChat({
|
|
|
481
662
|
visitorSessionId,
|
|
482
663
|
storageKeyPrefix
|
|
483
664
|
}) {
|
|
484
|
-
const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
|
|
485
|
-
const runRef = (0, import_react.useRef)(null);
|
|
486
665
|
const resolvedStorageKeyPrefix = (0, import_react.useMemo)(
|
|
487
666
|
() => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, version, runtimeOrigin }),
|
|
488
667
|
[customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
|
|
@@ -491,6 +670,12 @@ function useAgentChat({
|
|
|
491
670
|
() => visitorSessionId?.trim() || getOrCreateVisitorSessionId({ storageKeyPrefix: resolvedStorageKeyPrefix }),
|
|
492
671
|
[resolvedStorageKeyPrefix, visitorSessionId]
|
|
493
672
|
);
|
|
673
|
+
const [state, setState] = (0, import_react.useState)(
|
|
674
|
+
() => stateFromConversation(
|
|
675
|
+
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
|
|
676
|
+
)
|
|
677
|
+
);
|
|
678
|
+
const runRef = (0, import_react.useRef)(null);
|
|
494
679
|
const clientRef = (0, import_react.useRef)(
|
|
495
680
|
createAgentClient({
|
|
496
681
|
customerId,
|
|
@@ -501,20 +686,15 @@ function useAgentChat({
|
|
|
501
686
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
502
687
|
})
|
|
503
688
|
);
|
|
504
|
-
const identityRef = (0, import_react.useRef)(null);
|
|
505
689
|
const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
|
|
690
|
+
const identityRef = (0, import_react.useRef)(identityKey);
|
|
506
691
|
(0, import_react.useEffect)(() => {
|
|
507
|
-
if (identityRef.current === null) {
|
|
508
|
-
identityRef.current = identityKey;
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
692
|
if (identityRef.current === identityKey) {
|
|
512
693
|
return;
|
|
513
694
|
}
|
|
514
695
|
identityRef.current = identityKey;
|
|
515
696
|
runRef.current?.abort();
|
|
516
697
|
runRef.current = null;
|
|
517
|
-
clientRef.current.reset();
|
|
518
698
|
clientRef.current = createAgentClient({
|
|
519
699
|
customerId,
|
|
520
700
|
indexId,
|
|
@@ -523,81 +703,81 @@ function useAgentChat({
|
|
|
523
703
|
visitorSessionId: visitorId,
|
|
524
704
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
525
705
|
});
|
|
526
|
-
setState(
|
|
706
|
+
setState(
|
|
707
|
+
stateFromConversation(
|
|
708
|
+
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId)
|
|
709
|
+
)
|
|
710
|
+
);
|
|
527
711
|
}, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]);
|
|
712
|
+
(0, import_react.useEffect)(() => {
|
|
713
|
+
if (!hasVisitorMessages(state.messages)) return;
|
|
714
|
+
savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
|
|
715
|
+
messages: state.messages,
|
|
716
|
+
pending: isAgentBusy(state.phase),
|
|
717
|
+
streamingText: state.streamingText
|
|
718
|
+
});
|
|
719
|
+
}, [resolvedStorageKeyPrefix, state.messages, state.phase, state.streamingText, visitorId]);
|
|
528
720
|
const reset = (0, import_react.useCallback)(() => {
|
|
529
721
|
runRef.current?.abort();
|
|
530
722
|
runRef.current = null;
|
|
531
723
|
clientRef.current.reset();
|
|
724
|
+
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
532
725
|
setState(INITIAL_STATE);
|
|
533
|
-
}, []);
|
|
534
|
-
const
|
|
535
|
-
async (
|
|
536
|
-
|
|
537
|
-
clientRef.current.cancelActive();
|
|
538
|
-
const controller = new AbortController();
|
|
539
|
-
runRef.current = controller;
|
|
726
|
+
}, [resolvedStorageKeyPrefix, visitorId]);
|
|
727
|
+
const runTurn = (0, import_react.useCallback)(
|
|
728
|
+
async (input) => {
|
|
729
|
+
const { controller, initialText = "", resume, visitorText } = input;
|
|
540
730
|
const { signal } = controller;
|
|
541
731
|
const isActiveRun = () => runRef.current === controller && !signal.aborted;
|
|
542
|
-
const visitorMessage = {
|
|
543
|
-
id: `visitor-${Date.now()}`,
|
|
544
|
-
role: "visitor",
|
|
545
|
-
text: visitorText,
|
|
546
|
-
createdAt: Date.now()
|
|
547
|
-
};
|
|
548
|
-
setState((prev) => ({
|
|
549
|
-
...prev,
|
|
550
|
-
phase: "thinking",
|
|
551
|
-
messages: [...prev.messages, visitorMessage],
|
|
552
|
-
toolSteps: [{ id: "s1", label: "Starting Eve session", state: "active" }],
|
|
553
|
-
journey: null,
|
|
554
|
-
followUps: [],
|
|
555
|
-
streamingText: "",
|
|
556
|
-
error: null
|
|
557
|
-
}));
|
|
558
732
|
try {
|
|
559
|
-
let streamStarted =
|
|
560
|
-
|
|
733
|
+
let streamStarted = Boolean(initialText);
|
|
734
|
+
let streamed = initialText;
|
|
735
|
+
const planningPromise = resume ? Promise.resolve() : runStatusSequence(signal, (step) => {
|
|
561
736
|
if (streamStarted || !isActiveRun()) return;
|
|
562
737
|
setState((prev) => ({ ...prev, phase: "running-tools", toolSteps: [step] }));
|
|
563
738
|
});
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
739
|
+
const handlers = {
|
|
740
|
+
onStep: (label, detail) => {
|
|
741
|
+
if (streamStarted || !isActiveRun()) return;
|
|
742
|
+
setState((prev) => ({
|
|
743
|
+
...prev,
|
|
744
|
+
phase: "running-tools",
|
|
745
|
+
toolSteps: [{ id: `step-${label}`, label, detail, state: "active" }]
|
|
746
|
+
}));
|
|
747
|
+
},
|
|
748
|
+
onDelta: (delta) => {
|
|
749
|
+
if (!isActiveRun()) return;
|
|
750
|
+
void planningPromise.catch(() => {
|
|
751
|
+
});
|
|
752
|
+
if (!streamStarted) {
|
|
753
|
+
streamStarted = true;
|
|
570
754
|
setState((prev) => ({
|
|
571
755
|
...prev,
|
|
572
|
-
phase: "
|
|
573
|
-
toolSteps: [
|
|
756
|
+
phase: "streaming",
|
|
757
|
+
toolSteps: [],
|
|
758
|
+
streamingText: ""
|
|
574
759
|
}));
|
|
575
|
-
},
|
|
576
|
-
onDelta: (delta) => {
|
|
577
|
-
if (!isActiveRun()) return;
|
|
578
|
-
void planningPromise.catch(() => {
|
|
579
|
-
});
|
|
580
|
-
if (!streamStarted) {
|
|
581
|
-
streamStarted = true;
|
|
582
|
-
setState((prev) => ({
|
|
583
|
-
...prev,
|
|
584
|
-
phase: "streaming",
|
|
585
|
-
toolSteps: [],
|
|
586
|
-
streamingText: ""
|
|
587
|
-
}));
|
|
588
|
-
}
|
|
589
|
-
streamed += delta;
|
|
590
|
-
setState((prev) => ({ ...prev, phase: "streaming", streamingText: streamed }));
|
|
591
|
-
},
|
|
592
|
-
onComplete: () => {
|
|
593
|
-
if (!isActiveRun()) return;
|
|
594
|
-
streamStarted = true;
|
|
595
760
|
}
|
|
761
|
+
streamed += delta;
|
|
762
|
+
setState((prev) => ({ ...prev, phase: "streaming", streamingText: streamed }));
|
|
763
|
+
},
|
|
764
|
+
onComplete: () => {
|
|
765
|
+
if (!isActiveRun()) return;
|
|
766
|
+
streamStarted = true;
|
|
596
767
|
}
|
|
597
|
-
}
|
|
768
|
+
};
|
|
769
|
+
let finalText = resume ? await clientRef.current.resumeTurn({
|
|
770
|
+
handlers,
|
|
771
|
+
initialText,
|
|
772
|
+
message: visitorText,
|
|
773
|
+
signal
|
|
774
|
+
}) : await clientRef.current.sendTurn(visitorText, { handlers, signal });
|
|
775
|
+
if (resume && finalText === null) {
|
|
776
|
+
finalText = await clientRef.current.sendTurn(visitorText, { handlers, signal });
|
|
777
|
+
}
|
|
598
778
|
await planningPromise.catch(() => {
|
|
599
779
|
});
|
|
600
|
-
if (!isActiveRun()) return;
|
|
780
|
+
if (!isActiveRun() || finalText === null) return;
|
|
601
781
|
const agentMessage = {
|
|
602
782
|
id: `agent-${Date.now()}`,
|
|
603
783
|
role: "agent",
|
|
@@ -613,6 +793,7 @@ function useAgentChat({
|
|
|
613
793
|
followUps: [],
|
|
614
794
|
journey: null
|
|
615
795
|
}));
|
|
796
|
+
runRef.current = null;
|
|
616
797
|
} catch (error) {
|
|
617
798
|
if (error instanceof DOMException && error.name === "AbortError") return;
|
|
618
799
|
if (!isActiveRun()) return;
|
|
@@ -625,14 +806,66 @@ function useAgentChat({
|
|
|
625
806
|
streamingText: "",
|
|
626
807
|
error: message
|
|
627
808
|
}));
|
|
809
|
+
runRef.current = null;
|
|
628
810
|
}
|
|
629
811
|
},
|
|
630
|
-
[
|
|
812
|
+
[]
|
|
631
813
|
);
|
|
814
|
+
const submit = (0, import_react.useCallback)(
|
|
815
|
+
async (visitorText) => {
|
|
816
|
+
if (runRef.current) {
|
|
817
|
+
runRef.current.abort();
|
|
818
|
+
clientRef.current.cancelActive();
|
|
819
|
+
}
|
|
820
|
+
const controller = new AbortController();
|
|
821
|
+
runRef.current = controller;
|
|
822
|
+
const visitorMessage = {
|
|
823
|
+
id: `visitor-${Date.now()}`,
|
|
824
|
+
role: "visitor",
|
|
825
|
+
text: visitorText,
|
|
826
|
+
createdAt: Date.now()
|
|
827
|
+
};
|
|
828
|
+
setState((prev) => ({
|
|
829
|
+
...prev,
|
|
830
|
+
phase: "thinking",
|
|
831
|
+
messages: [...prev.messages, visitorMessage],
|
|
832
|
+
toolSteps: [{ id: "s1", label: "Starting Eve session", state: "active" }],
|
|
833
|
+
journey: null,
|
|
834
|
+
followUps: [],
|
|
835
|
+
streamingText: "",
|
|
836
|
+
error: null
|
|
837
|
+
}));
|
|
838
|
+
await runTurn({ controller, resume: false, visitorText });
|
|
839
|
+
},
|
|
840
|
+
[runTurn]
|
|
841
|
+
);
|
|
842
|
+
(0, import_react.useEffect)(() => {
|
|
843
|
+
const conversation = loadPersistedAgentConversation(
|
|
844
|
+
resolvedStorageKeyPrefix,
|
|
845
|
+
visitorId
|
|
846
|
+
);
|
|
847
|
+
if (!conversation?.pending) return;
|
|
848
|
+
const visitorMessage = [...conversation.messages].reverse().find((message) => message.role === "visitor");
|
|
849
|
+
if (!visitorMessage) return;
|
|
850
|
+
const controller = new AbortController();
|
|
851
|
+
runRef.current = controller;
|
|
852
|
+
void runTurn({
|
|
853
|
+
controller,
|
|
854
|
+
initialText: conversation.streamingText,
|
|
855
|
+
resume: true,
|
|
856
|
+
visitorText: visitorMessage.text
|
|
857
|
+
});
|
|
858
|
+
return () => {
|
|
859
|
+
if (runRef.current === controller) {
|
|
860
|
+
runRef.current = null;
|
|
861
|
+
}
|
|
862
|
+
controller.abort();
|
|
863
|
+
};
|
|
864
|
+
}, [identityKey, resolvedStorageKeyPrefix, runTurn, visitorId]);
|
|
632
865
|
(0, import_react.useEffect)(() => {
|
|
633
866
|
return () => {
|
|
634
867
|
runRef.current?.abort();
|
|
635
|
-
|
|
868
|
+
runRef.current = null;
|
|
636
869
|
};
|
|
637
870
|
}, []);
|
|
638
871
|
return {
|