@dp-bohrium/widget-sdk 0.8.0 → 0.10.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/AGENTS.md +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +0 -7
- package/assets/schemas/widget-manifest.schema.json +0 -12
- package/assets/schemas/widget-runtime-contracts.schema.json +906 -272
- package/assets/toolchain.json +8 -8
- package/conformance/development-diagnostics.json +128 -0
- package/dist/cli.mjs +184 -14
- package/dist/compatibility/testing.js +1 -1
- package/dist/contracts.js +3 -2
- package/dist/development-guards-COvBR0G1.js +136 -0
- package/dist/{dsh-BlpXm2Bv.mjs → dsh-BJWUB8Nq.mjs} +1308 -516
- package/dist/index.d.ts +2 -2
- package/dist/index.js +53 -17
- package/dist/protocol/development-guards.d.ts +9 -0
- package/dist/protocol/generated-development-policy.d.ts +22 -0
- package/dist/protocol/generated-host-runtime-config.d.ts +0 -22
- package/dist/protocol/generated-manifest.d.ts +0 -8
- package/dist/protocol/generated-plugin-manifest.d.ts +0 -8
- package/dist/protocol/generated-runtime-contracts.d.ts +230 -128
- package/dist/protocol/index.d.ts +7 -2
- package/dist/protocol/widget-recovery.d.ts +19 -0
- package/dist/{protocol-endpoint-CDz-YDGT.js → protocol-endpoint-DbT0OJNu.js} +5 -0
- package/dist/runtime/bootstrap.d.ts +3 -1
- package/dist/runtime/client.d.ts +5 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/protocol-endpoint.d.ts +5 -5
- package/dist/runtime/transport.d.ts +9 -9
- package/dist/{runtime-D2xmZVSb.js → runtime-Bn_ee8xy.js} +39 -5
- package/dist/widget-recovery-BF_sSUZX.js +472 -0
- package/dist/widget.d.ts +1 -41
- package/llms.txt +13 -1
- package/package.json +1 -1
- package/schemas/widget-manifest.schema.json +0 -12
- package/schemas/widget-runtime-contracts.schema.json +906 -272
- package/conformance/bridge/tasks-list-request.json +0 -17
- package/conformance/bridge/tasks-watch-request.json +0 -18
- package/dist/widget-events-gsKJbjqg.js +0 -65
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { r as MessagePortTransport, t as ProtocolEndpoint } from "./protocol-endpoint-
|
|
1
|
+
import { r as MessagePortTransport, t as ProtocolEndpoint } from "./protocol-endpoint-DbT0OJNu.js";
|
|
2
2
|
//#endregion
|
|
3
3
|
//#region src/runtime/version.ts
|
|
4
4
|
/** 从当前安装的 SDK package.json 读取版本,插件作者不需要重复填写。 */
|
|
5
|
-
const WIDGET_SDK_VERSION = "0.
|
|
5
|
+
const WIDGET_SDK_VERSION = "0.10.0";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/runtime/network.ts
|
|
8
8
|
const failureReasons = /* @__PURE__ */ new Set([
|
|
@@ -308,6 +308,7 @@ var WidgetClient = class {
|
|
|
308
308
|
endpoint;
|
|
309
309
|
widgetSdkVersion;
|
|
310
310
|
channelId;
|
|
311
|
+
developmentDiagnostics;
|
|
311
312
|
handshakeTimeoutMs;
|
|
312
313
|
capabilityTimeoutMs;
|
|
313
314
|
createMessageId;
|
|
@@ -327,6 +328,7 @@ var WidgetClient = class {
|
|
|
327
328
|
constructor(options, events = {}) {
|
|
328
329
|
this.widgetSdkVersion = options.widgetSdkVersion;
|
|
329
330
|
this.channelId = options.channelId;
|
|
331
|
+
this.developmentDiagnostics = options.developmentDiagnostics === true;
|
|
330
332
|
this.hostApiVersion = options.hostApiVersion ?? 1;
|
|
331
333
|
this.handshakeTimeoutMs = options.handshakeTimeoutMs ?? 5e3;
|
|
332
334
|
this.capabilityTimeoutMs = options.capabilityTimeoutMs ?? 5e3;
|
|
@@ -538,6 +540,18 @@ var WidgetClient = class {
|
|
|
538
540
|
this.failHandshake(toError$1(reason, "widget initialization failed"), "initialization-failed", "WIDGET_CRASHED");
|
|
539
541
|
}
|
|
540
542
|
}
|
|
543
|
+
reportDevelopmentDiagnostic(diagnostic) {
|
|
544
|
+
if (this.developmentDiagnostics && this.state === "ready") this.endpoint.send({
|
|
545
|
+
type: "diagnostic",
|
|
546
|
+
payload: diagnostic
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
reportRenderResult(result) {
|
|
550
|
+
if (this.developmentDiagnostics && this.state === "ready") this.endpoint.send({
|
|
551
|
+
type: "render-result",
|
|
552
|
+
payload: result
|
|
553
|
+
});
|
|
554
|
+
}
|
|
541
555
|
send(message) {
|
|
542
556
|
this.endpoint.send(message);
|
|
543
557
|
}
|
|
@@ -1071,6 +1085,11 @@ var WidgetRuntimeClient = class {
|
|
|
1071
1085
|
this.runtimeWindow.addEventListener("message", this.messageListener);
|
|
1072
1086
|
this.connectionTimer = setTimeout(() => this.failConnection(/* @__PURE__ */ new Error("widget connection timed out")), options.connectionTimeoutMs ?? options.handshakeTimeoutMs ?? 5e3);
|
|
1073
1087
|
try {
|
|
1088
|
+
this.runtimeWindow.parent.postMessage({
|
|
1089
|
+
type: "widget-bootstrap-offer",
|
|
1090
|
+
protocolVersion: 1,
|
|
1091
|
+
extensions: ["development-diagnostics-v1"]
|
|
1092
|
+
}, "*");
|
|
1074
1093
|
this.runtimeWindow.parent.postMessage({
|
|
1075
1094
|
type: WIDGET_BOOTSTRAP_READY,
|
|
1076
1095
|
protocolVersion: 1
|
|
@@ -1232,16 +1251,25 @@ var WidgetRuntimeClient = class {
|
|
|
1232
1251
|
if (this.disposed || this.client) return;
|
|
1233
1252
|
if (event.source !== this.runtimeWindow.parent) return;
|
|
1234
1253
|
if (this.options.expectedHostOrigin !== void 0 && event.origin !== this.options.expectedHostOrigin) return;
|
|
1235
|
-
|
|
1254
|
+
const extended = isRecord(event.data) && Array.isArray(event.data["extensions"]) && event.data["extensions"].length === 1 && event.data["extensions"][0] === "development-diagnostics-v1" && hasExactKeys(event.data, [
|
|
1255
|
+
"type",
|
|
1256
|
+
"protocolVersion",
|
|
1257
|
+
"channelId",
|
|
1258
|
+
"initPayload",
|
|
1259
|
+
"extensions"
|
|
1260
|
+
]);
|
|
1261
|
+
const candidate = extended ? Object.fromEntries(Object.entries(event.data).filter(([key]) => key !== "extensions")) : event.data;
|
|
1262
|
+
if (!isWidgetHostConnectMessage(candidate)) return;
|
|
1236
1263
|
const port = event.ports?.[0];
|
|
1237
1264
|
if (!port) return;
|
|
1238
1265
|
this.removeConnectionListener();
|
|
1239
1266
|
try {
|
|
1240
1267
|
const clientOptions = {
|
|
1241
1268
|
transport: new MessagePortTransport(port),
|
|
1242
|
-
channelId:
|
|
1269
|
+
channelId: candidate.channelId,
|
|
1243
1270
|
widgetSdkVersion: this.options.widgetSdkVersion ?? WIDGET_SDK_VERSION,
|
|
1244
|
-
initialPayload:
|
|
1271
|
+
initialPayload: candidate.initPayload,
|
|
1272
|
+
developmentDiagnostics: extended,
|
|
1245
1273
|
...this.options.hostApiVersion === void 0 ? {} : { hostApiVersion: this.options.hostApiVersion },
|
|
1246
1274
|
...this.options.capabilityTimeoutMs === void 0 ? {} : { capabilityTimeoutMs: this.options.capabilityTimeoutMs },
|
|
1247
1275
|
...this.options.handshakeTimeoutMs === void 0 ? {} : { handshakeTimeoutMs: this.options.handshakeTimeoutMs },
|
|
@@ -1259,6 +1287,12 @@ var WidgetRuntimeClient = class {
|
|
|
1259
1287
|
this.failConnection(toError(reason));
|
|
1260
1288
|
}
|
|
1261
1289
|
}
|
|
1290
|
+
reportDevelopmentDiagnostic(diagnostic) {
|
|
1291
|
+
this.client?.reportDevelopmentDiagnostic(diagnostic);
|
|
1292
|
+
}
|
|
1293
|
+
reportRenderResult(result) {
|
|
1294
|
+
this.client?.reportRenderResult(result);
|
|
1295
|
+
}
|
|
1262
1296
|
requireClient() {
|
|
1263
1297
|
if (this.disposed) throw new Error("client is disposed");
|
|
1264
1298
|
if (!this.client) throw new Error("widget host connection is not ready");
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
//#region src/protocol/plugin-manifest.ts
|
|
2
|
+
/**
|
|
3
|
+
* Widget Host 公开的固定激活事件。数组同时作为清单编辑器文档和 Host
|
|
4
|
+
* 运行时校验的单一事实源;带工具名的事件使用下方的动态形式。
|
|
5
|
+
*/
|
|
6
|
+
const WIDGET_ACTIVATION_EVENTS = [
|
|
7
|
+
"session.start",
|
|
8
|
+
"turn-start",
|
|
9
|
+
"turn-end",
|
|
10
|
+
"step-start",
|
|
11
|
+
"step-end",
|
|
12
|
+
"tool-call-generating",
|
|
13
|
+
"tool-call-waiting",
|
|
14
|
+
"tool-call-running",
|
|
15
|
+
"tool-call-result",
|
|
16
|
+
"tool-call-error",
|
|
17
|
+
"workspace-file-changed",
|
|
18
|
+
"workspace-tree-refresh",
|
|
19
|
+
"workspace-file-opened",
|
|
20
|
+
"ui-tool-opened",
|
|
21
|
+
"ui-panel-focused",
|
|
22
|
+
"host-surface-changed"
|
|
23
|
+
];
|
|
24
|
+
const TOOL_EVENT_PATTERN = /^(?:tool-call-(?:generating|waiting|running|result|error)):[A-Za-z0-9_.:-]+$/u;
|
|
25
|
+
/**
|
|
26
|
+
* 判断一个字符串是否为合法激活事件(固定事件或带工具名的动态形式)。
|
|
27
|
+
* @param value 待检查的字符串。
|
|
28
|
+
* @returns 若命中固定目录或工具事件模式则收窄为 `WidgetActivationEvent`。
|
|
29
|
+
*/
|
|
30
|
+
function isWidgetActivationEvent(value) {
|
|
31
|
+
return typeof value === "string" && (WIDGET_ACTIVATION_EVENTS.includes(value) || TOOL_EVENT_PATTERN.test(value));
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/protocol/widget-events.ts
|
|
35
|
+
/**
|
|
36
|
+
* Host 主动推送给已激活 Widget 的实时事件。`session.start` 只用于激活索引,
|
|
37
|
+
* 不属于这组运行期事件;激活后首个可用快照通过 `context.render` 交付。
|
|
38
|
+
*/
|
|
39
|
+
const WIDGET_EVENT_NAMES = [
|
|
40
|
+
"turn-start",
|
|
41
|
+
"turn-end",
|
|
42
|
+
"step-start",
|
|
43
|
+
"step-end",
|
|
44
|
+
"tool-call-generating",
|
|
45
|
+
"tool-call-waiting",
|
|
46
|
+
"tool-call-running",
|
|
47
|
+
"tool-call-result",
|
|
48
|
+
"tool-call-error",
|
|
49
|
+
"workspace-file-changed",
|
|
50
|
+
"workspace-tree-refresh",
|
|
51
|
+
"workspace-file-opened",
|
|
52
|
+
"ui-tool-opened",
|
|
53
|
+
"ui-panel-focused",
|
|
54
|
+
"host-surface-changed"
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* 判断一个字符串是否为合法的实时事件名。
|
|
58
|
+
* @param value 待检查的字符串。
|
|
59
|
+
* @returns 若 `value` 命中 `WIDGET_EVENT_NAMES` 则收窄为 `WidgetEventName`。
|
|
60
|
+
*/
|
|
61
|
+
function isWidgetEventName(value) {
|
|
62
|
+
return typeof value === "string" && WIDGET_EVENT_NAMES.includes(value);
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/protocol/widget-recovery.ts
|
|
66
|
+
function rule(input) {
|
|
67
|
+
const followUpActions = input.disposition === "repair" ? [
|
|
68
|
+
{
|
|
69
|
+
id: "build-project",
|
|
70
|
+
params: { target: "current-project" }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "refresh-preview",
|
|
74
|
+
params: { target: "current-project" }
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "verify-scenario",
|
|
78
|
+
params: { target: "next-built-generation" }
|
|
79
|
+
}
|
|
80
|
+
] : [];
|
|
81
|
+
const value = {
|
|
82
|
+
id: input.id,
|
|
83
|
+
version: 1,
|
|
84
|
+
sources: [input.source],
|
|
85
|
+
codes: input.codes,
|
|
86
|
+
disposition: input.disposition ?? "inspect",
|
|
87
|
+
nextAction: input.nextAction ?? {
|
|
88
|
+
id: "inspect",
|
|
89
|
+
params: { area: input.area }
|
|
90
|
+
},
|
|
91
|
+
verification: input.verification ?? {
|
|
92
|
+
target: "current-attempt",
|
|
93
|
+
milestone: "diagnostics-updated"
|
|
94
|
+
},
|
|
95
|
+
guidance: input.guidance,
|
|
96
|
+
followUpActions
|
|
97
|
+
};
|
|
98
|
+
Object.freeze(value.sources);
|
|
99
|
+
Object.freeze(value.codes);
|
|
100
|
+
Object.freeze(value.nextAction.params);
|
|
101
|
+
Object.freeze(value.nextAction);
|
|
102
|
+
Object.freeze(value.verification);
|
|
103
|
+
for (const action of followUpActions) {
|
|
104
|
+
Object.freeze(action.params);
|
|
105
|
+
Object.freeze(action);
|
|
106
|
+
}
|
|
107
|
+
Object.freeze(followUpActions);
|
|
108
|
+
return Object.freeze(value);
|
|
109
|
+
}
|
|
110
|
+
/** 通用恢复目录;宿主绑定已有工具及权限,Guest 不指定这些规则。 */
|
|
111
|
+
const STANDARD_RULES = Object.freeze([
|
|
112
|
+
rule({
|
|
113
|
+
id: "runtime-render",
|
|
114
|
+
source: "sdk",
|
|
115
|
+
codes: ["RENDER_FAILED", "UPDATE_FAILED"],
|
|
116
|
+
area: "render",
|
|
117
|
+
disposition: "repair",
|
|
118
|
+
nextAction: {
|
|
119
|
+
id: "edit-project",
|
|
120
|
+
params: { evidence: "diagnostics" }
|
|
121
|
+
},
|
|
122
|
+
verification: {
|
|
123
|
+
target: "next-built-generation",
|
|
124
|
+
milestone: "scenario-passed"
|
|
125
|
+
},
|
|
126
|
+
guidance: "读取当前请求的源码诊断,修复处理器后构建并刷新预览,再重跑原失败场景;ready 不代表恢复。"
|
|
127
|
+
}),
|
|
128
|
+
rule({
|
|
129
|
+
id: "bundle-load",
|
|
130
|
+
source: "sdk",
|
|
131
|
+
codes: ["BUNDLE_LOAD_FAILED", "DIGEST_MISMATCH"],
|
|
132
|
+
area: "bundle",
|
|
133
|
+
guidance: "检查入口资源、响应类型、实际摘要、模块依赖及 CSP 证据,再选择修复;不能从超时猜测根因。"
|
|
134
|
+
}),
|
|
135
|
+
rule({
|
|
136
|
+
id: "runtime-lease",
|
|
137
|
+
source: "sdk",
|
|
138
|
+
codes: ["RUNTIME_CONFIG_EXPIRED", "FILE_REFERENCE_INVALID"],
|
|
139
|
+
area: "lease",
|
|
140
|
+
guidance: "核对当前 scope、配置和引用;通过已授权的 acquire/续期取得新凭据,不重用一次性票据。"
|
|
141
|
+
}),
|
|
142
|
+
rule({
|
|
143
|
+
id: "runtime-authorization",
|
|
144
|
+
source: "sdk",
|
|
145
|
+
codes: ["WIDGET_VERSION_REVOKED", "FILE_PERMISSION_DENIED"],
|
|
146
|
+
area: "capability",
|
|
147
|
+
disposition: "external-action",
|
|
148
|
+
nextAction: {
|
|
149
|
+
id: "request-external-action",
|
|
150
|
+
params: { reason: "authorization" }
|
|
151
|
+
},
|
|
152
|
+
guidance: "保留现场并请求目标授权恢复;不要重试被撤销资源或绕过权限。继续不受影响的工作。"
|
|
153
|
+
}),
|
|
154
|
+
rule({
|
|
155
|
+
id: "runtime-profile",
|
|
156
|
+
source: "sdk",
|
|
157
|
+
codes: ["HOST_API_UNSUPPORTED"],
|
|
158
|
+
area: "configuration",
|
|
159
|
+
guidance: "核对实际 Host 兼容声明与工具链;按声明选择迁移或宿主升级,不修改版本字段伪装支持。"
|
|
160
|
+
}),
|
|
161
|
+
rule({
|
|
162
|
+
id: "runtime-handshake",
|
|
163
|
+
source: "sdk",
|
|
164
|
+
codes: ["HANDSHAKE_TIMEOUT"],
|
|
165
|
+
area: "handshake",
|
|
166
|
+
guidance: "先确认资源加载,再检查 profile、frame 来源与连接阶段;补充证据后再建立新 attempt。"
|
|
167
|
+
}),
|
|
168
|
+
rule({
|
|
169
|
+
id: "runtime-protocol",
|
|
170
|
+
source: "sdk",
|
|
171
|
+
codes: [
|
|
172
|
+
"MESSAGE_INVALID",
|
|
173
|
+
"CHANNEL_MISMATCH",
|
|
174
|
+
"MESSAGE_TOO_LARGE",
|
|
175
|
+
"MESSAGE_RATE_LIMITED",
|
|
176
|
+
"TOO_MANY_PENDING_REQUESTS"
|
|
177
|
+
],
|
|
178
|
+
area: "runtime",
|
|
179
|
+
guidance: "检查协议、消息大小、频率及 pending 诊断;保留既有保护,修复生产方后再连接,不能提高预算掩盖错误。"
|
|
180
|
+
}),
|
|
181
|
+
rule({
|
|
182
|
+
id: "runtime-capability",
|
|
183
|
+
source: "sdk",
|
|
184
|
+
codes: [
|
|
185
|
+
"CAPABILITY_UNDECLARED",
|
|
186
|
+
"CAPABILITY_DENIED",
|
|
187
|
+
"CAPABILITY_UNSUPPORTED",
|
|
188
|
+
"CAPABILITY_TIMEOUT",
|
|
189
|
+
"RESOURCE_LIMIT"
|
|
190
|
+
],
|
|
191
|
+
area: "capability",
|
|
192
|
+
guidance: "区分未声明、未授权、未接线、预算与超时;检查操作是否可能已执行,再选择声明修复、可用替代或授权流程。"
|
|
193
|
+
}),
|
|
194
|
+
rule({
|
|
195
|
+
id: "runtime-lifetime",
|
|
196
|
+
source: "sdk",
|
|
197
|
+
codes: ["WIDGET_CRASHED", "INSTANCE_DISPOSED"],
|
|
198
|
+
area: "runtime",
|
|
199
|
+
guidance: "查询当前挂载尝试和末次诊断;区分正常释放与崩溃,修复后创建新 attempt,不复活旧实例。"
|
|
200
|
+
}),
|
|
201
|
+
rule({
|
|
202
|
+
id: "capability-authorization",
|
|
203
|
+
source: "host",
|
|
204
|
+
codes: [
|
|
205
|
+
"permission-denied",
|
|
206
|
+
"network-consent-denied",
|
|
207
|
+
"network-credential-unavailable"
|
|
208
|
+
],
|
|
209
|
+
area: "capability",
|
|
210
|
+
disposition: "external-action",
|
|
211
|
+
nextAction: {
|
|
212
|
+
id: "request-external-action",
|
|
213
|
+
params: { reason: "authorization" }
|
|
214
|
+
},
|
|
215
|
+
guidance: "按具体 operation 核对授权或凭据可用性;使用既有授权入口,不从聊天索取凭据或绕过拒绝。"
|
|
216
|
+
}),
|
|
217
|
+
rule({
|
|
218
|
+
id: "capability-policy",
|
|
219
|
+
source: "host",
|
|
220
|
+
codes: [
|
|
221
|
+
"unsupported",
|
|
222
|
+
"network-policy-denied",
|
|
223
|
+
"network-redirect-denied",
|
|
224
|
+
"network-scope-invalid",
|
|
225
|
+
"network-dns-denied"
|
|
226
|
+
],
|
|
227
|
+
area: "capability",
|
|
228
|
+
guidance: "核对宿主接线、当前作用域和网络策略;选择允许的操作或替代路径,不反复原样调用。"
|
|
229
|
+
}),
|
|
230
|
+
rule({
|
|
231
|
+
id: "capability-budget",
|
|
232
|
+
source: "host",
|
|
233
|
+
codes: [
|
|
234
|
+
"resource-limit",
|
|
235
|
+
"network-request-too-large",
|
|
236
|
+
"network-response-too-large"
|
|
237
|
+
],
|
|
238
|
+
area: "capability",
|
|
239
|
+
guidance: "读取已知预算维度,缩小请求或分块;未知维度继续排查,不销毁健康 Bridge 或自动提高额度。"
|
|
240
|
+
}),
|
|
241
|
+
rule({
|
|
242
|
+
id: "capability-rate",
|
|
243
|
+
source: "host",
|
|
244
|
+
codes: ["network-rate-limited"],
|
|
245
|
+
area: "capability",
|
|
246
|
+
disposition: "retry",
|
|
247
|
+
nextAction: {
|
|
248
|
+
id: "wait",
|
|
249
|
+
params: { milestone: "budget-reset" }
|
|
250
|
+
},
|
|
251
|
+
guidance: "仅在确认原操作未执行且允许重试时,按宿主预算等待;预算未知时先 inspect,不能忙轮询。"
|
|
252
|
+
}),
|
|
253
|
+
rule({
|
|
254
|
+
id: "capability-reference",
|
|
255
|
+
source: "host",
|
|
256
|
+
codes: ["invalid-reference"],
|
|
257
|
+
area: "lease",
|
|
258
|
+
guidance: "重新校验当前作用域与文件引用,通过已有授权入口取得有效引用。"
|
|
259
|
+
}),
|
|
260
|
+
rule({
|
|
261
|
+
id: "capability-conflict",
|
|
262
|
+
source: "host",
|
|
263
|
+
codes: [
|
|
264
|
+
"revision-conflict",
|
|
265
|
+
"mutation-conflict",
|
|
266
|
+
"write-outcome-unknown"
|
|
267
|
+
],
|
|
268
|
+
area: "operation-outcome",
|
|
269
|
+
verification: {
|
|
270
|
+
target: "original-operation",
|
|
271
|
+
milestone: "operation-confirmed"
|
|
272
|
+
},
|
|
273
|
+
guidance: "按原 revision 和 mutation 身份核对资源及操作结果,再决定是否提交新修改;不自动重放写入。"
|
|
274
|
+
}),
|
|
275
|
+
rule({
|
|
276
|
+
id: "capability-transport",
|
|
277
|
+
source: "host",
|
|
278
|
+
codes: [
|
|
279
|
+
"timeout",
|
|
280
|
+
"internal-error",
|
|
281
|
+
"network-transport-unavailable",
|
|
282
|
+
"network-error"
|
|
283
|
+
],
|
|
284
|
+
area: "operation-outcome",
|
|
285
|
+
guidance: "保留 transport/HTTP/格式分类,先确认原操作是否执行;无响应不等于未产生副作用。"
|
|
286
|
+
}),
|
|
287
|
+
rule({
|
|
288
|
+
id: "toolchain-build",
|
|
289
|
+
source: "toolchain",
|
|
290
|
+
codes: [
|
|
291
|
+
"WIDGET_CLI_BUILD_FAILED",
|
|
292
|
+
"WIDGET_CLI_BUILD_UNRESOLVED_IMPORT",
|
|
293
|
+
"WIDGET_DEV_BUILD_FAILED"
|
|
294
|
+
],
|
|
295
|
+
area: "toolchain",
|
|
296
|
+
disposition: "repair",
|
|
297
|
+
nextAction: {
|
|
298
|
+
id: "edit-project",
|
|
299
|
+
params: { evidence: "diagnostics" }
|
|
300
|
+
},
|
|
301
|
+
verification: {
|
|
302
|
+
target: "next-built-generation",
|
|
303
|
+
milestone: "scenario-passed"
|
|
304
|
+
},
|
|
305
|
+
guidance: "读取编译器与相对源码诊断,修复后用原执行工具构建,核对回执与新产物,再刷新并复验。"
|
|
306
|
+
}),
|
|
307
|
+
rule({
|
|
308
|
+
id: "toolchain-validation",
|
|
309
|
+
source: "toolchain",
|
|
310
|
+
codes: [
|
|
311
|
+
"WIDGET_CLI_MANIFEST_INVALID",
|
|
312
|
+
"WIDGET_CLI_VALIDATION_FAILED",
|
|
313
|
+
"WIDGET_CLI_PACK_VALIDATION_FAILED",
|
|
314
|
+
"WIDGET_DEV_PROJECT_INVALID"
|
|
315
|
+
],
|
|
316
|
+
area: "configuration",
|
|
317
|
+
disposition: "repair",
|
|
318
|
+
nextAction: {
|
|
319
|
+
id: "edit-project",
|
|
320
|
+
params: { evidence: "diagnostics" }
|
|
321
|
+
},
|
|
322
|
+
verification: {
|
|
323
|
+
target: "next-built-generation",
|
|
324
|
+
milestone: "scenario-passed"
|
|
325
|
+
},
|
|
326
|
+
guidance: "依据字段与预期值修复工程清单、入口或样例,再检查并构建;不要打包不完整产物。"
|
|
327
|
+
}),
|
|
328
|
+
rule({
|
|
329
|
+
id: "toolchain-profile",
|
|
330
|
+
source: "toolchain",
|
|
331
|
+
codes: [
|
|
332
|
+
"WIDGET_CLI_PROJECT_TOOLCHAIN_REQUIRED",
|
|
333
|
+
"WIDGET_CLI_PROJECT_PROFILE_MISMATCH",
|
|
334
|
+
"WIDGET_CLI_PROJECT_SDK_MISMATCH",
|
|
335
|
+
"WIDGET_CLI_BUILD_RECEIPT_MISMATCH",
|
|
336
|
+
"WIDGET_CLI_HOST_PROFILE_UNSUPPORTED",
|
|
337
|
+
"WIDGET_CLI_HOST_COMPAT_INVALID",
|
|
338
|
+
"WIDGET_CLI_NO_WIDGET_PROJECT"
|
|
339
|
+
],
|
|
340
|
+
area: "toolchain",
|
|
341
|
+
guidance: "运行 doctor/integration 并核对当前工程与目标宿主;显式采用或迁移工具链,再构建,不伪造 receipt。"
|
|
342
|
+
}),
|
|
343
|
+
rule({
|
|
344
|
+
id: "development-host",
|
|
345
|
+
source: "toolchain",
|
|
346
|
+
codes: [
|
|
347
|
+
"WIDGET_DEV_FAILED",
|
|
348
|
+
"WIDGET_DEV_UNAUTHORIZED",
|
|
349
|
+
"WIDGET_DEV_CLAIM_REJECTED",
|
|
350
|
+
"WIDGET_DEV_PROFILE_NOT_FOUND"
|
|
351
|
+
],
|
|
352
|
+
area: "host-bridge",
|
|
353
|
+
guidance: "读取失败阶段、端点状态和具体响应;区分未监听、403、超时与不兼容,再检查目标宿主。"
|
|
354
|
+
})
|
|
355
|
+
]);
|
|
356
|
+
const UNKNOWN_RULE = rule({
|
|
357
|
+
id: "unknown",
|
|
358
|
+
source: "host",
|
|
359
|
+
codes: [],
|
|
360
|
+
area: "runtime",
|
|
361
|
+
guidance: "查询当前状态、实际产物和最后失败阶段,补充证据后选择恢复路径;不要猜测命令或反复执行。"
|
|
362
|
+
});
|
|
363
|
+
const OUTCOME_RULE = rule({
|
|
364
|
+
id: "operation-outcome",
|
|
365
|
+
source: "host",
|
|
366
|
+
codes: [],
|
|
367
|
+
area: "operation-outcome",
|
|
368
|
+
verification: {
|
|
369
|
+
target: "original-operation",
|
|
370
|
+
milestone: "operation-confirmed"
|
|
371
|
+
},
|
|
372
|
+
guidance: "原操作可能已执行或部分完成。先按原操作身份核对资源,再决定后续动作,不自动重放。"
|
|
373
|
+
});
|
|
374
|
+
const REPEATED_RULE = rule({
|
|
375
|
+
id: "repeated-without-progress",
|
|
376
|
+
source: "host",
|
|
377
|
+
codes: [],
|
|
378
|
+
area: "runtime",
|
|
379
|
+
guidance: "相同故障没有新证据或产物,先补充诊断或改变排查策略,不重复同一编辑、构建或重试。"
|
|
380
|
+
});
|
|
381
|
+
/** 包含 unknown 与操作核对规则,Agent 可按 plan.ruleId 定位每个实际计划。 */
|
|
382
|
+
const WIDGET_RECOVERY_RULES = Object.freeze([
|
|
383
|
+
...STANDARD_RULES,
|
|
384
|
+
UNKNOWN_RULE,
|
|
385
|
+
OUTCOME_RULE,
|
|
386
|
+
REPEATED_RULE
|
|
387
|
+
]);
|
|
388
|
+
/** 根据稳定码选择通用规则;未知码明确走排查,不匹配自由文本。 */
|
|
389
|
+
function findWidgetRecoveryRule(diagnostic) {
|
|
390
|
+
return WIDGET_RECOVERY_RULES.find((value) => value.sources.includes(diagnostic.source) && value.codes.includes(diagnostic.code)) ?? UNKNOWN_RULE;
|
|
391
|
+
}
|
|
392
|
+
/** 纯规则选择,不运行工具;宿主仍须校验实际能力、权限、scope 与恢复预算。 */
|
|
393
|
+
function createWidgetRecoveryPlan(diagnostic, context = {}) {
|
|
394
|
+
let selected = findWidgetRecoveryRule(diagnostic);
|
|
395
|
+
if (context.executionState === "unknown" || context.executionState === "partial" || context.mayHaveExecuted === true && context.executionState !== "finished") selected = OUTCOME_RULE;
|
|
396
|
+
else if (context.progress?.repeated && !context.progress.hasNewEvidence) selected = REPEATED_RULE;
|
|
397
|
+
else if (selected.disposition === "retry" && (diagnostic.retryable !== true || context.executionState !== "not_started" || context.mayHaveExecuted !== false)) selected = OUTCOME_RULE;
|
|
398
|
+
if (selected.disposition === "repair" && !diagnostic.diagnostics?.length) return {
|
|
399
|
+
schemaVersion: 1,
|
|
400
|
+
ruleId: selected.id,
|
|
401
|
+
ruleVersion: selected.version,
|
|
402
|
+
...context.progress ? { progress: { ...context.progress } } : {},
|
|
403
|
+
disposition: "inspect",
|
|
404
|
+
nextAction: {
|
|
405
|
+
id: "inspect",
|
|
406
|
+
params: { area: diagnostic.source === "toolchain" ? "toolchain" : "render" }
|
|
407
|
+
},
|
|
408
|
+
followUpActions: [],
|
|
409
|
+
verification: {
|
|
410
|
+
target: "current-attempt",
|
|
411
|
+
milestone: "diagnostics-updated"
|
|
412
|
+
},
|
|
413
|
+
guidance: "缺少可定位的诊断,先查询编译或源码证据,再修复;不能仅凭错误码猜测补丁。"
|
|
414
|
+
};
|
|
415
|
+
return {
|
|
416
|
+
schemaVersion: 1,
|
|
417
|
+
ruleId: selected.id,
|
|
418
|
+
ruleVersion: selected.version,
|
|
419
|
+
disposition: selected.disposition,
|
|
420
|
+
...context.progress ? { progress: { ...context.progress } } : {},
|
|
421
|
+
nextAction: structuredClone(selected.nextAction),
|
|
422
|
+
followUpActions: structuredClone(selected.followUpActions),
|
|
423
|
+
verification: { ...selected.verification },
|
|
424
|
+
guidance: selected.guidance
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/** 只投影已校验的稳定字段;Guest message、context 和恢复文字不进入可信诊断。 */
|
|
428
|
+
function projectWidgetRuntimeDiagnostic(error, operation, diagnostics) {
|
|
429
|
+
return {
|
|
430
|
+
schemaVersion: 1,
|
|
431
|
+
source: "sdk",
|
|
432
|
+
code: error.code,
|
|
433
|
+
phase: error.phase,
|
|
434
|
+
operation,
|
|
435
|
+
severity: error.code === "RENDER_FAILED" || error.code === "UPDATE_FAILED" ? "request-error" : "instance-failure",
|
|
436
|
+
message: "Widget runtime operation failed",
|
|
437
|
+
retryable: error.retryable,
|
|
438
|
+
correlationId: error.correlationId,
|
|
439
|
+
...diagnostics ? { diagnostics: structuredClone(diagnostics) } : {}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/** 能力拒绝只影响当前请求;不把它投影为实例终态。 */
|
|
443
|
+
function projectWidgetCapabilityDiagnostic(failure, operation) {
|
|
444
|
+
return {
|
|
445
|
+
schemaVersion: 1,
|
|
446
|
+
source: "host",
|
|
447
|
+
code: failure.reason,
|
|
448
|
+
phase: "capability",
|
|
449
|
+
operation,
|
|
450
|
+
severity: "request-error",
|
|
451
|
+
message: "Host capability request failed",
|
|
452
|
+
retryable: failure.retryable
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/** 工程错误仅提取稳定 code;原始 stdout/stderr 由可信工具单独脱敏投影。 */
|
|
456
|
+
function projectWidgetToolchainDiagnostic(error, operation, phase) {
|
|
457
|
+
return {
|
|
458
|
+
schemaVersion: 1,
|
|
459
|
+
source: "toolchain",
|
|
460
|
+
code: error.code,
|
|
461
|
+
phase,
|
|
462
|
+
operation,
|
|
463
|
+
severity: "request-error",
|
|
464
|
+
message: "Widget project operation failed"
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
/** 仅选择双方声明的扩展;实际 frame 来源和当前 attempt 仍由 Host 校验。 */
|
|
468
|
+
function selectWidgetDevelopmentExtensions(offer, supported) {
|
|
469
|
+
return offer?.extensions.includes("development-diagnostics-v1") && supported.includes("development-diagnostics-v1") ? ["development-diagnostics-v1"] : [];
|
|
470
|
+
}
|
|
471
|
+
//#endregion
|
|
472
|
+
export { projectWidgetRuntimeDiagnostic as a, WIDGET_EVENT_NAMES as c, isWidgetActivationEvent as d, projectWidgetCapabilityDiagnostic as i, isWidgetEventName as l, createWidgetRecoveryPlan as n, projectWidgetToolchainDiagnostic as o, findWidgetRecoveryRule as r, selectWidgetDevelopmentExtensions as s, WIDGET_RECOVERY_RULES as t, WIDGET_ACTIVATION_EVENTS as u };
|
package/dist/widget.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CapabilityRequest, CapabilityResult, FileDownloadResult, FileReadInput, FileReference, FileWriteInput, GrantedCapability, RegionCapabilityResult, RenderRequest,
|
|
1
|
+
import type { CapabilityRequest, CapabilityResult, FileDownloadResult, FileReadInput, FileReference, FileWriteInput, GrantedCapability, RegionCapabilityResult, RenderRequest, Theme, UiStateResult, UiStateWriteResult, WidgetEventName, WidgetLiveEvent } from "./protocol/index.js";
|
|
2
2
|
import { type FileReadCapabilityResult, type FileWriteCapabilityResult } from "./runtime/files.js";
|
|
3
3
|
import { type CreateWidgetClientOptions, type RegionName, type WidgetClientFactoryOptions, type WidgetClientState, type WidgetLiveEventContext, type WidgetLiveEventHandler } from "./runtime/index.js";
|
|
4
4
|
import type { WidgetNetworkClient } from "./runtime/network.js";
|
|
@@ -52,45 +52,6 @@ export type FileDownloadCapabilityResult = {
|
|
|
52
52
|
} | Extract<CapabilityResult, {
|
|
53
53
|
readonly ok: false;
|
|
54
54
|
}>;
|
|
55
|
-
export interface WidgetTaskApi {
|
|
56
|
-
/**
|
|
57
|
-
* 读取一次受 Host 限制的任务摘要页。返回的 taskId 可继续用于 tasks.watch;
|
|
58
|
-
* 此 API 只发现任务,不创建、调度或取消任务。
|
|
59
|
-
*/
|
|
60
|
-
list(input?: {
|
|
61
|
-
readonly cursor?: string;
|
|
62
|
-
readonly limit?: number;
|
|
63
|
-
}, options?: {
|
|
64
|
-
readonly timeoutMs?: number;
|
|
65
|
-
readonly signal?: AbortSignal;
|
|
66
|
-
}): Promise<TaskListCapabilityResult>;
|
|
67
|
-
/**
|
|
68
|
-
* 读取一次受 Host 限制的任务快照和事件页。调用方使用返回的 cursor
|
|
69
|
-
* 继续轮询;此 API 不创建、调度或取消任务。
|
|
70
|
-
*/
|
|
71
|
-
watch(input: {
|
|
72
|
-
readonly taskId: string;
|
|
73
|
-
readonly cursor?: string;
|
|
74
|
-
readonly limit?: number;
|
|
75
|
-
}, options?: {
|
|
76
|
-
readonly timeoutMs?: number;
|
|
77
|
-
readonly signal?: AbortSignal;
|
|
78
|
-
}): Promise<TaskWatchCapabilityResult>;
|
|
79
|
-
}
|
|
80
|
-
/** tasks.list 的类型化能力结果。 */
|
|
81
|
-
export type TaskListCapabilityResult = {
|
|
82
|
-
readonly ok: true;
|
|
83
|
-
readonly data: TaskListPage;
|
|
84
|
-
} | Extract<CapabilityResult, {
|
|
85
|
-
readonly ok: false;
|
|
86
|
-
}>;
|
|
87
|
-
/** tasks.watch 的类型化能力结果。 */
|
|
88
|
-
export type TaskWatchCapabilityResult = {
|
|
89
|
-
readonly ok: true;
|
|
90
|
-
readonly data: TaskObservationBatch;
|
|
91
|
-
} | Extract<CapabilityResult, {
|
|
92
|
-
readonly ok: false;
|
|
93
|
-
}>;
|
|
94
55
|
export interface WidgetUiStateApi {
|
|
95
56
|
read(key: string, options?: {
|
|
96
57
|
readonly timeoutMs?: number;
|
|
@@ -147,7 +108,6 @@ export interface WidgetContext {
|
|
|
147
108
|
readonly signal: AbortSignal;
|
|
148
109
|
readonly capabilities: WidgetCapabilityApi;
|
|
149
110
|
readonly files: WidgetFileApi;
|
|
150
|
-
readonly tasks: WidgetTaskApi;
|
|
151
111
|
readonly network: WidgetNetworkClient;
|
|
152
112
|
/** 低层 service.invoke 入口;保留给自定义 Host service。 */
|
|
153
113
|
readonly services: WidgetServiceClient;
|
package/llms.txt
CHANGED
|
@@ -28,7 +28,10 @@
|
|
|
28
28
|
- 先在 Manifest 声明 capability,再等待 Host grant。
|
|
29
29
|
- 用 `WidgetError` 的 `code/phase/retryable/correlationId` 诊断启动、渲染和能力失败。
|
|
30
30
|
- `context.render` 异常通过 `runWidgetHost` 的既有 `onError` / `events.onError` 和 Guest console
|
|
31
|
-
|
|
31
|
+
本地报告,不自动终止或重试。Host 协商 `development-diagnostics-v1` 后还会收到与请求关联的
|
|
32
|
+
`render-result`;使用 Host 的查询入口取得标准诊断与 SDK 恢复规则。旧 Host 不接收新增消息。
|
|
33
|
+
- 修复后构建并刷新当前预览,再查新代际回执。`handled` 证明当前处理器完成,仍需验收实际 DOM
|
|
34
|
+
和业务场景;`ready`、`unknown`、`unsupported` 和等待超时都不是恢复成功。
|
|
32
35
|
- `ok=false` 的能力结果是可处理状态,不要直接重试或绕过 Host。
|
|
33
36
|
- 文件读写使用 `context.files.read(input, options)` / `write(input, options)`;Manifest
|
|
34
37
|
分别申请 `files.read` / `files.write`。file-preview 的引用来自当前文件;Panel 只有获得
|
|
@@ -43,3 +46,12 @@
|
|
|
43
46
|
write-outcome-unknown,不自动重试;保留内容和同一 mutationId 核对结果,成功回执后才标记已保存。
|
|
44
47
|
- revision-conflict 需重读并合并;mutation-conflict 表示同一 mutationId 被用于不同内容。
|
|
45
48
|
- SDK 本地通过不等于 iframe 已真实渲染、Bundle 已发布或 Agent 已绑定。
|
|
49
|
+
|
|
50
|
+
## Widget 尺寸与滚动
|
|
51
|
+
|
|
52
|
+
- `panel` 和使用填充布局的 `file-preview` 由 Host 分配 iframe 高度。为 `html`、`body` 和挂载根设置 `height: 100%`,让滚动区或画布容器使用 `flex: 1; min-height: 0`。
|
|
53
|
+
- 按内容定高的嵌入式 `file-preview` 可以在第一次 `context.render` 中用 `context.resize(width, height)` 请求合适的初始尺寸。`request.context.viewport` 表示 Host 报告的当前视口,避免把视口或 iframe 测量值回报给 Host。
|
|
54
|
+
- 观察实际画布容器的大小并重绘。用真实 Host 改变窗口高度,核对内容区、iframe 和滚动区或画布的高度。
|
|
55
|
+
|
|
56
|
+
生成工程的 `src/widget.ts` 或 `src/widget.tsx` 带有布局示例。完整说明见
|
|
57
|
+
[样式指南](https://dev.dp.tech/bohrium-widget-sdk/guides/styling#面板高度与滚动)。
|
package/package.json
CHANGED
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
"properties": {
|
|
66
66
|
"files": { "$ref": "#/$defs/FilesCapabilityRequest" },
|
|
67
67
|
"events": { "$ref": "#/$defs/EventsCapabilityRequest" },
|
|
68
|
-
"tasks": { "$ref": "#/$defs/TasksCapabilityRequest" },
|
|
69
68
|
"state": { "$ref": "#/$defs/StateCapabilityRequest" },
|
|
70
69
|
"actions": { "$ref": "#/$defs/ActionsCapabilityRequest" },
|
|
71
70
|
"network": { "$ref": "#/$defs/NetworkCapabilityRequest" },
|
|
@@ -111,17 +110,6 @@
|
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
112
|
},
|
|
114
|
-
"TasksCapabilityRequest": {
|
|
115
|
-
"description": "任务发现与观察能力的申请;list / watch 至少显式申请一项,已申请项固定为 true。",
|
|
116
|
-
"title": "TasksCapabilityRequest",
|
|
117
|
-
"type": "object",
|
|
118
|
-
"additionalProperties": false,
|
|
119
|
-
"minProperties": 1,
|
|
120
|
-
"properties": {
|
|
121
|
-
"list": { "const": true },
|
|
122
|
-
"watch": { "const": true }
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
113
|
"StateCapabilityRequest": {
|
|
126
114
|
"description": "受限 UI 状态读写能力的申请;read / write 分别声明。",
|
|
127
115
|
"title": "StateCapabilityRequest",
|