@poodle64/librarian 2026.9.5 → 2026.9.7
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 +143 -44
- package/dist/attachments.d.ts +30 -0
- package/dist/attachments.js +80 -0
- package/dist/citations.d.ts +63 -0
- package/dist/citations.js +89 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.js +39 -13
- package/dist/components/activity-group/activity-group.svelte +36 -44
- package/dist/components/activity-group/activity-group.svelte.d.ts +1 -1
- package/dist/components/agent-transcript/agent-transcript.svelte +166 -26
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +8 -0
- package/dist/components/composer/composer.svelte +212 -59
- package/dist/components/composer/composer.svelte.d.ts +5 -1
- package/dist/components/conversation/conversation.svelte +179 -0
- package/dist/components/conversation/conversation.svelte.d.ts +26 -0
- package/dist/components/conversation/index.d.ts +2 -0
- package/dist/components/conversation/index.js +2 -0
- package/dist/components/document-pane/document-pane.svelte +177 -0
- package/dist/components/document-pane/document-pane.svelte.d.ts +9 -0
- package/dist/components/document-pane/index.d.ts +2 -0
- package/dist/components/document-pane/index.js +2 -0
- package/dist/components/markdown/index.d.ts +1 -1
- package/dist/components/markdown/index.js +1 -1
- package/dist/components/markdown/markdown.d.ts +13 -0
- package/dist/components/markdown/markdown.js +49 -1
- package/dist/components/markdown/markdown.svelte +85 -3
- package/dist/components/markdown/markdown.svelte.d.ts +6 -0
- package/dist/components/tool-row/tool-row.svelte +5 -11
- package/dist/components/working/working.svelte +1 -9
- package/dist/follow-scroll.svelte.d.ts +34 -0
- package/dist/follow-scroll.svelte.js +46 -0
- package/dist/transcript.svelte.d.ts +36 -4
- package/dist/transcript.svelte.js +50 -26
- package/package.json +16 -2
|
@@ -6,43 +6,62 @@
|
|
|
6
6
|
* deltas that belong to each. An event type this does not know about is kept
|
|
7
7
|
* verbatim under `other`, so nothing is silently dropped.
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* The question as the READER asked it.
|
|
11
|
+
*
|
|
12
|
+
* A host prepends a system preamble to every question before it goes to the
|
|
13
|
+
* library — cadmus sends `{room.preamble}\n\n{question}` — and echoing that
|
|
14
|
+
* back into the transcript shows a colleague the machine room. Leading
|
|
15
|
+
* blank-line-separated paragraphs addressed to the model are dropped.
|
|
16
|
+
*
|
|
17
|
+
* The LAST paragraph is never dropped, whatever it starts with: someone whose
|
|
18
|
+
* entire question is "You are wrong about the leave rule" must still see it.
|
|
19
|
+
*/
|
|
20
|
+
export function readerQuestion(question) {
|
|
21
|
+
const parts = question.split(/\n{2,}/);
|
|
22
|
+
let start = 0;
|
|
23
|
+
while (start < parts.length - 1 && /^(you are|you're|your role|act as|system:)\b/i.test(parts[start].trim()))
|
|
24
|
+
start += 1;
|
|
25
|
+
return parts.slice(start).join('\n\n').trim();
|
|
26
|
+
}
|
|
27
|
+
/** What Milton DID, in a reader's own words — never the tool's name or the
|
|
28
|
+
* raw command it ran.
|
|
10
29
|
*
|
|
11
30
|
* The collapsed row is read by someone asking a question about documents, not
|
|
12
31
|
* by an engineer: `Bash ls -1 .` tells them nothing and looks like a leak from
|
|
13
|
-
* the machine room.
|
|
14
|
-
*
|
|
32
|
+
* the machine room. Milton never narrates how he searched, so a tool this
|
|
33
|
+
* does not recognise falls back to something that names no mechanism at all.
|
|
15
34
|
*/
|
|
16
35
|
export function describe(block) {
|
|
17
36
|
const input = parseInput(block);
|
|
18
37
|
const command = typeof input.command === 'string' ? input.command : '';
|
|
19
38
|
if (block.name === 'Read') {
|
|
20
39
|
const path = str(input.file_path ?? input.path);
|
|
21
|
-
return { verb: '
|
|
40
|
+
return { verb: 'Read', object: documentName(path) };
|
|
22
41
|
}
|
|
23
42
|
if (block.name === 'Grep') {
|
|
24
|
-
return { verb: '
|
|
43
|
+
return { verb: 'Looked for', object: str(input.pattern) };
|
|
25
44
|
}
|
|
26
45
|
if (block.name === 'Glob') {
|
|
27
|
-
return { verb: '
|
|
46
|
+
return { verb: 'Looked for documents', object: '' };
|
|
28
47
|
}
|
|
29
48
|
if (block.name === 'Bash') {
|
|
30
49
|
if (/\bgrep\b|\brg\b/.test(command)) {
|
|
31
50
|
const quoted = command.match(/["']([^"']{2,60})["']/);
|
|
32
|
-
return { verb: '
|
|
51
|
+
return { verb: 'Looked for', object: quoted?.[1] ?? 'a phrase' };
|
|
33
52
|
}
|
|
34
53
|
if (/\bls\b/.test(command))
|
|
35
|
-
return { verb: '
|
|
54
|
+
return { verb: 'Looked through the library', object: '' };
|
|
36
55
|
if (/\bfind\b/.test(command))
|
|
37
|
-
return { verb: '
|
|
56
|
+
return { verb: 'Looked for documents', object: '' };
|
|
38
57
|
if (/\bcat\b|\bhead\b|\bsed\b/.test(command)) {
|
|
39
|
-
return { verb: '
|
|
58
|
+
return { verb: 'Read', object: documentName(lastPath(command)) };
|
|
40
59
|
}
|
|
41
60
|
if (/\bwc\b/.test(command))
|
|
42
|
-
return { verb: '
|
|
43
|
-
return { verb: '
|
|
61
|
+
return { verb: 'Checked', object: '' };
|
|
62
|
+
return { verb: 'Looked into it', object: '' };
|
|
44
63
|
}
|
|
45
|
-
return { verb:
|
|
64
|
+
return { verb: 'Looked into it', object: '' };
|
|
46
65
|
}
|
|
47
66
|
function parseInput(block) {
|
|
48
67
|
try {
|
|
@@ -64,12 +83,6 @@ function documentName(path) {
|
|
|
64
83
|
return prettify(segments.at(-2) ?? '');
|
|
65
84
|
return prettify(page.replace(/\.md$/, ''));
|
|
66
85
|
}
|
|
67
|
-
function listTarget(command) {
|
|
68
|
-
const target = command.trim().split(/\s+/).at(-1) ?? '';
|
|
69
|
-
if (!target || target === '.' || target.startsWith('-'))
|
|
70
|
-
return 'the collections';
|
|
71
|
-
return prettify(target.replace(/\/$/, ''));
|
|
72
|
-
}
|
|
73
86
|
function lastPath(command) {
|
|
74
87
|
return (command
|
|
75
88
|
.split(/\s+/)
|
|
@@ -123,6 +136,8 @@ export class Transcript {
|
|
|
123
136
|
sessionId = $state(null);
|
|
124
137
|
model = $state(null);
|
|
125
138
|
outcome = $state(null);
|
|
139
|
+
/** Sources for the answer, from the library's own `citations` frame. */
|
|
140
|
+
citations = $state([]);
|
|
126
141
|
other = $state([]);
|
|
127
142
|
/** Content-block index is per MESSAGE, so it repeats across turns; this
|
|
128
143
|
* maps the live index onto a position in the flat list. Cleared whenever a
|
|
@@ -135,6 +150,7 @@ export class Transcript {
|
|
|
135
150
|
reset() {
|
|
136
151
|
this.blocks = [];
|
|
137
152
|
this.outcome = null;
|
|
153
|
+
this.citations = [];
|
|
138
154
|
this.other = [];
|
|
139
155
|
this.#open.clear();
|
|
140
156
|
}
|
|
@@ -154,8 +170,14 @@ export class Transcript {
|
|
|
154
170
|
};
|
|
155
171
|
return;
|
|
156
172
|
}
|
|
173
|
+
// Emitted after the final assistant text, so it lands on a turn that is
|
|
174
|
+
// otherwise complete.
|
|
175
|
+
if (event.type === 'citations') {
|
|
176
|
+
this.citations = event.items ?? [];
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
157
179
|
if (event.type === 'library_error') {
|
|
158
|
-
this.outcome = { isError: true, error: event.error ?? '
|
|
180
|
+
this.outcome = { isError: true, error: event.error ?? "Milton can't be reached right now." };
|
|
159
181
|
return;
|
|
160
182
|
}
|
|
161
183
|
// A tool RESULT arrives as a user message carrying tool_result blocks.
|
|
@@ -289,9 +311,9 @@ function sameStep(a, b) {
|
|
|
289
311
|
}
|
|
290
312
|
function tally(group, block) {
|
|
291
313
|
const { verb } = describe(block);
|
|
292
|
-
if (verb === '
|
|
314
|
+
if (verb === 'Looked for')
|
|
293
315
|
group.searches += 1;
|
|
294
|
-
if (verb === '
|
|
316
|
+
if (verb === 'Read')
|
|
295
317
|
group.documents += 1;
|
|
296
318
|
const collection = collectionOf(block);
|
|
297
319
|
if (collection && !group.collections.includes(collection))
|
|
@@ -303,14 +325,16 @@ function collectionOf(block) {
|
|
|
303
325
|
const match = raw.match(/(?:^|["'\s/])([a-z0-9]+(?:-[a-z0-9]+)+)\/local\//);
|
|
304
326
|
return match?.[1] ?? '';
|
|
305
327
|
}
|
|
306
|
-
/** One line describing a whole investigation, for the collapsed state.
|
|
328
|
+
/** One line describing a whole investigation, for the collapsed state.
|
|
329
|
+
*
|
|
330
|
+
* Counts of what Milton did are fine ("1 search · 2 documents read"); how he
|
|
331
|
+
* organises what he knows is not — no collection count, no shelf, no corpus.
|
|
332
|
+
*/
|
|
307
333
|
export function summariseActivity(group) {
|
|
308
334
|
const parts = [];
|
|
309
335
|
if (group.searches)
|
|
310
336
|
parts.push(`${group.searches} search${group.searches === 1 ? '' : 'es'}`);
|
|
311
337
|
if (group.documents)
|
|
312
338
|
parts.push(`${group.documents} document${group.documents === 1 ? '' : 's'} read`);
|
|
313
|
-
|
|
314
|
-
parts.push(`${group.collections.length} collection${group.collections.length === 1 ? '' : 's'}`);
|
|
315
|
-
return parts.length ? parts.join(' · ') : 'Worked on it';
|
|
339
|
+
return parts.length ? parts.join(' · ') : 'Looked into it';
|
|
316
340
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/librarian",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.7",
|
|
4
4
|
"description": "Milton's conversation surface as a consumable Svelte 5 package: the stream client, transcript state and chat components (transcript, composer, markdown) every household app renders instead of rebuilding.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,18 @@
|
|
|
30
30
|
"types": "./dist/history.svelte.d.ts",
|
|
31
31
|
"svelte": "./dist/history.svelte.js"
|
|
32
32
|
},
|
|
33
|
+
"./citations": {
|
|
34
|
+
"types": "./dist/citations.d.ts",
|
|
35
|
+
"svelte": "./dist/citations.js"
|
|
36
|
+
},
|
|
37
|
+
"./attachments": {
|
|
38
|
+
"types": "./dist/attachments.d.ts",
|
|
39
|
+
"svelte": "./dist/attachments.js"
|
|
40
|
+
},
|
|
41
|
+
"./follow-scroll": {
|
|
42
|
+
"types": "./dist/follow-scroll.svelte.d.ts",
|
|
43
|
+
"svelte": "./dist/follow-scroll.svelte.js"
|
|
44
|
+
},
|
|
33
45
|
"./*": {
|
|
34
46
|
"types": "./dist/components/*/index.d.ts",
|
|
35
47
|
"svelte": "./dist/components/*/index.js"
|
|
@@ -55,6 +67,7 @@
|
|
|
55
67
|
"isomorphic-dompurify": "^3.23.0",
|
|
56
68
|
"jsdom": "^29.1.1",
|
|
57
69
|
"marked": "^18.0.11",
|
|
70
|
+
"playwright": "^1.62.0",
|
|
58
71
|
"publint": "^0.3.15",
|
|
59
72
|
"shiki": "^4.4.3",
|
|
60
73
|
"svelte": "^5.56.2",
|
|
@@ -67,7 +80,8 @@
|
|
|
67
80
|
"build": "svelte-kit sync && svelte-package && publint",
|
|
68
81
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
69
82
|
"test": "pnpm run build && vitest run",
|
|
70
|
-
"prepublishOnly": "pnpm run build"
|
|
83
|
+
"prepublishOnly": "pnpm run build",
|
|
84
|
+
"screenshots": "node scripts/screenshots.mjs"
|
|
71
85
|
},
|
|
72
86
|
"packageManager": "pnpm@10.28.0"
|
|
73
87
|
}
|