@zhin.js/agent 0.1.26 → 0.1.28
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/README.md +41 -17
- package/lib/ai-event-subscriber.d.ts.map +1 -1
- package/lib/ai-event-subscriber.js +10 -6
- package/lib/ai-event-subscriber.js.map +1 -1
- package/lib/init/register-typing-indicator.d.ts +4 -3
- package/lib/init/register-typing-indicator.d.ts.map +1 -1
- package/lib/init/register-typing-indicator.js +142 -58
- package/lib/init/register-typing-indicator.js.map +1 -1
- package/lib/security/dangerous-tool-policy.d.ts.map +1 -1
- package/lib/security/dangerous-tool-policy.js +56 -9
- package/lib/security/dangerous-tool-policy.js.map +1 -1
- package/lib/typing-indicator/adapter-integration.d.ts +23 -5
- package/lib/typing-indicator/adapter-integration.d.ts.map +1 -1
- package/lib/typing-indicator/adapter-integration.js +49 -57
- package/lib/typing-indicator/adapter-integration.js.map +1 -1
- package/lib/typing-indicator/index.d.ts +3 -3
- package/lib/typing-indicator/index.d.ts.map +1 -1
- package/lib/typing-indicator/index.js +11 -6
- package/lib/typing-indicator/index.js.map +1 -1
- package/lib/typing-indicator/integration.d.ts +4 -3
- package/lib/typing-indicator/integration.d.ts.map +1 -1
- package/lib/typing-indicator/integration.js +18 -17
- package/lib/typing-indicator/integration.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Zhin AI Agent 组合层:在 `@zhin.js/core` 的类型与 Provider 之上,提供会话管理、Agent 执行循环、ZhinAgent 与框架挂载(init)。
|
|
4
4
|
|
|
5
|
+
领域词汇见 [CONTEXT.md](./CONTEXT.md)。用户向文档:[AI 模块](https://zhin.js.org/advanced/ai)、[消息如何流转](../../docs/essentials/message-flow.md)。
|
|
6
|
+
|
|
5
7
|
## 功能特性
|
|
6
8
|
|
|
7
9
|
- 🤖 **Agent 循环**:`Agent` / `createAgent`,支持工具调用、迭代与事件
|
|
8
10
|
- 📝 **会话管理**:`SessionManager`、内存/数据库会话、`SessionManager.generateId`
|
|
9
11
|
- 🧠 **ZhinAgent**:与 Zhin 消息流集成的智能体(SOUL/TOOLS/AGENTS、工具收集、执行策略)
|
|
10
|
-
- 🔍
|
|
12
|
+
- 🔍 **模型自动发现**:通过 `@zhin.js/ai` 的 `ModelRegistry`(本包 re-export AI 原语,不单独导出 Registry)
|
|
11
13
|
- 🔄 **模型自动降级**:首选模型失败时自动切换到次优模型,支持 Chat / Vision / Agent 三条路径
|
|
12
14
|
- 🛡️ **6 层 Bash 安全**:`ExecPolicy` 纵深防御(危险黑名单、环境变量剥离、wrapper 剥离、复合命令拆分、只读放行、交互式审批)
|
|
13
15
|
- 📂 **文件访问安全**:`FilePolicy` 路径检查、设备路径拦截、命令读写分类
|
|
@@ -38,9 +40,9 @@ npm install zhin.js
|
|
|
38
40
|
|
|
39
41
|
主包 `zhin.js` 已依赖 `@zhin.js/agent` 并在 setup 中调用 `initAgentModule()`,插件可直接从 `zhin.js` 使用:
|
|
40
42
|
|
|
41
|
-
```
|
|
43
|
+
```typescript
|
|
42
44
|
import {
|
|
43
|
-
|
|
45
|
+
initAgentModule,
|
|
44
46
|
ZhinAgent,
|
|
45
47
|
Agent,
|
|
46
48
|
createAgent,
|
|
@@ -80,7 +82,7 @@ const result = await agent.run('你好')
|
|
|
80
82
|
|------|------|
|
|
81
83
|
| 初始化 | `initAgentModule` |
|
|
82
84
|
| Agent | `Agent`, `createAgent`, `formatToolTitle` |
|
|
83
|
-
|
|
|
85
|
+
| Model harness | `MODEL_HARNESS_DEFAULTS`, `resolveModelHarness`, `mergeModelHarnessValues` |
|
|
84
86
|
| 服务与会话 | `AIService`, `SessionManager`, `MemorySessionManager`, `DatabaseSessionManager`, `createMemorySessionManager`, `createDatabaseSessionManager` |
|
|
85
87
|
| ZhinAgent | `ZhinAgent`,以及 config / exec-policy / file-policy / tool-runtime / prompt / builtin-tools 等子模块 |
|
|
86
88
|
| 安全策略 | `checkExecPolicy`, `applyExecPolicyToTools`, `isDangerousCommand`, `stripEnvVarPrefix`, `stripSafeWrappers`, `splitCompoundCommand`, `extractCommandName`, `ExecPolicyResult`, `checkFileAccess`, `classifyBashCommand`, `isBlockedDevicePath` |
|
|
@@ -137,7 +139,7 @@ ai:
|
|
|
137
139
|
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp/zhin-mcp-test"]
|
|
138
140
|
```
|
|
139
141
|
|
|
140
|
-
**限制**:单 server 连接失败仅 warn,不阻塞回合;resources/prompts 暂不注入模型。与 **`
|
|
142
|
+
**限制**:单 server 连接失败仅 warn,不阻塞回合;resources/prompts 暂不注入模型。与 **`packages/host/mcp`**(MCP **Server**)方向相反。验收见 [examples/test-bot/ACCEPTANCE.md](../../examples/test-bot/ACCEPTANCE.md)。
|
|
141
143
|
|
|
142
144
|
## 多 Agent 使用方式
|
|
143
145
|
|
|
@@ -151,18 +153,6 @@ ai:
|
|
|
151
153
|
|
|
152
154
|
无需额外配置即可使用;若需放宽子 Agent 的工具范围,使用 `ai.agent.subagentTools` 显式追加白名单(不会自动继承主会话全部 skill/tool)。
|
|
153
155
|
|
|
154
|
-
## 工具命名策略
|
|
155
|
-
|
|
156
|
-
- 保留/内置工具名(如 `bash`、`read_file`、`spawn_task`)不可被插件或文件化工具覆盖。
|
|
157
|
-
- 非保留工具同名时采用 **后注册覆盖前注册**。
|
|
158
|
-
- 冲突统一以 warn 记录:包含 `name`、`source`、`action`(`ignored`/`overridden`)。
|
|
159
|
-
|
|
160
|
-
## Model harness(第一期)
|
|
161
|
-
|
|
162
|
-
- 默认参数通过 TypeScript 表维护:`src/zhin-agent/model-harness.ts`。
|
|
163
|
-
- 为新 provider/model 增补默认值时,直接在该表新增行并附测试。
|
|
164
|
-
- 第一阶段不引入新的 YAML 配置键;后续若支持 YAML 覆盖,TS 表仍作为默认层。
|
|
165
|
-
|
|
166
156
|
### 2. 用 AIService 创建多个不同配置的 Agent
|
|
167
157
|
|
|
168
158
|
`ctx.ai`(AIService)可以按需创建**多个互不共享状态的 Agent**,每个可指定不同 provider、model、systemPrompt、tools:
|
|
@@ -213,6 +203,40 @@ useContext('ai', async (ai) => {
|
|
|
213
203
|
|
|
214
204
|
本包只提供基础能力:`createAgent`、`ZhinAgent`、`ai.createAgent` 等。**多 Agent 串联/并联编排**(例如 A 的输出作为 B 的输入、按条件路由到不同专业 Agent)以及**按 bot / 按群组配置多个 ZhinAgent** 的调度与路由,将在 **zhin.js 主包**实现;插件侧通过 zhin.js 暴露的 API 使用即可,无需在业务里手写多实例维护与路由逻辑。
|
|
215
205
|
|
|
206
|
+
## 工具命名策略
|
|
207
|
+
|
|
208
|
+
- 保留/内置工具名(如 `bash`、`read_file`、`spawn_task`)不可被插件或文件化工具覆盖。
|
|
209
|
+
- 非保留工具同名时采用 **后注册覆盖前注册**。
|
|
210
|
+
- 冲突统一以 warn 记录:包含 `name`、`source`、`action`(`ignored`/`overridden`)。
|
|
211
|
+
|
|
212
|
+
## Model harness
|
|
213
|
+
|
|
214
|
+
按 provider / model 覆盖 Agent 循环默认参数(当前主要为 **`maxIterations`**)。
|
|
215
|
+
|
|
216
|
+
**合并顺序**(约定优先,详见 [ADR 0007](../../docs/adr/0007-ai-agent-model-harness-yaml-overrides.md)):
|
|
217
|
+
|
|
218
|
+
1. TypeScript 默认表 `MODEL_HARNESS_DEFAULTS`([`src/zhin-agent/model-harness.ts`](./src/zhin-agent/model-harness.ts))
|
|
219
|
+
2. YAML `ai.agent.modelHarness.providerPatterns`(匹配当前 provider,支持 `*` 通配;按对象键插入顺序叠加)
|
|
220
|
+
3. YAML `ai.agent.modelHarness.models`(`model` 或 `provider:model` 精确键)
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
ai:
|
|
224
|
+
agent:
|
|
225
|
+
modelHarness:
|
|
226
|
+
providerPatterns:
|
|
227
|
+
"open*":
|
|
228
|
+
maxIterations: 7
|
|
229
|
+
models:
|
|
230
|
+
"gpt-4o":
|
|
231
|
+
maxIterations: 8
|
|
232
|
+
"openai:gpt-4o":
|
|
233
|
+
maxIterations: 9
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
运行时由 `resolveModelHarness(providerName, modelName, config?.modelHarness)` 解析;包入口导出 `MODEL_HARNESS_DEFAULTS`、`resolveModelHarness`、`mergeModelHarnessValues` 与 `ModelHarnessConfig` 类型。未命中任何规则时回退 TS 默认行或空对象;当前仅消费 `maxIterations`,未知 YAML 字段会被忽略。
|
|
237
|
+
|
|
238
|
+
增补内置默认值:在 `model-harness.ts` 增加 `ModelHarnessRow` 并附测试;YAML 只做覆盖,不替代 TS 约定层。
|
|
239
|
+
|
|
216
240
|
## 开发
|
|
217
241
|
|
|
218
242
|
### 项目结构
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-event-subscriber.d.ts","sourceRoot":"","sources":["../src/ai-event-subscriber.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ai-event-subscriber.d.ts","sourceRoot":"","sources":["../src/ai-event-subscriber.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { storage } from '@zhin.js/core';
|
|
1
2
|
const EVENT_HANDLER_MAP = {
|
|
2
3
|
'ai.processing.start': 'onProcessingStart',
|
|
3
4
|
'ai.processing.finish': 'onProcessingFinish',
|
|
@@ -43,12 +44,15 @@ export function subscribeAIEvents(plugin, handlers, filter) {
|
|
|
43
44
|
const listener = (payload) => {
|
|
44
45
|
if (!matchesFilter(payload, filter))
|
|
45
46
|
return;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
handler
|
|
51
|
-
|
|
47
|
+
// broadcast 不经过 usePlugin 加载栈;在订阅方入口恢复 ALS,避免监听里 getPlugin() 失效
|
|
48
|
+
void storage.run(plugin, async () => {
|
|
49
|
+
handlers.onAny?.(eventName, payload);
|
|
50
|
+
const handlerName = EVENT_HANDLER_MAP[eventName];
|
|
51
|
+
const handler = handlerName ? handlers[handlerName] : undefined;
|
|
52
|
+
if (typeof handler === 'function') {
|
|
53
|
+
await handler(payload);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
52
56
|
};
|
|
53
57
|
plugin.on(eventName, listener);
|
|
54
58
|
return () => plugin.off(eventName, listener);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-event-subscriber.js","sourceRoot":"","sources":["../src/ai-event-subscriber.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ai-event-subscriber.js","sourceRoot":"","sources":["../src/ai-event-subscriber.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typing Indicator AI Event Binder
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 订阅 root 上的 AI 生命周期事件,驱动各平台 Bot 已有的 Typing Indicator。
|
|
5
|
+
* 平台适配器(如 ICQQ)应在 Bot 连接时自行挂载 `$typingIndicator`;
|
|
6
|
+
* 仅对未挂载的 Bot 才用通用 enableTypingIndicatorForBot 兜底。
|
|
6
7
|
*/
|
|
7
8
|
import type { AIServiceRefs } from './shared-refs.js';
|
|
8
|
-
export declare function registerTypingIndicator(
|
|
9
|
+
export declare function registerTypingIndicator(_refs: AIServiceRefs): void;
|
|
9
10
|
//# sourceMappingURL=register-typing-indicator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-typing-indicator.d.ts","sourceRoot":"","sources":["../../src/init/register-typing-indicator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"register-typing-indicator.d.ts","sourceRoot":"","sources":["../../src/init/register-typing-indicator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAyEtD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CA4OlE"}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typing Indicator AI Event Binder
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 订阅 root 上的 AI 生命周期事件,驱动各平台 Bot 已有的 Typing Indicator。
|
|
5
|
+
* 平台适配器(如 ICQQ)应在 Bot 连接时自行挂载 `$typingIndicator`;
|
|
6
|
+
* 仅对未挂载的 Bot 才用通用 enableTypingIndicatorForBot 兜底。
|
|
6
7
|
*/
|
|
7
8
|
import { getPlugin } from '@zhin.js/core';
|
|
8
9
|
import { subscribeAIEvents } from '../ai-event-subscriber.js';
|
|
9
|
-
import { enableTypingIndicatorForBot } from '../typing-indicator/adapter-integration.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import { enableTypingIndicatorForBot, } from '../typing-indicator/adapter-integration.js';
|
|
11
|
+
/** 与 ToolContext.scope / Message.$channel.type 一致(群聊 sceneId 通常只是群号,无 group: 前缀) */
|
|
12
|
+
function resolveSceneType(payload) {
|
|
13
|
+
const scope = payload.scope;
|
|
14
|
+
if (scope === 'group' || scope === 'channel' || scope === 'private') {
|
|
15
|
+
return scope;
|
|
16
|
+
}
|
|
17
|
+
const sceneId = payload.sceneId ?? '';
|
|
13
18
|
if (sceneId.startsWith('group:'))
|
|
14
19
|
return 'group';
|
|
15
20
|
if (sceneId.startsWith('channel:'))
|
|
@@ -18,10 +23,54 @@ function resolveSceneType(sceneId) {
|
|
|
18
23
|
return 'private';
|
|
19
24
|
return 'private';
|
|
20
25
|
}
|
|
21
|
-
|
|
26
|
+
/** sessionId 形如 platform:channelId:userId;群聊 channelId 在 sceneId */
|
|
27
|
+
function resolveTypingTargets(payload, sceneType) {
|
|
28
|
+
const { sceneId, userId, sessionId } = payload;
|
|
29
|
+
const parts = sessionId.split(':').filter((p) => p.length > 0);
|
|
30
|
+
let resolvedUserId = userId;
|
|
31
|
+
let groupId;
|
|
32
|
+
if (sceneType === 'group' || sceneType === 'channel') {
|
|
33
|
+
groupId = sceneId?.replace(/^(group|channel):/, '') || sceneId;
|
|
34
|
+
if (!groupId && parts.length >= 3) {
|
|
35
|
+
groupId = parts[1];
|
|
36
|
+
}
|
|
37
|
+
if (!resolvedUserId && parts.length >= 3) {
|
|
38
|
+
resolvedUserId = parts[parts.length - 1];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
if (sceneId?.startsWith('private:')) {
|
|
43
|
+
resolvedUserId = sceneId.slice('private:'.length);
|
|
44
|
+
}
|
|
45
|
+
else if (!resolvedUserId && parts.length >= 2) {
|
|
46
|
+
resolvedUserId = parts.length >= 3 ? parts[parts.length - 1] : parts[1];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
userId: resolvedUserId,
|
|
51
|
+
groupId: sceneType === 'group' || sceneType === 'channel' ? groupId : undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function resolveTypingSceneConfig(config, sceneType) {
|
|
55
|
+
const groupCfg = config.groupConfig;
|
|
56
|
+
const privateCfg = config.privateConfig;
|
|
57
|
+
if (sceneType === 'group' || sceneType === 'channel') {
|
|
58
|
+
return { ...config, ...groupCfg };
|
|
59
|
+
}
|
|
60
|
+
return { ...config, ...privateCfg };
|
|
61
|
+
}
|
|
62
|
+
function isGenericTypingManager(manager) {
|
|
63
|
+
return typeof manager.getActiveIndicator === 'function';
|
|
64
|
+
}
|
|
65
|
+
function resolveTypingManager(bot, platform, config, adapter) {
|
|
66
|
+
if (bot.$typingIndicator) {
|
|
67
|
+
return bot.$typingIndicator;
|
|
68
|
+
}
|
|
69
|
+
return enableTypingIndicatorForBot(bot, platform, config, adapter);
|
|
70
|
+
}
|
|
71
|
+
export function registerTypingIndicator(_refs) {
|
|
22
72
|
const plugin = getPlugin();
|
|
23
73
|
const { root, logger } = plugin;
|
|
24
|
-
// 绑定并订阅全局 AI 事件
|
|
25
74
|
const dispose = subscribeAIEvents(root, {
|
|
26
75
|
onProcessingStart: async (payload) => {
|
|
27
76
|
const { platform, botId, sessionId, messageId, sceneId } = payload;
|
|
@@ -34,24 +83,36 @@ export function registerTypingIndicator(refs) {
|
|
|
34
83
|
logger.debug(`[TypingIndicator] Bot not found for platform: ${platform}, botId: ${botId}`);
|
|
35
84
|
return;
|
|
36
85
|
}
|
|
37
|
-
// 获取此 bot 的独有配置
|
|
38
86
|
const botConfig = (bot.$config || {});
|
|
39
87
|
const config = (botConfig.typingIndicator || {});
|
|
40
|
-
// 如果配置中明确指明禁用,则不为其开启
|
|
41
88
|
if (config.enabled === false) {
|
|
42
89
|
return;
|
|
43
90
|
}
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
91
|
+
const manager = resolveTypingManager(bot, platform, config, adapter);
|
|
92
|
+
const sceneType = resolveSceneType(payload);
|
|
93
|
+
const targets = resolveTypingTargets(payload, sceneType);
|
|
94
|
+
const sceneConfig = resolveTypingSceneConfig(config, sceneType);
|
|
47
95
|
logger.debug(`[TypingIndicator] Auto starting indicator on event: processing.start for session: ${sessionId}`);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
96
|
+
if (isGenericTypingManager(manager)) {
|
|
97
|
+
await manager.start({
|
|
98
|
+
platform,
|
|
99
|
+
botId,
|
|
100
|
+
sessionId,
|
|
101
|
+
messageId,
|
|
102
|
+
sceneType,
|
|
103
|
+
userId: targets.userId,
|
|
104
|
+
groupId: targets.groupId,
|
|
105
|
+
}, sceneConfig);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
await manager.start({
|
|
109
|
+
messageId,
|
|
110
|
+
sessionId,
|
|
111
|
+
userId: targets.userId,
|
|
112
|
+
groupId: targets.groupId,
|
|
113
|
+
sceneType: sceneType === 'channel' ? 'group' : sceneType,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
55
116
|
}
|
|
56
117
|
catch (error) {
|
|
57
118
|
logger.error('[TypingIndicator] Error handling processing.start event:', error);
|
|
@@ -64,20 +125,30 @@ export function registerTypingIndicator(refs) {
|
|
|
64
125
|
try {
|
|
65
126
|
const adapter = root.injectAdapter(platform);
|
|
66
127
|
const bot = adapter?.bots?.get(botId);
|
|
67
|
-
if (!bot)
|
|
128
|
+
if (!bot?.$typingIndicator)
|
|
68
129
|
return;
|
|
130
|
+
const sceneType = resolveSceneType(payload);
|
|
131
|
+
const targets = resolveTypingTargets(payload, sceneType);
|
|
69
132
|
const manager = bot.$typingIndicator;
|
|
70
|
-
if (!manager)
|
|
71
|
-
return;
|
|
72
|
-
const sceneType = resolveSceneType(sceneId);
|
|
73
133
|
logger.debug(`[TypingIndicator] Auto stopping indicator on event: processing.finish for session: ${sessionId}`);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
134
|
+
if (isGenericTypingManager(manager)) {
|
|
135
|
+
await manager.stop({
|
|
136
|
+
platform,
|
|
137
|
+
botId,
|
|
138
|
+
sessionId,
|
|
139
|
+
messageId,
|
|
140
|
+
sceneType,
|
|
141
|
+
userId: targets.userId,
|
|
142
|
+
groupId: targets.groupId,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
await manager.stop({
|
|
147
|
+
sessionId,
|
|
148
|
+
userId: targets.userId,
|
|
149
|
+
groupId: targets.groupId,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
81
152
|
}
|
|
82
153
|
catch (error) {
|
|
83
154
|
logger.error('[TypingIndicator] Error handling processing.finish event:', error);
|
|
@@ -90,20 +161,30 @@ export function registerTypingIndicator(refs) {
|
|
|
90
161
|
try {
|
|
91
162
|
const adapter = root.injectAdapter(platform);
|
|
92
163
|
const bot = adapter?.bots?.get(botId);
|
|
93
|
-
if (!bot)
|
|
164
|
+
if (!bot?.$typingIndicator)
|
|
94
165
|
return;
|
|
166
|
+
const sceneType = resolveSceneType(payload);
|
|
167
|
+
const targets = resolveTypingTargets(payload, sceneType);
|
|
95
168
|
const manager = bot.$typingIndicator;
|
|
96
|
-
if (!manager)
|
|
97
|
-
return;
|
|
98
|
-
const sceneType = resolveSceneType(sceneId);
|
|
99
169
|
logger.debug(`[TypingIndicator] Auto stopping indicator on event: processing.error for session: ${sessionId}`);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
170
|
+
if (isGenericTypingManager(manager)) {
|
|
171
|
+
await manager.stop({
|
|
172
|
+
platform,
|
|
173
|
+
botId,
|
|
174
|
+
sessionId,
|
|
175
|
+
messageId,
|
|
176
|
+
sceneType,
|
|
177
|
+
userId: targets.userId,
|
|
178
|
+
groupId: targets.groupId,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
await manager.stop({
|
|
183
|
+
sessionId,
|
|
184
|
+
userId: targets.userId,
|
|
185
|
+
groupId: targets.groupId,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
107
188
|
}
|
|
108
189
|
catch (error) {
|
|
109
190
|
logger.error('[TypingIndicator] Error handling processing.error event:', error);
|
|
@@ -116,18 +197,19 @@ export function registerTypingIndicator(refs) {
|
|
|
116
197
|
try {
|
|
117
198
|
const adapter = root.injectAdapter(platform);
|
|
118
199
|
const bot = adapter?.bots?.get(botId);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const manager = bot.$typingIndicator;
|
|
122
|
-
if (!manager)
|
|
200
|
+
const manager = bot?.$typingIndicator;
|
|
201
|
+
if (!manager || !isGenericTypingManager(manager))
|
|
123
202
|
return;
|
|
124
|
-
const sceneType = resolveSceneType(
|
|
203
|
+
const sceneType = resolveSceneType(payload);
|
|
204
|
+
const targets = resolveTypingTargets(payload, sceneType);
|
|
125
205
|
const indicator = manager.getActiveIndicator({
|
|
126
206
|
platform,
|
|
127
207
|
botId,
|
|
128
208
|
sessionId,
|
|
129
209
|
messageId,
|
|
130
210
|
sceneType,
|
|
211
|
+
userId: targets.userId,
|
|
212
|
+
groupId: targets.groupId,
|
|
131
213
|
});
|
|
132
214
|
if (indicator && typeof indicator.update === 'function') {
|
|
133
215
|
await indicator.update(thinking);
|
|
@@ -144,18 +226,19 @@ export function registerTypingIndicator(refs) {
|
|
|
144
226
|
try {
|
|
145
227
|
const adapter = root.injectAdapter(platform);
|
|
146
228
|
const bot = adapter?.bots?.get(botId);
|
|
147
|
-
|
|
229
|
+
const manager = bot?.$typingIndicator;
|
|
230
|
+
if (!manager || !isGenericTypingManager(manager))
|
|
148
231
|
return;
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
return;
|
|
152
|
-
const sceneType = resolveSceneType(sceneId);
|
|
232
|
+
const sceneType = resolveSceneType(payload);
|
|
233
|
+
const targets = resolveTypingTargets(payload, sceneType);
|
|
153
234
|
const indicator = manager.getActiveIndicator({
|
|
154
235
|
platform,
|
|
155
236
|
botId,
|
|
156
237
|
sessionId,
|
|
157
238
|
messageId,
|
|
158
239
|
sceneType,
|
|
240
|
+
userId: targets.userId,
|
|
241
|
+
groupId: targets.groupId,
|
|
159
242
|
});
|
|
160
243
|
if (indicator && typeof indicator.update === 'function') {
|
|
161
244
|
const updateText = label ? `🔍 子任务执行里: ${label}...` : '🔍 正在调度和思考子代理处理...';
|
|
@@ -173,21 +256,22 @@ export function registerTypingIndicator(refs) {
|
|
|
173
256
|
try {
|
|
174
257
|
const adapter = root.injectAdapter(platform);
|
|
175
258
|
const bot = adapter?.bots?.get(botId);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const manager = bot.$typingIndicator;
|
|
179
|
-
if (!manager)
|
|
259
|
+
const manager = bot?.$typingIndicator;
|
|
260
|
+
if (!manager || !isGenericTypingManager(manager))
|
|
180
261
|
return;
|
|
181
|
-
const sceneType = resolveSceneType(
|
|
262
|
+
const sceneType = resolveSceneType(payload);
|
|
263
|
+
const targets = resolveTypingTargets(payload, sceneType);
|
|
182
264
|
const indicator = manager.getActiveIndicator({
|
|
183
265
|
platform,
|
|
184
266
|
botId,
|
|
185
267
|
sessionId,
|
|
186
268
|
messageId,
|
|
187
269
|
sceneType,
|
|
270
|
+
userId: targets.userId,
|
|
271
|
+
groupId: targets.groupId,
|
|
188
272
|
});
|
|
189
273
|
if (indicator && typeof indicator.update === 'function') {
|
|
190
|
-
const botConfig = (bot
|
|
274
|
+
const botConfig = (bot?.$config || {});
|
|
191
275
|
const config = (botConfig.typingIndicator || {});
|
|
192
276
|
const msg = String(config.message || '正在处理中...');
|
|
193
277
|
await indicator.update(msg);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-typing-indicator.js","sourceRoot":"","sources":["../../src/init/register-typing-indicator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"register-typing-indicator.js","sourceRoot":"","sources":["../../src/init/register-typing-indicator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dangerous-tool-policy.d.ts","sourceRoot":"","sources":["../../src/security/dangerous-tool-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGnG,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"dangerous-tool-policy.d.ts","sourceRoot":"","sources":["../../src/security/dangerous-tool-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGnG,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,CAAC;AA8EnG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,qBAAqB,CAuCxG;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,qBAAqB,CA8BnI;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,GAAG,WAAW,GAAG,WAAW,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,qBAAqB,CAyCzI;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAErE;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAEnE;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,GAAG,OAAO,CAGtG"}
|
|
@@ -28,24 +28,65 @@ function resolveExecAllowlistFromAiService(plugin) {
|
|
|
28
28
|
return [];
|
|
29
29
|
return allowlist.map((v) => String(v)).filter(Boolean);
|
|
30
30
|
}
|
|
31
|
+
function hasToolIdentity(context) {
|
|
32
|
+
return Boolean(context?.platform && context?.botId && context?.senderId);
|
|
33
|
+
}
|
|
31
34
|
function resolveRoleFromContext(context) {
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
const hasIdentity = hasToolIdentity(context);
|
|
36
|
+
if (!hasIdentity) {
|
|
37
|
+
return { role: 'unknown', hasIdentity: false };
|
|
34
38
|
}
|
|
35
39
|
try {
|
|
36
40
|
const plugin = getPlugin();
|
|
37
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
role: resolveToolRequesterRole(plugin, context),
|
|
43
|
+
plugin,
|
|
44
|
+
hasIdentity: true,
|
|
45
|
+
};
|
|
38
46
|
}
|
|
39
47
|
catch {
|
|
40
|
-
return {
|
|
48
|
+
return {
|
|
49
|
+
role: resolveRoleFromToolContextFallback(context),
|
|
50
|
+
hasIdentity: true,
|
|
51
|
+
};
|
|
41
52
|
}
|
|
42
53
|
}
|
|
54
|
+
/** getPlugin 不可用时,从 ToolContext 上的 IM 标记推断角色(测试/降级路径) */
|
|
55
|
+
function resolveRoleFromToolContextFallback(context) {
|
|
56
|
+
if (context.isOwner === true)
|
|
57
|
+
return 'owner';
|
|
58
|
+
if (context.isBotAdmin === true)
|
|
59
|
+
return 'admin';
|
|
60
|
+
if (context.fileRole === 'owner')
|
|
61
|
+
return 'owner';
|
|
62
|
+
if (context.fileRole === 'admin')
|
|
63
|
+
return 'admin';
|
|
64
|
+
if (context.fileRole === 'user')
|
|
65
|
+
return 'other';
|
|
66
|
+
return 'unknown';
|
|
67
|
+
}
|
|
68
|
+
function denyUnidentifiedTool(toolName) {
|
|
69
|
+
return {
|
|
70
|
+
allowed: false,
|
|
71
|
+
role: 'unknown',
|
|
72
|
+
reason: `无法确认调用者身份:工具「${toolName}」已拒绝。`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
43
75
|
export function checkFileToolAccess(toolName, context) {
|
|
44
|
-
const { role } = resolveRoleFromContext(context);
|
|
76
|
+
const { role, hasIdentity } = resolveRoleFromContext(context);
|
|
45
77
|
const op = FILE_TOOL_OPERATION[toolName];
|
|
46
|
-
if (role === 'owner'
|
|
78
|
+
if (role === 'owner') {
|
|
47
79
|
return { allowed: true, role };
|
|
48
80
|
}
|
|
81
|
+
if (!hasIdentity) {
|
|
82
|
+
return { allowed: true, role: 'unknown' };
|
|
83
|
+
}
|
|
84
|
+
if (role === 'unknown') {
|
|
85
|
+
if (op === 'read') {
|
|
86
|
+
return { allowed: true, role };
|
|
87
|
+
}
|
|
88
|
+
return denyUnidentifiedTool(toolName);
|
|
89
|
+
}
|
|
49
90
|
if (role === 'admin') {
|
|
50
91
|
if (op === 'delete') {
|
|
51
92
|
return {
|
|
@@ -94,11 +135,17 @@ export function checkSensitiveFilePathAccess(toolName, filePath, context) {
|
|
|
94
135
|
};
|
|
95
136
|
}
|
|
96
137
|
export function checkDangerousToolAccess(toolName, context) {
|
|
97
|
-
const { role, plugin } = resolveRoleFromContext(context);
|
|
138
|
+
const { role, plugin, hasIdentity } = resolveRoleFromContext(context);
|
|
139
|
+
if (!hasIdentity) {
|
|
140
|
+
return { allowed: true, role: 'unknown' };
|
|
141
|
+
}
|
|
98
142
|
try {
|
|
99
143
|
if (role === 'owner') {
|
|
100
144
|
return { allowed: true, role };
|
|
101
145
|
}
|
|
146
|
+
if (role === 'unknown') {
|
|
147
|
+
return denyUnidentifiedTool(toolName);
|
|
148
|
+
}
|
|
102
149
|
if (role === 'admin') {
|
|
103
150
|
const allowlist = plugin ? resolveExecAllowlistFromAiService(plugin) : [];
|
|
104
151
|
if (isAllowlisted(allowlist, toolName)) {
|
|
@@ -118,10 +165,10 @@ export function checkDangerousToolAccess(toolName, context) {
|
|
|
118
165
|
reason: `工具「${toolName}」为危险操作,仅 owner 可直接执行;admin 需 Owner 审批。`,
|
|
119
166
|
};
|
|
120
167
|
}
|
|
121
|
-
return
|
|
168
|
+
return denyUnidentifiedTool(toolName);
|
|
122
169
|
}
|
|
123
170
|
catch {
|
|
124
|
-
return
|
|
171
|
+
return denyUnidentifiedTool(toolName);
|
|
125
172
|
}
|
|
126
173
|
}
|
|
127
174
|
export function toOwnerSignal(decision) {
|