/** * 协议 v1 的 HTTP 表面(JSON Schema 为准,这里是 TS 镜像)。 * * 服务端与客户端之间只认这一套接口;引擎协议(Pi 的 JSONL、Codex 的 app-server)不越过适配层。 * 端点与规则见 docs/spec/protocol.md。 */ /** 路径前缀:协议改版就换前缀,不原地改语义 */ export const PROTOCOL_PREFIX = '/v1' as const; export const ENDPOINTS = { health: '/v1/health', me: '/v1/me', projects: '/v1/projects', sessions: '/v1/sessions', session: (sessionId: string) => `/v1/sessions/${sessionId}`, /** SSE:回放 + 实时事件流 */ sessionEvents: (sessionId: string) => `/v1/sessions/${sessionId}/events`, sessionMessages: (sessionId: string) => `/v1/sessions/${sessionId}/messages`, sessionCancel: (sessionId: string) => `/v1/sessions/${sessionId}/cancel`, capabilities: '/v1/capabilities', execution: (executionId: string) => `/v1/executions/${executionId}`, /** 执行追溯(A4.5):按 executionId 追到人、参数、审批与回执 */ executionTrace: (executionId: string) => `/v1/executions/${executionId}/trace`, approval: (approvalId: string) => `/v1/approvals/${approvalId}`, } as const; /** 端点规格:给实现与文档比对用的机器可读表(方法、路径、请求体、响应体) */ export interface EndpointSpec { method: 'GET' | 'POST'; /** 路径;路径参数用 {name} 占位 */ path: string; /** 请求体类型名;没有请求体写 '—' */ request: string; /** 响应体类型名;事件流写 'SSE' */ response: string; summary: string; } export const ENDPOINT_SPECS: readonly EndpointSpec[] = [ { method: 'GET', path: '/v1/health', request: '—', response: 'HealthResponse', summary: '健康检查' }, { method: 'GET', path: '/v1/me', request: '—', response: 'MeResponse', summary: '当前身份与客户端形态' }, { method: 'GET', path: '/v1/projects', request: '—', response: 'ProjectListResponse', summary: '可见项目' }, { method: 'GET', path: '/v1/sessions', request: '—', response: 'SessionListResponse', summary: '会话列表' }, { method: 'POST', path: '/v1/sessions', request: 'CreateSessionRequest', response: 'CreateSessionResponse', summary: '建会话' }, { method: 'GET', path: '/v1/sessions/{id}', request: '—', response: 'SessionDetailResponse', summary: '会话详情与最近消息' }, { method: 'GET', path: '/v1/sessions/{id}/events', request: '—', response: 'SSE', summary: '回放 + 实时事件流' }, { method: 'POST', path: '/v1/sessions/{id}/messages', request: 'SendMessageRequest', response: 'SendMessageResponse', summary: '发消息' }, { method: 'POST', path: '/v1/sessions/{id}/cancel', request: '—', response: 'CancelResponse', summary: '取消当前运行' }, { method: 'GET', path: '/v1/capabilities', request: '—', response: 'CapabilitiesResponse', summary: '按身份过滤的能力清单' }, { method: 'GET', path: '/v1/executions/{executionId}', request: '—', response: 'ExecutionReceipt', summary: '执行状态与回执' }, { method: 'GET', path: '/v1/executions/{executionId}/trace', request: '—', response: 'ExecutionTraceResponse', summary: '执行追溯:人、参数、审批与回执' }, { method: 'POST', path: '/v1/approvals/{approvalId}', request: 'ApprovalDecisionRequest', response: 'ApprovalDecisionResponse', summary: '确认卡响应' }, ]; /** 能力标识,形如 `aps.schedule.preview` */ export type CapabilityId = string; export interface HealthResponse { status: 'ok'; version: string; protocolVersion: number; } export interface MeResponse { userId: string; tenantId: string; displayName: string; /** 桌面端与浏览器共用同一身份模型,登录方式不同(见协议文档第 2 节) */ clientKind: 'desktop' | 'browser'; } /** 能力条目:按身份过滤后返回;描述七要素见 docs/spec/capability-description.md */ export interface CapabilitySummary { id: CapabilityId; providerId: string; title: string; purpose: string; effect: 'read' | 'draft' | 'write'; inputSchema?: Record; synonyms?: string[]; } export interface CapabilitiesResponse { capabilities: CapabilitySummary[]; } /** 会话概要(ADR-0003:个人会话只读,写操作必须在项目会话内) */ export interface SessionSummary { sessionId: string; projectId: string | null; scope: 'personal' | 'project'; status: 'active' | 'archived'; title: string; createdAt: number; } export interface ProjectSummary { projectId: string; name: string; status: 'active' | 'archived'; /** 项目内可写;个人会话只读(ADR-0003) */ writable: boolean; } export interface ProjectListResponse { projects: ProjectSummary[]; } export interface CreateSessionRequest { projectId?: string; scope: 'personal' | 'project'; title?: string; } export interface CreateSessionResponse { session: SessionSummary; } export interface SessionListResponse { sessions: SessionSummary[]; } export interface SessionDetailResponse { session: SessionSummary; /** 最近一段消息;完整历史走事件流回放 */ messages: ChatMessage[]; } export interface ChatMessage { messageId: string; role: 'user' | 'assistant' | 'system'; ts: number; text: string; } export interface SendMessageRequest { text: string; /** 同一写请求重试不重复生效 */ idempotencyKey: string; } export interface SendMessageResponse { accepted: true; messageId: string; runId: string; } export interface CancelResponse { state: 'cancelling' | 'idle'; } /** 能力调用的统一回执(架构设计 4.2) */ export interface ExecutionReceipt { executionId: string; status: 'accepted' | 'pending-confirmation' | 'succeeded' | 'failed'; result?: unknown; errorKind?: string; /** status = pending-confirmation 时给出待确认动作标识 */ approvalId?: string; } /** 审计链上的一条记录(A4.5):只追加,改一条就断链 */ export interface ExecutionTraceEntry { seq: number; ts: number; /** 事件类型:策略拒绝、确认、开始执行、结束执行、业务系统拒绝 */ event: string; /** 谁发起或谁决定的 */ actorUserId: string; capabilityId?: string; providerId?: string; /** 参数摘要,不是参数明文 */ paramsDigest?: string; decision?: string; detail?: Record; } /** 确认卡的持久记录(A4.4):重启后仍在,按 executionId 可追 */ export interface ExecutionApprovalRecord { approvalId: string; status: string; decision?: 'approved' | 'rejected' | 'expired'; decidedBy?: string; decidedAt?: number; /** 目标数据版本;版本变了确认就失效 */ revision: number; expiresAt: number; } /** 一次执行的追溯视图(A4.5):审计链 + 确认记录 + 回执,一个端点看全 */ export interface ExecutionTraceResponse { executionId: string; /** 没有走过确认卡的执行这里是 null */ approval: ExecutionApprovalRecord | null; entries: ExecutionTraceEntry[]; /** 审计链校验结果:false 表示有记录被改过 */ chainOk: boolean; receipt?: ExecutionReceipt; } export interface ApprovalDecisionRequest { decision: 'approve' | 'reject'; /** 目标数据版本;版本变了确认就失效 */ revision: number; idempotencyKey: string; note?: string; } export interface ApprovalDecisionResponse { approvalId: string; decision: 'approved' | 'rejected' | 'expired'; receipt?: ExecutionReceipt; } export interface ErrorEnvelope { error: { code: string; message: string; retryable: boolean; detail?: unknown; }; }