Skip to content

Commit 16ef2ec

Browse files
authored
Merge pull request #974 from nteract/intelligence-2
Add useConversationArc hook and docs
2 parents 2c3839a + 7919789 commit 16ef2ec

23 files changed

Lines changed: 2315 additions & 273 deletions

.clinerules

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,35 @@ function SuggestedChart({ data, intent }) {
326326
```
327327

328328
### Conversation-arc telemetry (`semiotic/ai`)
329-
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`, plus `interrogation-asked` / `interrogation-answered` for chat-with-chart sessions. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330330
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331331
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332-
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333-
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op). `getEvents()` returns a referentially stable snapshot until the next mutation — safe for `useSyncExternalStore`.
333+
- **`subscribeToConversationArcChange(fn)`** → fires on any state mutation (record / clear / flush / reset / enable). Used by the React hook for snapshot tracking; sinks should use `subscribe()` (event-typed) instead.
334+
- **`useConversationArc({ enableOnMount?, disableOnUnmount?, capacity?, sessionId? })`** → React hook returning `{ history, summary, enabled, sessionId, record, clear }`. `summary` is the live `ConversationArcSummary` (per-type counts, components seen, audiences seen, duration, latestArcId). Subscribes via `useSyncExternalStore` for tear-free re-renders.
335+
- **`summarizeArc(events)`** → pure reducer producing the same `ConversationArcSummary`. Server-safe, replay-safe.
336+
- **`recordAudienceChange(audience, previous?, { arcId?, meta? }?)`** → sugar over `record({ type: "audience-set", ... })`. Call from your audience-picker's `onChange`.
337+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`; `InterrogationAnsweredEvent` has `answer`, `annotationCount`, `latencyMs`, `error`).
338+
- **Auto-instrumented sources**: `useChartSuggestions` emits `suggestion-shown` whenever the suggestion list changes (dedup by component-list + intent signature); `useChartInterrogation` emits `interrogation-asked` on `ask()` and `interrogation-answered` (with `latencyMs`) when the response returns. Zero-overhead when arc store is disabled.
334339

335340
```ts
336-
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
341+
// React surface
342+
import { useConversationArc } from "semiotic/ai"
343+
344+
function ArcInspector() {
345+
const { history, summary, record, clear } = useConversationArc()
346+
return (
347+
<>
348+
<header>{summary.total} events · {summary.byType["chart-exported"] ?? 0} exports</header>
349+
<EventList events={history} />
350+
</>
351+
)
352+
}
337353

354+
// Non-React surface (sinks, replay)
355+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
338356
enableConversationArc()
339-
const store = getConversationArcStore()
340-
const unsub = store.subscribe((event) => console.log(event.type, event))
341-
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
357+
const unsub = getConversationArcStore().subscribe((event) => sendToAnalytics(event))
342358
```
343359

344360
### Annotation provenance + lifecycle (`semiotic/ai`, types also re-exported from `semiotic`)

.cursorrules

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,35 @@ function SuggestedChart({ data, intent }) {
326326
```
327327

328328
### Conversation-arc telemetry (`semiotic/ai`)
329-
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`, plus `interrogation-asked` / `interrogation-answered` for chat-with-chart sessions. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330330
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331331
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332-
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333-
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op). `getEvents()` returns a referentially stable snapshot until the next mutation — safe for `useSyncExternalStore`.
333+
- **`subscribeToConversationArcChange(fn)`** → fires on any state mutation (record / clear / flush / reset / enable). Used by the React hook for snapshot tracking; sinks should use `subscribe()` (event-typed) instead.
334+
- **`useConversationArc({ enableOnMount?, disableOnUnmount?, capacity?, sessionId? })`** → React hook returning `{ history, summary, enabled, sessionId, record, clear }`. `summary` is the live `ConversationArcSummary` (per-type counts, components seen, audiences seen, duration, latestArcId). Subscribes via `useSyncExternalStore` for tear-free re-renders.
335+
- **`summarizeArc(events)`** → pure reducer producing the same `ConversationArcSummary`. Server-safe, replay-safe.
336+
- **`recordAudienceChange(audience, previous?, { arcId?, meta? }?)`** → sugar over `record({ type: "audience-set", ... })`. Call from your audience-picker's `onChange`.
337+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`; `InterrogationAnsweredEvent` has `answer`, `annotationCount`, `latencyMs`, `error`).
338+
- **Auto-instrumented sources**: `useChartSuggestions` emits `suggestion-shown` whenever the suggestion list changes (dedup by component-list + intent signature); `useChartInterrogation` emits `interrogation-asked` on `ask()` and `interrogation-answered` (with `latencyMs`) when the response returns. Zero-overhead when arc store is disabled.
334339

