@cogitator-ai/tetsu 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -23
- package/dist/controller.d.ts +234 -0
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +82 -19
- package/dist/controller.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/operations.d.ts +13 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +50 -0
- package/dist/operations.js.map +1 -1
- package/dist/schemas.d.ts +48 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +51 -1
- package/dist/schemas.js.map +1 -1
- package/dist/socket.d.ts +15 -0
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +27 -11
- package/dist/socket.js.map +1 -1
- package/dist/streaming.d.ts +17 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +44 -0
- package/dist/streaming.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -63,24 +63,26 @@ A Tetsu controller named `Cogitator`: `operationId`s in the OpenAPI document are
|
|
|
63
63
|
|
|
64
64
|
## Endpoints
|
|
65
65
|
|
|
66
|
-
| Method | Path
|
|
67
|
-
| -------- |
|
|
68
|
-
| `GET` | `/health`, `/ready`
|
|
69
|
-
| `GET` | `/agents`
|
|
70
|
-
| `POST` | `/agents/:name/run`
|
|
71
|
-
| `POST` | `/agents/:name/stream`
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `POST` | `/
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
66
|
+
| Method | Path | Description |
|
|
67
|
+
| -------- | ----------------------------- | --------------------------------------- |
|
|
68
|
+
| `GET` | `/health`, `/ready` | Liveness and readiness, without `auth` |
|
|
69
|
+
| `GET` | `/agents` | Agents with their description and tools |
|
|
70
|
+
| `POST` | `/agents/:name/run` | Run an agent and wait for the answer |
|
|
71
|
+
| `POST` | `/agents/:name/stream` | Run an agent over SSE |
|
|
72
|
+
| `POST` | `/agents/:name/resume` | Resume a run paused for approvals |
|
|
73
|
+
| `POST` | `/agents/:name/resume/stream` | Resume a paused run over SSE |
|
|
74
|
+
| `GET` | `/tools` | Tools of every agent, as JSON Schema |
|
|
75
|
+
| `GET` | `/threads/:id` | Messages of a memory thread |
|
|
76
|
+
| `POST` | `/threads/:id/messages` | Append a message |
|
|
77
|
+
| `DELETE` | `/threads/:id` | Delete a thread |
|
|
78
|
+
| `GET` | `/workflows` | Workflows with their nodes |
|
|
79
|
+
| `POST` | `/workflows/:name/run` | Run a workflow |
|
|
80
|
+
| `POST` | `/workflows/:name/stream` | Run a workflow over SSE |
|
|
81
|
+
| `GET` | `/swarms` | Swarms with their agents |
|
|
82
|
+
| `POST` | `/swarms/:name/run` | Run a swarm |
|
|
83
|
+
| `POST` | `/swarms/:name/stream` | Run a swarm over SSE |
|
|
84
|
+
| `GET` | `/swarms/:name/blackboard` | Configured blackboard sections |
|
|
85
|
+
| `GET` | `/ws` | WebSocket, when `websocket` is set |
|
|
84
86
|
|
|
85
87
|
Request bodies:
|
|
86
88
|
|
|
@@ -88,6 +90,13 @@ Request bodies:
|
|
|
88
90
|
// POST /agents/:name/run, /agents/:name/stream
|
|
89
91
|
{ input: string; context?: Record<string, unknown>; threadId?: string }
|
|
90
92
|
|
|
93
|
+
// POST /agents/:name/resume, /agents/:name/resume/stream
|
|
94
|
+
{
|
|
95
|
+
threadId: string;
|
|
96
|
+
decisions?: Record<string, { approved: boolean; reason?: string }>; // by tool call id
|
|
97
|
+
defaultDecision?: { approved: boolean; reason?: string };
|
|
98
|
+
}
|
|
99
|
+
|
|
91
100
|
// POST /swarms/:name/run, /swarms/:name/stream
|
|
92
101
|
{ input: string; context?: Record<string, unknown>; threadId?: string; timeout?: number }
|
|
93
102
|
|
|
@@ -113,6 +122,7 @@ Every error is answered in Tetsu's envelope:
|
|
|
113
122
|
| 403 | `THREAD_FORBIDDEN` | `authorizeThread` refused the thread |
|
|
114
123
|
| 404 | `AGENT_NOT_FOUND`, `WORKFLOW_NOT_FOUND`, `SWARM_NOT_FOUND` | No such name |
|
|
115
124
|
| 409 | `BLACKBOARD_DISABLED` | The swarm has no blackboard |
|
|
125
|
+
| 409 | `RUN_NOT_PAUSED` | A resume named a thread without a paused run |
|
|
116
126
|
| 422 | `VALIDATION_FAILED` | The body or the path failed its schema; `issues` lists every problem |
|
|
117
127
|
| 501 | `PACKAGE_NOT_INSTALLED` | `@cogitator-ai/workflows` or `@cogitator-ai/swarms` is missing |
|
|
118
128
|
| 503 | `MEMORY_NOT_CONFIGURED` | A thread endpoint was called on a runtime without memory |
|
|
@@ -201,6 +211,39 @@ cogitatorController({
|
|
|
201
211
|
|
|
202
212
|
Use it for rules ownership does not express, such as thread ids your server hands out.
|
|
203
213
|
|
|
214
|
+
## Approvals
|
|
215
|
+
|
|
216
|
+
A tool with `requiresApproval` pauses the run before it executes. `POST /agents/:name/run` then answers with `status: 'paused'` and the calls waiting for a decision; the run's checkpoint stays on the server, stored by the runtime per thread:
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"output": "Let me refund that.",
|
|
221
|
+
"threadId": "thread_…",
|
|
222
|
+
"status": "paused",
|
|
223
|
+
"pendingApprovals": [
|
|
224
|
+
{
|
|
225
|
+
"toolCallId": "call_1",
|
|
226
|
+
"toolName": "refund",
|
|
227
|
+
"arguments": { "order": "A-1", "amount": 500 },
|
|
228
|
+
"description": "Refund an order",
|
|
229
|
+
"sideEffects": ["payment"]
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
"usage": { "inputTokens": 10, "outputTokens": 5, "totalTokens": 15 },
|
|
233
|
+
"toolCalls": []
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
A stream that pauses sends `{"type":"approval-required","threadId":"…","approvals":[…]}` right before `finish`. Continue with the decisions:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
curl -X POST localhost:3000/cogitator/agents/support/resume \
|
|
241
|
+
-H 'content-type: application/json' \
|
|
242
|
+
-d '{ "threadId": "thread_…", "decisions": { "call_1": { "approved": true } } }'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Approved calls run, declined ones answer the model with the `reason`, and calls without a decision (and no `defaultDecision`) pause the run again. `/agents/:name/resume/stream` streams the rest of the run like `/stream`. Only the user the run belongs to can resume it (`403 THREAD_ACCESS_DENIED` otherwise; `authorizeThread` is checked too), and a thread without a paused run answers `409 RUN_NOT_PAUSED`. Over the WebSocket, send `{ type: 'resume', id?, payload: { name, threadId, decisions?, defaultDecision? } }`.
|
|
246
|
+
|
|
204
247
|
## Streaming
|
|
205
248
|
|
|
206
249
|
`/stream` endpoints answer with `text/event-stream` through `@tetsujs/sse`: keep-alive comments every 15 seconds, backpressure, and the run is aborted when the client goes away. Events follow the Cogitator stream protocol shared with the other adapters, one JSON object per `data:` line, ending with `data: [DONE]`:
|
|
@@ -218,6 +261,8 @@ data: {"type":"finish","messageId":"msg_…","usage":{"inputTokens":12,"outputTo
|
|
|
218
261
|
data: [DONE]
|
|
219
262
|
```
|
|
220
263
|
|
|
264
|
+
An agent with `reasoning: { summary: true }` also streams its reasoning summary as `reasoning-start`, `reasoning-delta` and `reasoning-end` events. A text or reasoning part opens with its first delta and is closed before a part of the other kind, a tool call or `finish`, so parts never overlap. `POST /agents/:name/run` returns the summary as `reasoning`, and `usage` gains `reasoningTokens`, `cachedInputTokens` and `cacheWriteTokens` when the provider reports them.
|
|
265
|
+
|
|
221
266
|
A run that fails ends with `{"type":"error","message":"…","code":"…"}` instead of `finish`. An unexpected failure is also reported to the application's `reportError` with `source: "stream"`. Workflow streams send `workflow` events (`node_started`, `node_completed`, `node_error`, `node_progress`, `workflow_completed`), swarm streams send `swarm` events (`agent_start`, `agent_complete`, `agent_error`, `message`, `swarm_completed`).
|
|
222
267
|
|
|
223
268
|
Validation, `401`, `403` and `404` are answered as JSON before the stream opens.
|
|
@@ -230,11 +275,12 @@ cogitatorController({ cogitator, agents, auth, websocket: true });
|
|
|
230
275
|
|
|
231
276
|
The handshake runs `auth` like any route. Each socket runs one agent, workflow or swarm at a time.
|
|
232
277
|
|
|
233
|
-
| Client sends | Server answers
|
|
234
|
-
| ------------------------------------------------------------------------------------------------------------- |
|
|
235
|
-
| `{ type: 'run', id?, payload: { type: 'agent' \| 'workflow' \| 'swarm', name, input, context?, threadId? } }` | `event` frames: `token`, `tool-call`, `tool-result`, then `complete` with the result, or `cancelled` |
|
|
236
|
-
| `{ type: '
|
|
237
|
-
| `{ type: '
|
|
278
|
+
| Client sends | Server answers |
|
|
279
|
+
| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
280
|
+
| `{ type: 'run', id?, payload: { type: 'agent' \| 'workflow' \| 'swarm', name, input, context?, threadId? } }` | `event` frames: `token`, `reasoning`, `tool-call`, `tool-result`, then `complete` with the result, or `cancelled` |
|
|
281
|
+
| `{ type: 'resume', id?, payload: { name, threadId, decisions?, defaultDecision? } }` | the same frames for an agent run paused for [approvals](#approvals) |
|
|
282
|
+
| `{ type: 'stop' }` | cancels the current run |
|
|
283
|
+
| `{ type: 'ping', id? }` | `{ type: 'pong', id }` |
|
|
238
284
|
|
|
239
285
|
Errors arrive as `{ type: 'error', id, error, code }` and leave the socket open; an invalid frame gets `code: 'INVALID_MESSAGE'`. Closing the socket cancels its run. Bun's server-level options such as `maxPayloadLength` are set where the app is served:
|
|
240
286
|
|
package/dist/controller.d.ts
CHANGED
|
@@ -40,6 +40,21 @@ export declare const cogitatorController: (deps: CogitatorDeps) => {
|
|
|
40
40
|
context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
41
41
|
threadId: z.ZodOptional<z.ZodString>;
|
|
42
42
|
}, z.core.$strip>;
|
|
43
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
44
|
+
type: z.ZodLiteral<"resume">;
|
|
45
|
+
id: z.ZodOptional<z.ZodString>;
|
|
46
|
+
payload: z.ZodObject<{
|
|
47
|
+
threadId: z.ZodString;
|
|
48
|
+
decisions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
49
|
+
approved: z.ZodBoolean;
|
|
50
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, z.core.$strip>>>;
|
|
52
|
+
defaultDecision: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
approved: z.ZodBoolean;
|
|
54
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
55
|
+
}, z.core.$strip>>;
|
|
56
|
+
name: z.ZodString;
|
|
57
|
+
}, z.core.$strip>;
|
|
43
58
|
}, z.core.$strip>], "type">;
|
|
44
59
|
}, {
|
|
45
60
|
readonly beforeParse: readonly [import("@tetsujs/core").Hook<"beforeParse", import("@tetsujs/core").BaseCtx & {
|
|
@@ -116,12 +131,27 @@ export declare const cogitatorController: (deps: CogitatorDeps) => {
|
|
|
116
131
|
inputTokens: z.ZodNumber;
|
|
117
132
|
outputTokens: z.ZodNumber;
|
|
118
133
|
totalTokens: z.ZodNumber;
|
|
134
|
+
reasoningTokens: z.ZodOptional<z.ZodNumber>;
|
|
135
|
+
cachedInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
136
|
+
cacheWriteTokens: z.ZodOptional<z.ZodNumber>;
|
|
119
137
|
}, z.core.$strip>;
|
|
120
138
|
toolCalls: z.ZodArray<z.ZodObject<{
|
|
121
139
|
id: z.ZodString;
|
|
122
140
|
name: z.ZodString;
|
|
123
141
|
arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
124
142
|
}, z.core.$strip>>;
|
|
143
|
+
reasoning: z.ZodOptional<z.ZodString>;
|
|
144
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
145
|
+
completed: "completed";
|
|
146
|
+
paused: "paused";
|
|
147
|
+
}>>;
|
|
148
|
+
pendingApprovals: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
149
|
+
toolCallId: z.ZodString;
|
|
150
|
+
toolName: z.ZodString;
|
|
151
|
+
arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
152
|
+
description: z.ZodString;
|
|
153
|
+
sideEffects: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
154
|
+
}, z.core.$strip>>>;
|
|
125
155
|
}, z.core.$strip>;
|
|
126
156
|
readonly 403: z.ZodObject<{
|
|
127
157
|
status: z.ZodNumber;
|
|
@@ -210,6 +240,9 @@ export declare const cogitatorController: (deps: CogitatorDeps) => {
|
|
|
210
240
|
inputTokens: number;
|
|
211
241
|
outputTokens: number;
|
|
212
242
|
totalTokens: number;
|
|
243
|
+
reasoningTokens?: number | undefined;
|
|
244
|
+
cachedInputTokens?: number | undefined;
|
|
245
|
+
cacheWriteTokens?: number | undefined;
|
|
213
246
|
};
|
|
214
247
|
toolCalls: {
|
|
215
248
|
id: string;
|
|
@@ -217,6 +250,15 @@ export declare const cogitatorController: (deps: CogitatorDeps) => {
|
|
|
217
250
|
arguments: Record<string, unknown>;
|
|
218
251
|
}[];
|
|
219
252
|
structured?: unknown;
|
|
253
|
+
reasoning?: string | undefined;
|
|
254
|
+
status?: "completed" | "paused" | undefined;
|
|
255
|
+
pendingApprovals?: {
|
|
256
|
+
toolCallId: string;
|
|
257
|
+
toolName: string;
|
|
258
|
+
arguments: Record<string, unknown>;
|
|
259
|
+
description: string;
|
|
260
|
+
sideEffects?: string[] | undefined;
|
|
261
|
+
}[] | undefined;
|
|
220
262
|
}>, false>;
|
|
221
263
|
streamAgent: import("@tetsujs/core").RouteDef<"/agents/:name/stream", {
|
|
222
264
|
readonly params: z.ZodObject<{
|
|
@@ -251,6 +293,198 @@ export declare const cogitatorController: (deps: CogitatorDeps) => {
|
|
|
251
293
|
readonly error: unknown;
|
|
252
294
|
}, unknown>];
|
|
253
295
|
}, undefined, "POST", Promise<Response>, false>;
|
|
296
|
+
resumeAgent: import("@tetsujs/core").RouteDef<"/agents/:name/resume", {
|
|
297
|
+
readonly params: z.ZodObject<{
|
|
298
|
+
name: z.ZodString;
|
|
299
|
+
}, z.core.$strip>;
|
|
300
|
+
readonly body: z.ZodObject<{
|
|
301
|
+
threadId: z.ZodString;
|
|
302
|
+
decisions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
303
|
+
approved: z.ZodBoolean;
|
|
304
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
305
|
+
}, z.core.$strip>>>;
|
|
306
|
+
defaultDecision: z.ZodOptional<z.ZodObject<{
|
|
307
|
+
approved: z.ZodBoolean;
|
|
308
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
309
|
+
}, z.core.$strip>>;
|
|
310
|
+
}, z.core.$strip>;
|
|
311
|
+
readonly response: {
|
|
312
|
+
readonly 200: z.ZodObject<{
|
|
313
|
+
output: z.ZodString;
|
|
314
|
+
structured: z.ZodOptional<z.ZodUnknown>;
|
|
315
|
+
threadId: z.ZodString;
|
|
316
|
+
usage: z.ZodObject<{
|
|
317
|
+
inputTokens: z.ZodNumber;
|
|
318
|
+
outputTokens: z.ZodNumber;
|
|
319
|
+
totalTokens: z.ZodNumber;
|
|
320
|
+
reasoningTokens: z.ZodOptional<z.ZodNumber>;
|
|
321
|
+
cachedInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
322
|
+
cacheWriteTokens: z.ZodOptional<z.ZodNumber>;
|
|
323
|
+
}, z.core.$strip>;
|
|
324
|
+
toolCalls: z.ZodArray<z.ZodObject<{
|
|
325
|
+
id: z.ZodString;
|
|
326
|
+
name: z.ZodString;
|
|
327
|
+
arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
328
|
+
}, z.core.$strip>>;
|
|
329
|
+
reasoning: z.ZodOptional<z.ZodString>;
|
|
330
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
331
|
+
completed: "completed";
|
|
332
|
+
paused: "paused";
|
|
333
|
+
}>>;
|
|
334
|
+
pendingApprovals: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
335
|
+
toolCallId: z.ZodString;
|
|
336
|
+
toolName: z.ZodString;
|
|
337
|
+
arguments: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
338
|
+
description: z.ZodString;
|
|
339
|
+
sideEffects: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
340
|
+
}, z.core.$strip>>>;
|
|
341
|
+
}, z.core.$strip>;
|
|
342
|
+
readonly 403: z.ZodObject<{
|
|
343
|
+
status: z.ZodNumber;
|
|
344
|
+
message: z.ZodString;
|
|
345
|
+
error: z.ZodEnum<{
|
|
346
|
+
[x: string]: string;
|
|
347
|
+
}>;
|
|
348
|
+
}, z.core.$strip>;
|
|
349
|
+
readonly 404: z.ZodObject<{
|
|
350
|
+
status: z.ZodNumber;
|
|
351
|
+
message: z.ZodString;
|
|
352
|
+
error: z.ZodEnum<{
|
|
353
|
+
[x: string]: string;
|
|
354
|
+
}>;
|
|
355
|
+
}, z.core.$strip>;
|
|
356
|
+
readonly 409: z.ZodObject<{
|
|
357
|
+
status: z.ZodNumber;
|
|
358
|
+
message: z.ZodString;
|
|
359
|
+
error: z.ZodEnum<{
|
|
360
|
+
[x: string]: string;
|
|
361
|
+
}>;
|
|
362
|
+
}, z.core.$strip>;
|
|
363
|
+
readonly 400: z.ZodObject<{
|
|
364
|
+
status: z.ZodNumber;
|
|
365
|
+
message: z.ZodString;
|
|
366
|
+
error: z.ZodEnum<{
|
|
367
|
+
[x: string]: string;
|
|
368
|
+
}>;
|
|
369
|
+
}, z.core.$strip>;
|
|
370
|
+
readonly 429: z.ZodObject<{
|
|
371
|
+
status: z.ZodNumber;
|
|
372
|
+
message: z.ZodString;
|
|
373
|
+
error: z.ZodEnum<{
|
|
374
|
+
[x: string]: string;
|
|
375
|
+
}>;
|
|
376
|
+
}, z.core.$strip>;
|
|
377
|
+
readonly 500: z.ZodObject<{
|
|
378
|
+
status: z.ZodNumber;
|
|
379
|
+
message: z.ZodString;
|
|
380
|
+
error: z.ZodEnum<{
|
|
381
|
+
[x: string]: string;
|
|
382
|
+
}>;
|
|
383
|
+
}, z.core.$strip>;
|
|
384
|
+
readonly 502: z.ZodObject<{
|
|
385
|
+
status: z.ZodNumber;
|
|
386
|
+
message: z.ZodString;
|
|
387
|
+
error: z.ZodEnum<{
|
|
388
|
+
[x: string]: string;
|
|
389
|
+
}>;
|
|
390
|
+
}, z.core.$strip>;
|
|
391
|
+
readonly 503: z.ZodObject<{
|
|
392
|
+
status: z.ZodNumber;
|
|
393
|
+
message: z.ZodString;
|
|
394
|
+
error: z.ZodEnum<{
|
|
395
|
+
[x: string]: string;
|
|
396
|
+
}>;
|
|
397
|
+
}, z.core.$strip>;
|
|
398
|
+
readonly 504: z.ZodObject<{
|
|
399
|
+
status: z.ZodNumber;
|
|
400
|
+
message: z.ZodString;
|
|
401
|
+
error: z.ZodEnum<{
|
|
402
|
+
[x: string]: string;
|
|
403
|
+
}>;
|
|
404
|
+
}, z.core.$strip>;
|
|
405
|
+
readonly 507: z.ZodObject<{
|
|
406
|
+
status: z.ZodNumber;
|
|
407
|
+
message: z.ZodString;
|
|
408
|
+
error: z.ZodEnum<{
|
|
409
|
+
[x: string]: string;
|
|
410
|
+
}>;
|
|
411
|
+
}, z.core.$strip>;
|
|
412
|
+
};
|
|
413
|
+
}, {
|
|
414
|
+
readonly beforeParse: readonly [import("@tetsujs/core").Hook<"beforeParse", import("@tetsujs/core").BaseCtx & {
|
|
415
|
+
readonly params: {
|
|
416
|
+
[x: string]: string;
|
|
417
|
+
};
|
|
418
|
+
}, Pick<import("./auth.js").CallerFields, "cogitatorAuth">>];
|
|
419
|
+
readonly onError: readonly [import("@tetsujs/core").Hook<"onError", import("@tetsujs/core").BaseCtx & {
|
|
420
|
+
readonly error: unknown;
|
|
421
|
+
}, unknown>];
|
|
422
|
+
}, undefined, "POST", Promise<{
|
|
423
|
+
output: string;
|
|
424
|
+
threadId: string;
|
|
425
|
+
usage: {
|
|
426
|
+
inputTokens: number;
|
|
427
|
+
outputTokens: number;
|
|
428
|
+
totalTokens: number;
|
|
429
|
+
reasoningTokens?: number | undefined;
|
|
430
|
+
cachedInputTokens?: number | undefined;
|
|
431
|
+
cacheWriteTokens?: number | undefined;
|
|
432
|
+
};
|
|
433
|
+
toolCalls: {
|
|
434
|
+
id: string;
|
|
435
|
+
name: string;
|
|
436
|
+
arguments: Record<string, unknown>;
|
|
437
|
+
}[];
|
|
438
|
+
structured?: unknown;
|
|
439
|
+
reasoning?: string | undefined;
|
|
440
|
+
status?: "completed" | "paused" | undefined;
|
|
441
|
+
pendingApprovals?: {
|
|
442
|
+
toolCallId: string;
|
|
443
|
+
toolName: string;
|
|
444
|
+
arguments: Record<string, unknown>;
|
|
445
|
+
description: string;
|
|
446
|
+
sideEffects?: string[] | undefined;
|
|
447
|
+
}[] | undefined;
|
|
448
|
+
}>, false>;
|
|
449
|
+
streamResumeAgent: import("@tetsujs/core").RouteDef<"/agents/:name/resume/stream", {
|
|
450
|
+
readonly params: z.ZodObject<{
|
|
451
|
+
name: z.ZodString;
|
|
452
|
+
}, z.core.$strip>;
|
|
453
|
+
readonly body: z.ZodObject<{
|
|
454
|
+
threadId: z.ZodString;
|
|
455
|
+
decisions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
456
|
+
approved: z.ZodBoolean;
|
|
457
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
458
|
+
}, z.core.$strip>>>;
|
|
459
|
+
defaultDecision: z.ZodOptional<z.ZodObject<{
|
|
460
|
+
approved: z.ZodBoolean;
|
|
461
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
462
|
+
}, z.core.$strip>>;
|
|
463
|
+
}, z.core.$strip>;
|
|
464
|
+
readonly response: {
|
|
465
|
+
readonly 403: z.ZodObject<{
|
|
466
|
+
status: z.ZodNumber;
|
|
467
|
+
message: z.ZodString;
|
|
468
|
+
error: z.ZodEnum<{
|
|
469
|
+
[x: string]: string;
|
|
470
|
+
}>;
|
|
471
|
+
}, z.core.$strip>;
|
|
472
|
+
readonly 404: z.ZodObject<{
|
|
473
|
+
status: z.ZodNumber;
|
|
474
|
+
message: z.ZodString;
|
|
475
|
+
error: z.ZodLiteral<"AGENT_NOT_FOUND">;
|
|
476
|
+
}, z.core.$strip>;
|
|
477
|
+
};
|
|
478
|
+
}, {
|
|
479
|
+
readonly beforeParse: readonly [import("@tetsujs/core").Hook<"beforeParse", import("@tetsujs/core").BaseCtx & {
|
|
480
|
+
readonly params: {
|
|
481
|
+
[x: string]: string;
|
|
482
|
+
};
|
|
483
|
+
}, Pick<import("./auth.js").CallerFields, "cogitatorAuth">>];
|
|
484
|
+
readonly onError: readonly [import("@tetsujs/core").Hook<"onError", import("@tetsujs/core").BaseCtx & {
|
|
485
|
+
readonly error: unknown;
|
|
486
|
+
}, unknown>];
|
|
487
|
+
}, undefined, "POST", Promise<Response>, false>;
|
|
254
488
|
getThread: import("@tetsujs/core").RouteDef<"/threads/:id", {
|
|
255
489
|
readonly params: z.ZodObject<{
|
|
256
490
|
id: z.ZodString;
|
package/dist/controller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,OAAO,EAMR,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmD7B,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AA0D7D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8b9B,CAAC"}
|
package/dist/controller.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { controller, route } from '@tetsujs/core';
|
|
2
2
|
import { sse } from '@tetsujs/sse';
|
|
3
3
|
import { countMessageTokens } from '@cogitator-ai/memory';
|
|
4
|
-
import { createErrorEvent, createFinishEvent, createStartEvent, createSwarmEvent,
|
|
4
|
+
import { createApprovalRequiredEvent, createErrorEvent, createFinishEvent, createStartEvent, createSwarmEvent, createToolCallDeltaEvent, createToolCallEndEvent, createToolCallStartEvent, createToolResultEvent, createWorkflowEvent, generateId, } from '@cogitator-ai/server-shared';
|
|
5
5
|
import { assertThreadAccess, ensureThreadAccess } from '@cogitator-ai/core';
|
|
6
6
|
import { httpError } from '@tetsujs/core';
|
|
7
7
|
import { resolveCaller } from './auth.js';
|
|
8
8
|
import { clientClosedRequest, cogitatorErrors, describeError } from './errors.js';
|
|
9
|
-
import { checkThreadAccess, executeSwarm, executeWorkflow, findAgent, findSwarm, findWorkflow, listAgents, listSwarms, listTools, listWorkflows, runAgent, serializeSwarmUsage, toAgentRunResponse, toSwarmRunResponse, toWorkflowRunResponse, } from './operations.js';
|
|
10
|
-
import { AddMessageBody, AddMessageResponse, AgentListResponse, AgentRunResponse, BlackboardResponse, errorEnvelope, errorsEnvelope, failureEnvelope, HealthResponse, NameParams, ReadyResponse, RUN_FAILURES, RunBody, SwarmListResponse, SwarmRunBody, SwarmRunResponse, ThreadParams, ThreadResponse, ToolListResponse, WorkflowListResponse, WorkflowRunBody, WorkflowRunResponse, } from './schemas.js';
|
|
9
|
+
import { checkThreadAccess, executeSwarm, executeWorkflow, findAgent, findSwarm, findWorkflow, listAgents, listSwarms, listTools, listWorkflows, resumeAgent, runAgent, serializeSwarmUsage, toAgentRunResponse, toPendingApprovals, toResumeDecisions, toSwarmRunResponse, toWorkflowRunResponse, } from './operations.js';
|
|
10
|
+
import { AddMessageBody, AddMessageResponse, AgentListResponse, AgentRunResponse, BlackboardResponse, errorEnvelope, errorsEnvelope, failureEnvelope, HealthResponse, NameParams, ReadyResponse, ResumeBody, RUN_FAILURES, RunBody, SwarmListResponse, SwarmRunBody, SwarmRunResponse, ThreadParams, ThreadResponse, ToolListResponse, WorkflowListResponse, WorkflowRunBody, WorkflowRunResponse, } from './schemas.js';
|
|
11
11
|
import { cogitatorSocket } from './socket.js';
|
|
12
|
-
import { DONE_EVENT, eventsOf, resolveSignal, sseEvent } from './streaming.js';
|
|
12
|
+
import { DONE_EVENT, eventsOf, MessageParts, resolveSignal, sseEvent } from './streaming.js';
|
|
13
13
|
const AgentNotFound = errorEnvelope('AGENT_NOT_FOUND', 'No agent is registered under this name');
|
|
14
14
|
const WorkflowNotFound = errorEnvelope('WORKFLOW_NOT_FOUND', 'No workflow is registered under this name');
|
|
15
15
|
const SwarmNotFound = errorEnvelope('SWARM_NOT_FOUND', 'No swarm is registered under this name');
|
|
16
16
|
const RunNotFound = failureEnvelope(404, 'No agent, workflow or swarm is registered under this name, or the agent called a tool it does not have', ['SWARM_NOT_FOUND']);
|
|
17
17
|
const NotImplemented = failureEnvelope(501, 'The optional package this endpoint needs is not installed', ['PACKAGE_NOT_INSTALLED']);
|
|
18
18
|
const ThreadForbidden = failureEnvelope(403, 'The memory thread belongs to another user, or `authorizeThread` refused it', ['THREAD_FORBIDDEN']);
|
|
19
|
+
const ResumeForbidden = failureEnvelope(403, 'The paused run belongs to another user, or `authorizeThread` refused the thread', ['THREAD_FORBIDDEN']);
|
|
20
|
+
const ResumeConflict = failureEnvelope(409, 'The thread has no paused run (`RUN_NOT_PAUSED`), or the agent is already running');
|
|
19
21
|
const ThreadUnreadable = errorsEnvelope(['MEMORY_READ_FAILED'], 'The memory adapter could not read the thread, so its owner is unknown');
|
|
20
22
|
const ThreadUnwritable = errorsEnvelope(['MEMORY_READ_FAILED', 'MEMORY_WRITE_FAILED'], 'The memory adapter could not read or create the thread');
|
|
21
23
|
const MemoryNotConfigured = errorEnvelope('MEMORY_NOT_CONFIGURED', 'The Cogitator runtime has no memory adapter');
|
|
@@ -128,9 +130,66 @@ export const cogitatorController = controller('Cogitator', (deps) => {
|
|
|
128
130
|
const auth = ctx.cogitatorAuth;
|
|
129
131
|
const body = ctx.body;
|
|
130
132
|
await checkThreadAccess(deps, auth, body.threadId);
|
|
131
|
-
return sse(ctx, (signal) => agentEvents(deps, agent, body, auth, signal), {
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
return sse(ctx, (signal) => agentEvents((callbacks) => runAgent(deps, agent, { ...body, ...callbacks }, auth), signal), { until: until() });
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
resumeAgent: route({
|
|
137
|
+
method: 'POST',
|
|
138
|
+
path: '/agents/:name/resume',
|
|
139
|
+
schema: {
|
|
140
|
+
params: NameParams,
|
|
141
|
+
body: ResumeBody,
|
|
142
|
+
response: {
|
|
143
|
+
...RUN_FAILURES,
|
|
144
|
+
200: AgentRunResponse,
|
|
145
|
+
403: ResumeForbidden,
|
|
146
|
+
404: RunNotFound,
|
|
147
|
+
409: ResumeConflict,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
hooks: { beforeParse: [caller], onError: [errors] },
|
|
151
|
+
docs: {
|
|
152
|
+
summary: 'Resume a run paused for tool approvals and wait for its answer',
|
|
153
|
+
description: 'Approved calls run, declined ones answer the model with the reason, and calls without a decision pause the run again.',
|
|
154
|
+
tags: ['agents'],
|
|
155
|
+
},
|
|
156
|
+
handler: async (ctx) => {
|
|
157
|
+
const agent = findAgent(deps, ctx.params.name);
|
|
158
|
+
const { threadId } = ctx.body;
|
|
159
|
+
await checkThreadAccess(deps, ctx.cogitatorAuth, threadId);
|
|
160
|
+
const signal = ctx.req.signal;
|
|
161
|
+
try {
|
|
162
|
+
const result = await resumeAgent(deps, agent, threadId, { ...toResumeDecisions(ctx.body), signal }, ctx.cogitatorAuth);
|
|
163
|
+
return toAgentRunResponse(result);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (signal.aborted)
|
|
167
|
+
throw clientClosedRequest();
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
}),
|
|
172
|
+
streamResumeAgent: route({
|
|
173
|
+
method: 'POST',
|
|
174
|
+
path: '/agents/:name/resume/stream',
|
|
175
|
+
schema: {
|
|
176
|
+
params: NameParams,
|
|
177
|
+
body: ResumeBody,
|
|
178
|
+
response: { 403: ThreadForbidden, 404: AgentNotFound },
|
|
179
|
+
},
|
|
180
|
+
hooks: { beforeParse: [caller], onError: [errors] },
|
|
181
|
+
docs: {
|
|
182
|
+
summary: 'Resume a run paused for tool approvals and stream the rest of it',
|
|
183
|
+
description: STREAM_DESCRIPTION,
|
|
184
|
+
tags: ['agents'],
|
|
185
|
+
},
|
|
186
|
+
handler: async (ctx) => {
|
|
187
|
+
const agent = findAgent(deps, ctx.params.name);
|
|
188
|
+
const auth = ctx.cogitatorAuth;
|
|
189
|
+
const { threadId } = ctx.body;
|
|
190
|
+
const decisions = toResumeDecisions(ctx.body);
|
|
191
|
+
await checkThreadAccess(deps, auth, threadId);
|
|
192
|
+
return sse(ctx, (signal) => agentEvents((callbacks) => resumeAgent(deps, agent, threadId, { ...decisions, ...callbacks }, auth), signal), { until: until() });
|
|
134
193
|
},
|
|
135
194
|
}),
|
|
136
195
|
getThread: route({
|
|
@@ -386,23 +445,24 @@ function* failure(error) {
|
|
|
386
445
|
if (described.unexpected)
|
|
387
446
|
throw error;
|
|
388
447
|
}
|
|
389
|
-
async function* agentEvents(
|
|
448
|
+
async function* agentEvents(start, signal) {
|
|
390
449
|
const messageId = generateId('msg');
|
|
391
|
-
const
|
|
450
|
+
const parts = new MessageParts();
|
|
392
451
|
let result;
|
|
393
452
|
yield sseEvent(createStartEvent(messageId));
|
|
394
|
-
yield sseEvent(createTextStartEvent(textId));
|
|
395
453
|
try {
|
|
396
454
|
yield* eventsOf(signal, async (emit) => {
|
|
397
|
-
|
|
398
|
-
|
|
455
|
+
const emitAll = (events) => {
|
|
456
|
+
for (const event of events)
|
|
457
|
+
emit(sseEvent(event));
|
|
458
|
+
};
|
|
459
|
+
result = await start({
|
|
399
460
|
stream: true,
|
|
400
461
|
signal,
|
|
401
|
-
onToken: (token) =>
|
|
402
|
-
|
|
403
|
-
emit(sseEvent(createTextDeltaEvent(textId, token)));
|
|
404
|
-
},
|
|
462
|
+
onToken: (token) => emitAll(parts.delta('text', token)),
|
|
463
|
+
onReasoning: (delta) => emitAll(parts.delta('reasoning', delta)),
|
|
405
464
|
onToolCall: (call) => {
|
|
465
|
+
emitAll(parts.end('reasoning'));
|
|
406
466
|
emit(sseEvent(createToolCallStartEvent(call.id, call.name)));
|
|
407
467
|
emit(sseEvent(createToolCallDeltaEvent(call.id, JSON.stringify(call.arguments))));
|
|
408
468
|
emit(sseEvent(createToolCallEndEvent(call.id)));
|
|
@@ -410,19 +470,22 @@ async function* agentEvents(deps, agent, body, auth, signal) {
|
|
|
410
470
|
onToolResult: (toolResult) => {
|
|
411
471
|
emit(sseEvent(createToolResultEvent(generateId('res'), toolResult.callId, toolResult.result)));
|
|
412
472
|
},
|
|
413
|
-
}
|
|
473
|
+
});
|
|
414
474
|
});
|
|
415
475
|
}
|
|
416
476
|
catch (error) {
|
|
417
477
|
if (signal.aborted)
|
|
418
478
|
return;
|
|
419
|
-
yield
|
|
479
|
+
yield* parts.end().map(sseEvent);
|
|
420
480
|
yield* failure(error);
|
|
421
481
|
return;
|
|
422
482
|
}
|
|
423
483
|
if (signal.aborted || !result)
|
|
424
484
|
return;
|
|
425
|
-
yield
|
|
485
|
+
yield* parts.end().map(sseEvent);
|
|
486
|
+
if (result.status === 'paused') {
|
|
487
|
+
yield sseEvent(createApprovalRequiredEvent(result.threadId, toPendingApprovals(result.pendingApprovals ?? [])));
|
|
488
|
+
}
|
|
426
489
|
yield sseEvent(createFinishEvent(messageId, {
|
|
427
490
|
inputTokens: result.usage.inputTokens,
|
|
428
491
|
outputTokens: result.usage.outputTokens,
|