# @cogitator-ai/express 中文文档

规范地址：https://npmcn.com/package/%40cogitator-ai/express/

版本：0\.16\.1
npm 元数据更新时间：2026-10-29T00:50:02.063Z
中文译文更新时间：2026-11-10T10:08:18.046Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40cogitator-ai/express
原文对照：https://npmcn.com/package/%40cogitator-ai/express/?lang=original
源码仓库：https://github.com/cogitator-ai/Cogitator-AI

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：232 kB，111 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.12\.0
- 运行依赖：3 个
- peer 依赖：express \>=4\.18\.0
- 首次发布：2026-01-26T17:26:59.443Z
- 最新版本发布：2026-10-29T00:50:01.727Z
- 近 30 天下载量：9865（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：89（近 12 个月 89 个）

## 中文简介

Express.js 用于 Cogitator AI 运行时间的服务器适配器

## 安装

```sh
npm install @cogitator-ai/express
```

也可以使用：`pnpm add @cogitator-ai/express`、`yarn add @cogitator-ai/express`、`bun add @cogitator-ai/express`

## README

# @cogitator-ai/ 表达

Express.js 为Cogitator AI运行时间的服务器适配器. 自动生成 REST API 端点用于代理,工作流程,并有 SSE 流出, WebSocket 支持,和 Swagger 文档.

## 安装

```bash
npm install @cogitator-ai/express express
# or
pnpm add @cogitator-ai/express express
```

## 快速启动

```typescript
import express from 'express';
import { Cogitator, Agent, tool } from '@cogitator-ai/core';
import { CogitatorServer } from '@cogitator-ai/express';

const app = express();

const cogitator = new Cogitator({
  llm: {
    defaultModel: 'openai/gpt-6-luna',
    providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } },
  },
});

const chatAgent = new Agent({
  name: 'chat',
  description: 'General-purpose chat assistant',
  instructions: 'You are a helpful assistant.',
  model: 'openai/gpt-6-luna',
});

const server = new CogitatorServer({
  app,
  cogitator,
  agents: { chat: chatAgent },
  config: {
    basePath: '/api',
    enableSwagger: true,
  },
});

await server.init();
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
```

## 自动生成的终点

### 探员

```
GET    /api/agents                         - List all agents
POST   /api/agents/:name/run               - Run agent (JSON response)
POST   /api/agents/:name/stream            - Run agent (SSE stream)
POST   /api/agents/:name/resume            - Resume a run paused for tool approvals
POST   /api/agents/:name/resume/stream     - Resume a paused run (SSE stream)
POST   /api/agents/:name/agui              - Run or resume for an AG-UI client (AG-UI SSE)
```

运行/ 流体 : `{ input: string; context?: object; threadId?: string; multitaskStrategy?: 'enqueue' | 'reject' | 'interrupt' | 'rollback' }`。 。 。 . `input` 必须包含多于白空间和 `threadId`,发送时必须是非空字符串(`400 INVALID_INPUT` 否则,在模型被调用之前). 验证符来自 `@cogitator-ai/server-shared`因此,快活,快活,Hono和Koa拒绝完全相同的尸体。 这个 `usage` 运行中答案带 `inputTokens`, (中文(简体) ), `outputTokens` 财务报告和审定财务报表 `totalTokens`附加时 `reasoningTokens`, (中文(简体) ), `cachedInputTokens` 财务报告和审定财务报表 `cacheWriteTokens` 当模型报告它们时,形状与其他每个适配器相同. 这个 `finish` 一个代理流带有相同的 `usage` (无论运行成本或持续时间). 特工名单曝光 `config.description`绝不是特工的指示 认证 `userId` (从) `auth`)被传递到运行,当客户端断开时运行被中止.

-   运行发送 `context` 该模型读取 `system` 线程消息作为操作员指令,所以客户端可以不设置,除非服务器允许: `acceptContext` 列出 `context` 密钥客户端可能发送( e)`true` 接受任何密钥,对于像您自己的代码一样信任的客户端),并 `threadMessageRoles` 联合国 `POST /threads/:id/messages` (单位:千美元)`user` 财务报告和审定财务报表 `assistant` (默认情况下). 其他的都拒绝 `400 INVALID_INPUT`在HTTP和WebSocket上一样.
-   A 类 `POST` 身体不是JSON的(A`text/plain`拒绝使用 `415 UNSUPPORTED_MEDIA_TYPE` 在解析前 : 浏览器在不使用 CORS 预飞的情况下发送源代码 .
-   `POST /agents/:name/run` 财务报告和审定财务报表 `/resume` 回复 `toAgentRunResponse()` 联 合 国 `@cogitator-ai/server-shared`数字 : `output`, (中文(简体) ), `threadId`, (中文(简体) ), `usage`, (中文(简体) ), `toolCalls` (单位:千美元)`{ id, name, arguments }`, (中文(简体) ), `status` 财务报告和审定财务报表 `traceId`附加时 `reasoning`, (中文(简体) ), `pendingApprovals`, (中文(简体) ), `structured`, (中文(简体) ), `structuredError`, (中文(简体) ), `truncated`, (中文(简体) ), `blocked` 财务报告和审定财务报表 `iterationLimitReached` 当它们适用。 网页 `complete` 事件带有相同的对象,从不提示系统,历史,痕量跨度或暂停运行的检查点.
-   这个 `start` 代理流命名运行线索的事件(请求的) `threadId`,或一个新的),以及 `finish` 以运行结果重复它,所以一个发送无线的客户端继续与这个客户端的对话.
-   一旦运行结束,沼泽路线就会关闭,因此分布的沼泽不会留下雷地斯的连接,其状态会过期. `GET /swarms` 列出每个代理人、路由器和管道阶段.

