@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/dist/copy.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface LibrarianCopy {
|
|
|
31
31
|
answerFailed: string;
|
|
32
32
|
/** Shown when the stream never opened, or died mid-answer. */
|
|
33
33
|
unreachable: string;
|
|
34
|
+
/** Shown on a question stopped before a word of its answer came. */
|
|
35
|
+
noAnswer: string;
|
|
34
36
|
/** Heading over the list of what an answer cited. */
|
|
35
37
|
sources: string;
|
|
36
38
|
/** Heading over the follow-up questions the persona offered. */
|
|
@@ -55,6 +57,41 @@ export interface LibrarianCopy {
|
|
|
55
57
|
/** An artefact card's action, and the same action while it is open. */
|
|
56
58
|
openArtefact: string;
|
|
57
59
|
showingArtefact: string;
|
|
60
|
+
/** Beside the clock while the question waits behind someone else's. */
|
|
61
|
+
waiting: string;
|
|
62
|
+
/** Beside the clock on an answer this page did not start. */
|
|
63
|
+
stillAnswering: string;
|
|
64
|
+
/** The answer mark: the two verdicts, the optional note, and after. */
|
|
65
|
+
markHelpful: string;
|
|
66
|
+
markNotHelpful: string;
|
|
67
|
+
markNote: string;
|
|
68
|
+
markSend: string;
|
|
69
|
+
markNoted: string;
|
|
70
|
+
markFailed: string;
|
|
71
|
+
/** The fair-use notice, while there are questions left and once there are
|
|
72
|
+
* none, filled by `fill()`: `{resets}` is a time already, "midnight" or
|
|
73
|
+
* "2:00 pm". */
|
|
74
|
+
questionsLeft: string;
|
|
75
|
+
limitReached: string;
|
|
76
|
+
/** `{resets}` when the allowance comes back at the turn of the day. */
|
|
77
|
+
midnight: string;
|
|
78
|
+
support: string;
|
|
79
|
+
/** The conversation list. */
|
|
80
|
+
pastQuestions: string;
|
|
81
|
+
newQuestion: string;
|
|
82
|
+
readingPastQuestions: string;
|
|
83
|
+
noPastQuestions: string;
|
|
84
|
+
pastQuestionsFailed: string;
|
|
85
|
+
conversationActions: string;
|
|
86
|
+
rename: string;
|
|
87
|
+
download: string;
|
|
88
|
+
delete: string;
|
|
89
|
+
save: string;
|
|
90
|
+
cancel: string;
|
|
91
|
+
deletePrompt: string;
|
|
92
|
+
renameFailed: string;
|
|
93
|
+
downloadFailed: string;
|
|
94
|
+
deleteFailed: string;
|
|
58
95
|
}
|
|
59
96
|
/**
|
|
60
97
|
* The display name for a persona a host identified by its slug.
|
|
@@ -71,6 +108,9 @@ export declare function personaName(name: string): string;
|
|
|
71
108
|
* says it is, and a sentence that guesses is wrong for half of them.
|
|
72
109
|
*/
|
|
73
110
|
export declare function copyFor(name?: string): LibrarianCopy;
|
|
111
|
+
/** A sentence with `{name}` places, filled. A place with no value stays as
|
|
112
|
+
* written, so a host's override that drops one loses nothing else. */
|
|
113
|
+
export declare function fill(template: string, values: Record<string, string | number>): string;
|
|
74
114
|
/** The library's own words, for a host that names no persona. */
|
|
75
115
|
export declare const DEFAULT_COPY: LibrarianCopy;
|
|
76
116
|
/**
|
package/dist/copy.js
CHANGED
|
@@ -59,6 +59,7 @@ export function copyFor(name = DEFAULT_PERSONA) {
|
|
|
59
59
|
// simply stops is the one thing a reader must not have to guess about.
|
|
60
60
|
answerFailed: `${who} stopped before finishing this one.`,
|
|
61
61
|
unreachable: `${who} can't be reached right now.`,
|
|
62
|
+
noAnswer: 'Stopped before an answer came.',
|
|
62
63
|
sources: 'Sources',
|
|
63
64
|
suggestions: 'Ask next',
|
|
64
65
|
scope: `What ${who} answers from`,
|
|
@@ -74,9 +75,43 @@ export function copyFor(name = DEFAULT_PERSONA) {
|
|
|
74
75
|
closeSource: 'Close source',
|
|
75
76
|
documentUnavailable: "That document can't be opened right now.",
|
|
76
77
|
openArtefact: 'Open',
|
|
77
|
-
showingArtefact: 'Showing'
|
|
78
|
+
showingArtefact: 'Showing',
|
|
79
|
+
waiting: `${who} is answering another question first`,
|
|
80
|
+
stillAnswering: `${who} is still answering`,
|
|
81
|
+
markHelpful: 'Helpful',
|
|
82
|
+
markNotHelpful: 'Not helpful',
|
|
83
|
+
markNote: 'Anything to add? (optional)',
|
|
84
|
+
markSend: 'Send',
|
|
85
|
+
markNoted: 'Thanks, noted.',
|
|
86
|
+
markFailed: "That couldn't be sent. Nothing was recorded.",
|
|
87
|
+
// The industry wording, whole: the number, that it is a fair-use limit
|
|
88
|
+
// rather than a fault, and when it comes back. No apology.
|
|
89
|
+
questionsLeft: '{remaining} of {limit} questions left today. Resets at {resets}.',
|
|
90
|
+
limitReached: "You've reached today's fair-use limit of {limit} questions. It resets at {resets}.",
|
|
91
|
+
midnight: 'midnight',
|
|
92
|
+
support: 'Support',
|
|
93
|
+
pastQuestions: 'Past questions',
|
|
94
|
+
newQuestion: 'New question',
|
|
95
|
+
readingPastQuestions: 'Reading your past questions…',
|
|
96
|
+
noPastQuestions: 'No past questions yet.',
|
|
97
|
+
pastQuestionsFailed: "Your past questions couldn't be read just now.",
|
|
98
|
+
conversationActions: 'More',
|
|
99
|
+
rename: 'Rename',
|
|
100
|
+
download: 'Download',
|
|
101
|
+
delete: 'Delete',
|
|
102
|
+
save: 'Save',
|
|
103
|
+
cancel: 'Cancel',
|
|
104
|
+
deletePrompt: 'Delete this conversation? It goes from every device.',
|
|
105
|
+
renameFailed: "That conversation couldn't be renamed.",
|
|
106
|
+
downloadFailed: "That conversation couldn't be downloaded.",
|
|
107
|
+
deleteFailed: "That conversation couldn't be deleted."
|
|
78
108
|
};
|
|
79
109
|
}
|
|
110
|
+
/** A sentence with `{name}` places, filled. A place with no value stays as
|
|
111
|
+
* written, so a host's override that drops one loses nothing else. */
|
|
112
|
+
export function fill(template, values) {
|
|
113
|
+
return template.replace(/\{(\w+)\}/g, (place, key) => key in values ? String(values[key]) : place);
|
|
114
|
+
}
|
|
80
115
|
/** The library's own words, for a host that names no persona. */
|
|
81
116
|
export const DEFAULT_COPY = copyFor();
|
|
82
117
|
/**
|
package/dist/session.svelte.js
CHANGED
|
@@ -55,6 +55,12 @@ export class Session {
|
|
|
55
55
|
}
|
|
56
56
|
if (!FOLDED.has(event.type))
|
|
57
57
|
return;
|
|
58
|
+
// A user frame that is neither a replayed message nor a tool's result is
|
|
59
|
+
// the CLI nudging its own model (`isSynthetic`: "[structured-output-
|
|
60
|
+
// enforce] You MUST call the StructuredOutput tool…"): neither side of
|
|
61
|
+
// the conversation.
|
|
62
|
+
if (event.type === 'user' && !contentOf(event).some((b) => b.type === 'tool_result'))
|
|
63
|
+
return;
|
|
58
64
|
// Settled by its own `result`. A stream that broke mid-run is not: the
|
|
59
65
|
// run goes on, and a watch opened again folds the rest onto it.
|
|
60
66
|
const last = this.turns.at(-1);
|
|
@@ -90,7 +96,7 @@ export class Session {
|
|
|
90
96
|
}
|
|
91
97
|
/** The words of a replayed frame: a bare string, or its text blocks. */
|
|
92
98
|
function said(event) {
|
|
93
|
-
const content = event.message
|
|
99
|
+
const content = typeof event.message === 'object' ? event.message.content : undefined;
|
|
94
100
|
if (typeof content === 'string')
|
|
95
101
|
return content;
|
|
96
102
|
return contentOf(event)
|
|
@@ -299,7 +299,7 @@ export function fold(into, state, event) {
|
|
|
299
299
|
}
|
|
300
300
|
/** A message's content blocks; a user frame may carry a bare string instead. */
|
|
301
301
|
export function contentOf(event) {
|
|
302
|
-
const content = event.message
|
|
302
|
+
const content = typeof event.message === 'object' ? event.message.content : undefined;
|
|
303
303
|
return Array.isArray(content) ? content : [];
|
|
304
304
|
}
|
|
305
305
|
function wholeBlock(block, index) {
|
|
@@ -366,9 +366,11 @@ export function segment(blocks, describeTool) {
|
|
|
366
366
|
let current = null;
|
|
367
367
|
let lastTool = -1;
|
|
368
368
|
for (let i = 0; i < blocks.length; i += 1)
|
|
369
|
-
if (blocks[i]
|
|
369
|
+
if (isStep(blocks[i]))
|
|
370
370
|
lastTool = i;
|
|
371
371
|
for (const [i, block] of blocks.entries()) {
|
|
372
|
+
if (block.kind === 'tool' && !isStep(block))
|
|
373
|
+
continue;
|
|
372
374
|
if (block.kind === 'text' && i > lastTool) {
|
|
373
375
|
current = null;
|
|
374
376
|
out.push(block);
|
|
@@ -397,6 +399,18 @@ export function segment(blocks, describeTool) {
|
|
|
397
399
|
}
|
|
398
400
|
return out;
|
|
399
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* The tool the CLI adds under `--json-schema`. Calling it hands in the run's
|
|
404
|
+
* answer, which arrives again whole on the `result` (`structured_output`): it
|
|
405
|
+
* is not a step of the work, and the words before it are the answer, not
|
|
406
|
+
* narration ahead of more work. Measured on Claude Code 2.1.283, where a run
|
|
407
|
+
* with a schema always ends in this call.
|
|
408
|
+
*/
|
|
409
|
+
const HANDS_IN = 'StructuredOutput';
|
|
410
|
+
/** A call that is part of the work, rather than the handing-in of its answer. */
|
|
411
|
+
function isStep(block) {
|
|
412
|
+
return block.kind === 'tool' && block.name !== HANDS_IN;
|
|
413
|
+
}
|
|
400
414
|
function sameStep(last, block, words) {
|
|
401
415
|
if (last.block.kind !== block.kind)
|
|
402
416
|
return false;
|
|
@@ -436,7 +450,7 @@ export function summariseActivity(group) {
|
|
|
436
450
|
export function readFrom(blocks, describeTool) {
|
|
437
451
|
const out = [];
|
|
438
452
|
for (const block of blocks) {
|
|
439
|
-
if (block.kind !== 'tool' || block.result === undefined || block.isError)
|
|
453
|
+
if (block.kind !== 'tool' || !isStep(block) || block.result === undefined || block.isError)
|
|
440
454
|
continue;
|
|
441
455
|
const source = wordsFor(block, describeTool).source;
|
|
442
456
|
if (!source || out.some((c) => c.document_id === source.id))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/librarian",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.22",
|
|
4
4
|
"description": "An agent's conversation surface as a consumable Svelte 5 package: the stream client, transcript state and self-styling chat components (transcript, composer, markdown) every household app renders instead of rebuilding, speaking as whichever persona the app names.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"types": "./dist/session.svelte.d.ts",
|
|
31
31
|
"svelte": "./dist/session.svelte.js"
|
|
32
32
|
},
|
|
33
|
+
"./chat": {
|
|
34
|
+
"types": "./dist/chat.svelte.d.ts",
|
|
35
|
+
"svelte": "./dist/chat.svelte.js"
|
|
36
|
+
},
|
|
33
37
|
"./history": {
|
|
34
38
|
"types": "./dist/history.svelte.d.ts",
|
|
35
39
|
"svelte": "./dist/history.svelte.js"
|