@poodle64/librarian 2026.10.3 → 2026.10.4

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.
Files changed (38) hide show
  1. package/README.md +10 -4
  2. package/dist/chat.svelte.d.ts +40 -0
  3. package/dist/chat.svelte.js +57 -3
  4. package/dist/client.d.ts +3 -0
  5. package/dist/client.js +4 -1
  6. package/dist/components/activity-group/activity-group.svelte +21 -108
  7. package/dist/components/activity-group/activity-group.svelte.d.ts +3 -1
  8. package/dist/components/agent-transcript/agent-transcript.svelte +164 -4
  9. package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +18 -0
  10. package/dist/components/artefact-pane/artefact-pane.svelte +162 -18
  11. package/dist/components/artefact-pane/artefact-pane.svelte.d.ts +10 -1
  12. package/dist/components/conversation/conversation.svelte +74 -4
  13. package/dist/components/conversation/conversation.svelte.d.ts +15 -0
  14. package/dist/components/markdown/markdown.d.ts +11 -0
  15. package/dist/components/markdown/markdown.js +62 -2
  16. package/dist/components/markdown/markdown.svelte +22 -2
  17. package/dist/components/markdown/markdown.svelte.d.ts +4 -0
  18. package/dist/components/thinking-row/thinking-row.svelte +1 -0
  19. package/dist/components/thinking-row/thinking-row.svelte.d.ts +1 -0
  20. package/dist/components/tool-row/tool-row.svelte +40 -20
  21. package/dist/components/tool-row/tool-row.svelte.d.ts +2 -3
  22. package/dist/components/web-list/index.d.ts +2 -0
  23. package/dist/components/web-list/index.js +2 -0
  24. package/dist/components/web-list/web-list.svelte +84 -0
  25. package/dist/components/web-list/web-list.svelte.d.ts +9 -0
  26. package/dist/components/written-card/index.d.ts +2 -0
  27. package/dist/components/written-card/index.js +2 -0
  28. package/dist/components/written-card/written-card.svelte +130 -0
  29. package/dist/components/written-card/written-card.svelte.d.ts +15 -0
  30. package/dist/copy.d.ts +19 -0
  31. package/dist/copy.js +13 -0
  32. package/dist/transcript.svelte.d.ts +15 -34
  33. package/dist/transcript.svelte.js +52 -55
  34. package/dist/web-sources.d.ts +18 -0
  35. package/dist/web-sources.js +58 -0
  36. package/dist/written.d.ts +46 -0
  37. package/dist/written.js +41 -0
  38. package/package.json +5 -1
@@ -7,34 +7,40 @@
7
7
 
8
8
  The row says what the persona DID, in a reader's own words — never the
9
9
  tool's name or the raw command it ran: the host's `DescribeTool`, or the
10
- package's own `describe()`. Expanding a settled row shows what came back,
11
- never what was typed.
10
+ package's own `describe()`. Expanding it shows what went in and what came
11
+ back, the way Claude Code's own panel does.
12
12
  -->
13
13
  <script lang="ts">
14
14
  import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
15
15
  import { describe, type ToolBlock, type ToolWords } from '../../transcript.svelte';
16
+ import { resolveCopy, type ChatCopy } from '../../copy';
16
17
 
17
18
  interface Props {
18
19
  block: ToolBlock;
19
20
  running: boolean;
20
- /** Identical consecutive steps folded into this row — five pages of one
21
- * document is one act of reading to a human. */
22
- repeats?: number;
23
21
  /** The call in the persona's words. Absent takes the package's own. */
24
22
  words?: ToolWords;
23
+ copy?: Partial<ChatCopy>;
25
24
  }
26
25
 
27
- let { block, running, repeats = 1, words }: Props = $props();
26
+ let { block, running, words, copy }: Props = $props();
28
27
  let open = $state(false);
29
28
 
29
+ const label = $derived(resolveCopy(copy));
30
30
  const settled = $derived(block.result !== undefined);
31
31
  const said = $derived(words ?? describe(block));
32
32
  const tone = $derived(block.isError ? 'error' : settled ? 'success' : 'info');
