@cogitator-ai/next 0.5.0 → 0.6.1
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 +102 -3
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/use-agent.d.ts.map +1 -1
- package/dist/client/use-agent.js +25 -5
- package/dist/client/use-agent.js.map +1 -1
- package/dist/client/use-chat-state.d.ts +9 -1
- package/dist/client/use-chat-state.d.ts.map +1 -1
- package/dist/client/use-chat-state.js +11 -0
- package/dist/client/use-chat-state.js.map +1 -1
- package/dist/client/use-chat.d.ts.map +1 -1
- package/dist/client/use-chat.js +96 -17
- package/dist/client/use-chat.js.map +1 -1
- package/dist/handlers/agent.d.ts.map +1 -1
- package/dist/handlers/agent.js +2 -15
- package/dist/handlers/agent.js.map +1 -1
- package/dist/handlers/chat.d.ts.map +1 -1
- package/dist/handlers/chat.js +14 -109
- package/dist/handlers/chat.js.map +1 -1
- package/dist/handlers/http.d.ts +11 -4
- package/dist/handlers/http.d.ts.map +1 -1
- package/dist/handlers/http.js +13 -9
- package/dist/handlers/http.js.map +1 -1
- package/dist/handlers/result.d.ts +7 -0
- package/dist/handlers/result.d.ts.map +1 -0
- package/dist/handlers/result.js +42 -0
- package/dist/handlers/result.js.map +1 -0
- package/dist/handlers/resume.d.ts +23 -0
- package/dist/handlers/resume.d.ts.map +1 -0
- package/dist/handlers/resume.js +131 -0
- package/dist/handlers/resume.js.map +1 -0
- package/dist/handlers/stream-run.d.ts +18 -0
- package/dist/handlers/stream-run.d.ts.map +1 -0
- package/dist/handlers/stream-run.js +139 -0
- package/dist/handlers/stream-run.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/streaming/protocol.d.ts +33 -1
- package/dist/streaming/protocol.d.ts.map +1 -1
- package/dist/streaming/protocol.js +12 -0
- package/dist/streaming/protocol.js.map +1 -1
- package/dist/streaming/stream-writer.d.ts +5 -1
- package/dist/streaming/stream-writer.d.ts.map +1 -1
- package/dist/streaming/stream-writer.js +15 -1
- package/dist/streaming/stream-writer.js.map +1 -1
- package/dist/types.d.ts +62 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -159,11 +159,12 @@ Response format:
|
|
|
159
159
|
"totalTokens": 650
|
|
160
160
|
},
|
|
161
161
|
"toolCalls": [...],
|
|
162
|
-
"trace": { "traceId": "trace-xyz", "spans": [...] }
|
|
162
|
+
"trace": { "traceId": "trace-xyz", "spans": [...] },
|
|
163
|
+
"status": "completed"
|
|
163
164
|
}
|
|
164
165
|
```
|
|
165
166
|
|
|
166
|
-
A failed run answers `{ "error": message, "code": code }` with the status of its `CogitatorError` (for example `429 LLM_RATE_LIMITED`), and `500` for anything else.
|
|
167
|
+
A run waiting for [approvals](#approvals) answers `"status": "paused"` with its `pendingApprovals`. A failed run answers `{ "error": message, "code": code }` with the status of its `CogitatorError` (for example `429 LLM_RATE_LIMITED`), and `500` for anything else.
|
|
167
168
|
|
|
168
169
|
### Multiple users
|
|
169
170
|
|
|
@@ -181,6 +182,69 @@ export const POST = createChatHandler(cogitator, agent, {
|
|
|
181
182
|
|
|
182
183
|
A `threadId` that belongs to another user is refused before the model is called: `createAgentHandler` answers `403` with `code: "THREAD_ACCESS_DENIED"`, and `createChatHandler` ends the stream with an `error` event carrying that code. Without a `userId`, a caller can use only threads that have no owner.
|
|
183
184
|
|
|
185
|
+
### Approvals
|
|
186
|
+
|
|
187
|
+
A tool with `requiresApproval` pauses the run before it executes. The runtime keeps the run's checkpoint on the server, per thread; clients only see what waits for a decision:
|
|
188
|
+
|
|
189
|
+
- `createAgentHandler` answers with `"status": "paused"` and `pendingApprovals` (`toolCallId`, `toolName`, `arguments`, `description`, `sideEffects`).
|
|
190
|
+
- `createChatHandler` sends `{"type":"approval-required","threadId":"…","approvals":[…]}` right before `finish`.
|
|
191
|
+
|
|
192
|
+
`createResumeHandler` continues the run. It takes `{ threadId, decisions?, defaultDecision? }`, where a decision is `{ approved: true }` or `{ approved: false, reason? }` by tool call id. Approved calls run, declined ones answer the model with the reason, and calls left undecided pause the run again. Return the caller's `userId` from `beforeRun` as for the other handlers: only the user the run belongs to may resume it.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// app/api/chat/resume/route.ts
|
|
196
|
+
import { createResumeHandler } from '@cogitator-ai/next';
|
|
197
|
+
|
|
198
|
+
export const POST = createResumeHandler(cogitator, agent, {
|
|
199
|
+
stream: true,
|
|
200
|
+
beforeRun: async (req) => ({ userId: (await getSession(req)).id }),
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
By default it answers like `createAgentHandler`; with `stream: true` it streams the rest of the run like `createChatHandler`, which is what `useCogitatorChat` wants. Another user's run answers `403 THREAD_ACCESS_DENIED`, a thread without a paused run `409 RUN_NOT_PAUSED` (an `error` event when streaming).
|
|
205
|
+
|
|
206
|
+
On the client, `useCogitatorChat({ api, resumeApi })` exposes `pendingApprovals` and continues the run into a new assistant message:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
'use client';
|
|
210
|
+
|
|
211
|
+
import { useCogitatorChat } from '@cogitator-ai/next/client';
|
|
212
|
+
|
|
213
|
+
export function Chat() {
|
|
214
|
+
const { messages, pendingApprovals, approve, deny, isLoading } = useCogitatorChat({
|
|
215
|
+
api: '/api/chat',
|
|
216
|
+
resumeApi: '/api/chat/resume',
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<div>
|
|
221
|
+
{messages.map((m) => (
|
|
222
|
+
<p key={m.id}>{m.content}</p>
|
|
223
|
+
))}
|
|
224
|
+
{pendingApprovals.length > 0 && (
|
|
225
|
+
<div>
|
|
226
|
+
{pendingApprovals.map((call) => (
|
|
227
|
+
<p key={call.toolCallId}>
|
|
228
|
+
{call.toolName}: {JSON.stringify(call.arguments)}
|
|
229
|
+
</p>
|
|
230
|
+
))}
|
|
231
|
+
<button disabled={isLoading} onClick={() => approve()}>
|
|
232
|
+
Approve
|
|
233
|
+
</button>
|
|
234
|
+
<button disabled={isLoading} onClick={() => deny('Not approved')}>
|
|
235
|
+
Deny
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
)}
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
For one decision per call, use `resume({ decisions: { [toolCallId]: { approved: true } }, defaultDecision: { approved: false } })`.
|
|
245
|
+
|
|
246
|
+
`approve()` and `deny(reason?)` answer every pending call. The approvals clear once the resume is accepted, when the server has nothing paused anymore (`409`), or when the user sends a new message instead, which declines the waiting calls on the server. A JSON `resumeApi` works too: its answer is appended as one assistant message. `useCogitatorAgent({ api, resumeApi })` exposes `pendingApprovals` of the last result and `resume(decisions)`.
|
|
247
|
+
|
|
184
248
|
## Client Hooks
|
|
185
249
|
|
|
186
250
|
### `useCogitatorChat`
|
|
@@ -207,6 +271,12 @@ const {
|
|
|
207
271
|
appendMessage, // (message: ChatMessage) => void
|
|
208
272
|
clearMessages, // () => void
|
|
209
273
|
setMessages, // (messages: ChatMessage[]) => void
|
|
274
|
+
|
|
275
|
+
// Approvals (see "Approvals")
|
|
276
|
+
pendingApprovals, // PendingApproval[]
|
|
277
|
+
resume, // (decisions: ResumeDecisions) => Promise<void>
|
|
278
|
+
approve, // () => Promise<void>
|
|
279
|
+
deny, // (reason?: string) => Promise<void>
|
|
210
280
|
} = useCogitatorChat({
|
|
211
281
|
api: '/api/chat',
|
|
212
282
|
threadId: 'optional-thread-id',
|
|
@@ -218,6 +288,11 @@ const {
|
|
|
218
288
|
onFinish: (message) => console.log('Done:', message),
|
|
219
289
|
onToolCall: (toolCall) => console.log('Tool called:', toolCall.name),
|
|
220
290
|
onToolResult: (result) => console.log('Tool result:', result),
|
|
291
|
+
onReasoning: (delta) => console.log('Thinking:', delta),
|
|
292
|
+
onApprovalRequired: (approvals) => console.log('Waiting for approval:', approvals),
|
|
293
|
+
|
|
294
|
+
// Endpoint of a createResumeHandler, for resume/approve/deny
|
|
295
|
+
resumeApi: '/api/chat/resume',
|
|
221
296
|
|
|
222
297
|
// Retry configuration
|
|
223
298
|
retry: {
|
|
@@ -270,11 +345,15 @@ Hook for non-streaming batch requests (research, analysis, etc).
|
|
|
270
345
|
const {
|
|
271
346
|
run, // (input: AgentInput) => Promise<void>
|
|
272
347
|
result, // AgentResponse | null
|
|
348
|
+
reasoning, // string | undefined — result?.reasoning
|
|
349
|
+
pendingApprovals, // PendingApproval[] — result?.pendingApprovals
|
|
350
|
+
resume, // (decisions: ResumeDecisions) => Promise<void>
|
|
273
351
|
isLoading, // boolean
|
|
274
352
|
error, // Error | null
|
|
275
353
|
reset, // () => void
|
|
276
354
|
} = useCogitatorAgent({
|
|
277
355
|
api: '/api/research',
|
|
356
|
+
resumeApi: '/api/research/resume',
|
|
278
357
|
headers: { Authorization: 'Bearer token' },
|
|
279
358
|
|
|
280
359
|
onError: (error) => console.error(error),
|
|
@@ -327,7 +406,11 @@ data: {"type":"finish","messageId":"msg-1","usage":{...},"threadId":"thread-abc"
|
|
|
327
406
|
data: [DONE]
|
|
328
407
|
```
|
|
329
408
|
|
|
330
|
-
|
|
409
|
+
An agent with `reasoning: { summary: true }` also streams its reasoning summary as `reasoning-start`, `reasoning-delta` and `reasoning-end` events. A text or reasoning block opens with its first delta and is closed before a block of the other kind, a tool call or `finish`, so blocks never overlap. `useCogitatorChat` collects the deltas into `message.reasoning` (and calls `onReasoning` with each one), and `createAgentHandler` returns the summary as `reasoning` next to `usage.reasoningTokens`, `usage.cachedInputTokens` and `usage.cacheWriteTokens` when the provider reports them.
|
|
410
|
+
|
|
411
|
+
A run that pauses for [approvals](#approvals) sends `{"type":"approval-required","threadId":"…","approvals":[…]}` after the open block is closed and before `finish`.
|
|
412
|
+
|
|
413
|
+
If the run fails, the open text or reasoning block is closed and an `{"type":"error","message":"..."}` event is sent instead of `finish`.
|
|
331
414
|
|
|
332
415
|
The server-side building blocks are exported for custom handlers:
|
|
333
416
|
|
|
@@ -364,6 +447,22 @@ interface AgentResponse {
|
|
|
364
447
|
};
|
|
365
448
|
toolCalls: ToolCall[];
|
|
366
449
|
trace?: { traceId: string; spans: unknown[] };
|
|
450
|
+
reasoning?: string;
|
|
451
|
+
status?: 'completed' | 'paused';
|
|
452
|
+
pendingApprovals?: PendingApproval[];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
interface PendingApproval {
|
|
456
|
+
toolCallId: string;
|
|
457
|
+
toolName: string;
|
|
458
|
+
arguments: Record<string, unknown>;
|
|
459
|
+
description: string;
|
|
460
|
+
sideEffects?: string[];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
interface ResumeDecisions {
|
|
464
|
+
decisions?: Record<string, { approved: true } | { approved: false; reason?: string }>;
|
|
465
|
+
defaultDecision?: { approved: true } | { approved: false; reason?: string };
|
|
367
466
|
}
|
|
368
467
|
|
|
369
468
|
interface RetryConfig {
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { useCogitatorChat } from './use-chat.js';
|
|
2
2
|
export { useCogitatorAgent } from './use-agent.js';
|
|
3
3
|
export { HttpError } from './http-error.js';
|
|
4
|
-
export type { ChatMessage, ToolCall, UseChatOptions, UseChatReturn, AgentInput, AgentResponse, UseAgentOptions, UseAgentReturn, RetryConfig, ToolResultEvent, } from '../types.js';
|
|
4
|
+
export type { ChatMessage, ToolCall, UseChatOptions, UseChatReturn, AgentInput, AgentResponse, UseAgentOptions, UseAgentReturn, RetryConfig, ToolResultEvent, PendingApproval, ResumeDecisions, ToolApprovalDecision, } from '../types.js';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,YAAY,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,aAAa,EACb,UAAU,EACV,aAAa,EACb,eAAe,EACf,cAAc,EACd,WAAW,EACX,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,YAAY,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,aAAa,EACb,UAAU,EACV,aAAa,EACb,eAAe,EACf,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,eAAe,EACf,oBAAoB,GACrB,MAAM,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent.d.ts","sourceRoot":"","sources":["../../src/client/use-agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"use-agent.d.ts","sourceRoot":"","sources":["../../src/client/use-agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAKV,eAAe,EACf,cAAc,EACf,MAAM,aAAa,CAAC;AAMrB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAyG1E"}
|
package/dist/client/use-agent.js
CHANGED
|
@@ -2,32 +2,34 @@
|
|
|
2
2
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
3
3
|
import { withRetry } from './retry.js';
|
|
4
4
|
import { toHttpError } from './http-error.js';
|
|
5
|
+
const NO_APPROVALS = [];
|
|
5
6
|
export function useCogitatorAgent(options) {
|
|
6
|
-
const { api, headers, onError, onSuccess, retry } = options;
|
|
7
|
+
const { api, resumeApi, headers, onError, onSuccess, retry } = options;
|
|
7
8
|
const [result, setResult] = useState(null);
|
|
8
9
|
const [isLoading, setIsLoading] = useState(false);
|
|
9
10
|
const [error, setError] = useState(null);
|
|
10
11
|
const activeRequestRef = useRef(null);
|
|
12
|
+
const resultRef = useRef(null);
|
|
11
13
|
useEffect(() => {
|
|
12
14
|
return () => {
|
|
13
15
|
activeRequestRef.current?.abort();
|
|
14
16
|
activeRequestRef.current = null;
|
|
15
17
|
};
|
|
16
18
|
}, []);
|
|
17
|
-
const
|
|
19
|
+
const request = useCallback(async (url, body) => {
|
|
18
20
|
activeRequestRef.current?.abort();
|
|
19
21
|
const controller = new AbortController();
|
|
20
22
|
activeRequestRef.current = controller;
|
|
21
23
|
setIsLoading(true);
|
|
22
24
|
setError(null);
|
|
23
25
|
const executeRequest = async () => {
|
|
24
|
-
const response = await fetch(
|
|
26
|
+
const response = await fetch(url, {
|
|
25
27
|
method: 'POST',
|
|
26
28
|
headers: {
|
|
27
29
|
'Content-Type': 'application/json',
|
|
28
30
|
...headers,
|
|
29
31
|
},
|
|
30
|
-
body: JSON.stringify(
|
|
32
|
+
body: JSON.stringify(body),
|
|
31
33
|
signal: controller.signal,
|
|
32
34
|
});
|
|
33
35
|
if (!response.ok) {
|
|
@@ -39,6 +41,7 @@ export function useCogitatorAgent(options) {
|
|
|
39
41
|
const data = await withRetry(executeRequest, retry, controller.signal);
|
|
40
42
|
if (activeRequestRef.current !== controller)
|
|
41
43
|
return;
|
|
44
|
+
resultRef.current = data;
|
|
42
45
|
setResult(data);
|
|
43
46
|
onSuccess?.(data);
|
|
44
47
|
}
|
|
@@ -57,10 +60,24 @@ export function useCogitatorAgent(options) {
|
|
|
57
60
|
setIsLoading(false);
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
|
-
}, [
|
|
63
|
+
}, [headers, onError, onSuccess, retry]);
|
|
64
|
+
const run = useCallback((input) => request(api, input), [api, request]);
|
|
65
|
+
const resume = useCallback(async (decisions) => {
|
|
66
|
+
const threadId = resultRef.current?.threadId;
|
|
67
|
+
if (!resumeApi || !threadId) {
|
|
68
|
+
const e = new Error(resumeApi
|
|
69
|
+
? 'There is no result with a paused run to resume'
|
|
70
|
+
: 'useCogitatorAgent needs resumeApi to resume a paused run');
|
|
71
|
+
setError(e);
|
|
72
|
+
onError?.(e);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
await request(resumeApi, { threadId, ...decisions });
|
|
76
|
+
}, [onError, request, resumeApi]);
|
|
61
77
|
const reset = useCallback(() => {
|
|
62
78
|
activeRequestRef.current?.abort();
|
|
63
79
|
activeRequestRef.current = null;
|
|
80
|
+
resultRef.current = null;
|
|
64
81
|
setResult(null);
|
|
65
82
|
setError(null);
|
|
66
83
|
setIsLoading(false);
|
|
@@ -68,6 +85,9 @@ export function useCogitatorAgent(options) {
|
|
|
68
85
|
return {
|
|
69
86
|
run,
|
|
70
87
|
result,
|
|
88
|
+
reasoning: result?.reasoning,
|
|
89
|
+
pendingApprovals: result?.pendingApprovals ?? NO_APPROVALS,
|
|
90
|
+
resume,
|
|
71
91
|
isLoading,
|
|
72
92
|
error,
|
|
73
93
|
reset,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent.js","sourceRoot":"","sources":["../../src/client/use-agent.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"use-agent.js","sourceRoot":"","sources":["../../src/client/use-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatMessage, ToolCall } from '../types.js';
|
|
1
|
+
import type { ChatMessage, PendingApproval, ToolCall } from '../types.js';
|
|
2
2
|
export interface ChatState {
|
|
3
3
|
messages: ChatMessage[];
|
|
4
4
|
input: string;
|
|
@@ -7,7 +7,9 @@ export interface ChatState {
|
|
|
7
7
|
threadId: string | undefined;
|
|
8
8
|
currentMessageId: string | null;
|
|
9
9
|
currentContent: string;
|
|
10
|
+
currentReasoning: string;
|
|
10
11
|
currentToolCalls: ToolCall[];
|
|
12
|
+
pendingApprovals: PendingApproval[];
|
|
11
13
|
}
|
|
12
14
|
export type ChatAction = {
|
|
13
15
|
type: 'SET_INPUT';
|
|
@@ -34,11 +36,17 @@ export type ChatAction = {
|
|
|
34
36
|
} | {
|
|
35
37
|
type: 'APPEND_CONTENT';
|
|
36
38
|
payload: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'APPEND_REASONING';
|
|
41
|
+
payload: string;
|
|
37
42
|
} | {
|
|
38
43
|
type: 'ADD_TOOL_CALL';
|
|
39
44
|
payload: ToolCall;
|
|
40
45
|
} | {
|
|
41
46
|
type: 'FINISH_ASSISTANT_MESSAGE';
|
|
47
|
+
} | {
|
|
48
|
+
type: 'SET_PENDING_APPROVALS';
|
|
49
|
+
payload: PendingApproval[];
|
|
42
50
|
} | {
|
|
43
51
|
type: 'SET_MESSAGES';
|
|
44
52
|
payload: ChatMessage[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat-state.d.ts","sourceRoot":"","sources":["../../src/client/use-chat-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"use-chat-state.d.ts","sourceRoot":"","sources":["../../src/client/use-chat-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE1E,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,QAAQ,EAAE,CAAC;IAC7B,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACrC;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,KAAK,GAAG,IAAI,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,0BAA0B,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE/B,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CA4E3E;AAED,wBAAgB,kBAAkB,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAahG"}
|
|
@@ -19,10 +19,13 @@ export function chatReducer(state, action) {
|
|
|
19
19
|
...state,
|
|
20
20
|
currentMessageId: action.payload,
|
|
21
21
|
currentContent: '',
|
|
22
|
+
currentReasoning: '',
|
|
22
23
|
currentToolCalls: [],
|
|
23
24
|
};
|
|
24
25
|
case 'APPEND_CONTENT':
|
|
25
26
|
return { ...state, currentContent: state.currentContent + action.payload };
|
|
27
|
+
case 'APPEND_REASONING':
|
|
28
|
+
return { ...state, currentReasoning: state.currentReasoning + action.payload };
|
|
26
29
|
case 'ADD_TOOL_CALL':
|
|
27
30
|
return { ...state, currentToolCalls: [...state.currentToolCalls, action.payload] };
|
|
28
31
|
case 'FINISH_ASSISTANT_MESSAGE': {
|
|
@@ -32,6 +35,7 @@ export function chatReducer(state, action) {
|
|
|
32
35
|
id: state.currentMessageId,
|
|
33
36
|
role: 'assistant',
|
|
34
37
|
content: state.currentContent,
|
|
38
|
+
...(state.currentReasoning && { reasoning: state.currentReasoning }),
|
|
35
39
|
toolCalls: state.currentToolCalls.length > 0 ? state.currentToolCalls : undefined,
|
|
36
40
|
createdAt: new Date(),
|
|
37
41
|
};
|
|
@@ -40,9 +44,14 @@ export function chatReducer(state, action) {
|
|
|
40
44
|
messages: [...state.messages, assistantMessage],
|
|
41
45
|
currentMessageId: null,
|
|
42
46
|
currentContent: '',
|
|
47
|
+
currentReasoning: '',
|
|
43
48
|
currentToolCalls: [],
|
|
44
49
|
};
|
|
45
50
|
}
|
|
51
|
+
case 'SET_PENDING_APPROVALS':
|
|
52
|
+
if (action.payload.length === 0 && state.pendingApprovals.length === 0)
|
|
53
|
+
return state;
|
|
54
|
+
return { ...state, pendingApprovals: action.payload };
|
|
46
55
|
case 'SET_MESSAGES':
|
|
47
56
|
return { ...state, messages: action.payload };
|
|
48
57
|
case 'CLEAR_MESSAGES':
|
|
@@ -60,7 +69,9 @@ export function createInitialState(initialMessages, threadId) {
|
|
|
60
69
|
threadId,
|
|
61
70
|
currentMessageId: null,
|
|
62
71
|
currentContent: '',
|
|
72
|
+
currentReasoning: '',
|
|
63
73
|
currentToolCalls: [],
|
|
74
|
+
pendingApprovals: [],
|
|
64
75
|
};
|
|
65
76
|
}
|
|
66
77
|
//# sourceMappingURL=use-chat-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat-state.js","sourceRoot":"","sources":["../../src/client/use-chat-state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-chat-state.js","sourceRoot":"","sources":["../../src/client/use-chat-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat.d.ts","sourceRoot":"","sources":["../../src/client/use-chat.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"use-chat.d.ts","sourceRoot":"","sources":["../../src/client/use-chat.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,cAAc,EACd,aAAa,EAMd,MAAM,aAAa,CAAC;AAgDrB,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAkavE"}
|
package/dist/client/use-chat.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useReducer, useRef } from 'react';
|
|
|
3
3
|
import { parseSSEStream } from './sse-parser.js';
|
|
4
4
|
import { chatReducer, createInitialState, } from './use-chat-state.js';
|
|
5
5
|
import { withRetry } from './retry.js';
|
|
6
|
-
import { toHttpError } from './http-error.js';
|
|
6
|
+
import { HttpError, toHttpError } from './http-error.js';
|
|
7
7
|
let idCounter = 0;
|
|
8
8
|
function generateClientId() {
|
|
9
9
|
return `msg_${Date.now().toString(36)}_${(idCounter++).toString(36)}`;
|
|
@@ -21,6 +21,9 @@ function parseToolArguments(raw) {
|
|
|
21
21
|
catch { }
|
|
22
22
|
return {};
|
|
23
23
|
}
|
|
24
|
+
function isEventStream(response) {
|
|
25
|
+
return response.headers.get('Content-Type')?.includes('text/event-stream') ?? false;
|
|
26
|
+
}
|
|
24
27
|
function pendingAssistantMessage(state) {
|
|
25
28
|
if (!state.currentMessageId)
|
|
26
29
|
return null;
|
|
@@ -28,11 +31,12 @@ function pendingAssistantMessage(state) {
|
|
|
28
31
|
id: state.currentMessageId,
|
|
29
32
|
role: 'assistant',
|
|
30
33
|
content: state.currentContent,
|
|
34
|
+
...(state.currentReasoning && { reasoning: state.currentReasoning }),
|
|
31
35
|
toolCalls: state.currentToolCalls.length > 0 ? state.currentToolCalls : undefined,
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
38
|
export function useCogitatorChat(options) {
|
|
35
|
-
const { api, initialMessages, headers, onError, onFinish, onToolCall, onToolResult, retry } = options;
|
|
39
|
+
const { api, initialMessages, headers, onError, onFinish, onToolCall, onToolResult, onReasoning, onApprovalRequired, resumeApi, retry, } = options;
|
|
36
40
|
const [state, dispatch] = useReducer(chatReducer, createInitialState(initialMessages, options.threadId));
|
|
37
41
|
const stateRef = useRef(state);
|
|
38
42
|
const activeRequestRef = useRef(null);
|
|
@@ -55,6 +59,13 @@ export function useCogitatorChat(options) {
|
|
|
55
59
|
apply({ type: 'FINISH_ASSISTANT_MESSAGE' });
|
|
56
60
|
apply({ type: 'STOP_LOADING' });
|
|
57
61
|
}, [apply]);
|
|
62
|
+
const requireApproval = useCallback((approvals, threadId) => {
|
|
63
|
+
if (threadId && threadId !== stateRef.current.threadId) {
|
|
64
|
+
apply({ type: 'SET_THREAD_ID', payload: threadId });
|
|
65
|
+
}
|
|
66
|
+
apply({ type: 'SET_PENDING_APPROVALS', payload: approvals });
|
|
67
|
+
onApprovalRequired?.(approvals);
|
|
68
|
+
}, [apply, onApprovalRequired]);
|
|
58
69
|
const handleStreamEvent = useCallback((event, toolCalls) => {
|
|
59
70
|
switch (event.type) {
|
|
60
71
|
case 'start':
|
|
@@ -63,6 +74,10 @@ export function useCogitatorChat(options) {
|
|
|
63
74
|
case 'text-delta':
|
|
64
75
|
apply({ type: 'APPEND_CONTENT', payload: event.delta });
|
|
65
76
|
return false;
|
|
77
|
+
case 'reasoning-delta':
|
|
78
|
+
apply({ type: 'APPEND_REASONING', payload: event.delta });
|
|
79
|
+
onReasoning?.(event.delta);
|
|
80
|
+
return false;
|
|
66
81
|
case 'tool-call-start':
|
|
67
82
|
toolCalls.set(event.id, { name: event.toolName, args: '' });
|
|
68
83
|
return false;
|
|
@@ -95,6 +110,9 @@ export function useCogitatorChat(options) {
|
|
|
95
110
|
onToolResult?.(toolResult);
|
|
96
111
|
return false;
|
|
97
112
|
}
|
|
113
|
+
case 'approval-required':
|
|
114
|
+
requireApproval(event.approvals, event.threadId);
|
|
115
|
+
return false;
|
|
98
116
|
case 'finish':
|
|
99
117
|
if (event.threadId && event.threadId !== stateRef.current.threadId) {
|
|
100
118
|
apply({ type: 'SET_THREAD_ID', payload: event.threadId });
|
|
@@ -109,7 +127,7 @@ export function useCogitatorChat(options) {
|
|
|
109
127
|
default:
|
|
110
128
|
return false;
|
|
111
129
|
}
|
|
112
|
-
}, [apply, onError, onToolCall, onToolResult]);
|
|
130
|
+
}, [apply, onError, onReasoning, onToolCall, onToolResult, requireApproval]);
|
|
113
131
|
const processStream = useCallback(async (response, controller) => {
|
|
114
132
|
if (!response.body) {
|
|
115
133
|
throw new Error('Response body is null');
|
|
@@ -138,27 +156,41 @@ export function useCogitatorChat(options) {
|
|
|
138
156
|
onFinish?.(finished);
|
|
139
157
|
}
|
|
140
158
|
}, [apply, handleStreamEvent, onFinish]);
|
|
141
|
-
const
|
|
159
|
+
const processResult = useCallback(async (response, controller) => {
|
|
160
|
+
const result = (await response.json());
|
|
161
|
+
if (activeRequestRef.current !== controller)
|
|
162
|
+
return;
|
|
163
|
+
activeRequestRef.current = null;
|
|
164
|
+
const message = {
|
|
165
|
+
id: generateClientId(),
|
|
166
|
+
role: 'assistant',
|
|
167
|
+
content: result.output,
|
|
168
|
+
...(result.reasoning && { reasoning: result.reasoning }),
|
|
169
|
+
toolCalls: result.toolCalls.length > 0 ? result.toolCalls : undefined,
|
|
170
|
+
createdAt: new Date(),
|
|
171
|
+
};
|
|
172
|
+
apply({ type: 'APPEND_MESSAGE', payload: message });
|
|
173
|
+
if (result.status === 'paused') {
|
|
174
|
+
requireApproval(result.pendingApprovals ?? [], result.threadId);
|
|
175
|
+
}
|
|
176
|
+
else if (result.threadId && result.threadId !== stateRef.current.threadId) {
|
|
177
|
+
apply({ type: 'SET_THREAD_ID', payload: result.threadId });
|
|
178
|
+
}
|
|
179
|
+
apply({ type: 'STOP_LOADING' });
|
|
180
|
+
onFinish?.(message);
|
|
181
|
+
}, [apply, onFinish, requireApproval]);
|
|
182
|
+
const request = useCallback(async (url, body, handle, onFailure) => {
|
|
142
183
|
const controller = new AbortController();
|
|
143
184
|
activeRequestRef.current = controller;
|
|
144
185
|
apply({ type: 'START_LOADING' });
|
|
145
186
|
const doFetch = async () => {
|
|
146
|
-
const response = await fetch(
|
|
187
|
+
const response = await fetch(url, {
|
|
147
188
|
method: 'POST',
|
|
148
189
|
headers: {
|
|
149
190
|
'Content-Type': 'application/json',
|
|
150
191
|
...headers,
|
|
151
192
|
},
|
|
152
|
-
body: JSON.stringify(
|
|
153
|
-
messages: messages.map((m) => ({
|
|
154
|
-
id: m.id,
|
|
155
|
-
role: m.role,
|
|
156
|
-
content: m.content,
|
|
157
|
-
metadata: m.metadata,
|
|
158
|
-
})),
|
|
159
|
-
threadId,
|
|
160
|
-
metadata,
|
|
161
|
-
}),
|
|
193
|
+
body: JSON.stringify(body),
|
|
162
194
|
signal: controller.signal,
|
|
163
195
|
});
|
|
164
196
|
if (!response.ok) {
|
|
@@ -168,7 +200,7 @@ export function useCogitatorChat(options) {
|
|
|
168
200
|
};
|
|
169
201
|
try {
|
|
170
202
|
const response = await withRetry(doFetch, retry, controller.signal);
|
|
171
|
-
await
|
|
203
|
+
await handle(response, controller);
|
|
172
204
|
}
|
|
173
205
|
catch (err) {
|
|
174
206
|
if (activeRequestRef.current !== controller)
|
|
@@ -180,10 +212,24 @@ export function useCogitatorChat(options) {
|
|
|
180
212
|
return;
|
|
181
213
|
}
|
|
182
214
|
const error = err instanceof Error ? err : new Error('Unknown error');
|
|
215
|
+
onFailure?.(error);
|
|
183
216
|
apply({ type: 'SET_ERROR', payload: error });
|
|
184
217
|
onError?.(error);
|
|
185
218
|
}
|
|
186
|
-
}, [
|
|
219
|
+
}, [apply, headers, onError, retry]);
|
|
220
|
+
const sendWithMessages = useCallback(async (messages, threadId, metadata) => {
|
|
221
|
+
apply({ type: 'SET_PENDING_APPROVALS', payload: [] });
|
|
222
|
+
await request(api, {
|
|
223
|
+
messages: messages.map((m) => ({
|
|
224
|
+
id: m.id,
|
|
225
|
+
role: m.role,
|
|
226
|
+
content: m.content,
|
|
227
|
+
metadata: m.metadata,
|
|
228
|
+
})),
|
|
229
|
+
threadId,
|
|
230
|
+
metadata,
|
|
231
|
+
}, processStream);
|
|
232
|
+
}, [api, apply, processStream, request]);
|
|
187
233
|
const send = useCallback(async (inputOverride, metadata) => {
|
|
188
234
|
const messageContent = inputOverride ?? stateRef.current.input;
|
|
189
235
|
if (!messageContent.trim())
|
|
@@ -221,6 +267,35 @@ export function useCogitatorChat(options) {
|
|
|
221
267
|
apply({ type: 'SET_MESSAGES', payload: messages });
|
|
222
268
|
await sendWithMessages(messages, current.threadId, lastUserMessage.metadata);
|
|
223
269
|
}, [apply, interrupt, sendWithMessages]);
|
|
270
|
+
const fail = useCallback((message) => {
|
|
271
|
+
const error = new Error(message);
|
|
272
|
+
apply({ type: 'SET_ERROR', payload: error });
|
|
273
|
+
onError?.(error);
|
|
274
|
+
}, [apply, onError]);
|
|
275
|
+
const resume = useCallback(async (decisions) => {
|
|
276
|
+
const { threadId } = stateRef.current;
|
|
277
|
+
if (!resumeApi) {
|
|
278
|
+
fail('useCogitatorChat needs resumeApi to resume a paused run');
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!threadId) {
|
|
282
|
+
fail('There is no thread with a paused run to resume');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
interrupt();
|
|
286
|
+
await request(resumeApi, { threadId, ...decisions }, async (response, controller) => {
|
|
287
|
+
apply({ type: 'SET_PENDING_APPROVALS', payload: [] });
|
|
288
|
+
await (isEventStream(response) ? processStream : processResult)(response, controller);
|
|
289
|
+
}, (error) => {
|
|
290
|
+
if (error instanceof HttpError && error.status === 409) {
|
|
291
|
+
apply({ type: 'SET_PENDING_APPROVALS', payload: [] });
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}, [apply, fail, interrupt, processResult, processStream, request, resumeApi]);
|
|
295
|
+
const approve = useCallback(() => resume({ defaultDecision: { approved: true } }), [resume]);
|
|
296
|
+
const deny = useCallback((reason) => resume({
|
|
297
|
+
defaultDecision: reason === undefined ? { approved: false } : { approved: false, reason },
|
|
298
|
+
}), [resume]);
|
|
224
299
|
const setInput = useCallback((value) => {
|
|
225
300
|
apply({ type: 'SET_INPUT', payload: value });
|
|
226
301
|
}, [apply]);
|
|
@@ -252,6 +327,10 @@ export function useCogitatorChat(options) {
|
|
|
252
327
|
appendMessage,
|
|
253
328
|
clearMessages,
|
|
254
329
|
setMessages,
|
|
330
|
+
pendingApprovals: state.pendingApprovals,
|
|
331
|
+
resume,
|
|
332
|
+
approve,
|
|
333
|
+
deny,
|
|
255
334
|
};
|
|
256
335
|
}
|
|
257
336
|
//# sourceMappingURL=use-chat.js.map
|