### 线索( 记忆)

```
GET    /api/threads/:id               - Get thread messages, a page at a time with ?limit&cursor
POST   /api/threads/:id/messages      - Add message to thread
DELETE /api/threads/:id               - Delete thread
```

路线 `cogitator.getMemory()`,它连接了首先使用的已配置的内存适配器,因此在任何代理运行之前,线程可以在新服务器上被读取. 仅a `Cogitator` 无 `memory` 配置的答案 `503 UNAVAILABLE`。需要新信息 `role` (单位:千美元)`user` 或者说 `assistant`,则与 `threadMessageRoles`和一个非空字符串 `content`选项 `metadata` 存储在内存条目上.

`GET /threads/:id` 默认回答整个线程 。 与 `?limit=50` 它回答了最新50条信息(最老的首页)和a `nextCursor`,以及 `?limit=50&cursor=<nextCursor>` 在他们面前的50个,直到一个答复没有 `nextCursor`。用同样毫秒写出的信件从不跳过或跨行重复。 A 类 `limit` 1至 1000 之外或服务器从未给予的光标被拒绝 `400 INVALID_INPUT`。 。 。 .

```typescript
let cursor: string | undefined;
do {
  const query = new URLSearchParams({ limit: '50', ...(cursor && { cursor }) });
  const page = await (await fetch(`/api/threads/${threadId}?${query}`)).json();
  render(page.messages);
  cursor = page.nextCursor;
} while (cursor);
```

### 工作流量

```
GET    /api/workflows                 - List all workflows
POST   /api/workflows/:name/run            - Run workflow
POST   /api/workflows/:name/stream         - Stream workflow events
POST   /api/workflows/:name/resume         - Resume a workflow from a checkpoint
POST   /api/workflows/:name/resume/stream  - Resume a workflow and stream its events
```

车身 : `{ input?: object; options?: { maxConcurrency?: number; maxIterations?: number; checkpoint?: boolean } }`。 。 。 . `maxConcurrency`编号 :`maxIterations` 必须是正整数(另作400个)。 与 `limits.workflowMaxIterations` 或者说 `limits.workflowMaxConcurrency` 设置时,一个较大的值被维持到极限,一个要求无一物的请求则得到极限。 其他选择被放弃,以及 `checkpoint: true` 除非服务器已经 `workflowCheckpoints` (见 [检查站和恢复](https://npmcn.com/package/%40cogitator-ai/express/#readme-checkpoints-and-resume)) (中文(简体) ). 一个失败的工作流程会回答它失败的错误, 像其它运行错误一样映射( 点击的节点) `LLM_RATE_LIMITED` 答题 `429` 与 `Retry-After`,内部错误 `500 INTERNAL_ERROR`),其溪流以 `error` 带有该代码的事件代替 `workflow_completed`。断开连接会中止工作流程.

溪流传出 `{ type: 'workflow', event, data }` 事件: `node_started`, (中文(简体) ), `node_completed`, (中文(简体) ), `node_error`, (中文(简体) ), `node_progress` 终于 `workflow_completed` (单位:千美元)`{ workflowId, duration, checkpointId? }`),或者,当运行失败时, `workflow_failed` (单位:千美元)`{ checkpointId? }`紧接着 `error` 活动.

#### 检查站和恢复

`config.workflowCheckpoints` 使用任意 `CheckpointStore` 联 合 国 `@cogitator-ai/workflows`,例如 `PostgresCheckpointStore` 或者说 `RedisCheckpointStore`带着它,跑步 `options.checkpoint: true` 把它的检查站作为呼叫者 `userId` 那个 `auth` 和答复 `workflow_completed` 最后一个事件为 `checkpointId`。 。 。 .

`POST /workflows/:name/resume` 带 `{ checkpointId: string; options?: { maxConcurrency?: number; maxIterations?: number } }`,通过完成的检查站的节点并运行其余部分。 答案是说 `/run`,以及 `/resume/stream` 溪流类 `/stream`编辑 `limits` 等一会 `/run`,而恢复运行时会不断节省检查站,以便再次恢复运行。 缺少、属于另一个用户或属于另一个工作流程答案的检查点 `404 CHECKPOINT_NOT_FOUND` 之前什么运行,运行 `/resume/stream` 流开前. 无 `workflowCheckpoints` 每一个回复 `404 CHECKPOINT_NOT_FOUND`。失败的运行会用它保存的最后一个检查点来回答它的错误 `checkpointId`旁边 `code` 财务报告和审定财务报表 `message` (单位:千美元)`{ error: { message, code, checkpointId } }`),且故障流发送 `workflow_failed` 事件为 `{ checkpointId }` 紧接着 `error` ,所以客户端从那里恢复失败运行 。 复职报告再次没有将检查站命名为要恢复的检查站,这是它自己的最后一个检查站,或者,当它没有拯救任何检查站时,就是它开始的检查站.

