@blade-hq/agent-kit 0.5.5 → 0.5.7

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.
Files changed (34) hide show
  1. package/dist/{SkillStatusBar-BtvQmvxZ.d.ts → SkillStatusBar-Dlf-_G5d.d.ts} +2 -2
  2. package/dist/{blade-client-CqWTKQhP.d.ts → blade-client-7VANnJfr.d.ts} +6 -3
  3. package/dist/{chunk-Q6CSM5DE.js → chunk-ETHPRRT2.js} +11 -4
  4. package/dist/chunk-ETHPRRT2.js.map +1 -0
  5. package/dist/{chunk-557R7QZV.js → chunk-GIE2Q2MB.js} +28 -16
  6. package/dist/chunk-GIE2Q2MB.js.map +1 -0
  7. package/dist/{chunk-S6EPGDAL.js → chunk-K5EE7X2D.js} +152 -73
  8. package/dist/chunk-K5EE7X2D.js.map +1 -0
  9. package/dist/{chunk-NEI66DW6.js → chunk-STCTXRMJ.js} +2 -2
  10. package/dist/{chunk-HWOVPFWG.js → chunk-UM7G65GH.js} +116 -20
  11. package/dist/chunk-UM7G65GH.js.map +1 -0
  12. package/dist/{chunk-3ZEWA2YM.js → chunk-X3S36RR2.js} +2 -2
  13. package/dist/client/index.d.ts +111 -4
  14. package/dist/client/index.js +1 -1
  15. package/dist/react/api/vibe-coding.d.ts +3 -3
  16. package/dist/react/api/vibe-coding.js +2 -2
  17. package/dist/react/components/chat/index.d.ts +6 -5
  18. package/dist/react/components/chat/index.js +5 -5
  19. package/dist/react/components/plan/index.js +3 -3
  20. package/dist/react/components/session/index.d.ts +1 -1
  21. package/dist/react/components/session/index.js +3 -3
  22. package/dist/react/components/workspace/index.js +3 -3
  23. package/dist/react/index.d.ts +177 -9
  24. package/dist/react/index.js +469 -6
  25. package/dist/react/index.js.map +1 -1
  26. package/dist/{session-ADRevzHD.d.ts → session-BuaeCsMC.d.ts} +62 -2
  27. package/dist/style.css +1 -1
  28. package/package.json +1 -1
  29. package/dist/chunk-557R7QZV.js.map +0 -1
  30. package/dist/chunk-HWOVPFWG.js.map +0 -1
  31. package/dist/chunk-Q6CSM5DE.js.map +0 -1
  32. package/dist/chunk-S6EPGDAL.js.map +0 -1
  33. /package/dist/{chunk-NEI66DW6.js.map → chunk-STCTXRMJ.js.map} +0 -0
  34. /package/dist/{chunk-3ZEWA2YM.js.map → chunk-X3S36RR2.js.map} +0 -0
@@ -30,7 +30,7 @@ import {
30
30
  useTiptapVoiceInput,
31
31
  useVoiceInput,
32
32
  user_preferences_exports
33
- } from "../chunk-S6EPGDAL.js";
33
+ } from "../chunk-K5EE7X2D.js";
34
34
  import {
35
35
  CardCodeBlock,
36
36
  CardContext,
@@ -39,18 +39,18 @@ import {
39
39
  normalizeCodeLanguage,
40
40
  useCardContext,
41
41
  useHighlightedCodeHtml
42
- } from "../chunk-557R7QZV.js";
42
+ } from "../chunk-GIE2Q2MB.js";
43
43
  import {
44
44
  CardJSON,
45
45
  cardRegistry
46
46
  } from "../chunk-2UP7MG3J.js";
47
47
  import {
48
48
  useSession
49
- } from "../chunk-NEI66DW6.js";
49
+ } from "../chunk-STCTXRMJ.js";
50
50
  import {
51
51
  buildSessionBinaryPreviewTarget,
52
52
  resolveSessionFilePreviewTarget
53
- } from "../chunk-3ZEWA2YM.js";
53
+ } from "../chunk-X3S36RR2.js";
54
54
  import {
55
55
  PartnerSkillFile,
56
56
  PartnerSkillInstallPayload,
@@ -102,12 +102,12 @@ import {
102
102
  useTaskStore,
103
103
  useUiBridgeStore,
104
104
  useUiStore
105
- } from "../chunk-HWOVPFWG.js";
105
+ } from "../chunk-UM7G65GH.js";
106
106
  import "../chunk-J3XVFPOV.js";
