@invana/assistant 0.0.32

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 ADDED
@@ -0,0 +1,103 @@
1
+ # @invana/assistant
2
+
3
+ The analyst assistant, driven by JSON. `<ChatSession spec variant />` renders a whole conversation
4
+ — the analyst's questions, what the assistant asks back, its answers, and the run behind each —
5
+ from a `ConversationSpec`, the way `@invana/boards` renders a `BoardSpec`. Two variants draw
6
+ the same spec: `cli`, the console (caret prompts, status-dotted replies, step rows, a Tasks view),
7
+ and `web`, the chat (labelled turns, answer cards, `Answered in 1.4 s`). The composer is the same in
8
+ both, configured by the spec's `composer`.
9
+
10
+ The contract is the [Design Kit Spec](https://claude.ai/artifact/VcN3AYgmbdCpHbxXZjMir5): 29 ask
11
+ intents on 21 blocks that return a value, 14 answer intents on 41 blocks that show data, and 26 answer patterns. Its ids
12
+ are copied into `src/grammar/`, and a test fails when the block registry and the grammar disagree.
13
+
14
+ ```bash
15
+ pnpm add @invana/assistant
16
+ pnpm add @invana/ui @invana/charts @invana/tables @invana/forms @invana/styling react react-dom
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { ChatSession, fromNdjson, useChatSession } from '@invana/assistant';
23
+
24
+ const chat = useChatSession(initial);
25
+
26
+ <ChatSession
27
+ variant="web"
28
+ spec={chat.spec}
29
+ // One typed callback per event, then `onEvent` for all of them.
30
+ onPrompt={async (e) => chat.stream(fromNdjson(await fetch('/ask', { method: 'POST', body: JSON.stringify(e) })))}
31
+ onAction={(e) => e.action === 'load-canvas' && canvas.load(e.turn)}
32
+ onOpenRun={(e) => openRunView(e.turn)}
33
+ onStop={chat.stop}
34
+ onEvent={(e) => api.send(e)}
35
+ icons={{ send: <ArrowUp />, stop: <Square /> }}
36
+ />;
37
+ ```
38
+
39
+ The API sends patches — `add-turn`, `set-state`, `add-block`, `update-block`, `add-trace-step`,
40
+ `update-trace-step`, `update-turn`, `update-spec`, `set-records` (a model's live record count), and for the streaming effect `append-text` (words
41
+ into any block's text field) and `append-thinking` (a step's reasoning). Hand them over any way:
42
+
43
+ - `applyPatch(spec, patch)` and pass the spec — fully controlled;
44
+ - `useChatSession()` — `apply`, `stream(source)`, `play(script)`, `stop()`, `reset(spec)`;
45
+ - `<ChatSession stream={source}>` — the session applies them itself.
46
+
47
+ A source is any iterable or async iterable of patches: `fromNdjson(response)`, `fromEventSource(es)`,
48
+ a generator, or `playScript(script)` for a recorded run (`textDeltas` and `thinkingDeltas` build the
49
+ word-by-word steps). `stopPatches(spec)` is what stopping records: the running step stopped, the
50
+ rest left queued.
51
+
52
+ A trace step carries its record — `startedAt` and `duration`, `attempt` of `attempts`, streamed
53
+ `thinking`, and `io` (what went in, what came out) — and the answer its `startedAt`, `at`,
54
+ `duration`, `meta` line and `outcome` (what the run produced, with its actions).
55
+
56
+ Every action comes back as one `ConversationEvent` — `prompt` (with the composer's `settings` and
57
+ `files`), `reply`, `skip`, `change`, `action`, `scope`, `open`, `template`, `refine`, `rate`,
58
+ `stop`, `retry`, `open-run`, `setting`, `copy`, `toggle-steps`. The component changes nothing itself.
59
+
60
+ Beside each settled answer's time sit its actions, chosen by the `actions` prop — the built-ins by
61
+ name (`retry`, `copy`, `steps`, `rate`) and your own as `{ id, label, icon }`, sent as `action`:
62
+
63
+ ```tsx
64
+ <ChatSession
65
+ spec={spec}
66
+ actions={['retry', 'copy', { id: 'pin', label: 'Pin to report', icon: <Pin /> }, 'rate']}
67
+ onAction={(e) => e.action === 'pin' && reports.pin(e.turn)}
68
+ onCopy={(e) => toast('Copied')}
69
+ />
70
+ ```
71
+
72
+ `validate(spec)` checks a payload the compiler cannot see: unknown kinds and broken turns are
73
+ errors, and an answer that does not match its pattern is a warning.
74
+
75
+ A block with no renderer yet renders as a labelled placeholder with its JSON. Register your own
76
+ renderer, or replace a built-in one, with the traits that say how it sits:
77
+
78
+ ```tsx
79
+ <ChatSession
80
+ spec={spec}
81
+ registry={{
82
+ blocks: { subgraph: MyGraphBlock },
83
+ blockTraits: { subgraph: { placement: 'own' } }, // a card of its own, under the answer
84
+ asks: { picker: MyPickerAsk },
85
+ askTraits: { picker: { frame: 'none' } }, // drawn bare, not in the question card
86
+ }}
87
+ />
88
+ ```
89
+
90
+ `ChatSessionFrame` is the layout alone (a scrolling stack with a footer), and `ChatSessionTurn`
91
+ draws one turn outside a thread — for a block's board.
92
+
93
+ ## Moving from `@invana/ui`
94
+
95
+ `ChatSession*`, `ClarifyCard`, `EmissionCard` (with `EmissionHeader` and `CitationMarker`) and
96
+ `TemplatePicker` now import from here. They are still exported from `@invana/ui`, marked
97
+ `@deprecated`, for this release only; their names and props do not change.
98
+
99
+ ## Tests
100
+
101
+ ```bash
102
+ pnpm --filter @invana/assistant test
103
+ ```