@poodle64/librarian 2026.9.13 → 2026.9.15
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 +77 -19
- package/dist/client.d.ts +16 -1
- package/dist/client.js +55 -8
- package/dist/components/activity-group/activity-group.svelte +76 -8
- package/dist/components/agent-transcript/agent-transcript.svelte +346 -150
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +7 -0
- package/dist/components/artefact-card/artefact-card.svelte +30 -2
- package/dist/components/artefact-pane/artefact-pane.svelte +141 -50
- package/dist/components/composer/composer.svelte +255 -38
- package/dist/components/composer/composer.svelte.d.ts +6 -0
- package/dist/components/conversation/conversation.svelte +254 -81
- package/dist/components/conversation/conversation.svelte.d.ts +7 -0
- package/dist/components/document-pane/document-pane.svelte +216 -33
- package/dist/components/markdown/markdown.svelte +73 -24
- package/dist/components/scope-statement/scope-statement.svelte +79 -12
- package/dist/components/source-list/index.d.ts +2 -0
- package/dist/components/source-list/index.js +2 -0
- package/dist/components/source-list/source-list.svelte +137 -0
- package/dist/components/source-list/source-list.svelte.d.ts +13 -0
- package/dist/components/thinking-row/thinking-row.svelte +69 -18
- package/dist/components/tool-row/tool-row.svelte +132 -26
- package/dist/components/working/working.svelte +51 -7
- package/dist/components/working/working.svelte.d.ts +3 -0
- package/dist/copy.d.ts +45 -10
- package/dist/copy.js +77 -32
- package/dist/transcript.svelte.d.ts +10 -0
- package/dist/transcript.svelte.js +4 -1
- package/package.json +4 -2
package/dist/copy.js
CHANGED
|
@@ -2,48 +2,93 @@
|
|
|
2
2
|
* Every word this package puts in front of a reader, in one place.
|
|
3
3
|
*
|
|
4
4
|
* Here rather than inline in each component because these sentences are the
|
|
5
|
-
* package's opinion about how
|
|
6
|
-
* needs a different register — a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* package's opinion about how an agent talks to a colleague, and an app that
|
|
6
|
+
* needs a different register — a room whose boundary is worded by its own
|
|
7
|
+
* subject-matter owner — must be able to change one string without forking a
|
|
8
|
+
* component.
|
|
9
|
+
*
|
|
10
|
+
* NO STRING HERE NAMES A PERSONA. The name is an argument: a consuming app
|
|
11
|
+
* says who is speaking once, and every sentence the package renders is built
|
|
12
|
+
* from that. Milton is the library's own persona and therefore the default,
|
|
13
|
+
* which is a different thing from being baked in — a package that hardcodes
|
|
14
|
+
* one app's persona is a package that lies in every other app, and did:
|
|
15
|
+
* Pebblestone's Penny introduced herself as Milton for a fortnight.
|
|
9
16
|
*
|
|
10
17
|
* Every component takes `copy` as a PARTIAL and resolves it itself, so a host
|
|
11
|
-
* overriding one line does not have to restate the
|
|
12
|
-
*
|
|
13
|
-
* `Conversation`.
|
|
18
|
+
* overriding one line does not have to restate the others, and a component
|
|
19
|
+
* used directly behaves the same as one reached through `Conversation`.
|
|
14
20
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
21
|
+
/** The library's own persona: the default wherever a consumer names none. */
|
|
22
|
+
export const DEFAULT_PERSONA = 'Milton';
|
|
23
|
+
/**
|
|
24
|
+
* The display name for a persona a host identified by its slug.
|
|
25
|
+
*
|
|
26
|
+
* A route parameter is `penny`, and a colleague reading the screen is talking
|
|
27
|
+
* to Penny. A host that already wrote the name the way it should read — any
|
|
28
|
+
* capital at all — is left alone, so "McTavish" and "eight" both survive.
|
|
29
|
+
*/
|
|
30
|
+
export function personaName(name) {
|
|
31
|
+
const given = name.trim();
|
|
32
|
+
if (!given)
|
|
33
|
+
return DEFAULT_PERSONA;
|
|
34
|
+
if (given !== given.toLowerCase())
|
|
35
|
+
return given;
|
|
36
|
+
return given
|
|
37
|
+
.split(/[\s_-]+/)
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
40
|
+
.join(' ');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The package's words, spoken by one named persona.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately free of pronouns: the persona is whoever the consuming app
|
|
46
|
+
* says it is, and a sentence that guesses is wrong for half of them.
|
|
47
|
+
*/
|
|
48
|
+
export function copyFor(name = DEFAULT_PERSONA) {
|
|
49
|
+
const who = personaName(name);
|
|
50
|
+
return {
|
|
51
|
+
verified: 'verified',
|
|
52
|
+
notVerified: 'not verified',
|
|
53
|
+
// Two sentences, both plain: what happened, and the likeliest reason. It
|
|
54
|
+
// says nothing about collections, indexes or retrieval — a colleague
|
|
55
|
+
// reading it has no model of any of those, and the room's own
|
|
56
|
+
// instruction forbids naming them either.
|
|
57
|
+
notHeld: `${who} answered this one without a source. There may be no document here that covers it.`,
|
|
58
|
+
// A run can end `is_error` carrying no message at all, and a turn that
|
|
59
|
+
// simply stops is the one thing a reader must not have to guess about.
|
|
60
|
+
answerFailed: `${who} stopped before finishing this one.`,
|
|
61
|
+
unreachable: `${who} can't be reached right now.`,
|
|
62
|
+
sources: 'Sources',
|
|
63
|
+
suggestions: 'Ask next',
|
|
64
|
+
scope: `What ${who} answers from`,
|
|
65
|
+
welcome: `Ask ${who} a question.`,
|
|
66
|
+
askPlaceholder: `Ask ${who}…`,
|
|
67
|
+
answeringPlaceholder: `${who} is answering…`,
|
|
68
|
+
working: [`${who} is looking`, `${who} is reading`],
|
|
69
|
+
conversationLabel: `Conversation with ${who}`,
|
|
70
|
+
copyAnswer: 'Copy',
|
|
71
|
+
copiedAnswer: 'Copied',
|
|
72
|
+
askAgain: 'Ask again',
|
|
73
|
+
jumpToLatest: 'Jump to latest',
|
|
74
|
+
closeSource: 'Close source',
|
|
75
|
+
documentUnavailable: "That document can't be opened right now."
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** The library's own words, for a host that names no persona. */
|
|
79
|
+
export const DEFAULT_COPY = copyFor();
|
|
36
80
|
/**
|
|
37
|
-
*
|
|
81
|
+
* One persona's words with a host's overrides on top.
|
|
38
82
|
*
|
|
39
83
|
* An explicitly `undefined` key is dropped rather than spread, because
|
|
40
84
|
* `{...defaults, ...{ sources: undefined }}` leaves a component rendering
|
|
41
85
|
* nothing at all — and that is exactly the shape a host produces by passing a
|
|
42
86
|
* value it computed from state that has not loaded yet.
|
|
43
87
|
*/
|
|
44
|
-
export function resolveCopy(overrides) {
|
|
88
|
+
export function resolveCopy(overrides, name) {
|
|
89
|
+
const base = name === undefined ? DEFAULT_COPY : copyFor(name);
|
|
45
90
|
if (!overrides)
|
|
46
|
-
return
|
|
91
|
+
return base;
|
|
47
92
|
const given = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== undefined));
|
|
48
|
-
return { ...
|
|
93
|
+
return { ...base, ...given };
|
|
49
94
|
}
|
|
@@ -33,7 +33,12 @@ export interface Outcome {
|
|
|
33
33
|
costUsd?: number;
|
|
34
34
|
durationMs?: number;
|
|
35
35
|
isError?: boolean;
|
|
36
|
+
/** A message the run itself gave. Words, never a code. */
|
|
36
37
|
error?: string;
|
|
38
|
+
/** The stream never opened, or died before the run said anything. The
|
|
39
|
+
* sentence a reader sees is the persona's own (`copy.unreachable`), which
|
|
40
|
+
* only the rendering layer knows, so this carries the FACT and no words. */
|
|
41
|
+
unreachable?: boolean;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
44
|
* The question as the READER asked it.
|
|
@@ -117,6 +122,11 @@ export type Segment = ActivityGroup | TextBlock;
|
|
|
117
122
|
export interface Turn {
|
|
118
123
|
id: string;
|
|
119
124
|
question: string;
|
|
125
|
+
/** When the question was asked, epoch ms. A host that persists its
|
|
126
|
+
* conversations supplies it; a host that does not gets one stamped when
|
|
127
|
+
* the turn first appears on screen, which is right for a live turn and
|
|
128
|
+
* honestly absent for one read back out of history. */
|
|
129
|
+
at?: number;
|
|
120
130
|
blocks: Block[];
|
|
121
131
|
outcome: Outcome | null;
|
|
122
132
|
citations?: Citation[];
|
|
@@ -189,8 +189,11 @@ export class Transcript {
|
|
|
189
189
|
this.suggestions = (event.items ?? []).filter((item) => typeof item === 'string' && item.trim().length > 0);
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
|
+
// No persona and no sentence at this layer: `client.ts` raises this for a
|
|
193
|
+
// stream that would not open or died half-way, and the turn renders the
|
|
194
|
+
// unreachable line in whichever persona's voice the host named.
|
|
192
195
|
if (event.type === 'library_error') {
|
|
193
|
-
this.outcome = { isError: true, error: event.error
|
|
196
|
+
this.outcome = { isError: true, unreachable: true, error: event.error };
|
|
194
197
|
return;
|
|
195
198
|
}
|
|
196
199
|
// A tool RESULT arrives as a user message carrying tool_result blocks.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/librarian",
|
|
3
|
-
"version": "2026.9.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2026.9.15",
|
|
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",
|
|
7
7
|
"repository": {
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"svelte": "^5.54.0",
|
|
56
|
+
"@poodle64/design-tokens": "^2026.8.1",
|
|
56
57
|
"@poodle64/ui": "^2026.9.2",
|
|
57
58
|
"@lucide/svelte": "^1.7.0",
|
|
58
59
|
"marked": "^18.0.11",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@lucide/svelte": "^1.7.0",
|
|
65
|
+
"@poodle64/design-tokens": "workspace:*",
|
|
64
66
|
"@poodle64/ui": "workspace:*",
|
|
65
67
|
"@sveltejs/kit": "^2.63.0",
|
|
66
68
|
"@sveltejs/package": "^2.5.8",
|