@lovelaces-io/storyteller 0.3.0 → 0.4.0

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/AGENTS.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Storyteller (`@lovelaces-io/storyteller`) is a lightweight TypeScript logging library with zero production dependencies. You report beats of work as they happen; it keeps them and emits them as one structured record when the work finishes. Records go to pluggable audiences.
6
6
 
7
- Version: 0.3.0 (pre-1.0, API may change). Dual output: ESM + CJS.
7
+ Version: 0.3.1 (pre-1.0, API may change). Dual output: ESM + CJS.
8
8
 
9
9
  ## Narrate your work
10
10
 
@@ -84,6 +84,10 @@ Values dropped for size are replaced with an explicit `{ "@truncated": { kind, o
84
84
 
85
85
  The normalizer never throws. A hostile object cannot break the pipeline.
86
86
 
87
+ ## Secrets
88
+
89
+ Redaction happens at capture and again at the storage boundary: values under secret-named keys (`password`, `apiKey`, `dbPassword`, `x-api-key`), and recognisable secret formats inside any string (Stripe/OpenAI/GitHub keys, JWTs, PEM blocks, `Bearer …`, passwords in URLs, `?token=`), become `[redacted]` — error messages and stacks included. It is defense in depth, not a guarantee: a secret that looks like a word passes. Do not report a secret and rely on redaction; do not turn `redact` off in code that persists. `auditRedaction(value)` shows what would be removed, so check a real corpus before trusting coverage. `redactValues: "strict"` trades some legitimate content for more coverage.
90
+
87
91
  ## Output a program can read
88
92
 
89
93
  For machine consumption, use NDJSON — one JSON object per line, nothing else on the channel:
@@ -185,9 +189,42 @@ Built in: `consoleAudience()` (notes and stories, registered by default), `dbAud
185
189
 
186
190
  When an audience throws, the failure is reported through `onAudienceError` rather than swallowed, and never propagates into your code. When an audience is too slow, emissions past `maxInFlight` are dropped and counted in `droppedEmissions` on the closing story — so the loss shows up in the record instead of vanishing.
187
191
 
192
+ ## Keeping stories, and reading them back
193
+
194
+ A story is the unit of retrieval: complete, ordered, small enough for a context window. A `StoryStore` keeps them and answers structured questions; `storeAudience` is the one-line bridge from delivery.
195
+
196
+ ```typescript
197
+ import { memoryStore, stories, storeAudience } from "@lovelaces-io/storyteller";
198
+ import { fileStore } from "@lovelaces-io/storyteller/store/file"; // Node only
199
+
200
+ const kept = fileStore("./stories.jsonl"); // or memoryStore() for a browser, a test, one run
201
+ story.audience.add(storeAudience(kept));
202
+
203
+ await stories(kept).about("checkout").failing().since("24h"); // reads like the question
204
+ await stories(kept).slowerThan("5s").since("7d").oldest().limit(10);
205
+ await stories(kept).under(storyId); // its chapters
206
+ await kept.prune(new Date(Date.now() - 30 * 86_400_000));
207
+ ```
208
+
209
+ `stories(store)` is the vocabulary: `about`, `from`, `level`, `atLeast`, `failing`, `succeeding`, `slowerThan`, `since`, `until`, `under`, `newest`, `oldest`, `limit`, `skip`; then `all()`, `first()` or `count()`, or just `await` it. It compiles to a `StoryQuery` object, never a string. `about` searches title, note text, scalar context and error messages; `from` searches the origin. To write an adapter for a database, store `canonicalRow(story)` and make its query agree with `matchesQuery` — that agreement is the contract.
210
+
211
+ ## Rendering stories for humans
212
+
213
+ Stories are JSON so programs can read them. When a person needs to read one — an admin log screen, a terminal, a chat transcript — use `@lovelaces-io/storyteller-view`, a separate zero-dependency package from the same repo:
214
+
215
+ ```ts
216
+ import { renderStory, renderStoryText } from "@lovelaces-io/storyteller-view";
217
+
218
+ renderStory(event); // HTMLElement: timeline of notes, context trees, error with cause chain
219
+ renderStoryText(event, { colors: true }); // string: the same layout as indented text
220
+ ```
221
+
222
+ Both accept a `StoryEvent`, a `NoteEmission`, or a stored record read back from a database or NDJSON. Every marker the normalizer leaves (`@type`, `@truncated`, `[Circular → path]`, `[redacted]`) is shown as what it means rather than as a string, and the DOM renderer inserts text nodes only — never `innerHTML` — because story content is user input. Do not hand-roll a story renderer in an app; reach for the view and theme it with the `--stv-*` custom properties.
223
+
188
224
  ## Architecture
