opal-zero 1.2.3 → 1.2.5

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 CHANGED
@@ -1,4 +1,7 @@
1
- # opal-zero
1
+ <div align="center">
2
+ <img src="logo.svg" width="64" alt="OpalZero" />
3
+ <h1>opal-zero</h1>
4
+ </div>
2
5
 
3
6
  TypeScript SDK for [OpalZero](https://github.com/albertobarnabo/opal-zero-engine) — a self-hosted multi-agent intelligence kernel. Give it a plain-English intent; get back structured results streamed in real time.
4
7
 
@@ -41,7 +44,7 @@ yarn add opal-zero # yarn
41
44
  pnpm add opal-zero # pnpm
42
45
  ```
43
46
 
44
- React (`useMission`) is a peer dependency — install React separately if you haven't already:
47
+ React (`useOpalZero`) is a peer dependency — install React separately if you haven't already:
45
48
 
46
49
  ```bash
47
50
  npm install react react-dom
@@ -53,13 +56,13 @@ npm install react react-dom
53
56
 
54
57
  ```tsx
55
58
  import { OpalZeroClient } from "opal-zero";
56
- import { useMission } from "opal-zero/react";
59
+ import { useOpalZero } from "opal-zero/react";
57
60
 
58
61
  const client = new OpalZeroClient({ baseUrl: "http://localhost:8000" });
59
62
 
60
63
  export function MissionRunner() {
61
64
  const { run, status, cards, activeAgent, error, missionId, refine } =
62
- useMission({ client });
65
+ useOpalZero({ client });
63
66
 
64
67
  return (
65
68
  <div>
@@ -324,10 +327,10 @@ import type {
324
327
 
325
328
  ---
326
329
 
327
- ### `useMission(options)` · `opal-zero/react`
330
+ ### `useOpalZero(options)` · `opal-zero/react`
328
331
 
329
332
  ```ts
330
- import { useMission } from "opal-zero/react";
333
+ import { useOpalZero } from "opal-zero/react";
331
334
 
332
335
  const {
333
336
  run, // (intent: string, model?: string) => Promise<void>
@@ -339,7 +342,7 @@ const {
339
342
  missionId, // string | null — pass to refine()
340
343
  missionState, // MissionState | null — raw payload for custom renderers
341
344
  reset, // () => void — reset to idle
342
- } = useMission({
345
+ } = useOpalZero({
343
346
  client, // OpalZeroClient instance
344
347
  model?, // default model string
345
348
  onEvent?, // (event: MissionEvent) => void — tap every event for side effects
@@ -360,7 +363,7 @@ const {
360
363
 
361
364
  ### `parseBentoCards(state, options?)`
362
365
 
363
- Converts a raw `MissionState` into an ordered `BentoCard[]`. Used internally by `useMission` — export it when building a custom renderer.
366
+ Converts a raw `MissionState` into an ordered `BentoCard[]`. Used internally by `useOpalZero` — export it when building a custom renderer.
364
367
 
365
368
  ```ts
366
369
  import { parseBentoCards } from "opal-zero";
@@ -405,11 +408,11 @@ interface BentoCard {
405
408
 
406
409
  ### Mission refinement
407
410
 
408
- Refinement runs a second pass on an existing mission with a narrower intent. New results are merged into the original `data_payload` — keys that didn't exist before are added, keys that changed are updated. The `useMission` hook marks changed cards `isRefined: true` so you can highlight them.
411
+ Refinement runs a second pass on an existing mission with a narrower intent. New results are merged into the original `data_payload` — keys that didn't exist before are added, keys that changed are updated. The `useOpalZero` hook marks changed cards `isRefined: true` so you can highlight them.
409
412
 
410
413
  ```tsx
411
414
  // After a mission completes:
412
- const { missionId, refine } = useMission({ client });
415
+ const { missionId, refine } = useOpalZero({ client });
413
416
 
414
417
  // First run
415
418
  await run("Compare the top 3 EVs under $60k");
@@ -465,7 +468,7 @@ if (!tavily) {
465
468
  Use `onEvent` to tap the SSE stream for cross-cutting concerns without coupling them to component state:
466
469
 
467
470
  ```tsx
468
- useMission({
471
+ useOpalZero({
469
472
  client,
470
473
  onEvent(event) {
471
474
  analytics.track("opalzero_event", { type: event.type });
@@ -512,8 +515,8 @@ import type {
512
515
 
513
516
  // Bento / React hook
514
517
  BentoCard,
515
- UseMissionOptions,
516
- UseMissionReturn,
518
+ UseOpalZeroOptions,
519
+ UseOpalZeroReturn,
517
520
  } from "opal-zero";
518
521
  ```
519
522
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { OpalZeroClient } from "./client";
2
2
  export { parseBentoCards } from "./parseBentoCards";
3
- export type { OpalZeroClientConfig, MissionEvent, TaskStartedEvent, TaskCompletedEvent, TaskFailedEvent, GovernorExpandEvent, MissionCompleteEvent, MissionFailedEvent, MissionPausedEvent, AwaitingFeedbackEvent, UnknownEvent, MissionSnapshot, MissionSummary, MissionState, Task, MissionStatus, UploadResult, ConfigStatus, BentoCard, UseMissionOptions, UseMissionReturn, } from "./types";
3
+ export type { OpalZeroClientConfig, MissionEvent, TaskStartedEvent, TaskCompletedEvent, TaskFailedEvent, GovernorExpandEvent, MissionCompleteEvent, MissionFailedEvent, MissionPausedEvent, AwaitingFeedbackEvent, UnknownEvent, MissionSnapshot, MissionSummary, MissionState, Task, MissionStatus, UploadResult, ConfigStatus, BentoCard, UseOpalZeroOptions, UseOpalZeroReturn, } from "./types";
@@ -1 +1 @@
1
- export { useMission } from './useMission';
1
+ export { useOpalZero } from './useOpalZero';
@@ -1 +1 @@
1
- export { useMission } from './useMission';
1
+ export { useOpalZero } from './useOpalZero';
@@ -0,0 +1,2 @@
1
+ import type { UseOpalZeroOptions, UseOpalZeroReturn } from '../types';
2
+ export declare function useOpalZero({ client, model: defaultModel, onEvent }: UseOpalZeroOptions): UseOpalZeroReturn;
@@ -0,0 +1,101 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+ import { parseBentoCards } from '../parseBentoCards';
3
+ export function useOpalZero({ client, model: defaultModel, onEvent }) {
4
+ const [status, setStatus] = useState('idle');
5
+ const [cards, setCards] = useState([]);
6
+ const [activeAgent, setActiveAgent] = useState(null);
7
+ const [error, setError] = useState(null);
8
+ const [missionId, setMissionId] = useState(null);
9
+ const [missionState, setMissionState] = useState(null);
10
+ const refinedKeysRef = useRef(new Set());
11
+ function reset() {
12
+ setStatus('idle');
13
+ setCards([]);
14
+ setActiveAgent(null);
15
+ setError(null);
16
+ setMissionId(null);
17
+ setMissionState(null);
18
+ refinedKeysRef.current = new Set();
19
+ }
20
+ async function drainStream(stream, isRefinement, previousKeys) {
21
+ setStatus('running');
22
+ setActiveAgent(null);
23
+ setError(null);
24
+ try {
25
+ for await (const event of stream) {
26
+ onEvent?.(event);
27
+ switch (event.type) {
28
+ case 'task_started': {
29
+ const e = event;
30
+ setActiveAgent({ role: e.role, intent: e.intent });
31
+ break;
32
+ }
33
+ case 'task_completed':
34
+ case 'task_failed':
35
+ setActiveAgent(null);
36
+ break;
37
+ case 'mission_complete': {
38
+ const e = event;
39
+ setMissionId(e.mission_id);
40
+ const state = e.mission_state ?? null;
41
+ setMissionState(state);
42
+ if (state) {
43
+ const ms = state;
44
+ if (isRefinement) {
45
+ const newKeys = new Set(Object.keys(ms.data_payload ?? {}));
46
+ previousKeys.forEach(k => newKeys.delete(k));
47
+ newKeys.forEach(k => refinedKeysRef.current.add(k));
48
+ }
49
+ const parsed = parseBentoCards(ms, { refinedKeys: refinedKeysRef.current });
50
+ if (isRefinement) {
51
+ setCards(prev => {
52
+ const merged = [...prev];
53
+ for (const card of parsed) {
54
+ const idx = merged.findIndex(c => c.key === card.key);
55
+ if (idx >= 0)
56
+ merged[idx] = card;
57
+ else
58
+ merged.push(card);
59
+ }
60
+ return merged;
61
+ });
62
+ }
63
+ else {
64
+ setCards(parsed);
65
+ }
66
+ }
67
+ setStatus('complete');
68
+ break;
69
+ }
70
+ case 'mission_failed': {
71
+ const e = event;
72
+ setError(e.error);
73
+ setStatus('failed');
74
+ break;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ catch (e) {
80
+ const msg = e instanceof Error ? e.message : String(e);
81
+ setError(msg);
82
+ setStatus('failed');
83
+ }
84
+ }
85
+ const run = useCallback(async (intent, model) => {
86
+ reset();
87
+ refinedKeysRef.current = new Set();
88
+ const stream = client.execute(intent, model ?? defaultModel);
89
+ await drainStream(stream, false, new Set());
90
+ // eslint-disable-next-line react-hooks/exhaustive-deps
91
+ }, [client, defaultModel]);
92
+ const refine = useCallback(async (id, intent, model) => {
93
+ if (status === 'running')
94
+ return;
95
+ const previousKeys = new Set(Object.keys(missionState?.data_payload ?? {}));
96
+ const stream = client.missions.refine(id, intent, model ?? defaultModel);
97
+ await drainStream(stream, true, previousKeys);
98
+ // eslint-disable-next-line react-hooks/exhaustive-deps
99
+ }, [client, defaultModel, status, missionState]);
100
+ return { run, refine, status, cards, activeAgent, error, missionId, missionState, reset };
101
+ }
package/dist/types.d.ts CHANGED
@@ -112,14 +112,14 @@ export interface BentoCard {
112
112
  /** True if this card was added/updated by a refinement round */
113
113
  isRefined?: boolean;
114
114
  }
115
- export interface UseMissionOptions {
115
+ export interface UseOpalZeroOptions {
116
116
  client: OpalZeroClient;
117
117
  /** Default model to use. Can be overridden per-call in run(). */
118
118
  model?: string;
119
119
  /** Called for every SSE event before the hook updates its own state. Use this for side effects (trace logs, banners, etc.) that the hook doesn't need to manage. */
120
120
  onEvent?: (event: MissionEvent) => void;
121
121
  }
122
- export interface UseMissionReturn {
122
+ export interface UseOpalZeroReturn {
123
123
  /** Execute a new mission. Clears previous state before starting. */
124
124
  run: (intent: string, model?: string) => Promise<void>;
125
125
  /** Refine an existing mission without clearing the card grid. */
package/logo.svg ADDED
@@ -0,0 +1,8 @@
1
+ <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill="#0DD4C6" d="M 59.96,50.87 L 85.86,53.14 A 36,36,0,0,1,70.65,79.49 L 55.74,58.19 A 10,10,0,0,0,59.96,50.87 Z"/>
3
+ <path fill="#5B8FEE" d="M 54.23,59.06 L 65.21,82.63 A 36,36,0,0,1,34.79,82.63 L 45.77,59.06 A 10,10,0,0,0,54.23,59.06 Z"/>
4
+ <path fill="#9B68F0" d="M 44.26,58.19 L 29.35,79.49 A 36,36,0,0,1,14.14,53.14 L 40.04,50.87 A 10,10,0,0,0,44.26,58.19 Z"/>
5
+ <path fill="#0DD4C6" d="M 40.04,49.13 L 14.14,46.86 A 36,36,0,0,1,29.35,20.51 L 44.26,41.81 A 10,10,0,0,0,40.04,49.13 Z"/>
6
+ <path fill="#5B8FEE" d="M 45.77,40.94 L 34.79,17.37 A 36,36,0,0,1,65.21,17.37 L 54.23,40.94 A 10,10,0,0,0,45.77,40.94 Z"/>
7
+ <path fill="#9B68F0" d="M 55.74,41.81 L 70.65,20.51 A 36,36,0,0,1,85.86,46.86 L 59.96,49.13 A 10,10,0,0,0,55.74,41.81 Z"/>
8
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opal-zero",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "TypeScript client SDK for the OpalZero Intelligence Kernel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -20,7 +20,7 @@ export type {
20
20
  UploadResult,
21
21
  ConfigStatus,
22
22
  BentoCard,
23
- UseMissionOptions,
24
- UseMissionReturn,
23
+ UseOpalZeroOptions,
24
+ UseOpalZeroReturn,
25
25
  } from "./types";
26
26
  // Note: useMission is NOT exported from the main entry — import from 'opal-zero/react'
@@ -1 +1 @@
1
- export { useMission } from './useMission';
1
+ export { useOpalZero } from './useOpalZero';
@@ -3,8 +3,8 @@ import type {
3
3
  MissionStatus,
4
4
  MissionState,
5
5
  BentoCard,
6
- UseMissionOptions,
7
- UseMissionReturn,
6
+ UseOpalZeroOptions,
7
+ UseOpalZeroReturn,
8
8
  MissionEvent,
9
9
  TaskStartedEvent,
10
10
  MissionCompleteEvent,
@@ -12,7 +12,7 @@ import type {
12
12
  } from '../types';
13
13
  import { parseBentoCards } from '../parseBentoCards';
14
14
 
15
- export function useMission({ client, model: defaultModel, onEvent }: UseMissionOptions): UseMissionReturn {
15
+ export function useOpalZero({ client, model: defaultModel, onEvent }: UseOpalZeroOptions): UseOpalZeroReturn {
16
16
  const [status, setStatus] = useState<MissionStatus>('idle');
17
17
  const [cards, setCards] = useState<BentoCard[]>([]);
18
18
  const [activeAgent, setActiveAgent] = useState<{ role: string; intent: string } | null>(null);
package/src/types.ts CHANGED
@@ -156,7 +156,7 @@ export interface BentoCard {
156
156
 
157
157
  // ── useMission ────────────────────────────────────────────────────────────────
158
158
 
159
- export interface UseMissionOptions {
159
+ export interface UseOpalZeroOptions {
160
160
  client: OpalZeroClient;
161
161
  /** Default model to use. Can be overridden per-call in run(). */
162
162
  model?: string;
@@ -164,7 +164,7 @@ export interface UseMissionOptions {
164
164
  onEvent?: (event: MissionEvent) => void;
165
165
  }
166
166
 
167
- export interface UseMissionReturn {
167
+ export interface UseOpalZeroReturn {
168
168
  /** Execute a new mission. Clears previous state before starting. */
169
169
  run: (intent: string, model?: string) => Promise<void>;
170
170
  /** Refine an existing mission without clearing the card grid. */