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