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 +16 -13
- package/dist/index.d.ts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/useOpalZero.d.ts +2 -0
- package/dist/react/useOpalZero.js +101 -0
- package/dist/types.d.ts +2 -2
- package/logo.svg +8 -0
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/react/index.ts +1 -1
- package/src/react/{useMission.ts → useOpalZero.ts} +3 -3
- package/src/types.ts +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
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 (`
|
|
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 {
|
|
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
|
-
|
|
65
|
+
useOpalZero({ client });
|
|
63
66
|
|
|
64
67
|
return (
|
|
65
68
|
<div>
|
|
@@ -324,10 +327,10 @@ import type {
|
|
|
324
327
|
|
|
325
328
|
---
|
|
326
329
|
|
|
327
|
-
### `
|
|
330
|
+
### `useOpalZero(options)` · `opal-zero/react`
|
|
328
331
|
|
|
329
332
|
```ts
|
|
330
|
-
import {
|
|
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
|
-
} =
|
|
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 `
|
|
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 `
|
|
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 } =
|
|
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
|
-
|
|
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
|
-
|
|
516
|
-
|
|
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,
|
|
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";
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { useOpalZero } from './useOpalZero';
|
package/dist/react/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { useOpalZero } from './useOpalZero';
|
|
@@ -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
|
|
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
|
|
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
package/src/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type {
|
|
|
20
20
|
UploadResult,
|
|
21
21
|
ConfigStatus,
|
|
22
22
|
BentoCard,
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
UseOpalZeroOptions,
|
|
24
|
+
UseOpalZeroReturn,
|
|
25
25
|
} from "./types";
|
|
26
26
|
// Note: useMission is NOT exported from the main entry — import from 'opal-zero/react'
|
package/src/react/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { useOpalZero } from './useOpalZero';
|
|
@@ -3,8 +3,8 @@ import type {
|
|
|
3
3
|
MissionStatus,
|
|
4
4
|
MissionState,
|
|
5
5
|
BentoCard,
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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
|
|
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
|
|
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. */
|