33
- const again = $derived(
34
- said.repeat
35
- ? `· ${repeats} ${repeats === 1 ? said.repeat[0] : said.repeat[1]}`
36
- : `× ${repeats}`
37
- );
33
+ // Pretty while it parses; the raw text while it is still arriving.
34
+ const input = $derived.by(() => {
35
+ try {
36
+ const parsed: unknown = JSON.parse(block.rawInput);
37
+ return typeof parsed === 'object' && parsed !== null && Object.keys(parsed).length === 0
38
+ ? ''
39
+ : JSON.stringify(parsed, null, 2);
40
+ } catch {
41
+ return block.rawInput;
42
+ }
43
+ });
38
44
  </script>
39
45
 
40
46
  <div class="ds-lib-tool">
@@ -42,23 +48,27 @@
42
48
  type="button"
43
49
  class="ds-lib-tool-row"
44
50
  onclick={() => (open = !open)}
45
- disabled={!settled}
46
51
  aria-expanded={open}
47
52
  >
48
53
  <span class="ds-lib-tool-chevron" class:is-open={open}>
49
- {#if settled}<ChevronRightIcon size={12} />{/if}
54
+ <ChevronRightIcon size={12} />
50
55
  </span>
51
56
  <span class="ds-lib-tool-dot" data-tone={tone} class:is-live={!settled && running}></span>
52
57
  <span class="ds-lib-tool-said">
53
58
  <span class="ds-lib-tool-verb">{said.verb}</span>
54
59
  {#if said.object}<span class="ds-lib-tool-object">{said.object}</span>{/if}
55
- {#if repeats > 1}<span>{again}</span>{/if}
56
60
  </span>
61
+ {#if block.isError}<span class="ds-lib-tool-failed">{label.stepFailed}</span>{/if}
57
62
  </button>
58
63
 
59
64
  {#if open}
60
65
  <div class="ds-lib-tool-body">
66
+ {#if input}
67
+ <p class="ds-lib-tool-label">{label.stepInput}</p>
68
+ <pre class="ds-lib-tool-result">{input}</pre>
69
+ {/if}
61
70
  {#if block.result}
71
+ <p class="ds-lib-tool-label">{label.stepResult}</p>
62
72
  <pre class="ds-lib-tool-result">{block.result}</pre>
63
73
  {/if}
64
74
  </div>
@@ -87,10 +97,6 @@
87
97
  cursor: pointer;
88
98
  }
89
99
 
90
- .ds-lib-tool-row:disabled {
91
- cursor: default;
92
- }
93
-
94
100
  .ds-lib-tool-chevron {
95
101
  display: flex;
96
102
  width: 0.75rem;
@@ -150,8 +156,8 @@
150
156
  }
151
157
 
152
158
  .ds-lib-tool-result {
153
- margin-top: 0.25rem;
154
- max-height: 18rem;
159
+ margin: 0.125rem 0 0;
160
+ max-height: 12rem;
155
161
  overflow: auto;
156
162
  color: var(--ds-color-muted-foreground);
157
163
  font-family: var(--ds-font-code);
@@ -159,6 +165,20 @@
159
165
  white-space: pre-wrap;
160
166
  }
161
167
 
168
+ .ds-lib-tool-label {
169
+ margin: 0.25rem 0 0;
170
+ color: var(--ds-color-muted-foreground);
171
+ font-size: var(--ds-text-2xs);
172
+ letter-spacing: 0.05em;
173
+ text-transform: uppercase;
174
+ }
175
+
176
+ .ds-lib-tool-failed {
177
+ flex: none;
178
+ color: var(--ds-color-status-error);
179
+ font-size: var(--ds-text-2xs);
180
+ }
181
+
162
182
  .ds-lib-tool-lines {
163
183
  margin: 0;
164
184
  padding-inline-start: 1.375rem;
@@ -1,12 +1,11 @@
1
1
  import { type ToolBlock, type ToolWords } from '../../transcript.svelte';
2
+ import { type ChatCopy } from '../../copy';
2
3
  interface Props {
3
4
  block: ToolBlock;
4
5
  running: boolean;
5
- /** Identical consecutive steps folded into this row — five pages of one
6
- * document is one act of reading to a human. */
7
- repeats?: number;
8
6
  /** The call in the persona's words. Absent takes the package's own. */
9
7
  words?: ToolWords;
8
+ copy?: Partial<ChatCopy>;
10
9
  }
11
10
  declare const ToolRow: import("svelte").Component<Props, {}, "">;
12
11
  type ToolRow = ReturnType<typeof ToolRow>;
@@ -0,0 +1,2 @@
1
+ export { default as WebList } from './web-list.svelte';
2
+ export { default } from './web-list.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as WebList } from './web-list.svelte';
2
+ export { default } from './web-list.svelte';
@@ -0,0 +1,84 @@
1
+ <!--
2
+ The web pages an answer drew on, apart from the library's numbered sources.
3
+ Never numbered; each opens in a new tab.
4
+ -->
5
+ <script lang="ts">
6
+ import type { ChatCopy } from '../../copy';
7
+ import type { WebSource } from '../../web-sources';
8
+
9
+ interface Props {
10
+ sources: WebSource[];
11
+ words: ChatCopy;
12
+ }
13
+
14
+ let { sources, words }: Props = $props();
15
+ </script>
16
+
17
+ <section class="ds-lib-web">
18
+ <h2 class="ds-lib-web-heading">{words.fromTheWeb}</h2>
19
+ <ul class="ds-lib-web-list">
20
+ {#each sources as source (source.url)}
21
+ <li>
22
+ <a class="ds-lib-web-link" href={source.url} target="_blank" rel="noopener noreferrer">
23
+ <span class="ds-lib-web-title">{source.title}</span>
24
+ <span class="ds-lib-web-host">{source.host}</span>
25
+ </a>
26
+ </li>
27
+ {/each}
28
+ </ul>
29
+ </section>
30
+
31
+ <style>
32
+ .ds-lib-web-heading {
33
+ margin: 0 0 0.375rem;
34
+ color: var(--ds-color-muted-foreground);
35
+ font-family: inherit;
36
+ font-size: var(--ds-text-2xs);
37
+ font-weight: 500;
38
+ letter-spacing: 0.05em;
39
+ text-transform: uppercase;
40
+ }
41
+
42
+ .ds-lib-web-list {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: 0.125rem;
46
+ margin: 0;
47
+ border-top: 1px solid var(--ds-color-border);
48
+ padding: 0.375rem 0 0;
49
+ list-style: none;
50
+ }
51
+
52
+ .ds-lib-web-link {
53
+ display: flex;
54
+ align-items: baseline;
55
+ gap: 0.5rem;
56
+ border-radius: var(--ds-radius-md);
57
+ padding: 0.375rem 0.625rem;
58
+ color: inherit;
59
+ font-size: 0.875rem;
60
+ text-decoration: none;
61
+ }
62
+
63
+ .ds-lib-web-link:hover {
64
+ background: var(--ds-color-surface-2);
65
+ }
66
+
67
+ .ds-lib-web-link:focus-visible {
68
+ outline: 2px solid var(--ds-color-ring);
69
+ outline-offset: 2px;
70
+ }
71
+
72
+ .ds-lib-web-title {
73
+ min-width: 0;
74
+ color: var(--ds-color-foreground);
75
+ font-weight: 500;
76
+ overflow-wrap: anywhere;
77
+ }
78
+
79
+ .ds-lib-web-host {
80
+ flex: none;
81
+ color: var(--ds-color-muted-foreground);
82
+ font-size: var(--ds-text-2xs);
83
+ }
84
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { ChatCopy } from '../../copy';
2
+ import type { WebSource } from '../../web-sources';
3
+ interface Props {
4
+ sources: WebSource[];
5
+ words: ChatCopy;
6
+ }
7
+ declare const WebList: import("svelte").Component<Props, {}, "">;
8
+ type WebList = ReturnType<typeof WebList>;
9
+ export default WebList;
@@ -0,0 +1,2 @@
1
+ export { default as WrittenCard } from './written-card.svelte';
2
+ export { default } from './written-card.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as WrittenCard } from './written-card.svelte';
2
+ export { default } from './written-card.svelte';
@@ -0,0 +1,130 @@
1
+ <!--
2
+ A document the persona wrote, as a card in the turn: its name and version,
3
+ Open (the reading column shows that version) and a download link for the
4
+ source and for each format the app renders it as.
5
+ -->
6
+ <script lang="ts">
7
+ import FileTextIcon from '@lucide/svelte/icons/file-text';
8
+ import { fill, resolveCopy, type ChatCopy } from '../../copy';
9
+ import { downloads, type Written, type WrittenUrl } from '../../written';
10
+
11
+ interface Props {
12
+ written: Written;
13
+ /** The column is showing this version now. */
14
+ open?: boolean;
15
+ /** Omit and the card lists and links but does not open. */
16
+ onopen?: () => void;
17
+ /** Omit and no download links render. */
18
+ url?: WrittenUrl;
19
+ copy?: Partial<ChatCopy>;
20
+ }
21
+
22
+ let { written, open = false, onopen, url, copy }: Props = $props();
23
+
24
+ const words = $derived(resolveCopy(copy));
25
+ </script>
26
+
27
+ <div class="ds-lib-written" class:is-open={open}>
28
+ <span class="ds-lib-written-icon" aria-hidden="true"><FileTextIcon size={16} /></span>
29
+ <span class="ds-lib-written-text">
30
+ <span class="ds-lib-written-name">{written.name}</span>
31
+ <span class="ds-lib-written-version">{fill(words.writtenVersion, { n: written.version })}</span>
32
+ </span>
33
+ {#if onopen}
34
+ <button type="button" class="ds-lib-written-open" onclick={onopen}>
35
+ {open ? words.showingArtefact : words.openArtefact}
36
+ </button>
37
+ {/if}
38
+ {#if url}
39
+ <ul class="ds-lib-written-links" aria-label={words.download}>
40
+ {#each downloads(written, words) as link (link.format)}
41
+ <li><a href={url(written.name, written.version, link.format)}>{link.label}</a></li>
42
+ {/each}
43
+ </ul>
44
+ {/if}
45
+ </div>
46
+
47
+ <style>
48
+ .ds-lib-written {
49
+ display: flex;
50
+ flex-wrap: wrap;
51
+ align-items: center;
52
+ gap: 0.5rem 0.75rem;
53
+ border: 1px solid var(--ds-color-border);
54
+ border-radius: var(--ds-radius-lg);
55
+ background: var(--ds-color-surface-1);
56
+ padding: 0.625rem 0.75rem;
57
+ }
58
+
59
+ .ds-lib-written.is-open {
60
+ border-color: var(--ds-color-primary);
61
+ }
62
+
63
+ .ds-lib-written-icon {
64
+ display: flex;
65
+ width: 2rem;
66
+ height: 2rem;
67
+ flex: none;
68
+ align-items: center;
69
+ justify-content: center;
70
+ border: 1px solid var(--ds-color-border);
71
+ border-radius: var(--ds-radius-md);
72
+ color: var(--ds-color-muted-foreground);
73
+ }
74
+
75
+ .ds-lib-written-text {
76
+ display: flex;
77
+ min-width: 0;
78
+ flex: 1;
79
+ flex-direction: column;
80
+ }
81
+
82
+ .ds-lib-written-name {
83
+ font-size: 0.875rem;
84
+ font-weight: 500;
85
+ line-height: 1.25rem;
86
+ overflow-wrap: anywhere;
87
+ }
88
+
89
+ .ds-lib-written-version {
90
+ color: var(--ds-color-muted-foreground);
91
+ font-size: var(--ds-text-2xs);
92
+ }
93
+
94
+ .ds-lib-written-open {
95
+ border: 1px solid var(--ds-color-border);
96
+ border-radius: var(--ds-radius-md);
97
+ background: none;
98
+ padding: 0.25rem 0.625rem;
99
+ color: inherit;
100
+ font: inherit;
101
+ font-size: 0.8125rem;
102
+ cursor: pointer;
103
+ }
104
+
105
+ .ds-lib-written-open:focus-visible,
106
+ .ds-lib-written-links a:focus-visible {
107
+ outline: 2px solid var(--ds-color-ring);
108
+ outline-offset: 2px;
109
+ }
110
+
111
+ .ds-lib-written-links {
112
+ display: flex;
113
+ flex-basis: 100%;
114
+ flex-wrap: wrap;
115
+ gap: 0.75rem;
116
+ margin: 0;
117
+ padding: 0;
118
+ font-size: var(--ds-text-2xs);
119
+ list-style: none;
120
+ }
121
+
122
+ .ds-lib-written-links a {
123
+ color: var(--ds-color-muted-foreground);
124
+ text-decoration: underline;
125
+ }
126
+
127
+ .ds-lib-written-links a:hover {
128
+ color: var(--ds-color-foreground);
129
+ }
130
+ </style>
@@ -0,0 +1,15 @@
1
+ import { type ChatCopy } from '../../copy';
2
+ import { type Written, type WrittenUrl } from '../../written';
3
+ interface Props {
4
+ written: Written;
5
+ /** The column is showing this version now. */
6
+ open?: boolean;
7
+ /** Omit and the card lists and links but does not open. */
8
+ onopen?: () => void;
9
+ /** Omit and no download links render. */
10
+ url?: WrittenUrl;
11
+ copy?: Partial<ChatCopy>;
12
+ }
13
+ declare const WrittenCard: import("svelte").Component<Props, {}, "">;
14
+ type WrittenCard = ReturnType<typeof WrittenCard>;
15
+ export default WrittenCard;
package/dist/copy.d.ts CHANGED
@@ -69,6 +69,25 @@ export interface ChatCopy {
69
69
  showingArtefact: string;
70
70
  /** Beside the clock on an answer this page did not start. */
71
71
  stillAnswering: string;
72
+ /** An expanded tool step: what went in, what came back, and a call that failed. */
73
+ stepInput: string;
74
+ stepResult: string;
75
+ stepFailed: string;
76
+ /** Editing a question already asked: the control, the send, and what the
77
+ * edit does not carry. */
78
+ editQuestion: string;
79
+ editSend: string;
80
+ editFilesNote: string;
81
+ /** Web pages an answer drew on, listed apart from its numbered sources. */
82
+ fromTheWeb: string;
83
+ /** A document the persona wrote: its version (`{n}`), the version switcher's
84
+ * name, and what each download is called. */
85
+ writtenVersion: string;
86
+ writtenVersions: string;
87
+ formatMarkdown: string;
88
+ formatHtml: string;
89
+ formatWord: string;
90
+ formatPdf: string;
72
91
  /** The answer mark: the two verdicts, the optional note, and after. */
73
92
  markHelpful: string;
74
93
  markNotHelpful: string;
package/dist/copy.js CHANGED
@@ -80,6 +80,19 @@ export function copyFor(name = DEFAULT_PERSONA) {
80
80
  openArtefact: 'Open',
81
81
  showingArtefact: 'Showing',
82
82
  stillAnswering: `${who} is still answering`,
83
+ stepInput: 'Input',
84
+ stepResult: 'Result',
85
+ stepFailed: 'Failed',
86
+ editQuestion: 'Edit',
87
+ editSend: 'Send',
88
+ editFilesNote: 'Files attached to the original question are not sent again.',
89
+ fromTheWeb: 'From the web',
90
+ writtenVersion: 'Version {n}',
91
+ writtenVersions: 'Version',
92
+ formatMarkdown: 'Markdown',
93
+ formatHtml: 'HTML',
94
+ formatWord: 'Word',
95
+ formatPdf: 'PDF',
83
96
  markHelpful: 'Helpful',
84
97
  markNotHelpful: 'Not helpful',
85
98
  markNote: 'Anything to add? (optional)',
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { AgentEvent } from './client';
10
10
  import type { Citation } from './citations';
11
+ import type { Written } from './written';
11
12
  export interface TextBlock {
12
13
  kind: 'text';
13
14
  index: number;
@@ -66,12 +67,6 @@ export interface ToolWords {
66
67
  object: string;
67
68
  /** One dimmed line under the row. */
68
69
  detail?: string;
69
- /** How the activity line counts the call, singular and plural:
70
- * `['search', 'searches']` reads "2 searches". Absent is not counted. */
71
- tally?: [one: string, many: string];
72
- /** How the row counts a run of identical calls: `['page', 'pages']`
73
- * reads "· 5 pages". Absent reads "× 5". */
74
- repeat?: [one: string, many: string];
75
70
  /** Something the call read that a reader can open, listed under the
76
71
  * answer and handed back to the host's `oncite` when tapped. */
77
72
  source?: {
@@ -120,6 +115,8 @@ export declare class Transcript {
120
115
  outcome: Outcome | null;
121
116
  /** Sources for the answer, from the app's `citations` frame. */
122
117
  citations: Citation[];
118
+ /** The document versions the persona wrote, in order. */
119
+ written: Written[];
123
120
  other: AgentEvent[];
124
121
  reset(): void;
125
122
  apply(event: AgentEvent): void;
@@ -129,6 +126,7 @@ export interface FoldTarget {
129
126
  blocks: Block[];
130
127
  outcome: Outcome | null;
131
128
  citations?: Citation[];
129
+ written?: Written[];
132
130
  }
133
131
  /** Where one turn's fold keeps its place between events. */
134
132
  export interface FoldState {
@@ -155,29 +153,18 @@ export declare function fold(into: FoldTarget, state: FoldState, event: AgentEve
155
153
  /** A message's content blocks; a user frame may carry a bare string instead. */
156
154
  export declare function contentOf(event: AgentEvent): Array<Record<string, unknown>>;
157
155
  export interface ActivityStep {
158
- /** The block this step stands for; a repeated step keeps the FIRST.
156
+ /** The block this step stands for.
159
157
  *
160
158
  * A `text` block here is interstitial narration, not the answer — see
161
159
  * `segment()`. */
162
160
  block: Block;
163
- /** How many identical consecutive steps collapsed into this one. */
164
- repeats: number;
165
- /** A tool step in the persona's words, resolved once here so the row, the
166
- * count and the collapse all agree. */
161
+ /** A tool step in the persona's words, resolved once here. */
167
162
  words?: ToolWords;
168
163
  }
169
- /** One count on the activity line: "2 searches". */
170
- export interface Tally {
171
- one: string;
172
- many: string;
173
- count: number;
174
- }
175
164
  export interface ActivityGroup {
176
165
  kind: 'activity';
177
166
  index: number;
178
167
  steps: ActivityStep[];
179
- /** What the steps counted as, in the order each first appeared. */
180
- tallies: Tally[];
181
168
  }
182
169
  export type Segment = ActivityGroup | TextBlock;
183
170
  /** One question and the answer to it, as the transcript renders it.
@@ -196,6 +183,10 @@ export interface Turn {
196
183
  blocks: Block[];
197
184
  outcome: Outcome | null;
198
185
  citations?: Citation[];
186
+ /** The names of the files attached to the question. */
187
+ files?: string[];
188
+ /** The document versions the persona wrote answering it, in order. */
189
+ written?: Written[];
199
190
  /** Something this turn produced for the reader to open, carded under the
200
191
  * answer once the turn settles. */
201
192
  artefact?: Artefact;
@@ -223,15 +214,11 @@ export interface Artefact {
223
214
  /**
224
215
  * Fold a turn's flat block list into what a reader should actually see.
225
216
  *
226
- * Three problems this solves, all reported off a real transcript:
217
+ * Two problems this solves, reported off a real transcript:
227
218
  *
228
- * 1. Fifteen tool rows stood between the question and the first word of the
229
- * answer, so the answer had to be scrolled to. Contiguous activity becomes
230
- * ONE group the caller can collapse.
231
- * 2. "Reading pspf guidelines 2026" appeared five times in a row — five pages
232
- * of one document, which is one act of reading to a human. Consecutive
233
- * steps with the same label collapse to one row carrying a count.
234
- * 3. The persona's between-tool narration was rendering unfolded, as answer prose.
219
+ * 1. Contiguous activity becomes ONE group, so the answer's prose is never
220
+ * split by each step; every step stays its own collapsed row in it.
221
+ * 2. The persona's between-tool narration was rendering unfolded, as answer prose.
235
222
  * The caller stream carries no `thinking` blocks at all — measured on
236
223
  * production 11/09/2026 — so "Let me also check whether…" arrives as an
237
224
  * ordinary `text` block, indistinguishable from the answer except by
@@ -239,7 +226,7 @@ export interface Artefact {
239
226
  * narration; only the run of text after the LAST tool call is the answer.
240
227
  * Narration folds into the activity group as a thinking-shaped step.
241
228
  *
242
- * That third rule is provisional while a turn streams, and deliberately so: a
229
+ * That second rule is provisional while a turn streams, and deliberately so: a
243
230
  * text block that is currently last IS the answer as far as anything can know,
244
231
  * and renders as prose. The tool call that arrives after it re-homes it into
245
232
  * the group. Nothing here holds state to make that work — `segment` is pure
@@ -252,12 +239,6 @@ export interface Artefact {
252
239
  * off the prose itself would be unexplainable the first time it misfired.
253
240
  */
254
241
  export declare function segment(blocks: Block[], describeTool?: DescribeTool): Segment[];
255
- /** One line describing a whole investigation, for the collapsed state.
256
- *
257
- * Counts of what the persona did are fine ("1 search · 2 documents read"); how
258
- * it organises what it knows is not — no collection count, no corpus.
259
- */
260
- export declare function summariseActivity(group: ActivityGroup): string;
261
242
  /**
262
243
  * What a settled answer took, as one quiet line: `56.3s`, then what it cost
263
244
  * — only when the host can price it (`formatCost`) and the run actually