@cybernetyx1/atlasflow-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/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ PROPRIETARY SOFTWARE LICENSE
2
+
3
+ Copyright (c) 2026 Cybernetyx. All rights reserved.
4
+
5
+ This software and its source code are the proprietary and confidential property
6
+ of the copyright holder. The software is original work authored independently.
7
+
8
+ No part of this software may be copied, reproduced, modified, published,
9
+ distributed, sublicensed, or sold in any form or by any means without the prior
10
+ written permission of the copyright holder, except as expressly permitted by a
11
+ separate written agreement.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
16
+ HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION
17
+ OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE
18
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @cybernetyx1/atlasflow-react
2
+
3
+ React hooks and provider for wiring browser and app UIs to an AtlasFlow agent server.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pnpm add @cybernetyx1/atlasflow-react
9
+ ```
10
+
11
+ Part of the AtlasFlow monorepo. Proprietary.
12
+
13
+ ## Usage
14
+
15
+ Wrap your app in `AtlasFlowProvider` (pass either a prebuilt `client` or `options`). Then use `useAgent` to chat with an agent, `useRunWatch` to follow a durable run's event stream, and `useAtlasFlowClient` to reach the client directly.
16
+
17
+ Never put `ATLASFLOW_API_KEY` in browser code. Mint a short-lived scoped token on your server (see `@cybernetyx1/atlasflow-sdk`) and pass it to the provider.
18
+
19
+ ```tsx
20
+ import { AtlasFlowProvider, useAgent } from "@cybernetyx1/atlasflow-react";
21
+
22
+ function Chat() {
23
+ const agent = useAgent({ agent: "hello", instanceId: "user-123" });
24
+
25
+ return (
26
+ <form
27
+ onSubmit={(event) => {
28
+ event.preventDefault();
29
+ const input = new FormData(event.currentTarget).get("message");
30
+ void agent.send(String(input ?? ""));
31
+ event.currentTarget.reset();
32
+ }}
33
+ >
34
+ {agent.messages.map((message, index) => (
35
+ <p key={index}>{message.role}: {message.content}</p>
36
+ ))}
37
+ <input name="message" />
38
+ <button disabled={agent.isStreaming}>Send</button>
39
+ </form>
40
+ );
41
+ }
42
+
43
+ export function App({ token }: { token: string }) {
44
+ return (
45
+ <AtlasFlowProvider options={{ baseUrl: "https://agent.example", apiKey: token }}>
46
+ <Chat />
47
+ </AtlasFlowProvider>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ```tsx
53
+ import { useRunWatch, useAtlasFlowClient } from "@cybernetyx1/atlasflow-react";
54
+
55
+ function RunStatus() {
56
+ const client = useAtlasFlowClient();
57
+ // Pass a run id or the RunAdmission returned by client.runs.dispatch(...).
58
+ const run = useRunWatch({ target: "run_abc123" });
59
+ return <pre>{run.status}: {run.events.length} events</pre>;
60
+ }
61
+ ```
62
+
63
+ Exports: `AtlasFlowProvider`, `useAtlasFlowClient`, `useAgent`, `useRunWatch`, plus the `AgentChatMessage` / `AgentHookState` / `AgentStatus` types and the hook option/result types.
64
+
65
+ ## License
66
+
67
+ Proprietary. © 2026 Cybernetyx. See LICENSE.
@@ -0,0 +1,55 @@
1
+ import { ReactNode } from 'react';
2
+ import { AtlasEventLike, Usage, AtlasFlowClient, ClientOptions, AgentDispatchInput, RunWatchOptions, RunAdmission } from '@cybernetyx1/atlasflow-sdk';
3
+
4
+ type AgentStatus = "idle" | "streaming" | "success" | "error";
5
+ interface AgentChatMessage {
6
+ role: "user" | "assistant";
7
+ content: string;
8
+ runId?: string;
9
+ pending?: boolean;
10
+ error?: string;
11
+ }
12
+ interface AgentHookState {
13
+ status: AgentStatus;
14
+ messages: AgentChatMessage[];
15
+ events: AtlasEventLike[];
16
+ runId?: string;
17
+ data?: string;
18
+ result?: unknown;
19
+ usage?: Usage;
20
+ error?: Error;
21
+ }
22
+
23
+ interface AtlasFlowProviderProps {
24
+ client?: AtlasFlowClient;
25
+ options?: ClientOptions;
26
+ children?: ReactNode;
27
+ }
28
+ interface UseAgentOptions {
29
+ client?: AtlasFlowClient;
30
+ agent: string;
31
+ instanceId: string;
32
+ initialMessages?: AgentChatMessage[];
33
+ stream?: boolean;
34
+ }
35
+ interface UseAgentResult extends AgentHookState {
36
+ isStreaming: boolean;
37
+ send(input: string | AgentDispatchInput): Promise<void>;
38
+ reset(messages?: AgentChatMessage[]): void;
39
+ stop(): void;
40
+ }
41
+ interface RunWatchState {
42
+ status: "idle" | "watching" | "closed" | "error";
43
+ events: AtlasEventLike[];
44
+ error?: Error;
45
+ }
46
+ interface UseRunWatchOptions extends RunWatchOptions {
47
+ client?: AtlasFlowClient;
48
+ target?: string | RunAdmission | null;
49
+ }
50
+ declare function AtlasFlowProvider({ client, options, children }: AtlasFlowProviderProps): unknown;
51
+ declare function useAtlasFlowClient(client?: AtlasFlowClient): AtlasFlowClient;
52
+ declare function useAgent(options: UseAgentOptions): UseAgentResult;
53
+ declare function useRunWatch({ client: explicitClient, target, ...watchOptions }: UseRunWatchOptions): RunWatchState;
54
+
55
+ export { type AgentChatMessage, type AgentHookState, type AgentStatus, AtlasFlowProvider, type AtlasFlowProviderProps, type RunWatchState, type UseAgentOptions, type UseAgentResult, type UseRunWatchOptions, useAgent, useAtlasFlowClient, useRunWatch };
package/dist/index.js ADDED
@@ -0,0 +1,235 @@
1
+ // src/index.ts
2
+ import {
3
+ createContext,
4
+ createElement,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useReducer,
10
+ useRef
11
+ } from "react";
12
+ import {
13
+ createAtlasFlowClient
14
+ } from "@cybernetyx1/atlasflow-sdk";
15
+
16
+ // src/state.ts
17
+ function initialAgentState(messages = []) {
18
+ return { status: "idle", messages, events: [] };
19
+ }
20
+ function agentReducer(state, action) {
21
+ switch (action.type) {
22
+ case "reset":
23
+ return initialAgentState(action.messages ?? []);
24
+ case "start":
25
+ return {
26
+ ...state,
27
+ status: "streaming",
28
+ error: void 0,
29
+ data: void 0,
30
+ result: void 0,
31
+ usage: void 0,
32
+ events: [],
33
+ messages: [
34
+ ...state.messages,
35
+ { role: "user", content: action.message },
36
+ { role: "assistant", content: "", pending: true }
37
+ ]
38
+ };
39
+ case "event":
40
+ return applyAgentEvent(state, action.event);
41
+ case "sync_result":
42
+ return replaceAssistant(state, {
43
+ status: "success",
44
+ runId: action.result.runId,
45
+ data: action.result.data,
46
+ result: action.result.result,
47
+ usage: action.result.usage,
48
+ content: action.result.data,
49
+ pending: false
50
+ });
51
+ case "error":
52
+ return replaceAssistant(state, {
53
+ status: "error",
54
+ error: action.error,
55
+ content: state.messages.at(-1)?.role === "assistant" ? state.messages.at(-1)?.content ?? "" : "",
56
+ pending: false,
57
+ messageError: action.error.message
58
+ });
59
+ }
60
+ }
61
+ function applyAgentEvent(state, event) {
62
+ const events = [...state.events, event];
63
+ if (event.type === "text_delta") {
64
+ const delta = typeof event.delta === "string" ? event.delta : typeof event.text === "string" ? event.text : "";
65
+ return appendAssistant({ ...state, events }, delta);
66
+ }
67
+ if (event.type === "result") {
68
+ return replaceAssistant({
69
+ ...state,
70
+ events,
71
+ status: "success",
72
+ runId: typeof event.runId === "string" ? event.runId : state.runId,
73
+ data: typeof event.text === "string" ? event.text : typeof event.data === "string" ? event.data : state.data,
74
+ result: "data" in event ? event.data : state.result,
75
+ usage: isUsage(event.usage) ? event.usage : state.usage
76
+ }, {
77
+ status: "success",
78
+ content: typeof event.text === "string" ? event.text : void 0,
79
+ pending: false
80
+ });
81
+ }
82
+ if (event.type === "run_error") {
83
+ const err = new Error(typeof event.message === "string" ? event.message : "run failed");
84
+ return replaceAssistant({ ...state, events }, { status: "error", error: err, pending: false, messageError: err.message });
85
+ }
86
+ return { ...state, events };
87
+ }
88
+ function appendAssistant(state, delta) {
89
+ if (!delta) return state;
90
+ const messages = [...state.messages];
91
+ const last = messages.at(-1);
92
+ if (last?.role === "assistant") {
93
+ messages[messages.length - 1] = { ...last, content: last.content + delta, pending: true };
94
+ } else {
95
+ messages.push({ role: "assistant", content: delta, pending: true });
96
+ }
97
+ return { ...state, messages, data: (state.data ?? "") + delta };
98
+ }
99
+ function replaceAssistant(state, patch) {
100
+ const messages = [...state.messages];
101
+ const last = messages.at(-1);
102
+ const content = patch.content ?? (last?.role === "assistant" ? last.content : "");
103
+ const assistant = {
104
+ role: "assistant",
105
+ content,
106
+ ...patch.runId ?? state.runId ? { runId: patch.runId ?? state.runId } : {},
107
+ ...patch.pending !== void 0 ? { pending: patch.pending } : {},
108
+ ...patch.messageError ? { error: patch.messageError } : {}
109
+ };
110
+ if (last?.role === "assistant") messages[messages.length - 1] = assistant;
111
+ else messages.push(assistant);
112
+ const { content: _content, pending: _pending, messageError: _messageError, ...statePatch } = patch;
113
+ return { ...state, ...statePatch, messages };
114
+ }
115
+ function isUsage(value) {
116
+ if (!value || typeof value !== "object") return false;
117
+ const usage = value;
118
+ return typeof usage.totalTokens === "number" && typeof usage.costTotal === "number";
119
+ }
120
+
121
+ // src/index.ts
122
+ var AtlasFlowContext = createContext(void 0);
123
+ function AtlasFlowProvider({ client, options, children }) {
124
+ const resolved = useMemo(() => client ?? (options ? createAtlasFlowClient(options) : void 0), [client, options?.baseUrl, options?.apiKey, options?.fetch, options?.headers]);
125
+ if (!resolved) throw new Error("AtlasFlowProvider requires either client or options.");
126
+ return createElement(AtlasFlowContext.Provider, { value: resolved }, children);
127
+ }
128
+ function useAtlasFlowClient(client) {
129
+ const ctx = useContext(AtlasFlowContext);
130
+ const resolved = client ?? ctx;
131
+ if (!resolved) throw new Error("No AtlasFlow client found. Wrap your component in AtlasFlowProvider or pass { client }.");
132
+ return resolved;
133
+ }
134
+ function useAgent(options) {
135
+ const client = useAtlasFlowClient(options.client);
136
+ const [state, dispatch] = useReducer(agentReducer, initialAgentState(options.initialMessages));
137
+ const abortRef = useRef(null);
138
+ const stop = useCallback(() => {
139
+ abortRef.current?.abort();
140
+ abortRef.current = null;
141
+ }, []);
142
+ const reset = useCallback((messages = []) => {
143
+ stop();
144
+ dispatch({ type: "reset", messages });
145
+ }, [stop]);
146
+ const send = useCallback(
147
+ async (input) => {
148
+ stop();
149
+ const body = typeof input === "string" ? { message: input } : input;
150
+ const message = body.message ?? "";
151
+ dispatch({ type: "start", message });
152
+ const abort = new AbortController();
153
+ abortRef.current = abort;
154
+ try {
155
+ if (options.stream === false) {
156
+ const result = await client.agents.send(options.agent, options.instanceId, { ...body, message });
157
+ if (!abort.signal.aborted) dispatch({ type: "sync_result", result });
158
+ return;
159
+ }
160
+ for await (const event of client.agents.stream(options.agent, options.instanceId, { ...body, message, signal: abort.signal })) {
161
+ if (abort.signal.aborted) break;
162
+ dispatch({ type: "event", event });
163
+ }
164
+ } catch (err) {
165
+ if (!abort.signal.aborted) dispatch({ type: "error", error: normalizeError(err) });
166
+ } finally {
167
+ if (abortRef.current === abort) abortRef.current = null;
168
+ }
169
+ },
170
+ [client, options.agent, options.instanceId, options.stream, stop]
171
+ );
172
+ useEffect(() => stop, [stop]);
173
+ return {
174
+ ...state,
175
+ isStreaming: state.status === "streaming",
176
+ send,
177
+ reset,
178
+ stop
179
+ };
180
+ }
181
+ function useRunWatch({ client: explicitClient, target, ...watchOptions }) {
182
+ const client = useAtlasFlowClient(explicitClient);
183
+ const [state, setState] = useReducer(runWatchReducer, { status: "idle", events: [] });
184
+ const serializedOptions = JSON.stringify({
185
+ offset: watchOptions.offset,
186
+ after: watchOptions.after,
187
+ limit: watchOptions.limit,
188
+ tail: watchOptions.tail,
189
+ reconnect: watchOptions.reconnect
190
+ });
191
+ useEffect(() => {
192
+ if (!target) {
193
+ setState({ type: "reset" });
194
+ return;
195
+ }
196
+ const abort = new AbortController();
197
+ setState({ type: "start" });
198
+ void (async () => {
199
+ try {
200
+ for await (const event of client.runs.watch(target, { ...watchOptions, signal: abort.signal })) {
201
+ if (abort.signal.aborted) break;
202
+ setState({ type: "event", event });
203
+ }
204
+ if (!abort.signal.aborted) setState({ type: "closed" });
205
+ } catch (err) {
206
+ if (!abort.signal.aborted) setState({ type: "error", error: normalizeError(err) });
207
+ }
208
+ })();
209
+ return () => abort.abort();
210
+ }, [client, target, serializedOptions]);
211
+ return state;
212
+ }
213
+ function runWatchReducer(state, action) {
214
+ switch (action.type) {
215
+ case "reset":
216
+ return { status: "idle", events: [] };
217
+ case "start":
218
+ return { status: "watching", events: [], error: void 0 };
219
+ case "event":
220
+ return { ...state, events: [...state.events, action.event] };
221
+ case "closed":
222
+ return { ...state, status: "closed" };
223
+ case "error":
224
+ return { ...state, status: "error", error: action.error };
225
+ }
226
+ }
227
+ function normalizeError(err) {
228
+ return err instanceof Error ? err : new Error(String(err));
229
+ }
230
+ export {
231
+ AtlasFlowProvider,
232
+ useAgent,
233
+ useAtlasFlowClient,
234
+ useRunWatch
235
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@cybernetyx1/atlasflow-react",
3
+ "version": "0.1.0",
4
+ "description": "React provider and hooks for AtlasFlow agent chat and durable run event streams.",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "author": "Cybernetyx",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Cybernetyx/atlasflow.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "dependencies": {
23
+ "@cybernetyx1/atlasflow-sdk": "0.1.0"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=18"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "node --import tsx --test test/*.test.ts"
35
+ }
36
+ }