// 对外 API 的数据结构(docs/design/04-api.md) import type { InteractionRequest, ProviderId, UsagePayload } from './events' export type PermissionMode = 'ask' | 'auto' | 'readonly' export type SessionStatus = 'idle' | 'running' | 'waiting_input' export type RunStatus = 'queued' | 'running' | 'waiting_input' | 'completed' | 'failed' | 'interrupted' export interface SessionDto { id: string title: string provider: ProviderId model: string profileId: string | null status: SessionStatus archived: boolean source: string lastSeq: number createdAt: string updatedAt: string } export interface SessionDetailDto extends SessionDto { activeRunId: string | null pendingInteractions: InteractionRequest[] /** 实际生效的权限模式:会话覆盖值(仅后台可设)或 Profile 的设置 */ permissionMode: PermissionMode permissionModeOverridden: boolean systemPromptAppend: string } export interface CreateSessionBody { profileId?: string model?: string title?: string permissionMode?: PermissionMode systemPromptAppend?: string } export interface SendMessageBody { text: string attachments?: string[] } export interface RunDto { id: string sessionId: string status: RunStatus startedAt: string | null endedAt: string | null usage: UsagePayload | null error: string | null } export interface FileDto { id: string sessionId: string path: string origin: 'upload' | 'agent' mime: string size: number createdAt: string updatedAt: string } export interface FileAccessPolicy { mode: 'restricted' | 'broad' read: PathRule[] write: PathRule[] deny: string[] } export type PathRule = { knowledgeRootId: string } | { path: string } export interface ProfileDto { id: string name: string description: string provider: ProviderId model: string systemPrompt: string permissionMode: PermissionMode allowedTools: string[] disallowedTools: string[] skills: string[] | 'all' fileAccess: FileAccessPolicy knowledgeRootIds: string[] /** 可以使用的第三方 MCP 服务(内置工具始终可用) */ mcpServerIds: string[] isDefault: boolean /** 内置 Profile(通用助手)不可删除 */ builtin: boolean /** 内置 Profile 的种类:null 为通用助手,lead 为工作组的组长 */ builtinKey: 'lead' | null version: number updatedAt: string } export type ProfileInput = Omit & { mcpServerIds?: string[] } /** Profile 历史版本的内容(不含"是否默认") */ export type ProfileSnapshot = Omit export interface ProfileVersionDto { version: number createdAt: string snapshot: ProfileSnapshot /** 与上一个版本相比改动的字段 */ changed: (keyof ProfileSnapshot)[] } export interface KnowledgeRootDto { id: string name: string path: string description: string access: 'read' | 'write' enabled: boolean check: PathCheck } export interface PathCheck { exists: boolean readable: boolean isDirectory: boolean /** 命中的全局禁止项 */ denied: string | null /** 位于 home 下,systemd ProtectHome 部署时需要额外绑定 */ underHome: boolean message: string | null } export interface ProviderStatusDto { id: ProviderId enabled: boolean available: boolean loggedIn: boolean | null account: string | null cliPath: string version: string | null models: string[] defaultModel: string /** 模型的显示名与实际版本,如 sonnet → "Sonnet 5.5 · claude-sonnet-5-5"(引擎能提供时) */ modelLabels?: Record message: string | null } /** 订阅额度的一个计量窗口(如 5 小时、7 天) */ export interface QuotaWindowDto { key: string label: string /** 已用百分比 0-100;引擎没有给出时为 null */ usedPercent: number | null resetsAt: string | null } /** 引擎账户的订阅额度(ChatGPT / Claude 订阅的用量限制) */ export interface ProviderQuotaDto { provider: ProviderId /** 订阅类型,如 team、plus */ plan: string | null windows: QuotaWindowDto[] /** live:刚从引擎查询;last-run:查询失败,显示最近一次对话中引擎报告的数值 */ source: 'live' | 'last-run' fetchedAt: string message: string | null } export interface ResolvedAccessDto { readable: string[] writable: string[] denied: string[] mode: FileAccessPolicy['mode'] } export interface ApiKeyDto { id: string name: string prefix: string enabled: boolean allowedProfileIds: string[] defaultProfileId: string | null createdAt: string lastUsedAt: string | null } export interface ApiErrorBody { error: { code: string; message: string; details?: unknown } } export interface Page { items: T[] nextCursor: string | null } // ---- 技能与插件(通过 Claude Code 官方插件机制安装,ADR-0013)---- export type LicenseKind = 'open' | 'restricted' | 'unknown' export interface SkillInfo { /** 带插件前缀的技能 ID,例如 agent-skills:report-conventions、document-skills:xlsx */ id: string name: string description: string plugin: string /** local 本地技能目录;plugin Claude 插件;official 从官方仓库安装(Codex);system 引擎自带;community 第三方技能;custom 自定义技能(后两者两个引擎共用) */ source: 'local' | 'plugin' | 'official' | 'system' | 'community' | 'custom' /** 需要在 Profile 中单独勾选(选择"全部技能"时不包含) */ optIn?: boolean license: LicenseKind } export interface InstalledPluginDto { id: string name: string marketplace: string version: string | null enabled: boolean installPath: string installedAt: string | null lastUpdated: string | null skills: SkillInfo[] license: { kind: LicenseKind; summary: string | null } } export interface AvailablePluginDto { pluginId: string name: string description: string marketplaceName: string version: string | null installed: boolean } export interface MarketplaceDto { name: string source: string location: string | null } export interface PluginStateDto { cliPath: string installed: InstalledPluginDto[] available: AvailablePluginDto[] marketplaces: MarketplaceDto[] localSkills: SkillInfo[] error: string | null } export type PluginAction = | { action: 'marketplace-add'; source: string } | { action: 'marketplace-remove' | 'marketplace-update'; name: string } | { action: 'install' | 'uninstall' | 'update' | 'enable' | 'disable'; plugin: string } export interface ApiKeyProfiles { allowedProfileIds: string[] defaultProfileId: string } export interface CodexCatalogSkillDto { name: string description: string license: LicenseKind licenseName: string | null installed: boolean installedSha: string | null updatable: boolean } export interface CodexSkillCatalogDto { repo: string sha: string | null fetchedAt: string | null skills: CodexCatalogSkillDto[] unmanaged: string[] } // ---- 第三方技能(skills.sh 目录,从 GitHub 按提交安装,两个引擎共用,ADR-0017)---- /** 第三方技能的插件名,技能 ID 形如 community-skills: */ export const COMMUNITY_SKILL_PLUGIN = 'community-skills' export interface CommunitySearchResultDto { /** GitHub 仓库 owner/repo */ source: string skillId: string name: string installs: number installed: boolean /** 不能从这里安装的原因(如官方技能仓库应在对应引擎的页面安装) */ blocked: string | null } export interface CommunitySkillPreviewDto { source: string skillId: string /** 安装后的技能名(取自 SKILL.md 的 name) */ name: string description: string sha: string /** 技能在仓库中的路径 */ path: string skillMd: string files: { path: string; size: number }[] /** 技能包含可执行脚本(.py / .sh / .js 等) */ hasScripts: boolean license: LicenseKind licenseName: string | null /** 许可证来自哪里:技能目录、SKILL.md 声明或仓库根目录 */ licenseFrom: 'skill' | 'frontmatter' | 'repo' | null installed: { sha: string } | null /** 同名技能已从其他来源安装 */ conflict: string | null } export interface CommunitySkillDto { id: string name: string description: string source: string skillId: string path: string sha: string license: string | null installedAt: string } // ---- Codex 插件(OpenAI 官方 openai-curated 目录,只在 Playground 中使用,ADR-0018)---- export interface CodexPluginDto { /** 插件 ID(plugin/uninstall 使用),形如 gmail@openai-curated-remote */ id: string name: string displayName: string description: string category: string | null developer: string | null marketplace: string installed: boolean enabled: boolean /** NOT_AVAILABLE / AVAILABLE / INSTALLED_BY_DEFAULT */ installPolicy: string /** AVAILABLE / DISABLED_BY_ADMIN */ availability: string logoUrl: string | null } export interface CodexPluginCatalogDto { items: CodexPluginDto[] /** 符合筛选条件的总数 */ total: number categories: string[] fetchedAt: string | null error: string | null } export interface CodexPluginDetailDto extends CodexPluginDto { longDescription: string | null skills: { name: string; description: string }[] apps: { id: string; name: string; description: string | null; installUrl: string | null }[] mcpServers: string[] websiteUrl: string | null privacyPolicyUrl: string | null termsOfServiceUrl: string | null } export interface CodexPluginInstallResultDto { /** 需要在 ChatGPT 中授权的应用(打开 installUrl 完成连接) */ appsNeedingAuth: { id: string; name: string; installUrl: string | null }[] } // ---- 用量统计 ---- export interface UsageTotals { runs: number failed: number interrupted: number inputTokens: number outputTokens: number cacheReadTokens: number cacheWriteTokens: number /** 订阅登录下为引擎给出的估算值 */ costUsd: number durationMs: number } export interface UsageSummaryDto { /** 统计区间(含首尾,服务器本地日期) */ from: string to: string total: UsageTotals today: UsageTotals /** 每天一项,没有用量的日期也会列出 */ daily: ({ date: string } & UsageTotals)[] /** 按来源:API Key(平台)或后台 Playground(source = admin) */ bySource: ({ source: string; name: string } & UsageTotals)[] byModel: ({ provider: string; model: string } & UsageTotals)[] /** 按引擎:今天与整个区间 */ byProvider: { provider: string; today: UsageTotals; total: UsageTotals }[] } export interface RecentErrorDto { runId: string sessionId: string sessionTitle: string source: string provider: string error: string endedAt: string | null } // ---- 浏览器令牌(业务平台后端用 API Key 换取,交给浏览器使用,ADR-0019)---- export interface CreateBrowserTokenBody { /** 有效期(秒),默认 3600,最长 43200 */ ttlSeconds?: number } export interface BrowserTokenDto { token: string expiresAt: string } // ---- 对外 API 协议参考(后台"API 协议"页面,数据见 apps/server/src/api-reference/reference.ts)---- export interface ApiField { name: string type: string required?: boolean desc: string } export interface ApiEndpoint { method: 'GET' | 'POST' | 'PATCH' | 'DELETE' path: string summary: string desc?: string query?: ApiField[] body?: ApiField[] /** 请求体不是 JSON 时的说明(如 multipart) */ bodyNote?: string response: { status: number; type: string; desc?: string } errors?: string[] example?: string /** 后台"调试"时预填的请求体 */ sampleBody?: unknown } export interface ApiEndpointGroup { id: string title: string desc?: string endpoints: ApiEndpoint[] } export interface ApiTypeDef { name: string desc?: string fields: ApiField[] } export interface ApiEventDef { type: string payload: string desc: string persisted: boolean } export interface ApiErrorDef { status: number code: string desc: string } /** 概述中的条目:`代码`、**加粗** 两种标记;$BASE 替换为服务地址 */ export interface ApiOverview { points: string[] flow: string[] } /** 一套协议:Agent API(/v1,会话)或 Bot API(/bot/v1,机器人与工作组) */ export interface ApiSpec { id: 'agent' | 'bot' title: string /** 一句话说明,以及使用哪种 Key */ summary: string overview: ApiOverview groups: ApiEndpointGroup[] /** 事件信封的类型名(数据结构中的一项) */ eventEnvelope: string types: ApiTypeDef[] events: ApiEventDef[] errors: ApiErrorDef[] } export interface ApiReferenceDto { version: string specs: ApiSpec[] } // ---- MCP 服务(经 MCP Gateway 转接,两个引擎共用,ADR-0021)---- /** 工具策略:auto 直接调用;ask 跟随权限模式(需审批时询问);off 不提供 */ export type McpToolPolicy = 'auto' | 'ask' | 'off' export interface McpToolDto { name: string description: string /** 服务声明的只读工具(annotations.readOnlyHint);只读模式下只能调用这类工具 */ readOnly: boolean policy: McpToolPolicy /** 总是在后台执行(长时间运行的工具,docs/design/15-background-calls.md) */ background: boolean } /** 密钥类的值(请求头、环境变量):读取时只返回是否已设置 */ export interface McpSecretEntry { name: string /** 写入时为新值;读取时为 null(不返回密钥)。更新时传 null 表示保留原值 */ value: string | null isSet?: boolean } export interface McpServerInput { /** 工具名前缀:小写字母、数字、下划线、连字符,1-32 位 */ name: string title: string description: string transport: 'http' | 'stdio' enabled: boolean /** http:服务地址 */ url: string headers: McpSecretEntry[] /** stdio:命令、参数、环境变量 */ command: string args: string[] env: McpSecretEntry[] /** stdio:沙箱内是否允许联网 */ allowNetwork: boolean /** stdio:沙箱内额外只读 / 可写的路径 */ readPaths: string[] writePaths: string[] } export interface McpServerDto extends McpServerInput { id: string tools: McpToolDto[] toolsUpdatedAt: string | null status: 'connected' | 'idle' | 'error' lastError: string | null /** 本地进程最近的错误输出 */ stderrTail: string | null /** 勾选了这个服务的 Profile */ profileNames: string[] createdAt: string updatedAt: string } export interface McpBuiltinToolDto { name: string description: string readOnly: boolean } export interface McpStateDto { builtin: McpBuiltinToolDto[] servers: McpServerDto[] /** 本地进程沙箱(bubblewrap)是否可用 */ sandboxAvailable: boolean } export interface McpTestResultDto { ok: boolean tools: McpToolDto[] error: string | null stderrTail: string | null ms: number } // ---- 自定义技能(导入或在后台编写,两个引擎共用,docs/design/12-custom-skills.md)---- /** 自定义技能的插件名,技能 ID 形如 custom-skills: */ export const CUSTOM_SKILL_PLUGIN = 'custom-skills' export interface CustomSkillFileDto { path: string size: number sha: string /** 可以在后台以文本方式编辑 */ text: boolean } export interface FileChangesDto { added: string[] modified: string[] removed: string[] } export interface CustomSkillVersionDto { version: number createdAt: string /** create 新建;edit 编辑保存;import 首次导入;sync 同步;restore 恢复 */ reason: 'create' | 'edit' | 'import' | 'sync' | 'restore' changed: FileChangesDto fileCount: number size: number } export interface CustomSkillDto { id: string /** 技能名,也是目录名;技能 ID 为 custom-skills: */ name: string description: string origin: 'authored' | 'imported' sourceId: string | null sourceName: string | null enabled: boolean /** 包含在 Profile 的"全部技能"中(默认否,需要单独勾选) */ includeInAll: boolean /** 运行时生效的版本;null 表示还没有发布 */ publishedVersion: number | null latestVersion: number /** 编辑中、尚未保存为版本的改动 */ hasUnsavedChanges: boolean profileNames: string[] createdAt: string updatedAt: string } export interface CustomSkillDetailDto extends CustomSkillDto { /** 编辑中的文件(未保存的改动也在这里) */ files: CustomSkillFileDto[] versions: CustomSkillVersionDto[] } export interface SkillSourceInput { name: string description: string /** 服务器上的绝对路径 */ path: string syncMode: 'manual' | 'auto' intervalMin: number } export interface SkillSourceDto extends SkillSourceInput { id: string /** directory 服务器目录;upload 上传的 zip(一次性) */ kind: 'directory' | 'upload' lastSyncAt: string | null status: 'never' | 'ok' | 'error' lastError: string | null /** 从这个来源导入的技能 */ skillNames: string[] createdAt: string } export interface SkillScanItemDto { name: string description: string /** 在来源中的相对路径 */ path: string fileCount: number size: number /** new 新技能;changed 有变化;unchanged 无变化;conflict 同名技能已存在且来源不同;invalid 不能导入 */ status: 'new' | 'changed' | 'unchanged' | 'conflict' | 'invalid' reason: string | null changes: FileChangesDto warnings: string[] } export interface SkillScanDto { sourceId: string /** 导入时回传,扫描结果变化时拒绝导入(避免导入未审阅的内容) */ digest: string skills: SkillScanItemDto[] /** 从这个来源导入过、但来源中已不存在的技能 */ missing: string[] /** 跳过的文件(符号链接、特殊文件等) */ skipped: string[] } export interface FileDiffDto { path: string status: 'added' | 'removed' | 'modified' binary: boolean /** 文本文件的统一差异格式 */ patch: string | null } // ---- 后台文件管理(管理员)---- export interface FsRootDto { /** workspaces、kb:<资料库 id>、skills、runtime */ id: string name: string kind: 'workspaces' | 'knowledge' | 'readonly' writable: boolean description: string exists: boolean } export interface FsShortcutDto { label: string rootId: string path: string /** codicon 名称 */ icon: string } export interface FsEntryDto { name: string /** link:指向位置之外的符号链接(不跟随) */ type: 'file' | 'dir' | 'link' | 'other' size: number | null mtime: string /** 会话工作区根目录下的会话目录:会话标题 */ label?: string }