@hugex/ai 2.8.7 → 2.8.8

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.
Files changed (2) hide show
  1. package/README.md +283 -0
  2. package/package.json +3 -2
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # @hugex/ai
2
+
3
+ Ora agent runtime for Cloudflare Workers — flow execution (`FlowEngine`), session state (`AgentDO`), server route handlers, and the React client hook (`useAgent`).
4
+
5
+ Flows are **designed** in [hx-ai](https://github.com/hugex/hx) (`AgentDesign.tsx`, React Flow canvas) and **saved** to HX Core. Tenant apps install this package, wire the worker routes, and connect the UI with `useAgent`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @hugex/ai
11
+ ```
12
+
13
+ Peer/runtime deps (React 19, `react-router` 7, `zod`, `zustand`, `ai`, etc.) are declared in the package — align versions with your tenant app.
14
+
15
+ ## Exports
16
+
17
+ | Import | Purpose |
18
+ |---|---|
19
+ | `@hugex/ai` | Server: `handleAgentsRoutes`, `handleAuthRoutes`, `handleCommonRoutes`, `handleIntancesRoutes`, `AgentDO` |
20
+ | `@hugex/ai/client` | Client: `useAgent`, `useAgentStore`, `getAgentStore` |
21
+ | `@hugex/ai/common` | Shared types: `NodeType`, WS `messageTypes`, flow/message types |
22
+
23
+ ## Worker setup (required)
24
+
25
+ Each tenant Worker chains the route handlers and re-exports the Durable Object:
26
+
27
+ ```ts
28
+ // workers/app.ts
29
+ import {
30
+ handleAgentsRoutes,
31
+ handleAuthRoutes,
32
+ handleCommonRoutes,
33
+ handleIntancesRoutes,
34
+ } from '@hugex/ai';
35
+ import { createRequestHandler } from 'react-router';
36
+
37
+ const requestHandler = createRequestHandler(
38
+ () => import('virtual:react-router/server-build'),
39
+ import.meta.env.MODE,
40
+ );
41
+
42
+ export default {
43
+ async fetch(request, env, ctx) {
44
+ const common = await handleCommonRoutes(request, env, ctx);
45
+ if (common instanceof Response) return common;
46
+
47
+ const auth = await handleAuthRoutes(request, env, ctx);
48
+ if (auth instanceof Response) return auth;
49
+
50
+ const agent = await handleAgentsRoutes(request, env, ctx);
51
+ if (agent instanceof Response) return agent;
52
+
53
+ const instances = await handleIntancesRoutes(request, env, ctx);
54
+ if (instances instanceof Response) return instances;
55
+
56
+ return requestHandler(request, { cloudflare: { env, ctx }, state: { ...auth } });
57
+ },
58
+ } satisfies ExportedHandler<Env>;
59
+
60
+ export { AgentDO } from '@hugex/ai';
61
+ ```
62
+
63
+ `wrangler.jsonc` must bind the Durable Object:
64
+
65
+ ```jsonc
66
+ {
67
+ "durable_objects": {
68
+ "bindings": [{ "class_name": "AgentDO", "name": "AGENT" }]
69
+ },
70
+ "migrations": [{ "tag": "v2", "new_sqlite_classes": ["AgentDO"] }]
71
+ }
72
+ ```
73
+
74
+ Required env vars (typical): `HX_CORE_URL`, `HX_ORA_URL`, `HX_API_KEY`, tenant Vectorize binding `VDB_<tenant>_1024`, `AI` (Workers AI).
75
+
76
+ ## Flow IDs and agent configuration
77
+
78
+ A **flow** is the graph you edit in hx-ai **Agent Design** (`/ai/agents/:id`). The URL param `id` is the **flowId** (e.g. `1rShqGMrANRGmXTN1mjTJB`).
79
+
80
+ At runtime:
81
+
82
+ | Concept | Value | Notes |
83
+ |---|---|---|
84
+ | **flowId** | Agent Design `:id` | Identifies which flow graph to run; also used as the Cloudflare `agent` name |
85
+ | **sessionId** | UUID (or stable string) | One chat session; maps to one `AgentDO` instance |
86
+ | **DO name** | `` `${flowId}::${sessionId}` `` | Server-side session key |
87
+
88
+ Flow metadata (tenant, visibility, status) lives in HX Core KV. The DO loads flow **state** (nodes/edges) from `GET {HX_CORE_URL}/ora/flow/{flowId}/state` on WebSocket connect.
89
+
90
+ ### Node types (flow graph)
91
+
92
+ Design-time node data (configured in Agent Design panels):
93
+
94
+ | Node | Key data |
95
+ |---|---|
96
+ | **Start** | `greeting`, `allow_images`, `allow_documents`, `memory_*` toggles |
97
+ | **LLM** | `template`, `model`, `tools`, `varMapping`, `outVars` |
98
+ | **Memo** | `rag_id`, `query_key`, `topk` |
99
+ | **Classifier** | `input_variable`, `items[]` |
100
+ | **API** | `url`, `method`, `headers`, `body`, `varMapping` |
101
+ | **Answer** | Required flow terminus |
102
+
103
+ Rules: exactly one Start, at least one Answer, all paths connected.
104
+
105
+ ## `useAgent` — client hook
106
+
107
+ `useAgent` opens a WebSocket to the session DO, decodes the `TYPE::CONTENT` wire protocol, and writes conversation state into a Zustand store. UI components subscribe to the store and call `send`, `clearHistory`, etc.
108
+
109
+ ### Options
110
+
111
+ ```ts
112
+ useAgent({
113
+ host?: string; // WS host; omit when same origin as the page
114
+ basePath?: string; // path segment under host (see below)
115
+ agent?: string; // flowId
116
+ name?: string; // sessionId
117
+ query?: Record<string, string | null> | (() => Promise<Record<string, string | null>>);
118
+ user?: string; // anonymous session label; sent as UID on connect
119
+ storeKey?: string; // isolate store when multiple agents on one screen
120
+ });
121
+ ```
122
+
123
+ Returns `{ connection, store, send, clearHistory, sendAuth, setIdentity, approve, reject, ... }`.
124
+
125
+ ### WebSocket paths
126
+
127
+ Two server routes exist in `@hugex/ai`:
128
+
129
+ | Route | Auth | Typical use |
130
+ |---|---|---|
131
+ | `/api/agent/flow/{flowId}/{sessionId}` | JWT / `access_token` query | Tenant chat UIs, Run panel |
132
+ | `/api/ora/connect/{flowId}/{sessionId}` | Agent API key (`authorization` query/header) | hx-ai design assistant |
133
+
134
+ ### Pattern 1 — tenant chat (single agent)
135
+
136
+ Same-origin tenant app; no `host` needed. Uses the global store (`useAgentStore`).
137
+
138
+ ```tsx
139
+ import { useAgent, useAgentStore } from '@hugex/ai/client';
140
+
141
+ function Chat({ flowId, sessionId }: { flowId: string; sessionId: string }) {
142
+ const { messages, typingMessage } = useAgentStore();
143
+
144
+ const { send, clearHistory } = useAgent({
145
+ basePath: `api/agent/flow/${flowId}/${sessionId}`,
146
+ agent: flowId,
147
+ name: sessionId,
148
+ });
149
+
150
+ // render messages, call send(text) on submit
151
+ }
152
+ ```
153
+
154
+ Reference: `ora-base/app/secure/chat/components/ChatView.tsx`.
155
+
156
+ ### Pattern 2 — Run panel (design-time test)
157
+
158
+ Flow under design in Agent Design; `flowId` from route params; session from run store; JWT via `query`:
159
+
160
+ ```tsx
161
+ const { id: flowId } = useParams<{ id: string }>();
162
+ const sessionId = '…'; // from run session store
163
+ const socketUrl = tenant.data.ora.socketServer; // e.g. tenant worker host
164
+
165
+ const { send, sendAuth, clearHistory } = useAgent({
166
+ host: socketUrl,
167
+ basePath: `api/agent/flow/${flowId}/${sessionId}`,
168
+ agent: flowId,
169
+ name: sessionId,
170
+ query: async () => ({ access_token: await readAccessToken() }),
171
+ });
172
+
173
+ const messages = useAgentStore((s) => s.messages);
174
+ ```
175
+
176
+ Reference: `hx-ai/.../RunStatusPanel.tsx` (opened from `AgentDesign.tsx`).
177
+
178
+ Start-node flags (`allow_images`, `allow_documents`, `memory_enabled`) are read from the canvas nodes to drive the Run UI.
179
+
180
+ ### Pattern 3 — multiple agents on one screen (`storeKey`)
181
+
182
+ When two agents mount together (e.g. **Run panel** + **design assistant** in Agent Design), pass a stable `storeKey` so their chat state does not collide:
183
+
184
+ ```tsx
185
+ const { send, sendAuth, store } = useAgent({
186
+ host: ORA_SOCKET_HOST,
187
+ basePath: `api/ora/connect/${ORA_FLOW_ID}/${sessionId}`,
188
+ agent: ORA_FLOW_ID,
189
+ name: sessionId,
190
+ query: { authorization: ORA_AUTH_KEY },
191
+ user: email ?? undefined,
192
+ storeKey: 'agent-design', // stable for this component's lifetime
193
+ });
194
+
195
+ const messages = store((s) => s.messages);
196
+ const status = store((s) => s.status);
197
+ ```
198
+
199
+ Reference: `hx-ai/.../AgentPanel.tsx`. Child components that need the same isolated state must use the **same** `storeKey` via `getAgentStore('agent-design')`.
200
+
201
+ **Rule:** `storeKey` must be constant for the component lifetime (do not derive from per-render state).
202
+
203
+ ## Runtime credentials (`sendAuth`)
204
+
205
+ Tools may reference `{{ auth.NAME }}` placeholders. Push credentials with `sendAuth(name, value)` — they never appear in chat history.
206
+
207
+ **Important:** re-push credentials **before every `send`**, not once on connect. Durable Object hibernation clears in-memory auth mid-connection without disconnecting the WebSocket.
208
+
209
+ ```ts
210
+ const sendTurn = async (text: string) => {
211
+ const token = await readAccessToken();
212
+ if (token) sendAuth('ACCESS_TOKEN', token);
213
+ send(text);
214
+ };
215
+ ```
216
+
217
+ `clearHistory()` wipes server auth too — re-push after clearing.
218
+
219
+ ## Store fields (reactive UI)
220
+
221
+ Subscribe via `useAgentStore` (global) or `store` (isolated):
222
+
223
+ | Field | Use |
224
+ |---|---|
225
+ | `messages` | Chat history (replayed from DO on connect via `HIS::`) |
226
+ | `typingMessage` | Streaming assistant text (`TS` / `TSC`) |
227
+ | `reasoningMessage` | Streaming reasoning (`RS` / `RSC`) |
228
+ | `status` | `'connecting' \| 'connected' \| 'disconnected'` — gate input on `'connected'` |
229
+ | `flowConfig` | Start-node config from server (`CFG::`: greeting, attachments) |
230
+ | `events` | Tool/info events from `SE::` stream |
231
+ | `composioAuth` | OAuth redirect when Composio manual auth is required |
232
+ | `approvals` | Human-in-the-loop tool approvals |
233
+
234
+ ## Wire protocol (summary)
235
+
236
+ Inbound frames use `TYPE::CONTENT`:
237
+
238
+ | Code | Meaning |
239
+ |---|---|
240
+ | `HIS` | Full history replay |
241
+ | `SE` | Server event (tools, memory, Composio, …) |
242
+ | `TS` / `TSC` | Assistant text stream / complete |
243
+ | `RS` / `RSC` | Reasoning stream / complete |
244
+ | `CFG` | Flow config (attachments, greeting) |
245
+ | `ERR` | Error string |
246
+
247
+ Outbound: `M:` user message, `CH:` clear history, `AUT:` runtime credential, `UID:` session identity.
248
+
249
+ Full reference: Ora `WEBSOCKET_EVENTS.md` (in the `ora/` source repo).
250
+
251
+ ## Vite
252
+
253
+ Exclude `@hugex/ai` from `optimizeDeps` in tenant apps:
254
+
255
+ ```ts
256
+ optimizeDeps: {
257
+ exclude: ['@hugex/ai'],
258
+ },
259
+ ```
260
+
261
+ ## Release (maintainers)
262
+
263
+ From the `ora/` source repo:
264
+
265
+ ```bash
266
+ task publish:ai # bump patch, build, publish
267
+ task publish:ai -- --dry-run # build + npm pack only
268
+ task build:ai # build to ../ai without publish
269
+ ```
270
+
271
+ ## Architecture (high level)
272
+
273
+ ```
274
+ hx-ai Agent Design ──save──► HX Core (flow state + KV metadata)
275
+ ▲
276
+ Tenant Worker (@hugex/ai) ──fetch──┘
277
+ ├ handleAgentsRoutes → WebSocket → AgentDO (per flowId::sessionId)
278
+ └ FlowEngine runs Start → … → Answer
279
+
280
+ Client (useAgent) ◄──WebSocket TYPE::CONTENT──► AgentDO
281
+ ```
282
+
283
+ Session memory, secrets (`{{ secrets.NAME }}`), Vectorize memo, and tool execution run inside the DO; shared data is fetched from HX Core / hx-ora APIs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hugex/ai",
3
- "version": "2.8.7",
3
+ "version": "2.8.8",
4
4
  "description": "Ora AI Agent Framework",
5
5
  "author": "a@hugex.co",
6
6
  "license": "UNLICENSED",
@@ -45,7 +45,8 @@
45
45
  "access": "public"
46
46
  },
47
47
  "files": [
48
- "dist"
48
+ "dist",
49
+ "README.md"
49
50
  ],
50
51
  "main": "./dist/index.js",
51
52
  "module": "./dist/index.js",