```typescript
import { PostgresCheckpointStore } from '@cogitator-ai/workflows';

const server = new CogitatorServer({
  app,
  cogitator,
  workflows: { shipping },
  config: { auth, workflowCheckpoints: new PostgresCheckpointStore({ client: pool }) },
});
```

### 斯瓦尔斯

```
GET    /api/swarms                    - List all swarms
POST   /api/swarms/:name/run          - Run swarm
POST   /api/swarms/:name/stream       - Stream swarm events
GET    /api/swarms/:name/blackboard   - Get configured blackboard sections
```

运行/ 流体 : `{ input: string; context?: object; threadId?: string; timeout?: number }` (单位:千美元)`timeout` 必须是积极的,最多 `2147483647` (中文(简体) ). 与 `limits.swarmTimeout` 设定,会保持更长的超时 和运行,没有人得到它, 和一团流出的时间答案 `504 RUN_TIMEOUT`。断开连接会中止群。 溪流传出 `{ type: 'swarm', event, data }` 活动(`agent_start`, (中文(简体) ), `agent_complete`, (中文(简体) ), `agent_error`, (中文(简体) ), `message`最终 `swarm_completed` 产出和使用情况。 每次运行都会产生出一团新鲜,因此黑板端点返回所配置的初始区段.

### 工具文档( D)

```
GET    /api/tools                     - List all tools
GET    /api/health                    - Health check ({ status, uptime, timestamp })
GET    /api/ready                     - Readiness probe ({ status, checks })
GET    /api/docs                      - Swagger UI
GET    /api/openapi.json              - OpenAPI spec
```

## 配置

```typescript
const server = new CogitatorServer({
  app,
  cogitator,
  agents: { chat: chatAgent, research: researchAgent },
  workflows: { 'code-review': codeReviewWorkflow },
  swarms: { 'dev-team': devTeamSwarm },
  config: {
    basePath: '/cogitator',
    enableWebSocket: true,
    enableSwagger: true,

    // Authentication — throw to reject (401); the returned context is available
    // as req.cogitator.auth and its userId is passed to agent runs.
    // Also applied to WebSocket upgrade requests.
    auth: async (req) => {
      const token = req.headers.authorization?.replace('Bearer ', '');
      const user = await validateToken(token);
      return { userId: user.id, roles: user.roles };
    },

    // Rate limiting (in-memory, per process). windowMs must be positive and max
    // non-negative, otherwise the constructor throws. Requests without a client
    // address share a single bucket.
    rateLimit: {
      windowMs: 60000, // 1 minute
      max: 100, // 100 requests per window
      trustProxy: false, // true: key by the nearest X-Forwarded-For hop
    },

    // CORS — credentials default to true for explicit origins and to false for '*'.
    // `origin: '*'` with `credentials: true` reflects any origin with credentials;
    // only do that for trusted, non-cookie setups.
    cors: {
      origin: ['https://myapp.com'],
      credentials: true,
    },

    // Swagger customization
    swagger: {
      title: 'My AI API',
      description: 'AI-powered API endpoints',
      version: '1.0.0',
    },

    // Caps on what a request may ask a run for. A larger value is held to the cap,
    // a request without one gets the cap. Checked when the server is created.
    limits: { swarmTimeout: 120_000, workflowMaxIterations: 50, workflowMaxConcurrency: 4 },

    // 'deny' answers 401 to callers without a userId. Health, ready and the docs stay open.
    anonymous: 'deny',

    // Called with the auth context and the thread of every request that names one.
    // false answers 403 THREAD_FORBIDDEN and no run starts.
    authorizeThread: (auth, threadId) => threadId.startsWith(`${auth?.userId}:`),

    // Dependencies for the tools of HTTP agent runs, streams and resumes (context.deps).
    // Built from the request on the server, never read from the body.
    deps: async (req) => ({ db: await tenantDb(req.get('x-tenant')) }),

    // WebSocket runs build theirs from the upgrade request, a plain Node IncomingMessage.
    websocket: { deps: async (req) => ({ db: await tenantDb(req.headers['x-tenant']) }) },
  },
});
```

### 多个用户

何时 `auth` 返回 a `userId`呼叫者用线条做的每件事都被设定范围:

-   代理运行和流 (REST 和 WebSocket) 通过 `userId` 改为 `cogitator.run()`,所以运行创建的线程属于该用户,并且是一个 `threadId` 由他人拥有的被拒绝.
-   `GET /threads/:id`, (中文(简体) ), `POST /threads/:id/messages` 财务报告和审定财务报表 `DELETE /threads/:id` 先检查线的主人; `POST` 到未知的id 创建了呼叫者拥有的线程.
-   另一个用户的线索答案 `403` 有代码 `THREAD_ACCESS_DENIED`,其信息既不返回也不更改.
-   线索创建较早, 没有所有者( 无) `userId`仅对无 `userId`,例如没有 `auth` 已配置 .
-   `authorizeThread(auth, threadId)` 在所有权之上增加你自己的规则,用于共享或团队线程. 它运行在每一条线条命名的路线上(代理跑道,流道和恢复道,群跑道和流道, `GET`, (中文(简体) ), `DELETE` 财务报告和审定财务报表 `POST /threads/:id/messages`)和在WebSocket上运行 与一个 `threadId`,在尸体验证后,在核心检查所有者之前。 答复 `false` 拒绝使用 `403 THREAD_FORBIDDEN`,没有运行开始.
-   `anonymous: 'deny'` 拒绝每个来电者 `auth` 给予无 `userId` 与 `401 UNAUTHORIZED`,包括了WebSocket的升级. `/health`, (中文(简体) ), `/ready`, (中文(简体) ), `/openapi.json` 财务报告和审定财务报表 `/docs` 保持开放。 基础路径答案下的未知路径 `401` 所以匿名电话员无法查清有哪条路 `404 NOT_FOUND` 一旦打电话者有 `userId`默认, `'allow'`,让匿名的来电者像以前一样进入,以及未知路径的答案 `404 NOT_FOUND` 在错误信封中.

### 限制和道具

`limits` 上限客户端可能要求运行的: `swarmTimeout` (单位:百万美元), `workflowMaxIterations` 财务报告和审定财务报表 `workflowMaxConcurrency`。从不因一个限制而拒绝请求:它得到的要求和限制的比HTTP和SSE都小,或者在它要求无时得到的限制。 OpenAPI spec显示每个限制作为其域的默认值. 每个限制必须是正整数,或者构造器所抛出.

`deps: (req) => deps` 将依赖关系代理工具构建为 `context.deps` (数据库控件、租户、客户端) HTTP 运行、流出或恢复 `deps`。它们被建在服务器上,用于每个请求,从不来自身体。 钩子所抛出的错误,在流开始前如跑出错误一样被回答.

WebSocket 运行并恢复,从 `websocket.deps: (req) => deps`,在每次运行时都随升级请求一起发出。 这个要求是一个简单的节点 `IncomingMessage` 从未通过快递,所以它已经 `headers`, (中文(简体) ), `url` 财务报告和审定财务报表 `socket` 但是没有 `req.get()` 中间的器件也没有添加任何内容 这就是为什么它得到自己的钩子。 无 `websocket.deps` WebSocket的运行没有道具. 它所抛出的错误( 或一个) `authorizeThread` 答案 `{ type: 'error', id, error }` 并开始不跑.

### 核准

一个工具与 `requiresApproval` (单位:千美元)`true`,或参数的函数)在转弯执行前暂停运行:

