@happyvertical/smrt-chat 0.51.38 → 0.52.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/AGENTS.md +19 -2
- package/README.md +13 -0
- package/dist/assistant-turn-events.d.ts +152 -0
- package/dist/assistant-turn-events.d.ts.map +1 -0
- package/dist/assistant-turn-events.js +2 -0
- package/dist/assistant-turn.d.ts +236 -0
- package/dist/assistant-turn.d.ts.map +1 -0
- package/dist/chunks/{ChatService-D48KBDZy.js → ChatService-jJi2JJJi.js} +2 -2
- package/dist/chunks/ChatService-jJi2JJJi.js.map +1 -0
- package/dist/chunks/assistant-turn-events-BbZUf-Ju.js +77 -0
- package/dist/chunks/assistant-turn-events-BbZUf-Ju.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +776 -104
- package/dist/index.js.map +1 -1
- package/dist/internal/agent-runtime.js +1 -1
- package/dist/manifest.json +6 -2
- package/dist/models/AgentSession.d.ts +11 -1
- package/dist/models/AgentSession.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +7 -13
- package/dist/svelte/components/assistant/AssistantChoiceCards.svelte +309 -0
- package/dist/svelte/components/assistant/AssistantChoiceCards.svelte.d.ts +19 -0
- package/dist/svelte/components/assistant/AssistantChoiceCards.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/AssistantComposer.svelte +116 -14
- package/dist/svelte/components/assistant/AssistantComposer.svelte.d.ts +28 -0
- package/dist/svelte/components/assistant/AssistantComposer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/AssistantDock.svelte +196 -1
- package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts +38 -1
- package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/AssistantThreadList.svelte +13 -1
- package/dist/svelte/components/assistant/__tests__/AssistantDock.test.js +16 -16
- package/dist/svelte/components/assistant/__tests__/assistant-composer-dictation.test.js +197 -0
- package/dist/svelte/components/assistant/__tests__/assistant-dock-choices-pending.test.js +224 -0
- package/dist/svelte/components/assistant/__tests__/assistant-dock-choices-preview.test.js +312 -0
- package/dist/svelte/components/assistant/__tests__/assistant-dock-choices.test.js +338 -0
- package/dist/svelte/components/assistant/__tests__/assistant-dock-client-tools.test.js +442 -0
- package/dist/svelte/components/assistant/__tests__/assistant-dock-run.test.js +303 -0
- package/dist/svelte/components/assistant/__tests__/assistant-dock-settle.test.js +146 -0
- package/dist/svelte/components/assistant/assistant-choices.svelte.d.ts +251 -0
- package/dist/svelte/components/assistant/assistant-choices.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/assistant-choices.svelte.js +520 -0
- package/dist/svelte/components/assistant/assistant-transport.d.ts +54 -1
- package/dist/svelte/components/assistant/assistant-transport.d.ts.map +1 -1
- package/dist/svelte/components/assistant/assistant-transport.js +64 -0
- package/dist/svelte/components/assistant/client-tools.d.ts +85 -0
- package/dist/svelte/components/assistant/client-tools.d.ts.map +1 -0
- package/dist/svelte/components/assistant/client-tools.js +61 -0
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts +159 -0
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.js +0 -0
- package/dist/svelte/i18n.d.ts +15 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +17 -1
- package/dist/svelte/index.d.ts +6 -2
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +8 -1
- package/dist/tool-allow-list.d.ts +8 -0
- package/dist/tool-allow-list.d.ts.map +1 -0
- package/dist/tool-loop.d.ts +191 -3
- package/dist/tool-loop.d.ts.map +1 -1
- package/package.json +19 -15
- package/dist/chunks/ChatService-D48KBDZy.js.map +0 -1
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Choices: the assistant offers 1–4 options, the person picks one.
|
|
3
|
+
*
|
|
4
|
+
* The proposal counterpart of `assistant_propose_action` for work that has
|
|
5
|
+
* several good answers — "crop this tighter", "find me a picture of the
|
|
6
|
+
* arena". The page registers a {@link AssistantChoiceSource} while it can
|
|
7
|
+
* serve one; the dock offers each source to the model as a browser tool
|
|
8
|
+
* (`assistant_offer_<source id>`, effect `read`). When the model calls it the
|
|
9
|
+
* dock asks the SOURCE for the options (the model never supplies them), shows
|
|
10
|
+
* them as cards in the chat, and returns only their ids and labels to the
|
|
11
|
+
* model. Nothing changes until the person clicks a card: only then does the
|
|
12
|
+
* dock call the source's `apply`, in the page, as the person. The model has
|
|
13
|
+
* no way to apply an option.
|
|
14
|
+
*
|
|
15
|
+
* Images on the cards are restricted to same-origin paths (`/…`), so a
|
|
16
|
+
* source cannot leak a signed provider URL and an injected prompt cannot make
|
|
17
|
+
* the dock load a remote address.
|
|
18
|
+
*
|
|
19
|
+
* Options that take a while (generated pictures) arrive later: the offer
|
|
20
|
+
* carries `pending`, the cards show "Making…" placeholders and the source's
|
|
21
|
+
* `fill` adds options as they finish. The person can pick any option that
|
|
22
|
+
* has arrived. Such an offer is not replaced by the person's next message
|
|
23
|
+
* (it cost real work); "None of these" stops it.
|
|
24
|
+
*
|
|
25
|
+
* Previewing: a source that implements `preview` shows a pick in place on
|
|
26
|
+
* the page WITHOUT applying it. A click on a card only previews (another
|
|
27
|
+
* click swaps the preview; the offer's `original` card shows the page as it
|
|
28
|
+
* was); nothing is committed until the person presses the offer's commit
|
|
29
|
+
* button (`commit`, which applies the previewed option and resolves the
|
|
30
|
+
* offer). Cancel (`cancel`), dismissing, clearing the conversation and
|
|
31
|
+
* disposing all restore the original. The model still only offers.
|
|
32
|
+
*/
|
|
33
|
+
/** Tool-name prefix for choice sources (add `assistant_offer_*` to a server allow-list). */
|
|
34
|
+
export const ASSISTANT_CHOICE_TOOL_PREFIX = 'assistant_offer_';
|
|
35
|
+
/** At most this many options are shown for one offer. */
|
|
36
|
+
export const ASSISTANT_CHOICE_MAX_OPTIONS = 4;
|
|
37
|
+
const SOURCE_ID_PATTERN = /^[a-z][a-z0-9_]{0,39}$/;
|
|
38
|
+
/** A simple in-memory registry; the latest registration of an id wins. */
|
|
39
|
+
export function createAssistantChoiceSourceRegistry() {
|
|
40
|
+
// Raw: sources are host objects compared by identity, never deep-proxied.
|
|
41
|
+
let sources = $state.raw([]);
|
|
42
|
+
return {
|
|
43
|
+
register(source) {
|
|
44
|
+
if (!SOURCE_ID_PATTERN.test(source.id)) {
|
|
45
|
+
throw new Error(`Invalid choice source id "${source.id}"`);
|
|
46
|
+
}
|
|
47
|
+
sources = [...sources.filter((s) => s.id !== source.id), source];
|
|
48
|
+
return () => {
|
|
49
|
+
sources = sources.filter((s) => s !== source);
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
list() {
|
|
53
|
+
return sources;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** The browser tool name for a source. */
|
|
58
|
+
export function choiceToolName(sourceId) {
|
|
59
|
+
return `${ASSISTANT_CHOICE_TOOL_PREFIX}${sourceId}`;
|
|
60
|
+
}
|
|
61
|
+
/** A same-origin path, or null. Rejects `//host`, `\\`, schemes, and controls. */
|
|
62
|
+
export function safeChoiceImageUrl(value) {
|
|
63
|
+
if (typeof value !== 'string')
|
|
64
|
+
return null;
|
|
65
|
+
const url = value.trim();
|
|
66
|
+
if (!/^\/(?![/\\])/.test(url))
|
|
67
|
+
return null;
|
|
68
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: rejecting control characters is the point
|
|
69
|
+
if (/[\u0000-\u001f\s\\]/.test(url))
|
|
70
|
+
return null;
|
|
71
|
+
return url;
|
|
72
|
+
}
|
|
73
|
+
function clean(value, max) {
|
|
74
|
+
return typeof value === 'string' ? value.trim().slice(0, max) : '';
|
|
75
|
+
}
|
|
76
|
+
/** Normalize a source's options: unique ids, plain labels, safe images, at most 4. */
|
|
77
|
+
export function normalizeChoiceOptions(options) {
|
|
78
|
+
const seen = new Set();
|
|
79
|
+
const result = [];
|
|
80
|
+
for (const option of options) {
|
|
81
|
+
const id = clean(option?.id, 120);
|
|
82
|
+
const label = clean(option?.label, 80);
|
|
83
|
+
if (!id || !label || seen.has(id))
|
|
84
|
+
continue;
|
|
85
|
+
seen.add(id);
|
|
86
|
+
const description = clean(option.description, 160);
|
|
87
|
+
const imageUrl = safeChoiceImageUrl(option.imageUrl);
|
|
88
|
+
result.push({
|
|
89
|
+
id,
|
|
90
|
+
label,
|
|
91
|
+
...(description ? { description } : {}),
|
|
92
|
+
...(imageUrl
|
|
93
|
+
? { imageUrl, imageAlt: clean(option.imageAlt, 120) || label }
|
|
94
|
+
: {}),
|
|
95
|
+
...(option.value !== undefined ? { value: option.value } : {}),
|
|
96
|
+
});
|
|
97
|
+
if (result.length >= ASSISTANT_CHOICE_MAX_OPTIONS)
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/** The dock's choice state: offers from tool calls, and the person's picks. */
|
|
103
|
+
export class AssistantChoices {
|
|
104
|
+
getRegistry;
|
|
105
|
+
hold;
|
|
106
|
+
sets = $state([]);
|
|
107
|
+
/** Release functions of the holds for offers still open. */
|
|
108
|
+
releases = new Map();
|
|
109
|
+
/**
|
|
110
|
+
* Per-offer preview queue and request counter: previews run one at a time
|
|
111
|
+
* in click order, so the page ends on the last click, and only the latest
|
|
112
|
+
* request updates the offer.
|
|
113
|
+
*/
|
|
114
|
+
previewQueues = new Map();
|
|
115
|
+
previewRequests = new Map();
|
|
116
|
+
/** Abort controllers of pending offers still being filled. */
|
|
117
|
+
fills = new Map();
|
|
118
|
+
/**
|
|
119
|
+
* @param hold Called for each offer while it waits for the person (and
|
|
120
|
+
* after a failed apply); returns the release, called once they picked,
|
|
121
|
+
* dismissed, or the offer was replaced. The dock uses its run holds.
|
|
122
|
+
*/
|
|
123
|
+
constructor(getRegistry, hold) {
|
|
124
|
+
this.getRegistry = getRegistry;
|
|
125
|
+
this.hold = hold;
|
|
126
|
+
}
|
|
127
|
+
/** Keep one hold per open offer (`waiting` or `failed`). */
|
|
128
|
+
syncHolds() {
|
|
129
|
+
const open = new Set(this.sets
|
|
130
|
+
.filter((set) => set.status === 'waiting' || set.status === 'failed')
|
|
131
|
+
.map((set) => set.id));
|
|
132
|
+
for (const [id, release] of this.releases) {
|
|
133
|
+
if (!open.has(id)) {
|
|
134
|
+
release();
|
|
135
|
+
this.releases.delete(id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!this.hold)
|
|
139
|
+
return;
|
|
140
|
+
for (const set of this.sets) {
|
|
141
|
+
if (open.has(set.id) && !this.releases.has(set.id)) {
|
|
142
|
+
this.releases.set(set.id, this.hold(set));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
sources() {
|
|
147
|
+
return (this.getRegistry()?.list() ?? []).filter((source) => SOURCE_ID_PATTERN.test(source.id));
|
|
148
|
+
}
|
|
149
|
+
source(id) {
|
|
150
|
+
return this.sources().find((source) => source.id === id);
|
|
151
|
+
}
|
|
152
|
+
/** The browser tools for the registered sources. */
|
|
153
|
+
tools() {
|
|
154
|
+
return this.sources().map((source) => ({
|
|
155
|
+
name: choiceToolName(source.id),
|
|
156
|
+
description: [
|
|
157
|
+
source.description.slice(0, 800),
|
|
158
|
+
'Shows the options as cards in the chat; the person picks one and it is applied then. You cannot apply it yourself. Tell them to pick one.',
|
|
159
|
+
].join('\n'),
|
|
160
|
+
inputSchema: source.inputSchema ?? {
|
|
161
|
+
type: 'object',
|
|
162
|
+
additionalProperties: false,
|
|
163
|
+
properties: {},
|
|
164
|
+
},
|
|
165
|
+
effect: 'read',
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
isChoiceTool(name) {
|
|
169
|
+
return (name.startsWith(ASSISTANT_CHOICE_TOOL_PREFIX) &&
|
|
170
|
+
Boolean(this.source(name.slice(ASSISTANT_CHOICE_TOOL_PREFIX.length))));
|
|
171
|
+
}
|
|
172
|
+
/** Number of offers waiting for the person. */
|
|
173
|
+
get waitingCount() {
|
|
174
|
+
return this.sets.filter((set) => set.status === 'waiting').length;
|
|
175
|
+
}
|
|
176
|
+
/** The progress line of an offer still being made with nothing to pick yet. */
|
|
177
|
+
get pendingMessage() {
|
|
178
|
+
const making = this.sets.find((set) => set.status === 'waiting' && set.pending && set.options.length === 0);
|
|
179
|
+
return making?.pending?.message ?? null;
|
|
180
|
+
}
|
|
181
|
+
update(id, patch) {
|
|
182
|
+
this.sets = this.sets.map((set) => set.id === id ? { ...set, ...patch } : set);
|
|
183
|
+
this.syncHolds();
|
|
184
|
+
}
|
|
185
|
+
/** Run a model call to a choice tool: ask the source, show the cards. */
|
|
186
|
+
async offerFromCall(call, signal) {
|
|
187
|
+
const sourceId = call.name.slice(ASSISTANT_CHOICE_TOOL_PREFIX.length);
|
|
188
|
+
const source = this.source(sourceId);
|
|
189
|
+
if (!source)
|
|
190
|
+
return { id: call.id, ok: false, error: 'not_available' };
|
|
191
|
+
let offer;
|
|
192
|
+
try {
|
|
193
|
+
offer = await source.offer(call.args ?? {}, { signal });
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
return {
|
|
197
|
+
id: call.id,
|
|
198
|
+
ok: false,
|
|
199
|
+
error: err instanceof Error ? err.message : String(err),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const pending = offer?.pending && typeof offer.pending.fill === 'function'
|
|
203
|
+
? offer.pending
|
|
204
|
+
: null;
|
|
205
|
+
const options = normalizeChoiceOptions(offer?.options ?? []);
|
|
206
|
+
if (options.length === 0 && !pending) {
|
|
207
|
+
return { id: call.id, ok: false, error: 'Nothing to offer.' };
|
|
208
|
+
}
|
|
209
|
+
const pendingState = pending
|
|
210
|
+
? {
|
|
211
|
+
message: clean(pending.message, 160) || 'Making options…',
|
|
212
|
+
expected: Math.min(ASSISTANT_CHOICE_MAX_OPTIONS - options.length, Math.max(1, Math.floor(Number(pending.expected) || 0) || 4)),
|
|
213
|
+
}
|
|
214
|
+
: undefined;
|
|
215
|
+
const previewable = typeof source.preview === 'function';
|
|
216
|
+
const original = previewable
|
|
217
|
+
? normalizeChoiceOptions(offer?.original ? [offer.original] : [])[0]
|
|
218
|
+
: undefined;
|
|
219
|
+
const set = {
|
|
220
|
+
id: call.id,
|
|
221
|
+
sourceId,
|
|
222
|
+
title: clean(offer.title, 120) || 'Pick one',
|
|
223
|
+
options: options.filter((option) => option.id !== original?.id),
|
|
224
|
+
status: 'waiting',
|
|
225
|
+
...(previewable
|
|
226
|
+
? {
|
|
227
|
+
previewable: true,
|
|
228
|
+
...(original ? { original } : {}),
|
|
229
|
+
commitLabel: clean(offer.commitLabel, 40) || 'Use this',
|
|
230
|
+
cancelLabel: clean(offer.cancelLabel, 40) || 'Cancel',
|
|
231
|
+
}
|
|
232
|
+
: {}),
|
|
233
|
+
...(pendingState && pendingState.expected > 0
|
|
234
|
+
? { pending: pendingState, lasting: true }
|
|
235
|
+
: {}),
|
|
236
|
+
};
|
|
237
|
+
this.sets = [...this.sets.filter((s) => s.id !== call.id), set];
|
|
238
|
+
this.syncHolds();
|
|
239
|
+
if (pending && set.pending)
|
|
240
|
+
this.fill(set.id, pending);
|
|
241
|
+
return {
|
|
242
|
+
id: call.id,
|
|
243
|
+
ok: true,
|
|
244
|
+
result: JSON.stringify({
|
|
245
|
+
offered: true,
|
|
246
|
+
waitingForUser: true,
|
|
247
|
+
...(set.pending
|
|
248
|
+
? { stillMaking: true, progress: set.pending.message }
|
|
249
|
+
: {}),
|
|
250
|
+
options: options.map(({ id, label }) => ({ id, label })),
|
|
251
|
+
}),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** Run a pending offer's `fill`, adding its options as they finish. */
|
|
255
|
+
fill(setId, pending) {
|
|
256
|
+
const controller = new AbortController();
|
|
257
|
+
this.fills.get(setId)?.abort();
|
|
258
|
+
this.fills.set(setId, controller);
|
|
259
|
+
const live = () => !controller.signal.aborted
|
|
260
|
+
? this.sets.find((s) => s.id === setId && s.status !== 'dismissed')
|
|
261
|
+
: undefined;
|
|
262
|
+
const update = {
|
|
263
|
+
add: (added) => {
|
|
264
|
+
const set = live();
|
|
265
|
+
if (!set)
|
|
266
|
+
return;
|
|
267
|
+
const options = normalizeChoiceOptions([
|
|
268
|
+
...set.options,
|
|
269
|
+
...(Array.isArray(added) ? added : []),
|
|
270
|
+
]);
|
|
271
|
+
const remaining = set.pending
|
|
272
|
+
? Math.max(0, Math.min(ASSISTANT_CHOICE_MAX_OPTIONS, set.options.length + set.pending.expected) - options.length)
|
|
273
|
+
: 0;
|
|
274
|
+
this.update(setId, {
|
|
275
|
+
options,
|
|
276
|
+
...(set.pending
|
|
277
|
+
? { pending: { ...set.pending, expected: remaining } }
|
|
278
|
+
: {}),
|
|
279
|
+
});
|
|
280
|
+
},
|
|
281
|
+
status: (message) => {
|
|
282
|
+
const set = live();
|
|
283
|
+
const text = clean(message, 160);
|
|
284
|
+
if (set?.pending && text) {
|
|
285
|
+
this.update(setId, { pending: { ...set.pending, message: text } });
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
const finish = (err) => {
|
|
290
|
+
if (this.fills.get(setId) === controller)
|
|
291
|
+
this.fills.delete(setId);
|
|
292
|
+
const set = live();
|
|
293
|
+
if (!set)
|
|
294
|
+
return;
|
|
295
|
+
const message = err == null
|
|
296
|
+
? null
|
|
297
|
+
: (err instanceof Error ? err.message : String(err)).slice(0, 200);
|
|
298
|
+
if (set.options.length === 0) {
|
|
299
|
+
this.update(setId, {
|
|
300
|
+
status: 'unavailable',
|
|
301
|
+
pending: undefined,
|
|
302
|
+
error: message || 'Nothing came back. Try again.',
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this.update(setId, {
|
|
307
|
+
pending: undefined,
|
|
308
|
+
...(message ? { note: message } : {}),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
Promise.resolve()
|
|
313
|
+
.then(() => pending.fill(update, controller.signal))
|
|
314
|
+
.then(() => finish(null), (err) => finish(err ?? 'Nothing came back. Try again.'));
|
|
315
|
+
}
|
|
316
|
+
/** Stop filling an offer (it was picked, dismissed or cleared). */
|
|
317
|
+
stopFill(setId) {
|
|
318
|
+
this.fills.get(setId)?.abort();
|
|
319
|
+
this.fills.delete(setId);
|
|
320
|
+
}
|
|
321
|
+
/** The person picked `optionId`: apply it through its source. */
|
|
322
|
+
async choose(setId, optionId) {
|
|
323
|
+
const set = this.sets.find((s) => s.id === setId);
|
|
324
|
+
if (!set || (set.status !== 'waiting' && set.status !== 'failed'))
|
|
325
|
+
return;
|
|
326
|
+
const option = set.options.find((o) => o.id === optionId);
|
|
327
|
+
if (!option)
|
|
328
|
+
return;
|
|
329
|
+
const source = this.source(set.sourceId);
|
|
330
|
+
if (!source) {
|
|
331
|
+
this.update(setId, {
|
|
332
|
+
status: 'failed',
|
|
333
|
+
error: 'This is no longer on the page.',
|
|
334
|
+
});
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
this.update(setId, {
|
|
338
|
+
status: 'applying',
|
|
339
|
+
chosenOptionId: optionId,
|
|
340
|
+
error: undefined,
|
|
341
|
+
});
|
|
342
|
+
try {
|
|
343
|
+
const outcome = await source.apply(option);
|
|
344
|
+
// Picked: versions still being made are no longer needed.
|
|
345
|
+
this.stopFill(setId);
|
|
346
|
+
this.update(setId, {
|
|
347
|
+
status: 'applied',
|
|
348
|
+
pending: undefined,
|
|
349
|
+
previewOptionId: undefined,
|
|
350
|
+
...(typeof outcome === 'string' && outcome.trim()
|
|
351
|
+
? { outcome: outcome.trim().slice(0, 200) }
|
|
352
|
+
: {}),
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
this.update(setId, {
|
|
357
|
+
status: 'failed',
|
|
358
|
+
error: err instanceof Error ? err.message : String(err),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/** The person wants none of these. */
|
|
363
|
+
dismiss(setId) {
|
|
364
|
+
const set = this.sets.find((s) => s.id === setId);
|
|
365
|
+
if (set &&
|
|
366
|
+
(set.status === 'waiting' ||
|
|
367
|
+
set.status === 'failed' ||
|
|
368
|
+
set.status === 'unavailable')) {
|
|
369
|
+
this.stopFill(setId);
|
|
370
|
+
void this.restorePreview(setId);
|
|
371
|
+
this.update(setId, {
|
|
372
|
+
status: 'dismissed',
|
|
373
|
+
pending: undefined,
|
|
374
|
+
previewOptionId: undefined,
|
|
375
|
+
previewPendingId: undefined,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Put the page back if this offer is previewing something (or is about
|
|
381
|
+
* to). Queued behind any preview still running, and it cancels queued
|
|
382
|
+
* ones, so a slow preview never lands after the page was restored.
|
|
383
|
+
*/
|
|
384
|
+
async restorePreview(setId) {
|
|
385
|
+
const set = this.sets.find((s) => s.id === setId);
|
|
386
|
+
if (!set?.previewable ||
|
|
387
|
+
(set.previewOptionId === undefined && set.previewPendingId === undefined)) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
this.previewRequests.set(setId, (this.previewRequests.get(setId) ?? 0) + 1);
|
|
391
|
+
const { sourceId } = set;
|
|
392
|
+
const original = set.original ?? null;
|
|
393
|
+
const restore = async () => {
|
|
394
|
+
try {
|
|
395
|
+
await this.source(sourceId)?.preview?.(original);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
// Nothing more to do: the page is leaving or the picture is gone.
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const queued = (this.previewQueues.get(setId) ?? Promise.resolve()).then(restore);
|
|
402
|
+
this.previewQueues.set(setId, queued);
|
|
403
|
+
await queued;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* The person clicked a card of a previewable offer (or its `original`):
|
|
407
|
+
* show it in place. Nothing is applied.
|
|
408
|
+
*/
|
|
409
|
+
async preview(setId, optionId) {
|
|
410
|
+
const set = this.sets.find((s) => s.id === setId);
|
|
411
|
+
if (!set?.previewable ||
|
|
412
|
+
(set.status !== 'waiting' && set.status !== 'failed')) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const option = set.original?.id === optionId
|
|
416
|
+
? set.original
|
|
417
|
+
: set.options.find((o) => o.id === optionId);
|
|
418
|
+
const source = this.source(set.sourceId);
|
|
419
|
+
if (!option || !source?.preview)
|
|
420
|
+
return;
|
|
421
|
+
const request = (this.previewRequests.get(setId) ?? 0) + 1;
|
|
422
|
+
this.previewRequests.set(setId, request);
|
|
423
|
+
const latest = () => this.previewRequests.get(setId) === request;
|
|
424
|
+
this.update(setId, { previewPendingId: optionId });
|
|
425
|
+
const run = async () => {
|
|
426
|
+
// A newer click (or a restore) is queued behind this one: let it run
|
|
427
|
+
// instead. A closed offer previews nothing.
|
|
428
|
+
const current = this.sets.find((s) => s.id === setId);
|
|
429
|
+
if (!latest() ||
|
|
430
|
+
!current ||
|
|
431
|
+
(current.status !== 'waiting' && current.status !== 'failed')) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
// One preview at a time: put back any other offer's first.
|
|
435
|
+
for (const other of this.sets) {
|
|
436
|
+
if (other.id !== setId && other.previewOptionId !== undefined) {
|
|
437
|
+
await this.restorePreview(other.id);
|
|
438
|
+
this.update(other.id, { previewOptionId: undefined });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
await source.preview?.(option);
|
|
443
|
+
if (!latest())
|
|
444
|
+
return;
|
|
445
|
+
this.update(setId, {
|
|
446
|
+
status: 'waiting',
|
|
447
|
+
previewOptionId: optionId,
|
|
448
|
+
previewPendingId: undefined,
|
|
449
|
+
error: undefined,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
catch (err) {
|
|
453
|
+
if (!latest())
|
|
454
|
+
return;
|
|
455
|
+
this.update(setId, {
|
|
456
|
+
status: 'failed',
|
|
457
|
+
previewPendingId: undefined,
|
|
458
|
+
error: err instanceof Error ? err.message : String(err),
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
const queued = (this.previewQueues.get(setId) ?? Promise.resolve()).then(run);
|
|
463
|
+
this.previewQueues.set(setId, queued);
|
|
464
|
+
await queued;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* The person pressed the commit button: apply the previewed option (the
|
|
468
|
+
* one thing that changes the page). The original, or nothing, previewed
|
|
469
|
+
* means there is nothing to apply: that is a cancel.
|
|
470
|
+
*/
|
|
471
|
+
async commit(setId) {
|
|
472
|
+
const set = this.sets.find((s) => s.id === setId);
|
|
473
|
+
// Never commit while a preview is still being shown: the option that
|
|
474
|
+
// would be applied is not yet the one on the page.
|
|
475
|
+
if (!set?.previewable || set.previewPendingId !== undefined)
|
|
476
|
+
return;
|
|
477
|
+
const id = set.previewOptionId;
|
|
478
|
+
if (!id || id === set.original?.id) {
|
|
479
|
+
this.cancel(setId);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
await this.choose(setId, id);
|
|
483
|
+
}
|
|
484
|
+
/** The person wants to keep the original: restore it and close the offer. */
|
|
485
|
+
cancel(setId) {
|
|
486
|
+
this.dismiss(setId);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* A new request replaces offers still waiting, except ones that had
|
|
490
|
+
* options made for them (`lasting`): only the person dismisses those.
|
|
491
|
+
*/
|
|
492
|
+
supersedeWaiting() {
|
|
493
|
+
if (this.waitingCount === 0)
|
|
494
|
+
return;
|
|
495
|
+
for (const set of this.sets) {
|
|
496
|
+
if (set.status === 'waiting' && !set.lasting) {
|
|
497
|
+
void this.restorePreview(set.id);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
this.sets = this.sets.map((set) => set.status === 'waiting' && !set.lasting
|
|
501
|
+
? {
|
|
502
|
+
...set,
|
|
503
|
+
status: 'dismissed',
|
|
504
|
+
previewOptionId: undefined,
|
|
505
|
+
previewPendingId: undefined,
|
|
506
|
+
}
|
|
507
|
+
: set);
|
|
508
|
+
this.syncHolds();
|
|
509
|
+
}
|
|
510
|
+
clear() {
|
|
511
|
+
for (const controller of this.fills.values())
|
|
512
|
+
controller.abort();
|
|
513
|
+
this.fills.clear();
|
|
514
|
+
// An uncommitted preview never outlives the conversation.
|
|
515
|
+
for (const set of this.sets)
|
|
516
|
+
void this.restorePreview(set.id);
|
|
517
|
+
this.sets = [];
|
|
518
|
+
this.syncHolds();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* intentionally line up with `ChatThreadData`/`ChatMessageData` so a future
|
|
38
38
|
* adapter between the two is a narrow mapping, not a rewrite.
|
|
39
39
|
*/
|
|
40
|
+
import { type AssistantClientToolCall, type AssistantClientToolDeclaration, type AssistantClientToolResult, type AssistantTurnEvent } from '../../../assistant-turn-events.js';
|
|
40
41
|
export interface AssistantThreadSummary {
|
|
41
42
|
id: string;
|
|
42
43
|
title: string;
|
|
@@ -72,7 +73,21 @@ export interface ModelOption {
|
|
|
72
73
|
id: string;
|
|
73
74
|
label: string;
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
+
/** One streamed turn event, with persisted messages already mapped to the
|
|
77
|
+
* dock's {@link AssistantMessage} shape by the transport (#2908). */
|
|
78
|
+
export type AssistantTransportEvent = AssistantTurnEvent<AssistantMessage>;
|
|
79
|
+
/** Streaming inputs shared by a send and a resumed turn (#2908). A transport
|
|
80
|
+
* that does not stream ignores all three and resolves as before. */
|
|
81
|
+
export interface AssistantTurnStreamInput {
|
|
82
|
+
/** The page's browser tools to offer the model this turn. The server
|
|
83
|
+
* validates and allow-lists them; it never runs them. */
|
|
84
|
+
clientTools?: AssistantClientToolDeclaration[];
|
|
85
|
+
/** Live turn events (status, steps, token preview, persisted messages). */
|
|
86
|
+
onEvent?: (event: AssistantTransportEvent) => void;
|
|
87
|
+
/** Aborts the turn (the dock's Stop). */
|
|
88
|
+
signal?: AbortSignal;
|
|
89
|
+
}
|
|
90
|
+
export interface AssistantSendMessageInput extends AssistantTurnStreamInput {
|
|
76
91
|
threadId: string;
|
|
77
92
|
content: string;
|
|
78
93
|
attachments?: AssistantAttachmentRef[];
|
|
@@ -82,6 +97,17 @@ export interface AssistantSendMessageInput {
|
|
|
82
97
|
/** The model selected via ModelPicker, when the transport supports one. */
|
|
83
98
|
model?: string;
|
|
84
99
|
}
|
|
100
|
+
/** Resume a turn the server suspended on browser tool calls (#2908). */
|
|
101
|
+
export interface AssistantResumeTurnInput extends AssistantTurnStreamInput {
|
|
102
|
+
threadId: string;
|
|
103
|
+
/** The send this turn belongs to. */
|
|
104
|
+
clientRequestId: string;
|
|
105
|
+
/** From the suspension (`clientToolCalls.continuationId`). */
|
|
106
|
+
continuationId: string;
|
|
107
|
+
/** One result per suspended call. */
|
|
108
|
+
results: AssistantClientToolResult[];
|
|
109
|
+
model?: string;
|
|
110
|
+
}
|
|
85
111
|
export interface AssistantSendMessageResult {
|
|
86
112
|
/** True when the assistant turn has not resolved yet and the caller should
|
|
87
113
|
* poll `loadMessages` for the reply (mirrors anytown's `payload.inProgress`,
|
|
@@ -89,6 +115,15 @@ export interface AssistantSendMessageResult {
|
|
|
89
115
|
inProgress: boolean;
|
|
90
116
|
userMessage?: AssistantMessage;
|
|
91
117
|
assistantMessage?: AssistantMessage;
|
|
118
|
+
/** Every message the turn persisted (tool results, the reply), in order,
|
|
119
|
+
* for a streaming transport. */
|
|
120
|
+
messages?: AssistantMessage[];
|
|
121
|
+
/** Set when the turn is waiting on browser tools (#2908). The dock runs
|
|
122
|
+
* them and calls `resumeTurn`. */
|
|
123
|
+
clientToolCalls?: {
|
|
124
|
+
continuationId: string;
|
|
125
|
+
calls: AssistantClientToolCall[];
|
|
126
|
+
};
|
|
92
127
|
}
|
|
93
128
|
export interface AssistantTransport {
|
|
94
129
|
listThreads(): Promise<AssistantThreadSummary[]>;
|
|
@@ -101,6 +136,9 @@ export interface AssistantTransport {
|
|
|
101
136
|
* transport that has no model choice (e.g. a single fixed backend model)
|
|
102
137
|
* simply omits this method. */
|
|
103
138
|
listModels?(): Promise<ModelOption[]>;
|
|
139
|
+
/** Resume a turn suspended on browser tool calls (#2908). Required for a
|
|
140
|
+
* transport that ever returns `clientToolCalls`. */
|
|
141
|
+
resumeTurn?(input: AssistantResumeTurnInput): Promise<AssistantSendMessageResult>;
|
|
104
142
|
}
|
|
105
143
|
export interface InMemoryAssistantTransportOptions {
|
|
106
144
|
/** Simulates a still-processing turn: the Nth send for a given thread
|
|
@@ -219,6 +257,9 @@ export interface SmrtAssistantTransportOptions {
|
|
|
219
257
|
createThread: (title: string) => Promise<AssistantThreadSummary>;
|
|
220
258
|
sendMessage: (input: AssistantSendMessageInput) => Promise<AssistantSendMessageResult>;
|
|
221
259
|
uploadAttachment: (file: File) => Promise<AssistantAttachmentRef>;
|
|
260
|
+
/** Resumes a turn suspended on browser tools (#2908); see
|
|
261
|
+
* `AssistantTransport.resumeTurn`. */
|
|
262
|
+
resumeTurn?: (input: AssistantResumeTurnInput) => Promise<AssistantSendMessageResult>;
|
|
222
263
|
};
|
|
223
264
|
/** Static model catalog; when supplied, `listModels()` resolves to it and
|
|
224
265
|
* `AssistantDock` renders `ModelPicker`. The `model` field of a `send` is
|
|
@@ -236,4 +277,16 @@ export interface SmrtAssistantTransportOptions {
|
|
|
236
277
|
* `../../../models/ChatThread.ts:16`, `../../../models/ChatMessage.ts:26`).
|
|
237
278
|
*/
|
|
238
279
|
export declare function createSmrtAssistantTransport(options: SmrtAssistantTransportOptions): AssistantTransport;
|
|
280
|
+
/**
|
|
281
|
+
* Read a streamed assistant turn response (#2908) into an
|
|
282
|
+
* {@link AssistantSendMessageResult}, forwarding each event (with persisted
|
|
283
|
+
* messages mapped by `mapMessage`) to `onEvent`. For a host transport whose
|
|
284
|
+
* send route answers `text/event-stream` (the server side is
|
|
285
|
+
* `runAssistantTurn` + `createAssistantTurnResponse`). Rejects on an in-band
|
|
286
|
+
* `error` or a cut stream.
|
|
287
|
+
*/
|
|
288
|
+
export declare function readAssistantTurnResult<W = Record<string, unknown>>(response: Response, options: {
|
|
289
|
+
mapMessage: (wire: W) => AssistantMessage;
|
|
290
|
+
onEvent?: (event: AssistantTransportEvent) => void;
|
|
291
|
+
}): Promise<AssistantSendMessageResult>;
|
|
239
292
|
//# sourceMappingURL=assistant-transport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-transport.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/assistant/assistant-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH;;;;;;;;GAQG;AACH,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;qEAKiE;IACjE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"assistant-transport.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/assistant/assistant-transport.ts"],"names":[],"mappings":"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"}
|