335340
```ts
336-
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
341+
// React surface
342+
import { useConversationArc } from "semiotic/ai"
343+
344+
function ArcInspector() {
345+
const { history, summary, record, clear } = useConversationArc()
346+
return (
347+
<>
348+
<header>{summary.total} events · {summary.byType["chart-exported"] ?? 0} exports</header>
349+
<EventList events={history} />
350+
</>
351+
)
352+
}
337353

354+
// Non-React surface (sinks, replay)
355+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
338356
enableConversationArc()
339-
const store = getConversationArcStore()
340-
const unsub = store.subscribe((event) => console.log(event.type, event))
341-
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
357+
const unsub = getConversationArcStore().subscribe((event) => sendToAnalytics(event))
342358
```
343359

344360
### Annotation provenance + lifecycle (`semiotic/ai`, types also re-exported from `semiotic`)

.github/copilot-instructions.md

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,35 @@ function SuggestedChart({ data, intent }) {
326326
```
327327

328328
### Conversation-arc telemetry (`semiotic/ai`)
329-
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`, plus `interrogation-asked` / `interrogation-answered` for chat-with-chart sessions. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330330
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331331
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332-
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333-
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op). `getEvents()` returns a referentially stable snapshot until the next mutation — safe for `useSyncExternalStore`.
333+
- **`subscribeToConversationArcChange(fn)`** → fires on any state mutation (record / clear / flush / reset / enable). Used by the React hook for snapshot tracking; sinks should use `subscribe()` (event-typed) instead.
334+
- **`useConversationArc({ enableOnMount?, disableOnUnmount?, capacity?, sessionId? })`** → React hook returning `{ history, summary, enabled, sessionId, record, clear }`. `summary` is the live `ConversationArcSummary` (per-type counts, components seen, audiences seen, duration, latestArcId). Subscribes via `useSyncExternalStore` for tear-free re-renders.
335+
- **`summarizeArc(events)`** → pure reducer producing the same `ConversationArcSummary`. Server-safe, replay-safe.
336+
- **`recordAudienceChange(audience, previous?, { arcId?, meta? }?)`** → sugar over `record({ type: "audience-set", ... })`. Call from your audience-picker's `onChange`.
337+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`; `InterrogationAnsweredEvent` has `answer`, `annotationCount`, `latencyMs`, `error`).
338+
- **Auto-instrumented sources**: `useChartSuggestions` emits `suggestion-shown` whenever the suggestion list changes (dedup by component-list + intent signature); `useChartInterrogation` emits `interrogation-asked` on `ask()` and `interrogation-answered` (with `latencyMs`) when the response returns. Zero-overhead when arc store is disabled.
334339

335340
```ts
336-
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
341+
// React surface
342+
import { useConversationArc } from "semiotic/ai"
343+
344+
function ArcInspector() {
345+
const { history, summary, record, clear } = useConversationArc()
346+
return (
347+
<>
348+
<header>{summary.total} events · {summary.byType["chart-exported"] ?? 0} exports</header>
349+
<EventList events={history} />
350+
</>
351+
)
352+
}
337353

354+
// Non-React surface (sinks, replay)
355+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
338356
enableConversationArc()
339-
const store = getConversationArcStore()
340-
const unsub = store.subscribe((event) => console.log(event.type, event))
341-
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
357+
const unsub = getConversationArcStore().subscribe((event) => sendToAnalytics(event))
342358
```
343359

344360
### Annotation provenance + lifecycle (`semiotic/ai`, types also re-exported from `semiotic`)

.windsurfrules

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,35 @@ function SuggestedChart({ data, intent }) {
326326
```
327327

