@xyz-agent/extension-protocol 0.3.1 → 0.5.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/dist/index.d.mts +55 -20
- package/dist/index.mjs +15 -11
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -49,6 +49,14 @@ interface GuiComponentProps {
|
|
|
49
49
|
/** 列表树——替代 TUI 的 ⎿ ├─ └─ 缩进 */
|
|
50
50
|
'list-tree': {
|
|
51
51
|
items: TreeItem[];
|
|
52
|
+
/** 行首显示弱化序号(1/2/3…,mono tabular-nums)。扁平有序清单用(todo);
|
|
53
|
+
* 自带编号的文本(goal criteria "1. xxx")不要开,避免双重编号 */
|
|
54
|
+
numbered?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** 垂直组合容器——无视觉样式的透明分组。宿主壳层(WidgetArea)承担卡壳/head/折叠
|
|
57
|
+
* 后,widget 内容需要多组件组合时的组合根(替代「无头 card」的语义滥用) */
|
|
58
|
+
'group': {
|
|
59
|
+
children: GuiComponent[];
|
|
52
60
|
};
|
|
53
61
|
/** 双列网格——替代 TUI 的 │ 列分隔 */
|
|
54
62
|
'columns': {
|
|
@@ -73,6 +81,31 @@ interface GuiRenderResult {
|
|
|
73
81
|
/** 版本协商,前端检测,不认识降级 ansi-text */
|
|
74
82
|
v: typeof PROTOCOL_VERSION;
|
|
75
83
|
component: GuiComponent;
|
|
84
|
+
/**
|
|
85
|
+
* widget 宿主元数据(M17 对话流 widget 面板消费):标题/状态点/进度计数由
|
|
86
|
+
* 宿主壳层统一渲染成单一 head(含折叠交互),extension 不再用 card 原语
|
|
87
|
+
* 的 header 表达这些(壳层 head 与 payload card header 双头重复的根因修复)。
|
|
88
|
+
* 可选:不发时宿主 fallback 到 viewId 标题、无状态点/进度。
|
|
89
|
+
*/
|
|
90
|
+
meta?: WidgetMeta;
|
|
91
|
+
}
|
|
92
|
+
/** widget 宿主元数据——head 渲染契约(title + 状态点 + 进度 + 折叠 chevron)。 */
|
|
93
|
+
interface WidgetMeta {
|
|
94
|
+
/** head 标题(todo → "Todo";goal → slug) */
|
|
95
|
+
title: string;
|
|
96
|
+
/** head 状态点语义:running=accent / done=success / failed=danger / idle=neutral 弱点 */
|
|
97
|
+
status?: 'running' | 'done' | 'failed' | 'idle';
|
|
98
|
+
/** head 进度(mini bar + 计数文本);progress-bar 原语从 body 移入 head 的承载 */
|
|
99
|
+
progress?: {
|
|
100
|
+
/** fill 比例 = current/total */
|
|
101
|
+
current: number;
|
|
102
|
+
total: number;
|
|
103
|
+
/** 计数显示文本(head 空间有限,extension 全权格式化:todo "2/5"、goal "42%")。
|
|
104
|
+
* 缺省 `${current}/${total}` */
|
|
105
|
+
label?: string;
|
|
106
|
+
/** fill 语义色(预算阈值映射);缺省按 meta.status(done→success,否则 accent) */
|
|
107
|
+
severity?: 'ok' | 'warn' | 'danger';
|
|
108
|
+
};
|
|
76
109
|
}
|
|
77
110
|
interface StatItem {
|
|
78
111
|
label?: string;
|
|
@@ -135,23 +168,26 @@ interface GuiContext {
|
|
|
135
168
|
*/
|
|
136
169
|
declare function isGuiCapable(ctx: GuiContext): boolean;
|
|
137
170
|
/**
|
|
138
|
-
* 构造 GuiRenderResult
|
|
171
|
+
* 构造 GuiRenderResult。当前主用途是 guiSetWidget 的载荷(M17 对话流 widget 面板);
|
|
172
|
+
* details.__gui__(tool result 通道)为遗留兼容路径,todo/goal 已不再使用。
|
|
139
173
|
* stripUndefined 确保序列化不含 undefined(JSON.stringify 会丢弃 undefined 字段)。
|
|
140
174
|
*/
|
|
141
|
-
declare function guiResult(component: GuiComponent): GuiRenderResult;
|
|
175
|
+
declare function guiResult(component: GuiComponent, meta?: WidgetMeta): GuiRenderResult;
|
|
142
176
|
/**
|
|
143
177
|
* 构造 GuiComponent,带类型推断。
|
|
144
178
|
* 类型参数 T 约束 props 到对应类型的 props 形状。
|
|
145
179
|
*/
|
|
146
180
|
declare function guiComponent<T extends GuiComponentType>(type: T, props: GuiComponentProps[T]): GuiComponent<T>;
|
|
147
181
|
/**
|
|
148
|
-
* 设置 GUI widget。RPC 模式下用 marker 编码
|
|
149
|
-
* runtime event-adapter 检测 marker 解码为结构化 WS
|
|
150
|
-
*
|
|
182
|
+
* 设置 GUI widget。RPC 模式下用 marker 编码 GuiRenderResult JSON 进 string[],
|
|
183
|
+
* runtime event-adapter 检测 marker 解码为结构化 WS 帧(component + meta)。
|
|
184
|
+
* 本函数无 isGui 守卫(仅查 ctx.ui?.setWidget 存在性),调用方需先判定
|
|
185
|
+
* isGuiCapable(ctx)——TUI 模式误调会把 marker 编码行推给原生 widget 造成乱码。
|
|
151
186
|
*
|
|
152
|
-
*
|
|
187
|
+
* meta(标题/状态/进度)由宿主壳层渲染成统一 head;extension 不再用 card 原语
|
|
188
|
+
* header 表达这些。传 undefined 清除 widget。
|
|
153
189
|
*/
|
|
154
|
-
declare function guiSetWidget(ctx: GuiContext, key: string,
|
|
190
|
+
declare function guiSetWidget(ctx: GuiContext, key: string, result: GuiRenderResult | undefined): void;
|
|
155
191
|
/**
|
|
156
192
|
* 从 details 中提取 GuiRenderResult。前端统一用此函数读取 __gui__,
|
|
157
193
|
* 集中校验版本号,避免散落的 as 断言。
|
|
@@ -163,6 +199,13 @@ declare function extractGui(details: Record<string, unknown> | undefined): GuiRe
|
|
|
163
199
|
* 不校验 type 是否为已知值(GuiComponentRenderer 对未知 type 有 AnsiText 降级)。
|
|
164
200
|
*/
|
|
165
201
|
declare function isGuiComponent(value: unknown): value is GuiComponent;
|
|
202
|
+
/**
|
|
203
|
+
* v1.1 wire 格式校验:判断 unknown 值是否为合法 GuiRenderResult 信封
|
|
204
|
+
* (v === PROTOCOL_VERSION + component 是合法 GuiComponent)。meta 可选不校验深度。
|
|
205
|
+
* event-adapter 据此区分 v1.1 信封(解包 component + meta)与 v1 裸 component
|
|
206
|
+
* (旧版 extension 发出的 wire 格式,兼容窗口内两种并存)。
|
|
207
|
+
*/
|
|
208
|
+
declare function isGuiRenderResult(value: unknown): value is GuiRenderResult;
|
|
166
209
|
|
|
167
210
|
/**
|
|
168
211
|
* ask-user extension 的富交互类型定义。
|
|
@@ -195,14 +238,10 @@ interface AskUserQuestion {
|
|
|
195
238
|
* - 有 options 时:默认 true,前端在选项末尾追加 Other 输入框;设 false 则不追加
|
|
196
239
|
* - 无 options 时:整个问题就是自由输入,此字段被忽略 */
|
|
197
240
|
allowOther?: boolean;
|
|
198
|
-
/** 是否允许附加评论。选中后可追加短文本(4.0.1 restore:0.3.0 误删导致 pi-ask-user@4.0.0 ESM import 崩溃,见 .changeset/restore-ask-user-comment.md) */
|
|
199
|
-
allowComment?: boolean;
|
|
200
241
|
}
|
|
201
242
|
interface AskUserOption {
|
|
202
|
-
/**
|
|
243
|
+
/** 显示标签,回传时作为选中值(D1:proto 无独立 value,选中值统一用 label) */
|
|
203
244
|
label: string;
|
|
204
|
-
/** 回传值。未提供时用 label */
|
|
205
|
-
value?: string;
|
|
206
245
|
/** 描述(可选)。显示在 label 下方,解释 tradeoff */
|
|
207
246
|
description?: string;
|
|
208
247
|
}
|
|
@@ -210,16 +249,14 @@ interface AskUserOption {
|
|
|
210
249
|
* ask-user 富交互回传结果。key = question.header(header 缺失时用 question 文本)。
|
|
211
250
|
*
|
|
212
251
|
* 答案编码规则(避免逗号歧义):
|
|
213
|
-
* - 单选:value = 选中项的
|
|
214
|
-
* - 多选:value = JSON.stringify(选中项
|
|
215
|
-
* (不用逗号 join——option
|
|
252
|
+
* - 单选:value = 选中项的 label
|
|
253
|
+
* - 多选:value = JSON.stringify(选中项 label 数组),如 '["pg","mysql"]'
|
|
254
|
+
* (不用逗号 join——option label 可能含逗号导致 split 歧义)
|
|
216
255
|
* - Other 文本:单独 key `${header}__other`,value = 自由文本(不混进选中项数组)
|
|
217
|
-
* - comment:单独 key `${header}__comment`,value = 评论文本
|
|
218
256
|
*
|
|
219
257
|
* extension 解析示例:
|
|
220
258
|
* const selected = JSON.parse(answers[header]) // 多选 → string[]
|
|
221
259
|
* const other = answers[`${header}__other`] // Other 自由文本
|
|
222
|
-
* const comment = answers[`${header}__comment`] // 评论
|
|
223
260
|
*/
|
|
224
261
|
type AskUserAnswers = Record<string, string>;
|
|
225
262
|
|
|
@@ -266,12 +303,10 @@ declare function askUserInteract(ctx: GuiContext, questions: AskUserQuestion[],
|
|
|
266
303
|
declare function getAskUserAnswer(answers: AskUserAnswers, question: AskUserQuestion): string | string[] | undefined;
|
|
267
304
|
/** 从 answers 中提取 Other 自由文本 */
|
|
268
305
|
declare function getAskUserOther(answers: AskUserAnswers, question: AskUserQuestion): string | undefined;
|
|
269
|
-
/** 从 answers 中提取评论(4.0.1 restore:ask-user TUI 评论模式与 renderer AskUserOverlay 仍消费此 helper) */
|
|
270
|
-
declare function getAskUserComment(answers: AskUserAnswers, question: AskUserQuestion): string | undefined;
|
|
271
306
|
/**
|
|
272
307
|
* 类型守卫:验证 unknown 是否为合法的 AskUserQuestion。
|
|
273
308
|
* 用于前端从 runtime 透传的 askUserQuestions(unknown[])中安全收窄。
|
|
274
309
|
*/
|
|
275
310
|
declare function isAskUserQuestion(value: unknown): value is AskUserQuestion;
|
|
276
311
|
|
|
277
|
-
export { ASK_USER_MARKER, type AskUserAnswers, type AskUserOption, type AskUserQuestion, GUI_WIDGET_MARKER, type GuiComponent, type GuiComponentProps, type GuiComponentType, type GuiContext, type GuiRenderResult, PROTOCOL_VERSION, type StatItem, type TreeItem, type TreeItemIcon, askUserInteract, extractGui, getAskUserAnswer,
|
|
312
|
+
export { ASK_USER_MARKER, type AskUserAnswers, type AskUserOption, type AskUserQuestion, GUI_WIDGET_MARKER, type GuiComponent, type GuiComponentProps, type GuiComponentType, type GuiContext, type GuiRenderResult, PROTOCOL_VERSION, type StatItem, type TreeItem, type TreeItemIcon, type WidgetMeta, askUserInteract, extractGui, getAskUserAnswer, getAskUserOther, guiComponent, guiResult, guiSetWidget, isAskUserQuestion, isGuiCapable, isGuiComponent, isGuiRenderResult };
|
package/dist/index.mjs
CHANGED
|
@@ -8,19 +8,21 @@ var GUI_WIDGET_MARKER = "\0XYZ_GUI_WIDGET:";
|
|
|
8
8
|
function isGuiCapable(ctx) {
|
|
9
9
|
return ctx.mode === "rpc";
|
|
10
10
|
}
|
|
11
|
-
function guiResult(component) {
|
|
12
|
-
|
|
11
|
+
function guiResult(component, meta) {
|
|
12
|
+
const result = {
|
|
13
13
|
v: PROTOCOL_VERSION,
|
|
14
14
|
component: stripUndefined(component)
|
|
15
15
|
};
|
|
16
|
+
if (meta !== void 0) result.meta = stripUndefined(meta);
|
|
17
|
+
return result;
|
|
16
18
|
}
|
|
17
19
|
function guiComponent(type, props) {
|
|
18
20
|
return { type, props };
|
|
19
21
|
}
|
|
20
|
-
function guiSetWidget(ctx, key,
|
|
22
|
+
function guiSetWidget(ctx, key, result) {
|
|
21
23
|
if (!ctx.ui?.setWidget) return;
|
|
22
|
-
if (
|
|
23
|
-
const encoded = [GUI_WIDGET_MARKER + JSON.stringify(stripUndefined(
|
|
24
|
+
if (result) {
|
|
25
|
+
const encoded = [GUI_WIDGET_MARKER + JSON.stringify(stripUndefined(result))];
|
|
24
26
|
ctx.ui.setWidget(key, encoded);
|
|
25
27
|
} else {
|
|
26
28
|
ctx.ui.setWidget(key, void 0);
|
|
@@ -38,6 +40,11 @@ function isGuiComponent(value) {
|
|
|
38
40
|
const obj = value;
|
|
39
41
|
return typeof obj.type === "string" && obj.props !== null && obj.props !== void 0 && typeof obj.props === "object";
|
|
40
42
|
}
|
|
43
|
+
function isGuiRenderResult(value) {
|
|
44
|
+
if (value === null || typeof value !== "object") return false;
|
|
45
|
+
const obj = value;
|
|
46
|
+
return obj.v === PROTOCOL_VERSION && isGuiComponent(obj.component);
|
|
47
|
+
}
|
|
41
48
|
function stripUndefined(obj) {
|
|
42
49
|
if (obj === null || obj === void 0) return obj;
|
|
43
50
|
if (typeof obj !== "object") return obj;
|
|
@@ -100,13 +107,10 @@ function getAskUserAnswer(answers, question) {
|
|
|
100
107
|
function getAskUserOther(answers, question) {
|
|
101
108
|
return answers[`${askUserKey(question)}__other`];
|
|
102
109
|
}
|
|
103
|
-
function getAskUserComment(answers, question) {
|
|
104
|
-
return answers[`${askUserKey(question)}__comment`];
|
|
105
|
-
}
|
|
106
110
|
function isAskUserQuestion(value) {
|
|
107
111
|
if (typeof value !== "object" || value === null) return false;
|
|
108
112
|
const q = value;
|
|
109
|
-
return typeof q.question === "string" && (q.header === void 0 || typeof q.header === "string") && (q.options === void 0 || Array.isArray(q.options)) && (q.multiSelect === void 0 || typeof q.multiSelect === "boolean") && (q.allowOther === void 0 || typeof q.allowOther === "boolean")
|
|
113
|
+
return typeof q.question === "string" && (q.header === void 0 || typeof q.header === "string") && (q.options === void 0 || Array.isArray(q.options)) && (q.multiSelect === void 0 || typeof q.multiSelect === "boolean") && (q.allowOther === void 0 || typeof q.allowOther === "boolean");
|
|
110
114
|
}
|
|
111
115
|
export {
|
|
112
116
|
ASK_USER_MARKER,
|
|
@@ -115,12 +119,12 @@ export {
|
|
|
115
119
|
askUserInteract,
|
|
116
120
|
extractGui,
|
|
117
121
|
getAskUserAnswer,
|
|
118
|
-
getAskUserComment,
|
|
119
122
|
getAskUserOther,
|
|
120
123
|
guiComponent,
|
|
121
124
|
guiResult,
|
|
122
125
|
guiSetWidget,
|
|
123
126
|
isAskUserQuestion,
|
|
124
127
|
isGuiCapable,
|
|
125
|
-
isGuiComponent
|
|
128
|
+
isGuiComponent,
|
|
129
|
+
isGuiRenderResult
|
|
126
130
|
};
|
package/package.json
CHANGED