|
|
@@ -0,0 +1,391 @@
|
|
|
+// 对外 API(/v1)协议参考:后台"API 协议"页面与 PDF 的唯一数据来源。
|
|
|
+// test/api-reference.test.ts 会核对这里列出的接口与实际注册的 /v1 路由完全一致,新增或删除接口时必须同步修改。
|
|
|
+
|
|
|
+export interface Field {
|
|
|
+ name: string
|
|
|
+ type: string
|
|
|
+ required?: boolean
|
|
|
+ desc: string
|
|
|
+}
|
|
|
+
|
|
|
+export interface Endpoint {
|
|
|
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE'
|
|
|
+ path: string
|
|
|
+ summary: string
|
|
|
+ desc?: string
|
|
|
+ query?: Field[]
|
|
|
+ body?: Field[]
|
|
|
+ /** 请求体不是 JSON 时的说明(如 multipart) */
|
|
|
+ bodyNote?: string
|
|
|
+ response: { status: number; type: string; desc?: string }
|
|
|
+ errors?: string[]
|
|
|
+ example?: string
|
|
|
+}
|
|
|
+
|
|
|
+export interface EndpointGroup {
|
|
|
+ id: string
|
|
|
+ title: string
|
|
|
+ desc?: string
|
|
|
+ endpoints: Endpoint[]
|
|
|
+}
|
|
|
+
|
|
|
+export interface TypeDef {
|
|
|
+ name: string
|
|
|
+ desc?: string
|
|
|
+ fields: Field[]
|
|
|
+}
|
|
|
+
|
|
|
+export interface EventDef {
|
|
|
+ type: string
|
|
|
+ payload: string
|
|
|
+ desc: string
|
|
|
+ persisted: boolean
|
|
|
+}
|
|
|
+
|
|
|
+export const API_GROUPS: EndpointGroup[] = [
|
|
|
+ {
|
|
|
+ id: 'auth',
|
|
|
+ title: '令牌',
|
|
|
+ desc: '业务平台的后端用 API Key 换取短期浏览器令牌,交给前端使用;API Key 不要出现在前端代码中。',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'POST', path: '/v1/browser-tokens', summary: '签发浏览器令牌',
|
|
|
+ desc: '只能使用 API Key 调用(浏览器令牌不能再签发令牌)。令牌的权限与签发它的 API Key 相同;停用或删除 API Key 后,它签发的全部令牌立即失效。',
|
|
|
+ body: [{ name: 'ttlSeconds', type: 'number', desc: '有效期(秒),60 ~ 43200,默认 3600' }],
|
|
|
+ response: { status: 201, type: 'BrowserToken' },
|
|
|
+ errors: ['400 bad_request:ttlSeconds 超出范围', '403 forbidden:使用浏览器令牌调用'],
|
|
|
+ example: `curl -X POST $BASE/v1/browser-tokens \\
|
|
|
+ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \\
|
|
|
+ -d '{"ttlSeconds":3600}'
|
|
|
+# → 201 {"token":"bt_…","expiresAt":"2026-10-23T14:00:00.000Z"}`,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'catalog',
|
|
|
+ title: 'Profile 与引擎',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/profiles', summary: '本平台可用的 Profile',
|
|
|
+ desc: '只返回在后台为该 API Key 勾选的 Profile。isDefault 表示该平台的默认 Profile(创建会话不指定 Profile 时使用)。',
|
|
|
+ response: { status: 200, type: 'ProfileSummary[]' },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/providers', summary: '引擎状态与可选模型',
|
|
|
+ desc: '结果缓存 30 秒。',
|
|
|
+ response: { status: 200, type: 'ProviderStatus[]' },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'sessions',
|
|
|
+ title: '会话',
|
|
|
+ desc: '一个会话对应一个独立的工作区和一段连续的对话。会话归属于创建它的 API Key,其他平台看不到。',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'POST', path: '/v1/sessions', summary: '创建会话',
|
|
|
+ desc: '引擎和权限模式由 Profile 决定,不能在这里指定;模型可以在 Profile 引擎的可选模型中自选。',
|
|
|
+ body: [
|
|
|
+ { name: 'profileId', type: 'string', desc: '使用的 Profile,必须是本平台可用的;省略时使用默认 Profile' },
|
|
|
+ { name: 'model', type: 'string', desc: '模型,省略时使用 Profile 的默认模型' },
|
|
|
+ { name: 'title', type: 'string', desc: '标题(≤ 200 字),省略时用第一条消息自动生成' },
|
|
|
+ { name: 'systemPromptAppend', type: 'string', desc: '追加在 Profile 系统提示词之后的会话级提示(≤ 20000 字)' },
|
|
|
+ ],
|
|
|
+ response: { status: 201, type: 'Session' },
|
|
|
+ errors: ['403 profile_not_allowed:Profile 不在本平台可用列表中', '400 bad_request:模型不可用、引擎未启用,或传入了 provider / permissionMode'],
|
|
|
+ example: `curl -X POST $BASE/v1/sessions -H "Authorization: Bearer $TOKEN" \\
|
|
|
+ -H "Content-Type: application/json" -d '{"title":"三季度销售分析"}'`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/sessions', summary: '会话列表',
|
|
|
+ desc: '按最近更新时间倒序,游标分页。',
|
|
|
+ query: [
|
|
|
+ { name: 'limit', type: 'number', desc: '每页数量,1 ~ 200,默认 50' },
|
|
|
+ { name: 'cursor', type: 'string', desc: '上一页返回的 nextCursor' },
|
|
|
+ { name: 'archived', type: '"true" | "false"', desc: '只看已归档 / 未归档;省略时全部' },
|
|
|
+ ],
|
|
|
+ response: { status: 200, type: 'Page<Session>' },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/sessions/:id', summary: '会话详情',
|
|
|
+ desc: '包含正在运行的 Run 和未处理的交互,用于页面刷新后恢复状态。',
|
|
|
+ response: { status: 200, type: 'SessionDetail' },
|
|
|
+ errors: ['404 not_found', '403 profile_not_allowed:会话使用的 Profile 已不再对本平台开放'],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'PATCH', path: '/v1/sessions/:id', summary: '修改会话',
|
|
|
+ body: [
|
|
|
+ { name: 'title', type: 'string', desc: '标题' },
|
|
|
+ { name: 'archived', type: 'boolean', desc: '归档 / 取消归档' },
|
|
|
+ { name: 'model', type: 'string', desc: '更换模型(空闲时)' },
|
|
|
+ { name: 'systemPromptAppend', type: 'string', desc: '会话级追加提示(空闲时)' },
|
|
|
+ { name: 'profileId', type: 'string', desc: '更换 Profile(只能在还没有对话时)' },
|
|
|
+ ],
|
|
|
+ response: { status: 200, type: 'Session' },
|
|
|
+ errors: ['409 session_busy:运行中不能修改模型、提示词或 Profile', '400 bad_request:已经开始对话后更换 Profile'],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'DELETE', path: '/v1/sessions/:id', summary: '删除会话',
|
|
|
+ desc: '同时删除工作区中的全部文件,不可恢复。',
|
|
|
+ response: { status: 204, type: '—' },
|
|
|
+ errors: ['409 session_busy:运行中,请先中断'],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'messages',
|
|
|
+ title: '消息与运行',
|
|
|
+ desc: '发送消息会创建一个 Run(Agent 的一次执行)。同一会话同时只能有一个 Run;结果通过事件流推送。',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'POST', path: '/v1/sessions/:id/messages', summary: '发送消息',
|
|
|
+ body: [
|
|
|
+ { name: 'text', type: 'string', required: true, desc: '消息内容(1 ~ 100000 字)' },
|
|
|
+ { name: 'attachments', type: 'string[]', desc: '附件的 fileId(先通过上传接口上传);图片会直接交给模型识别' },
|
|
|
+ ],
|
|
|
+ response: { status: 202, type: 'Run', desc: 'Run 已排队,之后的进展通过事件流获得' },
|
|
|
+ errors: ['409 session_busy:上一个 Run 还没结束', '409 provider_unavailable:引擎当前不可用', '404 not_found:附件不存在'],
|
|
|
+ example: `curl -X POST $BASE/v1/sessions/$SID/messages -H "Authorization: Bearer $TOKEN" \\
|
|
|
+ -H "Content-Type: application/json" \\
|
|
|
+ -d '{"text":"分析附件中的销售数据并生成 Word 报告","attachments":["file_…"]}'`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/runs/:id', summary: 'Run 状态与用量',
|
|
|
+ response: { status: 200, type: 'Run' },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'POST', path: '/v1/runs/:id/interrupt', summary: '中断 Run',
|
|
|
+ desc: '事件流中会出现 run.interrupted。',
|
|
|
+ response: { status: 202, type: '{ ok: true }' },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'events',
|
|
|
+ title: '事件',
|
|
|
+ desc: '会话中发生的一切(用户消息、Agent 文本、工具调用、产出文件、审批、用量)都以事件的形式按顺序记录,seq 在会话内单调递增。',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/sessions/:id/stream', summary: '订阅事件流(SSE)',
|
|
|
+ desc: '推荐每个打开的会话保持一条连接。先补发 seq 大于 Last-Event-ID 的历史事件,再推送实时事件;每 15 秒发送一次 ": ping" 保活。浏览器原生 EventSource 不能设置 Authorization 头,请使用 SDK 的 client.subscribe(fetch 实现,自动断线续传)。',
|
|
|
+ query: [{ name: 'after', type: 'number', desc: '从哪个 seq 之后开始(也可以用请求头 Last-Event-ID,优先)' }],
|
|
|
+ response: { status: 200, type: 'text/event-stream', desc: '每条事件:id: <seq>、event: <type>、data: <AgentEvent JSON>' },
|
|
|
+ example: `id: 12
|
|
|
+event: message.delta
|
|
|
+data: {"seq":12,"type":"message.delta","sessionId":"ses_…","runId":"run_…","ts":"…","payload":{"messageId":"msg_…","text":"好的"}}`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/sessions/:id/events', summary: '拉取历史事件',
|
|
|
+ desc: '用于打开会话时渲染完整时间线。高频的增量事件(message.delta、reasoning.delta)不落库,历史中只有对应的 completed 事件。',
|
|
|
+ query: [
|
|
|
+ { name: 'after', type: 'number', desc: '只返回 seq 大于该值的事件,默认 0' },
|
|
|
+ { name: 'limit', type: 'number', desc: '最多返回条数,≤ 5000' },
|
|
|
+ ],
|
|
|
+ response: { status: 200, type: '{ items: AgentEvent[]; lastSeq: number }' },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'interactions',
|
|
|
+ title: '交互(审批与提问)',
|
|
|
+ desc: 'Agent 需要执行有风险的操作(命令、写文件)或需要用户做选择时,事件流中出现 interaction.required,Run 进入 waiting_input;回复后 Agent 继续。超时(默认 10 分钟)按拒绝处理。',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/interactions', summary: '未处理的交互',
|
|
|
+ query: [{ name: 'sessionId', type: 'string', required: true, desc: '会话 ID' }],
|
|
|
+ response: { status: 200, type: 'InteractionRequest[]' },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'POST', path: '/v1/interactions/:id', summary: '回复交互',
|
|
|
+ desc: '审批回复 decision;提问回复 answers(键为问题 id,多选时为数组)。拒绝时的 reason 会转告 Agent。',
|
|
|
+ bodyNote: 'InteractionResponse(三选一):{ decision: "allow_once" | "allow_session" } | { decision: "deny", reason? } | { answers: { [questionId]: string | string[] } }',
|
|
|
+ response: { status: 200, type: '{ ok: true }' },
|
|
|
+ errors: ['409 already_resolved:已被回复、超时或取消', '404 not_found'],
|
|
|
+ example: `curl -X POST $BASE/v1/interactions/$ITX -H "Authorization: Bearer $TOKEN" \\
|
|
|
+ -H "Content-Type: application/json" -d '{"decision":"allow_once"}'`,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'files',
|
|
|
+ title: '文件',
|
|
|
+ desc: '上传的文件保存在会话工作区的 uploads/,Agent 的产出一般在 outputs/。Agent 新建或修改文件时事件流中出现 file.changed。',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'POST', path: '/v1/sessions/:id/files', summary: '上传文件',
|
|
|
+ bodyNote: 'multipart/form-data,每个文件一个 file 字段;一次最多 20 个,单个文件不超过后台设置的上限(默认 50 MB)。同名文件自动改名。',
|
|
|
+ response: { status: 201, type: 'File[]' },
|
|
|
+ errors: ['413 file_too_large', '400 bad_request:没有收到文件'],
|
|
|
+ example: `curl -X POST $BASE/v1/sessions/$SID/files -H "Authorization: Bearer $TOKEN" \\
|
|
|
+ -F "file=@销售.xlsx" -F "file=@截图.png"`,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/sessions/:id/files', summary: '工作区文件列表',
|
|
|
+ desc: '包括上传的文件和 Agent 产出的文件。',
|
|
|
+ response: { status: 200, type: 'File[]' },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/files/:id', summary: '下载文件',
|
|
|
+ desc: '默认以附件下载;inline=1 用于在页面中预览(HTML / SVG 预览时禁止执行脚本)。',
|
|
|
+ query: [{ name: 'inline', type: '"1"', desc: '内联预览' }],
|
|
|
+ response: { status: 200, type: '文件内容', desc: 'Content-Type 为文件类型,Content-Disposition 带 UTF-8 文件名' },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'usage',
|
|
|
+ title: '用量',
|
|
|
+ endpoints: [
|
|
|
+ {
|
|
|
+ method: 'GET', path: '/v1/usage', summary: '本平台的用量',
|
|
|
+ desc: '只统计调用者所属 API Key 的会话;删除会话不影响历史用量。日期为服务器本地日期。',
|
|
|
+ query: [{ name: 'days', type: 'number', desc: '最近几天(含今天),1 ~ 366,默认 30' }],
|
|
|
+ response: { status: 200, type: 'UsageSummary' },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+]
|
|
|
+
|
|
|
+export const API_TYPES: TypeDef[] = [
|
|
|
+ {
|
|
|
+ name: 'BrowserToken', fields: [
|
|
|
+ { name: 'token', type: 'string', desc: '以 bt_ 开头,放在 Authorization: Bearer 中使用' },
|
|
|
+ { name: 'expiresAt', type: 'string', desc: '过期时间(ISO 8601)' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'ProfileSummary', fields: [
|
|
|
+ { name: 'id', type: 'string', desc: 'prf_ 开头' },
|
|
|
+ { name: 'name / description', type: 'string', desc: '名称与说明' },
|
|
|
+ { name: 'provider', type: '"claude" | "codex"', desc: '引擎' },
|
|
|
+ { name: 'model', type: 'string', desc: '默认模型' },
|
|
|
+ { name: 'permissionMode', type: '"ask" | "auto" | "readonly"', desc: '需审批 / 自动 / 只读' },
|
|
|
+ { name: 'isDefault', type: 'boolean', desc: '是否为本平台的默认 Profile' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'ProviderStatus', fields: [
|
|
|
+ { name: 'id', type: '"claude" | "codex"', desc: '' },
|
|
|
+ { name: 'enabled / available', type: 'boolean', desc: '已启用 / 当前可用(已登录)' },
|
|
|
+ { name: 'models', type: 'string[]', desc: '可选模型' },
|
|
|
+ { name: 'defaultModel', type: 'string', desc: '默认模型' },
|
|
|
+ { name: 'message', type: 'string | null', desc: '不可用的原因' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'Session', fields: [
|
|
|
+ { name: 'id', type: 'string', desc: 'ses_ 开头' },
|
|
|
+ { name: 'title', type: 'string', desc: '' },
|
|
|
+ { name: 'provider / model', type: 'string', desc: '引擎与模型' },
|
|
|
+ { name: 'profileId', type: 'string | null', desc: '' },
|
|
|
+ { name: 'status', type: '"idle" | "running" | "waiting_input"', desc: '空闲 / 运行中 / 等待回复交互' },
|
|
|
+ { name: 'archived', type: 'boolean', desc: '' },
|
|
|
+ { name: 'lastSeq', type: 'number', desc: '最后一个事件的 seq' },
|
|
|
+ { name: 'createdAt / updatedAt', type: 'string', desc: 'ISO 8601' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'SessionDetail', desc: 'Session 的全部字段,另外包括:', fields: [
|
|
|
+ { name: 'activeRunId', type: 'string | null', desc: '正在运行的 Run' },
|
|
|
+ { name: 'pendingInteractions', type: 'InteractionRequest[]', desc: '未处理的交互' },
|
|
|
+ { name: 'permissionMode', type: 'string', desc: '实际生效的权限模式' },
|
|
|
+ { name: 'systemPromptAppend', type: 'string', desc: '会话级追加提示' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'Page<T>', fields: [
|
|
|
+ { name: 'items', type: 'T[]', desc: '' },
|
|
|
+ { name: 'nextCursor', type: 'string | null', desc: '下一页的游标,null 表示没有更多' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'Run', fields: [
|
|
|
+ { name: 'id', type: 'string', desc: 'run_ 开头' },
|
|
|
+ { name: 'sessionId', type: 'string', desc: '' },
|
|
|
+ { name: 'status', type: '"queued" | "running" | "waiting_input" | "completed" | "failed" | "interrupted"', desc: '' },
|
|
|
+ { name: 'startedAt / endedAt', type: 'string | null', desc: '' },
|
|
|
+ { name: 'usage', type: 'Usage | null', desc: '本次 Run 的 token 用量' },
|
|
|
+ { name: 'error', type: 'string | null', desc: '失败原因' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'File', fields: [
|
|
|
+ { name: 'id', type: 'string', desc: 'file_ 开头' },
|
|
|
+ { name: 'path', type: 'string', desc: '工作区中的相对路径,如 uploads/销售.xlsx、outputs/报告.docx' },
|
|
|
+ { name: 'origin', type: '"upload" | "agent"', desc: '用户上传 / Agent 产出' },
|
|
|
+ { name: 'mime / size', type: 'string / number', desc: '类型与字节数' },
|
|
|
+ { name: 'createdAt / updatedAt', type: 'string', desc: '' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'Usage', fields: [
|
|
|
+ { name: 'inputTokens / outputTokens', type: 'number', desc: '' },
|
|
|
+ { name: 'cacheReadTokens / cacheWriteTokens', type: 'number', desc: '' },
|
|
|
+ { name: 'costUsd', type: 'number?', desc: '估算费用(订阅登录下不是实际扣费)' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'UsageSummary', fields: [
|
|
|
+ { name: 'from / to', type: 'string', desc: '统计区间(YYYY-MM-DD)' },
|
|
|
+ { name: 'total / today', type: 'UsageTotals', desc: '区间合计 / 今天' },
|
|
|
+ { name: 'daily', type: '({ date } & UsageTotals)[]', desc: '每天一项,没有用量的日期也会列出' },
|
|
|
+ { name: 'byModel / byProvider', type: 'array', desc: '按模型 / 按引擎汇总' },
|
|
|
+ { name: 'UsageTotals', type: 'object', desc: 'runs、failed、interrupted、inputTokens、outputTokens、cacheReadTokens、cacheWriteTokens、costUsd、durationMs' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'AgentEvent', desc: '所有事件的信封', fields: [
|
|
|
+ { name: 'seq', type: 'number', desc: '会话内单调递增,即 SSE 的 id' },
|
|
|
+ { name: 'type', type: 'string', desc: '事件类型,见"事件类型"' },
|
|
|
+ { name: 'sessionId', type: 'string', desc: '' },
|
|
|
+ { name: 'runId', type: 'string | null', desc: '所属 Run' },
|
|
|
+ { name: 'ts', type: 'string', desc: 'ISO 8601' },
|
|
|
+ { name: 'payload', type: 'object', desc: '随类型不同' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'InteractionRequest', desc: 'kind 为 approval(审批)或 question(提问)', fields: [
|
|
|
+ { name: 'interactionId', type: 'string', desc: 'itx_ 开头' },
|
|
|
+ { name: 'kind', type: '"approval" | "question"', desc: '' },
|
|
|
+ { name: 'expiresAt', type: 'string', desc: '超时时间,超时按拒绝处理' },
|
|
|
+ { name: 'tool / title / input', type: 'approval', desc: '要执行的工具、可读摘要、原始参数' },
|
|
|
+ { name: 'detail', type: 'approval', desc: '{ command?, path?, url?, reason? }' },
|
|
|
+ { name: 'options', type: 'approval', desc: '可选的决定:allow_once(允许一次)、allow_session(本会话内允许)、deny' },
|
|
|
+ { name: 'questions', type: 'question', desc: '[{ id, question, header?, multiSelect, options: [{ label, description? }] }]' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+]
|
|
|
+
|
|
|
+export const API_EVENTS: EventDef[] = [
|
|
|
+ { type: 'user.message', payload: '{ text, attachments: [{ fileId, name, mime, size }] }', desc: '用户发送的消息', persisted: true },
|
|
|
+ { type: 'run.queued', payload: '{ position }', desc: 'Run 排队中', persisted: true },
|
|
|
+ { type: 'run.started', payload: '{ provider, model }', desc: 'Run 开始执行', persisted: true },
|
|
|
+ { type: 'message.delta', payload: '{ messageId, text }', desc: 'Agent 文本增量(流式)', persisted: false },
|
|
|
+ { type: 'message.completed', payload: '{ messageId, text }', desc: 'Agent 一段完整的文本', persisted: true },
|
|
|
+ { type: 'reasoning.delta', payload: '{ blockId, text }', desc: '思考过程增量(建议默认折叠)', persisted: false },
|
|
|
+ { type: 'reasoning.completed', payload: '{ blockId, text }', desc: '一段完整的思考过程', persisted: true },
|
|
|
+ { type: 'tool.started', payload: '{ toolCallId, name, title, input }', desc: '开始调用工具;title 是可读摘要,如"读取 销售.xlsx"', persisted: true },
|
|
|
+ { type: 'tool.completed', payload: '{ toolCallId, status: "ok" | "error" | "denied", output, truncated }', desc: '工具调用结束', persisted: true },
|
|
|
+ { type: 'file.changed', payload: '{ fileId, path, kind: "created" | "modified", size, mime }', desc: '工作区中新建或修改了文件(Agent 产出)', persisted: true },
|
|
|
+ { type: 'interaction.required', payload: 'InteractionRequest', desc: '需要用户审批或回答问题', persisted: true },
|
|
|
+ { type: 'interaction.resolved', payload: '{ interactionId, resolution, answers?, reason? }', desc: 'resolution:allowed / denied / answered / timeout / cancelled。多个页面同时打开时用于同步关闭卡片', persisted: true },
|
|
|
+ { type: 'usage', payload: 'Usage', desc: '本次 Run 的 token 用量', persisted: true },
|
|
|
+ { type: 'run.completed', payload: '{ durationMs }', desc: 'Run 正常结束', persisted: true },
|
|
|
+ { type: 'run.failed', payload: '{ code, message }', desc: 'Run 失败', persisted: true },
|
|
|
+ { type: 'run.interrupted', payload: '{ reason: "user" | "restart" | "timeout" }', desc: 'Run 被中断', persisted: true },
|
|
|
+]
|
|
|
+
|
|
|
+export const API_ERRORS: { status: number; code: string; desc: string }[] = [
|
|
|
+ { status: 400, code: 'bad_request', desc: '参数不正确(details 中有逐项说明)' },
|
|
|
+ { status: 401, code: 'unauthorized', desc: '缺少 Authorization' },
|
|
|
+ { status: 401, code: 'invalid_api_key', desc: 'API Key 无效或已停用' },
|
|
|
+ { status: 401, code: 'invalid_token', desc: '浏览器令牌无效、过期,或签发它的 API Key 已停用(SDK 会自动刷新一次)' },
|
|
|
+ { status: 403, code: 'forbidden', desc: '无权执行该操作' },
|
|
|
+ { status: 403, code: 'profile_not_allowed', desc: 'Profile 不对本平台开放' },
|
|
|
+ { status: 404, code: 'not_found', desc: '资源不存在,或不属于本平台' },
|
|
|
+ { status: 409, code: 'session_busy', desc: '会话正在运行' },
|
|
|
+ { status: 409, code: 'provider_unavailable', desc: '引擎当前不可用(未登录或已停用)' },
|
|
|
+ { status: 409, code: 'already_resolved', desc: '交互已被回复、超时或取消' },
|
|
|
+ { status: 413, code: 'file_too_large', desc: '上传文件超过大小上限' },
|
|
|
+ { status: 500, code: 'internal_error', desc: '服务器内部错误' },
|
|
|
+]
|