107
107
  import {
108
108
  REGISTRY_PREFIX,
109
109
  normalizeResource
110
- } from "../chunk-Q6CSM5DE.js";
110
+ } from "../chunk-ETHPRRT2.js";
111
111
  import {
112
112
  cn,
113
113
  copyToClipboard
@@ -1132,6 +1132,466 @@ function deriveDeprecateEntryIds(stepRanges, messages, fromStep) {
1132
1132
  return ids;
1133
1133
  }
1134
1134
 
1135
+ // src/react/lib/html-source-locator.ts
1136
+ var SNIPPET_LIMIT = 2e3;
1137
+ var TEXT_ANCHOR_LIMIT = 120;
1138
+ var VOID_TAGS = /* @__PURE__ */ new Set([
1139
+ "area",
1140
+ "base",
1141
+ "br",
1142
+ "col",
1143
+ "embed",
1144
+ "hr",
1145
+ "img",
1146
+ "input",
1147
+ "link",
1148
+ "meta",
1149
+ "param",
1150
+ "source",
1151
+ "track",
1152
+ "wbr"
1153
+ ]);
1154
+ var STABLE_ATTRS = ["data-blade-id", "data-testid", "data-cy", "id"];
1155
+ function escapeRegExp(value) {
1156
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1157
+ }
1158
+ function normalizeTag(tag) {
1159
+ return tag.trim().toLowerCase().replace(/[^a-z0-9:-]/g, "") || "*";
1160
+ }
1161
+ function normalizeText(text) {
1162
+ return text.replace(/\s+/g, " ").trim();
1163
+ }
1164
+ function truncate(value, limit) {
1165
+ if (value.length <= limit) return value;
1166
+ return `${value.slice(0, Math.max(0, limit - 1))}\u2026`;
1167
+ }
1168
+ function lineColumnAt(content, index) {
1169
+ const before = content.slice(0, Math.max(0, index));
1170
+ const lines = before.split("\n");
1171
+ return { line: lines.length, column: lines[lines.length - 1].length + 1 };
1172
+ }
1173
+ function buildTagPattern(tag, innerPattern) {
1174
+ const tagPart = tag === "*" ? "[a-zA-Z][\\w:-]*" : escapeRegExp(tag);
1175
+ return new RegExp(`<${tagPart}\\b(?=[^>]*${innerPattern})[^>]*>`, "gi");
1176
+ }
1177
+ function attrPattern(attr, value) {
1178
+ const escapedAttr = escapeRegExp(attr);
1179
+ const escapedValue = escapeRegExp(value);
1180
+ return `\\b${escapedAttr}\\s*=\\s*(?:"${escapedValue}"|'${escapedValue}'|${escapedValue}(?=\\s|>|/))`;
1181
+ }
1182
+ function collectMatches(pattern, content) {
1183
+ const matches = [];
1184
+ pattern.lastIndex = 0;
1185
+ let match = pattern.exec(content);
1186
+ while (match != null) {
1187
+ matches.push(match);
1188
+ if (match[0].length === 0) pattern.lastIndex += 1;
1189
+ match = pattern.exec(content);
1190
+ }
1191
+ return matches;
1192
+ }
1193
+ function findOpeningTagByAttr(content, tag, attr, value) {
1194
+ if (!value.trim()) return null;
1195
+ const matches = collectMatches(buildTagPattern(tag, attrPattern(attr, value)), content);
1196
+ if (matches.length !== 1) return null;
1197
+ return { index: matches[0].index, text: matches[0][0] };
1198
+ }
1199
+ function findOpeningTagNearText(content, tag, visibleText) {
1200
+ const text = normalizeText(visibleText);
1201
+ if (!text || text.length > TEXT_ANCHOR_LIMIT) return null;
1202
+ const textMatches = collectMatches(new RegExp(escapeRegExp(text), "g"), normalizeText(content));
1203
+ if (textMatches.length !== 1) return null;
1204
+ const originalIndex = content.indexOf(text);
1205
+ if (originalIndex < 0) return null;
1206
+ const normalizedTag = normalizeTag(tag);
1207
+ const tagPart = normalizedTag === "*" ? "[a-zA-Z][\\w:-]*" : escapeRegExp(normalizedTag);
1208
+ const openPattern = new RegExp(`<${tagPart}\\b[^>]*>`, "gi");
1209
+ let candidate = null;
1210
+ let match = openPattern.exec(content);
1211
+ while (match != null) {
1212
+ if (match.index > originalIndex) break;
1213
+ candidate = match;
1214
+ match = openPattern.exec(content);
1215
+ }
1216
+ if (!candidate) return null;
1217
+ return { index: candidate.index, text: candidate[0] };
1218
+ }
1219
+ function findSnippetEnd(content, start, tag, openingText) {
1220
+ const openingEnd = start + openingText.length;
1221
+ const normalizedTag = normalizeTag(tag);
1222
+ if (VOID_TAGS.has(normalizedTag) || openingText.endsWith("/>")) return openingEnd;
1223
+ const closePattern = new RegExp(`</${escapeRegExp(normalizedTag)}>`, "i");
1224
+ const closeMatch = closePattern.exec(content.slice(openingEnd));
1225
+ if (!closeMatch) return openingEnd;
1226
+ return openingEnd + closeMatch.index + closeMatch[0].length;
1227
+ }
1228
+ function buildSnippet(content, start, tag, openingText) {
1229
+ const rawEnd = findSnippetEnd(content, start, tag, openingText);
1230
+ const boundedEnd = Math.min(rawEnd, start + SNIPPET_LIMIT);
1231
+ const snippet = content.slice(start, boundedEnd);
1232
+ const startPos = lineColumnAt(content, start);
1233
+ const endPos = lineColumnAt(content, boundedEnd);
1234
+ return {
1235
+ snippet: rawEnd > boundedEnd ? `${snippet}\u2026` : snippet,
1236
+ startLine: startPos.line,
1237
+ endLine: endPos.line,
1238
+ startColumn: startPos.column,
1239
+ endColumn: endPos.column
1240
+ };
1241
+ }
1242
+ function locateHtmlElementSource(html, element) {
1243
+ const tag = normalizeTag(element.tag);
1244
+ const attributes = element.attributes ?? {};
1245
+ const visibleText = normalizeText(element.visibleText ?? "");
1246
+ const searchAnchors = [];
1247
+ for (const attr of STABLE_ATTRS) {
1248
+ const value = attributes[attr];
1249
+ if (!value) continue;
1250
+ searchAnchors.push(`${attr}="${truncate(value, TEXT_ANCHOR_LIMIT)}"`);
1251
+ const found = findOpeningTagByAttr(html, tag, attr, value);
1252
+ if (found) {
1253
+ return {
1254
+ ...buildSnippet(html, found.index, tag, found.text),
1255
+ searchAnchors,
1256
+ locatorConfidence: "high",
1257
+ reason: `${attr} \u53EF\u552F\u4E00\u5B9A\u4F4D`
1258
+ };
1259
+ }
1260
+ }
1261
+ const ariaLabel = attributes["aria-label"];
1262
+ if (ariaLabel) {
1263
+ searchAnchors.push(`aria-label="${truncate(ariaLabel, TEXT_ANCHOR_LIMIT)}"`);
1264
+ const found = findOpeningTagByAttr(html, tag, "aria-label", ariaLabel);
1265
+ if (found) {
1266
+ return {
1267
+ ...buildSnippet(html, found.index, tag, found.text),
1268
+ searchAnchors,
1269
+ locatorConfidence: "medium",
1270
+ reason: "aria-label \u53EF\u552F\u4E00\u5B9A\u4F4D"
1271
+ };
1272
+ }
1273
+ }
1274
+ if (visibleText) {
1275
+ searchAnchors.push(`text="${truncate(visibleText, TEXT_ANCHOR_LIMIT)}"`);
1276
+ const found = findOpeningTagNearText(html, tag, visibleText);
1277
+ if (found) {
1278
+ return {
1279
+ ...buildSnippet(html, found.index, tag, found.text),
1280
+ searchAnchors,
1281
+ locatorConfidence: "medium",
1282
+ reason: "\u53EF\u89C1\u6587\u5B57\u53EF\u552F\u4E00\u5B9A\u4F4D"
1283
+ };
1284
+ }
1285
+ }
1286
+ return {
1287
+ searchAnchors,
1288
+ locatorConfidence: "low",
1289
+ reason: "\u672A\u80FD\u5728\u6E90 HTML \u4E2D\u552F\u4E00\u5B9A\u4F4D\uFF0C\u9700\u7ED3\u5408\u6587\u4EF6\u5185\u5BB9\u4EBA\u5DE5\u6838\u5BF9"
1290
+ };
1291
+ }
1292
+
1293
+ // src/react/lib/html-element-selection.ts
1294
+ var VALUE_LIMIT = 240;
1295
+ var TEXT_LIMIT = 500;
1296
+ var STYLE_KEYS = /* @__PURE__ */ new Set([
1297
+ "display",
1298
+ "position",
1299
+ "color",
1300
+ "backgroundColor",
1301
+ "fontSize",
1302
+ "fontWeight",
1303
+ "margin",
1304
+ "padding"
1305
+ ]);
1306
+ var SENSITIVE_NAME_RE = /(password|passwd|token|secret|authorization|api[-_]?key|cookie)/i;
1307
+ var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action"]);
1308
+ var USER_VALUE_ATTRS = /* @__PURE__ */ new Set(["value"]);
1309
+ function truncate2(value, limit, limits) {
1310
+ if (value.length <= limit) return value;
1311
+ limits.truncated = true;
1312
+ return `${value.slice(0, Math.max(0, limit - 1))}\u2026`;
1313
+ }
1314
+ function stripQueryAndHash(value) {
1315
+ try {
1316
+ const url = new URL(value, "http://blade.local");
1317
+ if (url.protocol === "http:" && url.host === "blade.local" && value.startsWith("/")) {
1318
+ return url.pathname;
1319
+ }
1320
+ url.search = "";
1321
+ url.hash = "";
1322
+ return url.toString();
1323
+ } catch {
1324
+ return value.split(/[?#]/, 1)[0];
1325
+ }
1326
+ }
1327
+ function sanitizeAttributes(attributes, limits) {
1328
+ const result = {};
1329
+ for (const [key, rawValue] of Object.entries(attributes)) {
1330
+ const normalizedKey = key.trim();
1331
+ if (!normalizedKey) continue;
1332
+ if (SENSITIVE_NAME_RE.test(normalizedKey) || USER_VALUE_ATTRS.has(normalizedKey.toLowerCase())) {
1333
+ result[normalizedKey] = "[\u5DF2\u8131\u654F]";
1334
+ limits.omittedSensitiveFields.push(normalizedKey);
1335
+ continue;
1336
+ }
1337
+ const normalizedValue = URL_ATTRS.has(normalizedKey.toLowerCase()) ? stripQueryAndHash(rawValue) : rawValue;
1338
+ result[normalizedKey] = truncate2(normalizedValue, VALUE_LIMIT, limits);
1339
+ }
1340
+ return result;
1341
+ }
1342
+ function sanitizeStyle(style, limits) {
1343
+ const result = {};
1344
+ for (const [key, value] of Object.entries(style)) {
1345
+ if (!STYLE_KEYS.has(key)) continue;
1346
+ const normalizedKey = key.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
1347
+ result[normalizedKey] = truncate2(String(value), 80, limits);
1348
+ }
1349
+ return result;
1350
+ }
1351
+ function limitList(values, limits) {
1352
+ if (!values?.length) return void 0;
1353
+ if (values.length > 5) limits.truncated = true;
1354
+ return values.slice(0, 5).map((item) => truncate2(item, 160, limits));
1355
+ }
1356
+ function fileNameFromPath(filePath, fallback) {
1357
+ return fallback?.trim() || filePath.split("/").pop() || filePath || "\u672A\u547D\u540D HTML";
1358
+ }
1359
+ function elementKindLabel(selection) {
1360
+ const tag = selection.tag.toLowerCase();
1361
+ const role = selection.role?.toLowerCase();
1362
+ if (role === "button" || tag === "button") return "\u6309\u94AE";
1363
+ if (role === "link" || tag === "a") return "\u94FE\u63A5";
1364
+ if (role === "textbox" || tag === "input" || tag === "textarea") return "\u8F93\u5165\u6846";
1365
+ if (tag === "img") return "\u56FE\u7247";
1366
+ if (tag === "table") return "\u8868\u683C";
1367
+ if (tag === "tr") return "\u8868\u683C\u884C";
1368
+ if (tag === "td" || tag === "th") return "\u5355\u5143\u683C";
1369
+ if (/^h[1-6]$/.test(tag)) return "\u6807\u9898";
1370
+ if (tag === "section" || tag === "article" || tag === "div") return "\u533A\u57DF";
1371
+ return "\u5185\u5BB9";
1372
+ }
1373
+ function humanElementLabel(selection) {
1374
+ const kind = elementKindLabel(selection);
1375
+ const text = (selection.accessibleName || selection.visibleText || "").replace(/\s+/g, " ").trim();
1376
+ return text ? `${kind}\u300C${text.slice(0, 24)}${text.length > 24 ? "\u2026" : ""}\u300D` : `${kind}<${selection.tag}>`;
1377
+ }
1378
+ function stableSelectorsFromAttributes(attributes) {
1379
+ const selectors = [];
1380
+ const add = (kind, value, confidence) => {
1381
+ if (value && !selectors.some((item) => item.kind === kind && item.value === value)) {
1382
+ selectors.push({ kind, value, confidence });
1383
+ }
1384
+ };
1385
+ add("data-blade-id", attributes["data-blade-id"], "high");
1386
+ add("data-testid", attributes["data-testid"] || attributes["data-cy"], "high");
1387
+ add("id", attributes.id, "high");
1388
+ add("aria", attributes["aria-label"], "medium");
1389
+ return selectors;
1390
+ }
1391
+ function mergeSelectors(selection, attributes) {
1392
+ const selectors = [...stableSelectorsFromAttributes(attributes), ...selection.selectors ?? []];
1393
+ const seen = /* @__PURE__ */ new Set();
1394
+ return selectors.filter((selector) => {
1395
+ if (!selector.value) return false;
1396
+ const key = `${selector.kind}:${selector.value}`;
1397
+ if (seen.has(key)) return false;
1398
+ seen.add(key);
1399
+ return true;
1400
+ });
1401
+ }
1402
+ function sanitizeGroupItems(selection, limits) {
1403
+ const items = selection.groupItems ?? [];
1404
+ if (!items.length) return void 0;
1405
+ if (items.length > 12) limits.truncated = true;
1406
+ return items.slice(0, 12).map((item) => {
1407
+ const label = item.label ? truncate2(item.label.replace(/\s+/g, " ").trim(), 120, limits) : void 0;
1408
+ const visibleText = item.visibleText ? truncate2(item.visibleText.replace(/\s+/g, " ").trim(), 240, limits) : void 0;
1409
+ const accessibleName = item.accessibleName ? truncate2(item.accessibleName.replace(/\s+/g, " ").trim(), 160, limits) : void 0;
1410
+ return {
1411
+ label,
1412
+ tag: item.tag.toLowerCase(),
1413
+ role: item.role,
1414
+ accessible_name: accessibleName,
1415
+ visible_text: visibleText,
1416
+ selectors: item.selectors ?? [],
1417
+ dom_path: item.domPath,
1418
+ rect: item.rect
1419
+ };
1420
+ });
1421
+ }
1422
+ function simpleContentHash(content) {
1423
+ let hash = 2166136261;
1424
+ for (let index = 0; index < content.length; index += 1) {
1425
+ hash ^= content.charCodeAt(index);
1426
+ hash = Math.imul(hash, 16777619);
1427
+ }
1428
+ return (hash >>> 0).toString(16).padStart(8, "0");
1429
+ }
1430
+ function sourceToContext(source) {
1431
+ return {
1432
+ snippet: source.snippet,
1433
+ start_line: source.startLine,
1434
+ end_line: source.endLine,
1435
+ start_column: source.startColumn,
1436
+ end_column: source.endColumn,
1437
+ search_anchors: source.searchAnchors,
1438
+ locator_confidence: source.locatorConfidence,
1439
+ reason: source.reason
1440
+ };
1441
+ }
1442
+ function contextMarkdown(context) {
1443
+ const source = context.source;
1444
+ const rect = context.element.rect;
1445
+ const rectText = rect ? `x=${Math.round(rect.x)}, y=${Math.round(rect.y)}, w=${Math.round(rect.width)}, h=${Math.round(rect.height)}` : "\u672A\u77E5";
1446
+ const isRunningPage = context.source_kind === "running_page";
1447
+ const selectors = context.element.selectors.length ? context.element.selectors.map((selector, index) => `${index + 1}. ${selector.kind}="${selector.value}"\uFF08${selector.confidence}\uFF09`).join("\n") : "\u65E0\u7A33\u5B9A\u9009\u62E9\u5668\uFF1B\u8BF7\u7ED3\u5408\u6E90\u7247\u6BB5\u548C\u641C\u7D22\u951A\u70B9\u5B9A\u4F4D\u3002";
1448
+ const anchors = source.search_anchors.length ? source.search_anchors.map((anchor) => `- ${anchor}`).join("\n") : "- \u65E0";
1449
+ const sourceSnippet = source.snippet?.trim() ? `
1450
+
1451
+ ## \u6E90 HTML \u7247\u6BB5
1452
+ \`\`\`html
1453
+ ${source.snippet.trim()}
1454
+ \`\`\`` : "";
1455
+ const groupText = context.group ? `
1456
+
1457
+ ## \u7EC4\u5408\u9009\u533A
1458
+ \u7528\u6237\u6846\u9009\u7684\u662F\u4E00\u7EC4\u754C\u9762\u5185\u5BB9\uFF0C\u4E0D\u662F\u5355\u4E2A\u5B64\u7ACB\u5143\u7D20\u3002\u8BF7\u628A\u8FD9\u4E9B\u5185\u5BB9\u4F5C\u4E3A\u540C\u4E00\u4E2A\u8C03\u6574\u8303\u56F4\u5904\u7406\u3002
1459
+ ${context.group.items.map((item, index) => {
1460
+ const name = item.visible_text || item.accessible_name || item.label || item.tag;
1461
+ const rect2 = item.rect ? ` x=${Math.round(item.rect.x)}, y=${Math.round(item.rect.y)}, w=${Math.round(item.rect.width)}, h=${Math.round(item.rect.height)}` : "";
1462
+ return `${index + 1}. ${item.tag}${item.role ? ` role=${item.role}` : ""}\uFF1A${name}${rect2}`;
1463
+ }).join("\n")}` : "";
1464
+ const targetLine = isRunningPage ? `- \u9875\u9762\uFF1A\`${context.preview.url || context.file.path}\`` : `- \u6587\u4EF6\uFF1A\`${context.file.path}\``;
1465
+ const sourceHint = isRunningPage ? "\u8FD9\u662F\u7528\u6237\u5728\u53F3\u4FA7\u9884\u89C8\u4E2D\u9009\u4E2D\u7684\u8FD0\u884C\u9875\u9762\u5185\u5BB9\u3002\u8BF7\u4F18\u5148\u6839\u636E\u9875\u9762 URL\u3001\u53EF\u89C1\u6587\u5B57\u3001\u7A33\u5B9A\u6807\u8BB0\u548C\u5468\u8FB9\u5143\u7D20\uFF0C\u5728\u524D\u7AEF\u6E90\u7801\u4E2D\u5B9A\u4F4D\u5BF9\u5E94\u7EC4\u4EF6\u6216\u9875\u9762\uFF1B\u4E0D\u8981\u8981\u6C42\u7528\u6237\u63D0\u4F9B data \u5C5E\u6027\u3002" : "\u8FD9\u662F\u7528\u6237\u5728\u53F3\u4FA7 HTML \u9884\u89C8\u4E2D\u9009\u4E2D\u7684\u754C\u9762\u5185\u5BB9\u3002\u8BF7\u4F18\u5148\u7F16\u8F91\u6307\u5B9A\u6587\u4EF6\uFF0C\u5E76\u7528\u6E90 HTML \u7247\u6BB5\u3001\u7A33\u5B9A\u6807\u8BB0\u548C\u53EF\u89C1\u6587\u5B57\u5B9A\u4F4D\uFF0C\u4E0D\u8981\u4F9D\u8D56\u622A\u56FE\u731C\u6D4B\u3002";
1466
+ const screenshotNote = context.screenshot ? context.screenshot.included ? "\n\n## \u9009\u4E2D\u533A\u57DF\u622A\u56FE\n\u5DF2\u968F\u672C\u6761\u6D88\u606F\u9644\u52A0\u4E3A\u56FE\u7247\u3002\u82E5\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C\u8BF7\u76F4\u63A5\u5FFD\u7565\u622A\u56FE\u5E76\u4F7F\u7528\u4E0A\u9762\u7684\u6587\u672C\u4E0A\u4E0B\u6587\u3002" : `
1467
+
1468
+ ## \u9009\u4E2D\u533A\u57DF\u622A\u56FE
1469
+ \u622A\u56FE\u751F\u6210\u5931\u8D25${context.screenshot.reason ? `\uFF08${context.screenshot.reason}\uFF09` : ""}\uFF0C\u672C\u6761\u6D88\u606F\u4EC5\u5305\u542B\u6587\u672C\u4E0A\u4E0B\u6587\u3002` : "";
1470
+ const surroundings = [
1471
+ context.surroundings.parent_summary ? `- \u7236\u7EA7\uFF1A${context.surroundings.parent_summary}` : null,
1472
+ ...(context.surroundings.previous_siblings ?? []).map((item) => `- \u524D\u4E00\u4E2A\u5143\u7D20\uFF1A${item}`),
1473
+ ...(context.surroundings.next_siblings ?? []).map((item) => `- \u540E\u4E00\u4E2A\u5143\u7D20\uFF1A${item}`)
1474
+ ].filter(Boolean);
1475
+ return `${sourceHint}
1476
+
1477
+ ## \u7528\u6237\u9009\u4E2D
1478
+ ${targetLine}
1479
+ - \u5143\u7D20\uFF1A${context.element.label}
1480
+ - \u4F4D\u7F6E\uFF1A${rectText}
1481
+ - \u6E90\u5B9A\u4F4D\uFF1A${source.locator_confidence}\uFF08${source.reason}\uFF09
1482
+
1483
+ ## \u7A33\u5B9A\u5B9A\u4F4D\u5019\u9009
1484
+ ${selectors}
1485
+
1486
+ ## \u641C\u7D22\u951A\u70B9
1487
+ ${anchors}${sourceSnippet}${groupText}${screenshotNote}
1488
+
1489
+ ## \u5468\u8FB9\u5185\u5BB9
1490
+ ${surroundings.length ? surroundings.join("\n") : "- \u65E0\u53EF\u7528\u5468\u8FB9\u6458\u8981"}
1491
+
1492
+ ## \u539F\u59CB\u7ED3\u6784\u5316\u6570\u636E
1493
+ \`\`\`json
1494
+ ${JSON.stringify(context, null, 2)}
1495
+ \`\`\``;
1496
+ }
1497
+ function buildHtmlElementSelectionContext(input) {
1498
+ const limits = { truncated: false, omittedSensitiveFields: [] };
1499
+ const rawAttributes = input.selection.attributes ?? {};
1500
+ const attributes = sanitizeAttributes(rawAttributes, {
1501
+ get truncated() {
1502
+ return limits.truncated;
1503
+ },
1504
+ set truncated(value) {
1505
+ limits.truncated = value;
1506
+ },
1507
+ omittedSensitiveFields: limits.omittedSensitiveFields
1508
+ });
1509
+ const visibleText = input.selection.visibleText ? truncate2(input.selection.visibleText.replace(/\s+/g, " ").trim(), TEXT_LIMIT, limits) : void 0;
1510
+ const accessibleName = input.selection.accessibleName ? truncate2(input.selection.accessibleName.replace(/\s+/g, " ").trim(), TEXT_LIMIT, limits) : void 0;
1511
+ const html = input.html ?? "";
1512
+ const sourceKind = input.sourceKind ?? "html_file";
1513
+ const source = html ? locateHtmlElementSource(html, {
1514
+ tag: input.selection.tag,
1515
+ attributes,
1516
+ visibleText: visibleText || accessibleName
1517
+ }) : {
1518
+ searchAnchors: visibleText ? [`text="${visibleText}"`] : [],
1519
+ locatorConfidence: "low",
1520
+ reason: sourceKind === "running_page" ? "\u8FD0\u884C\u9875\u9762\u672A\u63D0\u4F9B\u6E90 HTML\uFF0C\u9700\u7ED3\u5408\u524D\u7AEF\u6E90\u7801\u548C\u8DEF\u7531\u4EBA\u5DE5\u6838\u5BF9" : "\u672A\u63D0\u4F9B\u6E90 HTML\uFF0C\u9700\u7ED3\u5408\u6587\u4EF6\u5185\u5BB9\u4EBA\u5DE5\u6838\u5BF9"
1521
+ };
1522
+ const viewport = input.selection.viewport ?? {
1523
+ width: 0,
1524
+ height: 0,
1525
+ devicePixelRatio: 1
1526
+ };
1527
+ const groupItems = sanitizeGroupItems(input.selection, limits);
1528
+ const groupLabel = input.selection.selectionMode === "group" ? input.selection.groupLabel || `\u7EC4\u5408\u9009\u533A\uFF08${input.selection.groupCount || groupItems?.length || 0} \u4E2A\u5185\u5BB9\uFF09` : void 0;
1529
+ const elementLabel = groupLabel ? `\u7EC4\u5408\u533A\u57DF\u300C${groupLabel}\u300D` : humanElementLabel({ ...input.selection, visibleText, accessibleName });
1530
+ const context = {
1531
+ schema: "blade.html_element_selection.v1",
1532
+ selected_at: input.selectedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1533
+ source_kind: sourceKind,
1534
+ file: {
1535
+ path: input.filePath,
1536
+ name: fileNameFromPath(input.filePath, input.fileName),
1537
+ language: "html",
1538
+ content_hash: simpleContentHash(html || input.pageUrl || input.filePath)
1539
+ },
1540
+ preview: {
1541
+ mode: sourceKind === "running_page" ? "running_page" : "render",
1542
+ title: input.title,
1543
+ url: input.pageUrl,
1544
+ viewport: {
1545
+ width: viewport.width,
1546
+ height: viewport.height,
1547
+ device_pixel_ratio: viewport.devicePixelRatio
1548
+ }
1549
+ },
1550
+ element: {
1551
+ label: elementLabel,
1552
+ tag: input.selection.tag.toLowerCase(),
1553
+ role: input.selection.role,
1554
+ accessible_name: accessibleName,
1555
+ visible_text: visibleText,
1556
+ attributes,
1557
+ selectors: mergeSelectors(input.selection, attributes),
1558
+ dom_path: input.selection.domPath,
1559
+ rect: input.selection.rect,
1560
+ style: sanitizeStyle(input.selection.style ?? {}, limits)
1561
+ },
1562
+ group: groupItems?.length ? {
1563
+ label: groupLabel || `\u7EC4\u5408\u9009\u533A\uFF08${groupItems.length} \u4E2A\u5185\u5BB9\uFF09`,
1564
+ count: input.selection.groupCount || groupItems.length,
1565
+ items: groupItems
1566
+ } : void 0,
1567
+ source: sourceToContext(source),
1568
+ screenshot: input.selection.screenshotDataUrl ? {
1569
+ included: true,
1570
+ note: "\u5DF2\u9644\u52A0\u9009\u4E2D\u5143\u7D20\u622A\u56FE\uFF1B\u975E\u591A\u6A21\u6001\u6A21\u578B\u4F1A\u81EA\u52A8\u5FFD\u7565\u56FE\u7247\uFF0C\u8BF7\u4F18\u5148\u4F7F\u7528\u6587\u672C\u4E0A\u4E0B\u6587\u3002"
1571
+ } : input.selection.screenshotStatus === "failed" ? {
1572
+ included: false,
1573
+ note: "\u622A\u56FE\u751F\u6210\u5931\u8D25\uFF1B\u5DF2\u4FDD\u7559\u6587\u672C\u4E0A\u4E0B\u6587\u4F9B\u6A21\u578B\u5B9A\u4F4D\u3002",
1574
+ reason: input.selection.screenshotError
1575
+ } : void 0,
1576
+ surroundings: {
1577
+ parent_summary: input.selection.surroundings?.parentSummary,
1578
+ children_summary: limitList(input.selection.surroundings?.childrenSummary, limits),
1579
+ previous_siblings: limitList(input.selection.surroundings?.previousSiblings, limits),
1580
+ next_siblings: limitList(input.selection.surroundings?.nextSiblings, limits)
1581
+ },
1582
+ limits: {
1583
+ truncated: limits.truncated || Boolean(source.snippet?.endsWith("\u2026")),
1584
+ omitted_sensitive_fields: limits.omittedSensitiveFields
1585
+ }
1586
+ };
1587
+ return {
1588
+ label: `HTML \u9009\u4E2D\u9879\uFF1A${context.element.label}`,
1589
+ content: contextMarkdown(context),
1590
+ context,
1591
+ imageUrl: input.selection.screenshotDataUrl
1592
+ };
1593
+ }
1594
+
1135
1595
  // src/react/schemas/session.ts
1136
1596
  import { type } from "arktype";
1137
1597
  var SessionStatus = type(
@@ -1173,6 +1633,7 @@ var LayoutType = /* @__PURE__ */ ((LayoutType2) => {
1173
1633
  LayoutType2["BladeCoa"] = "blade-coa";
1174
1634
  LayoutType2["ChatPreview"] = "chat-preview";
1175
1635
  LayoutType2["ChatOnly"] = "chat-only";
1636
+ LayoutType2["SolutionApp"] = "solution-app";
1176
1637
  return LayoutType2;
1177
1638
  })(LayoutType || {});
1178
1639
 
@@ -1394,6 +1855,7 @@ export {
1394
1855
  attachHostBridgeListener,
1395
1856
  auth_exports as authApi,
1396
1857
  bootstrapBladeClient,
1858
+ buildHtmlElementSelectionContext,
1397
1859
  buildMessageContent,
1398
1860
  buildSessionBinaryPreviewTarget,
1399
1861
  buildStepRanges,
@@ -1420,6 +1882,7 @@ export {
1420
1882
  isUiMeta,
1421
1883
  knowledgeApi,
1422
1884
  listSkillOrgs,
1885
+ locateHtmlElementSource,
1423
1886
  memories_exports as memoriesApi,
1424
1887
  models_exports as modelsApi,
1425
1888
  normalizeCodeLanguage,