@zaofan/dsh-qqbot 1.7.8 → 1.7.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.
@@ -89,4 +89,24 @@ export declare function syncContextTools(agentCtx: unknown, shouldHave: boolean,
89
89
  dataRoot?: string;
90
90
  sessionKey?: string;
91
91
  }, reminderText?: string): void;
92
+ /**
93
+ * 把"三个上下文工具 + QQ 群管理工具"**全局注册一次、永不注销**。
94
+ *
95
+ * 幂等靠 globalThis 记句柄:跨热刷(模块重新 import)依然有效,避免热刷后重复注册被宿主判重名。
96
+ * ⚠️ 可见性不在这里决定 —— 交给 {@link restrictContextTools} 按会话剪枝。
97
+ */
98
+ export declare function ensureContextToolsGlobal(globalCtx: unknown): void;
99
+ /**
100
+ * 按会话**实时剪枝**三个上下文工具的可见性(2026-10-09,主人要的"实时变更")。
101
+ *
102
+ * 语义:**开了「无上下文模式 + 智能判断」**的会话 → 看得见那三个工具;
103
+ * 否则该会话**连工具名都看不到**(模型清单里没有 = 零 token),而**注册表纹丝不动**。
104
+ * 面板一改开关 → 下一次 pre-step(下一回合/下一 step)即生效;撤销限制是立即的。
105
+ *
106
+ * 幂等:同一作用域状态没变化就直接 return,不重复设/撤 restriction(避免无谓的变更通知与抖动)。
107
+ */
108
+ export declare function restrictContextTools(agentCtx: unknown, shouldHave: boolean, env?: {
109
+ dataRoot?: string;
110
+ sessionKey?: string;
111
+ }): void;
92
112
  //# sourceMappingURL=channel-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AA4FlE,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,eAAO,MAAM,IAAI,wBAAwB,CAAC;AAI1C,eAAO,MAAM,MAAM,UAAY,CAAC;AAWhC,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAiC;AAkD5F,wCAAwC;AACxC,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAErH;AACD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAE9E;AACD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAI/D;AAkFD,mDAAmD;AACnD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA67DvD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,GAAG,KAAK,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAkBvC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,GAAG,KAAK,EACtB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BvC;AAOD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;IAE1C;AAED,yCAAyC;AACzC,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAExF;AAYD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExD;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAKxI;AA4RD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAyI;AACzL,wBAAgB,eAAe,IAAI,MAAM,CAA2F;AA8CpI;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CA+CtJ"}