1.  `POST /api/agents/:name/run` 答案为 `status: 'paused'` 财务报告和审定财务报表 `pendingApprovals` (单位:千美元)`{ toolCallId, toolName, arguments, description, sideEffects? }`; 已完成运行 `status: 'completed'`。运行的检查点停留在服务器上,从不发送给客户端.
2.  `POST /api/agents/:name/stream` 发射 `{ type: 'approval-required', threadId, approvals }` 刚刚 `finish`。 。 。 .
3.  客户端显示待决的电话,并将答案发送给 `POST /api/agents/:name/resume`数字 :

```typescript
await fetch('/api/agents/support/resume', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    threadId,
    decisions: { [toolCallId]: { approved: true } },
    defaultDecision: { approved: false, reason: 'Not approved' },
  }),
});
```

躯体是 `{ threadId, decisions?, defaultDecision? }`,在决定是 `{ approved: boolean, reason?: string, updatedArguments?: object, remember?: boolean | { rule?: string, layer?: string } }` (见 [权限](https://cogitator.app/docs/tools/permissions#always-allow)); 畸形身体获得 `400 INVALID_INPUT`。反应的形状与 `/run` 并可能再次暂停 电话没有决定。 运行恢复为认证 `userId`: 无暂停的线索运行答案 `409 RUN_NOT_PAUSED`,另一个用户暂停运行 `403 THREAD_ACCESS_DENIED`通过 WebSocket, 发送 `{ type: 'resume', id, payload: { name, threadId, decisions?, defaultDecision? } }`; 暂停运行 `complete` 事件携带 `status` 财务报告和审定财务报表 `pendingApprovals`。 。 。 .

工具也可以向用户提问 `context.interrupt()`,它暂停运行的方式相同。 答案随即 `interrupts` (单位:千美元)`{ id, kind, reason, message, responseSchema?, expiresAt?, toolCallId?, toolName?, details? }`, (中文(简体) ), `kind` 一个 `input`, (中文(简体) ), `plan` 财务报告和审定财务报表 `budget`),溪流发出 `{ type: 'interrupt-required', threadId, interrupts }` 改为 `approval-required`,客户端则通过中断id恢复回答: `{ threadId, responses: { [id]: answer } }`,在HTTP和WebSocket上同样使用(`payload: { name, threadId, responses }`) (中文(简体) ). 与其 `responseSchema` 拒绝使用 `400 INTERRUPT_ANSWER_INVALID`后发送 `expiresAt` 与 `410 INTERRUPT_EXPIRED` (单位:千美元)`/resume/stream` 两者都作为 `error` 与此相关的事件 `code`) (中文(简体) ).

