@poodle64/librarian 2026.9.26 → 2026.9.27
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 +4 -0
- package/dist/chat.svelte.d.ts +12 -0
- package/dist/chat.svelte.js +27 -4
- package/dist/components/agent-transcript/agent-transcript.svelte +15 -0
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +7 -0
- package/dist/components/answer-share/answer-share.svelte +129 -0
- package/dist/components/answer-share/answer-share.svelte.d.ts +13 -0
- package/dist/components/answer-share/index.d.ts +2 -0
- package/dist/components/answer-share/index.js +2 -0
- package/dist/components/conversation/conversation.svelte +8 -1
- package/dist/components/conversation/conversation.svelte.d.ts +3 -0
- package/dist/copy.d.ts +8 -0
- package/dist/copy.js +6 -0
- package/dist/transcript.svelte.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ src/lib/
|
|
|
29
29
|
conversation/ the whole reading surface: scroll, pill, pane, composer slot
|
|
30
30
|
conversation-list/ past conversations, newest first: reopen, rename, download, delete
|
|
31
31
|
answer-mark/ helpful or not, with a note, on the last answer
|
|
32
|
+
answer-share/ share an answer read-only, and stop sharing it
|
|
32
33
|
fair-use-notice/ today's allowance, beside the composer
|
|
33
34
|
scope-statement/ what this room answers from, and what it does not hold
|
|
34
35
|
agent-transcript/ one question and everything Milton did answering it
|
|
@@ -308,6 +309,7 @@ the transport passes them through:
|
|
|
308
309
|
| `remove` | `DELETE conversations/{id}` |
|
|
309
310
|
| `download` | `GET conversations/{id}/export`, as `{ name, body }` |
|
|
310
311
|
| `mark` | its answer mark, where the agent takes one (optional) |
|
|
312
|
+
| `share` | share an answer read-only, or stop (optional) |
|
|
311
313
|
|
|
312
314
|
What the controller relies on the routes to say:
|
|
313
315
|
|
|
@@ -339,6 +341,8 @@ the job may go on by itself; `sendWhileRunning` is true.
|
|
|
339
341
|
| Concern | How |
|
|
340
342
|
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
341
343
|
| The answer mark | `onmark(turn, verdict)` on `Conversation`: resolve true once recorded. Offered on the last settled answer only; without it no mark renders |
|
|
344
|
+
| Sharing an answer | `onshare(turn, share)` on `Conversation`, over `chat.share`: every settled answer offers Share, which copies the link `StoredTurn.shared` names; a shared one offers Copy link and Stop sharing. Nothing is shared unless the reader asks. A host's own `turn` places `AnswerShare` (`./answer-share`) with the `onshare` and `shared` it is handed |
|
|
345
|
+
| A shared answer, read | `Conversation` given `[storedTurn(id, 0, answer)]` from `./chat` and no handlers, no composer: the question, the answer and its sources, read-only, citations opening as in the room |
|
|
342
346
|
| Waiting, still answering | `waiting` and `answering` on `Conversation`, from the controller |
|
|
343
347
|
| 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 |
|
|
344
348
|
| 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 |
|
package/dist/chat.svelte.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface StoredTurn {
|
|
|
45
45
|
kind?: string | null;
|
|
46
46
|
/** How hard the persona worked on this one. */
|
|
47
47
|
depth?: Depth | null;
|
|
48
|
+
/** Where this answer's read-only view lives while it is shared. */
|
|
49
|
+
shared?: string | null;
|
|
48
50
|
}
|
|
49
51
|
/** A conversation reopened. */
|
|
50
52
|
export interface ConversationRead extends ConversationSummary {
|
|
@@ -107,6 +109,10 @@ export interface RoomTransport {
|
|
|
107
109
|
/** The answer mark, where the agent takes one. `turn` is the answer's
|
|
108
110
|
* zero-based position in the conversation. */
|
|
109
111
|
mark?(id: string, turn: number, verdict: Verdict): Promise<void>;
|
|
112
|
+
/** Shares answer `turn` read-only with whoever may enter the room, or
|
|
113
|
+
* stops sharing it. Resolves to where it is read while shared, null once
|
|
114
|
+
* it is not. */
|
|
115
|
+
share?(id: string, turn: number, share: boolean): Promise<string | null>;
|
|
110
116
|
}
|
|
111
117
|
/** A job's routes. */
|
|
112
118
|
export interface JobTransport {
|
|
@@ -199,4 +205,10 @@ export declare class Chat {
|
|
|
199
205
|
download(id: string): Promise<boolean>;
|
|
200
206
|
/** Mark an answer the agent holds. False when it could not be recorded. */
|
|
201
207
|
mark(turn: Turn, verdict: Verdict): Promise<boolean>;
|
|
208
|
+
/** Share an answer the agent holds, or stop sharing it. False when that
|
|
209
|
+
* was not recorded. */
|
|
210
|
+
share(turn: Turn, share: boolean): Promise<boolean>;
|
|
202
211
|
}
|
|
212
|
+
/** A turn as read back, ready for `Conversation`: given one and no handlers,
|
|
213
|
+
* that is the read-only view of a shared answer. */
|
|
214
|
+
export declare function storedTurn(conversation: string, index: number, stored: StoredTurn): Turn;
|
package/dist/chat.svelte.js
CHANGED
|
@@ -62,6 +62,8 @@ export class Chat {
|
|
|
62
62
|
#watch = null;
|
|
63
63
|
#woken = false;
|
|
64
64
|
#wake = null;
|
|
65
|
+
// The turns the server recorded, which its routes count by place; a mark
|
|
66
|
+
// or a share on one still being written is the server's to refuse.
|
|
65
67
|
// Plain, as `Session`'s seen-set: nothing renders it.
|
|
66
68
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
67
69
|
#held = new Set();
|
|
@@ -279,15 +281,33 @@ export class Chat {
|
|
|
279
281
|
const id = this.conversationId;
|
|
280
282
|
if (!id || !this.#room?.mark || !this.#held.has(turn.id))
|
|
281
283
|
return false;
|
|
282
|
-
const position = this.turns.filter((t) => this.#held.has(t.id)).findIndex((t) => t.id === turn.id);
|
|
283
284
|
try {
|
|
284
|
-
await this.#room.mark(id, position, verdict);
|
|
285
|
+
await this.#room.mark(id, this.#position(turn), verdict);
|
|
285
286
|
return true;
|
|
286
287
|
}
|
|
287
288
|
catch {
|
|
288
289
|
return false;
|
|
289
290
|
}
|
|
290
291
|
}
|
|
292
|
+
/** Share an answer the agent holds, or stop sharing it. False when that
|
|
293
|
+
* was not recorded. */
|
|
294
|
+
async share(turn, share) {
|
|
295
|
+
const id = this.conversationId;
|
|
296
|
+
if (!id || !this.#room?.share || !this.#held.has(turn.id))
|
|
297
|
+
return false;
|
|
298
|
+
try {
|
|
299
|
+
turn.shared = (await this.#room.share(id, this.#position(turn), share)) ?? undefined;
|
|
300
|
+
this.#version += 1;
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/** A turn's place among the questions the agent was asked: the server's count. */
|
|
308
|
+
#position(turn) {
|
|
309
|
+
return this.turns.filter((t) => this.#held.has(t.id)).findIndex((t) => t.id === turn.id);
|
|
310
|
+
}
|
|
291
311
|
#leave() {
|
|
292
312
|
this.#generation += 1;
|
|
293
313
|
// A stop still waiting to be addressed is carried on the stream it
|
|
@@ -547,7 +567,9 @@ export class Chat {
|
|
|
547
567
|
return true;
|
|
548
568
|
}
|
|
549
569
|
}
|
|
550
|
-
|
|
570
|
+
/** A turn as read back, ready for `Conversation`: given one and no handlers,
|
|
571
|
+
* that is the read-only view of a shared answer. */
|
|
572
|
+
export function storedTurn(conversation, index, stored) {
|
|
551
573
|
return {
|
|
552
574
|
id: `${conversation}:${index}`,
|
|
553
575
|
question: stored.question,
|
|
@@ -556,7 +578,8 @@ function storedTurn(conversation, index, stored) {
|
|
|
556
578
|
outcome: null,
|
|
557
579
|
citations: stored.citations ?? [],
|
|
558
580
|
kind: stored.kind ?? undefined,
|
|
559
|
-
depth: stored.depth ?? undefined
|
|
581
|
+
depth: stored.depth ?? undefined,
|
|
582
|
+
shared: stored.shared ?? undefined
|
|
560
583
|
};
|
|
561
584
|
}
|
|
562
585
|
function epoch(value) {
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
import { DEFAULT_PERSONA, personaName, resolveCopy, type LibrarianCopy } from '../../copy';
|
|
38
38
|
import type { Verdict } from '../../chat.svelte';
|
|
39
39
|
import AnswerMark from '../answer-mark/answer-mark.svelte';
|
|
40
|
+
import AnswerShare from '../answer-share/answer-share.svelte';
|
|
40
41
|
import Working from '../working/working.svelte';
|
|
41
42
|
import ActivityGroup from '../activity-group/activity-group.svelte';
|
|
42
43
|
import ArtefactCard from '../artefact-card/artefact-card.svelte';
|
|
@@ -90,6 +91,13 @@
|
|
|
90
91
|
/** Records a verdict on this answer. Omit and no mark renders: a
|
|
91
92
|
* control that records nothing is worse than none. */
|
|
92
93
|
onmark?: (verdict: Verdict) => Promise<boolean>;
|
|
94
|
+
/** Where this answer's read-only view lives while it is shared, as the
|
|
95
|
+
* host's own address. */
|
|
96
|
+
shared?: string;
|
|
97
|
+
/** Shares this answer (true) or stops sharing it (false), resolving
|
|
98
|
+
* true once recorded. Omit and nothing about sharing renders: nothing
|
|
99
|
+
* is shared unless the reader asks. */
|
|
100
|
+
onshare?: (share: boolean) => Promise<boolean>;
|
|
93
101
|
/** How hard the persona worked on this one, for the footer's cost line
|
|
94
102
|
* (`Quick · 56 s · about A$0.34`). Absent renders the footer exactly
|
|
95
103
|
* as it always has: the duration alone. */
|
|
@@ -121,6 +129,8 @@
|
|
|
121
129
|
waiting = false,
|
|
122
130
|
answering,
|
|
123
131
|
onmark,
|
|
132
|
+
shared,
|
|
133
|
+
onshare,
|
|
124
134
|
depth,
|
|
125
135
|
formatCost
|
|
126
136
|
}: AgentTranscriptProps = $props();
|
|
@@ -373,6 +383,9 @@
|
|
|
373
383
|
<span>{copied ? words.copiedAnswer : words.copyAnswer}</span>
|
|
374
384
|
</button>
|
|
375
385
|
{/if}
|
|
386
|
+
{#if onshare && hasAnswer && !failure}
|
|
387
|
+
<AnswerShare {onshare} {shared} copy={words} />
|
|
388
|
+
{/if}
|
|
376
389
|
{#if onmark && hasAnswer && !failure}
|
|
377
390
|
<AnswerMark {onmark} copy={words} />
|
|
378
391
|
{/if}
|
|
@@ -570,8 +583,10 @@
|
|
|
570
583
|
color: var(--ds-color-muted-foreground);
|
|
571
584
|
}
|
|
572
585
|
|
|
586
|
+
/* 44px tall: each is a thumb's target on a phone. */
|
|
573
587
|
.ds-lib-action {
|
|
574
588
|
display: flex;
|
|
589
|
+
min-height: 2.75rem;
|
|
575
590
|
align-items: center;
|
|
576
591
|
gap: 0.375rem;
|
|
577
592
|
border: 0;
|
|
@@ -49,6 +49,13 @@ export interface AgentTranscriptProps {
|
|
|
49
49
|
/** Records a verdict on this answer. Omit and no mark renders: a
|
|
50
50
|
* control that records nothing is worse than none. */
|
|
51
51
|
onmark?: (verdict: Verdict) => Promise<boolean>;
|
|
52
|
+
/** Where this answer's read-only view lives while it is shared, as the
|
|
53
|
+
* host's own address. */
|
|
54
|
+
shared?: string;
|
|
55
|
+
/** Shares this answer (true) or stops sharing it (false), resolving
|
|
56
|
+
* true once recorded. Omit and nothing about sharing renders: nothing
|
|
57
|
+
* is shared unless the reader asks. */
|
|
58
|
+
onshare?: (share: boolean) => Promise<boolean>;
|
|
52
59
|
/** How hard the persona worked on this one, for the footer's cost line
|
|
53
60
|
* (`Quick · 56 s · about A$0.34`). Absent renders the footer exactly
|
|
54
61
|
* as it always has: the duration alone. */
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Share one answer read-only with whoever may ask here, and stop sharing it.
|
|
3
|
+
|
|
4
|
+
Rendered by `AgentTranscript` in its action row, and by a host's own turn
|
|
5
|
+
beside its own presentation, only when the host passes `onshare`: nothing
|
|
6
|
+
is shared unless the reader asks. A share copies its link at once; a
|
|
7
|
+
browser that refuses a copy after the round trip (Safari) still has "Copy
|
|
8
|
+
link". Several roots, as `AnswerMark`: the buttons sit in the row, and the
|
|
9
|
+
line saying who can open it drops under it.
|
|
10
|
+
-->
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import { tick } from 'svelte';
|
|
13
|
+
import CheckIcon from '@lucide/svelte/icons/check';
|
|
14
|
+
import LinkIcon from '@lucide/svelte/icons/link';
|
|
15
|
+
import Share2Icon from '@lucide/svelte/icons/share-2';
|
|
16
|
+
import { resolveCopy, type LibrarianCopy } from '../../copy';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
/** Shares the answer (true) or stops sharing it (false), resolving true
|
|
20
|
+
* once recorded. */
|
|
21
|
+
onshare: (share: boolean) => Promise<boolean>;
|
|
22
|
+
/** Where the read-only view lives while the answer is shared, as the
|
|
23
|
+
* host's own address. */
|
|
24
|
+
shared?: string;
|
|
25
|
+
copy?: Partial<LibrarianCopy>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let { onshare, shared, copy }: Props = $props();
|
|
29
|
+
|
|
30
|
+
const words = $derived(resolveCopy(copy));
|
|
31
|
+
|
|
32
|
+
let saving = $state(false);
|
|
33
|
+
let failed = $state(false);
|
|
34
|
+
let copied = $state(false);
|
|
35
|
+
|
|
36
|
+
async function send(share: boolean) {
|
|
37
|
+
if (saving) return;
|
|
38
|
+
saving = true;
|
|
39
|
+
failed = false;
|
|
40
|
+
try {
|
|
41
|
+
failed = !(await onshare(share));
|
|
42
|
+
} finally {
|
|
43
|
+
saving = false;
|
|
44
|
+
}
|
|
45
|
+
if (share && !failed) {
|
|
46
|
+
await tick();
|
|
47
|
+
await copyLink();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function copyLink() {
|
|
52
|
+
if (!shared) return;
|
|
53
|
+
try {
|
|
54
|
+
await navigator.clipboard.writeText(new URL(shared, location.href).href);
|
|
55
|
+
copied = true;
|
|
56
|
+
setTimeout(() => (copied = false), 1600);
|
|
57
|
+
} catch {
|
|
58
|
+
// Refused; "Copy link" is still there to tap.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
{#if shared}
|
|
64
|
+
<button type="button" class="ds-lib-share" onclick={copyLink} disabled={saving}>
|
|
65
|
+
{#if copied}<CheckIcon size={14} />{:else}<LinkIcon size={14} />{/if}
|
|
66
|
+
<span>{copied ? words.linkCopied : words.copyLink}</span>
|
|
67
|
+
</button>
|
|
68
|
+
<button type="button" class="ds-lib-share" onclick={() => send(false)} disabled={saving}>
|
|
69
|
+
<span>{words.stopSharing}</span>
|
|
70
|
+
</button>
|
|
71
|
+
{:else}
|
|
72
|
+
<button type="button" class="ds-lib-share" onclick={() => send(true)} disabled={saving}>
|
|
73
|
+
<Share2Icon size={14} />
|
|
74
|
+
<span>{words.share}</span>
|
|
75
|
+
</button>
|
|
76
|
+
{/if}
|
|
77
|
+
{#if shared || failed}
|
|
78
|
+
<p class="ds-lib-share-line" class:is-failed={failed} role="status">
|
|
79
|
+
{failed ? words.shareFailed : words.sharedWith}
|
|
80
|
+
</p>
|
|
81
|
+
{/if}
|
|
82
|
+
|
|
83
|
+
<style>
|
|
84
|
+
/* 44px tall: a thumb's target on a phone. */
|
|
85
|
+
.ds-lib-share {
|
|
86
|
+
display: flex;
|
|
87
|
+
min-height: 2.75rem;
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: 0.375rem;
|
|
90
|
+
border: 0;
|
|
91
|
+
border-radius: var(--ds-radius-md);
|
|
92
|
+
background: none;
|
|
93
|
+
padding: 0.25rem 0.375rem;
|
|
94
|
+
color: inherit;
|
|
95
|
+
font: inherit;
|
|
96
|
+
font-size: var(--ds-text-2xs);
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
transition:
|
|
99
|
+
color 150ms ease,
|
|
100
|
+
background-color 150ms ease;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ds-lib-share:hover:not(:disabled) {
|
|
104
|
+
background: var(--ds-color-surface-1);
|
|
105
|
+
color: var(--ds-color-foreground);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.ds-lib-share:disabled {
|
|
109
|
+
cursor: default;
|
|
110
|
+
opacity: 0.6;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.ds-lib-share:focus-visible {
|
|
114
|
+
outline: 2px solid var(--ds-color-ring);
|
|
115
|
+
outline-offset: 2px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Its own line under the row, whatever sits after the buttons in it. */
|
|
119
|
+
.ds-lib-share-line {
|
|
120
|
+
order: 1;
|
|
121
|
+
flex-basis: 100%;
|
|
122
|
+
margin: 0 0 0 0.375rem;
|
|
123
|
+
font-size: var(--ds-text-2xs);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.ds-lib-share-line.is-failed {
|
|
127
|
+
color: var(--ds-color-status-error);
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type LibrarianCopy } from '../../copy';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Shares the answer (true) or stops sharing it (false), resolving true
|
|
4
|
+
* once recorded. */
|
|
5
|
+
onshare: (share: boolean) => Promise<boolean>;
|
|
6
|
+
/** Where the read-only view lives while the answer is shared, as the
|
|
7
|
+
* host's own address. */
|
|
8
|
+
shared?: string;
|
|
9
|
+
copy?: Partial<LibrarianCopy>;
|
|
10
|
+
}
|
|
11
|
+
declare const AnswerShare: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type AnswerShare = ReturnType<typeof AnswerShare>;
|
|
13
|
+
export default AnswerShare;
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
/** Records a verdict on an answer. Offered on the last answer only, as
|
|
58
58
|
* asking again is; without it no mark renders. */
|
|
59
59
|
onmark?: (turn: Turn, verdict: Verdict) => Promise<boolean>;
|
|
60
|
+
/** Shares an answer read-only, or stops sharing it (`Chat.share`).
|
|
61
|
+
* Offered on every settled answer; without it nothing is shareable. */
|
|
62
|
+
onshare?: (turn: Turn, share: boolean) => Promise<boolean>;
|
|
60
63
|
/** The last question waits behind somebody else's (`Chat.waiting`). */
|
|
61
64
|
waiting?: boolean;
|
|
62
65
|
/** When the last answer began, epoch ms, where this page did not see it
|
|
@@ -111,6 +114,7 @@
|
|
|
111
114
|
onregenerate,
|
|
112
115
|
onsuggest,
|
|
113
116
|
onmark,
|
|
117
|
+
onshare,
|
|
114
118
|
waiting = false,
|
|
115
119
|
answering = null,
|
|
116
120
|
copy,
|
|
@@ -323,7 +327,10 @@
|
|
|
323
327
|
onregenerate: last && !running ? onregenerate : undefined,
|
|
324
328
|
onsuggest: last && !running ? onsuggest : undefined,
|
|
325
329
|
onmark:
|
|
326
|
-
last && !running && onmark ? (verdict: Verdict) => onmark(turn, verdict) : undefined
|
|
330
|
+
last && !running && onmark ? (verdict: Verdict) => onmark(turn, verdict) : undefined,
|
|
331
|
+
shared: turn.shared,
|
|
332
|
+
onshare:
|
|
333
|
+
onshare && !(last && running) ? (share: boolean) => onshare(turn, share) : undefined
|
|
327
334
|
} satisfies AgentTranscriptProps}
|
|
328
335
|
{#if presentTurn}
|
|
329
336
|
{@render presentTurn(props)}
|
|
@@ -33,6 +33,9 @@ interface Props {
|
|
|
33
33
|
/** Records a verdict on an answer. Offered on the last answer only, as
|
|
34
34
|
* asking again is; without it no mark renders. */
|
|
35
35
|
onmark?: (turn: Turn, verdict: Verdict) => Promise<boolean>;
|
|
36
|
+
/** Shares an answer read-only, or stops sharing it (`Chat.share`).
|
|
37
|
+
* Offered on every settled answer; without it nothing is shareable. */
|
|
38
|
+
onshare?: (turn: Turn, share: boolean) => Promise<boolean>;
|
|
36
39
|
/** The last question waits behind somebody else's (`Chat.waiting`). */
|
|
37
40
|
waiting?: boolean;
|
|
38
41
|
/** When the last answer began, epoch ms, where this page did not see it
|
package/dist/copy.d.ts
CHANGED
|
@@ -80,6 +80,14 @@ export interface LibrarianCopy {
|
|
|
80
80
|
markSend: string;
|
|
81
81
|
markNoted: string;
|
|
82
82
|
markFailed: string;
|
|
83
|
+
/** Sharing an answer: the action, once shared, who can open it, and a
|
|
84
|
+
* share or a stop that did not go through. */
|
|
85
|
+
share: string;
|
|
86
|
+
copyLink: string;
|
|
87
|
+
linkCopied: string;
|
|
88
|
+
stopSharing: string;
|
|
89
|
+
sharedWith: string;
|
|
90
|
+
shareFailed: string;
|
|
83
91
|
/** The fair-use notice, while there are questions left and once there are
|
|
84
92
|
* none, filled by `fill()`: `{resets}` is a time already, "midnight" or
|
|
85
93
|
* "2:00 pm". */
|
package/dist/copy.js
CHANGED
|
@@ -90,6 +90,12 @@ export function copyFor(name = DEFAULT_PERSONA) {
|
|
|
90
90
|
markSend: 'Send',
|
|
91
91
|
markNoted: 'Thanks, noted.',
|
|
92
92
|
markFailed: "That couldn't be sent. Nothing was recorded.",
|
|
93
|
+
share: 'Share',
|
|
94
|
+
copyLink: 'Copy link',
|
|
95
|
+
linkCopied: 'Link copied',
|
|
96
|
+
stopSharing: 'Stop sharing',
|
|
97
|
+
sharedWith: `Shared: anyone who can ask ${who} here can open the link.`,
|
|
98
|
+
shareFailed: "That didn't go through. Try again.",
|
|
93
99
|
// The industry wording, whole: the number, that it is a fair-use limit
|
|
94
100
|
// rather than a fault, and when it comes back. No apology.
|
|
95
101
|
questionsLeft: '{remaining} of {limit} questions left today. Resets at {resets}.',
|
|
@@ -213,6 +213,9 @@ export interface Turn {
|
|
|
213
213
|
/** How hard the persona worked on this one, as `client.ask()` sent it.
|
|
214
214
|
* Absent, a host that never offers the choice. */
|
|
215
215
|
depth?: Depth;
|
|
216
|
+
/** Where this answer's read-only view lives while it is shared, as the
|
|
217
|
+
* host's own address. Absent while it is not. */
|
|
218
|
+
shared?: string;
|
|
216
219
|
}
|
|
217
220
|
/** A turn's artefact, in the host's own words. */
|
|
218
221
|
export interface Artefact {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/librarian",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.27",
|
|
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",
|