@poodle64/librarian 2026.9.20 → 2026.9.22
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/README.md +133 -7
- package/dist/chat.svelte.d.ts +189 -0
- package/dist/chat.svelte.js +554 -0
- package/dist/client.d.ts +29 -6
- package/dist/client.js +11 -3
- package/dist/components/agent-transcript/agent-transcript.svelte +42 -6
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +9 -0
- package/dist/components/answer-mark/answer-mark.svelte +223 -0
- package/dist/components/answer-mark/answer-mark.svelte.d.ts +11 -0
- package/dist/components/answer-mark/index.d.ts +2 -0
- package/dist/components/answer-mark/index.js +2 -0
- package/dist/components/conversation/conversation.svelte +21 -3
- package/dist/components/conversation/conversation.svelte.d.ts +9 -0
- package/dist/components/conversation-list/conversation-list.svelte +496 -0
- package/dist/components/conversation-list/conversation-list.svelte.d.ts +26 -0
- package/dist/components/conversation-list/index.d.ts +2 -0
- package/dist/components/conversation-list/index.js +2 -0
- package/dist/components/fair-use-notice/fair-use-notice.svelte +97 -0
- package/dist/components/fair-use-notice/fair-use-notice.svelte.d.ts +11 -0
- package/dist/components/fair-use-notice/index.d.ts +2 -0
- package/dist/components/fair-use-notice/index.js +2 -0
- package/dist/components/working/working.svelte +8 -3
- package/dist/components/working/working.svelte.d.ts +3 -0
- package/dist/copy.d.ts +40 -0
- package/dist/copy.js +36 -1
- package/dist/session.svelte.js +7 -1
- package/dist/transcript.svelte.js +17 -3
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ src/lib/
|
|
|
19
19
|
client.ts ask() and watch(): Claude Code's OWN events, unaltered
|
|
20
20
|
transcript.svelte.ts Transcript state, the fold/segment/describe helpers
|
|
21
21
|
session.svelte.ts Session: a whole stream of runs, as turns
|
|
22
|
+
chat.svelte.ts Chat: the page's controller, over a room's routes or a job's
|
|
22
23
|
citations.ts the Citation shape, `[n]` markers, the trust mark
|
|
23
24
|
copy.ts every word this package says, and the host's overrides
|
|
24
25
|
attachments.ts what a reader may attach, and the limits
|
|
@@ -26,6 +27,9 @@ src/lib/
|
|
|
26
27
|
history.svelte.ts the browser-held conversation list, per caller
|
|
27
28
|
components/
|
|
28
29
|
conversation/ the whole reading surface: scroll, pill, pane, composer slot
|
|
30
|
+
conversation-list/ past conversations, newest first: reopen, rename, download, delete
|
|
31
|
+
answer-mark/ helpful or not, with a note, on the last answer
|
|
32
|
+
fair-use-notice/ today's allowance, beside the composer
|
|
29
33
|
scope-statement/ what this room answers from, and what it does not hold
|
|
30
34
|
agent-transcript/ one question and everything Milton did answering it
|
|
31
35
|
document-pane/ the cited document, open at the cited passage
|
|
@@ -180,6 +184,116 @@ Nothing here fetches on its own behalf. The library's document read is
|
|
|
180
184
|
authenticated, and a package that called it directly would be reaching past
|
|
181
185
|
the app's proxy with a session it has no business holding.
|
|
182
186
|
|
|
187
|
+
### A room's page, whole: `Chat`
|
|
188
|
+
|
|
189
|
+
The hand-wired page above is the parts. A stamped room page is `Chat` over
|
|
190
|
+
the room's routes, and the components bound to it:
|
|
191
|
+
|
|
192
|
+
```svelte
|
|
193
|
+
<script lang="ts">
|
|
194
|
+
import { Chat } from '@poodle64/librarian/chat';
|
|
195
|
+
import Conversation from '@poodle64/librarian/conversation';
|
|
196
|
+
import ConversationList from '@poodle64/librarian/conversation-list';
|
|
197
|
+
import Composer from '@poodle64/librarian/composer';
|
|
198
|
+
import FairUseNotice from '@poodle64/librarian/fair-use-notice';
|
|
199
|
+
|
|
200
|
+
const chat = new Chat(roomTransport(room)); // the app's own client, below
|
|
201
|
+
const wanted = $derived(page.url.searchParams.get('c'));
|
|
202
|
+
|
|
203
|
+
// The address names the conversation; `open` and `new` are safe here.
|
|
204
|
+
$effect(() => {
|
|
205
|
+
if (wanted && wanted !== chat.conversationId) void chat.open(wanted);
|
|
206
|
+
else if (!wanted) chat.new();
|
|
207
|
+
});
|
|
208
|
+
</script>
|
|
209
|
+
|
|
210
|
+
<ConversationList
|
|
211
|
+
conversations={chat.conversations}
|
|
212
|
+
failed={chat.listFailed}
|
|
213
|
+
current={chat.conversationId}
|
|
214
|
+
href={(id) => `?c=${id}`}
|
|
215
|
+
onnew={() => chat.new()}
|
|
216
|
+
onrename={(id, title) => chat.rename(id, title)}
|
|
217
|
+
ondownload={(id) => chat.download(id)}
|
|
218
|
+
ondelete={(id) => chat.remove(id)}
|
|
219
|
+
/>
|
|
220
|
+
<Conversation
|
|
221
|
+
turns={chat.turns}
|
|
222
|
+
running={chat.busy}
|
|
223
|
+
version={chat.version}
|
|
224
|
+
waiting={chat.waiting}
|
|
225
|
+
answering={chat.answering}
|
|
226
|
+
onregenerate={() => chat.again()}
|
|
227
|
+
onsuggest={(question) => chat.ask(question)}
|
|
228
|
+
onmark={(turn, verdict) => chat.mark(turn, verdict)}
|
|
229
|
+
>
|
|
230
|
+
{#snippet composer()}
|
|
231
|
+
<FairUseNotice quota={chat.quota} />
|
|
232
|
+
<Composer
|
|
233
|
+
bind:value={chat.draft}
|
|
234
|
+
bind:files={chat.files}
|
|
235
|
+
running={chat.busy}
|
|
236
|
+
sendWhileRunning={chat.sendWhileRunning}
|
|
237
|
+
onsubmit={() => chat.ask()}
|
|
238
|
+
onstop={() => chat.stop()}
|
|
239
|
+
/>
|
|
240
|
+
{/snippet}
|
|
241
|
+
</Conversation>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`chat.list()` reads the list once; after that it follows every answer.
|
|
245
|
+
|
|
246
|
+
The transport is the app's own client over its routes, and the shapes are
|
|
247
|
+
the routes' own bodies (`ConversationSummary`, `ConversationRead`,
|
|
248
|
+
`StoredTurn`, `Quota`), so the stamped slice returns them as they are and
|
|
249
|
+
the transport passes them through:
|
|
250
|
+
|
|
251
|
+
| `RoomTransport` | Route |
|
|
252
|
+
| --------------- | ----------------------------------------------------- |
|
|
253
|
+
| `ask` | `POST rooms/{room}/ask`, through `client.ask()` |
|
|
254
|
+
| `read` | `GET conversations/{id}` |
|
|
255
|
+
| `stop` | `POST conversations/{id}/stop` |
|
|
256
|
+
| `quota` | `GET quota` |
|
|
257
|
+
| `list` | this person's conversations in the room |
|
|
258
|
+
| `rename` | `PATCH conversations/{id}` |
|
|
259
|
+
| `remove` | `DELETE conversations/{id}` |
|
|
260
|
+
| `download` | `GET conversations/{id}/export`, as `{ name, body }` |
|
|
261
|
+
| `mark` | its answer mark, where the agent takes one (optional) |
|
|
262
|
+
|
|
263
|
+
What the controller relies on the routes to say:
|
|
264
|
+
|
|
265
|
+
| The route says | The page |
|
|
266
|
+
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
267
|
+
| a `queued` frame, repeated while the ask waits | `waiting`, by the frame's type alone, until the agent starts: "Milton is answering another question first", beside the clock |
|
|
268
|
+
| `system/init` with `session_id` | `conversationId`, which the host puts in the address |
|
|
269
|
+
| 429 on the ask | drops the turn, puts the words and files back in the box, reads the allowance |
|
|
270
|
+
| 409 on the ask: the conversation is already being answered | drops the turn, keeps the words, shows it still answering |
|
|
271
|
+
| `answering_since` on a read | `answering`: the question in flight with "still answering" and its clock, read again every 5 s till it clears |
|
|
272
|
+
| a stream that dropped (`library_error` with `dropped`) on a named conversation | reads it rather than failing the turn: a locked phone loses the connection, not the answer |
|
|
273
|
+
|
|
274
|
+
The library's `queued` frame is `{ type: 'queued', message }`, `message` its
|
|
275
|
+
own sentence; `isQueued(event)` from `./client` narrows an event to it
|
|
276
|
+
(`QueuedEvent`) for a host that reads it.
|
|
277
|
+
|
|
278
|
+
A turn belongs to the server. Leaving a conversation, or the page, leaves
|
|
279
|
+
its answer being written; `stop()` asks the server to end it, and before the
|
|
280
|
+
agent has named a new conversation it holds the stream open, unseen, until
|
|
281
|
+
it does. `again()` asks the last question as a new turn: the conversation is
|
|
282
|
+
append-only because the agent's transcript is.
|
|
283
|
+
|
|
284
|
+
A job is `new Chat(jobTransport)`, where `JobTransport` is its `watch`,
|
|
285
|
+
`message` and `stop` (godswood's run view). The turns are `Session`'s; a
|
|
286
|
+
message goes onto the run while it works and opens its stream again once it
|
|
287
|
+
has settled; `carriesOn()` keeps a closed stream watched while the host says
|
|
288
|
+
the job may go on by itself; `sendWhileRunning` is true.
|
|
289
|
+
|
|
290
|
+
| Concern | How |
|
|
291
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
292
|
+
| The answer mark | `onmark(turn, verdict)` on `Conversation`: resolve true once recorded. Offered on the last settled answer only; without it no mark renders |
|
|
293
|
+
| Waiting, still answering | `waiting` and `answering` on `Conversation`, from the controller |
|
|
294
|
+
| The allowance | `FairUseNotice quota={…}` in the composer snippet: a count while there are questions left, a notice with `support_url` once there are none. Nothing for `exempt`, or a `limit` of 0 |
|
|
295
|
+
| Past conversations | `ConversationList`: newest first, `href` makes each row a link, and each act renders only when its handler is given; a delete asks first |
|
|
296
|
+
|
|
183
297
|
### Watching a session somebody else started
|
|
184
298
|
|
|
185
299
|
A session the APP started — a persona reading a document with nobody asking —
|
|
@@ -187,7 +301,10 @@ is watched rather than asked. `watch()` GETs its stream; `Session` folds it
|
|
|
187
301
|
into turns: the prompt and every later message on the reader's side (the CLI
|
|
188
302
|
echoes each one back under `--replay-user-messages`), each run's answer on the
|
|
189
303
|
persona's, each settled by its own `result`. A stream without deltas is folded
|
|
190
|
-
from its whole messages.
|
|
304
|
+
from its whole messages. A run under `--json-schema` hands in its answer by
|
|
305
|
+
calling the CLI's `StructuredOutput` tool: that call is no step of the work,
|
|
306
|
+
the words before it stay the answer, and what it handed in is the run's
|
|
307
|
+
`outcome.structuredOutput`, the natural source of the host's `artefact`. Events are deduped by `uuid`, so opening the watch
|
|
191
308
|
again after sending a message folds only what is new.
|
|
192
309
|
|
|
193
310
|
```svelte
|
|
@@ -394,8 +511,10 @@ paragraphs addressed to the model before the question renders, so a
|
|
|
394
511
|
colleague never sees it. Pass the question as it went on the wire; the
|
|
395
512
|
transcript shows what they asked.
|
|
396
513
|
|
|
397
|
-
`createHistory(namespace)` from `@poodle64/librarian/history`
|
|
398
|
-
|
|
514
|
+
`createHistory(namespace)` from `@poodle64/librarian/history` keeps a
|
|
515
|
+
browser-only list for a page with no server-side one (a page on `Chat` reads
|
|
516
|
+
the server's), under its own `localStorage` key for each app, or each room
|
|
517
|
+
inside an app:
|
|
399
518
|
|
|
400
519
|
```ts
|
|
401
520
|
import { createHistory, titleFrom } from '@poodle64/librarian/history';
|
|
@@ -413,7 +532,7 @@ pnpm run test # build + vitest
|
|
|
413
532
|
pnpm run screenshots # the state grid, real engine (see below)
|
|
414
533
|
```
|
|
415
534
|
|
|
416
|
-
`docs/screenshots/` is
|
|
535
|
+
`docs/screenshots/` is twenty-three states x three widths x both themes, taken by
|
|
417
536
|
`scripts/screenshots.mjs` against the console's `/librarian` lab route
|
|
418
537
|
running from its own static build. The same script asserts what a screenshot
|
|
419
538
|
cannot: that nothing scrolls sideways at any width, that the source pane
|
|
@@ -424,9 +543,13 @@ its question and takes the rest of the row with it, that the persona's
|
|
|
424
543
|
between-tool narration is nowhere in the answer prose before the activity
|
|
425
544
|
line is opened, that a job's artefact and the pages it read open in the
|
|
426
545
|
host's own columns and never in a pane of the package's, and that the reading
|
|
427
|
-
column holds its 46rem measure and stays centred at every width.
|
|
428
|
-
|
|
429
|
-
|
|
546
|
+
column holds its 46rem measure and stays centred at every width. Its `room`
|
|
547
|
+
scene is `Chat` itself over a fake of the room's routes, driven: a question
|
|
548
|
+
waits behind another, streams, counts against the allowance and takes a mark;
|
|
549
|
+
one asked again is stopped; a conversation reopened mid-answer says so and
|
|
550
|
+
settles; one is renamed and the open one deleted. It exits non-zero on any of
|
|
551
|
+
them. The job states fold a stream captured from the real CLI
|
|
552
|
+
(`src/test/fixtures/`).
|
|
430
553
|
|
|
431
554
|
The console's `app.css` deliberately does NOT scan this package's `dist`, so
|
|
432
555
|
the grid is taken in a consumer that compiles none of its Tailwind classes —
|
|
@@ -437,6 +560,9 @@ pnpm --filter @poodle64/console run build
|
|
|
437
560
|
pnpm --filter @poodle64/librarian run screenshots
|
|
438
561
|
```
|
|
439
562
|
|
|
563
|
+
Where Playwright's own Chromium will not start (NixOS), name one that does:
|
|
564
|
+
`CHROMIUM=/nix/store/…-playwright-browsers/chromium-…/chrome-linux/chrome`.
|
|
565
|
+
|
|
440
566
|
## Releasing
|
|
441
567
|
|
|
442
568
|
1. Change a component; bump `version` in `package.json` (CalVer).
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page's controller: one conversation with one agent, over a room's routes
|
|
3
|
+
* or a job's watch, message and stop.
|
|
4
|
+
*
|
|
5
|
+
* A room is asked. Each question streams its own turn, the agent names the
|
|
6
|
+
* conversation on its `init` event, and the conversation is read back whole
|
|
7
|
+
* when it is reopened. The turn belongs to the server, not the page: a
|
|
8
|
+
* refresh, a locked phone or a closed tab leaves it running, so a reopened
|
|
9
|
+
* conversation whose answer is still being written shows that, with its
|
|
10
|
+
* clock, and is read again once it settles. Stop is a request to the server
|
|
11
|
+
* for the same reason, never just a closed connection.
|
|
12
|
+
*
|
|
13
|
+
* A job is watched. Somebody else started it; the reader follows it from its
|
|
14
|
+
* first event, says things to it while it works, and stops it. Its stream is
|
|
15
|
+
* folded by `Session`, and a stream that closes is opened again when the
|
|
16
|
+
* reader sends a message, or while the host says the job may carry on.
|
|
17
|
+
*
|
|
18
|
+
* Nothing here knows a URL. The transport is the host's own client over the
|
|
19
|
+
* routes, and its shapes are the routes' own bodies, so a stamped app passes
|
|
20
|
+
* what its client returns straight through.
|
|
21
|
+
*/
|
|
22
|
+
import { type AgentEvent } from './client';
|
|
23
|
+
import type { Citation } from './citations';
|
|
24
|
+
import { type Turn } from './transcript.svelte';
|
|
25
|
+
/** One conversation in the list. */
|
|
26
|
+
export interface ConversationSummary {
|
|
27
|
+
/** The agent's own session id: what resumes it, what names it in the address. */
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
/** ISO 8601. */
|
|
31
|
+
last_activity_at: string;
|
|
32
|
+
/** ISO 8601 while an answer is being written; null once it settles, or once
|
|
33
|
+
* the worker writing it is gone. */
|
|
34
|
+
answering_since?: string | null;
|
|
35
|
+
}
|
|
36
|
+
/** One question and its answer, as the agent's transcript keeps them. */
|
|
37
|
+
export interface StoredTurn {
|
|
38
|
+
/** As the reader asked it, never with a preamble. */
|
|
39
|
+
question: string;
|
|
40
|
+
answer: string;
|
|
41
|
+
/** ISO 8601, when it was asked. */
|
|
42
|
+
at?: string | null;
|
|
43
|
+
citations?: Citation[];
|
|
44
|
+
}
|
|
45
|
+
/** A conversation reopened. */
|
|
46
|
+
export interface ConversationRead extends ConversationSummary {
|
|
47
|
+
turns: StoredTurn[];
|
|
48
|
+
}
|
|
49
|
+
/** Today's fair-use allowance. */
|
|
50
|
+
export interface Quota {
|
|
51
|
+
/** 0: no limit. */
|
|
52
|
+
limit: number;
|
|
53
|
+
remaining: number;
|
|
54
|
+
/** Nothing is counted for this person, and nothing is shown. */
|
|
55
|
+
exempt: boolean;
|
|
56
|
+
reached: boolean;
|
|
57
|
+
/** ISO 8601. */
|
|
58
|
+
resets_at: string;
|
|
59
|
+
support_url?: string | null;
|
|
60
|
+
}
|
|
61
|
+
/** A reader's verdict on one answer; each one replaces the last. */
|
|
62
|
+
export interface Verdict {
|
|
63
|
+
helpful: boolean;
|
|
64
|
+
note?: string | null;
|
|
65
|
+
}
|
|
66
|
+
export interface AskRequest {
|
|
67
|
+
question: string;
|
|
68
|
+
files: File[];
|
|
69
|
+
/** The conversation this carries on; null starts one. */
|
|
70
|
+
resume: string | null;
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A room's routes, as the host's own client reaches them. Every method but
|
|
75
|
+
* `ask` may reject; the controller says what failed.
|
|
76
|
+
*/
|
|
77
|
+
export interface RoomTransport {
|
|
78
|
+
/** `POST rooms/{room}/ask`: `client.ask()` over it, which never throws. A
|
|
79
|
+
* refusal arrives as `library_error` with its `status`: 429 is a spent
|
|
80
|
+
* allowance, 409 a conversation already being answered. */
|
|
81
|
+
ask(request: AskRequest): AsyncIterable<AgentEvent>;
|
|
82
|
+
/** `GET conversations/{id}`. */
|
|
83
|
+
read(id: string): Promise<ConversationRead>;
|
|
84
|
+
/** `POST conversations/{id}/stop`. */
|
|
85
|
+
stop(id: string): Promise<void>;
|
|
86
|
+
/** `GET quota`. */
|
|
87
|
+
quota(): Promise<Quota>;
|
|
88
|
+
/** This person's conversations in this room. */
|
|
89
|
+
list(): Promise<ConversationSummary[]>;
|
|
90
|
+
/** `PATCH conversations/{id}`. */
|
|
91
|
+
rename(id: string, title: string): Promise<void>;
|
|
92
|
+
/** `DELETE conversations/{id}`. */
|
|
93
|
+
remove(id: string): Promise<void>;
|
|
94
|
+
/** `GET conversations/{id}/export`: the file, and the name to save it as. */
|
|
95
|
+
download(id: string): Promise<{
|
|
96
|
+
name: string;
|
|
97
|
+
body: Blob;
|
|
98
|
+
}>;
|
|
99
|
+
/** The answer mark, where the agent takes one. `turn` is the answer's
|
|
100
|
+
* zero-based position in the conversation. */
|
|
101
|
+
mark?(id: string, turn: number, verdict: Verdict): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
/** A job's routes. */
|
|
104
|
+
export interface JobTransport {
|
|
105
|
+
/** `GET …/jobs/{id}/watch`: `client.watch()` over it, which never throws. */
|
|
106
|
+
watch(id: string, signal: AbortSignal): AsyncIterable<AgentEvent>;
|
|
107
|
+
/** `POST …/jobs/{id}/message`: onto the run while it works, a new run once
|
|
108
|
+
* it has settled. */
|
|
109
|
+
message(id: string, text: string): Promise<void>;
|
|
110
|
+
/** `POST …/jobs/{id}/stop`. */
|
|
111
|
+
stop(id: string): Promise<void>;
|
|
112
|
+
/** The job may carry on by itself after its stream closes (a pipeline
|
|
113
|
+
* still on it), so the stream is watched again. Absent, it is watched
|
|
114
|
+
* again only when the reader sends a message. */
|
|
115
|
+
carriesOn?(): boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface ChatOptions {
|
|
118
|
+
/** How often a conversation answering out of sight is read again. */
|
|
119
|
+
pollMs?: number;
|
|
120
|
+
/** How often a closed job is asked whether it carries on. */
|
|
121
|
+
rewatchMs?: number;
|
|
122
|
+
}
|
|
123
|
+
export declare class Chat {
|
|
124
|
+
#private;
|
|
125
|
+
/** The words in the box and the files with them: bind the composer here,
|
|
126
|
+
* so a question the server refuses comes back exactly as typed. */
|
|
127
|
+
draft: string;
|
|
128
|
+
files: File[];
|
|
129
|
+
/** When an answer this page is not streaming began, epoch ms: a
|
|
130
|
+
* conversation reopened mid-answer. */
|
|
131
|
+
answering: number | null;
|
|
132
|
+
/** The agent is answering somebody else's question first. */
|
|
133
|
+
waiting: boolean;
|
|
134
|
+
/** Today's allowance; null until read, and in a job. */
|
|
135
|
+
quota: Quota | null;
|
|
136
|
+
/** The open conversation, or the job; null for a new one. The host keeps
|
|
137
|
+
* it in the address. */
|
|
138
|
+
conversationId: string | null;
|
|
139
|
+
/** This person's conversations, as `list()` last read them; null until then. */
|
|
140
|
+
conversations: ConversationSummary[] | null;
|
|
141
|
+
listFailed: boolean;
|
|
142
|
+
constructor(transport: RoomTransport | JobTransport, options?: ChatOptions);
|
|
143
|
+
/** Every turn, in order. A live one grows in place; key on `version`. */
|
|
144
|
+
get turns(): Turn[];
|
|
145
|
+
/** Bumped on every event folded, for `Conversation`'s follow-scroll. */
|
|
146
|
+
get version(): number;
|
|
147
|
+
/** This page is streaming an answer. */
|
|
148
|
+
get running(): boolean;
|
|
149
|
+
/** Something is being answered, here or out of sight: the composer waits
|
|
150
|
+
* and offers Stop. */
|
|
151
|
+
get busy(): boolean;
|
|
152
|
+
/** A job reads what the reader says while it works; a room waits for its answer. */
|
|
153
|
+
get sendWhileRunning(): boolean;
|
|
154
|
+
/** Reopen a conversation, or start watching a job. False when it could not
|
|
155
|
+
* be read, which leaves the page on a new conversation.
|
|
156
|
+
*
|
|
157
|
+
* Safe to call from an effect that follows the page's address: nothing
|
|
158
|
+
* it reads of its own becomes that effect's to follow. */
|
|
159
|
+
open(id: string): Promise<boolean>;
|
|
160
|
+
/** Back to a new conversation. A turn still being answered goes on without
|
|
161
|
+
* the page, and is there when its conversation is reopened. Safe to call
|
|
162
|
+
* from an effect, as `open` is. */
|
|
163
|
+
new(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Ask `question`, or what is in the box. Resolves once the turn settles:
|
|
166
|
+
* true if it was asked, false if it was refused, here or by the server, in
|
|
167
|
+
* which case typed words go back into the box.
|
|
168
|
+
*/
|
|
169
|
+
ask(question?: string): Promise<boolean>;
|
|
170
|
+
/** The last question again, as a new turn: the conversation is append-only,
|
|
171
|
+
* because the agent's transcript is. */
|
|
172
|
+
again(): Promise<boolean>;
|
|
173
|
+
/** Stop the answer being written, wherever it is being written. */
|
|
174
|
+
stop(): Promise<void>;
|
|
175
|
+
/** Read today's allowance again. One that cannot be read is left as it was. */
|
|
176
|
+
refreshQuota(): Promise<void>;
|
|
177
|
+
/** Read this person's conversations. Read again after every answer once
|
|
178
|
+
* read at all, so the list follows what they ask. */
|
|
179
|
+
list(): Promise<boolean>;
|
|
180
|
+
rename(id: string, title: string): Promise<boolean>;
|
|
181
|
+
/** Forget a conversation everywhere. The open one, forgotten, leaves a new one. */
|
|
182
|
+
remove(id: string): Promise<boolean>;
|
|
183
|
+
/** Save a conversation as the file the server names. Fetched rather than
|
|
184
|
+
* navigated to, so one deleted a moment ago elsewhere is a false here,
|
|
185
|
+
* not the reader's page replaced by an error body. */
|
|
186
|
+
download(id: string): Promise<boolean>;
|
|
187
|
+
/** Mark an answer the agent holds. False when it could not be recorded. */
|
|
188
|
+
mark(turn: Turn, verdict: Verdict): Promise<boolean>;
|
|
189
|
+
}
|