@openhex-ai/agent-sdk 0.0.1 → 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 +123 -25
- package/dist/client-1lMkhPMb.d.ts +1066 -0
- package/dist/index.d.ts +5 -1066
- package/dist/react/index.d.ts +277 -0
- package/dist/react/index.js +1529 -0
- package/dist/react/index.js.map +1 -0
- package/dist/tools/index.d.ts +41 -2
- package/dist/{index-DOE19uln.d.ts → tools-DWFaPtFE.d.ts} +1 -38
- package/package.json +28 -3
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@ Build AI agents on the **Openhex** platform. The same agent loop, built-in
|
|
|
4
4
|
tools, hooks, sessions, and MCP support that power Openhex agents — exposed as a
|
|
5
5
|
programmable TypeScript library.
|
|
6
6
|
|
|
7
|
-
> **Status.** The **chat API
|
|
8
|
-
>
|
|
9
|
-
> conversations protocol and the published
|
|
10
|
-
> The local `query()` agent loop is still a
|
|
11
|
-
> `NotImplementedError`) and lands in a follow-up MR.
|
|
7
|
+
> **Status.** The **chat API**, the **React chat UI** (`/react`), and the
|
|
8
|
+
> **workspace API** are implemented — all speak the exact protocol the platform
|
|
9
|
+
> exposes (the user-side apps' conversations protocol and the published
|
|
10
|
+
> `/api/v2/workspaces/*` surface). The local `query()` agent loop is still a
|
|
11
|
+
> scaffold (throws `NotImplementedError`) and lands in a follow-up MR.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -37,12 +37,12 @@ back over SSE until the turn completes.
|
|
|
37
37
|
```ts
|
|
38
38
|
import { OpenhexClient } from '@openhex-ai/agent-sdk';
|
|
39
39
|
|
|
40
|
-
const client = new OpenhexClient({ agentId: '…'
|
|
40
|
+
const client = new OpenhexClient({ agentId: '…' /* apiKey from env */ });
|
|
41
41
|
|
|
42
42
|
// Simplest: send and await the aggregated turn (creates a conversation).
|
|
43
43
|
const turn = await client.sendMessage('Summarize the latest support tickets');
|
|
44
44
|
console.log(turn.text);
|
|
45
|
-
console.log(turn.toolCalls);
|
|
45
|
+
console.log(turn.toolCalls); // tools the agent used
|
|
46
46
|
console.log(turn.conversationId); // reuse to continue the chat
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -85,7 +85,7 @@ for await (const record of client.chat.stream(conversationId, { lastEventId: use
|
|
|
85
85
|
if (record.raw.type === 'result') break;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
await client.chat.interrupt(conversationId);
|
|
88
|
+
await client.chat.interrupt(conversationId); // POST /conversations/:id/interrupt
|
|
89
89
|
const { entries } = await client.chat.messages(conversationId); // full history
|
|
90
90
|
```
|
|
91
91
|
|
|
@@ -93,6 +93,97 @@ Each stream record is `{ id, seq, sender, event, timestamp, sessionId, raw }`
|
|
|
93
93
|
where `raw` carries the agent-runtime event (`{ type: 'assistant', message: { content: […] } }`,
|
|
94
94
|
`{ type: 'result' }`, etc.) — identical to what the app's chat UI consumes.
|
|
95
95
|
|
|
96
|
+
## React chat UI (`/react`)
|
|
97
|
+
|
|
98
|
+
Drop a chat box onto any page and it talks to your agent over the same
|
|
99
|
+
protocol. The React layer is a **separate entry point** so the core SDK stays
|
|
100
|
+
framework-free; `react` / `react-dom` are peer dependencies and the styles are
|
|
101
|
+
self-contained (no CSS import, no build step).
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install @openhex-ai/agent-sdk react react-dom
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Turnkey floating widget
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { ChatWidget } from '@openhex-ai/agent-sdk/react';
|
|
111
|
+
|
|
112
|
+
export function App() {
|
|
113
|
+
return (
|
|
114
|
+
<ChatWidget
|
|
115
|
+
agentId="ce9382ad-…"
|
|
116
|
+
token={sessionToken} // a member session token from YOUR backend
|
|
117
|
+
title="HYROX 备赛教练"
|
|
118
|
+
greeting="嗨!我是你的 HYROX 备赛教练,问我任何训练问题吧。"
|
|
119
|
+
accentColor="#0f766e"
|
|
120
|
+
theme="auto" // light | dark | auto
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
That renders a launcher bubble (bottom-right) that opens a panel — a corner
|
|
127
|
+
panel on desktop, full-screen on mobile. Use `<ChatBox>` instead to embed the
|
|
128
|
+
panel inline in your own layout (it fills its parent).
|
|
129
|
+
|
|
130
|
+
### Browser-safe auth — never ship an API key
|
|
131
|
+
|
|
132
|
+
A `mysta_…` API key is a **secret**; it must never reach the browser. Mint a
|
|
133
|
+
short-lived **member session token** on your backend and hand it to the widget:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// —— your backend (Node) ——
|
|
137
|
+
import { OpenhexClient } from '@openhex-ai/agent-sdk';
|
|
138
|
+
const openhex = new OpenhexClient({ apiKey: process.env.OPENHEX_API_KEY }); // sk_ws_… or owner JWT
|
|
139
|
+
app.post('/chat-token', async (req, res) => {
|
|
140
|
+
const { token } = await openhex.workspace('acme').mintSession({
|
|
141
|
+
sp_user_ref: req.user.id,
|
|
142
|
+
ttl_seconds: 3600,
|
|
143
|
+
});
|
|
144
|
+
res.json({ token });
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
// —— your frontend ——
|
|
150
|
+
<ChatWidget
|
|
151
|
+
agentId="…"
|
|
152
|
+
getToken={async () => (await fetch('/chat-token', { method: 'POST' })).json().then(r => r.token)}
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`getToken` is called before every request, so an expiring token refreshes
|
|
157
|
+
transparently. Pass a static `token` if you mint one per page load, or a fully
|
|
158
|
+
built `client` (an `OpenhexClient`) for total control.
|
|
159
|
+
|
|
160
|
+
### Headless hook — bring your own UI
|
|
161
|
+
|
|
162
|
+
`useOpenhexChat` owns the conversation state (messages, streaming, interrupt,
|
|
163
|
+
retry) so you can render whatever you like:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
import { useOpenhexChat } from '@openhex-ai/agent-sdk/react';
|
|
167
|
+
|
|
168
|
+
function MyChat() {
|
|
169
|
+
const { messages, send, isResponding, interrupt } = useOpenhexChat({
|
|
170
|
+
agentId: '…',
|
|
171
|
+
getToken,
|
|
172
|
+
});
|
|
173
|
+
// …render `messages`, call `send(text)` / `interrupt()`
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
| Prop | Purpose |
|
|
178
|
+
| ----------------------------------------------- | ---------------------------------------------------------- |
|
|
179
|
+
| `agentId` | agent to route a new conversation to |
|
|
180
|
+
| `conversationId` | resume an existing conversation (loads history) |
|
|
181
|
+
| `token` / `getToken` | member session token (static or refreshing) |
|
|
182
|
+
| `client` | a pre-built `OpenhexClient` / `AgentChatClient` (advanced) |
|
|
183
|
+
| `theme` / `accentColor` | one-prop theming (`light` \| `dark` \| `auto`) |
|
|
184
|
+
| `greeting`, `title`, `avatarUrl`, `placeholder` | presentation |
|
|
185
|
+
| `injectStyles={false}` | opt out of the bundled CSS and style it yourself |
|
|
186
|
+
|
|
96
187
|
## Workspaces
|
|
97
188
|
|
|
98
189
|
A **workspace** is a billing + membership boundary you (a service provider /
|
|
@@ -103,11 +194,11 @@ reporting. It speaks the same contract documented at
|
|
|
103
194
|
|
|
104
195
|
Auth is the `Bearer` token the client is configured with:
|
|
105
196
|
|
|
106
|
-
| Token
|
|
107
|
-
|
|
|
108
|
-
| Workspace API key (`sk_ws_…`) | `whoami`, member provision/suspend, sessions, member list, credit grants
|
|
109
|
-
| Owner user JWT
|
|
110
|
-
| none
|
|
197
|
+
| Token | Unlocks |
|
|
198
|
+
| ----------------------------- | ----------------------------------------------------------------------------------------- |
|
|
199
|
+
| Workspace API key (`sk_ws_…`) | `whoami`, member provision/suspend, sessions, member list, credit grants |
|
|
200
|
+
| Owner user JWT | the above **plus** reporting (`ledger`, `insights`, member ledger) and API-key management |
|
|
201
|
+
| none | the public SMS-signup endpoints (`sendSmsCode` / `verifySmsCode`) |
|
|
111
202
|
|
|
112
203
|
```ts
|
|
113
204
|
import { OpenhexClient } from '@openhex-ai/agent-sdk';
|
|
@@ -124,7 +215,10 @@ const ws = client.workspace(me.slug);
|
|
|
124
215
|
// Provision a member (idempotent on your sp_user_ref), grant credits,
|
|
125
216
|
// then mint a session JWT for the member's client.
|
|
126
217
|
const member = await ws.provisionMember({ sp_user_ref: 'user-42', display_name: 'Ada' });
|
|
127
|
-
await ws.grantCredits(member.member_id, {
|
|
218
|
+
await ws.grantCredits(member.member_id, {
|
|
219
|
+
amount: 500,
|
|
220
|
+
idempotency_key: `bonus:${member.member_id}`,
|
|
221
|
+
});
|
|
128
222
|
const session = await ws.mintSession({ sp_user_ref: 'user-42', ttl_seconds: 3600 });
|
|
129
223
|
```
|
|
130
224
|
|
|
@@ -133,7 +227,10 @@ Public SMS signup (no auth — requires `public_sms_signup_enabled` on the works
|
|
|
133
227
|
```ts
|
|
134
228
|
const pub = new OpenhexClient({ apiKey: 'unused' }); // token ignored on these routes
|
|
135
229
|
await pub.workspaces.sendSmsCode('acme', { phone: '13800000000' });
|
|
136
|
-
const session = await pub.workspaces.verifySmsCode('acme', {
|
|
230
|
+
const session = await pub.workspaces.verifySmsCode('acme', {
|
|
231
|
+
phone: '13800000000',
|
|
232
|
+
code: '123456',
|
|
233
|
+
});
|
|
137
234
|
```
|
|
138
235
|
|
|
139
236
|
> The admin-side routes (workspace ledger, insights, per-member ledger,
|
|
@@ -179,17 +276,18 @@ const q = query({
|
|
|
179
276
|
|
|
180
277
|
## Concepts
|
|
181
278
|
|
|
182
|
-
| Concept | Where
|
|
183
|
-
| --------------------------- |
|
|
279
|
+
| Concept | Where |
|
|
280
|
+
| --------------------------- | ----------------------------------------------------------- |
|
|
184
281
|
| Chat with a published agent | `client.sendMessage()`, `client.runTurn()`, `client.chat.*` |
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
|
|
|
282
|
+
| Embeddable chat UI | `<ChatWidget>`, `<ChatBox>`, `useOpenhexChat()` (`/react`) |
|
|
283
|
+
| Resumable record stream | `client.chat.stream({ lastEventId })` |
|
|
284
|
+
| One-shot / streaming runs | `query()` (scaffold) |
|
|
285
|
+
| Stateful client + sessions | `OpenhexClient` |
|
|
286
|
+
| Built-in & custom tools | `options.allowedTools`, `tool()`, `createSdkMcpServer()` |
|
|
287
|
+
| Lifecycle hooks | `options.hooks`, `hookMatcher()` |
|
|
288
|
+
| Subagents | `options.agents` |
|
|
289
|
+
| External integrations (MCP) | `options.mcpServers` |
|
|
290
|
+
| Permissions | `options.permissionMode`, `options.canUseTool` |
|
|
193
291
|
|
|
194
292
|
The surface intentionally mirrors the [Claude Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview)
|
|
195
293
|
so the mental model transfers directly.
|