328328
### Conversation-arc telemetry (`semiotic/ai`)
329-
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
329+
Opt-in event store that records the arc of an AI-assisted session: `suggestion-shown → suggestion-chosen → audience-set → chart-rendered → chart-edited → chart-replaced → chart-exported | chart-abandoned`, plus `interrogation-asked` / `interrogation-answered` for chat-with-chart sessions. Module-scoped, no React provider needed. Default surface is a no-op — call `enableConversationArc()` to start recording.
330330
- **`enableConversationArc({ capacity?, sessionId? })`** → enables recording. Bounded ring buffer (default 1000 events). Safe to call multiple times; reuses the existing session unless `sessionId` is overridden.
331331
- **`disableConversationArc()`** → stops recording without dropping buffered events.
332-
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op).
333-
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`).
332+
- **`getConversationArcStore()`** → returns `{ enabled, sessionId, capacity, record(input), flush(), getEvents(), subscribe(listener), clear(), reset() }`. Methods are safe to call when disabled (no-op). `getEvents()` returns a referentially stable snapshot until the next mutation — safe for `useSyncExternalStore`.
333+
- **`subscribeToConversationArcChange(fn)`** → fires on any state mutation (record / clear / flush / reset / enable). Used by the React hook for snapshot tracking; sinks should use `subscribe()` (event-typed) instead.
334+
- **`useConversationArc({ enableOnMount?, disableOnUnmount?, capacity?, sessionId? })`** → React hook returning `{ history, summary, enabled, sessionId, record, clear }`. `summary` is the live `ConversationArcSummary` (per-type counts, components seen, audiences seen, duration, latestArcId). Subscribes via `useSyncExternalStore` for tear-free re-renders.
335+
- **`summarizeArc(events)`** → pure reducer producing the same `ConversationArcSummary`. Server-safe, replay-safe.
336+
- **`recordAudienceChange(audience, previous?, { arcId?, meta? }?)`** → sugar over `record({ type: "audience-set", ... })`. Call from your audience-picker's `onChange`.
337+
- **Events**: `ConversationArcEvent` discriminated union with `type`, `timestamp`, `sessionId`, optional `arcId` + `meta`. Each variant carries its own payload (e.g. `SuggestionShownEvent` has `components`, `intent`, `topScore`, `audience`; `InterrogationAnsweredEvent` has `answer`, `annotationCount`, `latencyMs`, `error`).
338+
- **Auto-instrumented sources**: `useChartSuggestions` emits `suggestion-shown` whenever the suggestion list changes (dedup by component-list + intent signature); `useChartInterrogation` emits `interrogation-asked` on `ask()` and `interrogation-answered` (with `latencyMs`) when the response returns. Zero-overhead when arc store is disabled.
334339

335340
```ts
336-
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
341+
// React surface
342+
import { useConversationArc } from "semiotic/ai"
343+
344+
function ArcInspector() {
345+
const { history, summary, record, clear } = useConversationArc()
346+
return (
347+
<>
348+
<header>{summary.total} events · {summary.byType["chart-exported"] ?? 0} exports</header>
349+
<EventList events={history} />
350+
</>
351+
)
352+
}
337353

354+
// Non-React surface (sinks, replay)
355+
import { enableConversationArc, getConversationArcStore } from "semiotic/ai"
338356
enableConversationArc()
339-
const store = getConversationArcStore()
340-
const unsub = store.subscribe((event) => console.log(event.type, event))
341-
store.record({ type: "suggestion-shown", components: ["LineChart"], intent: "trend" })
357+
const unsub = getConversationArcStore().subscribe((event) => sendToAnalytics(event))
342358
```
343359

344360
### Annotation provenance + lifecycle (`semiotic/ai`, types also re-exported from `semiotic`)

0 commit comments

Comments
 (0)