1
+ {"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"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"}
@@ -2214,10 +2214,15 @@ export async function apply(ctx) {
2214
2214
  // ⛔ 别再改回 true:那等于给所有会话开后门。
2215
2215
  // 代价:插件作用域不再"提前"备一份,开启的会话若在极早期回合看不到工具,等下一个 step
2216
2216
  // (pre-step 每个 step 都跑)即出现 —— 比"人人都有"划算得多。
2217
- try {
2218
- syncContextTools(ctx, false);
2219
- }
2220
- catch { /* ignore */ }
2217
+ // ⚠️ 2026-10-09 **第二次修正**(实测抓到;主人定的"注册不动 + 实时剪枝"要落地必须这样):
2218
+ // ⛔ **不要在这里注册那三个上下文工具** —— 本函数是"每会话"被调用的
2219
+ // (session-manager 传进来的是 **agentCtx**),在这儿 register 等于注册进**该 agent 作用域**;
2220
+ // 而 `restrict({deny})` 按宿主文档**只过滤"继承来的"(全局层 + 祖先层)工具,
2221
+ // 管不到本作用域自己注册的** ⇒ 剪枝永远打空
2222
+ // (trace 原话:`restrict 失败: names unknown global tools "context_memo","context_compact","context_drop"`)。
2223
+ // ✅ 全局注册统一交给 session-manager 的 pre-step 用**插件根 ctx** 做:
2224
+ // `ensureContextToolsGlobal(this.ctx)` —— 它内部用 `ctx.get('tools')` 取服务,绕开 inject 限制。
2225
+ // 这里只保留下面 ②段"QQ 群管理工具"的按会话注册(那本来就是会话级工具)。
2221
2226
  const toolDefs = [
2222
2227
  { name: 'send_media', tool: sendMediaTool },
2223
2228
  { name: 'recall_message', tool: recallTool },
@@ -2960,4 +2965,123 @@ export function syncContextTools(agentCtx, shouldHave, env, reminderText) {
2960
2965
  traceToolReg('[ctxtool-reg] 异常: ' + String(e?.message ?? e).slice(0, 160));
2961
2966
  }
2962
2967
  }
2968
+ // ══════════════════════════════════════════════════════════════════════════
2969
+ // 2026-10-09 架构微调(主人定):**注册不动 + 按会话实时剪枝可见性**
2970
+ // ══════════════════════════════════════════════════════════════════════════
2971
+ // 旧做法是 pre-step 每步都 `syncContextTools()`:内部"先注销、再重新注册",
2972
+ // 于是**每一步都动一次工具表** → 宿主每步都发"工具已更新 · 移除/新增"(还进上下文,纯烧 token),
2973
+ // 多会话并存时更是"你挂我卸"来回抖。
2974
+ //
2975
+ // 主人的判断是对的:不该用"注册/注销"表达"这个会话要不要有",而该
2976
+ // **注册只做一次,可见性按会话实时剪枝** —— 宿主正好提供了这个接口:
2977
+ // `ctx.tools.restrict({ allow | deny })`,**per-scope**(只作用于调用方那一个 agent 作用域),
2978
+ // 且"只过滤**继承**来的工具(全局层+祖先层),不动该作用域自己注册的"。
2979
+ //
2980
+ // 于是现在的分工:
2981
+ // · {@link ensureContextToolsGlobal} —— 全局注册一次(永不注销),幂等;
2982
+ // · {@link restrictContextTools} —— 每个会话每次 pre-step 声明"我要不要看得到那三个",
2983
+ // 状态没变就一根手指都不动(不产生任何变更通知)。
2984
+ // =========================================================================
2985
+ /** 三个上下文工具的名字(restrict 用;必须与 ctxToolsRef() 里的 name 完全一致) */
2986
+ const CTX_TOOL_NAMES = ['context_memo', 'context_compact', 'context_drop'];
2987
+ /**
2988
+ * 把"三个上下文工具 + QQ 群管理工具"**全局注册一次、永不注销**。
2989
+ *
2990
+ * 幂等靠 globalThis 记句柄:跨热刷(模块重新 import)依然有效,避免热刷后重复注册被宿主判重名。
2991
+ * ⚠️ 可见性不在这里决定 —— 交给 {@link restrictContextTools} 按会话剪枝。
2992
+ */
2993
+ export function ensureContextToolsGlobal(globalCtx) {
2994
+ try {
2995
+ if (!globalCtx || typeof globalCtx !== 'object')
2996
+ return;
2997
+ // ⚠️ 2026-10-09 修(实测抓到):apply 的注入列表是 `["settings","webServer"]`,**没有 `tools`** ⇒
2998
+ // 直接读 `ctx.tools` 会被宿主拒(`cannot get property "tools" without inject`),异常被 catch 吞掉,
2999
+ // 表现就是"全局注册压根没发生"(trace 里只有别的报错、没有"全局注册 N 件")。
3000
+ // 正解:**用 cordis 的运行时取服务 `ctx.get('tools')`**(不需要 inject 声明)——
3001
+ // 本插件别处也是这个姿势(见 ext-capabilities 给扩展工具的 kernel 文档:`ctx.get('tools')`)。
3002
+ const ctxObj = globalCtx;
3003
+ let toolsObj;
3004
+ try {
3005
+ toolsObj = ctxObj.tools;
3006
+ }
3007
+ catch { /* 未声明 inject 时会抛 —— 忽略,下面改走 get */ }
3008
+ if (!toolsObj || typeof toolsObj.register !== 'function') {
3009
+ try {
3010
+ toolsObj = ctxObj.get?.('tools');
3011
+ }
3012
+ catch { /* ignore */ }
3013
+ }
3014
+ const reg = toolsObj?.register;
3015
+ if (!toolsObj || typeof reg !== 'function') {
3016
+ traceToolReg('[ctxtool-reg] 全局注册跳过: 拿不到 tools 服务(ctx.tools 被拒 且 ctx.get("tools") 为空)');
3017
+ return;
3018
+ }
3019
+ const g = globalThis;
3020
+ if (Array.isArray(g.__qqbotCtxToolsGlobal) && g.__qqbotCtxToolsGlobal.length > 0)
3021
+ return;
3022
+ const ds = [];
3023
+ for (const t of [...ctxToolsRef(), ...qunAdminRef()]) {
3024
+ try {
3025
+ const d = reg.call(toolsObj, t);
3026
+ if (typeof d === 'function')
3027
+ ds.push(d);
3028
+ }
3029
+ catch { /* 单个失败不影响其它 */ }
3030
+ }
3031
+ g.__qqbotCtxToolsGlobal = ds;
3032
+ traceToolReg(`[ctxtool-reg] 全局注册 ${ds.length} 件(永不注销;可见性改由 per-scope restrict 控制)`);
3033
+ }
3034
+ catch (e) {
3035
+ traceToolReg('[ctxtool-reg] 全局注册异常: ' + String(e?.message ?? e).slice(0, 160));
3036
+ }
3037
+ }
3038
+ /** 每个 agent 作用域当前的可见性状态(幂等用:状态没变就不动) */
3039
+ const ctxRestrictState = new WeakMap();
3040
+ /**
3041
+ * 按会话**实时剪枝**三个上下文工具的可见性(2026-10-09,主人要的"实时变更")。
3042
+ *
3043
+ * 语义:**开了「无上下文模式 + 智能判断」**的会话 → 看得见那三个工具;
3044
+ * 否则该会话**连工具名都看不到**(模型清单里没有 = 零 token),而**注册表纹丝不动**。
3045
+ * 面板一改开关 → 下一次 pre-step(下一回合/下一 step)即生效;撤销限制是立即的。
3046
+ *
3047
+ * 幂等:同一作用域状态没变化就直接 return,不重复设/撤 restriction(避免无谓的变更通知与抖动)。
3048
+ */
3049
+ export function restrictContextTools(agentCtx, shouldHave, env) {
3050
+ try {
3051
+ if (!agentCtx || typeof agentCtx !== 'object')
3052
+ return;
3053
+ const key = agentCtx;
3054
+ // 会话坐标每次都刷新(工具执行时靠它定位数据根/会话)
3055
+ if (env && env.dataRoot) {
3056
+ ctxEnvMap.set(key, { dataRoot: String(env.dataRoot), sessionKey: String(env.sessionKey ?? '') });
3057
+ }
3058
+ const st = ctxRestrictState.get(key);
3059
+ if (st && st.shouldHave === shouldHave)
3060
+ return; // ← 状态一致:不动(这是"不抖"的关键)
3061
+ if (st?.dispose) {
3062
+ try {
3063
+ st.dispose();
3064
+ }
3065
+ catch { /* ignore */ }
3066
+ }
3067
+ const toolsObj = agentCtx.tools;
3068
+ const restrict = toolsObj?.restrict;
3069
+ let dispose;
3070
+ if (!shouldHave && typeof restrict === 'function') {
3071
+ try {
3072
+ const d = restrict.call(toolsObj, { deny: [...CTX_TOOL_NAMES] });
3073
+ if (typeof d === 'function')
3074
+ dispose = d;
3075
+ }
3076
+ catch (e) {
3077
+ traceToolReg('[ctxtool-reg] restrict 失败: ' + String(e?.message ?? e).slice(0, 160));
3078
+ }
3079
+ }
3080
+ ctxRestrictState.set(key, dispose ? { shouldHave, dispose } : { shouldHave });
3081
+ traceToolReg(`[ctxtool-reg] ${shouldHave ? '放开(本会话继承全局三个工具)' : '剪枝(本会话看不到三个工具)'} dataRoot=${String(env?.dataRoot ?? '')} sk=${String(env?.sessionKey ?? '')}`);
3082
+ }
3083
+ catch (e) {
3084
+ traceToolReg('[ctxtool-reg] 剪枝异常: ' + String(e?.message ?? e).slice(0, 160));
3085
+ }
3086
+ }
2963
3087
  //# sourceMappingURL=channel-tools.js.map