189
225
 
190
226
  ```
227
+ packages/core/
191
228
  src/
192
229
  storyteller.ts — core class, types, event building, delivery
193
230
  normalize.ts — turns any value into something storable
@@ -207,6 +244,10 @@ snippets/
207
244
  agents-section.md — the guidance block consumers paste into their AGENTS.md
208
245
  ```
209
246
 
247
+ The repository is a workspace: this package is `packages/core`; add-on packages with
248
+ real dependencies (a SQLite store, an MCP server) live beside it under `packages/`
249
+ and are never imported by core.
250
+
210
251
  `snippets/agents-section.md` is the single source for that block. It is embedded
211
252
  verbatim in README.md and written by `storyteller init`, and `npm run check:snippet`
212
253
  fails the build if the copies drift or it outgrows its 40-line budget. Edit the
package/README.md CHANGED
@@ -248,6 +248,33 @@ story.audience.add({
248
248
 
249
249
  When an audience throws, the failure is reported rather than swallowed, and never reaches your code. When one is too slow, emissions past `maxInFlight` are dropped and counted in `droppedEmissions` on the closing record — visible loss beats silent loss.
250
250
 
251
+ ## Keep Them, Ask Them
252
+
253
+ A story is the unit of retrieval: complete, ordered, small enough for a context window. Keep them in a store and the question *why did last night's sync fail?* has somewhere to look.
254
+
255
+ ```ts
256
+ import { stories, storeAudience } from "@lovelaces-io/storyteller";
257
+ import { fileStore } from "@lovelaces-io/storyteller/store/file";
258
+
259
+ const kept = fileStore("./stories.jsonl"); // or memoryStore() anywhere
260
+ story.audience.add(storeAudience(kept));
261
+
262
+ await stories(kept).failing().since("1h");
263
+ await stories(kept).about("checkout").from("payment-service").level("oops").since("24h");
264
+ await stories(kept).slowerThan("5s").since("7d").oldest().limit(10);
265
+ ```
266
+
267
+ Ask in words; every store answers the same question. Then let an agent ask: **the Librarian** is a read-only MCP server over any store.
268
+
269
+ ```jsonc
270
+ // .mcp.json
271
+ { "mcpServers": { "storyteller": { "command": "npx", "args": ["-y", "@lovelaces-io/storyteller-mcp", "./stories.jsonl"] } } }
272
+ ```
273
+
274
+ Redaction runs at capture and again at the storage boundary — secret-named keys and recognisable secret formats inside any string — so what is kept is what is safe to keep. Defense in depth, not a guarantee; [SECURITY.md](./SECURITY.md) says exactly what it does not promise.
275
+
276
+ For a person, `@lovelaces-io/storyteller-view` renders a story as a timeline, in a page or a terminal: [storyteller.lovelaces.io/docs/view](https://storyteller.lovelaces.io/docs/view).
277
+
251
278
  ## Configuration
252
279
 
253
280
  Every option can also come from the environment, so you can change behavior without touching code:
@@ -300,6 +327,9 @@ const story = useStoryteller({ origin: { who: "worker" } });
300
327
  - [How It Works](docs/HOW-IT-WORKS.md) — narrative guide
301
328
  - [Changelog](CHANGELOG.md)
302
329
  - [For AI Agents](AGENTS.md) — guidance for AI coding assistants
330
+ - [The Library](https://storyteller.lovelaces.io/docs/library) — keep stories, ask in words, let an agent read them back
331
+ - [Story view](https://storyteller.lovelaces.io/docs/view) — `@lovelaces-io/storyteller-view`, stories rendered for humans
332
+ - [The Librarian](packages/mcp/README.md) — `@lovelaces-io/storyteller-mcp`, the read-only MCP server
303
333
 
304
334
  ## License
305
335