运行或流体也可以命名为 `multitaskStrategy` 对于已经运行在这个 Cogitator 中的线程 : `enqueue` 等待运行, `reject` 拒绝新运行 `THREAD_BUSY`, (中文(简体) ), `interrupt` 停止运行( 它失败) `RUN_INTERRUPTED`和 `rollback` 停止它和从它开始的树枝. `POST /agents/:name/run` 以 `409`。一条流已回答 `200` 发送 `start` 当它的运行遇到线条的其他运行, 所以它得到它们作为一个 `error` 事件代替( e)`{ type: 'error', message, code: 'THREAD_BUSY' }`),和一个WebSocket `run` 作为 `error` 边框。 没有一只,就是猎人 `multitaskStrategy` ,并拒绝使用 `400 INVALID_INPUT`。网络安全套 `run` 载荷也带走了 沼泽路线没有线条队列并忽略 `multitaskStrategy`。 。 。 .

`POST /agents/:name/resume/stream` 带着同样的身体和溪流 其他跑步一样 `/stream`。 。 。 . `start` 财务报告和审定财务报表 `finish` 将恢复的线程命名,在溪流打开前拒绝的一具尸体、代理或线被回答为JSON,就像 `/resume`。没有暂停运行的线程以一个 `error` 事件, `code: 'RUN_NOT_PAUSED'`,而断开连接会中止恢复.

## SSE 流线

这个 `/agents/:name/stream` 端点返回 Server- Sent 事件 :

```typescript
// Client-side
const response = await fetch('/api/agents/chat/stream', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ input: 'Hello!' }),
});

const reader = response.body!.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const lines = decoder.decode(value).split('\n');
  for (const line of lines) {
    if (line.startsWith('data: ') && line !== 'data: [DONE]') {
      const event = JSON.parse(line.slice(6));
      console.log(event);
      // { type: 'text-delta', id: '...', delta: 'Hello' }
    }
  }
}
```

当SSE流打开时,它写着 `: keep-alive` 注释每 `sseHeartbeatMs` (默认5 s, `0` 关闭)),由SSE客户端跳过,因此一个代理或负载平衡器不会关闭在慢工具或模型上等待的流(nginx关闭连接静默60 s).

活动 : `start`, (中文(简体) ), `text-start`编号 :`text-delta`编号 :`text-end`, (中文(简体) ), `tool-call-start`编号 :`tool-call-delta`编号 :`tool-call-end` (与模型的工具叫id), `tool-result` (单位:千美元)`toolCallId` 与呼叫相匹配, `approval-required`, (中文(简体) ), `interrupt-required`, (中文(简体) ), `error` (单位:千美元)`{ message, code }`),以及 `finish` 和运行的 `usage` (与JSON 答案相同的对象),然后 `data: [DONE]`。 。 。 .

当代理设置 `reasoning: { summary: true }` 并且提供者返回一个推理摘要, 它作为自己的部分流出 : `reasoning-start`, (中文(简体) ), `reasoning-delta` (单位:千美元)`{ id, delta }`, (中文(简体) ), `reasoning-end`。一个推理部分总是在文本或工具调用开始前关闭,因此推理部分和文本部分永远不会互通.

## 可恢复流

与 `resumableStreams` 一个客户端丢失了 `/agents/:name/stream` 或者说 `/agents/:name/resume/stream` (a flashy network,一个去睡觉的标签) 得到它错过的东西, 运行在它离开的时候进行:

```typescript
const server = new CogitatorServer({
  app,
  cogitator,
  agents: { chat: chatAgent },
  config: {
    resumableStreams: true,
  },
});
```

