dsh-code-ui 1.6.2 → 1.6.3

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/lib/client.js CHANGED
@@ -1367,7 +1367,6 @@ window.__ModuleLoader__.load({
1367
1367
  }
1368
1368
  function consumePendingRefs() {
1369
1369
  const pend = pendingRefs()
1370
- if (pend.length === 0) return
1371
1370
  const ids = pend.map((r) => r.id)
1372
1371
  for (let i = 0; i < pend.length; i++) {
1373
1372
  // 冻结发送时刻的编号到记录上:AI 经 get_referenced_snippets 读到的编号 = 用户在 tag 上看到的编号
@@ -1376,37 +1375,10 @@ window.__ModuleLoader__.load({
1376
1375
  pend[i].seq = i + 1
1377
1376
  }
1378
1377
  emit()
1379
- callHost('use-references', { ids }).then(() => {})
1380
- }
1381
- function buildSendPayload(draft) {
1382
- // 引用文本不粘贴进消息(明面上不展示代码正文):引用统一存 host 端,AI 通过 get_referenced_snippets
1383
- // 工具读取;消息里附「结构化指针」——人可读、AI 可读,消息区引用卡也按它解析还原(单一事实源)。
1384
- // 只取未使用引用(used 的已随旧消息消耗,不再重复附带)。整文件引用:给出绝对路径。
1385
- const text = draft || ''
1386
- if (text.indexOf('[code UI] 已标记') >= 0) return text // 幂等:已带指针不重复附加
1387
- // 编号 = 待用引用创建序(1 起),与输入框 tag、AI 工具输出三处同号:用户说「引用N」AI 即知所指
1388
- // 已被行内 chip 携带的引用不再自动附带(发送时由 codec.serialize 在 chip 位置展开)。
1389
- // chip 集合以草稿 occurrences 实时为准(用户删 chip 后自动回到自动附带),chipRefs 为兜底
1390
- const chipIds = store.draftChipIds || store.chipRefs
1391
- const pend = numberedPendingRefs().filter((x) => !chipIds || !chipIds.has(x.r.id))
1392
- const codeRefs = pend.filter((x) => x.r.kind === 'cite' && x.r.lineStart)
1393
- const fileRefs = pend.filter((x) => x.r.kind === 'cite' && !x.r.lineStart)
1394
- const trRefs = pend.filter((x) => x.r.kind === 'translate')
1395
- const items = []
1396
- for (const x of codeRefs) items.push('- ' + x.n + ': 代码引用 ' + baseName(x.r.path) + ':' + x.r.lineStart + '-' + x.r.lineEnd + '(' + x.r.path + ')')
1397
- for (const x of fileRefs) items.push('- ' + x.n + ': 文件引用 ' + baseName(x.r.path) + '(整文件,绝对路径:' + x.r.path + ')')
1398
- for (const x of trRefs) items.push(x.r.lineStart
1399
- ? '- ' + x.n + ': 翻译引用 ' + baseName(x.r.path) + ':' + x.r.lineStart + '-' + x.r.lineEnd + '(' + x.r.path + ')'
1400
- : '- ' + x.n + ': 翻译引用 ' + baseName(x.r.path) + '(' + x.r.path + ')')
1401
- const parts = []
1402
- if (codeRefs.length > 0) parts.push(codeRefs.length + ' 段代码引用')
1403
- if (fileRefs.length > 0) parts.push(fileRefs.length + ' 个文件引用')
1404
- if (trRefs.length > 0) parts.push(trRefs.length + ' 条翻译请求')
1405
- if (parts.length === 0) return text
1406
- let ptr = '[code UI] 已标记 ' + parts.join('、') + '(代码正文不进消息,请调用 get_referenced_snippets 工具读取;条目编号与用户输入框上的引用 tag 序号一致,用户说「引用N」即指该条):'
1407
- if (items.length > 0) ptr += '\n' + items.join('\n')
1408
- const sep = text.trim() ? '\n\n' : ''
1409
- return text + sep + ptr
1378
+ // 每次发送都通知宿主(含空列表=清除该会话的「最近消息引用批次」):
1379
+ // 批次经宿主 systemPrompt 动态 section 进模型上下文——消息正文不再附加指针文本
1380
+ // (用户要求:对话区不展示引用指针,但 AI 要知道并调用 get_referenced_snippets 读取)。
1381
+ callHost('use-references', { ids: ids, sessionId: store.inputSessionId || '' }).then(() => {})
1410
1382
  }
1411
1383
 
1412
1384
  /* —— 消息区引用卡:解析消息里的结构化指针,在用户气泡内追加 Trae 式引用行 —— */
@@ -1587,18 +1559,10 @@ window.__ModuleLoader__.load({
1587
1559
  patchedShells.add(shell)
1588
1560
  const origSubmit = shell.submit
1589
1561
  shell.submit = function () {
1590
- let hadRefs = false
1591
- try {
1592
- const hasRefs = pendingRefs().length > 0
1593
- hadRefs = hasRefs
1594
- const draft = store.inputDraft || ''
1595
- if (hasRefs && !selfSending && String(draft).indexOf('[code UI]') < 0) {
1596
- shell.setDraft(buildSendPayload(draft))
1597
- }
1598
- } catch (e) {}
1599
- const result = origSubmit.apply(shell, arguments)
1600
- try { if (hadRefs && !selfSending) consumePendingRefs() } catch (e) {}
1601
- return result
1562
+ // 引用不再附加到消息正文:消耗只做两件事——本地标记 used + 通知宿主更新
1563
+ // 「最近消息引用批次」(宿主经 systemPrompt section 透给模型,对话区不展示)。
1564
+ try { if (!selfSending) consumePendingRefs() } catch (e) {}
1565
+ return origSubmit.apply(shell, arguments)
1602
1566
  }
1603
1567
  } catch (e) {}
1604
1568
  }
@@ -1638,21 +1602,9 @@ window.__ModuleLoader__.load({
1638
1602
  const orig = inputActions.submit
1639
1603
  inputActions.__cdexPatched = true
1640
1604
  inputActions.submit = function () {
1641
- let attached = false
1642
- let hadRefs = false
1643
- try {
1644
- const hasRefs = pendingRefs().length > 0
1645
- hadRefs = hasRefs
1646
- const draft = store.inputDraft || ''
1647
- if (hasRefs && !selfSending && draft.indexOf('[code UI]') < 0) {
1648
- inputActions.setDraft(buildSendPayload(draft))
1649
- attached = true
1650
- }
1651
- } catch (e) {}
1652
- const result = orig.apply(inputActions, arguments)
1653
- // 引用随本条消息消耗:chip 展开(attached=false)与自动附带(attached=true)两条路径都要消耗
1654
- try { if (hadRefs && !selfSending) consumePendingRefs() } catch (e) {}
1655
- return result
1605
+ // 引用不进消息正文(指针经宿主 systemPrompt section 只给模型);这里只消耗。
1606
+ try { if (!selfSending) consumePendingRefs() } catch (e) {}
1607
+ return orig.apply(inputActions, arguments)
1656
1608
  }
1657
1609
  return () => {
1658
1610
  try { inputActions.submit = orig; delete inputActions.__cdexPatched } catch (e) {}
@@ -1690,10 +1642,10 @@ window.__ModuleLoader__.load({
1690
1642
  }
1691
1643
  const text = taEl.value
1692
1644
  if (!text.trim() && refs.length === 0) return
1693
- const payload = buildSendPayload(text)
1645
+ // 引用不进消息正文(指针经宿主 systemPrompt section 只给模型),原文直发
1694
1646
  selfSending = true
1695
1647
  try {
1696
- st.inputActions.setDraft(payload)
1648
+ st.inputActions.setDraft(text)
1697
1649
  st.inputActions.submit()
1698
1650
  } finally { selfSending = false }
1699
1651
  // 引用随本条消息消耗(chip 清空;记录留在【引用/备注】已使用区)
@@ -2523,10 +2475,13 @@ window.__ModuleLoader__.load({
2523
2475
  const r = parseChipRef(refJson)
2524
2476
  return r ? chipShortLabel(r) : String(refJson)
2525
2477
  },
2478
+ // 发送展开为「空」:chip 不进消息正文(用户要求对话区不展示引用指针)。
2479
+ // AI 侧靠宿主 systemPrompt 动态 section 获知本条消息的引用并调
2480
+ // get_referenced_snippets 读取——编号与 chip tag 一致。无效数据仍透出提示。
2526
2481
  serialize: async (refJson) => {
2527
2482
  const r = parseChipRef(refJson)
2528
2483
  if (!r) return '(code UI 引用数据无效: ' + refJson + ')'
2529
- return formatRefModelLine(r)
2484
+ return ''
2530
2485
  },
2531
2486
  },
2532
2487
  }), 'code-ui: @ 引用源原型'))
package/lib/index.js CHANGED
@@ -17,6 +17,37 @@ const references = [];
17
17
  const notes = [];
18
18
  let idSeq = 1;
19
19
 
20
+ // 会话「最近一条消息」的引用批次(sessionId → [{ seq, line }]):发送时由 use-references
21
+ // 写入(空列表 = 清除),systemPrompt 动态 section 在模型上下文组装时按会话读出——
22
+ // 引用指针只进模型上下文(AI 可见、对话区不展示),不再附加到消息正文。
23
+ const lastRefBatches = new Map();
24
+
25
+ // 引用条目 → 模型侧指针行(与客户端 formatRefModelLine 同格式,编号由批次位置给出)
26
+ function refModelLine(r) {
27
+ if (r.kind === "translate") {
28
+ return r.lineStart
29
+ ? "- 翻译引用 " + baseName(r.path) + ":" + r.lineStart + "-" + r.lineEnd + "(" + r.path + ")"
30
+ : "- 翻译引用 " + baseName(r.path) + "(" + r.path + ")";
31
+ }
32
+ return r.lineStart
33
+ ? "- 代码引用 " + baseName(r.path) + ":" + r.lineStart + "-" + r.lineEnd + "(" + r.path + ")"
34
+ : "- 文件引用 " + baseName(r.path) + "(整文件,绝对路径:" + r.path + ")";
35
+ }
36
+ // systemPrompt 动态 section 文本:按组装上下文里的 agent → session 取批次;无批次返回空串。
37
+ function refSectionText(ac) {
38
+ try {
39
+ const agent = ac && (ac.agent || ac.scope);
40
+ const header = agent && agent.session && agent.session.header;
41
+ const sid = header && header.id ? String(header.id) : "";
42
+ const batch = sid ? lastRefBatches.get(sid) : undefined;
43
+ if (!batch || batch.length === 0) return "";
44
+ return [
45
+ "用户刚发送的消息中通过 code UI 编辑器标记了以下引用(编号与用户在编辑器引用 tag 上看到的序号一致)。代码正文不进消息正文:请调用 get_referenced_snippets 工具按编号读取(kind: cite=代码引用 / translate=翻译请求 / all=全部;用户说「引用N」即指编号 N)。",
46
+ ...batch.map((b) => b.line),
47
+ ].join("\n");
48
+ } catch (e) { return ""; }
49
+ }
50
+
20
51
  function now() {
21
52
  return new Date().toISOString();
22
53
  }
@@ -1025,10 +1056,22 @@ function rpcHandler(ctx) {
1025
1056
  // 引用一次性消耗:把本条消息附带的引用标记为 used(不删除——AI 在消息发出后才经
1026
1057
  // get_referenced_snippets 读取,删除就读不到了;used 记录保留供用户在【引用/备注】查找)。
1027
1058
  const ids = Array.isArray(args.ids) ? args.ids : [];
1059
+ const sid = typeof args.sessionId === "string" ? args.sessionId : "";
1028
1060
  let n = 0;
1029
1061
  for (const r of references) {
1030
1062
  if (!r.used && ids.indexOf(r.id) >= 0) { r.used = true; r.usedAt = now(); n++; }
1031
1063
  }
1064
+ // 记录/清除该会话「最近一条消息」的引用批次:模型上下文组装时经 systemPrompt
1065
+ // 动态 section 透出(消息正文不再附加指针文本)。
1066
+ if (sid) {
1067
+ const batch = [];
1068
+ for (let i = 0; i < ids.length; i++) {
1069
+ const r = references.find((x) => x.id === ids[i]);
1070
+ if (r) batch.push({ seq: i + 1, line: refModelLine(r) });
1071
+ }
1072
+ if (batch.length > 0) lastRefBatches.set(sid, batch);
1073
+ else lastRefBatches.delete(sid);
1074
+ }
1032
1075
  return ok({ ok: true, used: n });
1033
1076
  }
1034
1077
 
@@ -1370,6 +1413,18 @@ export function apply(ctx) {
1370
1413
  registerSnippetsTool(c.tools);
1371
1414
  });
1372
1415
 
1416
+ // 模型上下文隐藏 section:引用指针只进 system prompt(AI 可见),不再附加到消息正文。
1417
+ // text 回调在每次模型上下文组装时按组装 agent → session 取「最近一条消息的引用批次」。
1418
+ // 顺序取 2950(工具说明之后、交付引用之前);无 systemPrompt 服务的宿主静默跳过。
1419
+ ctx.inject(["systemPrompt"], (c) => {
1420
+ try {
1421
+ const sp = c.systemPrompt;
1422
+ if (!sp || typeof sp.section !== "function") return;
1423
+ const dispose = sp.section({ name: "code-ui-references", order: 2950, text: refSectionText });
1424
+ ctx.effect(() => () => { Promise.resolve(dispose()).catch(() => {}); }, "dsh-code-ui: ref prompt section");
1425
+ } catch (e) {}
1426
+ });
1427
+
1373
1428
  // 注册 Typert Remote 服务到宿主 root Context(api-gateway 只扫描它所在 Context 的
1374
1429
  // reflect.props;root 与 gateway 同层,0.1.5-rc.2 / 0.2.0-rc.2 一致)。服务生命周期
1375
1430
  // 挂到本插件 fiber:卸载时自动从 root 摘除,gateway 经 internal/service 事件失效缓存。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-code-ui",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "Cursor 风格的 AI 代码编辑器工作台插件(DSH):文件树、多标签页编辑、语法高亮、引用/翻译行内标签给 AI、右侧停靠/悬浮窗口双布局",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",