@zhushanwen/extension-protocol 0.11.0 → 0.12.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/background-task-entry.js +19 -6
- package/dist/index.d.ts +60 -4
- package/dist/index.js +24 -2
- package/package.json +1 -1
|
@@ -171,6 +171,11 @@ import { openSync, readSync, closeSync, statSync } from "fs";
|
|
|
171
171
|
var TAIL_WINDOW_MARGIN_BYTES = 64;
|
|
172
172
|
function readOutputTail(outputFile, opts, onLog) {
|
|
173
173
|
const { maxBytes, maxLines } = opts;
|
|
174
|
+
if (!Number.isInteger(maxLines) || maxLines <= 0) {
|
|
175
|
+
throw new RangeError(
|
|
176
|
+
`readOutputTail: maxLines must be a positive integer, got ${maxLines} (outputFile: ${outputFile})`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
174
179
|
let size;
|
|
175
180
|
try {
|
|
176
181
|
size = statSync(outputFile).size;
|
|
@@ -182,7 +187,14 @@ function readOutputTail(outputFile, opts, onLog) {
|
|
|
182
187
|
let fd;
|
|
183
188
|
try {
|
|
184
189
|
fd = openSync(outputFile, "r");
|
|
185
|
-
|
|
190
|
+
let totalRead = 0;
|
|
191
|
+
while (totalRead < windowSize) {
|
|
192
|
+
const bytesRead = readSync(fd, buffer, totalRead, windowSize - totalRead, size - windowSize + totalRead);
|
|
193
|
+
if (bytesRead <= 0) break;
|
|
194
|
+
totalRead += bytesRead;
|
|
195
|
+
}
|
|
196
|
+
const text = (totalRead === windowSize ? buffer : buffer.subarray(0, totalRead)).toString("utf8");
|
|
197
|
+
return finishTail(text, { size, windowSize, maxBytes, maxLines });
|
|
186
198
|
} catch {
|
|
187
199
|
return void 0;
|
|
188
200
|
} finally {
|
|
@@ -194,13 +206,14 @@ function readOutputTail(outputFile, opts, onLog) {
|
|
|
194
206
|
}
|
|
195
207
|
}
|
|
196
208
|
}
|
|
197
|
-
|
|
209
|
+
}
|
|
210
|
+
function finishTail(text, bounds) {
|
|
198
211
|
const lines = text.split("\n");
|
|
199
|
-
const firstLineIsPartial = windowSize < size && lines.length > 0;
|
|
212
|
+
const firstLineIsPartial = bounds.windowSize < bounds.size && lines.length > 0;
|
|
200
213
|
const effectiveLines = firstLineIsPartial ? lines.slice(1) : lines;
|
|
201
|
-
const byteTruncated = size > maxBytes;
|
|
202
|
-
const shown = effectiveLines.slice(-maxLines).join("\n");
|
|
203
|
-
return { text: shown, truncated: byteTruncated || effectiveLines.length > maxLines };
|
|
214
|
+
const byteTruncated = bounds.size > bounds.maxBytes;
|
|
215
|
+
const shown = effectiveLines.slice(-bounds.maxLines).join("\n");
|
|
216
|
+
return { text: shown, truncated: byteTruncated || effectiveLines.length > bounds.maxLines };
|
|
204
217
|
}
|
|
205
218
|
export {
|
|
206
219
|
atomicWriteRegistry,
|
package/dist/index.d.ts
CHANGED
|
@@ -55,7 +55,7 @@ interface GuiComponentProps {
|
|
|
55
55
|
* 自带编号的文本(goal criteria "1. xxx")不要开,避免双重编号 */
|
|
56
56
|
numbered?: boolean;
|
|
57
57
|
};
|
|
58
|
-
/**
|
|
58
|
+
/** 垂直组合容器——无视觉样式的透明分组。宿主壳层(托盘 widget 面板)承担卡壳/head/折叠
|
|
59
59
|
* 后,widget 内容需要多组件组合时的组合根(替代「无头 card」的语义滥用) */
|
|
60
60
|
'group': {
|
|
61
61
|
children: GuiComponent[];
|
|
@@ -72,6 +72,13 @@ interface GuiComponentProps {
|
|
|
72
72
|
active?: boolean;
|
|
73
73
|
status?: 'done' | 'pending';
|
|
74
74
|
}[];
|
|
75
|
+
/**
|
|
76
|
+
* 容器化分段(可选):第 i 段 = tabs[i] 激活时渲染的子树,与 tabs 等长。
|
|
77
|
+
* 缺省维持「纯展示 tab-bar」现状(旧 extension 一行不改);宿主本地持有
|
|
78
|
+
* active(首挂载取 tabs[i].active,点击只切本地索引,后续推送不重置用户
|
|
79
|
+
* 选择);与 tabs 长度不等时忽略 sections 退化为纯展示(见协议文档)。
|
|
80
|
+
*/
|
|
81
|
+
sections?: GuiComponent[][];
|
|
75
82
|
};
|
|
76
83
|
/** 自定义组件——逃生口(仅限内置 extension 编译期注册) */
|
|
77
84
|
'custom': {
|
|
@@ -84,7 +91,7 @@ interface GuiRenderResult {
|
|
|
84
91
|
v: typeof PROTOCOL_VERSION;
|
|
85
92
|
component: GuiComponent;
|
|
86
93
|
/**
|
|
87
|
-
* widget 宿主元数据(
|
|
94
|
+
* widget 宿主元数据(composer 任务托盘的协议 widget 区消费):标题/状态点/进度计数由
|
|
88
95
|
* 宿主壳层统一渲染成单一 head(含折叠交互),extension 不再用 card 原语
|
|
89
96
|
* 的 header 表达这些(壳层 head 与 payload card header 双头重复的根因修复)。
|
|
90
97
|
* 可选:不发时宿主 fallback 到 viewId 标题、无状态点/进度。
|
|
@@ -108,6 +115,21 @@ interface WidgetMeta {
|
|
|
108
115
|
/** fill 语义色(预算阈值映射);缺省按 meta.status(done→success,否则 accent) */
|
|
109
116
|
severity?: 'ok' | 'warn' | 'danger';
|
|
110
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* 托盘 icon:icon key 字符串(宿主按 lucide 名解析)或自定义形状 `{ paths }`。
|
|
120
|
+
* **形状归 extension、风格归宿主锁死**——extension 只给 path d 数组,线宽/颜色/
|
|
121
|
+
* 尺寸由宿主固定(与 @lucide 细线 icon 同构),故不会出现风格失控。
|
|
122
|
+
* 自定义形状经 `validateWidgetIconPaths` 白名单校验(字符集/条数/长度上限),
|
|
123
|
+
* 超限落兜底 icon + warn。缺省 → 宿主按 widgetKey 内置映射 → 通用 widget icon。
|
|
124
|
+
*/
|
|
125
|
+
icon?: string | {
|
|
126
|
+
paths: string[];
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* 托盘 badge:extension 全权格式化的短文本('2' / '42%' / '!'),建议 ≤6 字符
|
|
130
|
+
* (宿主超长 truncate 至 6,全文进 title)。缺省 → 宿主按 progress 派生 → 无 badge。
|
|
131
|
+
*/
|
|
132
|
+
badge?: string;
|
|
111
133
|
}
|
|
112
134
|
interface StatItem {
|
|
113
135
|
label?: string;
|
|
@@ -171,7 +193,7 @@ interface GuiContext {
|
|
|
171
193
|
*/
|
|
172
194
|
declare function isGuiCapable(ctx: GuiContext): boolean;
|
|
173
195
|
/**
|
|
174
|
-
* 构造 GuiRenderResult。当前主用途是 guiSetWidget 的载荷(
|
|
196
|
+
* 构造 GuiRenderResult。当前主用途是 guiSetWidget 的载荷(composer 任务托盘的协议 widget 区);
|
|
175
197
|
* details.__gui__(tool result 通道)为遗留兼容路径,todo/goal 已不再使用。
|
|
176
198
|
* stripUndefined 确保序列化不含 undefined(JSON.stringify 会丢弃 undefined 字段)。
|
|
177
199
|
*/
|
|
@@ -215,9 +237,43 @@ interface DualWidgetContent {
|
|
|
215
237
|
* hasUI 守卫留在调用方(goal FR-6.6 语义,本 helper 不感知)。
|
|
216
238
|
*/
|
|
217
239
|
declare function setWidgetDual(ctx: GuiContext, key: string, content: DualWidgetContent | undefined): void;
|
|
240
|
+
/** 拒绝原因(宿主 warn 文案与 fallback 分诊用;判定顺序同 validateWidgetIconPaths 实现)。 */
|
|
241
|
+
type WidgetIconPathsRejection =
|
|
242
|
+
/** 入参不是数组(含 undefined / null / 其他类型) */
|
|
243
|
+
'not-array'
|
|
244
|
+
/** 空数组——渲染无形状,等同无效自定义 icon */
|
|
245
|
+
| 'empty'
|
|
246
|
+
/** 条数 > 8 */
|
|
247
|
+
| 'too-many'
|
|
248
|
+
/** 存在非 string 条目 */
|
|
249
|
+
| 'non-string'
|
|
250
|
+
/** 单条 > 512 字符 */
|
|
251
|
+
| 'too-long'
|
|
252
|
+
/** 全部条目总长 > 2048 字符 */
|
|
253
|
+
| 'total-too-long'
|
|
254
|
+
/** 含白名单外字符 */
|
|
255
|
+
| 'illegal-char';
|
|
256
|
+
/** icon paths 校验判定:合法携带白名单内的 paths 副本;非法携带原因(不抛异常)。 */
|
|
257
|
+
type WidgetIconPathsValidation = {
|
|
258
|
+
valid: true;
|
|
259
|
+
paths: string[];
|
|
260
|
+
} | {
|
|
261
|
+
valid: false;
|
|
262
|
+
reason: WidgetIconPathsRejection;
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* 校验 WidgetMeta.icon 的自定义形状 paths(`{ paths: string[] }` 的内层数组)。
|
|
266
|
+
* 不抛异常——非法即返回原因,宿主据此落兜底 icon + warn(协议对 extension 的
|
|
267
|
+
* 宽容边界:坏数据不崩渲染,只是拿不到自定义形状)。
|
|
268
|
+
*/
|
|
269
|
+
declare function validateWidgetIconPaths(value: unknown): WidgetIconPathsValidation;
|
|
218
270
|
/**
|
|
219
271
|
* 从 details 中提取 GuiRenderResult。前端统一用此函数读取 __gui__,
|
|
220
272
|
* 集中校验版本号,避免散落的 as 断言。
|
|
273
|
+
* component 字段校验(质量收敛批升级):从「仅查存在性」升级为复用 isGuiComponent
|
|
274
|
+
* 结构校验(type 字符串 + props 对象——含 null 排除;component 不是函数,可调用性
|
|
275
|
+
* 检查对其无意义)。非法值 → undefined,走调用方既有降级路径(AnsiText/纯文本兜底),
|
|
276
|
+
* 畸形 component 不再进渲染层。
|
|
221
277
|
*/
|
|
222
278
|
declare function extractGui(details: Record<string, unknown> | undefined): GuiRenderResult | undefined;
|
|
223
279
|
/**
|
|
@@ -795,4 +851,4 @@ declare function collectActivePendingIds(entries: unknown[], opts?: CollectPendi
|
|
|
795
851
|
type MappedPendingStatus = 'completed' | 'failed' | 'cancelled' | 'expired' | 'time_limited' | 'aborted';
|
|
796
852
|
declare function mapReasonToStatus(reason: string): MappedPendingStatus;
|
|
797
853
|
|
|
798
|
-
export { ASK_USER_MARKER, type AskUserAnswers, type AskUserOption, type AskUserQuestion, BRIDGE_MARKER, BRIDGE_METHODS, type BridgeErrorResponse, type BridgeInterceptResponse, type BridgeMethod, type BridgeRequest, type BridgeSyncPayload, type BridgeToolExecuteResponse, type ChannelErrorResult, type CollectPendingIdsOptions, type DualWidgetContent, GUI_WIDGET_MARKER, type GuiComponent, type GuiComponentProps, type GuiComponentType, type GuiContext, type GuiRenderResult, INFLIGHT_REPORT_ACK, type MappedPendingStatus, type MarkerRpcOptions, type MarkerRpcResult, PENDING_REGISTER_ENTRY_TYPE, PENDING_UNREGISTER_ENTRY_TYPE, PROTOCOL_VERSION, type PendingEntriesScan, SESSION_MANAGER_ACTIONS, SESSION_MANAGER_MARKER, SUBAGENTS_ENGINES_FILENAME, SUBAGENT_INFLIGHT_MARKER, type SessionManagerAbortParams, type SessionManagerAbortResult, type SessionManagerAction, type SessionManagerCreateParams, type SessionManagerCreateResult, type SessionManagerErrorResult, type SessionManagerHistoryParams, type SessionManagerHistoryResult, type SessionManagerListParams, type SessionManagerListResult, type SessionManagerParams, type SessionManagerSendParams, type SessionManagerSendResult, type SessionManagerSessionSummary, type SessionManagerStatusParams, type SessionManagerStatusResult, type StatItem, type SubagentEngineConfigView, type SubagentEnginesFile, type SubagentInFlightReport, type TreeItem, type TreeItemIcon, type WidgetMeta, applyPendingDiff, askUserInteract, callMarkerRpc, collectActivePendingIds, extractGui, firstContentText, formatChannelErrorText, getAskUserAnswer, getAskUserOther, guiComponent, guiResult, guiSetWidget, isAskUserQuestion, isBridgeErrorResponse, isBridgeInterceptResponse, isBridgeSyncPayload, isBridgeToolExecuteResponse, isChannelErrorResult, isGuiCapable, isGuiComponent, isGuiRenderResult, isInFlightReportAck, isSessionManagerAbortParams, isSessionManagerCreateParams, isSessionManagerHistoryParams, isSessionManagerListParams, isSessionManagerSendParams, isSessionManagerStatusParams, isSubagentInFlightReport, isSyncedTool, mapReasonToStatus, scanPendingEntries, setWidgetDual };
|
|
854
|
+
export { ASK_USER_MARKER, type AskUserAnswers, type AskUserOption, type AskUserQuestion, BRIDGE_MARKER, BRIDGE_METHODS, type BridgeErrorResponse, type BridgeInterceptResponse, type BridgeMethod, type BridgeRequest, type BridgeSyncPayload, type BridgeToolExecuteResponse, type ChannelErrorResult, type CollectPendingIdsOptions, type DualWidgetContent, GUI_WIDGET_MARKER, type GuiComponent, type GuiComponentProps, type GuiComponentType, type GuiContext, type GuiRenderResult, INFLIGHT_REPORT_ACK, type MappedPendingStatus, type MarkerRpcOptions, type MarkerRpcResult, PENDING_REGISTER_ENTRY_TYPE, PENDING_UNREGISTER_ENTRY_TYPE, PROTOCOL_VERSION, type PendingEntriesScan, SESSION_MANAGER_ACTIONS, SESSION_MANAGER_MARKER, SUBAGENTS_ENGINES_FILENAME, SUBAGENT_INFLIGHT_MARKER, type SessionManagerAbortParams, type SessionManagerAbortResult, type SessionManagerAction, type SessionManagerCreateParams, type SessionManagerCreateResult, type SessionManagerErrorResult, type SessionManagerHistoryParams, type SessionManagerHistoryResult, type SessionManagerListParams, type SessionManagerListResult, type SessionManagerParams, type SessionManagerSendParams, type SessionManagerSendResult, type SessionManagerSessionSummary, type SessionManagerStatusParams, type SessionManagerStatusResult, type StatItem, type SubagentEngineConfigView, type SubagentEnginesFile, type SubagentInFlightReport, type TreeItem, type TreeItemIcon, type WidgetIconPathsRejection, type WidgetIconPathsValidation, type WidgetMeta, applyPendingDiff, askUserInteract, callMarkerRpc, collectActivePendingIds, extractGui, firstContentText, formatChannelErrorText, getAskUserAnswer, getAskUserOther, guiComponent, guiResult, guiSetWidget, isAskUserQuestion, isBridgeErrorResponse, isBridgeInterceptResponse, isBridgeSyncPayload, isBridgeToolExecuteResponse, isChannelErrorResult, isGuiCapable, isGuiComponent, isGuiRenderResult, isInFlightReportAck, isSessionManagerAbortParams, isSessionManagerCreateParams, isSessionManagerHistoryParams, isSessionManagerListParams, isSessionManagerSendParams, isSessionManagerStatusParams, isSubagentInFlightReport, isSyncedTool, mapReasonToStatus, scanPendingEntries, setWidgetDual, validateWidgetIconPaths };
|
package/dist/index.js
CHANGED
|
@@ -51,9 +51,30 @@ function setWidgetDual(ctx, key, content) {
|
|
|
51
51
|
ctx.ui.setWidget(key, content.text);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
var ICON_PATH_D_ALLOWED_RE = /^[MLCQAZHVSTmlcqazhvst0-9 ,.\-]+$/;
|
|
55
|
+
var ICON_PATHS_MAX_ENTRIES = 8;
|
|
56
|
+
var ICON_PATH_MAX_CHARS = 512;
|
|
57
|
+
var ICON_PATHS_MAX_TOTAL_CHARS = 2048;
|
|
58
|
+
function validateWidgetIconPaths(value) {
|
|
59
|
+
if (!Array.isArray(value)) return { valid: false, reason: "not-array" };
|
|
60
|
+
if (value.length === 0) return { valid: false, reason: "empty" };
|
|
61
|
+
if (value.length > ICON_PATHS_MAX_ENTRIES) return { valid: false, reason: "too-many" };
|
|
62
|
+
const entries = value;
|
|
63
|
+
const paths = [];
|
|
64
|
+
let total = 0;
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
if (typeof entry !== "string") return { valid: false, reason: "non-string" };
|
|
67
|
+
if (entry.length > ICON_PATH_MAX_CHARS) return { valid: false, reason: "too-long" };
|
|
68
|
+
if (!ICON_PATH_D_ALLOWED_RE.test(entry)) return { valid: false, reason: "illegal-char" };
|
|
69
|
+
paths.push(entry);
|
|
70
|
+
total += entry.length;
|
|
71
|
+
}
|
|
72
|
+
if (total > ICON_PATHS_MAX_TOTAL_CHARS) return { valid: false, reason: "total-too-long" };
|
|
73
|
+
return { valid: true, paths };
|
|
74
|
+
}
|
|
54
75
|
function extractGui(details) {
|
|
55
76
|
const g = details?.__gui__;
|
|
56
|
-
if (g && typeof g === "object" && "v" in g &&
|
|
77
|
+
if (g && typeof g === "object" && "v" in g && isGuiComponent(g.component) && g.v === PROTOCOL_VERSION) {
|
|
57
78
|
return g;
|
|
58
79
|
}
|
|
59
80
|
return void 0;
|
|
@@ -385,5 +406,6 @@ export {
|
|
|
385
406
|
isTerminalBackgroundTaskState,
|
|
386
407
|
mapReasonToStatus,
|
|
387
408
|
scanPendingEntries,
|
|
388
|
-
setWidgetDual
|
|
409
|
+
setWidgetDual,
|
|
410
|
+
validateWidgetIconPaths
|
|
389
411
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/extension-protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Cross-layer pi extension contract package: dual-mode (TUI/GUI) rendering helpers, session/background-task/pending-entries protocols, and shared behavior primitives",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|