Files
Agent-System-Frontend/src/contracts.ts
T
weichenandCursor f718095d78 Show background notices without blocking the composer
Why: A background child should not look like the conversation is busy, and its completion note needs a compact row instead of a full assistant bubble.
Evidence: vitest run passed, including design-guard. tsc -b passed.
Impact: The stop button cancels the conversation run. System notices fold with the shared Collapsible.
Refs: feature/agent-harness-p3b
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 15:30:50 +00:00

299 lines
11 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.
// 从 Agent-System-Core ts/types.ts 同步(make contracts 生成;Core 为权威)。
// Agent-System-Core 契约的 TypeScript 镜像。
// 线格式契约以 Go 类型(camelCase JSON 标签)与 testdata/golden/*.json 黄金样本为准;
// 本文件手工镜像同一契约,前端按未知 type/字段静默忽略(前向兼容原则)。
export type ID = string;
// ---- 领域模型 ----
export interface Project {
id: ID; owner?: ID; name: string; createdAt: string; updatedAt: string;
settings?: Record<string, unknown>;
}
export interface Conversation {
id: ID; projectId: ID; owner?: ID; title: string; profileId?: ID;
pinned?: boolean; archived?: boolean;
allowCrossRead?: boolean; crossPrivate?: boolean;
createdAt: string; updatedAt: string;
}
export type Role = 'user' | 'assistant' | 'system';
export type PartType =
| 'text' | 'thinking' | 'tool_call' | 'tool_result' | 'artifact_ref'
| 'hitl' | 'plan' | 'error' | 'card';
export interface Part {
type: PartType;
text?: string;
toolCall?: { id: string; name: string; arguments?: string; durationMs?: number };
toolResult?: { callId: string; status: 'ok' | 'error'; summary?: string; truncated?: boolean };
artifact?: { artifactId: ID; name: string; kind?: string };
hitl?: { cardId: ID; question: string; resolved: boolean; answer?: string };
plan?: PlanSnapshot;
card?: { kind: string; data: UINode[]; cardId?: ID }; // cardId 非空 = 收件箱卡片引用部件(submit_card)
error?: { code: string; message: string };
process?: boolean; // run 终态后折叠为摘要行
durationMs?: number; // thinking 部件的思考时长(毫秒);工具耗时在 toolCall.durationMs
}
export interface Message {
id: ID; conversationId: ID; runId?: ID; role: Role;
parts: Part[]; createdAt: string;
}
export type RunKind = 'conversation' | 'subagent' | 'tool';
export type RunStatus =
| 'pending' | 'running' | 'waiting_hitl' | 'completed'
| 'failed' | 'cancelled' | 'interrupted';
export type RunPhase = 'thinking' | 'acting' | 'responding' | 'done';
export interface Usage {
promptTokens: number; completionTokens: number; totalTokens: number;
durationMs: number; toolCalls: number;
}
export interface Run {
id: ID; parentRunId?: ID; conversationId: ID; projectId: ID; owner?: ID;
kind: RunKind; profileId?: ID; status: RunStatus; phase?: RunPhase;
agentName?: string; createdAt: string; endedAt?: string;
usage?: Usage; error?: { code: string; message: string };
}
export interface Artifact {
id: ID; projectId: ID; owner?: ID; name: string; kind: string; mime?: string;
size: number; sourceRun?: ID; createdAt: string; updatedAt: string;
}
export interface PlanSnapshot {
planId: ID; title: string;
steps: { id: string; text: string; status: 'pending' | 'in_progress' | 'done' | 'skipped'; agentRun?: ID }[];
}
export interface TodoList {
conversationId: ID;
items: { id: string; text: string; status: 'pending' | 'in_progress' | 'done' }[];
}
// ---- 流式事件 ----
export type StreamEventType =
| 'message.delta' | 'thinking.delta' | 'tool.call' | 'tool.result'
| 'run.phase' | 'hitl.request' | 'hitl.resolved' | 'plan.update' | 'todo.update'
| 'agent.spawn' | 'agent.progress' | 'agent.result'
| 'artifact.created' | 'artifact.updated'
| 'run.completed' | 'run.failed' | 'error'
| 'card.created' | 'card.updated' | 'card.status' | 'card.resolved';
export interface StreamEvent<P = unknown> {
seq: number; runId?: ID; conversationId: ID; ts: string; v: number;
type: StreamEventType; payload?: P;
}
// payload 形状(按 type 判别;未知字段忽略)
export interface MessageDeltaPayload { index: number; text: string }
export interface AgentSpawnPayload { agentRun: ID; name: string; task: string }
export interface AgentProgressPayload { agentRun: ID; note?: string; phase?: RunPhase }
export interface AgentResultPayload { agentRun: ID; status: RunStatus; summary?: string }
export interface HITLResolvedPayload { cardId: ID; answer?: string; kind: 'choice' | 'free' | 'timeout' | 'cancelled' }
export interface RunCompletedPayload {
usage: Usage
context?: ContextObservation
}
export interface ContextObservation {
items: string[]
truncations?: string[]
estChars: number
}
export interface RunFailedPayload { code: string; message: string }
// ---- 收件箱卡片 ----
export type CardKind =
| 'progress' | 'confirm' | 'compare' | 'review-list'
| 'result' | 'error' | 'status';
export type CardStatus =
| 'active' | 'action_required' | 'done'
| 'resolved' | 'failed' | 'dismissed';
export interface ActivityCard {
id: ID; projectId: ID; conversationId?: ID;
sourceRunId?: ID; anchorMessageId?: ID;
kind: CardKind; title: string; status: CardStatus; tone?: string;
body?: UINode[]; actions?: { id: string; label: string; style?: 'primary' | 'default' | 'danger'; command?: string }[];
pinned?: boolean; createdAt: string; updatedAt: string;
}
export interface CardPayload { card: ActivityCard }
export interface CardStatusPayload { cardId: ID; status: CardStatus; tone?: string; note?: string }
export interface CardResolvedPayload { cardId: ID; actionId?: string; answer?: string }
// ---- HITL ----
export type HITLKind = 'choice' | 'input' | 'confirm' | 'form';
export interface HITLChoice {
id: string; label: string; description: string; impact: string; recommended?: boolean;
}
export interface HITLCard {
id: ID; runId: ID; conversationId: ID; question: string; kind: HITLKind;
choices?: HITLChoice[]; allowFreeInput: boolean; freeInputHint?: string;
timeoutSec?: number | null; form?: UINode[]; createdAt: string; resolved: boolean;
anchorMessageId?: ID; blocked?: boolean; blockReason?: string;
}
export interface HITLResponse {
cardId: ID; choiceId?: string; freeText?: string; formData?: unknown;
}
// ---- UI 词汇 ----
export type UINodeType =
| 'section' | 'list' | 'kv' | 'form' | 'markdown' | 'doc-ref' | 'actions' | 'chart-lite'
| 'avatar' | 'progress' | 'compare' | 'tree' | 'table' | 'steps' | 'tabs' | 'timeline';
export interface UINode {
type: UINodeType;
title?: string;
items?: string[];
kv?: [string, string][];
fields?: UIField[];
markdown?: string;
docId?: ID;
actions?: { id: string; label: string; style?: 'primary' | 'default' | 'danger' }[];
chart?: { kind: 'bar' | 'line'; labels: string[]; series: number[] };
children?: UINode[];
avatar?: { name?: string; sub?: string; tone?: string; stack?: { name?: string; sub?: string; tone?: string }[] };
progress?: { label?: string; value: number; tone?: string };
compare?: {
left: { title?: string; tone?: string; nodes?: UINode[] };
right: { title?: string; tone?: string; nodes?: UINode[] };
};
tree?: {
nodes: {
id: string; label: string; tone?: string; badge?: string; count?: number;
selected?: boolean; children?: unknown[];
}[];
};
table?: {
columns: { key: string; label: string }[];
rows: { id?: string; cells: [string, string][]; actions?: UINode['actions'] }[];
};
steps?: { current: number; steps: { label: string; status?: 'pending' | 'active' | 'done' | 'failed'; tone?: string }[] };
tabs?: { active?: string; tabs: { id: string; label: string; nodes?: UINode[] }[] };
timeline?: { items: { time?: string; title: string; tone?: string; markdown?: string; ref?: string }[] };
}
export interface UIField {
key: string; label: string;
type: 'text' | 'textarea' | 'number' | 'boolean' | 'select';
options?: string[]; required?: boolean; placeholder?: string; help?: string; default?: string;
}
// ---- 提示词库(prompt-library)----
export interface PromptTrigger {
kind: 'intent' | 'command' | 'attachment' | 'manual';
keywords?: string[]; command?: string;
}
export interface PromptModule {
id: string; layer: 'base' | 'env' | 'task' | 'agent' | 'tool' | 'aux' | 'style';
version: number; title: string; description?: string; priority?: number;
triggers?: PromptTrigger[]; requiresAttachments?: boolean;
body: string; outputContract?: string;
source?: 'builtin' | 'plugin' | 'user'; owner?: string;
basedOnVersion?: number; drifted?: boolean; disabled?: boolean;
/** 子代理 profile。只对 agent.sub.* 有意义;空字段表示沿用内置。 */
tools?: string[]; model?: string; maxTurns?: number;
}
export interface PromptBlock { moduleId: string; version: number; layer: string; body: string; }
export interface PromptHitRecord { moduleId: string; layer: string; reason: string; injected: boolean; }
// ---- 画像 / 设置 ----
export interface AgentProfile {
id: string; owner?: string; name: string; description?: string; builtin?: boolean; source?: string;
systemPrompt: string;
/** 提示词库模块引用(有序;空 = 默认 base+env 全集)。 */
promptModules?: string[];
model: { chat?: string; lightweight?: string; embedding?: string; rerank?: string };
tools: { allow: string[]; deny?: string[]; confirm?: string[] };
context: {
maxHistoryMessages: number; summarizeBeyond?: boolean;
memoryBudgetPct?: number; knowledgeBudgetPct?: number; maxToolResultChars?: number;
};
hitl: { askOnAmbiguity: boolean; timeoutSec?: number | null };
budget: { maxTokens?: number | null; maxToolCalls?: number | null; maxTurns?: number | null };
}
export interface SettingDesc {
key: string; group: string; label: string; description?: string;
type: 'string' | 'number' | 'boolean' | 'enum' | 'secret';
default?: unknown; options?: string[];
effect: 'hot' | 'new-run' | 'restart';
secret?: boolean;
}
// ---- 插件 ----
export interface PluginManifest {
id: string; name: string; version: string; apiVersion: string;
requires?: { network?: string[] };
provides: {
tools?: string[]; workflows?: string[]; intents?: string[];
prompts?: string[]; panels?: string[]; cards?: string[]; settings?: string[];
commands?: string[]; workspaces?: string[];
skills?: string[]; mcpServers?: string[]; knowledge?: string[];
parsers?: string[]; agents?: string[]; channels?: string[]; pages?: string[];
};
scope?: { tenants?: string[]; projects?: string[] };
backend?: { mode?: 'inproc' | 'sidecar'; command?: string; args?: string[]; schemaVersion?: number };
}
export interface PluginPanel {
id: string; title: string; icon?: string; body: UINode[]; refreshTool?: string;
}
export interface PluginWorkspace {
id: string; title: string; icon?: string; shortcut?: string;
badgeTool?: string; body: UINode[]; refreshTool?: string;
}
export interface PluginPageDecl {
id: string; title: string; module: string;
}
// ---- 摄取 / 检索 ----
export interface ParsedDocument {
meta: {
filename: string; mime?: string; parser: string; confidence?: number;
crossCheck?: { with: string; diffBlocks: number; verdict: 'agree' | 'differ' };
parsedAt: string;
};
blocks: {
type: 'heading' | 'para' | 'list' | 'table' | 'code' | 'image-ref';
level?: number; text?: string; items?: string[];
rows?: string[][]; ref?: string; confidence?: number;
}[];
warnings?: string[];
}
export interface RetrievalHit {
doc: { id: ID; owner?: ID; projectId?: ID; source: string; title: string; ref?: string; chunk: string; createdAt: string };
score: number; kind: 'fulltext' | 'vector' | 'plugin';
}
export interface UploadInfo {
artifactId: ID; filename: string; mime?: string; size: number;
parsed?: ParsedDocument;
}