-   每个事件都带有SSE `id`, (中文(简体) ), `<streamId>:<seq>` 与 `seq` 从1起。 心跳的评论没有.
-   一个断开连接的客户端不再停止运行. 它的事件不断进入商店.
-   同样的路线 `Last-Event-ID: <id>` 重新连接而不是启动运行, 并且身体没有被读取: 答案用其 id 重放之后的事件, 然后跟踪运行到 `finish` (和 `data: [DONE]`)或(或) `error`. 若干客户可能遵循一个流程. `?lastEventId=<id>` 对无法设置头部的客户端也起作用,但它将ID放入了URL和访问日志,因此更倾向于头部.
-   `DELETE /agents/:name/stream/:streamId` 取消运行( e)`204`:它的溪流以一个 `error` 事件 `STREAM_CANCELLED`。溪流 id 是事件 ID 在上次事件之前的一部分 `:`。 。 。 .
-   一条溪流与发起它的呼唤者相连: `auth` 返回(用户代号、角色、权限、元数据,例如租户),所以没有用户代号的呼叫者也被分开。 给 `owner: (auth) => key` 将其绑入更少的字段,例如当身份带有符号过期时,仅用户的ID。 任何人,未知或已过期的溪流和事件 ID该溪流从来没有发送得到 `404 STREAM_NOT_FOUND` 在任何 SSE 标题之前, 代码不是 `<streamId>:<seq>` 获得 `400 INVALID_INPUT`。 。 。 . `authorizeThread` 每次重新连接和取消时, 都会询问溪流的线程( t)`403 THREAD_FORBIDDEN`) (中文(简体) ).
-   不再保存的事件后重新连接( 多于 `maxEvents` 从此以后 `error` 事件为 `STREAM_RESUME_GAP`:取出线程(`GET /threads/:id`)来追. 一个读取缓慢的客户端最多只能持有 `maxEvents` 事件和追赶 从商店.
-   跑步没人跟着 `detachedTimeoutMs` (默认为5分钟)被停止,所以一个再也没有回来的客户不会运行成本上升,每一次运行在 `until` 中止( 通过信号, 您的服务器停止, 并提前中止) `server.close()`等待着溪流的开通。 这样的流以 `error` 事件 `STREAM_ABORTED`。 。 。 .
-   一条奔跑的溪流永远不会被赶走: `maxStreams` 溪流都继续,一个新的 拒绝与 `503 STREAM_LIMIT_REACHED`。 。 。 .

`true` 将事件保存在进程记忆中:每条溪流有1000个事件,一个完成的溪流5分钟,1000个溪流一次(`maxEvents`, (中文(简体) ), `retentionMs`, (中文(简体) ), `maxStreams`) (中文(简体) ). 负载平衡器后面有多个进程,通过 a `store` 共享(`StreamReplayStore` 从 `@cogitator-ai/server-shared`, 由 Redis 或 类似 的 pub/ sub 支持), 因此重新连接或取消, 在另一个进程上着陆到达了溪流及其运行. 默认关闭 : 没有 ID, 而客户端中断运行 .

## WebSocket 支持

启用实时双向通信( 需要可选 `ws` 软件包 :

```typescript
import { createServer } from 'http';

const server = new CogitatorServer({
  // ...
  config: {
    enableWebSocket: true,
    websocket: { path: '/api/ws', pingInterval: 30000, maxPayloadSize: 1024 * 1024 },
  },
});

await server.init();

const httpServer = createServer(app);
await server.attachWebSocket(httpServer); // defaults to `${basePath}/ws`
httpServer.listen(3000);
```

`attachWebSocket` 丢出,如果 `enableWebSocket` 关闭, `init()` 未运行,或 `ws` 未安装。 配置 `auth` 函数运行在升级请求上(在扔出时以401被拒绝,或当它给出不时) `userId` 下级 `anonymous: 'deny'`A级 `run` 或者说 `resume` 在线条上 `authorizeThread` 拒绝答案 `{ type: 'error', id, error }` 没有开始,和 `websocket.deps` 与升级请求一起调用。 对于定制设置, `setupWebSocket(httpServer, routeContext, config)` 也被导出.

议定书:

| 消息 | 方向 | 说明 |
| --- | --- | --- |
| `{ type: 'run', id, payload: { type: 'agent', name, input, context?, threadId? } }` | 客户-服务员 | 运行代理; 事件到达为 `{ type: 'event', id, payload }` (单位:千美元)`token`, (中文(简体) ), `reasoning`, (中文(简体) ), `tool-call`, (中文(简体) ), `tool-result`, (中文(简体) ), `complete`, (中文(简体) ), `cancelled`(中文(简体) ) |
| `{ type: 'resume', id, payload: { name, threadId, decisions?, defaultDecision? } }` | 客户-服务员 | 恢复已暂停的运行, 以便工具批准; 事件如 `run` |
| `{ type: 'stop' }` | 客户-服务员 | 中止客户端的运行代理( emits) `cancelled`(中文(简体) ) |
| `{ type: 'subscribe', channel: 'agent:<name>' }` | 客户-服务员 | 接收由同一用户在别处启动的该代理的运行事件, 运行在线索上 `authorizeThread` 允许 |
| `{ type: 'unsubscribe', channel }` | 客户-服务员 | 停止接收频道事件 |
| `{ type: 'ping' }` | 客户-服务员 | 答复 `{ type: 'pong' }` |
| `{ type: 'subscribed' | 'unsubscribed', id, channel }` | 服务器 — 客户端 | 订阅确认(每次连接最多64个频道) |
| `{ type: 'error', id?, error }` | 服务器 — 客户端 | 无效的消息或有效载荷, 未知代理, 或运行失败 |

只有代理运行在WebSocket上得到支持,使用HTTP端点进行工作流程和群集. 一个新的 `run` 从同一连接中止上一个连接。 运行失败报告 a `CogitatorError`'消息,任何其他错误都会被记录并发送为 `Internal server error`。服务器按 `pingInterval` 和终止客户 错过了一磅.

订阅者只得到运行时的事件 `userId` 由召来者发起 `authorizeThread` 设置, 当 `authorizeThread(subscriberAuth, threadId)` 允许在运行的线条上进行 `threadId` 或为它而创造的一个核心。 答案是每个用户一次被问到并运行,一个用户拒绝,或丢弃,没有获得运行事件. 无 `authorizeThread`,没有 `userId` 分享彼此运行的事件, 所以设置 `authorizeThread` 或者说 `anonymous: 'deny'` 匿名者不得见面.

客户端使用量 :

```typescript
const ws = new WebSocket('ws://localhost:3000/api/ws');

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log(message);
};

// Run agent
ws.send(
  JSON.stringify({
    type: 'run',
    id: 'req-1',
    payload: {
      type: 'agent',
      name: 'chat',
      input: 'Hello!',
    },
  })
);
```

## 自定义中端软件

使用内置的中件工厂或创建自己的:

```typescript
import {
  createAuthMiddleware,
  createRateLimitMiddleware,
  createCorsMiddleware,
  errorHandler,
} from '@cogitator-ai/express';

// Use individually
app.use(
  '/api',
  createAuthMiddleware(async (req) => {
    // Custom auth logic
  })
);

app.use(
  '/api',
  createRateLimitMiddleware({
    windowMs: 60000,
    max: 100,
    keyGenerator: (req) => req.headers['x-api-key'] as string,
    trustProxy: false, // set true if behind a trusted reverse proxy
  })
);
```

## 自定义路由

创建有流支持的自定义路由 :

```typescript
import { Router } from 'express';
import { ExpressStreamWriter, setupSSEHeaders, generateId } from '@cogitator-ai/express';

const router = Router();

router.post('/custom/stream', async (req, res) => {
  setupSSEHeaders(res);
  const writer = new ExpressStreamWriter(res);
  const messageId = generateId('msg');
  const textId = generateId('txt');

  // Use res.on('close') (not req.on('close'), which fires once the body is read)
  res.on('close', () => writer.close());

  writer.start(messageId);
  writer.textStart(textId);

  // Your streaming logic
  writer.textDelta(textId, 'Hello ');
  writer.textDelta(textId, 'World!');

  writer.textEnd(textId);
  writer.finish(messageId);
  writer.close();
});
```

## API 参考

### 协作者服务员

```typescript
class CogitatorServer {
  constructor(options: CogitatorServerConfig);
  init(): Promise<void>;
  attachWebSocket(server: http.Server): Promise<WebSocketServer>;
  readonly isInitialized: boolean;
}
```

### 协作者服务器配置

```typescript
interface CogitatorServerConfig {
  app: Router;
  cogitator: Cogitator;
  agents?: Record<string, Agent>;
  workflows?: Record<string, Workflow>;
  swarms?: Record<string, SwarmConfig>;
  config?: {
    basePath?: string; // Default: '/cogitator'
    enableWebSocket?: boolean; // Default: false
    enableSwagger?: boolean; // Default: true
    auth?: AuthFunction;
    rateLimit?: RateLimitConfig;
    cors?: CorsConfig;
    swagger?: SwaggerConfig;
    websocket?: WebSocketConfig;
    sseHeartbeatMs?: number; // Default: 5000, 0 turns SSE heartbeats off
    acceptContext?: boolean | string[]; // context keys clients may send: none by default, a list, or true for any
    threadMessageRoles?: ('user' | 'assistant' | 'system')[]; // roles of POST /threads/:id/messages, default user and assistant
    limits?: RunLimits; // { swarmTimeout?, workflowMaxIterations?, workflowMaxConcurrency? }
    anonymous?: 'allow' | 'deny'; // Default: 'allow'
    authorizeThread?: (
      auth: AuthContext | undefined,
      threadId: string
    ) => boolean | Promise<boolean>;
    deps?: (req: Request) => unknown | Promise<unknown>; // HTTP runs, WebSocket runs use websocket.deps
    workflowCheckpoints?: CheckpointStore; // workflow checkpoints and /resume, none by default
    resumableStreams?: boolean | ResumableStreamOptions; // see Resumable streams, off by default
  };
}

interface WebSocketConfig {
  path?: string; // Default: `${basePath}/ws`
  pingInterval?: number; // Default: 30000
  maxPayloadSize?: number; // Default: 1 MiB
  deps?: (req: IncomingMessage) => unknown | Promise<unknown>; // deps of WebSocket runs, from the upgrade request
}
```

`CogitatorServer` 当控制台启动时, 它不会写给它 . `init()` 和 WebSocket 设置报告 `debug` 职等(`getLogger()` 从 `@cogitator-ai/core`, (中文(简体) ), `LOG_LEVEL=debug` 看到他们,或 `setLogger()` 引导它们进入自己的日志。 错误仍在记录中 .

### 快报

```typescript
class ExpressStreamWriter {
  constructor(res: Response);
  start(messageId: string): void;
  textStart(id: string): void;
  textDelta(id: string, delta: string): void;
  textEnd(id: string): void;
  reasoningStart(id: string): void;
  reasoningDelta(id: string, delta: string): void;
  reasoningEnd(id: string): void;
  toolCallStart(id: string, toolName: string): void;
  toolCallDelta(id: string, argsTextDelta: string): void;
  toolCallEnd(id: string): void;
  toolResult(id: string, toolCallId: string, result: unknown): void;
  approvalRequired(threadId: string, approvals: readonly PendingApproval[]): void;
  workflowEvent(event: string, data: unknown): void;
  swarmEvent(event: string, data: unknown): void;
  error(message: string, code?: string): void;
  finish(messageId: string, usage?: Usage): void;
  close(): void;
  readonly isClosed: boolean;
}
```

回复结束后的写作(或客户端已断开)被忽略.

## 处理错误

所有端点返回一致的错误反应 :

```json
{
  "error": {
    "message": "Agent 'unknown' not found",
    "code": "NOT_FOUND"
  }
}
```

HTTP 状态代码错误代码映射 :

-   `INVALID_INPUT` - 400个(鉴定错误和JSON身体缺陷)
-   `UNAUTHORIZED` 第401条
-   `THREAD_ACCESS_DENIED` ^ 403(另一个用户的线程)
-   `THREAD_FORBIDDEN` ——403(`authorizeThread` 拒绝线索)
-   `NOT_FOUND` 第404条
-   `RUN_NOT_PAUSED` ~ 409 (恢复线程而不暂停运行)
-   `PAYLOAD_TOO_LARGE` − 413个
-   `RATE_LIMIT_EXCEEDED` - 429个
-   `INTERNAL_ERROR` ——500个
-   `UNIMPLEMENTED` + 501(缺少可选工作流程/群包)
-   `UNAVAILABLE` ~ 503 (没有配置内存)
-   `RUN_TIMEOUT` • 504(一群人没有时间)

A 类 `CogitatorError` 保留它的信息、代码和答案 `statusCode`,即代码的映射状态,除非错误被赋予了另一个(例如) `LLM_RATE_LIMITED` - 429页)。 当它说什么时候重试(`retryAfter`,正如供应商费率限制所做的那样,答案带有 `Retry-After` 在整秒钟。 地图是 `describeHttpError` 联 合 国 `@cogitator-ai/server-shared`,所以快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快取快 服务器上记录了任何其他错误并回答为 `500 Internal server error`,因此连接字符串,文件路径和其他内部内容永远无法到达客户端. 同样的映射适用于一个失败的工作流程, 同样的遮盖到SSE `error` 事件、 `node_error`编号 :`agent_error` 流事件和 WebSocket 运行错误.

## 文档

全向导 : [cogitator.app/docs/服务器-适应器/表达式](https://cogitator.app/docs/server-adapters/express). 工具核准: [核准/文件/工具/核准](https://cogitator.app/docs/tools/approvals)。 。 。 .

## 许可证

麻省理工学院

## 常见问题

### @cogitator-ai/express 是什么？

Express\.js 用于 Cogitator AI 运行时间的服务器适配器

### 如何安装 @cogitator-ai/express？

在项目目录运行 npm install @cogitator-ai/express，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：express（\>=4\.18\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @cogitator-ai/express 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @cogitator-ai/express 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @cogitator-ai/express 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.12\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @cogitator-ai/express 可以商用吗？

@cogitator-ai/express 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @cogitator-ai/express 的下载量有多少？

根据 npm 官方下载统计，@cogitator-ai/express 在 2026/10/11 – 2026/11/09 这 30 天内被下载 9,865 次，日均约 329 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @cogitator-ai/express 还在维护吗？

最新版本 0\.16\.1 发布于 2026/10/29（12 天前）；至今共发布 89 个版本，其中过去 12 个月发布了 89 个；首次发布于 2026/01/27。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

