@deuz-sdk/react 1.7.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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Umutcan Edizaslan (U-C4N / Deuz)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @deuz-sdk/react
2
+
3
+ React bindings for the [Deuz SDK](https://github.com/Deuz-AI/Deuz-SDK) — `useChat` / `useObject` hooks and minimal headless components on top of `@deuz-sdk/core`.
4
+
5
+ ```bash
6
+ npm i @deuz-sdk/core @deuz-sdk/react
7
+ ```
8
+
9
+ Thin adapter by design: every chat-state transformation lives in `@deuz-sdk/core/chat` (the pure reducer/branch helpers) and the Deuz UI wire (`@deuz-sdk/core/ui`); this package only binds them to React state. `react` is a peer dependency (`^18 || ^19`). The legacy `@deuz-sdk/core/react` subpath keeps working but is frozen; new features land here.
10
+
11
+ ## API
12
+
13
+ ### `useChat(options): UseChatResult`
14
+
15
+ Options: `api`, `initialMessages?` (rendered via `uiFromMessages`), `headers?`, `body?`, `chatId?` (merged into every request body), `resume?` (`{ endpoint, lastEventId? }` — enables `reconnect()`), `generateId?`, `onToolCall?` (client-tool executor, auto round-trip with self-healing errors), `onError?`, `fetch?`.
16
+
17
+ Result:
18
+
19
+ - State: `messages: UIMessage[]`, `status: 'idle' | 'streaming' | 'error'`, `error`, `pendingApprovals: ToolApprovalRequest[]`, `cost?: { costUsd, cacheSavingsUsd? }`, `budgetExceeded?: { kind, limit, value }`, `dataParts: { name, payload }[]`, `citations`.
20
+ - Methods: `sendMessage(text)`, `stop()`, `regenerate()` (core `dropTrailingAssistant`), `editAndResend(messageId, text)` (core `branchBeforeUserMessage`), `addToolApprovalResponse(response)` (auto-preserves the request's signed `token`; auto-resumes with `approvalResponses` once every pending approval has a verdict), `reconnect()` (reads the resume endpoint via `connectDeuzStream`).
21
+
22
+ ### `useObject<T>(options): UseObjectResult<T>`
23
+
24
+ Streams `toDeuzObjectStreamResponse` output. Options: `api`, `headers?`, `fetch?`. Result: `object` (latest `DeepPartial<T>`), `isLoading`, `error`, `submit(input)`, `stop()`.
25
+
26
+ ### Headless components (zero styling)
27
+
28
+ - `ToolApprovalCard({ approval, onRespond, render? })` — Approve/Deny for one pending approval; the verdict always carries the request's signed `token`. `render` overrides presentation and receives wired `approve()` / `deny(reason?)` callbacks.
29
+ - `CostBadge({ cost, format? })` — `$X.XXXX` plus ` (saved $Y.YYYY)` when cache savings are positive; `format` overrides.
30
+
31
+ ### Re-exported core types
32
+
33
+ `UIMessage`, `UIToolCall`, `AssistantTurnState`, `ChatHistory`, `DeuzUIPart`, `Message`, `ToolApprovalRequest`, `ToolApprovalResponse`.
34
+
35
+ See the [main README](https://github.com/Deuz-AI/Deuz-SDK#readme) for the full SDK tour.
package/dist/index.cjs ADDED
@@ -0,0 +1,368 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var chat = require('@deuz-sdk/core/chat');
5
+ var ui = require('@deuz-sdk/core/ui');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ // src/use-chat.ts
9
+ function foldPart(turn, part) {
10
+ const next = chat.applyUIPart(turn, part);
11
+ if (part.type === "tool-approval-request" && part.token !== void 0) {
12
+ return {
13
+ ...next,
14
+ approvals: next.approvals.map(
15
+ (a) => a.approvalId === part.approvalId && a.token === void 0 ? { ...a, token: part.token } : a
16
+ )
17
+ };
18
+ }
19
+ return next;
20
+ }
21
+ var defaultGenerateId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `deuz-ui-${Math.random().toString(36).slice(2)}`;
22
+ function useChat(options) {
23
+ const genId = options.generateId ?? defaultGenerateId;
24
+ const [messages, setMessages] = react.useState(
25
+ () => chat.uiFromMessages(options.initialMessages ?? [], genId)
26
+ );
27
+ const [status, setStatus] = react.useState("idle");
28
+ const [error, setError] = react.useState(void 0);
29
+ const [pendingApprovals, setPendingApprovals] = react.useState([]);
30
+ const [cost, setCost] = react.useState(void 0);
31
+ const [budgetExceeded, setBudgetExceeded] = react.useState(
32
+ void 0
33
+ );
34
+ const [dataParts, setDataParts] = react.useState([]);
35
+ const [citations, setCitations] = react.useState([]);
36
+ const uiRef = react.useRef(messages);
37
+ const canonicalRef = react.useRef(
38
+ options.initialMessages ? [...options.initialMessages] : []
39
+ );
40
+ const turnRef = react.useRef(void 0);
41
+ const approvalsRef = react.useRef([]);
42
+ const verdictsRef = react.useRef([]);
43
+ const abortRef = react.useRef(null);
44
+ const lastEventIdRef = react.useRef(options.resume?.lastEventId);
45
+ const pushMessage = react.useCallback((message) => {
46
+ uiRef.current = [...uiRef.current, message];
47
+ setMessages(uiRef.current);
48
+ }, []);
49
+ const syncTurn = react.useCallback((turn) => {
50
+ uiRef.current = [...uiRef.current.slice(0, -1), turn.message];
51
+ setMessages(uiRef.current);
52
+ if (turn.costUsd !== void 0) {
53
+ setCost({
54
+ costUsd: turn.costUsd,
55
+ ...turn.cacheSavingsUsd !== void 0 ? { cacheSavingsUsd: turn.cacheSavingsUsd } : {}
56
+ });
57
+ }
58
+ setBudgetExceeded(turn.budgetExceeded);
59
+ setDataParts(turn.dataParts);
60
+ setCitations(turn.citations);
61
+ }, []);
62
+ const stop = react.useCallback(() => {
63
+ abortRef.current?.abort();
64
+ }, []);
65
+ const run = react.useCallback(
66
+ async (approvalResponses) => {
67
+ const controller = new AbortController();
68
+ abortRef.current = controller;
69
+ setStatus("streaming");
70
+ setError(void 0);
71
+ try {
72
+ for (; ; ) {
73
+ const doFetch = options.fetch ?? fetch;
74
+ const res = await doFetch(options.api, {
75
+ method: "POST",
76
+ headers: { "content-type": "application/json", ...options.headers },
77
+ body: JSON.stringify({
78
+ messages: canonicalRef.current,
79
+ ...options.chatId !== void 0 ? { chatId: options.chatId } : {},
80
+ ...approvalResponses?.length ? { approvalResponses } : {},
81
+ ...options.body
82
+ }),
83
+ signal: controller.signal
84
+ });
85
+ approvalResponses = void 0;
86
+ let turn = chat.createAssistantTurn(genId());
87
+ turnRef.current = turn;
88
+ pushMessage(turn.message);
89
+ syncTurn(turn);
90
+ for await (const part of ui.readDeuzStream(res)) {
91
+ turn = foldPart(turn, part);
92
+ turnRef.current = turn;
93
+ syncTurn(turn);
94
+ }
95
+ if (turn.error !== void 0) throw new Error(turn.error);
96
+ canonicalRef.current = [...canonicalRef.current, chat.assistantMessageFromTurn(turn)];
97
+ turnRef.current = void 0;
98
+ if (turn.approvals.length > 0) {
99
+ approvalsRef.current = turn.approvals;
100
+ verdictsRef.current = [];
101
+ setPendingApprovals(turn.approvals);
102
+ return;
103
+ }
104
+ const toolCalls = turn.message.toolCalls ?? [];
105
+ const serverResults = new Set(turn.serverResults);
106
+ const clientPending = toolCalls.filter((t) => !serverResults.has(t.toolCallId));
107
+ if (clientPending.length === 0 || !options.onToolCall) return;
108
+ const results = [];
109
+ for (const call of clientPending) {
110
+ try {
111
+ const out = await options.onToolCall({
112
+ toolCallId: call.toolCallId,
113
+ toolName: call.toolName,
114
+ input: call.input
115
+ });
116
+ results.push({ toolCallId: call.toolCallId, result: out });
117
+ turn = chat.applyUIPart(turn, {
118
+ type: "tool-result",
119
+ toolCallId: call.toolCallId,
120
+ toolName: call.toolName,
121
+ output: out
122
+ });
123
+ } catch (err) {
124
+ const message = err instanceof Error ? err.message : String(err);
125
+ results.push({ toolCallId: call.toolCallId, result: message, isError: true });
126
+ turn = chat.applyUIPart(turn, {
127
+ type: "tool-result",
128
+ toolCallId: call.toolCallId,
129
+ toolName: call.toolName,
130
+ output: message,
131
+ isError: true
132
+ });
133
+ }
134
+ syncTurn(turn);
135
+ }
136
+ canonicalRef.current = [...canonicalRef.current, chat.clientToolResultMessage(results)];
137
+ }
138
+ } catch (err) {
139
+ if (controller.signal.aborted) return;
140
+ const e = err instanceof Error ? err : new Error(String(err));
141
+ setError(e);
142
+ setStatus("error");
143
+ options.onError?.(e);
144
+ return;
145
+ } finally {
146
+ setStatus((s) => s === "error" ? s : "idle");
147
+ }
148
+ },
149
+ [
150
+ options.api,
151
+ options.fetch,
152
+ options.headers,
153
+ options.body,
154
+ options.chatId,
155
+ options.generateId,
156
+ options.onToolCall,
157
+ options.onError,
158
+ pushMessage,
159
+ syncTurn
160
+ ]
161
+ );
162
+ const sendMessage = react.useCallback(
163
+ async (text) => {
164
+ canonicalRef.current = [...canonicalRef.current, { role: "user", content: text }];
165
+ pushMessage({ id: genId(), role: "user", content: text });
166
+ approvalsRef.current = [];
167
+ verdictsRef.current = [];
168
+ setPendingApprovals([]);
169
+ await run();
170
+ },
171
+ [run, pushMessage, options.generateId]
172
+ );
173
+ const regenerate = react.useCallback(async () => {
174
+ const cut = chat.dropTrailingAssistant({ ui: uiRef.current, canonical: canonicalRef.current });
175
+ uiRef.current = cut.ui;
176
+ canonicalRef.current = cut.canonical;
177
+ setMessages(cut.ui);
178
+ approvalsRef.current = [];
179
+ verdictsRef.current = [];
180
+ setPendingApprovals([]);
181
+ await run();
182
+ }, [run]);
183
+ const editAndResend = react.useCallback(
184
+ async (messageId, text) => {
185
+ const cut = chat.branchBeforeUserMessage(
186
+ { ui: uiRef.current, canonical: canonicalRef.current },
187
+ messageId
188
+ );
189
+ if (!cut) return;
190
+ uiRef.current = cut.ui;
191
+ canonicalRef.current = cut.canonical;
192
+ setMessages(cut.ui);
193
+ approvalsRef.current = [];
194
+ verdictsRef.current = [];
195
+ setPendingApprovals([]);
196
+ await sendMessage(text);
197
+ },
198
+ [sendMessage]
199
+ );
200
+ const addToolApprovalResponse = react.useCallback(
201
+ async (response) => {
202
+ const request = approvalsRef.current.find((p) => p.approvalId === response.approvalId);
203
+ const verdict = response.token === void 0 && request?.token !== void 0 ? { ...response, token: request.token } : response;
204
+ verdictsRef.current = [
205
+ ...verdictsRef.current.filter((v) => v.approvalId !== verdict.approvalId),
206
+ verdict
207
+ ];
208
+ const verdicts = verdictsRef.current;
209
+ const allSettled = approvalsRef.current.every(
210
+ (p) => verdicts.some((v) => v.approvalId === p.approvalId)
211
+ );
212
+ if (!allSettled) return;
213
+ approvalsRef.current = [];
214
+ verdictsRef.current = [];
215
+ setPendingApprovals([]);
216
+ await run(verdicts);
217
+ },
218
+ [run]
219
+ );
220
+ const reconnect = react.useCallback(async () => {
221
+ const resume = options.resume;
222
+ if (!resume) return;
223
+ const controller = new AbortController();
224
+ abortRef.current = controller;
225
+ setStatus("streaming");
226
+ setError(void 0);
227
+ try {
228
+ const cursor = lastEventIdRef.current;
229
+ let turn;
230
+ if (turnRef.current && cursor !== void 0) {
231
+ turn = turnRef.current;
232
+ } else {
233
+ turn = chat.createAssistantTurn(genId());
234
+ if (!turnRef.current) pushMessage(turn.message);
235
+ }
236
+ turnRef.current = turn;
237
+ const parts = ui.connectDeuzStream(resume.endpoint, {
238
+ ...cursor !== void 0 ? { lastEventId: cursor } : {},
239
+ onCursor: (id) => {
240
+ lastEventIdRef.current = id;
241
+ },
242
+ signal: controller.signal,
243
+ ...options.fetch ? { fetch: options.fetch } : {},
244
+ ...options.headers ? { headers: options.headers } : {}
245
+ });
246
+ for await (const part of parts) {
247
+ turn = foldPart(turn, part);
248
+ turnRef.current = turn;
249
+ syncTurn(turn);
250
+ }
251
+ if (turn.error !== void 0) throw new Error(turn.error);
252
+ canonicalRef.current = [...canonicalRef.current, chat.assistantMessageFromTurn(turn)];
253
+ turnRef.current = void 0;
254
+ if (turn.approvals.length > 0) {
255
+ approvalsRef.current = turn.approvals;
256
+ verdictsRef.current = [];
257
+ setPendingApprovals(turn.approvals);
258
+ }
259
+ } catch (err) {
260
+ if (controller.signal.aborted) return;
261
+ const e = err instanceof Error ? err : new Error(String(err));
262
+ setError(e);
263
+ setStatus("error");
264
+ options.onError?.(e);
265
+ return;
266
+ } finally {
267
+ setStatus((s) => s === "error" ? s : "idle");
268
+ }
269
+ }, [
270
+ options.resume,
271
+ options.fetch,
272
+ options.headers,
273
+ options.generateId,
274
+ options.onError,
275
+ pushMessage,
276
+ syncTurn
277
+ ]);
278
+ return {
279
+ messages,
280
+ status,
281
+ error,
282
+ pendingApprovals,
283
+ ...cost !== void 0 ? { cost } : {},
284
+ ...budgetExceeded !== void 0 ? { budgetExceeded } : {},
285
+ dataParts,
286
+ citations,
287
+ sendMessage,
288
+ stop,
289
+ regenerate,
290
+ editAndResend,
291
+ addToolApprovalResponse,
292
+ reconnect
293
+ };
294
+ }
295
+ function useObject(options) {
296
+ const [object, setObject] = react.useState(void 0);
297
+ const [isLoading, setLoading] = react.useState(false);
298
+ const [error, setError] = react.useState(void 0);
299
+ const abortRef = react.useRef(null);
300
+ const stop = react.useCallback(() => {
301
+ abortRef.current?.abort();
302
+ }, []);
303
+ const submit = react.useCallback(
304
+ async (input) => {
305
+ abortRef.current?.abort();
306
+ const controller = new AbortController();
307
+ abortRef.current = controller;
308
+ setLoading(true);
309
+ setError(void 0);
310
+ setObject(void 0);
311
+ try {
312
+ const doFetch = options.fetch ?? fetch;
313
+ const res = await doFetch(options.api, {
314
+ method: "POST",
315
+ headers: { "content-type": "application/json", ...options.headers },
316
+ body: JSON.stringify({ input }),
317
+ signal: controller.signal
318
+ });
319
+ for await (const part of ui.readDeuzStream(res)) {
320
+ if (part.type === "object-delta") {
321
+ setObject(part.object);
322
+ } else if (part.type === "error") {
323
+ setError(new Error(part.message));
324
+ break;
325
+ }
326
+ }
327
+ } catch (err) {
328
+ if (!controller.signal.aborted) {
329
+ setError(err instanceof Error ? err : new Error(String(err)));
330
+ }
331
+ } finally {
332
+ setLoading(false);
333
+ }
334
+ },
335
+ [options.api, options.fetch, options.headers]
336
+ );
337
+ return { object, isLoading, error, submit, stop };
338
+ }
339
+ function ToolApprovalCard({ approval, onRespond, render }) {
340
+ const respond = (approved, reason) => onRespond({
341
+ approvalId: approval.approvalId,
342
+ approved,
343
+ ...reason !== void 0 ? { reason } : {},
344
+ ...approval.token !== void 0 ? { token: approval.token } : {}
345
+ });
346
+ const approve = () => respond(true);
347
+ const deny = (reason) => respond(false, reason);
348
+ if (render) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render({ approval, approve, deny }) });
349
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-deuz": "tool-approval", children: [
350
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-deuz": "tool-approval-name", children: approval.toolName }),
351
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { "data-deuz": "tool-approval-input", children: JSON.stringify(approval.input, null, 2) }),
352
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: approve, children: "Approve" }),
353
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => deny(), children: "Deny" })
354
+ ] });
355
+ }
356
+ function CostBadge({ cost, format }) {
357
+ if (cost === void 0) return null;
358
+ if (format) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: format(cost) });
359
+ const saved = cost.cacheSavingsUsd !== void 0 && cost.cacheSavingsUsd > 0 ? ` (saved $${cost.cacheSavingsUsd.toFixed(4)})` : "";
360
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-deuz": "cost-badge", children: `$${cost.costUsd.toFixed(4)}${saved}` });
361
+ }
362
+
363
+ exports.CostBadge = CostBadge;
364
+ exports.ToolApprovalCard = ToolApprovalCard;
365
+ exports.useChat = useChat;
366
+ exports.useObject = useObject;
367
+ //# sourceMappingURL=index.cjs.map
368
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/use-chat.ts","../src/use-object.ts","../src/components.tsx"],"names":["applyUIPart","useState","uiFromMessages","useRef","useCallback","createAssistantTurn","readDeuzStream","assistantMessageFromTurn","clientToolResultMessage","dropTrailingAssistant","branchBeforeUserMessage","connectDeuzStream","jsx","Fragment","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * useChat — the React binding over `@deuz-sdk/core/chat` + the Deuz UI wire.\n *\n * THIN by contract: every chat-state transformation is a core call\n * (`createAssistantTurn`/`applyUIPart`/`assistantMessageFromTurn`/\n * `clientToolResultMessage`/`uiFromMessages`/`dropTrailingAssistant`/\n * `branchBeforeUserMessage`). This hook only owns React state, the fetch\n * round-trips, and abort wiring. Supersedes the frozen `@deuz-sdk/core/react`.\n */\nimport { useCallback, useRef, useState } from 'react';\nimport type { Message, ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';\nimport {\n applyUIPart,\n assistantMessageFromTurn,\n branchBeforeUserMessage,\n clientToolResultMessage,\n createAssistantTurn,\n dropTrailingAssistant,\n uiFromMessages,\n} from '@deuz-sdk/core/chat';\nimport type { AssistantTurnState, UIMessage } from '@deuz-sdk/core/chat';\nimport { connectDeuzStream, readDeuzStream } from '@deuz-sdk/core/ui';\n\n/**\n * Fold one wire part via the core reducer. Trivial glue on top: core 1.7.0's\n * `applyUIPart` drops the `token` field of a `tool-approval-request` when it\n * builds the approval entry — re-attach it so verdicts can echo it (D4).\n */\nfunction foldPart(\n turn: AssistantTurnState,\n part: Parameters<typeof applyUIPart>[1],\n): AssistantTurnState {\n const next = applyUIPart(turn, part);\n if (part.type === 'tool-approval-request' && part.token !== undefined) {\n return {\n ...next,\n approvals: next.approvals.map((a) =>\n a.approvalId === part.approvalId && a.token === undefined ? { ...a, token: part.token } : a,\n ),\n };\n }\n return next;\n}\n\n/** Local id fallback — this package is not edge-lint-constrained. */\nconst defaultGenerateId = (): string =>\n typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : `deuz-ui-${Math.random().toString(36).slice(2)}`;\n\n/** Live cumulative USD cost, from the wire's `cost` parts (last one wins). */\nexport interface UseChatCost {\n costUsd: number;\n cacheSavingsUsd?: number;\n}\n\n/** The server's budget guardrail tripped (`budget-exceeded` part). */\nexport interface UseChatBudgetExceeded {\n kind: 'usd' | 'tokens';\n limit: number;\n value: number;\n}\n\nexport interface UseChatResumeOptions {\n /**\n * Resume endpoint (see core's `resumeDeuzStreamResponse`): a GET URL or a\n * factory returning the `Response` for a given cursor. Do NOT point it at\n * the generating POST route — that would re-run the model.\n */\n endpoint: string | ((ctx: { lastEventId?: string }) => Response | Promise<Response>);\n /** Cursor to resume from (e.g. persisted across a page reload). */\n lastEventId?: string;\n}\n\nexport interface UseChatOptions {\n /** Endpoint serving `toDeuzStreamResponse` output. */\n api: string;\n /** Seed canonical history (e.g. restored from a ChatStore) — rendered via `uiFromMessages`. */\n initialMessages?: Message[];\n headers?: Record<string, string>;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Chat identity — merged into every request body (server-side ChatStore persistence). */\n chatId?: string;\n /** Enables `reconnect()` against a resume endpoint (wire v2, `connectDeuzStream`). */\n resume?: UseChatResumeOptions;\n /** Id source for UI messages/turns. Default: `crypto.randomUUID` (with a fallback). */\n generateId?: () => string;\n /**\n * Client-tool executor: called for every streamed tool call the SERVER did\n * not execute. The return value is appended as its tool_result and the chat\n * auto-continues; a throw self-heals as an is_error result.\n */\n onToolCall?: (call: {\n toolCallId: string;\n toolName: string;\n input: unknown;\n }) => Promise<unknown> | unknown;\n onError?: (error: Error) => void;\n /** Injectable for tests / custom transports. Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport interface UseChatResult {\n messages: UIMessage[];\n status: 'idle' | 'streaming' | 'error';\n error: Error | undefined;\n /** Gated tool calls awaiting verdicts — the chat is PAUSED while non-empty. */\n pendingApprovals: ToolApprovalRequest[];\n /** Live cumulative cost from the wire's `cost` parts (last one wins). */\n cost?: UseChatCost;\n /** Set when the server's budget guardrail tripped this turn. */\n budgetExceeded?: UseChatBudgetExceeded;\n /** This turn's app-defined `data-{name}` parts, in arrival order. */\n dataParts: Array<{ name: string; payload: unknown }>;\n /** This turn's RAG citations. */\n citations: AssistantTurnState['citations'];\n sendMessage: (text: string) => Promise<void>;\n /** Abort the in-flight stream (not an error). */\n stop: () => void;\n /** Drop the trailing assistant/tool turns (core `dropTrailingAssistant`) and re-run. */\n regenerate: () => Promise<void>;\n /** Cut history before `messageId` (core `branchBeforeUserMessage`) and send `text`. */\n editAndResend: (messageId: string, text: string) => Promise<void>;\n /**\n * Record one verdict (the request's signed `token` is auto-preserved). Once\n * EVERY pending approval has a verdict, the chat auto-resumes with\n * `approvalResponses` in the request body.\n */\n addToolApprovalResponse: (response: ToolApprovalResponse) => Promise<void>;\n /**\n * Re-read the stream from `resume.endpoint` via `connectDeuzStream` and fold\n * the parts into the current turn. No-op unless `options.resume` is set.\n */\n reconnect: () => Promise<void>;\n}\n\nexport function useChat(options: UseChatOptions): UseChatResult {\n const genId = options.generateId ?? defaultGenerateId;\n const [messages, setMessages] = useState<UIMessage[]>(() =>\n uiFromMessages(options.initialMessages ?? [], genId),\n );\n const [status, setStatus] = useState<'idle' | 'streaming' | 'error'>('idle');\n const [error, setError] = useState<Error | undefined>(undefined);\n const [pendingApprovals, setPendingApprovals] = useState<ToolApprovalRequest[]>([]);\n const [cost, setCost] = useState<UseChatCost | undefined>(undefined);\n const [budgetExceeded, setBudgetExceeded] = useState<UseChatBudgetExceeded | undefined>(\n undefined,\n );\n const [dataParts, setDataParts] = useState<Array<{ name: string; payload: unknown }>>([]);\n const [citations, setCitations] = useState<AssistantTurnState['citations']>([]);\n\n const uiRef = useRef<UIMessage[]>(messages);\n const canonicalRef = useRef<Message[]>(\n options.initialMessages ? [...options.initialMessages] : [],\n );\n /** The in-flight turn — kept across a drop so `reconnect()` can continue it. */\n const turnRef = useRef<AssistantTurnState | undefined>(undefined);\n const approvalsRef = useRef<ToolApprovalRequest[]>([]);\n const verdictsRef = useRef<ToolApprovalResponse[]>([]);\n const abortRef = useRef<AbortController | null>(null);\n const lastEventIdRef = useRef<string | undefined>(options.resume?.lastEventId);\n\n const pushMessage = useCallback((message: UIMessage): void => {\n uiRef.current = [...uiRef.current, message];\n setMessages(uiRef.current);\n }, []);\n\n /** Sync ALL turn-derived React state (the turn's message is the trailing UI element). */\n const syncTurn = useCallback((turn: AssistantTurnState): void => {\n uiRef.current = [...uiRef.current.slice(0, -1), turn.message];\n setMessages(uiRef.current);\n if (turn.costUsd !== undefined) {\n setCost({\n costUsd: turn.costUsd,\n ...(turn.cacheSavingsUsd !== undefined ? { cacheSavingsUsd: turn.cacheSavingsUsd } : {}),\n });\n }\n setBudgetExceeded(turn.budgetExceeded);\n setDataParts(turn.dataParts);\n setCitations(turn.citations);\n }, []);\n\n const stop = useCallback((): void => {\n abortRef.current?.abort();\n }, []);\n\n const run = useCallback(\n async (approvalResponses?: ToolApprovalResponse[]): Promise<void> => {\n const controller = new AbortController();\n abortRef.current = controller;\n setStatus('streaming');\n setError(undefined);\n try {\n // One iteration per model round; client-tool results loop back in.\n for (;;) {\n const doFetch = options.fetch ?? fetch;\n const res = await doFetch(options.api, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...options.headers },\n body: JSON.stringify({\n messages: canonicalRef.current,\n ...(options.chatId !== undefined ? { chatId: options.chatId } : {}),\n ...(approvalResponses?.length ? { approvalResponses } : {}),\n ...options.body,\n }),\n signal: controller.signal,\n });\n approvalResponses = undefined; // consumed by the first round only\n\n let turn = createAssistantTurn(genId());\n turnRef.current = turn;\n pushMessage(turn.message);\n syncTurn(turn); // resets this-turn state (dataParts/citations/budget)\n\n for await (const part of readDeuzStream(res)) {\n turn = foldPart(turn, part);\n turnRef.current = turn;\n syncTurn(turn);\n }\n if (turn.error !== undefined) throw new Error(turn.error);\n\n // Append the canonical assistant turn (client-tools reconstruction).\n canonicalRef.current = [...canonicalRef.current, assistantMessageFromTurn(turn)];\n turnRef.current = undefined;\n\n // Approval pause: verdicts arrive via addToolApprovalResponse.\n if (turn.approvals.length > 0) {\n approvalsRef.current = turn.approvals;\n verdictsRef.current = [];\n setPendingApprovals(turn.approvals);\n return;\n }\n\n // Client-tool auto-round-trip: everything the server didn't execute.\n const toolCalls = turn.message.toolCalls ?? [];\n const serverResults = new Set(turn.serverResults);\n const clientPending = toolCalls.filter((t) => !serverResults.has(t.toolCallId));\n if (clientPending.length === 0 || !options.onToolCall) return;\n const results: Array<{ toolCallId: string; result: unknown; isError?: boolean }> = [];\n for (const call of clientPending) {\n try {\n const out = await options.onToolCall({\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n input: call.input,\n });\n results.push({ toolCallId: call.toolCallId, result: out });\n turn = applyUIPart(turn, {\n type: 'tool-result',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n output: out,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n results.push({ toolCallId: call.toolCallId, result: message, isError: true });\n turn = applyUIPart(turn, {\n type: 'tool-result',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n output: message,\n isError: true,\n });\n }\n syncTurn(turn);\n }\n canonicalRef.current = [...canonicalRef.current, clientToolResultMessage(results)];\n // loop → next round POSTs the extended history\n }\n } catch (err) {\n if (controller.signal.aborted) return; // user abort — not an error\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setStatus('error');\n options.onError?.(e);\n return;\n } finally {\n setStatus((s) => (s === 'error' ? s : 'idle'));\n }\n },\n [\n options.api,\n options.fetch,\n options.headers,\n options.body,\n options.chatId,\n options.generateId,\n options.onToolCall,\n options.onError,\n pushMessage,\n syncTurn,\n ],\n );\n\n const sendMessage = useCallback(\n async (text: string): Promise<void> => {\n canonicalRef.current = [...canonicalRef.current, { role: 'user', content: text }];\n pushMessage({ id: genId(), role: 'user', content: text });\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await run();\n },\n [run, pushMessage, options.generateId],\n );\n\n const regenerate = useCallback(async (): Promise<void> => {\n const cut = dropTrailingAssistant({ ui: uiRef.current, canonical: canonicalRef.current });\n uiRef.current = cut.ui;\n canonicalRef.current = cut.canonical;\n setMessages(cut.ui);\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await run();\n }, [run]);\n\n const editAndResend = useCallback(\n async (messageId: string, text: string): Promise<void> => {\n const cut = branchBeforeUserMessage(\n { ui: uiRef.current, canonical: canonicalRef.current },\n messageId,\n );\n if (!cut) return; // not a user message — nothing to branch\n uiRef.current = cut.ui;\n canonicalRef.current = cut.canonical;\n setMessages(cut.ui);\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await sendMessage(text);\n },\n [sendMessage],\n );\n\n const addToolApprovalResponse = useCallback(\n async (response: ToolApprovalResponse): Promise<void> => {\n // Preserve the request's signed token unless the caller set one.\n const request = approvalsRef.current.find((p) => p.approvalId === response.approvalId);\n const verdict =\n response.token === undefined && request?.token !== undefined\n ? { ...response, token: request.token }\n : response;\n verdictsRef.current = [\n ...verdictsRef.current.filter((v) => v.approvalId !== verdict.approvalId),\n verdict,\n ];\n const verdicts = verdictsRef.current;\n const allSettled = approvalsRef.current.every((p) =>\n verdicts.some((v) => v.approvalId === p.approvalId),\n );\n if (!allSettled) return;\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await run(verdicts);\n },\n [run],\n );\n\n const reconnect = useCallback(async (): Promise<void> => {\n const resume = options.resume;\n if (!resume) return;\n const controller = new AbortController();\n abortRef.current = controller;\n setStatus('streaming');\n setError(undefined);\n try {\n const cursor = lastEventIdRef.current;\n let turn: AssistantTurnState;\n if (turnRef.current && cursor !== undefined) {\n turn = turnRef.current; // continue in place — replay dedupes by seq upstream\n } else {\n // No cursor → full replay rebuilds the turn from the start; it lands in\n // the trailing slot (replacing a partial turn if one is on screen).\n turn = createAssistantTurn(genId());\n if (!turnRef.current) pushMessage(turn.message);\n }\n turnRef.current = turn;\n const parts = connectDeuzStream(resume.endpoint, {\n ...(cursor !== undefined ? { lastEventId: cursor } : {}),\n onCursor: (id) => {\n lastEventIdRef.current = id;\n },\n signal: controller.signal,\n ...(options.fetch ? { fetch: options.fetch } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n });\n for await (const part of parts) {\n turn = foldPart(turn, part);\n turnRef.current = turn;\n syncTurn(turn);\n }\n if (turn.error !== undefined) throw new Error(turn.error);\n canonicalRef.current = [...canonicalRef.current, assistantMessageFromTurn(turn)];\n turnRef.current = undefined;\n if (turn.approvals.length > 0) {\n approvalsRef.current = turn.approvals;\n verdictsRef.current = [];\n setPendingApprovals(turn.approvals);\n }\n } catch (err) {\n if (controller.signal.aborted) return; // user abort — not an error\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setStatus('error');\n options.onError?.(e);\n return;\n } finally {\n setStatus((s) => (s === 'error' ? s : 'idle'));\n }\n }, [\n options.resume,\n options.fetch,\n options.headers,\n options.generateId,\n options.onError,\n pushMessage,\n syncTurn,\n ]);\n\n return {\n messages,\n status,\n error,\n pendingApprovals,\n ...(cost !== undefined ? { cost } : {}),\n ...(budgetExceeded !== undefined ? { budgetExceeded } : {}),\n dataParts,\n citations,\n sendMessage,\n stop,\n regenerate,\n editAndResend,\n addToolApprovalResponse,\n reconnect,\n };\n}\n","/**\n * useObject — streams `toDeuzObjectStreamResponse` output (`object-delta`\n * parts) into React state. Ported from the frozen `@deuz-sdk/core/react` hook.\n */\nimport { useCallback, useRef, useState } from 'react';\nimport type { DeepPartial } from '@deuz-sdk/core';\nimport { readDeuzStream } from '@deuz-sdk/core/ui';\n\nexport interface UseObjectOptions {\n /** Endpoint serving `toDeuzObjectStreamResponse` output. */\n api: string;\n headers?: Record<string, string>;\n /** Injectable for tests / custom transports. Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport interface UseObjectResult<T> {\n /** Latest partial (each `object-delta` replaces it wholesale). */\n object: DeepPartial<T> | undefined;\n isLoading: boolean;\n error: Error | undefined;\n /** POSTs `{ input }` to `api` and streams partials into `object`. */\n submit: (input: unknown) => Promise<void>;\n /** Abort the in-flight stream (not an error). */\n stop: () => void;\n}\n\nexport function useObject<T = unknown>(options: UseObjectOptions): UseObjectResult<T> {\n const [object, setObject] = useState<DeepPartial<T> | undefined>(undefined);\n const [isLoading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n const abortRef = useRef<AbortController | null>(null);\n\n const stop = useCallback((): void => {\n abortRef.current?.abort();\n }, []);\n\n const submit = useCallback(\n async (input: unknown): Promise<void> => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n setLoading(true);\n setError(undefined);\n setObject(undefined);\n try {\n const doFetch = options.fetch ?? fetch;\n const res = await doFetch(options.api, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...options.headers },\n body: JSON.stringify({ input }),\n signal: controller.signal,\n });\n for await (const part of readDeuzStream(res)) {\n if (part.type === 'object-delta') {\n setObject(part.object as DeepPartial<T>);\n } else if (part.type === 'error') {\n setError(new Error(part.message));\n break;\n }\n }\n } catch (err) {\n if (!controller.signal.aborted) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n setLoading(false);\n }\n },\n [options.api, options.fetch, options.headers],\n );\n\n return { object, isLoading, error, submit, stop };\n}\n","/**\n * Headless chat components — zero styling, zero business logic. They only\n * wire core types (`ToolApprovalRequest`/`ToolApprovalResponse`, the useChat\n * cost state) to DOM events; presentation is overridable via render props.\n */\nimport type { ReactNode } from 'react';\nimport type { ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';\nimport type { UseChatCost } from './use-chat';\n\nexport interface ToolApprovalCardProps {\n approval: ToolApprovalRequest;\n /** Feed this straight into `useChat().addToolApprovalResponse`. */\n onRespond: (response: ToolApprovalResponse) => void;\n /** Override presentation; the component supplies the wired callbacks. */\n render?: (ctx: {\n approval: ToolApprovalRequest;\n approve: () => void;\n deny: (reason?: string) => void;\n }) => ReactNode;\n}\n\n/**\n * One pending tool approval. The verdict always carries the request's signed\n * `token` (D4) — callers never have to thread it themselves.\n */\nexport function ToolApprovalCard({ approval, onRespond, render }: ToolApprovalCardProps) {\n const respond = (approved: boolean, reason?: string): void =>\n onRespond({\n approvalId: approval.approvalId,\n approved,\n ...(reason !== undefined ? { reason } : {}),\n ...(approval.token !== undefined ? { token: approval.token } : {}),\n });\n const approve = (): void => respond(true);\n const deny = (reason?: string): void => respond(false, reason);\n\n if (render) return <>{render({ approval, approve, deny })}</>;\n return (\n <div data-deuz=\"tool-approval\">\n <span data-deuz=\"tool-approval-name\">{approval.toolName}</span>\n <pre data-deuz=\"tool-approval-input\">{JSON.stringify(approval.input, null, 2)}</pre>\n <button type=\"button\" onClick={approve}>\n Approve\n </button>\n <button type=\"button\" onClick={() => deny()}>\n Deny\n </button>\n </div>\n );\n}\n\nexport interface CostBadgeProps {\n /** The `useChat` cost state (undefined until the first `cost` part arrives). */\n cost: UseChatCost | undefined;\n /** Override presentation. */\n format?: (cost: UseChatCost) => ReactNode;\n}\n\n/** Live cost readout: `$X.XXXX`, plus ` (saved $Y.YYYY)` when caching saved money. */\nexport function CostBadge({ cost, format }: CostBadgeProps) {\n if (cost === undefined) return null;\n if (format) return <>{format(cost)}</>;\n const saved =\n cost.cacheSavingsUsd !== undefined && cost.cacheSavingsUsd > 0\n ? ` (saved $${cost.cacheSavingsUsd.toFixed(4)})`\n : '';\n return <span data-deuz=\"cost-badge\">{`$${cost.costUsd.toFixed(4)}${saved}`}</span>;\n}\n"]}
@@ -0,0 +1,142 @@
1
+ import { Message, ToolApprovalRequest, ToolApprovalResponse, DeepPartial } from '@deuz-sdk/core';
2
+ export { Message, ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';
3
+ import { UIMessage, AssistantTurnState } from '@deuz-sdk/core/chat';
4
+ export { AssistantTurnState, ChatHistory, UIMessage, UIToolCall } from '@deuz-sdk/core/chat';
5
+ import * as react from 'react';
6
+ import { ReactNode } from 'react';
7
+ export { DeuzUIPart } from '@deuz-sdk/core/ui';
8
+
9
+ /** Live cumulative USD cost, from the wire's `cost` parts (last one wins). */
10
+ interface UseChatCost {
11
+ costUsd: number;
12
+ cacheSavingsUsd?: number;
13
+ }
14
+ /** The server's budget guardrail tripped (`budget-exceeded` part). */
15
+ interface UseChatBudgetExceeded {
16
+ kind: 'usd' | 'tokens';
17
+ limit: number;
18
+ value: number;
19
+ }
20
+ interface UseChatResumeOptions {
21
+ /**
22
+ * Resume endpoint (see core's `resumeDeuzStreamResponse`): a GET URL or a
23
+ * factory returning the `Response` for a given cursor. Do NOT point it at
24
+ * the generating POST route — that would re-run the model.
25
+ */
26
+ endpoint: string | ((ctx: {
27
+ lastEventId?: string;
28
+ }) => Response | Promise<Response>);
29
+ /** Cursor to resume from (e.g. persisted across a page reload). */
30
+ lastEventId?: string;
31
+ }
32
+ interface UseChatOptions {
33
+ /** Endpoint serving `toDeuzStreamResponse` output. */
34
+ api: string;
35
+ /** Seed canonical history (e.g. restored from a ChatStore) — rendered via `uiFromMessages`. */
36
+ initialMessages?: Message[];
37
+ headers?: Record<string, string>;
38
+ /** Extra fields merged into every request body. */
39
+ body?: Record<string, unknown>;
40
+ /** Chat identity — merged into every request body (server-side ChatStore persistence). */
41
+ chatId?: string;
42
+ /** Enables `reconnect()` against a resume endpoint (wire v2, `connectDeuzStream`). */
43
+ resume?: UseChatResumeOptions;
44
+ /** Id source for UI messages/turns. Default: `crypto.randomUUID` (with a fallback). */
45
+ generateId?: () => string;
46
+ /**
47
+ * Client-tool executor: called for every streamed tool call the SERVER did
48
+ * not execute. The return value is appended as its tool_result and the chat
49
+ * auto-continues; a throw self-heals as an is_error result.
50
+ */
51
+ onToolCall?: (call: {
52
+ toolCallId: string;
53
+ toolName: string;
54
+ input: unknown;
55
+ }) => Promise<unknown> | unknown;
56
+ onError?: (error: Error) => void;
57
+ /** Injectable for tests / custom transports. Defaults to global fetch. */
58
+ fetch?: typeof fetch;
59
+ }
60
+ interface UseChatResult {
61
+ messages: UIMessage[];
62
+ status: 'idle' | 'streaming' | 'error';
63
+ error: Error | undefined;
64
+ /** Gated tool calls awaiting verdicts — the chat is PAUSED while non-empty. */
65
+ pendingApprovals: ToolApprovalRequest[];
66
+ /** Live cumulative cost from the wire's `cost` parts (last one wins). */
67
+ cost?: UseChatCost;
68
+ /** Set when the server's budget guardrail tripped this turn. */
69
+ budgetExceeded?: UseChatBudgetExceeded;
70
+ /** This turn's app-defined `data-{name}` parts, in arrival order. */
71
+ dataParts: Array<{
72
+ name: string;
73
+ payload: unknown;
74
+ }>;
75
+ /** This turn's RAG citations. */
76
+ citations: AssistantTurnState['citations'];
77
+ sendMessage: (text: string) => Promise<void>;
78
+ /** Abort the in-flight stream (not an error). */
79
+ stop: () => void;
80
+ /** Drop the trailing assistant/tool turns (core `dropTrailingAssistant`) and re-run. */
81
+ regenerate: () => Promise<void>;
82
+ /** Cut history before `messageId` (core `branchBeforeUserMessage`) and send `text`. */
83
+ editAndResend: (messageId: string, text: string) => Promise<void>;
84
+ /**
85
+ * Record one verdict (the request's signed `token` is auto-preserved). Once
86
+ * EVERY pending approval has a verdict, the chat auto-resumes with
87
+ * `approvalResponses` in the request body.
88
+ */
89
+ addToolApprovalResponse: (response: ToolApprovalResponse) => Promise<void>;
90
+ /**
91
+ * Re-read the stream from `resume.endpoint` via `connectDeuzStream` and fold
92
+ * the parts into the current turn. No-op unless `options.resume` is set.
93
+ */
94
+ reconnect: () => Promise<void>;
95
+ }
96
+ declare function useChat(options: UseChatOptions): UseChatResult;
97
+
98
+ interface UseObjectOptions {
99
+ /** Endpoint serving `toDeuzObjectStreamResponse` output. */
100
+ api: string;
101
+ headers?: Record<string, string>;
102
+ /** Injectable for tests / custom transports. Defaults to global fetch. */
103
+ fetch?: typeof fetch;
104
+ }
105
+ interface UseObjectResult<T> {
106
+ /** Latest partial (each `object-delta` replaces it wholesale). */
107
+ object: DeepPartial<T> | undefined;
108
+ isLoading: boolean;
109
+ error: Error | undefined;
110
+ /** POSTs `{ input }` to `api` and streams partials into `object`. */
111
+ submit: (input: unknown) => Promise<void>;
112
+ /** Abort the in-flight stream (not an error). */
113
+ stop: () => void;
114
+ }
115
+ declare function useObject<T = unknown>(options: UseObjectOptions): UseObjectResult<T>;
116
+
117
+ interface ToolApprovalCardProps {
118
+ approval: ToolApprovalRequest;
119
+ /** Feed this straight into `useChat().addToolApprovalResponse`. */
120
+ onRespond: (response: ToolApprovalResponse) => void;
121
+ /** Override presentation; the component supplies the wired callbacks. */
122
+ render?: (ctx: {
123
+ approval: ToolApprovalRequest;
124
+ approve: () => void;
125
+ deny: (reason?: string) => void;
126
+ }) => ReactNode;
127
+ }
128
+ /**
129
+ * One pending tool approval. The verdict always carries the request's signed
130
+ * `token` (D4) — callers never have to thread it themselves.
131
+ */
132
+ declare function ToolApprovalCard({ approval, onRespond, render }: ToolApprovalCardProps): react.JSX.Element;
133
+ interface CostBadgeProps {
134
+ /** The `useChat` cost state (undefined until the first `cost` part arrives). */
135
+ cost: UseChatCost | undefined;
136
+ /** Override presentation. */
137
+ format?: (cost: UseChatCost) => ReactNode;
138
+ }
139
+ /** Live cost readout: `$X.XXXX`, plus ` (saved $Y.YYYY)` when caching saved money. */
140
+ declare function CostBadge({ cost, format }: CostBadgeProps): react.JSX.Element | null;
141
+
142
+ export { CostBadge, type CostBadgeProps, ToolApprovalCard, type ToolApprovalCardProps, type UseChatBudgetExceeded, type UseChatCost, type UseChatOptions, type UseChatResult, type UseChatResumeOptions, type UseObjectOptions, type UseObjectResult, useChat, useObject };
@@ -0,0 +1,142 @@
1
+ import { Message, ToolApprovalRequest, ToolApprovalResponse, DeepPartial } from '@deuz-sdk/core';
2
+ export { Message, ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';
3
+ import { UIMessage, AssistantTurnState } from '@deuz-sdk/core/chat';
4
+ export { AssistantTurnState, ChatHistory, UIMessage, UIToolCall } from '@deuz-sdk/core/chat';
5
+ import * as react from 'react';
6
+ import { ReactNode } from 'react';
7
+ export { DeuzUIPart } from '@deuz-sdk/core/ui';
8
+
9
+ /** Live cumulative USD cost, from the wire's `cost` parts (last one wins). */
10
+ interface UseChatCost {
11
+ costUsd: number;
12
+ cacheSavingsUsd?: number;
13
+ }
14
+ /** The server's budget guardrail tripped (`budget-exceeded` part). */
15
+ interface UseChatBudgetExceeded {
16
+ kind: 'usd' | 'tokens';
17
+ limit: number;
18
+ value: number;
19
+ }
20
+ interface UseChatResumeOptions {
21
+ /**
22
+ * Resume endpoint (see core's `resumeDeuzStreamResponse`): a GET URL or a
23
+ * factory returning the `Response` for a given cursor. Do NOT point it at
24
+ * the generating POST route — that would re-run the model.
25
+ */
26
+ endpoint: string | ((ctx: {
27
+ lastEventId?: string;
28
+ }) => Response | Promise<Response>);
29
+ /** Cursor to resume from (e.g. persisted across a page reload). */
30
+ lastEventId?: string;
31
+ }
32
+ interface UseChatOptions {
33
+ /** Endpoint serving `toDeuzStreamResponse` output. */
34
+ api: string;
35
+ /** Seed canonical history (e.g. restored from a ChatStore) — rendered via `uiFromMessages`. */
36
+ initialMessages?: Message[];
37
+ headers?: Record<string, string>;
38
+ /** Extra fields merged into every request body. */
39
+ body?: Record<string, unknown>;
40
+ /** Chat identity — merged into every request body (server-side ChatStore persistence). */
41
+ chatId?: string;
42
+ /** Enables `reconnect()` against a resume endpoint (wire v2, `connectDeuzStream`). */
43
+ resume?: UseChatResumeOptions;
44
+ /** Id source for UI messages/turns. Default: `crypto.randomUUID` (with a fallback). */
45
+ generateId?: () => string;
46
+ /**
47
+ * Client-tool executor: called for every streamed tool call the SERVER did
48
+ * not execute. The return value is appended as its tool_result and the chat
49
+ * auto-continues; a throw self-heals as an is_error result.
50
+ */
51
+ onToolCall?: (call: {
52
+ toolCallId: string;
53
+ toolName: string;
54
+ input: unknown;
55
+ }) => Promise<unknown> | unknown;
56
+ onError?: (error: Error) => void;
57
+ /** Injectable for tests / custom transports. Defaults to global fetch. */
58
+ fetch?: typeof fetch;
59
+ }
60
+ interface UseChatResult {
61
+ messages: UIMessage[];
62
+ status: 'idle' | 'streaming' | 'error';
63
+ error: Error | undefined;
64
+ /** Gated tool calls awaiting verdicts — the chat is PAUSED while non-empty. */
65
+ pendingApprovals: ToolApprovalRequest[];
66
+ /** Live cumulative cost from the wire's `cost` parts (last one wins). */
67
+ cost?: UseChatCost;
68
+ /** Set when the server's budget guardrail tripped this turn. */
69
+ budgetExceeded?: UseChatBudgetExceeded;
70
+ /** This turn's app-defined `data-{name}` parts, in arrival order. */
71
+ dataParts: Array<{
72
+ name: string;
73
+ payload: unknown;
74
+ }>;
75
+ /** This turn's RAG citations. */
76
+ citations: AssistantTurnState['citations'];
77
+ sendMessage: (text: string) => Promise<void>;
78
+ /** Abort the in-flight stream (not an error). */
79
+ stop: () => void;
80
+ /** Drop the trailing assistant/tool turns (core `dropTrailingAssistant`) and re-run. */
81
+ regenerate: () => Promise<void>;
82
+ /** Cut history before `messageId` (core `branchBeforeUserMessage`) and send `text`. */
83
+ editAndResend: (messageId: string, text: string) => Promise<void>;
84
+ /**
85
+ * Record one verdict (the request's signed `token` is auto-preserved). Once
86
+ * EVERY pending approval has a verdict, the chat auto-resumes with
87
+ * `approvalResponses` in the request body.
88
+ */
89
+ addToolApprovalResponse: (response: ToolApprovalResponse) => Promise<void>;
90
+ /**
91
+ * Re-read the stream from `resume.endpoint` via `connectDeuzStream` and fold
92
+ * the parts into the current turn. No-op unless `options.resume` is set.
93
+ */
94
+ reconnect: () => Promise<void>;
95
+ }
96
+ declare function useChat(options: UseChatOptions): UseChatResult;
97
+
98
+ interface UseObjectOptions {
99
+ /** Endpoint serving `toDeuzObjectStreamResponse` output. */
100
+ api: string;
101
+ headers?: Record<string, string>;
102
+ /** Injectable for tests / custom transports. Defaults to global fetch. */
103
+ fetch?: typeof fetch;
104
+ }
105
+ interface UseObjectResult<T> {
106
+ /** Latest partial (each `object-delta` replaces it wholesale). */
107
+ object: DeepPartial<T> | undefined;
108
+ isLoading: boolean;
109
+ error: Error | undefined;
110
+ /** POSTs `{ input }` to `api` and streams partials into `object`. */
111
+ submit: (input: unknown) => Promise<void>;
112
+ /** Abort the in-flight stream (not an error). */
113
+ stop: () => void;
114
+ }
115
+ declare function useObject<T = unknown>(options: UseObjectOptions): UseObjectResult<T>;
116
+
117
+ interface ToolApprovalCardProps {
118
+ approval: ToolApprovalRequest;
119
+ /** Feed this straight into `useChat().addToolApprovalResponse`. */
120
+ onRespond: (response: ToolApprovalResponse) => void;
121
+ /** Override presentation; the component supplies the wired callbacks. */
122
+ render?: (ctx: {
123
+ approval: ToolApprovalRequest;
124
+ approve: () => void;
125
+ deny: (reason?: string) => void;
126
+ }) => ReactNode;
127
+ }
128
+ /**
129
+ * One pending tool approval. The verdict always carries the request's signed
130
+ * `token` (D4) — callers never have to thread it themselves.
131
+ */
132
+ declare function ToolApprovalCard({ approval, onRespond, render }: ToolApprovalCardProps): react.JSX.Element;
133
+ interface CostBadgeProps {
134
+ /** The `useChat` cost state (undefined until the first `cost` part arrives). */
135
+ cost: UseChatCost | undefined;
136
+ /** Override presentation. */
137
+ format?: (cost: UseChatCost) => ReactNode;
138
+ }
139
+ /** Live cost readout: `$X.XXXX`, plus ` (saved $Y.YYYY)` when caching saved money. */
140
+ declare function CostBadge({ cost, format }: CostBadgeProps): react.JSX.Element | null;
141
+
142
+ export { CostBadge, type CostBadgeProps, ToolApprovalCard, type ToolApprovalCardProps, type UseChatBudgetExceeded, type UseChatCost, type UseChatOptions, type UseChatResult, type UseChatResumeOptions, type UseObjectOptions, type UseObjectResult, useChat, useObject };
package/dist/index.js ADDED
@@ -0,0 +1,363 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+ import { uiFromMessages, createAssistantTurn, assistantMessageFromTurn, applyUIPart, clientToolResultMessage, dropTrailingAssistant, branchBeforeUserMessage } from '@deuz-sdk/core/chat';
3
+ import { readDeuzStream, connectDeuzStream } from '@deuz-sdk/core/ui';
4
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
+
6
+ // src/use-chat.ts
7
+ function foldPart(turn, part) {
8
+ const next = applyUIPart(turn, part);
9
+ if (part.type === "tool-approval-request" && part.token !== void 0) {
10
+ return {
11
+ ...next,
12
+ approvals: next.approvals.map(
13
+ (a) => a.approvalId === part.approvalId && a.token === void 0 ? { ...a, token: part.token } : a
14
+ )
15
+ };
16
+ }
17
+ return next;
18
+ }
19
+ var defaultGenerateId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `deuz-ui-${Math.random().toString(36).slice(2)}`;
20
+ function useChat(options) {
21
+ const genId = options.generateId ?? defaultGenerateId;
22
+ const [messages, setMessages] = useState(
23
+ () => uiFromMessages(options.initialMessages ?? [], genId)
24
+ );
25
+ const [status, setStatus] = useState("idle");
26
+ const [error, setError] = useState(void 0);
27
+ const [pendingApprovals, setPendingApprovals] = useState([]);
28
+ const [cost, setCost] = useState(void 0);
29
+ const [budgetExceeded, setBudgetExceeded] = useState(
30
+ void 0
31
+ );
32
+ const [dataParts, setDataParts] = useState([]);
33
+ const [citations, setCitations] = useState([]);
34
+ const uiRef = useRef(messages);
35
+ const canonicalRef = useRef(
36
+ options.initialMessages ? [...options.initialMessages] : []
37
+ );
38
+ const turnRef = useRef(void 0);
39
+ const approvalsRef = useRef([]);
40
+ const verdictsRef = useRef([]);
41
+ const abortRef = useRef(null);
42
+ const lastEventIdRef = useRef(options.resume?.lastEventId);
43
+ const pushMessage = useCallback((message) => {
44
+ uiRef.current = [...uiRef.current, message];
45
+ setMessages(uiRef.current);
46
+ }, []);
47
+ const syncTurn = useCallback((turn) => {
48
+ uiRef.current = [...uiRef.current.slice(0, -1), turn.message];
49
+ setMessages(uiRef.current);
50
+ if (turn.costUsd !== void 0) {
51
+ setCost({
52
+ costUsd: turn.costUsd,
53
+ ...turn.cacheSavingsUsd !== void 0 ? { cacheSavingsUsd: turn.cacheSavingsUsd } : {}
54
+ });
55
+ }
56
+ setBudgetExceeded(turn.budgetExceeded);
57
+ setDataParts(turn.dataParts);
58
+ setCitations(turn.citations);
59
+ }, []);
60
+ const stop = useCallback(() => {
61
+ abortRef.current?.abort();
62
+ }, []);
63
+ const run = useCallback(
64
+ async (approvalResponses) => {
65
+ const controller = new AbortController();
66
+ abortRef.current = controller;
67
+ setStatus("streaming");
68
+ setError(void 0);
69
+ try {
70
+ for (; ; ) {
71
+ const doFetch = options.fetch ?? fetch;
72
+ const res = await doFetch(options.api, {
73
+ method: "POST",
74
+ headers: { "content-type": "application/json", ...options.headers },
75
+ body: JSON.stringify({
76
+ messages: canonicalRef.current,
77
+ ...options.chatId !== void 0 ? { chatId: options.chatId } : {},
78
+ ...approvalResponses?.length ? { approvalResponses } : {},
79
+ ...options.body
80
+ }),
81
+ signal: controller.signal
82
+ });
83
+ approvalResponses = void 0;
84
+ let turn = createAssistantTurn(genId());
85
+ turnRef.current = turn;
86
+ pushMessage(turn.message);
87
+ syncTurn(turn);
88
+ for await (const part of readDeuzStream(res)) {
89
+ turn = foldPart(turn, part);
90
+ turnRef.current = turn;
91
+ syncTurn(turn);
92
+ }
93
+ if (turn.error !== void 0) throw new Error(turn.error);
94
+ canonicalRef.current = [...canonicalRef.current, assistantMessageFromTurn(turn)];
95
+ turnRef.current = void 0;
96
+ if (turn.approvals.length > 0) {
97
+ approvalsRef.current = turn.approvals;
98
+ verdictsRef.current = [];
99
+ setPendingApprovals(turn.approvals);
100
+ return;
101
+ }
102
+ const toolCalls = turn.message.toolCalls ?? [];
103
+ const serverResults = new Set(turn.serverResults);
104
+ const clientPending = toolCalls.filter((t) => !serverResults.has(t.toolCallId));
105
+ if (clientPending.length === 0 || !options.onToolCall) return;
106
+ const results = [];
107
+ for (const call of clientPending) {
108
+ try {
109
+ const out = await options.onToolCall({
110
+ toolCallId: call.toolCallId,
111
+ toolName: call.toolName,
112
+ input: call.input
113
+ });
114
+ results.push({ toolCallId: call.toolCallId, result: out });
115
+ turn = applyUIPart(turn, {
116
+ type: "tool-result",
117
+ toolCallId: call.toolCallId,
118
+ toolName: call.toolName,
119
+ output: out
120
+ });
121
+ } catch (err) {
122
+ const message = err instanceof Error ? err.message : String(err);
123
+ results.push({ toolCallId: call.toolCallId, result: message, isError: true });
124
+ turn = applyUIPart(turn, {
125
+ type: "tool-result",
126
+ toolCallId: call.toolCallId,
127
+ toolName: call.toolName,
128
+ output: message,
129
+ isError: true
130
+ });
131
+ }
132
+ syncTurn(turn);
133
+ }
134
+ canonicalRef.current = [...canonicalRef.current, clientToolResultMessage(results)];
135
+ }
136
+ } catch (err) {
137
+ if (controller.signal.aborted) return;
138
+ const e = err instanceof Error ? err : new Error(String(err));
139
+ setError(e);
140
+ setStatus("error");
141
+ options.onError?.(e);
142
+ return;
143
+ } finally {
144
+ setStatus((s) => s === "error" ? s : "idle");
145
+ }
146
+ },
147
+ [
148
+ options.api,
149
+ options.fetch,
150
+ options.headers,
151
+ options.body,
152
+ options.chatId,
153
+ options.generateId,
154
+ options.onToolCall,
155
+ options.onError,
156
+ pushMessage,
157
+ syncTurn
158
+ ]
159
+ );
160
+ const sendMessage = useCallback(
161
+ async (text) => {
162
+ canonicalRef.current = [...canonicalRef.current, { role: "user", content: text }];
163
+ pushMessage({ id: genId(), role: "user", content: text });
164
+ approvalsRef.current = [];
165
+ verdictsRef.current = [];
166
+ setPendingApprovals([]);
167
+ await run();
168
+ },
169
+ [run, pushMessage, options.generateId]
170
+ );
171
+ const regenerate = useCallback(async () => {
172
+ const cut = dropTrailingAssistant({ ui: uiRef.current, canonical: canonicalRef.current });
173
+ uiRef.current = cut.ui;
174
+ canonicalRef.current = cut.canonical;
175
+ setMessages(cut.ui);
176
+ approvalsRef.current = [];
177
+ verdictsRef.current = [];
178
+ setPendingApprovals([]);
179
+ await run();
180
+ }, [run]);
181
+ const editAndResend = useCallback(
182
+ async (messageId, text) => {
183
+ const cut = branchBeforeUserMessage(
184
+ { ui: uiRef.current, canonical: canonicalRef.current },
185
+ messageId
186
+ );
187
+ if (!cut) return;
188
+ uiRef.current = cut.ui;
189
+ canonicalRef.current = cut.canonical;
190
+ setMessages(cut.ui);
191
+ approvalsRef.current = [];
192
+ verdictsRef.current = [];
193
+ setPendingApprovals([]);
194
+ await sendMessage(text);
195
+ },
196
+ [sendMessage]
197
+ );
198
+ const addToolApprovalResponse = useCallback(
199
+ async (response) => {
200
+ const request = approvalsRef.current.find((p) => p.approvalId === response.approvalId);
201
+ const verdict = response.token === void 0 && request?.token !== void 0 ? { ...response, token: request.token } : response;
202
+ verdictsRef.current = [
203
+ ...verdictsRef.current.filter((v) => v.approvalId !== verdict.approvalId),
204
+ verdict
205
+ ];
206
+ const verdicts = verdictsRef.current;
207
+ const allSettled = approvalsRef.current.every(
208
+ (p) => verdicts.some((v) => v.approvalId === p.approvalId)
209
+ );
210
+ if (!allSettled) return;
211
+ approvalsRef.current = [];
212
+ verdictsRef.current = [];
213
+ setPendingApprovals([]);
214
+ await run(verdicts);
215
+ },
216
+ [run]
217
+ );
218
+ const reconnect = useCallback(async () => {
219
+ const resume = options.resume;
220
+ if (!resume) return;
221
+ const controller = new AbortController();
222
+ abortRef.current = controller;
223
+ setStatus("streaming");
224
+ setError(void 0);
225
+ try {
226
+ const cursor = lastEventIdRef.current;
227
+ let turn;
228
+ if (turnRef.current && cursor !== void 0) {
229
+ turn = turnRef.current;
230
+ } else {
231
+ turn = createAssistantTurn(genId());
232
+ if (!turnRef.current) pushMessage(turn.message);
233
+ }
234
+ turnRef.current = turn;
235
+ const parts = connectDeuzStream(resume.endpoint, {
236
+ ...cursor !== void 0 ? { lastEventId: cursor } : {},
237
+ onCursor: (id) => {
238
+ lastEventIdRef.current = id;
239
+ },
240
+ signal: controller.signal,
241
+ ...options.fetch ? { fetch: options.fetch } : {},
242
+ ...options.headers ? { headers: options.headers } : {}
243
+ });
244
+ for await (const part of parts) {
245
+ turn = foldPart(turn, part);
246
+ turnRef.current = turn;
247
+ syncTurn(turn);
248
+ }
249
+ if (turn.error !== void 0) throw new Error(turn.error);
250
+ canonicalRef.current = [...canonicalRef.current, assistantMessageFromTurn(turn)];
251
+ turnRef.current = void 0;
252
+ if (turn.approvals.length > 0) {
253
+ approvalsRef.current = turn.approvals;
254
+ verdictsRef.current = [];
255
+ setPendingApprovals(turn.approvals);
256
+ }
257
+ } catch (err) {
258
+ if (controller.signal.aborted) return;
259
+ const e = err instanceof Error ? err : new Error(String(err));
260
+ setError(e);
261
+ setStatus("error");
262
+ options.onError?.(e);
263
+ return;
264
+ } finally {
265
+ setStatus((s) => s === "error" ? s : "idle");
266
+ }
267
+ }, [
268
+ options.resume,
269
+ options.fetch,
270
+ options.headers,
271
+ options.generateId,
272
+ options.onError,
273
+ pushMessage,
274
+ syncTurn
275
+ ]);
276
+ return {
277
+ messages,
278
+ status,
279
+ error,
280
+ pendingApprovals,
281
+ ...cost !== void 0 ? { cost } : {},
282
+ ...budgetExceeded !== void 0 ? { budgetExceeded } : {},
283
+ dataParts,
284
+ citations,
285
+ sendMessage,
286
+ stop,
287
+ regenerate,
288
+ editAndResend,
289
+ addToolApprovalResponse,
290
+ reconnect
291
+ };
292
+ }
293
+ function useObject(options) {
294
+ const [object, setObject] = useState(void 0);
295
+ const [isLoading, setLoading] = useState(false);
296
+ const [error, setError] = useState(void 0);
297
+ const abortRef = useRef(null);
298
+ const stop = useCallback(() => {
299
+ abortRef.current?.abort();
300
+ }, []);
301
+ const submit = useCallback(
302
+ async (input) => {
303
+ abortRef.current?.abort();
304
+ const controller = new AbortController();
305
+ abortRef.current = controller;
306
+ setLoading(true);
307
+ setError(void 0);
308
+ setObject(void 0);
309
+ try {
310
+ const doFetch = options.fetch ?? fetch;
311
+ const res = await doFetch(options.api, {
312
+ method: "POST",
313
+ headers: { "content-type": "application/json", ...options.headers },
314
+ body: JSON.stringify({ input }),
315
+ signal: controller.signal
316
+ });
317
+ for await (const part of readDeuzStream(res)) {
318
+ if (part.type === "object-delta") {
319
+ setObject(part.object);
320
+ } else if (part.type === "error") {
321
+ setError(new Error(part.message));
322
+ break;
323
+ }
324
+ }
325
+ } catch (err) {
326
+ if (!controller.signal.aborted) {
327
+ setError(err instanceof Error ? err : new Error(String(err)));
328
+ }
329
+ } finally {
330
+ setLoading(false);
331
+ }
332
+ },
333
+ [options.api, options.fetch, options.headers]
334
+ );
335
+ return { object, isLoading, error, submit, stop };
336
+ }
337
+ function ToolApprovalCard({ approval, onRespond, render }) {
338
+ const respond = (approved, reason) => onRespond({
339
+ approvalId: approval.approvalId,
340
+ approved,
341
+ ...reason !== void 0 ? { reason } : {},
342
+ ...approval.token !== void 0 ? { token: approval.token } : {}
343
+ });
344
+ const approve = () => respond(true);
345
+ const deny = (reason) => respond(false, reason);
346
+ if (render) return /* @__PURE__ */ jsx(Fragment, { children: render({ approval, approve, deny }) });
347
+ return /* @__PURE__ */ jsxs("div", { "data-deuz": "tool-approval", children: [
348
+ /* @__PURE__ */ jsx("span", { "data-deuz": "tool-approval-name", children: approval.toolName }),
349
+ /* @__PURE__ */ jsx("pre", { "data-deuz": "tool-approval-input", children: JSON.stringify(approval.input, null, 2) }),
350
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: approve, children: "Approve" }),
351
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => deny(), children: "Deny" })
352
+ ] });
353
+ }
354
+ function CostBadge({ cost, format }) {
355
+ if (cost === void 0) return null;
356
+ if (format) return /* @__PURE__ */ jsx(Fragment, { children: format(cost) });
357
+ const saved = cost.cacheSavingsUsd !== void 0 && cost.cacheSavingsUsd > 0 ? ` (saved $${cost.cacheSavingsUsd.toFixed(4)})` : "";
358
+ return /* @__PURE__ */ jsx("span", { "data-deuz": "cost-badge", children: `$${cost.costUsd.toFixed(4)}${saved}` });
359
+ }
360
+
361
+ export { CostBadge, ToolApprovalCard, useChat, useObject };
362
+ //# sourceMappingURL=index.js.map
363
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/use-chat.ts","../src/use-object.ts","../src/components.tsx"],"names":["useState","useRef","useCallback","readDeuzStream"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * useChat — the React binding over `@deuz-sdk/core/chat` + the Deuz UI wire.\n *\n * THIN by contract: every chat-state transformation is a core call\n * (`createAssistantTurn`/`applyUIPart`/`assistantMessageFromTurn`/\n * `clientToolResultMessage`/`uiFromMessages`/`dropTrailingAssistant`/\n * `branchBeforeUserMessage`). This hook only owns React state, the fetch\n * round-trips, and abort wiring. Supersedes the frozen `@deuz-sdk/core/react`.\n */\nimport { useCallback, useRef, useState } from 'react';\nimport type { Message, ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';\nimport {\n applyUIPart,\n assistantMessageFromTurn,\n branchBeforeUserMessage,\n clientToolResultMessage,\n createAssistantTurn,\n dropTrailingAssistant,\n uiFromMessages,\n} from '@deuz-sdk/core/chat';\nimport type { AssistantTurnState, UIMessage } from '@deuz-sdk/core/chat';\nimport { connectDeuzStream, readDeuzStream } from '@deuz-sdk/core/ui';\n\n/**\n * Fold one wire part via the core reducer. Trivial glue on top: core 1.7.0's\n * `applyUIPart` drops the `token` field of a `tool-approval-request` when it\n * builds the approval entry — re-attach it so verdicts can echo it (D4).\n */\nfunction foldPart(\n turn: AssistantTurnState,\n part: Parameters<typeof applyUIPart>[1],\n): AssistantTurnState {\n const next = applyUIPart(turn, part);\n if (part.type === 'tool-approval-request' && part.token !== undefined) {\n return {\n ...next,\n approvals: next.approvals.map((a) =>\n a.approvalId === part.approvalId && a.token === undefined ? { ...a, token: part.token } : a,\n ),\n };\n }\n return next;\n}\n\n/** Local id fallback — this package is not edge-lint-constrained. */\nconst defaultGenerateId = (): string =>\n typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : `deuz-ui-${Math.random().toString(36).slice(2)}`;\n\n/** Live cumulative USD cost, from the wire's `cost` parts (last one wins). */\nexport interface UseChatCost {\n costUsd: number;\n cacheSavingsUsd?: number;\n}\n\n/** The server's budget guardrail tripped (`budget-exceeded` part). */\nexport interface UseChatBudgetExceeded {\n kind: 'usd' | 'tokens';\n limit: number;\n value: number;\n}\n\nexport interface UseChatResumeOptions {\n /**\n * Resume endpoint (see core's `resumeDeuzStreamResponse`): a GET URL or a\n * factory returning the `Response` for a given cursor. Do NOT point it at\n * the generating POST route — that would re-run the model.\n */\n endpoint: string | ((ctx: { lastEventId?: string }) => Response | Promise<Response>);\n /** Cursor to resume from (e.g. persisted across a page reload). */\n lastEventId?: string;\n}\n\nexport interface UseChatOptions {\n /** Endpoint serving `toDeuzStreamResponse` output. */\n api: string;\n /** Seed canonical history (e.g. restored from a ChatStore) — rendered via `uiFromMessages`. */\n initialMessages?: Message[];\n headers?: Record<string, string>;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Chat identity — merged into every request body (server-side ChatStore persistence). */\n chatId?: string;\n /** Enables `reconnect()` against a resume endpoint (wire v2, `connectDeuzStream`). */\n resume?: UseChatResumeOptions;\n /** Id source for UI messages/turns. Default: `crypto.randomUUID` (with a fallback). */\n generateId?: () => string;\n /**\n * Client-tool executor: called for every streamed tool call the SERVER did\n * not execute. The return value is appended as its tool_result and the chat\n * auto-continues; a throw self-heals as an is_error result.\n */\n onToolCall?: (call: {\n toolCallId: string;\n toolName: string;\n input: unknown;\n }) => Promise<unknown> | unknown;\n onError?: (error: Error) => void;\n /** Injectable for tests / custom transports. Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport interface UseChatResult {\n messages: UIMessage[];\n status: 'idle' | 'streaming' | 'error';\n error: Error | undefined;\n /** Gated tool calls awaiting verdicts — the chat is PAUSED while non-empty. */\n pendingApprovals: ToolApprovalRequest[];\n /** Live cumulative cost from the wire's `cost` parts (last one wins). */\n cost?: UseChatCost;\n /** Set when the server's budget guardrail tripped this turn. */\n budgetExceeded?: UseChatBudgetExceeded;\n /** This turn's app-defined `data-{name}` parts, in arrival order. */\n dataParts: Array<{ name: string; payload: unknown }>;\n /** This turn's RAG citations. */\n citations: AssistantTurnState['citations'];\n sendMessage: (text: string) => Promise<void>;\n /** Abort the in-flight stream (not an error). */\n stop: () => void;\n /** Drop the trailing assistant/tool turns (core `dropTrailingAssistant`) and re-run. */\n regenerate: () => Promise<void>;\n /** Cut history before `messageId` (core `branchBeforeUserMessage`) and send `text`. */\n editAndResend: (messageId: string, text: string) => Promise<void>;\n /**\n * Record one verdict (the request's signed `token` is auto-preserved). Once\n * EVERY pending approval has a verdict, the chat auto-resumes with\n * `approvalResponses` in the request body.\n */\n addToolApprovalResponse: (response: ToolApprovalResponse) => Promise<void>;\n /**\n * Re-read the stream from `resume.endpoint` via `connectDeuzStream` and fold\n * the parts into the current turn. No-op unless `options.resume` is set.\n */\n reconnect: () => Promise<void>;\n}\n\nexport function useChat(options: UseChatOptions): UseChatResult {\n const genId = options.generateId ?? defaultGenerateId;\n const [messages, setMessages] = useState<UIMessage[]>(() =>\n uiFromMessages(options.initialMessages ?? [], genId),\n );\n const [status, setStatus] = useState<'idle' | 'streaming' | 'error'>('idle');\n const [error, setError] = useState<Error | undefined>(undefined);\n const [pendingApprovals, setPendingApprovals] = useState<ToolApprovalRequest[]>([]);\n const [cost, setCost] = useState<UseChatCost | undefined>(undefined);\n const [budgetExceeded, setBudgetExceeded] = useState<UseChatBudgetExceeded | undefined>(\n undefined,\n );\n const [dataParts, setDataParts] = useState<Array<{ name: string; payload: unknown }>>([]);\n const [citations, setCitations] = useState<AssistantTurnState['citations']>([]);\n\n const uiRef = useRef<UIMessage[]>(messages);\n const canonicalRef = useRef<Message[]>(\n options.initialMessages ? [...options.initialMessages] : [],\n );\n /** The in-flight turn — kept across a drop so `reconnect()` can continue it. */\n const turnRef = useRef<AssistantTurnState | undefined>(undefined);\n const approvalsRef = useRef<ToolApprovalRequest[]>([]);\n const verdictsRef = useRef<ToolApprovalResponse[]>([]);\n const abortRef = useRef<AbortController | null>(null);\n const lastEventIdRef = useRef<string | undefined>(options.resume?.lastEventId);\n\n const pushMessage = useCallback((message: UIMessage): void => {\n uiRef.current = [...uiRef.current, message];\n setMessages(uiRef.current);\n }, []);\n\n /** Sync ALL turn-derived React state (the turn's message is the trailing UI element). */\n const syncTurn = useCallback((turn: AssistantTurnState): void => {\n uiRef.current = [...uiRef.current.slice(0, -1), turn.message];\n setMessages(uiRef.current);\n if (turn.costUsd !== undefined) {\n setCost({\n costUsd: turn.costUsd,\n ...(turn.cacheSavingsUsd !== undefined ? { cacheSavingsUsd: turn.cacheSavingsUsd } : {}),\n });\n }\n setBudgetExceeded(turn.budgetExceeded);\n setDataParts(turn.dataParts);\n setCitations(turn.citations);\n }, []);\n\n const stop = useCallback((): void => {\n abortRef.current?.abort();\n }, []);\n\n const run = useCallback(\n async (approvalResponses?: ToolApprovalResponse[]): Promise<void> => {\n const controller = new AbortController();\n abortRef.current = controller;\n setStatus('streaming');\n setError(undefined);\n try {\n // One iteration per model round; client-tool results loop back in.\n for (;;) {\n const doFetch = options.fetch ?? fetch;\n const res = await doFetch(options.api, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...options.headers },\n body: JSON.stringify({\n messages: canonicalRef.current,\n ...(options.chatId !== undefined ? { chatId: options.chatId } : {}),\n ...(approvalResponses?.length ? { approvalResponses } : {}),\n ...options.body,\n }),\n signal: controller.signal,\n });\n approvalResponses = undefined; // consumed by the first round only\n\n let turn = createAssistantTurn(genId());\n turnRef.current = turn;\n pushMessage(turn.message);\n syncTurn(turn); // resets this-turn state (dataParts/citations/budget)\n\n for await (const part of readDeuzStream(res)) {\n turn = foldPart(turn, part);\n turnRef.current = turn;\n syncTurn(turn);\n }\n if (turn.error !== undefined) throw new Error(turn.error);\n\n // Append the canonical assistant turn (client-tools reconstruction).\n canonicalRef.current = [...canonicalRef.current, assistantMessageFromTurn(turn)];\n turnRef.current = undefined;\n\n // Approval pause: verdicts arrive via addToolApprovalResponse.\n if (turn.approvals.length > 0) {\n approvalsRef.current = turn.approvals;\n verdictsRef.current = [];\n setPendingApprovals(turn.approvals);\n return;\n }\n\n // Client-tool auto-round-trip: everything the server didn't execute.\n const toolCalls = turn.message.toolCalls ?? [];\n const serverResults = new Set(turn.serverResults);\n const clientPending = toolCalls.filter((t) => !serverResults.has(t.toolCallId));\n if (clientPending.length === 0 || !options.onToolCall) return;\n const results: Array<{ toolCallId: string; result: unknown; isError?: boolean }> = [];\n for (const call of clientPending) {\n try {\n const out = await options.onToolCall({\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n input: call.input,\n });\n results.push({ toolCallId: call.toolCallId, result: out });\n turn = applyUIPart(turn, {\n type: 'tool-result',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n output: out,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n results.push({ toolCallId: call.toolCallId, result: message, isError: true });\n turn = applyUIPart(turn, {\n type: 'tool-result',\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n output: message,\n isError: true,\n });\n }\n syncTurn(turn);\n }\n canonicalRef.current = [...canonicalRef.current, clientToolResultMessage(results)];\n // loop → next round POSTs the extended history\n }\n } catch (err) {\n if (controller.signal.aborted) return; // user abort — not an error\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setStatus('error');\n options.onError?.(e);\n return;\n } finally {\n setStatus((s) => (s === 'error' ? s : 'idle'));\n }\n },\n [\n options.api,\n options.fetch,\n options.headers,\n options.body,\n options.chatId,\n options.generateId,\n options.onToolCall,\n options.onError,\n pushMessage,\n syncTurn,\n ],\n );\n\n const sendMessage = useCallback(\n async (text: string): Promise<void> => {\n canonicalRef.current = [...canonicalRef.current, { role: 'user', content: text }];\n pushMessage({ id: genId(), role: 'user', content: text });\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await run();\n },\n [run, pushMessage, options.generateId],\n );\n\n const regenerate = useCallback(async (): Promise<void> => {\n const cut = dropTrailingAssistant({ ui: uiRef.current, canonical: canonicalRef.current });\n uiRef.current = cut.ui;\n canonicalRef.current = cut.canonical;\n setMessages(cut.ui);\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await run();\n }, [run]);\n\n const editAndResend = useCallback(\n async (messageId: string, text: string): Promise<void> => {\n const cut = branchBeforeUserMessage(\n { ui: uiRef.current, canonical: canonicalRef.current },\n messageId,\n );\n if (!cut) return; // not a user message — nothing to branch\n uiRef.current = cut.ui;\n canonicalRef.current = cut.canonical;\n setMessages(cut.ui);\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await sendMessage(text);\n },\n [sendMessage],\n );\n\n const addToolApprovalResponse = useCallback(\n async (response: ToolApprovalResponse): Promise<void> => {\n // Preserve the request's signed token unless the caller set one.\n const request = approvalsRef.current.find((p) => p.approvalId === response.approvalId);\n const verdict =\n response.token === undefined && request?.token !== undefined\n ? { ...response, token: request.token }\n : response;\n verdictsRef.current = [\n ...verdictsRef.current.filter((v) => v.approvalId !== verdict.approvalId),\n verdict,\n ];\n const verdicts = verdictsRef.current;\n const allSettled = approvalsRef.current.every((p) =>\n verdicts.some((v) => v.approvalId === p.approvalId),\n );\n if (!allSettled) return;\n approvalsRef.current = [];\n verdictsRef.current = [];\n setPendingApprovals([]);\n await run(verdicts);\n },\n [run],\n );\n\n const reconnect = useCallback(async (): Promise<void> => {\n const resume = options.resume;\n if (!resume) return;\n const controller = new AbortController();\n abortRef.current = controller;\n setStatus('streaming');\n setError(undefined);\n try {\n const cursor = lastEventIdRef.current;\n let turn: AssistantTurnState;\n if (turnRef.current && cursor !== undefined) {\n turn = turnRef.current; // continue in place — replay dedupes by seq upstream\n } else {\n // No cursor → full replay rebuilds the turn from the start; it lands in\n // the trailing slot (replacing a partial turn if one is on screen).\n turn = createAssistantTurn(genId());\n if (!turnRef.current) pushMessage(turn.message);\n }\n turnRef.current = turn;\n const parts = connectDeuzStream(resume.endpoint, {\n ...(cursor !== undefined ? { lastEventId: cursor } : {}),\n onCursor: (id) => {\n lastEventIdRef.current = id;\n },\n signal: controller.signal,\n ...(options.fetch ? { fetch: options.fetch } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n });\n for await (const part of parts) {\n turn = foldPart(turn, part);\n turnRef.current = turn;\n syncTurn(turn);\n }\n if (turn.error !== undefined) throw new Error(turn.error);\n canonicalRef.current = [...canonicalRef.current, assistantMessageFromTurn(turn)];\n turnRef.current = undefined;\n if (turn.approvals.length > 0) {\n approvalsRef.current = turn.approvals;\n verdictsRef.current = [];\n setPendingApprovals(turn.approvals);\n }\n } catch (err) {\n if (controller.signal.aborted) return; // user abort — not an error\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setStatus('error');\n options.onError?.(e);\n return;\n } finally {\n setStatus((s) => (s === 'error' ? s : 'idle'));\n }\n }, [\n options.resume,\n options.fetch,\n options.headers,\n options.generateId,\n options.onError,\n pushMessage,\n syncTurn,\n ]);\n\n return {\n messages,\n status,\n error,\n pendingApprovals,\n ...(cost !== undefined ? { cost } : {}),\n ...(budgetExceeded !== undefined ? { budgetExceeded } : {}),\n dataParts,\n citations,\n sendMessage,\n stop,\n regenerate,\n editAndResend,\n addToolApprovalResponse,\n reconnect,\n };\n}\n","/**\n * useObject — streams `toDeuzObjectStreamResponse` output (`object-delta`\n * parts) into React state. Ported from the frozen `@deuz-sdk/core/react` hook.\n */\nimport { useCallback, useRef, useState } from 'react';\nimport type { DeepPartial } from '@deuz-sdk/core';\nimport { readDeuzStream } from '@deuz-sdk/core/ui';\n\nexport interface UseObjectOptions {\n /** Endpoint serving `toDeuzObjectStreamResponse` output. */\n api: string;\n headers?: Record<string, string>;\n /** Injectable for tests / custom transports. Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nexport interface UseObjectResult<T> {\n /** Latest partial (each `object-delta` replaces it wholesale). */\n object: DeepPartial<T> | undefined;\n isLoading: boolean;\n error: Error | undefined;\n /** POSTs `{ input }` to `api` and streams partials into `object`. */\n submit: (input: unknown) => Promise<void>;\n /** Abort the in-flight stream (not an error). */\n stop: () => void;\n}\n\nexport function useObject<T = unknown>(options: UseObjectOptions): UseObjectResult<T> {\n const [object, setObject] = useState<DeepPartial<T> | undefined>(undefined);\n const [isLoading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n const abortRef = useRef<AbortController | null>(null);\n\n const stop = useCallback((): void => {\n abortRef.current?.abort();\n }, []);\n\n const submit = useCallback(\n async (input: unknown): Promise<void> => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n setLoading(true);\n setError(undefined);\n setObject(undefined);\n try {\n const doFetch = options.fetch ?? fetch;\n const res = await doFetch(options.api, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...options.headers },\n body: JSON.stringify({ input }),\n signal: controller.signal,\n });\n for await (const part of readDeuzStream(res)) {\n if (part.type === 'object-delta') {\n setObject(part.object as DeepPartial<T>);\n } else if (part.type === 'error') {\n setError(new Error(part.message));\n break;\n }\n }\n } catch (err) {\n if (!controller.signal.aborted) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n setLoading(false);\n }\n },\n [options.api, options.fetch, options.headers],\n );\n\n return { object, isLoading, error, submit, stop };\n}\n","/**\n * Headless chat components — zero styling, zero business logic. They only\n * wire core types (`ToolApprovalRequest`/`ToolApprovalResponse`, the useChat\n * cost state) to DOM events; presentation is overridable via render props.\n */\nimport type { ReactNode } from 'react';\nimport type { ToolApprovalRequest, ToolApprovalResponse } from '@deuz-sdk/core';\nimport type { UseChatCost } from './use-chat';\n\nexport interface ToolApprovalCardProps {\n approval: ToolApprovalRequest;\n /** Feed this straight into `useChat().addToolApprovalResponse`. */\n onRespond: (response: ToolApprovalResponse) => void;\n /** Override presentation; the component supplies the wired callbacks. */\n render?: (ctx: {\n approval: ToolApprovalRequest;\n approve: () => void;\n deny: (reason?: string) => void;\n }) => ReactNode;\n}\n\n/**\n * One pending tool approval. The verdict always carries the request's signed\n * `token` (D4) — callers never have to thread it themselves.\n */\nexport function ToolApprovalCard({ approval, onRespond, render }: ToolApprovalCardProps) {\n const respond = (approved: boolean, reason?: string): void =>\n onRespond({\n approvalId: approval.approvalId,\n approved,\n ...(reason !== undefined ? { reason } : {}),\n ...(approval.token !== undefined ? { token: approval.token } : {}),\n });\n const approve = (): void => respond(true);\n const deny = (reason?: string): void => respond(false, reason);\n\n if (render) return <>{render({ approval, approve, deny })}</>;\n return (\n <div data-deuz=\"tool-approval\">\n <span data-deuz=\"tool-approval-name\">{approval.toolName}</span>\n <pre data-deuz=\"tool-approval-input\">{JSON.stringify(approval.input, null, 2)}</pre>\n <button type=\"button\" onClick={approve}>\n Approve\n </button>\n <button type=\"button\" onClick={() => deny()}>\n Deny\n </button>\n </div>\n );\n}\n\nexport interface CostBadgeProps {\n /** The `useChat` cost state (undefined until the first `cost` part arrives). */\n cost: UseChatCost | undefined;\n /** Override presentation. */\n format?: (cost: UseChatCost) => ReactNode;\n}\n\n/** Live cost readout: `$X.XXXX`, plus ` (saved $Y.YYYY)` when caching saved money. */\nexport function CostBadge({ cost, format }: CostBadgeProps) {\n if (cost === undefined) return null;\n if (format) return <>{format(cost)}</>;\n const saved =\n cost.cacheSavingsUsd !== undefined && cost.cacheSavingsUsd > 0\n ? ` (saved $${cost.cacheSavingsUsd.toFixed(4)})`\n : '';\n return <span data-deuz=\"cost-badge\">{`$${cost.costUsd.toFixed(4)}${saved}`}</span>;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@deuz-sdk/react",
3
+ "version": "1.7.0",
4
+ "description": "React bindings for the Deuz SDK — useChat/useObject hooks and headless chat components.",
5
+ "license": "MIT",
6
+ "author": "Umutcan Edizaslan (U-C4N)",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Deuz-AI/Deuz-SDK.git",
10
+ "directory": "packages/react"
11
+ },
12
+ "homepage": "https://github.com/Deuz-AI/Deuz-SDK#readme",
13
+ "bugs": "https://github.com/Deuz-AI/Deuz-SDK/issues",
14
+ "type": "module",
15
+ "sideEffects": false,
16
+ "engines": {
17
+ "node": ">=22.0.0"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "main": "./dist/index.cjs",
26
+ "module": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "require": {
35
+ "types": "./dist/index.d.cts",
36
+ "default": "./dist/index.cjs"
37
+ }
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "test": "vitest run",
44
+ "lint": "eslint .",
45
+ "typecheck": "tsc --noEmit",
46
+ "verify:package": "publint --strict && attw --pack .",
47
+ "prepublishOnly": "npm run build"
48
+ },
49
+ "keywords": [
50
+ "ai",
51
+ "react",
52
+ "hooks",
53
+ "chat",
54
+ "useChat",
55
+ "streaming",
56
+ "deuz"
57
+ ],
58
+ "peerDependencies": {
59
+ "@deuz-sdk/core": "^1.7.0",
60
+ "react": "^18.0.0 || ^19.0.0"
61
+ },
62
+ "devDependencies": {
63
+ "@arethetypeswrong/cli": "^0.18.0",
64
+ "@deuz-sdk/core": "^1.7.0",
65
+ "@testing-library/react": "^16.3.2",
66
+ "@types/react": "^19.2.17",
67
+ "eslint": "^9.18.0",
68
+ "eslint-config-prettier": "^9.1.0",
69
+ "jsdom": "^29.1.1",
70
+ "publint": "^0.3.0",
71
+ "react": "^19.2.7",
72
+ "react-dom": "^19.2.7",
73
+ "tsup": "^8.5.0",
74
+ "typescript": "^5.7.0",
75
+ "typescript-eslint": "^8.20.0",
76
+ "vitest": "^4.0.0"
77
+ }
78
+ }