@cogitator-ai/tetsu 0.7.1 → 0.8.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 +64 -40
- package/dist/controller.d.ts +265 -6
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +85 -18
- package/dist/controller.js.map +1 -1
- package/dist/errors.d.ts +22 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +65 -3
- package/dist/errors.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 +17 -2
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +45 -6
- package/dist/operations.js.map +1 -1
- package/dist/schemas.d.ts +113 -3
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +54 -10
- package/dist/schemas.js.map +1 -1
- package/dist/socket.js +2 -2
- package/dist/socket.js.map +1 -1
- package/dist/types.d.ts +11 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -46,51 +46,54 @@ curl -X POST http://localhost:3000/cogitator/agents/chat/run \
|
|
|
46
46
|
-H 'Content-Type: application/json' -d '{"input": "Hello"}'
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
A run that waits on a slow tool or model can stay silent for longer than the `idleTimeout` of `Bun.serve` (10 seconds by default), after which Bun closes the connection. The controller keeps both kinds of answer open without any setting: the JSON routes (`run`, `resume`, workflow and swarm `run`) lift the idle timeout for their own request once its body is validated (`ctx.server.timeout(ctx.req, 0)`), and SSE streams write a heartbeat comment every `sseHeartbeatMs` (5 seconds by default). If you change `idleTimeout`, keep it above `sseHeartbeatMs` and at 5 seconds or more, since Bun cuts connections at once below that whatever is written.
|
|
49
|
+
A run that waits on a slow tool or model can stay silent for longer than the `idleTimeout` of `Bun.serve` (10 seconds by default), after which Bun closes the connection. The controller keeps both kinds of answer open without any setting: the JSON routes (`run`, `resume`, workflow `run` and `resume`, swarm `run`) lift the idle timeout for their own request once its body is validated (`ctx.server.timeout(ctx.req, 0)`), and SSE streams write a heartbeat comment every `sseHeartbeatMs` (5 seconds by default). If you change `idleTimeout`, keep it above `sseHeartbeatMs` and at 5 seconds or more, since Bun cuts connections at once below that whatever is written.
|
|
50
50
|
|
|
51
51
|
## `cogitatorController(deps)`
|
|
52
52
|
|
|
53
53
|
A Tetsu controller named `Cogitator`: `operationId`s in the OpenAPI document are `cogitatorRunAgent`, `cogitatorGetThread` and so on.
|
|
54
54
|
|
|
55
|
-
| Option
|
|
56
|
-
|
|
|
57
|
-
| `cogitator`
|
|
58
|
-
| `agents`
|
|
59
|
-
| `workflows`
|
|
60
|
-
| `swarms`
|
|
61
|
-
| `auth`
|
|
62
|
-
| `anonymous`
|
|
63
|
-
| `authorizeThread`
|
|
64
|
-
| `limits`
|
|
65
|
-
| `deps`
|
|
66
|
-
| `websocket`
|
|
67
|
-
| `until`
|
|
68
|
-
| `sseHeartbeatMs`
|
|
69
|
-
| `acceptContext`
|
|
70
|
-
| `threadMessageRoles`
|
|
55
|
+
| Option | Type | Description |
|
|
56
|
+
| --------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `cogitator` | `Cogitator` | **Required.** The runtime |
|
|
58
|
+
| `agents` | `Record<string, Agent>` | Agents by name |
|
|
59
|
+
| `workflows` | `Record<string, Workflow>` | Workflows by name |
|
|
60
|
+
| `swarms` | `Record<string, SwarmConfig>` | Swarms by name |
|
|
61
|
+
| `auth` | `(ctx) => AuthContext \| undefined` or hook | Establishes the caller, see [Authentication](#authentication) |
|
|
62
|
+
| `anonymous` | `'allow' \| 'deny'` | Whether callers without a `userId` may use the API (default `'allow'`), see [Anonymous callers](#anonymous-callers) |
|
|
63
|
+
| `authorizeThread` | `(auth, threadId) => boolean` | Decides who may use a memory thread, see [Users and threads](#users-and-threads) |
|
|
64
|
+
| `limits` | `RunLimits` | Caps on what a request may ask a swarm or workflow run for, see [Run limits](#run-limits) |
|
|
65
|
+
| `deps` | `(ctx) => unknown \| Promise<unknown>` | Builds the `deps` agent runs pass to their tools, see [Run deps](#run-deps) |
|
|
66
|
+
| `websocket` | `boolean \| { path?, maxRuns?, maxBufferedBytes? }` | Serve the WebSocket endpoint (default path `/ws`), see [WebSocket](#websocket) |
|
|
67
|
+
| `until` | `AbortSignal \| (() => AbortSignal)` | Ends open streams and sockets, such as `draining` from `@tetsujs/lifecycle` |
|
|
68
|
+
| `sseHeartbeatMs` | `number` | Heartbeat comment interval of SSE streams (default `5000`, `0` turns it off) |
|
|
69
|
+
| `acceptContext` | `boolean \| string[]` | Keys of a run `context` clients may send: none by default, a list, or `true` for any (the model reads it as data from the application) |
|
|
70
|
+
| `threadMessageRoles` | `ThreadMessageRole[]` | Roles `POST /threads/:id/messages` accepts (default `user` and `assistant`) |
|
|
71
|
+
| `workflowCheckpoints` | `CheckpointStore` | Where workflow runs keep their checkpoints, for `options.checkpoint` and `/resume`, see [Checkpoints and resume](#checkpoints-and-resume) |
|
|
71
72
|
|
|
72
73
|
## Endpoints
|
|
73
74
|
|
|
74
|
-
| Method | Path
|
|
75
|
-
| -------- |
|
|
76
|
-
| `GET` | `/health`, `/ready`
|
|
77
|
-
| `GET` | `/agents`
|
|
78
|
-
| `POST` | `/agents/:name/run`
|
|
79
|
-
| `POST` | `/agents/:name/stream`
|
|
80
|
-
| `POST` | `/agents/:name/resume`
|
|
81
|
-
| `POST` | `/agents/:name/resume/stream`
|
|
82
|
-
| `GET` | `/tools`
|
|
83
|
-
| `GET` | `/threads/:id`
|
|
84
|
-
| `POST` | `/threads/:id/messages`
|
|
85
|
-
| `DELETE` | `/threads/:id`
|
|
86
|
-
| `GET` | `/workflows`
|
|
87
|
-
| `POST` | `/workflows/:name/run`
|
|
88
|
-
| `POST` | `/workflows/:name/stream`
|
|
89
|
-
| `
|
|
90
|
-
| `POST` | `/
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
75
|
+
| Method | Path | Description |
|
|
76
|
+
| -------- | -------------------------------- | ------------------------------------------------- |
|
|
77
|
+
| `GET` | `/health`, `/ready` | Liveness and readiness, without `auth` |
|
|
78
|
+
| `GET` | `/agents` | Agents with their description and tools |
|
|
79
|
+
| `POST` | `/agents/:name/run` | Run an agent and wait for the answer |
|
|
80
|
+
| `POST` | `/agents/:name/stream` | Run an agent over SSE |
|
|
81
|
+
| `POST` | `/agents/:name/resume` | Resume a run paused for approvals |
|
|
82
|
+
| `POST` | `/agents/:name/resume/stream` | Resume a paused run over SSE |
|
|
83
|
+
| `GET` | `/tools` | Tools of every agent, as JSON Schema |
|
|
84
|
+
| `GET` | `/threads/:id` | Messages of a thread, a page with `?limit&cursor` |
|
|
85
|
+
| `POST` | `/threads/:id/messages` | Append a message |
|
|
86
|
+
| `DELETE` | `/threads/:id` | Delete a thread |
|
|
87
|
+
| `GET` | `/workflows` | Workflows with their nodes |
|
|
88
|
+
| `POST` | `/workflows/:name/run` | Run a workflow |
|
|
89
|
+
| `POST` | `/workflows/:name/stream` | Run a workflow over SSE |
|
|
90
|
+
| `POST` | `/workflows/:name/resume` | Resume a workflow from a checkpoint |
|
|
91
|
+
| `POST` | `/workflows/:name/resume/stream` | Resume a workflow over SSE |
|
|
92
|
+
| `GET` | `/swarms` | Swarms with their agents |
|
|
93
|
+
| `POST` | `/swarms/:name/run` | Run a swarm |
|
|
94
|
+
| `POST` | `/swarms/:name/stream` | Run a swarm over SSE |
|
|
95
|
+
| `GET` | `/swarms/:name/blackboard` | Configured blackboard sections |
|
|
96
|
+
| `GET` | `/ws` | WebSocket, when `websocket` is set |
|
|
94
97
|
|
|
95
98
|
Request bodies:
|
|
96
99
|
|
|
@@ -109,18 +112,38 @@ Request bodies:
|
|
|
109
112
|
{ input: string; context?: Record<string, unknown>; threadId?: string; timeout?: number } // timeout at most 2147483647 ms, held to limits.swarmTimeout
|
|
110
113
|
|
|
111
114
|
// POST /workflows/:name/run, /workflows/:name/stream
|
|
112
|
-
{ input?: Record<string, unknown>; options?: { maxConcurrency?: number; maxIterations?: number; checkpoint?:
|
|
115
|
+
{ input?: Record<string, unknown>; options?: { maxConcurrency?: number; maxIterations?: number; checkpoint?: boolean } } // held to limits, checkpoint: true only with workflowCheckpoints
|
|
116
|
+
|
|
117
|
+
// POST /workflows/:name/resume, /workflows/:name/resume/stream
|
|
118
|
+
{ checkpointId: string; options?: { maxConcurrency?: number; maxIterations?: number } } // held to limits
|
|
113
119
|
|
|
114
120
|
// POST /threads/:id/messages
|
|
115
121
|
{ role: 'user' | 'assistant' | 'system'; content: string; metadata?: Record<string, unknown> } // system only with threadMessageRoles
|
|
116
122
|
```
|
|
117
123
|
|
|
118
|
-
The schemas are refined with the validators every Cogitator adapter shares (`@cogitator-ai/server-shared`), so Tetsu refuses the same bodies as Express, Fastify, Hono, Koa and Next, under its own `VALIDATION_FAILED` envelope. A run sends `context` to the model as data the application supplied and the model reads a `system` thread message as operator instructions, so both are refused unless the controller accepts them: `acceptContext` lists the `context` keys clients may send (or `true` for any), and `threadMessageRoles` the roles they may add (`user` and `assistant` by default). A body that is not JSON (`text/plain`, a form) is refused with `415 UNSUPPORTED_MEDIA_TYPE` before it is read, since browsers send those across origins without a CORS preflight. `options.checkpoint: true`
|
|
124
|
+
The schemas are refined with the validators every Cogitator adapter shares (`@cogitator-ai/server-shared`), so Tetsu refuses the same bodies as Express, Fastify, Hono, Koa and Next, under its own `VALIDATION_FAILED` envelope. A run sends `context` to the model as data the application supplied and the model reads a `system` thread message as operator instructions, so both are refused unless the controller accepts them: `acceptContext` lists the `context` keys clients may send (or `true` for any), and `threadMessageRoles` the roles they may add (`user` and `assistant` by default). A body that is not JSON (`text/plain`, a form) is refused with `415 UNSUPPORTED_MEDIA_TYPE` before it is read, since browsers send those across origins without a CORS preflight. `options.checkpoint: true` fails validation unless the controller has `workflowCheckpoints`, see [Checkpoints and resume](#checkpoints-and-resume).
|
|
119
125
|
|
|
120
126
|
`GET /threads/:id` answers the whole thread, or one page of it with `?limit=50`: the newest 50 messages (oldest first within the page) and a `nextCursor`. Pass it back as `?limit=50&cursor=<nextCursor>` for the 50 before them, until an answer has no `nextCursor`. Messages written in the same millisecond are neither skipped nor repeated across pages. A `limit` outside 1 to 1000 or a cursor the server did not give answers `422 VALIDATION_FAILED`.
|
|
121
127
|
|
|
122
128
|
`POST /agents/:name/run` and `/resume` answer `{ output, threadId, usage, toolCalls, status, traceId }` plus, when they apply, `reasoning`, `pendingApprovals`, `structured`, `structuredError`, `truncated`, `blocked` and `iterationLimitReached`: the same object as every other adapter, built by `toAgentRunResponse()` of `@cogitator-ai/server-shared`. It never carries the system prompt, the history, trace spans or the checkpoint of a paused run. Swarm routes close the swarm they build once the run ends, so a distributed swarm leaves no Redis connections behind, and `GET /swarms` lists every agent, a router and pipeline stages included.
|
|
123
129
|
|
|
130
|
+
### Checkpoints and resume
|
|
131
|
+
|
|
132
|
+
`workflowCheckpoints` takes any `CheckpointStore` of `@cogitator-ai/workflows`, such as `PostgresCheckpointStore` or `RedisCheckpointStore`. With it, a run with `options.checkpoint: true` saves its checkpoints there as the caller's, the `userId` that `auth` returned (none for an anonymous caller), and its answer and `workflow_completed` event carry the last one as `checkpointId`. Without it such a run is refused with `422 VALIDATION_FAILED`.
|
|
133
|
+
|
|
134
|
+
`POST /workflows/:name/resume` takes `{ checkpointId: string; options?: { maxConcurrency?: number; maxIterations?: number } }`, passes over the nodes the checkpoint completed and runs the rest. It answers like `/run`, and `/resume/stream` streams like `/stream`. The `limits` hold as on `/run`, and the resumed run keeps saving checkpoints, so it can be resumed again. A checkpoint that is missing, belongs to another user or to another workflow answers `404 CHECKPOINT_NOT_FOUND` before anything runs, on `/resume/stream` before the stream opens. Without `workflowCheckpoints` every resume answers `404 CHECKPOINT_NOT_FOUND`. A run that fails answers its error with the last checkpoint it saved as `checkpointId`, next to `error` and `message` (`{ status, message, error, checkpointId }`), and a failed stream sends a `workflow_failed` event with `{ checkpointId }` right before its `error` event, so the client resumes the failed run from there. A resume that fails again names the checkpoint to resume from, its own last one or, when it saved none, the one it started from. A failed run with a checkpoint is answered by the controller itself, so the application's own `onError` hooks do not see its error: a `CogitatorError` keeps its status and code, an `HttpError` a node throws keeps its status and body, and any other error becomes `500 INTERNAL_SERVER_ERROR`, reported to `reportError`. Throw an `HttpError` or a `CogitatorError` from a node to choose the answer. A failed run without a checkpoint throws its error as it is, for those hooks.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { PostgresCheckpointStore } from '@cogitator-ai/workflows';
|
|
138
|
+
|
|
139
|
+
cogitatorController({
|
|
140
|
+
cogitator,
|
|
141
|
+
workflows: { shipping },
|
|
142
|
+
auth,
|
|
143
|
+
workflowCheckpoints: new PostgresCheckpointStore({ client: pool }),
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
124
147
|
## Errors
|
|
125
148
|
|
|
126
149
|
Every error is answered in Tetsu's envelope:
|
|
@@ -136,6 +159,7 @@ Every error is answered in Tetsu's envelope:
|
|
|
136
159
|
| 403 | `THREAD_ACCESS_DENIED` | The thread belongs to another user |
|
|
137
160
|
| 403 | `THREAD_FORBIDDEN` | `authorizeThread` refused the thread |
|
|
138
161
|
| 404 | `AGENT_NOT_FOUND`, `WORKFLOW_NOT_FOUND`, `SWARM_NOT_FOUND` | No such name |
|
|
162
|
+
| 404 | `CHECKPOINT_NOT_FOUND` | A workflow resume named a checkpoint that is missing, of another user or of another workflow |
|
|
139
163
|
| 409 | `BLACKBOARD_DISABLED` | The swarm has no blackboard |
|
|
140
164
|
| 409 | `RUN_NOT_PAUSED` | A resume named a thread without a paused run |
|
|
141
165
|
| 413 | `BODY_TOO_LARGE` | The body is over the application's body limit |
|
|
@@ -335,7 +359,7 @@ Each event matches `StreamEventSchema` from `@cogitator-ai/server-shared`, which
|
|
|
335
359
|
|
|
336
360
|
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 and text after a tool call starts a new part. An answer the model gave in one piece arrives as one text part before `finish`, and a resumed run that pauses again sends `approval-required` before `finish`. `POST /agents/:name/run` returns the summary as `reasoning`, and `usage` gains `reasoningTokens`, `cachedInputTokens` and `cacheWriteTokens` when the provider reports them. The `finish` event of an agent stream carries the same `usage` as the JSON answer, these counts included.
|
|
337
361
|
|
|
338
|
-
A run that fails ends with `{"type":"error","message":"…","code":"…"}` instead of `finish`. A `CogitatorError` keeps its message and code, anything else is sent as `"message":"Internal server error","code":"INTERNAL_ERROR"` and 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`, every event the swarm itself emits under its own name, such as `swarm:start` or `agent:message`, and `swarm_completed`).
|
|
362
|
+
A run that fails ends with `{"type":"error","message":"…","code":"…"}` instead of `finish`. A `CogitatorError` keeps its message and code, anything else is sent as `"message":"Internal server error","code":"INTERNAL_ERROR"` and reported to the application's `reportError` with `source: "stream"`. Workflow streams send `workflow` events (`node_started`, `node_completed`, `node_error`, `node_progress`, `workflow_completed`, and `workflow_failed` with `{ checkpointId? }` right before the `error` event of a failed run), swarm streams send `swarm` events (`agent_start`, `agent_complete`, `agent_error`, `message`, every event the swarm itself emits under its own name, such as `swarm:start` or `agent:message`, and `swarm_completed`).
|
|
339
363
|
|
|
340
364
|
Validation (`415`, `422`), `401`, `404` and the `403 THREAD_FORBIDDEN` of `authorizeThread` are answered as JSON before the stream opens. Whether the thread belongs to the caller is checked by the run itself, so another user's thread ends an open stream with an `error` event, `"code":"THREAD_ACCESS_DENIED"`, instead of `finish`.
|
|
341
365
|
|