@opengeni/react 0.1.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/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # @opengeni/react
2
+
3
+ React hooks and styled components for OpenGeni, built on
4
+ [`@opengeni/sdk`](../sdk): live session streaming, a chat composer, a message
5
+ timeline that renders streaming deltas / tool calls / spawned-worker status,
6
+ session status badges, and fleet tiles for workspace overviews.
7
+
8
+ Design-system-first: every visual decision routes through CSS-variable tokens
9
+ (`styles/tokens.css`) — color, typography, radius, shadow, motion. Dark mode is
10
+ the first-class default; light is an opt-in via `data-og-theme="light"` on any
11
+ ancestor. Components are styled with Tailwind v4 utilities mapped onto the
12
+ tokens, Radix primitives for behavior, and Motion for state-communicating
13
+ animation. Override the tokens to rebrand everything.
14
+
15
+ ## Install & styles
16
+
17
+ The package ships TypeScript source plus two CSS entries. In your Tailwind v4
18
+ entry CSS:
19
+
20
+ ```css
21
+ @import "tailwindcss";
22
+ @import "@opengeni/react/styles.css";
23
+ @source "../node_modules/@opengeni/react/src";
24
+ ```
25
+
26
+ (`@source` lets Tailwind compile the utilities used inside the components.
27
+ Consuming only the tokens without Tailwind? Import
28
+ `@opengeni/react/tokens.css` and use the `--og-*` variables directly.)
29
+
30
+ ## Quick start
31
+
32
+ ```tsx
33
+ import { OpenGeniClient } from "@opengeni/sdk";
34
+ import {
35
+ ChatComposer,
36
+ MessageTimeline,
37
+ OpenGeniProvider,
38
+ SessionStatus,
39
+ useComposer,
40
+ useSessionEvents,
41
+ } from "@opengeni/react";
42
+
43
+ const client = new OpenGeniClient({ baseUrl: "/api/opengeni" }); // proxy through your API
44
+
45
+ function OpsChannel({ sessionId }: { sessionId: string }) {
46
+ const { timeline, sessionStatus } = useSessionEvents(sessionId);
47
+ const composer = useComposer(sessionId);
48
+ return (
49
+ <div className="flex h-full flex-col">
50
+ {sessionStatus ? <SessionStatus status={sessionStatus} /> : null}
51
+ <MessageTimeline items={timeline} status={sessionStatus} className="min-h-0 flex-1" />
52
+ <ChatComposer composer={composer} status={sessionStatus} />
53
+ </div>
54
+ );
55
+ }
56
+
57
+ export function App() {
58
+ return (
59
+ <OpenGeniProvider client={client} workspaceId={workspaceId}>
60
+ <OpsChannel sessionId={sessionId} />
61
+ </OpenGeniProvider>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## Hooks
67
+
68
+ - `useSessionEvents(sessionId)` — live stream + replay on the SDK's
69
+ exactly-once/ordered event delivery; returns the raw `events`, the projected
70
+ `timeline`, the latest `sessionStatus`, and the connection state. Updates are
71
+ batched per animation-frame-ish window so long replays stay smooth.
72
+ - `useComposer(sessionId, { sendExtras, defaultMode })` — draft/send/interrupt
73
+ state plus the compose-time **queue-vs-steer** choice (`mode`/`setMode`,
74
+ default `"queue"`): queue stacks the message behind the running turn, steer
75
+ interrupts and injects it now. Drafts survive failed sends; each draft
76
+ reuses one `clientEventId` across retries so the server dedupes.
77
+ `sendExtras` (object or function evaluated at send time) merges
78
+ resources/tools/model/reasoningEffort into every message. All human input is
79
+ plain chat text by design; approvals flow as control events
80
+ (`useSessionControl`), not bespoke widgets.
81
+ - `useTurnQueue(sessionId, { events })` — the live turn queue: `queue` (queued
82
+ turns in execution order), `activeTurn`, and optimistic `editTurn` /
83
+ `reorderTurns` / `removeTurn` that reconcile with the server (failed
84
+ mutations roll back via refetch). Live-updates on `turn.*` events — pass the
85
+ `events` log from `useSessionEvents` to reuse its stream, or let it tail the
86
+ session itself.
87
+ - `useGoal(sessionId, { events })` — goal state + autonomy counters
88
+ (`autoContinuations`, `noProgressStreak`) with `pause(rationale?)` /
89
+ `resume()`. Goal-less sessions yield `goal: null`. Live-updates on `goal.*`
90
+ events.
91
+ - `useSessionControl(sessionId)` — `interrupt(reason?)` and
92
+ `approve`/`reject(approvalId, message?)` for `requires_action` approvals.
93
+ - `useSession(sessionId)` — fetch one session (optional polling).
94
+ - `useWorkspaceSessions()` / `useScheduledTasks()` — workspace lists for
95
+ fleet/manager views (optional polling).
96
+ - `useEnvironments()` — workspace environments with create/update/remove and
97
+ write-only `setVariable`/`deleteVariable` (values never come back on reads).
98
+ - `usePacks()` — capability packs + installations with
99
+ register/enable/remove and `installationFor(packId)`.
100
+ - `useWorkspaces()` — the caller's workspaces with create/update (client-only;
101
+ not bound to the provider's workspace).
102
+ - `useBillingUsage({ accountId?, workspaceId? })` — credit balance + recent
103
+ usage events for billing meters (client-only, optional polling).
104
+
105
+ All workspace-scoped hooks resolve the client/workspace from
106
+ `<OpenGeniProvider>` or accept `{ client, workspaceId }` per call
107
+ (`useWorkspaces`/`useBillingUsage` need only the client). They depend on
108
+ `SessionClientLike` — a structural slice of `OpenGeniClient` — so proxy-backed
109
+ or scripted clients work unchanged.
110
+
111
+ ## Timeline projection
112
+
113
+ `buildTimeline(events)` is a pure, tested reducer from the raw event log to
114
+ renderable items (user/agent messages with streaming flags, tool calls matched
115
+ to outputs, `session_create`/`session_send_message` calls promoted to worker
116
+ items, sandbox operations with command output, goal markers, status changes,
117
+ notices). User messages carry their attached `resources` and requested `tools`
118
+ so consumers can render attachment chips. `groupTimeline` clusters consecutive
119
+ activity for collapsed display. Use them directly if you want custom rendering
120
+ with the same semantics.
121
+
122
+ ## Components
123
+
124
+ - `ChatComposer` — auto-growing textarea, Enter-to-send (IME-safe), stop
125
+ control while a turn runs, inline error recovery. Slots for app chrome:
126
+ `controlsStart` (footer controls like model pickers / attach buttons),
127
+ `header` (e.g. attachment chips above the field), and `onPaste`
128
+ (paste-image-to-attach).
129
+ - `MessageTimeline` — the session timeline with stick-to-bottom scrolling, a
130
+ "jump to latest" affordance, streaming caret, collapsible activity clusters,
131
+ and worker cards (wire `onOpenSession` to drill into a worker). Pass
132
+ `renderMessageText` to plug a markdown renderer.
133
+ - `SessionStatus` / `StatusDot` — status badges; live states breathe.
134
+ - `FleetTile` — one session in a fleet grid: title, status, model, recency.
135
+
136
+ ## Demo harness
137
+
138
+ `bun run demo` (from this package) serves a harness that drives the real hooks
139
+ and components against a scripted mock client — a manager ops-channel narrative
140
+ with streaming, tool calls, and a worker spawn, plus fleet and scheduled-task
141
+ views and a dark/light toggle. `bun run demo:build` is part of the repo gate.