@zaofan/dsh-qqbot 1.11.7 → 1.11.9
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/client/qqbot-settings.js +125 -63
- package/dist/channel-tools.d.ts +18 -0
- package/dist/channel-tools.d.ts.map +1 -1
- package/dist/channel-tools.js +77 -0
- package/dist/channel-tools.js.map +1 -1
- package/dist/session/session-manager.d.ts +16 -0
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +88 -37
- package/dist/session/session-manager.js.map +1 -1
- package/package.json +145 -145
- package/settings-host.js +73 -24
package/client/qqbot-settings.js
CHANGED
|
@@ -587,14 +587,11 @@ window.__ModuleLoader__.load({
|
|
|
587
587
|
if (document.getElementById(EXT_UI_CSS_ID)) return
|
|
588
588
|
var s = document.createElement('style')
|
|
589
589
|
s.id = EXT_UI_CSS_ID
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
+ 'scroll-snap-type:x proximity;padding:2px 0 6px}'
|
|
594
|
-
+ '.qqs-ext-cards::-webkit-scrollbar{display:none}'
|
|
595
|
-
+ '.qqs-ext-card{flex:0 0 auto;scroll-snap-align:start;min-width:220px;max-width:320px}'
|
|
596
|
-
+ '.qqs-top-tabs{scrollbar-width:none}'
|
|
590
|
+
// ★ 2026-10-27:内嵌卡片那条已删(改顶栏页签),这里只留页签/整页卡片需要的样式
|
|
591
|
+
s.textContent =
|
|
592
|
+
'.qqs-top-tabs{scrollbar-width:none}'
|
|
597
593
|
+ '.qqs-top-tabs::-webkit-scrollbar{display:none}'
|
|
594
|
+
+ '.qqs-ext-card{flex:0 0 auto;scroll-snap-align:start;min-width:220px;max-width:320px}'
|
|
598
595
|
+ '.qqs-ext-page-card{display:block;min-width:260px}'
|
|
599
596
|
document.head.appendChild(s)
|
|
600
597
|
// 鼠标滚轮 → 横向滚动(2026-10-26 主人实测"不能横滑"):
|
|
@@ -1226,12 +1223,7 @@ window.__ModuleLoader__.load({
|
|
|
1226
1223
|
// ② 扩展脚本报错只白自己那张卡,不整页崩
|
|
1227
1224
|
var EXT_UI_LIST = '/api/qqbot-settings/ext-ui/list'
|
|
1228
1225
|
var EXT_UI_FILE = '/api/qqbot-settings/ext-ui/file?f='
|
|
1229
|
-
|
|
1230
|
-
return fetch(EXT_UI_FILE + encodeURIComponent(f) + '&id=' + encodeURIComponent(extId))
|
|
1231
|
-
.then(function (r) { return r.json() })
|
|
1232
|
-
.then(function (j) { return j && j.ok ? String(j.text || '') : '' })
|
|
1233
|
-
.catch(function () { return '' })
|
|
1234
|
-
}
|
|
1226
|
+
// ★ 2026-10-27 删:extUiText 只被 mountExtUiCards 用过(已删),成死代码。
|
|
1235
1227
|
function extUiApi(extId) {
|
|
1236
1228
|
return {
|
|
1237
1229
|
extId: extId,
|
|
@@ -1265,46 +1257,11 @@ window.__ModuleLoader__.load({
|
|
|
1265
1257
|
+ '.qqs-ext-page-card{display:block}'
|
|
1266
1258
|
document.head.appendChild(s)
|
|
1267
1259
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
if (!j || !j.ok) return
|
|
1274
|
-
var list = (j.items || []).filter(function (x) { return x.slot === 'settings' })
|
|
1275
|
-
if (!list.length) { box.style.display = 'none'; return }
|
|
1276
|
-
box.style.display = ''
|
|
1277
|
-
// 幂等:卡片数量没变就不重挂(避免每次 setState 都把扩展的 DOM 冲掉)
|
|
1278
|
-
var sig = list.map(function (x) { return x.extId }).join(',')
|
|
1279
|
-
if (box.getAttribute('data-sig') === sig) return
|
|
1280
|
-
box.setAttribute('data-sig', sig)
|
|
1281
|
-
box.innerHTML = ''
|
|
1282
|
-
list.forEach(function (it) {
|
|
1283
|
-
var host = document.createElement('div')
|
|
1284
|
-
host.className = 'qqs-ext-card'
|
|
1285
|
-
var sr = host.attachShadow({ mode: 'open' })
|
|
1286
|
-
box.appendChild(host)
|
|
1287
|
-
Promise.all([
|
|
1288
|
-
extUiText(it.extId, 'card.html'),
|
|
1289
|
-
it.files && it.files.indexOf('card.css') >= 0 ? extUiText(it.extId, 'card.css') : Promise.resolve(''),
|
|
1290
|
-
it.files && it.files.indexOf('card.js') >= 0 ? extUiText(it.extId, 'card.js') : Promise.resolve(''),
|
|
1291
|
-
]).then(function (res) {
|
|
1292
|
-
sr.innerHTML = '<style>' + (res[1] || '') + '</style>' + (res[0] || '')
|
|
1293
|
-
if (!res[2]) return
|
|
1294
|
-
try {
|
|
1295
|
-
// Function 而不是 eval:作用域干净,且把 qqbotExt 作为形参传进去
|
|
1296
|
-
new Function('qqbotExt', 'shadowRoot', res[2])(extUiApi(it.extId), sr)
|
|
1297
|
-
} catch (e) {
|
|
1298
|
-
var d = document.createElement('div')
|
|
1299
|
-
d.style.cssText = 'color:#c33;font-size:12px;padding:4px 0'
|
|
1300
|
-
d.textContent = '卡片脚本出错: ' + (e && e.message ? e.message : e)
|
|
1301
|
-
sr.appendChild(d)
|
|
1302
|
-
}
|
|
1303
|
-
})
|
|
1304
|
-
})
|
|
1305
|
-
}).catch(function () { /* 静默:扩展 UI 挂了不该影响设置页本体 */ })
|
|
1306
|
-
}
|
|
1307
|
-
setTimeout(function () { try { mountExtUiCards() } catch (e) { /* ignore */ } }, 0)
|
|
1260
|
+
// ★ 2026-10-27 删:这是第一版「把扩展卡片内嵌在设置页内容区」的实现。
|
|
1261
|
+
// 后来改成顶栏页签(ExtCardPage + Shadow DOM),两套并存会导致
|
|
1262
|
+
// 同一张卡在设置页出现两次;而且这里用的还是闭包内那个旧 extUiApi
|
|
1263
|
+
// (没有 getConfig/setConfig)→ 卡片报 'qqbotExt.getConfig is not a function'。
|
|
1264
|
+
// 统一走顶栏页签后删掉。
|
|
1308
1265
|
|
|
1309
1266
|
return h('div', { style: { maxWidth: 720 } },
|
|
1310
1267
|
h('h2', { style: { margin: '0 0 6px' } }, 'QQ 机器人设置'),
|
|
@@ -1317,8 +1274,8 @@ window.__ModuleLoader__.load({
|
|
|
1317
1274
|
h(SButton, { variant: 'ghost', onClick: load }, '放弃修改(重新读取)'),
|
|
1318
1275
|
msg ? h('span', { style: { fontSize: 13, color: T.text2 } }, msg) : null,
|
|
1319
1276
|
close ? h(SButton, { variant: 'ghost', onClick: close }, '完成') : null),
|
|
1320
|
-
// ★
|
|
1321
|
-
|
|
1277
|
+
// ★ 2026-10-27:这里原来挂 #qqs-ext-cards(内嵌卡片),已改为顶栏页签,容器不需要了
|
|
1278
|
+
null)
|
|
1322
1279
|
}
|
|
1323
1280
|
|
|
1324
1281
|
// ── ⑥ QQ 群管理(单开大卡片; P3+ 设计系统版: 横排 Tab + 状态条 + 表格 + 空状态 + 预留接口位) ──
|
|
@@ -2803,6 +2760,34 @@ var QQS_THEME_CSS = [
|
|
|
2803
2760
|
document.body.appendChild(wrap)
|
|
2804
2761
|
var ball = wrap.querySelector('#qqs-dock-ball')
|
|
2805
2762
|
var badge = wrap.querySelector('#qqs-dock-badge')
|
|
2763
|
+
// ── 2026-10-28(主人定):悬浮球**只在 QQ 会话里显示** ──
|
|
2764
|
+
// 判定沿用 session-lookup(state.detectedHit 的同一个接口),未命中就不显示。
|
|
2765
|
+
// 为什么还要轮询:会话切换(web 侧点开另一个会话)不会通知这里;
|
|
2766
|
+
// 只比 sessionId 字符串,变了才发请求,开销可忽略。
|
|
2767
|
+
var _dockSid = ''
|
|
2768
|
+
var _dockHit = null
|
|
2769
|
+
function refreshDockVisibility() {
|
|
2770
|
+
var sid = ''
|
|
2771
|
+
try { sid = curSessionId() } catch (eS) { sid = '' }
|
|
2772
|
+
// 同一会话、已知结论 → 直接套用(不发请求,也不闪)
|
|
2773
|
+
if (sid === _dockSid && _dockHit !== null) { wrap.style.display = _dockHit ? '' : 'none'; return }
|
|
2774
|
+
_dockSid = sid
|
|
2775
|
+
if (!sid) { _dockHit = false; wrap.style.display = 'none'; return }
|
|
2776
|
+
// 会话变了:先按"未命中"藏起来,拿到结论再显示(宁可晚一点,也别让它乱闪)
|
|
2777
|
+
_dockHit = null
|
|
2778
|
+
wrap.style.display = 'none'
|
|
2779
|
+
qqHitOf(sid, function (hit) {
|
|
2780
|
+
if (sid !== _dockSid) return // 查询期间又切了会话:这次结论作废
|
|
2781
|
+
_dockHit = hit
|
|
2782
|
+
wrap.style.display = hit ? '' : 'none'
|
|
2783
|
+
})
|
|
2784
|
+
}
|
|
2785
|
+
try {
|
|
2786
|
+
refreshDockVisibility()
|
|
2787
|
+
// 事件驱动(2026-10-28,主人嫌 2s 轮询费性能):不再 setInterval ——
|
|
2788
|
+
// apply() 里的 refreshQqUi 在「sessions.list 变化 / 窗口聚焦 / 页面重新可见」时调这里。
|
|
2789
|
+
window.__qqsRefreshDock = refreshDockVisibility
|
|
2790
|
+
} catch (eVis) { /* 显隐失败不影响球本身 */ }
|
|
2806
2791
|
var panel = null
|
|
2807
2792
|
var open = false
|
|
2808
2793
|
// ⚠️ 2026-09-27 修复「拖到屏幕外后球永久消失」:拖拽时本来有 clamp(行1749),
|
|
@@ -6761,6 +6746,31 @@ var QQS_THEME_CSS = [
|
|
|
6761
6746
|
return ''
|
|
6762
6747
|
}
|
|
6763
6748
|
|
|
6749
|
+
/**
|
|
6750
|
+
* 当前会话是不是 QQ 会话(2026-10-28)。
|
|
6751
|
+
* 同一次查询在窗口内共享 —— 悬浮球显隐、扩展页签显隐都走这里,不重复打后端。
|
|
6752
|
+
* ⚠️ 否定结论只缓存 2 秒:宿主刚启动时后端可能还没就绪,长缓存会让悬浮球一直不出现。
|
|
6753
|
+
*/
|
|
6754
|
+
function qqHitOf(sid, cb) {
|
|
6755
|
+
if (!sid) { try { cb(false) } catch (e0) {} ; return }
|
|
6756
|
+
var c = (window.__qqsHitCache = window.__qqsHitCache || {})
|
|
6757
|
+
var e = c[sid]
|
|
6758
|
+
if (e && Date.now() - e.at < (e.hit ? 10000 : 2000)) { try { cb(e.hit) } catch (e1) {} ; return }
|
|
6759
|
+
try {
|
|
6760
|
+
fetch('/api/qqbot-settings/session-lookup?sessionId=' + encodeURIComponent(sid))
|
|
6761
|
+
.then(function (r) { return r.json() })
|
|
6762
|
+
.then(function (d) {
|
|
6763
|
+
var hit = !!(d && d.hits && d.hits.length)
|
|
6764
|
+
c[sid] = { at: Date.now(), hit: hit }
|
|
6765
|
+
try { cb(hit) } catch (e2) {}
|
|
6766
|
+
})
|
|
6767
|
+
.catch(function () { try { cb(false) } catch (e3) {} })
|
|
6768
|
+
} catch (e4) { try { cb(false) } catch (e5) {} }
|
|
6769
|
+
}
|
|
6770
|
+
|
|
6771
|
+
// (2026-10-28)页签显隐不用 DOM 方案了 —— 见 apply() 里的 setExtTabVisible:
|
|
6772
|
+
// `slots.inject` 的返回值是 disposer,注销后宿主自己会刷新页签列表。
|
|
6773
|
+
|
|
6764
6774
|
function curWorkspaceCwd() {
|
|
6765
6775
|
try {
|
|
6766
6776
|
var svc = _extSessionsSvc
|
|
@@ -7805,14 +7815,28 @@ var QQS_THEME_CSS = [
|
|
|
7805
7815
|
function (props) { return h(QqbotHome, { close: props && props.close }) })
|
|
7806
7816
|
})
|
|
7807
7817
|
})
|
|
7808
|
-
// 「QQ
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7818
|
+
// 「QQ机器人扩展」会话页签:**只在 QQ 会话里注入**(2026-10-28 主人定)
|
|
7819
|
+
// 为什么不用 DOM 隐藏:`slots.inject(key, cb)` 的返回值是 disposer(已核 ui-renderer 源码:
|
|
7820
|
+
// `return () => { disposeController(); }`),注销后宿主 `slots.subscribe("conversation.view")`
|
|
7821
|
+
// 会自动刷新页签列表 —— 比按文案找按钮稳得多。
|
|
7822
|
+
var _extTabDisposed = null
|
|
7823
|
+
function setExtTabVisible(show) {
|
|
7824
|
+
if (show) {
|
|
7825
|
+
if (_extTabDisposed) return
|
|
7826
|
+
try {
|
|
7827
|
+
_extTabDisposed = slots.inject('conversation.view', function () {
|
|
7828
|
+
return slots.register(
|
|
7829
|
+
{ name: 'conversation.view', id: 'qqbot-ext', order: 81, label: 'QQ机器人扩展' },
|
|
7830
|
+
ExtPanel)
|
|
7831
|
+
}) || null
|
|
7832
|
+
if (!_extTabDisposed) _extTabDisposed = function () {}
|
|
7833
|
+
} catch (eTab) { _extTabDisposed = null }
|
|
7834
|
+
} else {
|
|
7835
|
+
if (!_extTabDisposed) return
|
|
7836
|
+
try { _extTabDisposed() } catch (eTab2) {}
|
|
7837
|
+
_extTabDisposed = null
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7816
7840
|
safe('approval float', startApprovalFloat)
|
|
7817
7841
|
// sessions 服务(宿主根服务): 权威读 web 当前会话(current)与 cwd(agent id === session id)
|
|
7818
7842
|
var sessionsSvc = null
|
|
@@ -7820,6 +7844,44 @@ var QQS_THEME_CSS = [
|
|
|
7820
7844
|
if (!sessionsSvc) { try { sessionsSvc = ctx.get && ctx.get('sessions') } catch (e) {} }
|
|
7821
7845
|
_extSessionsSvc = sessionsSvc // 供 ExtPanel 读当前工作区 cwd
|
|
7822
7846
|
safe('dock', function () { startQqDock(sessionsSvc) })
|
|
7847
|
+
// ── 统一刷新:页签 + 悬浮球,**事件驱动,不轮询**(2026-10-28)──
|
|
7848
|
+
// 信号源:`sessions.list`(ObservableSnapshot,宿主在左侧会话列表切换时就会推)
|
|
7849
|
+
// + window focus / 页面可见性(兜底,切回标签页时补一次)。
|
|
7850
|
+
var _qqUiRetry = 0
|
|
7851
|
+
var refreshQqUi = function () {
|
|
7852
|
+
// dock 球的显隐也走这同一个信号(否则切会话时页签跟走了、球还赖着不动)
|
|
7853
|
+
try { if (typeof window !== 'undefined' && typeof window.__qqsRefreshDock === 'function') window.__qqsRefreshDock() } catch (eDock) {}
|
|
7854
|
+
var sid = ''
|
|
7855
|
+
try { sid = curSessionId() } catch (eSid) { sid = '' }
|
|
7856
|
+
// 有限次重试(≈7 秒内 6 次):宿主刚启动时"当前会话"或后端 lookup 可能还没就绪,
|
|
7857
|
+
// 一次性判空就再也不显示球了(2026-10-28 主人实测)。不是长期轮询。
|
|
7858
|
+
function retryLater() {
|
|
7859
|
+
if (_qqUiRetry >= 6) return
|
|
7860
|
+
_qqUiRetry++
|
|
7861
|
+
setTimeout(function () { refreshQqUi() }, 1200)
|
|
7862
|
+
}
|
|
7863
|
+
if (!sid) { setExtTabVisible(false); retryLater(); return }
|
|
7864
|
+
qqHitOf(sid, function (hit) {
|
|
7865
|
+
var now = ''
|
|
7866
|
+
try { now = curSessionId() } catch (eNow) {}
|
|
7867
|
+
if (now !== sid) return // 查询期间切走了:别把旧结论套到新会话上
|
|
7868
|
+
setExtTabVisible(!!hit)
|
|
7869
|
+
if (hit) { _qqUiRetry = 0; return }
|
|
7870
|
+
retryLater()
|
|
7871
|
+
})
|
|
7872
|
+
}
|
|
7873
|
+
safe('qq ui refresh', function () {
|
|
7874
|
+
refreshQqUi()
|
|
7875
|
+
try {
|
|
7876
|
+
if (sessionsSvc && sessionsSvc.list && typeof sessionsSvc.list.subscribe === 'function') {
|
|
7877
|
+
sessionsSvc.list.subscribe(function () { refreshQqUi() })
|
|
7878
|
+
}
|
|
7879
|
+
} catch (eSub) {}
|
|
7880
|
+
try {
|
|
7881
|
+
window.addEventListener('focus', refreshQqUi)
|
|
7882
|
+
document.addEventListener('visibilitychange', function () { if (!document.hidden) refreshQqUi() })
|
|
7883
|
+
} catch (eWin) {}
|
|
7884
|
+
})
|
|
7823
7885
|
}
|
|
7824
7886
|
|
|
7825
7887
|
exports.inject = ['slots', 'sessions']
|
package/dist/channel-tools.d.ts
CHANGED
|
@@ -110,6 +110,24 @@ export declare function syncContextTools(agentCtx: unknown, shouldHave: boolean,
|
|
|
110
110
|
*/
|
|
111
111
|
export declare function ensureExtToolsGlobal(globalCtx: unknown): void;
|
|
112
112
|
export declare function ensureContextToolsGlobal(globalCtx: unknown): void;
|
|
113
|
+
/**
|
|
114
|
+
* 按会话同步「上下文压缩三件套 + QQ 群管理」的**注册状态**(幂等)。
|
|
115
|
+
*
|
|
116
|
+
* @param agentCtx 该 QQ 会话的 agent 作用域(`record.agentCtx` 或 `agent.ctx`)
|
|
117
|
+
* @param opts.ctxTools 要不要 context_memo / context_compact / context_drop(跟「无上下文·智能判断」开关)
|
|
118
|
+
* @param opts.groupAdmin 要不要 qq_group_admin(跟 groupAdmin.enabled)
|
|
119
|
+
* @param opts.dataRoot 会话坐标(工具执行时反查数据根用)
|
|
120
|
+
* @param opts.sessionKey 会话坐标(备忘类工具要求非空)
|
|
121
|
+
*
|
|
122
|
+
* 失败策略:拿不到 `tools.register` 或注册抛错时**不记状态**(写 `__retry`),下一次 pre-step 重试;
|
|
123
|
+
* 绝不影响会话主流程。
|
|
124
|
+
*/
|
|
125
|
+
export declare function syncChannelScopedTools(agentCtx: unknown, opts: {
|
|
126
|
+
ctxTools: boolean;
|
|
127
|
+
groupAdmin: boolean;
|
|
128
|
+
dataRoot?: string;
|
|
129
|
+
sessionKey?: string;
|
|
130
|
+
}): void;
|
|
113
131
|
export declare function applyExtDisabledRestrict(agentCtx: unknown, dataRoot: string, extToolNames: string[]): void;
|
|
114
132
|
export declare function restrictContextTools(agentCtx: unknown, shouldHave: boolean, env?: {
|
|
115
133
|
dataRoot?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"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"}
|
package/dist/channel-tools.js
CHANGED
|
@@ -4047,6 +4047,83 @@ function toolsOfScope(agentCtx) {
|
|
|
4047
4047
|
*
|
|
4048
4048
|
* 幂等:restrict 返回 dispose;每次 apply 先撤销上一次的,再按当前 disabled 重挂。
|
|
4049
4049
|
*/
|
|
4050
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
4051
|
+
// 2026-10-28(主人定,对应 issue #26):上下文/群管理工具改为**按 QQ 会话注册**
|
|
4052
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
4053
|
+
// 旧做法(2026-10-09 起):「插件根作用域全局注册一次 + pre-step 逐会话 restrict deny」。
|
|
4054
|
+
// · 当初改成全局,是为了避开"每步先注销再重注册 → 工具表反复变动 → 宿主每步发
|
|
4055
|
+
// 「工具已更新·移除/新增」"的抖动(那批通知还进上下文,纯烧 token)。
|
|
4056
|
+
// · 代价是:① 第一次 pre-step 之前,无关工作区的会话也能看到这四个名字;
|
|
4057
|
+
// ② `restrict` 只过滤**继承来的**工具,全局层本身就是泄漏源,剪枝只是事后补救。
|
|
4058
|
+
//
|
|
4059
|
+
// 现在照 `mountChannelTools` 的路子(**per-agent register**),配一个幂等状态机:
|
|
4060
|
+
// · 状态没变 → 一根手指都不动(不产生任何变更通知,同样不抖);
|
|
4061
|
+
// · 状态变了 → 先撤旧的、再按新状态注册(面板一改开关,下一 step 生效)。
|
|
4062
|
+
// 非 QQ 会话根本不经过这里 ⇒ 天生看不到,零窗口期、零 token。
|
|
4063
|
+
const ctxScopeState = new WeakMap();
|
|
4064
|
+
/**
|
|
4065
|
+
* 按会话同步「上下文压缩三件套 + QQ 群管理」的**注册状态**(幂等)。
|
|
4066
|
+
*
|
|
4067
|
+
* @param agentCtx 该 QQ 会话的 agent 作用域(`record.agentCtx` 或 `agent.ctx`)
|
|
4068
|
+
* @param opts.ctxTools 要不要 context_memo / context_compact / context_drop(跟「无上下文·智能判断」开关)
|
|
4069
|
+
* @param opts.groupAdmin 要不要 qq_group_admin(跟 groupAdmin.enabled)
|
|
4070
|
+
* @param opts.dataRoot 会话坐标(工具执行时反查数据根用)
|
|
4071
|
+
* @param opts.sessionKey 会话坐标(备忘类工具要求非空)
|
|
4072
|
+
*
|
|
4073
|
+
* 失败策略:拿不到 `tools.register` 或注册抛错时**不记状态**(写 `__retry`),下一次 pre-step 重试;
|
|
4074
|
+
* 绝不影响会话主流程。
|
|
4075
|
+
*/
|
|
4076
|
+
export function syncChannelScopedTools(agentCtx, opts) {
|
|
4077
|
+
try {
|
|
4078
|
+
if (!agentCtx || typeof agentCtx !== 'object')
|
|
4079
|
+
return;
|
|
4080
|
+
const key = agentCtx;
|
|
4081
|
+
// 会话坐标每次都刷新(工具执行时靠它定位数据根/会话)
|
|
4082
|
+
if (opts.dataRoot) {
|
|
4083
|
+
ctxEnvMap.set(key, { dataRoot: String(opts.dataRoot), sessionKey: String(opts.sessionKey ?? '') });
|
|
4084
|
+
}
|
|
4085
|
+
const sig = `${opts.ctxTools ? '1' : '0'}${opts.groupAdmin ? '1' : '0'}`;
|
|
4086
|
+
const st = ctxScopeState.get(key);
|
|
4087
|
+
if (st && st.sig === sig)
|
|
4088
|
+
return; // 状态一致 → 不动(这是"不抖"的关键)
|
|
4089
|
+
const toolsObj = toolsOfScope(agentCtx);
|
|
4090
|
+
const reg = toolsObj?.register;
|
|
4091
|
+
if (!toolsObj || typeof reg !== 'function') {
|
|
4092
|
+
traceToolReg('[scoped-tool] 拿不到 tools.register,跳过(下次再试)');
|
|
4093
|
+
return;
|
|
4094
|
+
}
|
|
4095
|
+
// 先撤旧的(换开关后不能叠加)
|
|
4096
|
+
if (st)
|
|
4097
|
+
for (const d of st.disposers) {
|
|
4098
|
+
try {
|
|
4099
|
+
d();
|
|
4100
|
+
}
|
|
4101
|
+
catch { /* ignore */ }
|
|
4102
|
+
}
|
|
4103
|
+
const want = [
|
|
4104
|
+
...(opts.ctxTools ? ctxToolsRef() : []),
|
|
4105
|
+
...(opts.groupAdmin ? qunAdminRef() : []),
|
|
4106
|
+
];
|
|
4107
|
+
const disposers = [];
|
|
4108
|
+
for (const t of want) {
|
|
4109
|
+
try {
|
|
4110
|
+
const d = reg.call(toolsObj, t);
|
|
4111
|
+
if (typeof d === 'function')
|
|
4112
|
+
disposers.push(d);
|
|
4113
|
+
}
|
|
4114
|
+
catch { /* 单个工具失败不影响其它 */ }
|
|
4115
|
+
}
|
|
4116
|
+
// 全成功才认这个 sig;否则标 __retry,下一 step 重来
|
|
4117
|
+
const okAll = disposers.length === want.length;
|
|
4118
|
+
ctxScopeState.set(key, { sig: okAll ? sig : `__retry-${sig}`, disposers });
|
|
4119
|
+
traceToolReg(`[scoped-tool] 按会话注册 ctx=${opts.ctxTools ? 1 : 0} groupAdmin=${opts.groupAdmin ? 1 : 0}`
|
|
4120
|
+
+ ` want=${want.length} ok=${disposers.length}`
|
|
4121
|
+
+ ` dataRoot=${String(opts.dataRoot ?? '')} sk=${String(opts.sessionKey ?? '')}`);
|
|
4122
|
+
}
|
|
4123
|
+
catch (e) {
|
|
4124
|
+
traceToolReg('[scoped-tool] 异常: ' + String(e?.message ?? e).slice(0, 160));
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4050
4127
|
let __extRestrictDispose;
|
|
4051
4128
|
export function applyExtDisabledRestrict(agentCtx, dataRoot, extToolNames) {
|
|
4052
4129
|
try {
|