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 +17 -62
- package/lib/index.js +55 -0
- package/package.json +1 -1
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
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
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
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
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
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
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
|
-
|
|
1645
|
+
// 引用不进消息正文(指针经宿主 systemPrompt section 只给模型),原文直发
|
|
1694
1646
|
selfSending = true
|
|
1695
1647
|
try {
|
|
1696
|
-
st.inputActions.setDraft(
|
|
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
|
|
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 事件失效缓存。
|