@x-otto/interchange 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -0
- package/dist/index.d.ts +1373 -0
- package/dist/index.js +2 -0
- package/package.json +28 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1373 @@
|
|
|
1
|
+
//#region src/message.d.ts
|
|
2
|
+
/** Message: AgentMessage/UserMessage/SystemMessage interchange types. */
|
|
3
|
+
type Timestamp = number;
|
|
4
|
+
type KnownApi = 'openai-completions' | 'openai-responses' | 'azure-openai-responses' | 'openai-codex-responses' | 'anthropic-messages' | 'bedrock-converse-stream' | 'google-generative-ai' | 'google-gemini-cli' | 'google-vertex';
|
|
5
|
+
type Api = KnownApi | (string & {});
|
|
6
|
+
type KnownProvider = 'anthropic' | 'openai' | 'google' | 'amazon-bedrock' | 'github-copilot' | 'xai' | 'groq' | 'openrouter' | 'deepseek' | 'ollama' | 'moonshot' | 'custom';
|
|
7
|
+
type Provider = KnownProvider | (string & {});
|
|
8
|
+
type StopReason = 'end_turn' | 'max_tokens' | 'tool_use' | 'stop_sequence' | 'refusal';
|
|
9
|
+
interface Usage {
|
|
10
|
+
inputTokens: number;
|
|
11
|
+
outputTokens: number;
|
|
12
|
+
cacheReadTokens: number;
|
|
13
|
+
cacheWriteTokens: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 结构化的 zod 兼容垫片——协议层不直接依赖 zod,靠结构类型让 provider/工具定义喂入真 zod schema。
|
|
17
|
+
*/
|
|
18
|
+
interface ZodType<T = unknown> {
|
|
19
|
+
parse(data: unknown): T;
|
|
20
|
+
safeParse(data: unknown): {
|
|
21
|
+
success: boolean;
|
|
22
|
+
data?: T;
|
|
23
|
+
error?: unknown;
|
|
24
|
+
};
|
|
25
|
+
toJSONSchema?: () => unknown;
|
|
26
|
+
}
|
|
27
|
+
interface TextContent {
|
|
28
|
+
type: 'text';
|
|
29
|
+
text: string;
|
|
30
|
+
signature?: string;
|
|
31
|
+
}
|
|
32
|
+
interface ImageContent {
|
|
33
|
+
type: 'image';
|
|
34
|
+
mime: string;
|
|
35
|
+
source: string;
|
|
36
|
+
}
|
|
37
|
+
interface ThinkingContent {
|
|
38
|
+
type: 'thinking';
|
|
39
|
+
text: string;
|
|
40
|
+
signature?: string;
|
|
41
|
+
}
|
|
42
|
+
interface ToolCall {
|
|
43
|
+
type: 'tool_call';
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
arguments: Record<string, unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* 流式参数 JSON 解析失败标记(RFC-142 D4)——`stopReason: max_tokens` 截断半截
|
|
49
|
+
* JSON 时 `parseToolArguments` 置位,runtime 责任链据此把该调用转为明确错误
|
|
50
|
+
* tool_result 而非带空参数进入 zod 校验(与污染类错误分离错误通道)。
|
|
51
|
+
*
|
|
52
|
+
* **进程内标记,不进 wire**:各 provider 序列化发给模型时均为显式字段拾取
|
|
53
|
+
* (`{type, id, name, input: arguments}`,无 spread),本字段天然剥除——有测试钉住
|
|
54
|
+
* (RFC-142 重要事项规则4)。
|
|
55
|
+
*/
|
|
56
|
+
argumentsParseFailed?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A2UI content block(RFC-105 D6/M105-5b):agent 生成的声明式 UI payload。
|
|
60
|
+
* 对齐 a2ui-project/a2ui(v0.9)的核心哲学——扁平组件列表 + ID 引用 + 受信组件 catalog、
|
|
61
|
+
* 纯数据非代码、未知组件类型文本降级。
|
|
62
|
+
*
|
|
63
|
+
* 组件类型(TUI 终端安全子集,首阶段只读 display):
|
|
64
|
+
* - Text:纯文本(text 字段)。
|
|
65
|
+
* - Card:带标题容器(title + children ID 引用)。
|
|
66
|
+
* - List:条目列表(items ID 引用 + ordered 标志)。
|
|
67
|
+
* - Progress:ASCII 进度条(value 0-100,label 可选)。
|
|
68
|
+
* - Chip:内联彩色标签(text + color)。
|
|
69
|
+
* - Button:交互按钮(RFC-105 D6 F5/M105-5c,数字键触发 a2ui action;只读 display 通过
|
|
70
|
+
* renderA2uiBlock 提取为 A2uiButtonInfo 元数据)。
|
|
71
|
+
* - Form:交互式表单(RFC-211 M1:字段定义 + TUI 弹窗填写 + web-ui 原生表单)。
|
|
72
|
+
* 字段类型 text/select/toggle,payload 原样回流,不可执行代码。
|
|
73
|
+
* 未知 type → 文本摘要表示(不崩,fail-soft,R7)。
|
|
74
|
+
*/
|
|
75
|
+
interface A2uiComponent {
|
|
76
|
+
id: string;
|
|
77
|
+
type: 'Text' | 'Card' | 'List' | 'Progress' | 'Chip' | 'Button' | 'Form' | (string & {});
|
|
78
|
+
text?: string;
|
|
79
|
+
title?: string;
|
|
80
|
+
children?: string[];
|
|
81
|
+
items?: string[];
|
|
82
|
+
ordered?: boolean;
|
|
83
|
+
value?: number;
|
|
84
|
+
max?: number;
|
|
85
|
+
label?: string;
|
|
86
|
+
color?: 'accent' | 'green' | 'amber' | 'red' | 'gray';
|
|
87
|
+
action?: string;
|
|
88
|
+
shortcut?: string;
|
|
89
|
+
/** 表单标题(弹窗标题 + 对话流摘要标题)。 */
|
|
90
|
+
formTitle?: string;
|
|
91
|
+
/** 表单字段定义。 */
|
|
92
|
+
fields?: A2uiFormField[];
|
|
93
|
+
}
|
|
94
|
+
/** 单个表单字段(`A2uiComponent.type === 'Form'` 时使用)。 */
|
|
95
|
+
interface A2uiFormField {
|
|
96
|
+
/** 字段 id(Form 内唯一,submit payload 的 key)。 */
|
|
97
|
+
id: string;
|
|
98
|
+
/** 字段类型。 */
|
|
99
|
+
type: 'text' | 'select' | 'toggle';
|
|
100
|
+
/** 显示标签。 */
|
|
101
|
+
label: string;
|
|
102
|
+
/** 默认值(text=字符串, select=option id, toggle=boolean)。 */
|
|
103
|
+
defaultValue?: string | boolean;
|
|
104
|
+
/** 占位符(text 类型)。 */
|
|
105
|
+
placeholder?: string;
|
|
106
|
+
/** 选项列表(select 类型必填)。 */
|
|
107
|
+
options?: Array<{
|
|
108
|
+
id: string;
|
|
109
|
+
label: string;
|
|
110
|
+
}>;
|
|
111
|
+
/** 是否必填(渲染提示,不做前端校验——非目标)。 */
|
|
112
|
+
required?: boolean;
|
|
113
|
+
}
|
|
114
|
+
interface A2uiContent {
|
|
115
|
+
type: 'a2ui';
|
|
116
|
+
/** A2UI 协议版本(v0.9 snapshot),供未来版本迁移用。 */
|
|
117
|
+
version: string;
|
|
118
|
+
/** 扁平组件列表,ID 引用构建组件树,增量子集可追加/替换。 */
|
|
119
|
+
components: A2uiComponent[];
|
|
120
|
+
}
|
|
121
|
+
type ContentPart = TextContent | ImageContent | ThinkingContent | ToolCall | A2uiContent;
|
|
122
|
+
interface UserMessage {
|
|
123
|
+
role: 'user';
|
|
124
|
+
content: string | ContentPart[];
|
|
125
|
+
timestamp: Timestamp;
|
|
126
|
+
uuid?: string;
|
|
127
|
+
/**
|
|
128
|
+
* RFC-050 REV-1:引擎注入的内部消息(如预算收尾 steering、截断续写提示),模型需要看到、
|
|
129
|
+
* 但**不应渲染给用户**(否则 resume 时显示成用户从未输入的发言)。provider 序列化忽略此字段。
|
|
130
|
+
*/
|
|
131
|
+
internal?: boolean;
|
|
132
|
+
}
|
|
133
|
+
interface AssistantMessage {
|
|
134
|
+
role: 'assistant';
|
|
135
|
+
/**
|
|
136
|
+
* 消息内容。A2uiContent 是前向兼容新增(RFC-155 M2 结案后的协议层收口):
|
|
137
|
+
* provider 序列化层仅消费 text/tool_call/thinking,a2ui 块被静默跳过不出进 LLM 请求体;
|
|
138
|
+
* 渲染层(message-lines.ts)经 `renderA2uiBlock` 渲染。
|
|
139
|
+
*/
|
|
140
|
+
content: (TextContent | ThinkingContent | ToolCall | A2uiContent)[];
|
|
141
|
+
api: Api;
|
|
142
|
+
provider: Provider;
|
|
143
|
+
model: string;
|
|
144
|
+
usage: Usage;
|
|
145
|
+
stopReason: StopReason;
|
|
146
|
+
uuid?: string;
|
|
147
|
+
}
|
|
148
|
+
interface ToolResultMessage<T extends unknown = unknown> {
|
|
149
|
+
role: 'tool_result';
|
|
150
|
+
toolCallId: string;
|
|
151
|
+
toolName: string;
|
|
152
|
+
/**
|
|
153
|
+
* 工具结果内容。A2uiContent 是前向兼容新增(RFC-155 M2 结案后的协议层收口):
|
|
154
|
+
* provider 序列化仅消费 text,a2ui 块被静默跳过;渲染层(message-lines.ts)经
|
|
155
|
+
* `renderA2uiBlock` 渲染为终端卡片。
|
|
156
|
+
*/
|
|
157
|
+
content: (TextContent | ImageContent | A2uiContent)[];
|
|
158
|
+
details: T;
|
|
159
|
+
isError: boolean;
|
|
160
|
+
timestamp: Timestamp;
|
|
161
|
+
uuid?: string;
|
|
162
|
+
/** RFC-050 REV-1:引擎合成的内部 tool_result(如预算耗尽未执行的占位),不渲染给用户。 */
|
|
163
|
+
internal?: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* RFC-144 M1:mid-conversation system 通知消息——**真正进 provider 请求**的协议消息类型
|
|
167
|
+
* (区别于 `@otto/hook-contracts` 的 `SystemAgentMessage`,后者是 hooks/UI 域、
|
|
168
|
+
* convertToLLM 类型边界显式过滤、绝不进 provider 请求)。
|
|
169
|
+
*
|
|
170
|
+
* 用途:lifecycleAsync 工具(RFC-144)真正完成时,追加一条独立消息告知 LLM"运营方观察到
|
|
171
|
+
* 的状态变化"(Anthropic mid-conversation system message 官方场景之一),不修改/替换任何
|
|
172
|
+
* 历史消息(纯追加语义),LLM 在下一轮推理中作为权威事实处理。
|
|
173
|
+
*
|
|
174
|
+
* provider 序列化:
|
|
175
|
+
* - Anthropic(原生支持,Claude Opus 4.8+)→ `{role: 'system', content}` 追加进 messages 数组,
|
|
176
|
+
* 放置规则见 RFC-144 重要事项规则 5(必须紧跟携带 tool_result 的 user turn 之后)。
|
|
177
|
+
* - 不支持该特性的模型/provider(如 OpenAI,或 Anthropic 旧模型)→ 降级为等价的
|
|
178
|
+
* `role: 'user'` 消息 + 显式文本前缀(牺牲"运营方权威"语义,保留基本可用性),
|
|
179
|
+
* 降级逻辑归属 provider adapter 层(本类型定义不关心降级细节)。
|
|
180
|
+
*
|
|
181
|
+
* 不可变性(RFC-144 禁区):一旦追加,绝不编辑或删除已发送的 system 通知——结果更新一律
|
|
182
|
+
* 追加新消息,不改写旧消息(Anthropic 官方限制:编辑会使该点之后的 prompt cache 失效)。
|
|
183
|
+
*/
|
|
184
|
+
interface SystemNotificationMessage {
|
|
185
|
+
role: 'system_notification';
|
|
186
|
+
/** 通知正文(如 "[Tool completion] pnpm build finished: exit 0")。 */
|
|
187
|
+
content: string;
|
|
188
|
+
/**
|
|
189
|
+
* 该通知对应的工具执行是否失败——显式字段,供消费方(渲染层/审计)直接读取,不必解析
|
|
190
|
+
* `content` 文案判断状态(RFC-144 M2:文案格式可能演进,靠字符串匹配脆弱且易误判)。
|
|
191
|
+
*/
|
|
192
|
+
isError?: boolean;
|
|
193
|
+
/** 关联的 lifecycleAsync 工具调用 id,供消费方(渲染层/审计)溯源。 */
|
|
194
|
+
relatedToolCallId?: string;
|
|
195
|
+
timestamp: Timestamp;
|
|
196
|
+
uuid?: string;
|
|
197
|
+
}
|
|
198
|
+
type Message = UserMessage | AssistantMessage | ToolResultMessage | SystemNotificationMessage;
|
|
199
|
+
/**
|
|
200
|
+
* 历史压缩摘要消息的标记前缀——单一真相源(跨包协议常量)。
|
|
201
|
+
* 生产者 @otto/memory 注入摘要消息时用、压缩去重(isSummaryMessage)时识别;消费者
|
|
202
|
+
* @otto/agent 的 ContextPipeline 从会话树恢复摘要时用同一前缀重建摘要消息。放在协议叶子,
|
|
203
|
+
* 避免双份字面量漂移导致 isSummaryMessage 失配。
|
|
204
|
+
*/
|
|
205
|
+
declare const CONVERSATION_SUMMARY_PREFIX = "[Conversation Summary]";
|
|
206
|
+
/**
|
|
207
|
+
* 从 assistant 消息的内容块中提取全部 tool_call 项(终局架构 review 2026-07-10 P0-3:
|
|
208
|
+
* 从 @otto/ai 物理迁移——此前是 @otto/ai 唯一自己实现(非 re-export)的两个函数,只依赖
|
|
209
|
+
* AssistantMessage/ToolCall,真源本就该在协议叶子而非模型注册中枢)。
|
|
210
|
+
*/
|
|
211
|
+
declare function getToolCallsByAssistantMessage(message: AssistantMessage): ToolCall[];
|
|
212
|
+
/** 该 assistant 消息是否含至少一个 tool_call。 */
|
|
213
|
+
declare function hasToolCalls(message: AssistantMessage): boolean;
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/stream.d.ts
|
|
216
|
+
/**
|
|
217
|
+
* 供应商 usage 快照(状态栏「周/小时额度」展示,RFC 无编号临时功能——状态栏 provider usage)。
|
|
218
|
+
*
|
|
219
|
+
* 仅 Anthropic OAuth 订阅计划(Claude Pro/Max)请求会带 anthropic-ratelimit-unified-* 响应头族;
|
|
220
|
+
* API key 走标准 RPM/TPM 限流不带此头族,故 provider/ai 层解析不到时返回 null,调用方天然
|
|
221
|
+
* 实现"API key 不显示"(见 packages/ai/src/provider-usage.ts extractProviderUsageFromHeaders
|
|
222
|
+
* 与 packages/ai/src/auth-store.ts getAuthSource)。
|
|
223
|
+
*
|
|
224
|
+
* usedPercent 未经 Anthropic 官方文档确认(仅有 status/representative-claim/reset 三个字段有
|
|
225
|
+
* SDK 源码级证据),探测不到时为 undefined——UI 侧绝不显示猜测/编造的百分比数字。
|
|
226
|
+
*/
|
|
227
|
+
interface ProviderUsageSnapshot {
|
|
228
|
+
/**
|
|
229
|
+
* 订阅配额状态。'allowed'/'allowed_warning'/'rejected' 来自 Anthropic unified 头族的
|
|
230
|
+
* 真实语义;**'unknown' = 无订阅配额数据**(RFC-118 小修 S11:OpenAI 只有标准
|
|
231
|
+
* x-ratelimit-* 头无配额语义,此前重载 'allowed' 造成同字段双语义——UI 按
|
|
232
|
+
* status!=='rejected' 判「额度 OK」会让 OpenAI 用户永远看不到预警)。
|
|
233
|
+
* 消费端对 'unknown' 应展示中性态(不判额度充足也不报警)。
|
|
234
|
+
*/
|
|
235
|
+
status: 'allowed' | 'allowed_warning' | 'rejected' | 'unknown';
|
|
236
|
+
limitType?: 'five_hour' | 'seven_day' | 'seven_day_opus' | 'seven_day_sonnet' | 'seven_day_overage_included';
|
|
237
|
+
/** Unix 时间戳(秒)——配额重置时刻。 */
|
|
238
|
+
resetsAt?: number;
|
|
239
|
+
/** 已用百分比(0-100)。未探测到时 undefined。 */
|
|
240
|
+
usedPercent?: number;
|
|
241
|
+
/**
|
|
242
|
+
* Overage(额外用量池)状态。
|
|
243
|
+
* 仅当标准额度已耗尽(status='rejected')且 overage 可用(overageStatus='allowed'/'allowed_warning')
|
|
244
|
+
* 时有意义——此时 isUsingOverage = true,状态栏应展示 overage 标识而非 rejected 错误态。
|
|
245
|
+
* 无 overage 头族的请求(API key / 未开通 overage)此字段为 undefined。
|
|
246
|
+
*/
|
|
247
|
+
overageStatus?: 'allowed' | 'allowed_warning' | 'rejected';
|
|
248
|
+
/** Overage 池重置时刻(Unix 秒)。 */
|
|
249
|
+
overageResetsAt?: number;
|
|
250
|
+
/** Overage 不可用原因(仅在 overageStatus='rejected' 时可能有值)。 */
|
|
251
|
+
overageDisabledReason?: string;
|
|
252
|
+
/**
|
|
253
|
+
* 标准 API 限流快照(x-ratelimit-* 头族,有官方文档)。与 Anthropic unified 订阅配额
|
|
254
|
+
* 不同——这里表示的是分钟级请求/Token 的 token bucket 限流,任何认证方式的请求都返回
|
|
255
|
+
* (Anthropic API key + OpenAI API key + OAuth 订阅)。undefined = 没有标准限流头。
|
|
256
|
+
*
|
|
257
|
+
* 格式来自 OpenAI Rate Limits 文档(x-ratelimit-limit-requests/remaining-requests/
|
|
258
|
+
* reset-requests/-limit-tokens/-remaining-tokens/-reset-tokens),Anthropic 也用
|
|
259
|
+
* 同名头族但加上 `anthropic-` 前缀。
|
|
260
|
+
*/
|
|
261
|
+
standardLimits?: {
|
|
262
|
+
remainingRequests?: number;
|
|
263
|
+
remainingTokens?: number;
|
|
264
|
+
limitRequests?: number;
|
|
265
|
+
limitTokens?: number;
|
|
266
|
+
resetRequestsMs?: number;
|
|
267
|
+
resetTokensMs?: number;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 流式增量事件——provider converse() 的统一产物面。RFC-067 M111(A5):所有 provider 都必须
|
|
272
|
+
* 发齐 text/thinking/tool_call 的 start/delta/end 包络(end 带累积 content),否则下游
|
|
273
|
+
* 「完成通知」会丢内容。单一真相源避免 anthropic 全发 12 变体而 OpenAI 系发不全的漂移。
|
|
274
|
+
*/
|
|
275
|
+
type StreamEvent = {
|
|
276
|
+
type: 'start';
|
|
277
|
+
partial: AssistantMessage;
|
|
278
|
+
} | {
|
|
279
|
+
type: 'text_start';
|
|
280
|
+
index: number;
|
|
281
|
+
partial: AssistantMessage;
|
|
282
|
+
} | {
|
|
283
|
+
type: 'text_delta';
|
|
284
|
+
index: number;
|
|
285
|
+
delta: string;
|
|
286
|
+
partial: AssistantMessage;
|
|
287
|
+
} | {
|
|
288
|
+
type: 'text_end';
|
|
289
|
+
index: number;
|
|
290
|
+
content: string;
|
|
291
|
+
partial: AssistantMessage;
|
|
292
|
+
} | {
|
|
293
|
+
type: 'thinking_start';
|
|
294
|
+
index: number;
|
|
295
|
+
partial: AssistantMessage;
|
|
296
|
+
} | {
|
|
297
|
+
type: 'thinking_delta';
|
|
298
|
+
index: number;
|
|
299
|
+
delta: string;
|
|
300
|
+
partial: AssistantMessage;
|
|
301
|
+
} | {
|
|
302
|
+
type: 'thinking_end';
|
|
303
|
+
index: number;
|
|
304
|
+
content: string;
|
|
305
|
+
partial: AssistantMessage;
|
|
306
|
+
} | {
|
|
307
|
+
type: 'tool_call_start';
|
|
308
|
+
index: number;
|
|
309
|
+
partial: AssistantMessage;
|
|
310
|
+
} | {
|
|
311
|
+
type: 'tool_call_delta';
|
|
312
|
+
index: number;
|
|
313
|
+
delta: string;
|
|
314
|
+
partial: AssistantMessage;
|
|
315
|
+
} | {
|
|
316
|
+
type: 'tool_call_end';
|
|
317
|
+
index: number;
|
|
318
|
+
toolCall: ToolCall;
|
|
319
|
+
partial: AssistantMessage;
|
|
320
|
+
} | {
|
|
321
|
+
type: 'done';
|
|
322
|
+
reason: StopReason;
|
|
323
|
+
message: AssistantMessage; /** 本轮成功响应解析出的供应商 usage 快照(仅 Anthropic OAuth 订阅计划非 null)。 */
|
|
324
|
+
providerUsage?: ProviderUsageSnapshot | null;
|
|
325
|
+
} | {
|
|
326
|
+
type: 'error';
|
|
327
|
+
error: Error;
|
|
328
|
+
};
|
|
329
|
+
//#endregion
|
|
330
|
+
//#region src/model.d.ts
|
|
331
|
+
/**
|
|
332
|
+
* 推理投入档(reasoning effort)——RFC-235 对齐 Claude Code 词表:`low/medium/high/xhigh/max`。
|
|
333
|
+
* 去除历史遗留的 `minimal`(无对应厂商语义,存量配置载入时迁移为 `low`),新增 `max`(Claude
|
|
334
|
+
* Code 最高档 + DeepSeek `reasoning_effort:'max'` 顶档)。各模型真实支持的子集由 manifest 的
|
|
335
|
+
* `Model.thinkingLevels` 声明,provider 组装请求体时按"≤该档最高支持档"降级(见 RFC-235 §3.3)。
|
|
336
|
+
*/
|
|
337
|
+
type ThinkingLevel = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
338
|
+
/**
|
|
339
|
+
* 模型能力优势标签(供大模型为 subagent 选型)。非 /models API 返回——由
|
|
340
|
+
* model-capabilities 据「精选家族图 + 启发式」推导。
|
|
341
|
+
* planning=方案/架构 · knowledge=知识/通识 · coding=编码 · reasoning=深度推理
|
|
342
|
+
* · vision=视觉 · speed=低延迟 · long-context=长上下文
|
|
343
|
+
*/
|
|
344
|
+
type ModelStrength = 'planning' | 'knowledge' | 'coding' | 'reasoning' | 'vision' | 'speed' | 'long-context';
|
|
345
|
+
interface Cost {
|
|
346
|
+
input: number;
|
|
347
|
+
output: number;
|
|
348
|
+
cacheRead: number;
|
|
349
|
+
cacheWrite: number;
|
|
350
|
+
}
|
|
351
|
+
interface Model<T = Api> {
|
|
352
|
+
id: string;
|
|
353
|
+
name: string;
|
|
354
|
+
api: T;
|
|
355
|
+
provider: Provider;
|
|
356
|
+
baseUrl: string;
|
|
357
|
+
reasoning: boolean;
|
|
358
|
+
input: ('text' | 'image')[];
|
|
359
|
+
/**
|
|
360
|
+
* 美元 / 百万 token 定价(终局修复:改为可选,不再强制归零)。三家主流供应商
|
|
361
|
+
* (Anthropic/OpenAI/GitHub Copilot,逐一核实过官方文档+SDK 源码)的 `/models` 列表接口
|
|
362
|
+
* 均不返回定价——这不是接口设计缺陷,是行业普遍现状:`/models` 回答"能用哪些模型",
|
|
363
|
+
* 定价只写在面向人类阅读的文档页(会随时间调整),没有供应商把它做成程序化查询接口。
|
|
364
|
+
* 缺省(`undefined`)= 该模型没有已知定价来源(如运行时从供应商接口动态发现的新型号),
|
|
365
|
+
* UI 层应据此不显示开销估算,而非显示误导性的 $0(用户可能误以为模型免费,实际正常计费)。
|
|
366
|
+
* 有值 = 人工核实过的真实历史定价(当前来自各插件 manifest 声明)。
|
|
367
|
+
*/
|
|
368
|
+
cost?: Cost;
|
|
369
|
+
contextWindow: number;
|
|
370
|
+
maxOutputTokens: number;
|
|
371
|
+
thinkingLevels?: ThinkingLevel[];
|
|
372
|
+
/**
|
|
373
|
+
* thinking 配置格式声明:
|
|
374
|
+
* - 'enabled'(缺省):标准 Anthropic `{ type: 'enabled', budget_tokens: N }`
|
|
375
|
+
* - 'adaptive':`{ type: 'adaptive' }` + 顶层 `output_config.effort`(部分网关要求此格式)
|
|
376
|
+
*/
|
|
377
|
+
thinkingMode?: 'enabled' | 'adaptive';
|
|
378
|
+
/** 能力优势标签(供 subagent 选型);由 model-capabilities 推导,非内置硬编码真源。 */
|
|
379
|
+
strengths?: ModelStrength[];
|
|
380
|
+
/**
|
|
381
|
+
* 图片约束声明(RFC-148 M5):单请求最大图片数 + 超阈值后的单图最大边长约束。
|
|
382
|
+
* 由插件 manifest 的模型条目声明(供应商专属约束,如 Anthropic 官方规则"单请求
|
|
383
|
+
* >maxImagesPerRequest 张图时单图任一边 > maxDimensionPxIfOverLimit 即 400",
|
|
384
|
+
* 见 `packages/agent/src/image-degradation.ts` 消费方)。缺省 = 无已知约束
|
|
385
|
+
* (引擎侧不主动截断,行为等价于约束不存在——不臆造保守默认值)。
|
|
386
|
+
*/
|
|
387
|
+
imageConstraints?: {
|
|
388
|
+
maxImagesPerRequest: number;
|
|
389
|
+
maxDimensionPxIfOverLimit?: number;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* 工具调用支持声明(RFC-130 M131-2,同 `imageConstraints` 声明化模式):该模型/provider
|
|
393
|
+
* 是否支持客户端工具委托。缺省 `undefined` = 支持(向后兼容——绝大多数 provider 支持工具,
|
|
394
|
+
* 不臆造保守默认值破坏存量行为)。仅当 provider 协议层明确不支持工具委托时(如 Cursor
|
|
395
|
+
* agent API 只服务端自跑内置工具,M131-3-00b 已证伪双向映射可行性)声明 `false`,宿主
|
|
396
|
+
* (`session-config-resolver.ts`)据此剥离工具集,避免 provider 抛协议错误——引擎侧
|
|
397
|
+
* 只读布尔声明,不识别具体厂商字符串(终局判据,同 RFC-148 系列的 zero-vendor-knowledge
|
|
398
|
+
* 原则)。
|
|
399
|
+
*/
|
|
400
|
+
supportsTools?: boolean;
|
|
401
|
+
/**
|
|
402
|
+
* Responses API 请求体策略声明(RFC-213):供 `openai-responses` 协议实现按声明覆盖
|
|
403
|
+
* 标准请求体字段,替代此前失效的 `model.api === 'openai-codex-responses'` 字符串判断
|
|
404
|
+
* (RFC-122 provider 插件化迁移后该字面量已物理不可达,是死代码——见 RFC-213 背景的
|
|
405
|
+
* 逐层源码追溯)。缺省 = 标准行为(不设置 `store`、按
|
|
406
|
+
* `context.maxTokens ?? model.maxOutputTokens` 发送 `max_output_tokens`)。
|
|
407
|
+
*/
|
|
408
|
+
responseBodyPolicy?: {
|
|
409
|
+
/** 显式设置 body.store(如 ZDR 合规场景需要 false)。缺省不设置该字段(沿用 API 默认)。 */store?: boolean;
|
|
410
|
+
/**
|
|
411
|
+
* false = 不发送 max_output_tokens 字段。官方 `openai/codex` CLI 源码(`ResponsesApiRequest`
|
|
412
|
+
* 结构体)从不发送该字段(用 `reasoning`/`text` 控制输出),且社区证据(reasoning 模型
|
|
413
|
+
* 系列经 Responses API 收到该字段会被 400 拒绝)表明这不是可选的风格差异。缺省 true(发送)。
|
|
414
|
+
*/
|
|
415
|
+
sendMaxOutputTokens?: boolean;
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
type ModelSpec = string | {
|
|
419
|
+
provider: string;
|
|
420
|
+
name: string;
|
|
421
|
+
api?: Api;
|
|
422
|
+
} | Model;
|
|
423
|
+
//#endregion
|
|
424
|
+
//#region src/model-capability.d.ts
|
|
425
|
+
/**
|
|
426
|
+
* 该模型是否支持图像输入。
|
|
427
|
+
*
|
|
428
|
+
* 保守默认:仅当 `input` 数组**明确**包含 `'image'` 时返回 `true`;`input`
|
|
429
|
+
* 缺失/为空数组时返回 `false`(RFC-181 §3 D1/§4 规则 2——第三方插件 manifest
|
|
430
|
+
* 漏写 `input` 字段时,保守降级为"不支持"比误放行裸发图像给不支持视觉的
|
|
431
|
+
* provider(导致 API 400 回合中断)更安全;误降级的代价仅是输出质量下降,
|
|
432
|
+
* 可通过补全 manifest 声明恢复)。
|
|
433
|
+
*/
|
|
434
|
+
declare function modelSupportsImages(model: Pick<Model, 'input'>): boolean;
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/tool.d.ts
|
|
437
|
+
/**
|
|
438
|
+
* 工具错误的类型化类别——在**产生处**定型,取代下游对错误文本的英文子串猜测
|
|
439
|
+
* (agent work-loop 旧 categoriseToolError)。未设时下游回退启发式匹配(向后兼容未迁移的工具)。
|
|
440
|
+
*/
|
|
441
|
+
type ToolErrorKind = 'validation' | 'permission' | 'not_found' | 'timeout' | 'network' | 'io' | 'runtime' | 'aborted' | 'unknown';
|
|
442
|
+
interface ToolResult {
|
|
443
|
+
content: (TextContent | ImageContent)[];
|
|
444
|
+
isError?: boolean;
|
|
445
|
+
/** 错误类别(产生处定型,仅 isError 时有意义)。 */
|
|
446
|
+
errorKind?: ToolErrorKind;
|
|
447
|
+
details?: Record<string, unknown>;
|
|
448
|
+
/**
|
|
449
|
+
* RFC-153 D6:工具来源标记(存 pluginId),`undefined` 表示非插件来源(内建工具)。
|
|
450
|
+
* 仅 `isError:true` 的异常路径才由 `toAgentTool` 包装层补充;正常路径不设置。
|
|
451
|
+
* 只服务"标记来源、供 UI 展示",不改变任何重试/降级决策(规则 8)。
|
|
452
|
+
*/
|
|
453
|
+
pluginSource?: string;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* ToolResult.content 的文本块拼接——供事件面(tool.call.end.resultText)与其他
|
|
457
|
+
* "需要工具结果纯文本"的消费方共用的单一真源。图片块跳过(无文本可拼)。
|
|
458
|
+
*/
|
|
459
|
+
declare function extractToolResultText(result: ToolResult): string;
|
|
460
|
+
interface ToolCallContext<Params = unknown> {
|
|
461
|
+
id: string;
|
|
462
|
+
params: Params;
|
|
463
|
+
signal: AbortSignal;
|
|
464
|
+
sessionId?: string;
|
|
465
|
+
agentName?: string;
|
|
466
|
+
/** 当前会话委托深度(root=0);委托工具据此回传 caller 深度给护栏。 */
|
|
467
|
+
depth?: number;
|
|
468
|
+
onUpdate?: (update: string) => void;
|
|
469
|
+
}
|
|
470
|
+
interface ToolCallEvent {
|
|
471
|
+
id: string;
|
|
472
|
+
name: string;
|
|
473
|
+
arguments: Record<string, unknown>;
|
|
474
|
+
}
|
|
475
|
+
interface ToolDescriptionContext {
|
|
476
|
+
projectRoot: string;
|
|
477
|
+
}
|
|
478
|
+
interface AgentTool<Params = unknown> {
|
|
479
|
+
name: string;
|
|
480
|
+
description: string | ((ctx: ToolDescriptionContext) => string);
|
|
481
|
+
parameters: ZodType<Params>;
|
|
482
|
+
/** 工具对模型的暴露策略,替换旧 visibility。缺省 'direct'。 */
|
|
483
|
+
exposure?: 'direct' | 'deferred' | 'hidden';
|
|
484
|
+
/** 标记为只读工具,可与其他只读工具并发执行 */
|
|
485
|
+
readonly?: boolean;
|
|
486
|
+
/**
|
|
487
|
+
* RFC-093:本工具结果的上下文预算(chars)。超预算时结果落盘、上下文留 preview+路径占位符。
|
|
488
|
+
* 缺省用引擎全局 maxToolResultChars(100K)。声明更小值(如 30K)促使大输出工具更早落盘。
|
|
489
|
+
*/
|
|
490
|
+
resultBudgetChars?: number;
|
|
491
|
+
/**
|
|
492
|
+
* 按**入参**判断该次调用是否可与其他并发安全调用并行(而非静态 bool)。
|
|
493
|
+
* 例:task_delegate tracked ephemeral 可并行、sticky(复用共享子会话)不可;untracked 恒 ephemeral 可并行。
|
|
494
|
+
* work-loop 分桶取并:`readonly || isConcurrencySafe(input)` → 进有上限的并行桶;否则串行。
|
|
495
|
+
* 缺省(未声明)视为不可并行(除非 readonly)。
|
|
496
|
+
*/
|
|
497
|
+
isConcurrencySafe?: (input: Params) => boolean;
|
|
498
|
+
/**
|
|
499
|
+
* 声明哪些参数承载文件系统路径(如 ['path'] / ['filePath'])。PEP(permission/file
|
|
500
|
+
* guard)据此解析受管路径——取代两份 extractPath 对 path/file_path/filePath 的硬编码猜测。
|
|
501
|
+
* 工具是路径参数命名的唯一真相源;不声明 = 该工具无路径参数,guard 不参与路径规则匹配。
|
|
502
|
+
*/
|
|
503
|
+
pathParams?: readonly string[];
|
|
504
|
+
/**
|
|
505
|
+
* 把 pathParams 的原始值解析成该工具实际操作的绝对路径(通常 `resolve(projectRoot, raw)`)。
|
|
506
|
+
* PEP 据此做受管路径匹配——否则 file-guard 的锚定模式(`^/etc/` 等)对相对穿越(`../../etc/x`)
|
|
507
|
+
* 失配,因为匹配的是原始相对串而非工具真正落地的绝对路径。缺省 = 直接用原始值。
|
|
508
|
+
*/
|
|
509
|
+
resolvePath?: (rawPath: string) => string;
|
|
510
|
+
/**
|
|
511
|
+
* 标记本工具为异步执行。异步工具不占 runBounded worker 槽位,改为 Promise.all 统一等待。
|
|
512
|
+
* 适用场景:长时构建、独立后台任务等最终结果不依赖前序工具输出的操作。
|
|
513
|
+
*
|
|
514
|
+
* 约束:声明 async:true 必须同时声明 readonly 或 isConcurrencySafe
|
|
515
|
+
* (workLoop runTools 入口 assertion 强制执行)。
|
|
516
|
+
*/
|
|
517
|
+
async?: boolean;
|
|
518
|
+
/**
|
|
519
|
+
* RFC-144 M1:标记本工具为生命周期异步(工具执行不阻塞 agent loop)。
|
|
520
|
+
*
|
|
521
|
+
* 与 `async`(RFC-135,同 turn 内并行、仍 `await Promise.all` 阻塞 runTools 返回)语义不同:
|
|
522
|
+
* `lifecycleAsync` 工具启动时立即推入终态 placeholder tool_result(禁止性措辞,防止 LLM
|
|
523
|
+
* 误判"已完成"),不进 syncCalls/asyncCalls/sequentialCalls 任一执行桶,runTools 立即返回、
|
|
524
|
+
* agent loop 继续推进而非等待。工具真正完成时通过 `agent.notifyToolCompletion()` 追加
|
|
525
|
+
* mid-conversation system message 通知结果(不替换 placeholder,纯追加)。
|
|
526
|
+
*
|
|
527
|
+
* 两者正交可共存:工具可同时声明 `async:true` + `lifecycleAsync:true`
|
|
528
|
+
* (既不占 worker 槽位、也不阻塞 loop)——`lifecycleAsync` 优先,runTools 分类时率先
|
|
529
|
+
* 过滤,不进 asyncCalls 桶。
|
|
530
|
+
*
|
|
531
|
+
* 谓词形式(RFC-144 采纳评估):与 `isConcurrencySafe` 同构——同一工具的不同调用参数
|
|
532
|
+
* 可能需要不同的生命周期语义(如 `task_delegate` 仅 `mode=sync` 是长阻塞候选,
|
|
533
|
+
* `mode=background` 本就立即返回)。分桶时以本次调用的 arguments 求值;求值异常按
|
|
534
|
+
* false(同步执行)处理,fail-safe。
|
|
535
|
+
*
|
|
536
|
+
* 详见 `docs/rfc/RFC-144-tool-lifecycle-async.md` 决策 1/2 与
|
|
537
|
+
* `docs/rfc/RFC-144-adoption-assessment.md`。
|
|
538
|
+
*/
|
|
539
|
+
lifecycleAsync?: boolean | ((input: Params) => boolean);
|
|
540
|
+
/**
|
|
541
|
+
* RFC-144 采纳评估:lifecycleAsync placeholder 的工具级附加警示(可选)。
|
|
542
|
+
*
|
|
543
|
+
* 通用 placeholder 已包含"不得假设结果"的禁止性措辞(经真实模型端到端验证);
|
|
544
|
+
* 部分工具需要额外的领域警示——如 `task_delegate` 的子代理可能编辑工作区文件,
|
|
545
|
+
* 主代理在收到完成通知前不应读写可能冲突的路径。声明后追加到 placeholder 文本末尾。
|
|
546
|
+
*/
|
|
547
|
+
lifecycleAsyncNote?: string;
|
|
548
|
+
execute: (ctx: ToolCallContext<Params>) => Promise<ToolResult>;
|
|
549
|
+
}
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/session-event.d.ts
|
|
552
|
+
interface AskUserQuestion {
|
|
553
|
+
id: string;
|
|
554
|
+
text: string;
|
|
555
|
+
type?: 'text' | 'choice';
|
|
556
|
+
options?: string[];
|
|
557
|
+
}
|
|
558
|
+
interface GrillOption {
|
|
559
|
+
label: string;
|
|
560
|
+
rationale: string;
|
|
561
|
+
recommended?: boolean;
|
|
562
|
+
preview?: string;
|
|
563
|
+
}
|
|
564
|
+
interface GrillQuestion {
|
|
565
|
+
id: string;
|
|
566
|
+
header: string;
|
|
567
|
+
question: string;
|
|
568
|
+
background: string;
|
|
569
|
+
recommendation: string;
|
|
570
|
+
options?: GrillOption[];
|
|
571
|
+
allowFreeform?: boolean;
|
|
572
|
+
multiSelect?: boolean;
|
|
573
|
+
evidence?: string[];
|
|
574
|
+
askedBy: string;
|
|
575
|
+
}
|
|
576
|
+
interface GrillAnswer {
|
|
577
|
+
answer: string;
|
|
578
|
+
selectedOptionLabels?: string[];
|
|
579
|
+
acceptedRecommendation: boolean;
|
|
580
|
+
/**
|
|
581
|
+
* 回答的来源(终局语义):
|
|
582
|
+
* - 'human':真实用户(TUI/交互端)作答。
|
|
583
|
+
* - 'auto_recommendation':调用方/LLM 侧在无交互需求下按推荐直接沉降(推荐被采纳,且这不是环境限制导致)。
|
|
584
|
+
* - 'environment_headless':运行环境本身不支持问询(headless/无 UI),自动把推荐沉降。
|
|
585
|
+
* 插件状态机不应把这种回答当作"用户明确接受"来推进候选。
|
|
586
|
+
*/
|
|
587
|
+
resolvedBy: 'human' | 'auto_recommendation' | 'environment_headless';
|
|
588
|
+
/** grill_me allowFreeform:用户在选项之外输入的补充文字。 */
|
|
589
|
+
freeformText?: string;
|
|
590
|
+
}
|
|
591
|
+
type GrillRequest = (question: Omit<GrillQuestion, 'id'>) => Promise<GrillAnswer>;
|
|
592
|
+
type AgentSessionEventType = AgentSessionEvent['type'];
|
|
593
|
+
/** prompt.end 的结束原因——决定回合摘要行显示"完成"还是"终止"/"错误"。 */
|
|
594
|
+
type PromptStopReason = 'completed' | 'interrupted' | 'error';
|
|
595
|
+
type AgentSessionEvent = {
|
|
596
|
+
type: 'session.start';
|
|
597
|
+
sessionId: string;
|
|
598
|
+
} | {
|
|
599
|
+
type: 'session.end';
|
|
600
|
+
sessionId: string;
|
|
601
|
+
} | {
|
|
602
|
+
type: 'prompt.start';
|
|
603
|
+
sessionId: string;
|
|
604
|
+
text: string;
|
|
605
|
+
}
|
|
606
|
+
/** model:本次 prompt 真正执行所用的模型 id 快照(发布时由 AgentSession 在 prompt() 入口捕获,
|
|
607
|
+
* 避免下游投影层在事件消费时刻惰性读取 session.model 产生的"回合摘要模型与实际执行模型错配")。
|
|
608
|
+
* thinkingLevel:同快照捕获的本轮实际 effort 档(RFC-235,供回合摘要行显示 ·Effort)。 */
|
|
609
|
+
| {
|
|
610
|
+
type: 'prompt.end';
|
|
611
|
+
sessionId: string;
|
|
612
|
+
tokenUsage?: {
|
|
613
|
+
input: number;
|
|
614
|
+
output: number;
|
|
615
|
+
};
|
|
616
|
+
model?: string;
|
|
617
|
+
thinkingLevel?: string;
|
|
618
|
+
stopReason?: PromptStopReason;
|
|
619
|
+
} /** RFC-094 D3:todo 闸门自动续跑的中间轮信号(一个用户 prompt 恰好一对 start/end,中间每次续跑发一次)。 */ | {
|
|
620
|
+
type: 'prompt.continued';
|
|
621
|
+
sessionId: string;
|
|
622
|
+
round: number;
|
|
623
|
+
maxRounds: number;
|
|
624
|
+
incompleteCount: number;
|
|
625
|
+
} | {
|
|
626
|
+
type: 'stream.event';
|
|
627
|
+
sessionId: string;
|
|
628
|
+
event: StreamEvent;
|
|
629
|
+
} | {
|
|
630
|
+
type: 'messages.persist';
|
|
631
|
+
sessionId: string;
|
|
632
|
+
role: string;
|
|
633
|
+
} | {
|
|
634
|
+
type: 'agent.status';
|
|
635
|
+
sessionId: string;
|
|
636
|
+
from: string;
|
|
637
|
+
to: string;
|
|
638
|
+
} | {
|
|
639
|
+
type: 'streaming.start';
|
|
640
|
+
sessionId: string;
|
|
641
|
+
model: string;
|
|
642
|
+
} | {
|
|
643
|
+
type: 'streaming.end';
|
|
644
|
+
sessionId: string;
|
|
645
|
+
model: string;
|
|
646
|
+
stopReason: string;
|
|
647
|
+
}
|
|
648
|
+
/** toolTurnCount/maxToolTurns:本轮工具预算窗口已用/上限(供宿主渲染 x/y 提示)。
|
|
649
|
+
* promptOutputTokens/promptOutputTokenBudget(RFC-104 D5):token 预算用量(未配置时 budget 缺省)。 */
|
|
650
|
+
| {
|
|
651
|
+
type: 'turn.start';
|
|
652
|
+
sessionId: string;
|
|
653
|
+
turnIndex: number;
|
|
654
|
+
toolTurnCount: number;
|
|
655
|
+
maxToolTurns: number;
|
|
656
|
+
promptOutputTokens: number;
|
|
657
|
+
promptOutputTokenBudget?: number;
|
|
658
|
+
} /** RFC-104 D5:工具预算触顶通知——extended(扩展续跑)/ steered(收尾轮)/ progressDenied(无进展被拒)。 */ | {
|
|
659
|
+
type: 'budget.notice';
|
|
660
|
+
sessionId: string;
|
|
661
|
+
turnIndex: number;
|
|
662
|
+
maxTurns: number;
|
|
663
|
+
extended: boolean;
|
|
664
|
+
steered?: boolean;
|
|
665
|
+
progressDenied?: boolean;
|
|
666
|
+
} /** RFC-104 D3/D5:per-prompt token/时间预算超额通知(R7:携带真实 used/budget)。 */ | {
|
|
667
|
+
type: 'prompt.budget.notice';
|
|
668
|
+
sessionId: string;
|
|
669
|
+
turnIndex: number;
|
|
670
|
+
dimension: 'tokens' | 'time';
|
|
671
|
+
used: number;
|
|
672
|
+
budget: number;
|
|
673
|
+
steered: boolean;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* RFC-203 D2:滑动窗口早期停滞检出通知——触顶前发现原地打转(同签名反复且零写成功),
|
|
677
|
+
* 已注入软引导(每 prompt 至多一次,与 budget.notice/prompt.budget.notice 的"触顶后"
|
|
678
|
+
* 语义正交:本事件恒早于任何预算触顶)。
|
|
679
|
+
*/
|
|
680
|
+
| {
|
|
681
|
+
type: 'progress.stall.notice';
|
|
682
|
+
sessionId: string;
|
|
683
|
+
turnIndex: number;
|
|
684
|
+
windowTurns: number;
|
|
685
|
+
maxRepeat: number;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* RFC-232 D4:工具调用协议违约(stopReason='tool_use' 但响应无 tool_call block)——引擎兜底
|
|
689
|
+
* 触发时的用户提示信号。outcome=retry(已注入纠正提示重跑)/terminate(重试耗尽干净终止);
|
|
690
|
+
* retryCount 为已重试次数。TUI 据此弹 toast,避免无声中断(对齐 progress.stall.notice 纹理)。
|
|
691
|
+
*/
|
|
692
|
+
| {
|
|
693
|
+
type: 'tool.call.mismatch.notice';
|
|
694
|
+
sessionId: string;
|
|
695
|
+
turnIndex: number;
|
|
696
|
+
outcome: 'retry' | 'terminate';
|
|
697
|
+
retryCount: number;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* RFC-204 D1/D2:会话累计成本超过软预算阈值——session 生命周期内至多一次(成本单调递增,
|
|
701
|
+
* 一旦超过恒超过,不像 per-prompt 预算需要重置)。不阻断,仅提醒。`totalCostUSD`/
|
|
702
|
+
* `budgetUSD` 携带真实数字(评审 H1:对齐 prompt.budget.notice 既有纹理,消费方不必
|
|
703
|
+
* 额外查一次 /cost 才知道超了多少)。
|
|
704
|
+
*/
|
|
705
|
+
| {
|
|
706
|
+
type: 'session.cost-budget-exceeded';
|
|
707
|
+
sessionId: string;
|
|
708
|
+
totalCostUSD: number;
|
|
709
|
+
budgetUSD: number;
|
|
710
|
+
} | {
|
|
711
|
+
type: 'turn.end';
|
|
712
|
+
sessionId: string;
|
|
713
|
+
turnIndex: number;
|
|
714
|
+
} | {
|
|
715
|
+
type: 'tool.call.start';
|
|
716
|
+
sessionId: string;
|
|
717
|
+
toolCall: ToolCallEvent;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* resultText:该次调用真实结果的文本预览(按 toolCall.id 精确对应,非"树上最后一条"猜测)。
|
|
721
|
+
* 修复根因(2026-07 残影事故):投影层此前经 ProjectCtx.toolResultText 从 session.messages()
|
|
722
|
+
* 倒查"最后一条 tool_result"取文本——但工具结果要等整个 prompt 结束才批量落树
|
|
723
|
+
* (persistence.persistNew),回合中期读到的永远是上一轮遗留的旧结果,且完全不按 id 匹配,
|
|
724
|
+
* 并行/异步工具场景下每个工具都会显示同一条陈旧文本。事件自带该字段后投影层不再猜测。
|
|
725
|
+
* undefined 表示上游未提供(旧 replay 数据 / 生产者未升级),消费端可回退旧启发式。
|
|
726
|
+
* details:工具结果携带的结构化数据(`ToolResult.details`),供 a2ui 渲染器等下游
|
|
727
|
+
* 消费方读取(如 plugin-deploy-kit 的 `get_deploy_status` 将部署状态放 details 中,
|
|
728
|
+
* a2ui-renderers/deploy-status.ts 读取 `m?.details` 构造 a2ui 组件树)。undefined
|
|
729
|
+
* 表示上游未提供(旧数据/生产者未升级),消费端可回退 null。
|
|
730
|
+
* resultTokensEstimate:本次工具结果**经 clamp/spill 后真正进入模型上下文**那部分内容的
|
|
731
|
+
* token 估算,供状态栏上下文 live 预算使用。它刻意独立于 resultText 预览(512 字符 UI 量级,
|
|
732
|
+
* 用它估算会低估),也不等于原始结果全长(原始可能远超入模上限,用它估算会高估、导致
|
|
733
|
+
* 回合结算时占用条明显回落)。
|
|
734
|
+
*/
|
|
735
|
+
| {
|
|
736
|
+
type: 'tool.call.end';
|
|
737
|
+
sessionId: string;
|
|
738
|
+
toolCall: ToolCallEvent;
|
|
739
|
+
isError: boolean;
|
|
740
|
+
resultText?: string;
|
|
741
|
+
resultTokensEstimate?: number;
|
|
742
|
+
details?: Record<string, unknown>;
|
|
743
|
+
} | {
|
|
744
|
+
type: 'compaction.start';
|
|
745
|
+
sessionId: string;
|
|
746
|
+
messageCount: number;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* `summary`:本次压缩产出的完整摘要文本(可选——手动 forceMemoryTransform 路径与自动
|
|
750
|
+
* keep-recent 路径均能产出,undefined 表示上游未提供/旧数据)。供 `/compact log` 详情
|
|
751
|
+
* 面板展示"压缩成了什么",而非仅有 before/after 计数——用户需要能核实摘要没有丢失
|
|
752
|
+
* 关键信息才敢信任自动压缩。
|
|
753
|
+
*/
|
|
754
|
+
| {
|
|
755
|
+
type: 'compaction.end';
|
|
756
|
+
sessionId: string;
|
|
757
|
+
retainedCount: number;
|
|
758
|
+
summary?: string;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* 终局 review 补齐(2026-07-21):上下文溢出(413)触发的**反应式**压缩——与
|
|
762
|
+
* `compaction.start`/`compaction.end`(token 阈值驱动的主动压缩)是完全独立的触发路径
|
|
763
|
+
* (引擎侧 `work-loop-stream-error.ts` 的 `attemptImageDegradation`/反应式压缩分支),
|
|
764
|
+
* 此前只 emit + record trace,无任何 UI 投影——用户遇到上下文溢出被自动挽救时界面上
|
|
765
|
+
* 悄无声息。`summary` 可选(反应式压缩走 `forceMemoryTransform`,产出摘要文本与主动
|
|
766
|
+
* 压缩路径同源,undefined 表示上游未提供),供 `/compact log` 详情展示。
|
|
767
|
+
*/
|
|
768
|
+
| {
|
|
769
|
+
type: 'compaction.reactive.notice';
|
|
770
|
+
sessionId: string;
|
|
771
|
+
messagesBefore: number;
|
|
772
|
+
messagesAfter: number;
|
|
773
|
+
summary?: string;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* 终局 review 补齐(2026-07-21):图片降级兜底(413/400 image-constraint 触发,见
|
|
777
|
+
* `packages/agent/src/image-degradation.ts`)——为满足供应商图片数量/尺寸限制,丢弃
|
|
778
|
+
* 部分历史图片内容(保留文本占位)后重试。此前同 `compaction.reactive` 全仓零 UI 投影
|
|
779
|
+
* (`engine-nodes.ts` 注释自述"全仓无代码显式监听")。`trigger` 区分触发原因:
|
|
780
|
+
* 'context-overflow' = 上下文整体溢出连带降级图片;'image-constraint' = 供应商图片
|
|
781
|
+
* 专属约束(数量/尺寸)直接拒绝。
|
|
782
|
+
*/
|
|
783
|
+
| {
|
|
784
|
+
type: 'image.degradation.notice';
|
|
785
|
+
sessionId: string;
|
|
786
|
+
degradedCount: number;
|
|
787
|
+
trigger: 'context-overflow' | 'image-constraint';
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* `prunedCount`/`messagesBefore`/`messagesAfter`:裁剪明细(可选,向后兼容旧生产者)。
|
|
791
|
+
* 供 `/compact log` 展示"裁剪了多少条工具输出"而非仅有 tokensSaved 单一数字。
|
|
792
|
+
*/
|
|
793
|
+
| {
|
|
794
|
+
type: 'memory.pruned';
|
|
795
|
+
sessionId: string;
|
|
796
|
+
tokensSaved: number;
|
|
797
|
+
prunedCount?: number;
|
|
798
|
+
messagesBefore?: number;
|
|
799
|
+
messagesAfter?: number;
|
|
800
|
+
} | {
|
|
801
|
+
type: 'session.cleared';
|
|
802
|
+
sessionId: string;
|
|
803
|
+
clearedCount: number;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* 修复(历史裁剪可观测性,2026-07-12;语义澄清 RFC-159 T6,2026-07-13):会话内存态
|
|
807
|
+
* 消息数超上限(`capStoredHistory`,纯条数防御上限,与 memory.pruned 的 token 维度裁剪
|
|
808
|
+
* 是完全不同的机制,勿混)裁剪最早的消息条目时广播——此前该操作零 UI 反馈,长会话(内存
|
|
809
|
+
* 消息数超上限,如默认 2000 条)resume 后用户发现前段历史消失却毫无线索。RFC-159 起持久
|
|
810
|
+
* 层为 append-forever,被裁剪的条目仍完整保留在 DB(默认 SQLite 后端),本事件语义是
|
|
811
|
+
* "内存视图收窄"而非数据删除。`removedCount` 为本次从内存视图移除的条目数。
|
|
812
|
+
*/
|
|
813
|
+
| {
|
|
814
|
+
type: 'session.history-capped';
|
|
815
|
+
sessionId: string;
|
|
816
|
+
removedCount: number;
|
|
817
|
+
/**
|
|
818
|
+
* RFC-178 D6:触发来源分型(可选,缺省视为 'count' 兼容旧生产方)。
|
|
819
|
+
* - 'count':条数上限(SESSION_MAX_HISTORY_MESSAGES,触发器 A)
|
|
820
|
+
* - 'bytes':字节预算(SESSION_MAX_CONTENT_BYTES,触发器 B 硬段)
|
|
821
|
+
* - 'memory-pressure':内存压力收紧(ResidencyGovernor warning/critical 系数,触发器 C)
|
|
822
|
+
* 前端据此分型文案(修正此前"已达内存上限"对条数触发的误导表述)。
|
|
823
|
+
*/
|
|
824
|
+
trigger?: 'count' | 'bytes' | 'memory-pressure'; /** 触发时刻的有效 cap 值(条数触发=条数;字节触发=字节;便于提示与诊断)。 */
|
|
825
|
+
capValue?: number;
|
|
826
|
+
/**
|
|
827
|
+
* RFC-321 R12:本次裁剪是否**有损**——即裁掉的是模型仍在使用的活跃上下文,且没有
|
|
828
|
+
* 摘要覆盖(纯信息丢失),而非"已被摘要替换的死历史"(无损,常态)。
|
|
829
|
+
*
|
|
830
|
+
* 仅在"裁剪会触及活跃上下文 + 逃生阀压缩已连续失败达阈值"时为 true:正常路径下
|
|
831
|
+
* 会先请求压缩建边界并跳过本轮裁剪,裁剪永远只碰死历史。前端应对 `lossy:true` 给出
|
|
832
|
+
* 显著提示(用户有权知道上下文被真实削减),缺省/false 沿用既有轻量提示。
|
|
833
|
+
*/
|
|
834
|
+
lossy?: boolean;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* RFC-181 D6/M3:视觉委托结果(仅在会话主模型不支持视觉输入、且宿主注入了
|
|
838
|
+
* describeImages 端口并被实际调用时触发)。供 TUI 渲染"N 张图已由 vision 模型代述"
|
|
839
|
+
* 一次性提示——`describedCount` 为成功代述的图片数,`failedCount` 为委托失败/降级为
|
|
840
|
+
* 占位文本的图片数(两者之和 = `attemptedCount`)。
|
|
841
|
+
*/
|
|
842
|
+
| {
|
|
843
|
+
type: 'image.vision-delegate';
|
|
844
|
+
sessionId: string;
|
|
845
|
+
attemptedCount: number;
|
|
846
|
+
describedCount: number;
|
|
847
|
+
failedCount: number;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* RFC-159 D3:会话写租约被拒——另一存活进程持有该会话的单写者租约,本进程的落盘被
|
|
851
|
+
* fail-safe 拒绝(只读降级,绝不静默覆盖)。用户可感知"本进程改动不会被保存",
|
|
852
|
+
* 避免多开 --continue 同一会话时静默丢失工作。
|
|
853
|
+
*/
|
|
854
|
+
/**
|
|
855
|
+
* RFC-186 D2:暂停请求已发出,但尚未在 turn 边界生效——当前 in-flight turn 仍在执行。
|
|
856
|
+
* 区别于 run.paused:run.pause_requested 在 pauseRun() 调用时刻同步发布;
|
|
857
|
+
* run.paused 在 turn 边界真正生效后(workLoop break,post-workLoop 检测点)发布。
|
|
858
|
+
*/
|
|
859
|
+
| {
|
|
860
|
+
type: 'run.pause_requested';
|
|
861
|
+
sessionId: string;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* RFC-180:turn 边界软停止已生效——当前 in-flight turn 安全落盘后,agent 不再推进
|
|
865
|
+
* 下一轮(不检查 followUp、不触发 todo 闸门续跑)。区别于 abort:不打断当前工具执行,
|
|
866
|
+
* 会话保持在正常可续跑状态(AgentStatus 仍是 'completed',非 'aborted')。
|
|
867
|
+
* RFC-186 D2:本事件现由 post-workLoop 检测点发布(而非 pauseRun() 调用点),
|
|
868
|
+
* 确保其语义精确表示"暂停已真正生效"。
|
|
869
|
+
*/
|
|
870
|
+
| {
|
|
871
|
+
type: 'run.paused';
|
|
872
|
+
sessionId: string;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* RFC-186 D4:等待中的暂停请求已被取消(仅在 turn 边界到达前有效——
|
|
876
|
+
* cancelPauseRun() 的 isBusy 守卫保证暂停生效后无法取消,只能走 resumeRun())。
|
|
877
|
+
*/
|
|
878
|
+
| {
|
|
879
|
+
type: 'run.pause_cancelled';
|
|
880
|
+
sessionId: string;
|
|
881
|
+
} /** RFC-180:暂停已解除(显式 /continue 或提交新消息隐式触发)。 */ | {
|
|
882
|
+
type: 'run.resumed';
|
|
883
|
+
sessionId: string;
|
|
884
|
+
} | {
|
|
885
|
+
type: 'session.write-lease-denied';
|
|
886
|
+
sessionId: string;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* 2026-07-14:会话写租约已恢复——此前因写租约被拒(session.write-lease-denied)
|
|
890
|
+
* 而只读降级的会话,后续 save 成功获取到租约。用户此前收到的"只读"提示需要一个
|
|
891
|
+
* 对称的"已恢复可写"信号,否则只能靠自行猜测问题是否已解决(另一进程是否已关闭)。
|
|
892
|
+
*/
|
|
893
|
+
| {
|
|
894
|
+
type: 'session.write-lease-restored';
|
|
895
|
+
sessionId: string;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* RFC-305 D3:restore 时 trace↔DB 对账检出崩溃丢失缺口——trace append-log(独立落盘、
|
|
899
|
+
* 逐事件实时写)在 DB 最后一条已持久化消息时间戳**之后**仍记录了 turn.end 事件,判定
|
|
900
|
+
* 上次进程异常终止(OOM/kill)时有已发生但未落库的 turn 永久丢失。仅可见性告警,无自动
|
|
901
|
+
* 恢复(trace 是有损骨架,RFC-102 D1)。对账用时间戳而非 turn 号(trace 的 turn 字段
|
|
902
|
+
* per-prompt 重置,跨 prompt 不可比)。lostTurns = 缺口 turn 数下界估计(已扣除在途容忍)。
|
|
903
|
+
*/
|
|
904
|
+
| {
|
|
905
|
+
type: 'session.crash-gap-detected';
|
|
906
|
+
sessionId: string;
|
|
907
|
+
lostTurns: number; /** DB 侧最后一条已持久化 entry 的时间戳(ms epoch)。 */
|
|
908
|
+
lastPersistedAt: number; /** trace 侧最后一条 turn.end 事件的时间戳(ms epoch)。 */
|
|
909
|
+
lastTraceAt: number;
|
|
910
|
+
} | {
|
|
911
|
+
type: 'approval.required';
|
|
912
|
+
sessionId: string;
|
|
913
|
+
id: string;
|
|
914
|
+
toolName: string;
|
|
915
|
+
arguments: Record<string, unknown>;
|
|
916
|
+
description: string; /** HITL 显示风险等级(权限层派生,仅显示提示;缺省下游回退 medium)。 */
|
|
917
|
+
risk?: 'low' | 'medium' | 'high';
|
|
918
|
+
} | {
|
|
919
|
+
type: 'approval.resolved';
|
|
920
|
+
sessionId: string;
|
|
921
|
+
id: string;
|
|
922
|
+
approved: boolean;
|
|
923
|
+
} | {
|
|
924
|
+
type: 'ask.user.required';
|
|
925
|
+
sessionId: string;
|
|
926
|
+
requestId: string;
|
|
927
|
+
question: GrillQuestion;
|
|
928
|
+
} | {
|
|
929
|
+
type: 'ask.user.resolved';
|
|
930
|
+
sessionId: string;
|
|
931
|
+
requestId: string;
|
|
932
|
+
} | {
|
|
933
|
+
type: 'plan.approval.required';
|
|
934
|
+
sessionId: string;
|
|
935
|
+
requestId: string;
|
|
936
|
+
planContent: string;
|
|
937
|
+
} | {
|
|
938
|
+
type: 'plan.approval.resolved';
|
|
939
|
+
sessionId: string;
|
|
940
|
+
requestId: string;
|
|
941
|
+
action: string;
|
|
942
|
+
}
|
|
943
|
+
/** RFC-105 D6 F5/M105-5c:a2ui 交互回流——web-ui/TUI 用户点击按钮/提交表单等触发的主机回调;
|
|
944
|
+
* 引擎将 actionPayload 注入为一条 user-message 起新回合(等同用户键入),
|
|
945
|
+
* 会话流自然回流结果到 a2ui 插件的数据源。 */
|
|
946
|
+
| {
|
|
947
|
+
type: 'a2ui.action';
|
|
948
|
+
sessionId: string;
|
|
949
|
+
componentId: string;
|
|
950
|
+
action: string;
|
|
951
|
+
payload?: Record<string, unknown>;
|
|
952
|
+
} | {
|
|
953
|
+
type: 'error';
|
|
954
|
+
sessionId: string;
|
|
955
|
+
error: Error;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* RFC-337 D3:一条 steer(运行中追加)消息已被模型消费进本回合上下文——`id` 为该 steer
|
|
959
|
+
* 的稳定 id(`AgentSession.steer()` 返回值)。cli 侧据此把对应的待定 steer echo 转正
|
|
960
|
+
* (commit 到 scrollback),此后该追加不再可撤回(ESC 撤回退化为中止整回合)。
|
|
961
|
+
*/
|
|
962
|
+
| {
|
|
963
|
+
type: 'steer.consumed';
|
|
964
|
+
sessionId: string;
|
|
965
|
+
id: string;
|
|
966
|
+
};
|
|
967
|
+
type AgentSessionSubscriber = (event: AgentSessionEvent) => void;
|
|
968
|
+
type AgentSessionEventMap = { [E in AgentSessionEvent as E['type']]: (event: E) => void };
|
|
969
|
+
//#endregion
|
|
970
|
+
//#region src/engine-command.d.ts
|
|
971
|
+
/**
|
|
972
|
+
* engine-command.ts — RFC-090:renderer→engine 命令协议
|
|
973
|
+
*
|
|
974
|
+
* `EngineCommand` 是可序列化的联合类型,engine/renderer 共享于 @otto/interchange。
|
|
975
|
+
* 硬上限 15 种 command kind。同进程 transport 无需序列化(直接函数调用);
|
|
976
|
+
* 进程分离(RFC-091)后通过 stdin/stdout/WebSocket 传输。
|
|
977
|
+
*/
|
|
978
|
+
type EngineCommand = {
|
|
979
|
+
kind: 'submit';
|
|
980
|
+
input: string;
|
|
981
|
+
} | {
|
|
982
|
+
kind: 'abort';
|
|
983
|
+
} | {
|
|
984
|
+
kind: 'steer';
|
|
985
|
+
input: string;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* RFC-180:请求在当前 turn 边界后暂停(软停止,不打断 in-flight 工具执行)。
|
|
989
|
+
* 命名不用 'pause'(过于通用,易与 DebugCommand 的 'stop'/'continue' 混淆)——
|
|
990
|
+
* `pauseAfterTurn` 显式传达"turn 边界生效"的延迟语义。
|
|
991
|
+
*/
|
|
992
|
+
| {
|
|
993
|
+
kind: 'pauseAfterTurn';
|
|
994
|
+
} /** RFC-180:解除暂停(显式 /continue 命令,或提交新消息时隐式触发)。 */ | {
|
|
995
|
+
kind: 'resumeRun';
|
|
996
|
+
} | {
|
|
997
|
+
kind: 'approval';
|
|
998
|
+
approved: boolean;
|
|
999
|
+
approvalId: string;
|
|
1000
|
+
} | {
|
|
1001
|
+
kind: 'answer';
|
|
1002
|
+
requestId: string;
|
|
1003
|
+
answer: string;
|
|
1004
|
+
} | {
|
|
1005
|
+
kind: 'setModel';
|
|
1006
|
+
modelId: string;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* RFC-303 D8 修正:`replacement` 运行时语义是 `AgentMessage[]`(会话压缩后的替换消息数组),
|
|
1010
|
+
* 但 `AgentMessage` 定义在 `@otto/hook-contracts`(依赖本包 type-only),本包不能反向引用其
|
|
1011
|
+
* 类型——故用 `unknown[]`(准确反映"数组"而非之前声明的 `string`)。此前声明为 `string` 是
|
|
1012
|
+
* 未被检查出的类型错误:发送侧靠 `as unknown as string` 双重断言掩盖真实值仍是数组;接收侧
|
|
1013
|
+
* `engine-main.ts` 曾用 `String(cmd.replacement)` 把数组转成 `"[object Object]"`,破坏数据
|
|
1014
|
+
* (因整体走 `JSON.stringify(cmd)`/`JSON.parse(line)` 序列化,数组结构本身完好,只是消费侧
|
|
1015
|
+
* 类型声明说谎导致错误处理)。消费方须自行按 `AgentMessage[]` 断言(协议层不做该断言)。
|
|
1016
|
+
*/
|
|
1017
|
+
| {
|
|
1018
|
+
kind: 'recordCompaction';
|
|
1019
|
+
summary: string;
|
|
1020
|
+
replacement: unknown[];
|
|
1021
|
+
} | {
|
|
1022
|
+
kind: 'stop';
|
|
1023
|
+
} | {
|
|
1024
|
+
kind: 'updateTodos';
|
|
1025
|
+
todos: unknown[];
|
|
1026
|
+
} | {
|
|
1027
|
+
kind: 'updateEditedFiles';
|
|
1028
|
+
files: unknown[];
|
|
1029
|
+
} | {
|
|
1030
|
+
kind: 'updateSubagents';
|
|
1031
|
+
subagents: unknown[];
|
|
1032
|
+
} | {
|
|
1033
|
+
kind: 'setTurnCount';
|
|
1034
|
+
count: number;
|
|
1035
|
+
};
|
|
1036
|
+
//#endregion
|
|
1037
|
+
//#region src/todo.d.ts
|
|
1038
|
+
/**
|
|
1039
|
+
* Todo 状态与条目 — 单一真源,供 write_todos 等工具跨层消费。
|
|
1040
|
+
* 原在 @otto/orchestration-contracts 与本包各有一份逐字相同的镜像定义
|
|
1041
|
+
* (M-C 沉入 protocol 时未删旧副本,post-RFC-092 review F4 发现并归一)。
|
|
1042
|
+
* @otto/orchestration-contracts/src/primitives.ts 现从本文件 re-export,不再自行定义。
|
|
1043
|
+
*/
|
|
1044
|
+
type TodoStatus = 'pending' | 'in_progress' | 'done' | 'skipped' | 'failed';
|
|
1045
|
+
interface TodoItem {
|
|
1046
|
+
id: string;
|
|
1047
|
+
title: string;
|
|
1048
|
+
status: TodoStatus;
|
|
1049
|
+
}
|
|
1050
|
+
//#endregion
|
|
1051
|
+
//#region src/child-process.d.ts
|
|
1052
|
+
/** Child process: config/input serialization for subprocess communication. */
|
|
1053
|
+
/**
|
|
1054
|
+
* 已启动子进程的注册配置(调用方保证子进程已 spawn,此处仅注册追踪)。
|
|
1055
|
+
* 与 runtime 内部 ProcessSpawnConfig 共享字段语义,但只保留 registerChild 需要的 6 个。
|
|
1056
|
+
*/
|
|
1057
|
+
interface ChildProcessConfig {
|
|
1058
|
+
/** 可执行文件路径。 */
|
|
1059
|
+
command: string;
|
|
1060
|
+
/** 命令行参数。 */
|
|
1061
|
+
args?: string[];
|
|
1062
|
+
/** 进程所有者(如 { type: 'agent-tool', id: 'ripgrep' })。 */
|
|
1063
|
+
owner: {
|
|
1064
|
+
type: string;
|
|
1065
|
+
id: string;
|
|
1066
|
+
};
|
|
1067
|
+
/** 进程分类(如 'tool' / 'lsp' / 'background')。 */
|
|
1068
|
+
category: string;
|
|
1069
|
+
/**
|
|
1070
|
+
* 生命周期:'pinned'(常驻,免 LRU 驱逐)| 'evictable'(短命,可驱逐)。
|
|
1071
|
+
* 默认 evictable。
|
|
1072
|
+
*/
|
|
1073
|
+
lifecycle?: 'pinned' | 'evictable';
|
|
1074
|
+
/** 工作目录。 */
|
|
1075
|
+
cwd?: string;
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* 进程追踪器(最小接口——工具层仅需 registerChild)。
|
|
1079
|
+
* runtime 的 ProcessRuntime 类结构化满足此接口。
|
|
1080
|
+
*/
|
|
1081
|
+
interface ProcessTracker {
|
|
1082
|
+
/**
|
|
1083
|
+
* 注册已 spawn 的子进程到引擎统一生命周期管理。
|
|
1084
|
+
* @returns 进程 pid(调用方可不消费返回值)。
|
|
1085
|
+
*/
|
|
1086
|
+
registerChild(config: ChildProcessConfig, child: {
|
|
1087
|
+
pid?: number;
|
|
1088
|
+
}): {
|
|
1089
|
+
pid: number;
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
//#endregion
|
|
1093
|
+
//#region src/image.d.ts
|
|
1094
|
+
/** Image: base64/mime/url model for image payloads (unified image type). */
|
|
1095
|
+
interface NormalizedImage {
|
|
1096
|
+
/** 裸 base64(已去空白),不含 `data:...;base64,` 前缀。 */
|
|
1097
|
+
base64: string;
|
|
1098
|
+
/** 尽力得到的 MIME(小写),如 `image/png`;无法判定时为 ''。 */
|
|
1099
|
+
mediaType: string;
|
|
1100
|
+
/**
|
|
1101
|
+
* base64 是否可解码:非空 + 合法 charset + 长度 %4≠1 + 不是字面量 "undefined"。
|
|
1102
|
+
* 为 false 时调用方应**降级为文本占位**而非把坏数据发往 API(否则整请求 400)。
|
|
1103
|
+
*/
|
|
1104
|
+
valid: boolean;
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* 把图片来源归一为 { 裸 base64, mediaType, valid }。
|
|
1108
|
+
* 接受两种形态:完整 data URI 或裸 base64。
|
|
1109
|
+
* mediaType 优先取 data URI 内声明的 mime,其次回退到显式传入的 mime。
|
|
1110
|
+
*/
|
|
1111
|
+
declare function normalizeImageData(source: string, mime?: string): NormalizedImage;
|
|
1112
|
+
/**
|
|
1113
|
+
* 终局架构 review 建议优化项:类型名从 AnthropicImageMime 改为 CommonImageMime——这 4 种
|
|
1114
|
+
* 格式(jpeg/png/gif/webp)是业界广泛支持的通用图片格式,非 Anthropic 独有(OpenAI 等
|
|
1115
|
+
* provider 同样接受这 4 种)。保留 `toAnthropicImageMime` 函数名不变——该名字准确描述了
|
|
1116
|
+
* 它的真实用途("归一到 Anthropic API 要求的枚举值",函数体内的兜底逻辑与 400 错误规避
|
|
1117
|
+
* 都是针对 Anthropic API 的具体行为),改类型名不改函数名两者互不矛盾:类型是通用值域,
|
|
1118
|
+
* 函数是该值域到 Anthropic 特定契约的映射。
|
|
1119
|
+
*/
|
|
1120
|
+
type CommonImageMime = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
|
|
1121
|
+
/** @deprecated 改名为 CommonImageMime(类型语义修正,非厂商专属)。保留别名过渡,下个大版本移除。 */
|
|
1122
|
+
type AnthropicImageMime = CommonImageMime;
|
|
1123
|
+
/**
|
|
1124
|
+
* 归一到 Anthropic 接受的 4 种图片 MIME 之一。
|
|
1125
|
+
* 未知 mediaType 时按 base64 魔数嗅探,仍无法判定回退 image/png。
|
|
1126
|
+
* **永不返回空**——避免 media_type 字段缺失被 Anthropic API 以 "Field required" 拒绝。
|
|
1127
|
+
*/
|
|
1128
|
+
declare function toAnthropicImageMime(mediaType: string, base64: string): CommonImageMime;
|
|
1129
|
+
//#endregion
|
|
1130
|
+
//#region src/plugin-perf.d.ts
|
|
1131
|
+
/**
|
|
1132
|
+
* plugin-perf.ts — 插件性能采集器结果的共享契约(RFC-118 小修,review S10)。
|
|
1133
|
+
*
|
|
1134
|
+
* 背景:RFC-116 时 coding(生产侧 collect-plugin-perf-metrics.ts)与 tui(消费侧
|
|
1135
|
+
* perf-store.ts)各自手动镜像定义此类型,无编译期同步——coding 侧新增字段 tui 不感知。
|
|
1136
|
+
* 上移 protocol 后两侧 import 同源,镜像漂移消除。
|
|
1137
|
+
*/
|
|
1138
|
+
/** 单个插件采集器的结果——成功时 `value` 有值,失败时 `error` 有值,二者互斥。 */
|
|
1139
|
+
interface PluginPerfMetricResult {
|
|
1140
|
+
id: string;
|
|
1141
|
+
label: string;
|
|
1142
|
+
value?: unknown;
|
|
1143
|
+
error?: string;
|
|
1144
|
+
}
|
|
1145
|
+
//#endregion
|
|
1146
|
+
//#region src/input.d.ts
|
|
1147
|
+
/** Input: mentions, sigils, content providers — user input enrichment types. */
|
|
1148
|
+
/** sigil 子类型:`hashtag` 属 `#`,其余属 `@`。`resource` = 插件自定义资源(RFC-210 D2);
|
|
1149
|
+
* `shortcut` = 用户在本仓库自定义的文本快捷方式(RFC-233)。 */
|
|
1150
|
+
type SigilKind = 'hashtag' | 'file' | 'person' | 'agent' | 'skill' | 'mcp' | 'plugin' | 'model' | 'resource' | 'shortcut';
|
|
1151
|
+
/** 触发前缀。 */
|
|
1152
|
+
type SigilPrefix = '#' | '@';
|
|
1153
|
+
/** 条目来源标识(unregisterBySource 据此批量注销,防僵尸条目)。 */
|
|
1154
|
+
type SigilSource = 'builtin' | 'plugin' | 'skill' | 'mcp' | (string & {});
|
|
1155
|
+
/**
|
|
1156
|
+
* 动态供给函数(pull 模型):按 query 懒查候选,服务 @file 等大候选集——
|
|
1157
|
+
* 不全量 register()。可同步或异步(文件系统遍历走异步)。
|
|
1158
|
+
*/
|
|
1159
|
+
type SigilProvider = (query: string, sigil: SigilPrefix) => SigilEntry[] | Promise<SigilEntry[]>;
|
|
1160
|
+
/** sigil 面板条目:label(展示)与 value(替换内容)严格分离(R2)。 */
|
|
1161
|
+
interface SigilEntry {
|
|
1162
|
+
/** 面板中展示的名称(简短、人类可读)。绝不进入 prompt 文本。 */
|
|
1163
|
+
label: string;
|
|
1164
|
+
/** 选中后替换 #trigger/@trigger 的实质内容。绝不在面板中完整展示。 */
|
|
1165
|
+
value: string;
|
|
1166
|
+
/** 面板中展示的描述(可选,列表副标题)。 */
|
|
1167
|
+
description?: string;
|
|
1168
|
+
/** 条目类型(影响面板分组/着色,并决定属于 # 还是 @)。 */
|
|
1169
|
+
kind: SigilKind;
|
|
1170
|
+
/** 来源标识(builtin | plugin | skill | mcp | 自定义 pluginId)。 */
|
|
1171
|
+
source?: SigilSource;
|
|
1172
|
+
/**
|
|
1173
|
+
* RFC-210 D2:resolve 引用。非空 = "提交时展开"型资源——展开管线(app.expandSigilChips)
|
|
1174
|
+
* 用此 id 查 SigilResolverRegistry 拿 resolve 函数,把该条目的 chip 替换为其返回值。
|
|
1175
|
+
* 空 = 透传型(chip 原样给 LLM)或纯文本型(value 即内容),行为与现状完全一致。
|
|
1176
|
+
*/
|
|
1177
|
+
resolverId?: string;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* RFC-210 D2:@ 资源提交时展开函数(代码契约——不可序列化,归 coding 层
|
|
1181
|
+
* SigilResolverRegistry,与纯数据的 PluginInputRegistry 平行;D9 两类契约纪律)。
|
|
1182
|
+
*/
|
|
1183
|
+
interface SigilResolver {
|
|
1184
|
+
/** resolver id,命名空间 `<pluginId>:<name>`(内置无前缀)。 */
|
|
1185
|
+
id: string;
|
|
1186
|
+
/** 提交时展开:输入 chip 参数(`[@kind:param]` 的 param 段),返回发给 LLM 的内容。 */
|
|
1187
|
+
resolve: (param: string, ctx: SigilResolveContext) => string | Promise<string>;
|
|
1188
|
+
/** 超时(ms,缺省 10_000,对齐既有 CONTEXT_CHIPS 约束)。 */
|
|
1189
|
+
timeoutMs?: number;
|
|
1190
|
+
/**
|
|
1191
|
+
* uiHostOnly(RFC-210 评审 B11):true = 读取"UI 所在机器"的本地资源(非 workspaceDir
|
|
1192
|
+
* 内容),只应在与用户同机的宿主进程(TUI/cli)执行;service expand 端点跳过执行。
|
|
1193
|
+
* @clipboard = true(读桌面剪贴板);@changes/@git = false(读 workspaceDir git 状态,
|
|
1194
|
+
* service 为远程 workspace 服务时服务器上的状态正是用户要的内容)。
|
|
1195
|
+
*/
|
|
1196
|
+
uiHostOnly?: boolean;
|
|
1197
|
+
}
|
|
1198
|
+
/** SigilResolver.resolve 的执行上下文。 */
|
|
1199
|
+
interface SigilResolveContext {
|
|
1200
|
+
workspaceDir: string;
|
|
1201
|
+
/** 输出上限(字符,缺省 20_000,超限截断——对齐既有 MAX_OUTPUT)。 */
|
|
1202
|
+
maxOutput: number;
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* 插件输入注册表(UI 无关,由 createApp() 创建单例,实现留 @otto/coding)。
|
|
1206
|
+
*
|
|
1207
|
+
* 静态 push 供给(register)+ 动态 pull 供给(registerProvider,服务 @file
|
|
1208
|
+
* 大候选集懒查,RFC §1.3.1 F-domain / RFC-044 M044-07)。
|
|
1209
|
+
*/
|
|
1210
|
+
interface PluginInputRegistry {
|
|
1211
|
+
/** 注册一批条目(last-write-wins,按 (label, kind) 复合键去重)。 */
|
|
1212
|
+
register(entries: SigilEntry[]): void;
|
|
1213
|
+
/**
|
|
1214
|
+
* 注册某 sigil 的动态供给器(pull 模型,按 query 懒查,如 @file)。
|
|
1215
|
+
* 返回注销函数。其结果在 searchAsync 中与静态条目合并。
|
|
1216
|
+
*/
|
|
1217
|
+
registerProvider(sigil: SigilPrefix, provider: SigilProvider): () => void;
|
|
1218
|
+
/**
|
|
1219
|
+
* 按 query + sigil 过滤匹配条目。过滤在此完成(R4),UI 只渲染结果。
|
|
1220
|
+
* 命中数达 maxResults 立即返回,不全量遍历(F-domain 性能)。
|
|
1221
|
+
* 仅静态条目;动态 provider 结果见 searchAsync。
|
|
1222
|
+
*/
|
|
1223
|
+
search(query: string, sigil?: SigilPrefix, maxResults?: number): SigilEntry[];
|
|
1224
|
+
/**
|
|
1225
|
+
* 异步搜索:静态条目(即时)+ 动态 provider 结果(await)合并去重。
|
|
1226
|
+
* UI 应优先用此方法以纳入 @file 等懒查候选;无 provider 时等价 search。
|
|
1227
|
+
*/
|
|
1228
|
+
searchAsync(query: string, sigil?: SigilPrefix, maxResults?: number): Promise<SigilEntry[]>;
|
|
1229
|
+
/** 返回某 sigil(或全部)的全量条目(只读快照)。 */
|
|
1230
|
+
getAll(sigil?: SigilPrefix): readonly SigilEntry[];
|
|
1231
|
+
/** 按 label 查首个匹配条目。 */
|
|
1232
|
+
findByLabel(label: string): SigilEntry | undefined;
|
|
1233
|
+
/** 注销某 label 的所有条目,返回是否删除过。 */
|
|
1234
|
+
unregister(label: string): boolean;
|
|
1235
|
+
/** 注销某 source 的所有条目(插件 unload 必调,防僵尸——R5)。 */
|
|
1236
|
+
unregisterBySource(source: string): void;
|
|
1237
|
+
}
|
|
1238
|
+
/** 根据 kind 推断其归属的 sigil 前缀。 */
|
|
1239
|
+
declare function sigilOf(kind: SigilKind): SigilPrefix;
|
|
1240
|
+
/** sigil 触发信息:检测到的 sigil 字符 + 查询串 + 锚点位置。 */
|
|
1241
|
+
interface SigilTrigger {
|
|
1242
|
+
/** 触发的 sigil 字符。 */
|
|
1243
|
+
sigil: SigilPrefix;
|
|
1244
|
+
/** sigil 后的查询串(不含 sigil 字符本身)。 */
|
|
1245
|
+
query: string;
|
|
1246
|
+
/** sigil 字符在文本中的起始偏移(替换时从此处删到 cursorOffset)。 */
|
|
1247
|
+
anchor: number;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* 从光标前文本提取 sigil 触发。无触发返回 null。
|
|
1251
|
+
* anchor 指向 sigil 字符位置(替换区间 = [anchor, cursorOffset))。
|
|
1252
|
+
*/
|
|
1253
|
+
declare function extractSigilPrefix(input: string, cursorOffset: number): SigilTrigger | null;
|
|
1254
|
+
/**
|
|
1255
|
+
* 设计问题 #5 / RFC-212 D1:判断当前触发是否落在「已被 Esc 驳回」的同一 sigil token 上。
|
|
1256
|
+
* 为真则不应弹回面板——用户 Esc 关掉后继续在同一 `@`/`#` 上输入,不再打扰。
|
|
1257
|
+
* 比较只看 sigil + anchor(token 身份)——anchor 由 `adjustDismissedSigil` 随编辑动态位移,
|
|
1258
|
+
* 不是原始触发时的绝对位置,故本函数无需再关心"位置是否漂移"。
|
|
1259
|
+
*/
|
|
1260
|
+
declare function isSigilDismissed(trig: Pick<SigilTrigger, 'sigil' | 'anchor'>, dismissed: Pick<SigilTrigger, 'sigil' | 'anchor'> | null): boolean;
|
|
1261
|
+
/**
|
|
1262
|
+
* RFC-212 D1:token 身份绑定的驳回记录。`tokenLength` 锚定在驳回快照时刻
|
|
1263
|
+
* (= `cursorOffset - anchor`,即 1 + query.length),后续编辑不动态重算,
|
|
1264
|
+
* 语义见 `adjustDismissedSigil` 的四类区间判定。
|
|
1265
|
+
*/
|
|
1266
|
+
interface DismissedSigil {
|
|
1267
|
+
sigil: SigilPrefix;
|
|
1268
|
+
anchor: number;
|
|
1269
|
+
tokenLength: number;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* RFC-212 D1 状态机核心:给定旧驳回记录与本次文本编辑(oldText → newText),
|
|
1273
|
+
* 计算编辑后应生效的驳回记录(`null` = 清态回 IDLE,编辑摧毁了 token 或与其无关)。
|
|
1274
|
+
* 纯函数,不依赖 React state/ref,可独立单测。
|
|
1275
|
+
*
|
|
1276
|
+
* 判定依据编辑区间 `[start, end)`(old 坐标系)与 token 范围 `[anchor, anchor+tokenLength)`
|
|
1277
|
+
* 的相对关系,四类穷举(`anchor < tokenEnd` 恒成立,`start <= end` 恒成立):
|
|
1278
|
+
*
|
|
1279
|
+
* 1. `end <= anchor`(编辑完全在 token 之前)→ 锚点位移:`anchor += delta`,tokenLength 不变。
|
|
1280
|
+
* 2. `start >= tokenEnd`(编辑完全在 token 之后,"离开"场景)→ anchor/tokenLength 不变;
|
|
1281
|
+
* 是否真的维持驳回态,由调用方比较新触发的 anchor 是否等于此处的 anchor 决定
|
|
1282
|
+
* (`isSigilDismissed`)——本函数只负责"token 本身未被触碰"这一半的判定。
|
|
1283
|
+
* 3. `start > anchor && start < tokenEnd`(编辑落在 token 内部、不含 `@`/`#` 字符本身)
|
|
1284
|
+
* → 维持驳回态,anchor/tokenLength 均不变(不因编辑扩大 tokenLength——用户打字越过
|
|
1285
|
+
* 快照边界即落入情形 2,交由新触发比较决定去留,而非线性扩张判定区间)。
|
|
1286
|
+
* 4. 其余(`start <= anchor` 且与 token 有重叠,即编辑波及了 sigil 字符本身)
|
|
1287
|
+
* → 清态回 IDLE(token 身份已被破坏)。
|
|
1288
|
+
*/
|
|
1289
|
+
declare function adjustDismissedSigil(dismissed: DismissedSigil | null, oldText: string, newText: string): DismissedSigil | null;
|
|
1290
|
+
/**
|
|
1291
|
+
* 用选中条目的 value 替换 [anchor, cursorOffset) 区间(即 `#query`/`@query`)。
|
|
1292
|
+
* 返回新文本与新光标偏移(落在替换内容末尾)。
|
|
1293
|
+
*
|
|
1294
|
+
* 注意:value 原样注入(含换行/markdown 不校验,R2)。
|
|
1295
|
+
* 调用方应走 PromptInput.update() 应用结果,以重算 detectMode(D7:# 替换后 mode 通常变 'send')。
|
|
1296
|
+
*/
|
|
1297
|
+
declare function applySigilCompletion(input: string, cursorOffset: number, anchor: number, entry: SigilEntry): {
|
|
1298
|
+
text: string;
|
|
1299
|
+
offset: number;
|
|
1300
|
+
};
|
|
1301
|
+
/**
|
|
1302
|
+
* RFC-212 D3:用选中条目的 value 替换一个**已存在 chip**的 `[chipStart, chipEnd)` 区间
|
|
1303
|
+
* ——与 `applySigilCompletion`(从 anchor 插入,输入是"用户刚打的 @query")语义不同:
|
|
1304
|
+
* 这里输入是"光标停靠在一个已展开的 chip 上,用户按 Enter 重新选择替换它",替换区间是
|
|
1305
|
+
* chip 的实际边界,不涉及 anchor/query 概念。返回新文本与新光标偏移(落在替换内容末尾)。
|
|
1306
|
+
*
|
|
1307
|
+
* 注意:与 `applySigilCompletion` 不同,本函数不追加尾随空格——chip 替换场景两侧通常
|
|
1308
|
+
* 已有边界(chip 本身的方括号,或前后文本),追加空格会破坏原有间距,交由调用方决定
|
|
1309
|
+
* 是否需要额外分隔符。
|
|
1310
|
+
*/
|
|
1311
|
+
declare function applySigilReplacement(input: string, chipStart: number, chipEnd: number, entry: SigilEntry): {
|
|
1312
|
+
text: string;
|
|
1313
|
+
offset: number;
|
|
1314
|
+
};
|
|
1315
|
+
/**
|
|
1316
|
+
* RFC-215 D1:文本变更来源。每个修改输入框文本的路径必须声明自己是谁——
|
|
1317
|
+
* 取代旧 `detectSigil: boolean`(普查发现 16 类调用点中 10 类语义错误,布尔无法
|
|
1318
|
+
* 强迫调用方思考来源语义,见 RFC-215 §0.4)。
|
|
1319
|
+
*
|
|
1320
|
+
* sigil 触发准入的唯一判定:仅 `'typing'` 允许进入检测(`evaluateSigilTrigger`)。
|
|
1321
|
+
* 新增来源归类不确定时选 `'programmatic'`(最保守:不触发任何检测)。
|
|
1322
|
+
*/
|
|
1323
|
+
type TextChangeSource = /** 用户逐字键入/删除(editKey 路径、IME 多字符 chunk)。 */'typing' /** 历史导航召回、搜索回填、/history 面板回填、外部编辑器写回、undo 回退、提交阻断跳转。 */ | 'programmatic' /** 粘贴插入(含短粘贴直插与大段折叠后插入)——RFC-215 D2:不触发 sigil 面板。 */ | 'paste'
|
|
1324
|
+
/**
|
|
1325
|
+
* ghost/typeahead/shell 补全接受、sigil 补全/删除/chip 替换回写。
|
|
1326
|
+
* 当前对 sigil 检测的行为等价 `'programmatic'`(都不触发),预留语义位——
|
|
1327
|
+
* 补全接受保留 typeahead 上下文语义、程序回填重置一切瞬态,未来分叉时不需改签名
|
|
1328
|
+
* (RFC-215 D1,评审 F2:勿因"有值无独立消费"误删)。
|
|
1329
|
+
*/
|
|
1330
|
+
| 'completion';
|
|
1331
|
+
/** `evaluateSigilTrigger` 的判定结果(RFC-215 D3)。 */
|
|
1332
|
+
interface SigilTriggerDecision {
|
|
1333
|
+
/**
|
|
1334
|
+
* - `'open'`:产生新触发,调用方应设触发上下文并打开面板。
|
|
1335
|
+
* - `'suppress'`:本次变更不产生新触发,也不主动关闭已开面板——调用方仅清触发上下文,
|
|
1336
|
+
* 面板关闭交由既有失焦/Esc 机制(与旧 `!detectSigil` 分支行为一致)。
|
|
1337
|
+
* - `'close'`:触发条件已消失(原 `@query` 被编辑破坏),调用方应清触发上下文并
|
|
1338
|
+
* 通知宿主关闭面板(宿主未接线关闭回调时退化为 suppress 行为,向后兼容)。
|
|
1339
|
+
*/
|
|
1340
|
+
action: 'open' | 'suppress' | 'close';
|
|
1341
|
+
/** action='open' 时的触发信息。 */
|
|
1342
|
+
trigger?: SigilTrigger;
|
|
1343
|
+
/** 经 `adjustDismissedSigil` 位移后的驳回态(任何来源都执行位移;open 时已清为 null)。 */
|
|
1344
|
+
dismissed: DismissedSigil | null;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* RFC-215 D3:sigil 触发准入状态机(纯函数)。与 `adjustDismissedSigil`(RFC-212 D1,
|
|
1348
|
+
* 驳回态维护)合并为完整的一台触发机器——本函数管"是否触发",后者管"已驳回状态下
|
|
1349
|
+
* 是否重新触发",调用顺序内聚在此,外部只消费 decision。
|
|
1350
|
+
*
|
|
1351
|
+
* 判定顺序(RFC-215 D3,六步):
|
|
1352
|
+
* ① 位移驳回态(任何来源都执行——粘贴/回填同样会移动被驳回 token 的位置,
|
|
1353
|
+
* 跳过位移会复发 RFC-212 缺陷 1;重要事项规则 3);
|
|
1354
|
+
* ② 非 `'typing'` 来源 → suppress(历史导航/粘贴/补全接受不触发面板——本 RFC 的 bug 修复本体);
|
|
1355
|
+
* ③ 文本非增长 → suppress(保留既有 `grew` 优化语义:删除/替换不产生新触发);
|
|
1356
|
+
* ④ 无 sigil 触发命中(或被 `sigilFilter` 过滤)→ close(原触发被编辑破坏,面板应关闭);
|
|
1357
|
+
* ⑤ 触发命中但落在已驳回 token 上 → suppress(Esc 驳回后继续输入不再打扰);
|
|
1358
|
+
* ⑥ 触发命中且未被驳回 → open + 清驳回态。
|
|
1359
|
+
*
|
|
1360
|
+
* `sigilFilter` 缺省 `(sigil) => sigil === '@'`——与 TUI 现状的 @-only 硬过滤一致
|
|
1361
|
+
* (`#` 走 memory 模式检测,不在本函数管辖;评审 F4-2)。
|
|
1362
|
+
*/
|
|
1363
|
+
declare function evaluateSigilTrigger(input: {
|
|
1364
|
+
source: TextChangeSource;
|
|
1365
|
+
oldText: string;
|
|
1366
|
+
newText: string;
|
|
1367
|
+
cursorOffset: number;
|
|
1368
|
+
dismissed: DismissedSigil | null;
|
|
1369
|
+
sigilFilter?: (sigil: SigilPrefix) => boolean;
|
|
1370
|
+
}): SigilTriggerDecision;
|
|
1371
|
+
//#endregion
|
|
1372
|
+
export { type A2uiComponent, type A2uiContent, type AgentSessionEvent, type AgentSessionEventMap, type AgentSessionEventType, type AgentSessionSubscriber, type AgentTool, type AnthropicImageMime, type Api, type AskUserQuestion, type AssistantMessage, CONVERSATION_SUMMARY_PREFIX, type ChildProcessConfig, type CommonImageMime, type ContentPart, type Cost, type DismissedSigil, type EngineCommand, type GrillAnswer, type GrillOption, type GrillQuestion, type GrillRequest, type ImageContent, type KnownApi, type KnownProvider, type Message, type Model, type ModelSpec, type ModelStrength, type NormalizedImage, type PluginInputRegistry, type PluginPerfMetricResult, type ProcessTracker, type PromptStopReason, type Provider, type ProviderUsageSnapshot, type SigilEntry, type SigilKind, type SigilPrefix, type SigilProvider, type SigilResolveContext, type SigilResolver, type SigilSource, type SigilTrigger, type SigilTriggerDecision, type StopReason, type StreamEvent, type SystemNotificationMessage, type TextChangeSource, type TextContent, type ThinkingContent, type ThinkingLevel, type TodoItem, type TodoStatus, type ToolCall, type ToolCallContext, type ToolCallEvent, type ToolDescriptionContext, type ToolErrorKind, type ToolResult, type ToolResultMessage, type Usage, type UserMessage, type ZodType, adjustDismissedSigil, applySigilCompletion, applySigilReplacement, evaluateSigilTrigger, extractSigilPrefix, extractToolResultText, getToolCallsByAssistantMessage, hasToolCalls, isSigilDismissed, modelSupportsImages, normalizeImageData, sigilOf, toAnthropicImageMime };
|
|
1373
|
+
//# sourceMappingURL=index.d.ts.map
|