@dbx-tools/genie-shared 0.1.20 → 0.1.22

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 (2) hide show
  1. package/README.md +206 -0
  2. package/package.json +3 -3
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # @dbx-tools/genie-shared
2
+
3
+ Pure types and sync helpers for `@dbx-tools/genie`. Wire-format zod
4
+ schemas (extending the generated `@dbx-tools/sdk-shared` Genie
5
+ shapes), the high-level `GenieChatEvent` discriminated union the
6
+ `genieEventChat` driver emits, and the per-event detectors that
7
+ derive those events from a `GenieMessage` snapshot diff.
8
+
9
+ No `node:*`, no `WorkspaceClient`, no I/O. Safe to import from any
10
+ runtime, including browser bundles.
11
+
12
+ ```ts
13
+ import {
14
+ // Wire schemas (extended over @dbx-tools/sdk-shared)
15
+ GenieMessageSchema,
16
+ GenieAttachmentSchema,
17
+ GenieQueryAttachmentSchema,
18
+ GenieThoughtSchema,
19
+ // High-level event union
20
+ GenieChatEventSchema,
21
+ type GenieChatEvent,
22
+ type GenieChatLocation,
23
+ // Status helpers
24
+ TERMINAL_STATUSES,
25
+ isTerminalStatus,
26
+ humanizeStatus,
27
+ // Attachment discriminator
28
+ detectAttachmentType,
29
+ tagAttachment,
30
+ // Pure event detectors + orchestrator
31
+ eventsFromMessage,
32
+ detectStatus,
33
+ detectThinking,
34
+ detectText,
35
+ detectQuery,
36
+ detectStatement,
37
+ detectRows,
38
+ detectSuggestedQuestions,
39
+ detectAttachmentAdded,
40
+ } from "@dbx-tools/genie-shared";
41
+ ```
42
+
43
+ Server-side chat driving (`genieChat`, `genieEventChat`) lives in
44
+ `@dbx-tools/genie` and pulls these types in. Frontends only need this
45
+ package.
46
+
47
+ ## Widened wire schemas
48
+
49
+ The SDK shapes from `@dbx-tools/sdk-shared` are re-exported with a
50
+ few fields Genie ships on the wire that the upstream `.d.ts` doesn't
51
+ currently type:
52
+
53
+ | Schema | Extension |
54
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
55
+ | `GenieMessageSchema` | Adds `auto_regenerate_count: number` and re-types `attachments` to the local `GenieAttachmentSchema`. |
56
+ | `GenieAttachmentSchema` | Re-types `query` to the thoughts-aware `GenieQueryAttachmentSchema`, adds `attachment_type` discriminator literal so consumers can `switch (att.attachment_type)` instead of probing which sub-object is populated. |
57
+ | `GenieQueryAttachmentSchema` | Adds `thoughts: GenieThought[]` (the streamed reasoning payload). |
58
+ | `GenieThoughtSchema` | New: `{ thought_type, content }`. See [`GenieThoughtType`](#thought-types) for the known kinds. |
59
+
60
+ ### Thought types
61
+
62
+ Open at the type level (`| (string & {})`) so a new server-side
63
+ thought type doesn't break compilation; the four known types still
64
+ narrow correctly under `switch`:
65
+
66
+ | `thought_type` | Content |
67
+ | ------------------------------- | -------------------------------------------------------------------------------------- |
68
+ | `THOUGHT_TYPE_DESCRIPTION` | One-paragraph restatement of what the user asked. |
69
+ | `THOUGHT_TYPE_DATA_SOURCING` | Markdown bullets of the fully-qualified `catalog.schema.table` sources Genie chose. |
70
+ | `THOUGHT_TYPE_STEPS` | High-level plan Genie wrote before running SQL (one bullet per step). |
71
+ | `THOUGHT_TYPE_UNDERSTANDING` | Ambiguity / interpretation notes ("'revenue' could be gross, net, or recognized..."). |
72
+
73
+ ### Attachment discriminator
74
+
75
+ Genie populates only one of `query` / `text` / `suggested_questions`
76
+ per attachment slot. `tagAttachment(att)` stamps the matching
77
+ discriminator literal onto `att.attachment_type`; `detectAttachmentType(att)`
78
+ just returns it without copying.
79
+
80
+ ```ts
81
+ import { tagAttachment, detectAttachmentType } from "@dbx-tools/genie-shared";
82
+
83
+ const tagged = tagAttachment(att);
84
+ switch (tagged.attachment_type) {
85
+ case "query": // tagged.query is non-null
86
+ case "text": // tagged.text is non-null
87
+ case "suggested_questions": // tagged.suggested_questions is non-null
88
+ }
89
+ ```
90
+
91
+ ## Terminal-status helpers
92
+
93
+ ```ts
94
+ import { TERMINAL_STATUSES, isTerminalStatus, humanizeStatus } from "@dbx-tools/genie-shared";
95
+
96
+ TERMINAL_STATUSES; // ["COMPLETED", "FAILED", "CANCELLED"] as const
97
+
98
+ if (isTerminalStatus(msg.status)) {
99
+ // msg.status is now TerminalStatus
100
+ }
101
+
102
+ humanizeStatus("EXECUTING_QUERY"); // "Running SQL query"
103
+ humanizeStatus("FUTURE_NEW_STATE"); // "Future new state" (falls back to tokenizer)
104
+ ```
105
+
106
+ `humanizeStatus` is the single source of truth for status pill
107
+ labels. Both the server (via `@dbx-tools/genie`) and any UI that
108
+ subscribes to `status` events call it so labels stay in lock-step
109
+ across the wire.
110
+
111
+ ## `GenieChatEvent` union
112
+
113
+ `genieEventChat` (in `@dbx-tools/genie`) drives a turn and yields a
114
+ stream of these. Each variant is a flat `{ type, ...fields }` object
115
+ with `type` as the discriminator and snake_case payload fields hoisted
116
+ to the top level - no `payload` wrapper.
117
+
118
+ | `type` | Source | Notable fields |
119
+ | --------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
120
+ | `question` | Lifecycle. Fires once per turn, on the first `message` yield. | `content`, `message_id`, `conversation_id`, `space_id` |
121
+ | `message` | Lifecycle. Fires once per poll yield. | `message: GenieMessage` |
122
+ | `status` | Top-level `message.status` transitioned. | `status`, `previous_status` |
123
+ | `attachment` | A new attachment slot appeared in `message.attachments[]`. | `index`, `attachment_type` |
124
+ | `thinking` | A new `(thought_type, content)` tuple on a query attachment (value-based dedupe). | `text`, `thought_type` |
125
+ | `text` | Text-attachment `content` appeared or changed. | `text` |
126
+ | `query` | SQL was finalized on a query attachment (transitioned undefined -> string or rewrote). | `sql`, `title?`, `description?` |
127
+ | `statement` | SQL submitted to a warehouse and a `statement_id` was assigned. | `statement_id` |
128
+ | `rows` | Row count on a query attachment changed (fires for `undefined -> 0` and `0 -> N`). | `row_count`, `previous_row_count`, `statement_id` |
129
+ | `suggested_questions` | Follow-up suggested-questions array appeared or rewrote. | `questions: string[]` |
130
+ | `result` | Lifecycle. Fires once on the terminal snapshot. | `status: TerminalStatus`, `message: GenieMessage` |
131
+
132
+ Every variant carries a `GenieChatLocation` mixin (`space_id`,
133
+ `conversation_id?`, `message_id?`, `attachment_id?`) so subscribers
134
+ can route, log, or correlate without re-walking the message.
135
+
136
+ ```ts
137
+ import { type GenieChatEvent } from "@dbx-tools/genie-shared";
138
+
139
+ function handleEvent(event: GenieChatEvent) {
140
+ switch (event.type) {
141
+ case "question":
142
+ console.log(`[Q]`, event.content);
143
+ break;
144
+ case "thinking":
145
+ console.log(`[think:${event.thought_type}]`, event.text);
146
+ break;
147
+ case "query":
148
+ console.log(`[sql]`, event.title, "\n", event.sql);
149
+ break;
150
+ case "rows":
151
+ console.log(`[rows]`, event.previous_row_count, "->", event.row_count);
152
+ break;
153
+ case "result":
154
+ console.log(`[done]`, event.status);
155
+ break;
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### Stream order per turn
161
+
162
+ 1. `question` (deferred to the first `message` yield so the
163
+ assigned `message_id` is present).
164
+ 2. `message` for every poll yield (carries the raw snapshot on
165
+ `event.message`).
166
+ 3. Any derived events the snapshot diff produced (`status`,
167
+ `attachment`, `thinking`, `text`, `query`, `statement`, `rows`,
168
+ `suggested_questions`) in that fixed order.
169
+ 4. On the terminal snapshot, a final `result` event.
170
+
171
+ Errors propagate via the generator throwing - there is no `error`
172
+ variant on the union. Wrap the `for await` in `try / catch` if you
173
+ need to handle failures.
174
+
175
+ ## Detectors + `eventsFromMessage`
176
+
177
+ If you want to derive events from `GenieMessage` snapshots on the
178
+ client (e.g. replaying persisted history, or driving your own loop
179
+ that bypasses `genieEventChat`), use the pure detectors directly:
180
+
181
+ ```ts
182
+ import {
183
+ eventsFromMessage,
184
+ detectStatus,
185
+ detectText,
186
+ } from "@dbx-tools/genie-shared";
187
+
188
+ // All detectors, in stable wire order:
189
+ for (const event of eventsFromMessage(current, previous, spaceId)) {
190
+ handleEvent(event);
191
+ }
192
+
193
+ // Or one detector at a time:
194
+ const statusEvent = detectStatus.detect(current, previous, spaceId);
195
+ const textEvent = detectText.detect(currAttachment, prevAttachment, location, idx);
196
+ ```
197
+
198
+ Each detector is built with `eventDetector(name, detect)` so the
199
+ event name (`"status"`) is a string literal that TS uses to look up
200
+ the diff signature (`message` vs `attachment`) and the allowed
201
+ return-fields shape (`DetectorResult<T>`). Mis-typing the event name
202
+ or returning the wrong fields fails to compile.
203
+
204
+ ## License
205
+
206
+ Apache-2.0
package/package.json CHANGED
@@ -9,10 +9,10 @@
9
9
  }
10
10
  },
11
11
  "name": "@dbx-tools/genie-shared",
12
- "version": "0.1.20",
12
+ "version": "0.1.22",
13
13
  "dependencies": {
14
- "@dbx-tools/sdk-shared": "0.1.20",
15
- "@dbx-tools/shared": "0.1.20",
14
+ "@dbx-tools/sdk-shared": "0.1.22",
15
+ "@dbx-tools/shared": "0.1.22",
16
16
  "zod": "^4.3.6"
17
17
  },
18
18
  "module": "index.ts",