@theoremjs/web 0.3.4-next.389.gecc7333 → 0.3.4-next.401.gec4c582
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/client/chat.d.ts +4 -3
- package/client/chat.js +24 -3
- package/package.json +2 -2
package/client/chat.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type AttachmentValidationIssue, type ComposerMenuAction, type ComposerP
|
|
|
2
2
|
import { type ClientFailure } from './failure.ts';
|
|
3
3
|
import { type PageTools } from './live/live-page-tool.ts';
|
|
4
4
|
import type { AnsweringGate, ToolDecisionAction } from './tool-resume.ts';
|
|
5
|
-
import type
|
|
5
|
+
import { type ConversationView, type TheoremTransport, type TurnEventSink } from './transport.ts';
|
|
6
6
|
export type ChatOptions = {
|
|
7
7
|
transport: TheoremTransport;
|
|
8
8
|
/** The profile's composer interface, as `transport.describe()` returns it. `null` while it loads. */
|
|
@@ -23,9 +23,10 @@ export type ChatOptions = {
|
|
|
23
23
|
initialText?: string;
|
|
24
24
|
/**
|
|
25
25
|
* Each new ref the host issues for the conversation: once the message posts, and again as the
|
|
26
|
-
* reply's runs are saved. Keep the latest to resume the chat later.
|
|
26
|
+
* reply's runs are saved. Keep the latest to resume the chat later. `undefined` when the host
|
|
27
|
+
* can no longer open the ref the chat resumed: the chat starts a new one, so forget it.
|
|
27
28
|
*/
|
|
28
|
-
onConversationChange?: (ref: string) => void;
|
|
29
|
+
onConversationChange?: (ref: string | undefined) => void;
|
|
29
30
|
/**
|
|
30
31
|
* The value chosen for each of the profile's `inputs.slots`. Sent with every turn. A slot you
|
|
31
32
|
* name here is fixed to that value; one you leave out is the visitor's to pick (the chat's
|
package/client/chat.js
CHANGED
|
@@ -9,6 +9,7 @@ import { warnPageToolMismatch } from "./live/live-page-tool.js";
|
|
|
9
9
|
import { withPageToolDispatch } from "./page-dispatch.js";
|
|
10
10
|
import { sessionAfter, stampWorked } from "./run-commit.js";
|
|
11
11
|
import { keepsConversation, resumeInterfaceTool, streamInterfaceDraftTurn, streamInterfaceTurn, } from "./run-session.js";
|
|
12
|
+
import { isUnknownConversation, } from "./transport.js";
|
|
12
13
|
import { foldAssistantTurn, issuedOperationId } from "./turn-client.js";
|
|
13
14
|
const DELIVERY_ORDER = ['sending', 'sent', 'delivered', 'read'];
|
|
14
15
|
/** The server's own reports (bookkeeping, a failure, the end), which say nothing of the model having the message. */
|
|
@@ -29,6 +30,16 @@ function deliveryOf(line) {
|
|
|
29
30
|
return SERVER_EVENTS.has(line.type) ? 'sent' : 'delivered';
|
|
30
31
|
}
|
|
31
32
|
/** The streamed blocks, ending on the turn's error unless the stream already carried it. */
|
|
33
|
+
/**
|
|
34
|
+
* `next` with the ids `shown` gave the blocks in the same places. The host mints its own ids, so a
|
|
35
|
+
* redraw from it would otherwise remount every message on screen, and cut short what eases there.
|
|
36
|
+
*/
|
|
37
|
+
function keepIds(shown, next) {
|
|
38
|
+
return next.map((block, index) => {
|
|
39
|
+
const was = shown[index];
|
|
40
|
+
return was && was.kind === block.kind && was.id !== block.id ? { ...block, id: was.id } : block;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
32
43
|
function withTurnError(blocks, error) {
|
|
33
44
|
if (blocks.some((block) => block.kind === 'error'))
|
|
34
45
|
return blocks;
|
|
@@ -359,9 +370,11 @@ class Chat {
|
|
|
359
370
|
}
|
|
360
371
|
/** Draws the conversation as the host shows it: its transcript, and the reply that waits on gates. */
|
|
361
372
|
#draw(view, session, events) {
|
|
373
|
+
const shown = [...this.#state.blocks, ...this.#state.streamBlocks];
|
|
374
|
+
const blocks = keepIds(shown, view.transcript);
|
|
362
375
|
this.#set({
|
|
363
|
-
blocks
|
|
364
|
-
streamBlocks: view.paused?.blocks ?? [],
|
|
376
|
+
blocks,
|
|
377
|
+
streamBlocks: keepIds(shown.slice(blocks.length), view.paused?.blocks ?? []),
|
|
365
378
|
// why: A reply at rest leaves the run's own events (tokens, an ask); a paused one, the host's.
|
|
366
379
|
session: sessionAfter(session, view.paused?.events ?? events),
|
|
367
380
|
chatStarted: view.transcript.length > 0 || view.paused !== undefined,
|
|
@@ -400,7 +413,15 @@ class Chat {
|
|
|
400
413
|
if (this.#state.blocks !== before || this.#busy)
|
|
401
414
|
return;
|
|
402
415
|
this.#draw(view, this.#state.session, []);
|
|
403
|
-
}, (err) =>
|
|
416
|
+
}, (err) => {
|
|
417
|
+
// why: Nobody asked for the lost conversation by hand; the chat just starts a new one.
|
|
418
|
+
if (!isUnknownConversation(err))
|
|
419
|
+
return this.#showFailure(turnFailure(err, iface.labels));
|
|
420
|
+
if (this.#conversation !== ref)
|
|
421
|
+
return;
|
|
422
|
+
this.#conversation = undefined;
|
|
423
|
+
this.#options.onConversationChange?.(undefined);
|
|
424
|
+
});
|
|
404
425
|
}
|
|
405
426
|
/**
|
|
406
427
|
* Settles a restored page gate as unanswered.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theoremjs/web",
|
|
3
|
-
"version": "0.3.4-next.
|
|
3
|
+
"version": "0.3.4-next.401.gec4c582",
|
|
4
4
|
"description": "Theorem on the web platform, with no UI framework: Web-standard server handlers that serve a profile, and the browser client that talks to them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,6 @@
|
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"zod": "^4.6.5",
|
|
28
|
-
"@theoremjs/agents": "^0.3.4-next.
|
|
28
|
+
"@theoremjs/agents": "^0.3.4-next.401.gec4c582"
|
|
29
29
|
}
|
|
30
30
|
}
|