aps-agent/contracts/protocol.ts

222 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 协议 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<string, unknown>;
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<string, unknown>;
}
/** 确认卡的持久记录(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;
};
}