@zhin.js/ai 0.0.2 → 1.0.1
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/CHANGELOG.md +2 -7
- package/lib/agent.d.ts +54 -6
- package/lib/agent.d.ts.map +1 -1
- package/lib/agent.js +468 -116
- package/lib/agent.js.map +1 -1
- package/lib/compaction.d.ts +132 -0
- package/lib/compaction.d.ts.map +1 -0
- package/lib/compaction.js +370 -0
- package/lib/compaction.js.map +1 -0
- package/lib/context-manager.d.ts.map +1 -1
- package/lib/context-manager.js +10 -3
- package/lib/context-manager.js.map +1 -1
- package/lib/conversation-memory.d.ts +192 -0
- package/lib/conversation-memory.d.ts.map +1 -0
- package/lib/conversation-memory.js +619 -0
- package/lib/conversation-memory.js.map +1 -0
- package/lib/index.d.ts +25 -163
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +24 -1122
- package/lib/index.js.map +1 -1
- package/lib/output.d.ts +93 -0
- package/lib/output.d.ts.map +1 -0
- package/lib/output.js +176 -0
- package/lib/output.js.map +1 -0
- package/lib/providers/anthropic.d.ts +7 -0
- package/lib/providers/anthropic.d.ts.map +1 -1
- package/lib/providers/anthropic.js +5 -0
- package/lib/providers/anthropic.js.map +1 -1
- package/lib/providers/ollama.d.ts +10 -0
- package/lib/providers/ollama.d.ts.map +1 -1
- package/lib/providers/ollama.js +19 -4
- package/lib/providers/ollama.js.map +1 -1
- package/lib/providers/openai.d.ts +7 -0
- package/lib/providers/openai.d.ts.map +1 -1
- package/lib/providers/openai.js +11 -0
- package/lib/providers/openai.js.map +1 -1
- package/lib/rate-limiter.d.ts +38 -0
- package/lib/rate-limiter.d.ts.map +1 -0
- package/lib/rate-limiter.js +86 -0
- package/lib/rate-limiter.js.map +1 -0
- package/lib/session.d.ts +7 -0
- package/lib/session.d.ts.map +1 -1
- package/lib/session.js +47 -18
- package/lib/session.js.map +1 -1
- package/lib/storage.d.ts +68 -0
- package/lib/storage.d.ts.map +1 -0
- package/lib/storage.js +105 -0
- package/lib/storage.js.map +1 -0
- package/lib/tone-detector.d.ts +19 -0
- package/lib/tone-detector.d.ts.map +1 -0
- package/lib/tone-detector.js +72 -0
- package/lib/tone-detector.js.map +1 -0
- package/lib/types.d.ts +84 -8
- package/lib/types.d.ts.map +1 -1
- package/package.json +13 -42
- package/src/agent.ts +518 -135
- package/src/compaction.ts +529 -0
- package/src/context-manager.ts +10 -9
- package/src/conversation-memory.ts +816 -0
- package/src/index.ts +121 -1406
- package/src/output.ts +261 -0
- package/src/providers/anthropic.ts +4 -0
- package/src/providers/ollama.ts +23 -4
- package/src/providers/openai.ts +8 -1
- package/src/rate-limiter.ts +129 -0
- package/src/session.ts +47 -18
- package/src/storage.ts +135 -0
- package/src/tone-detector.ts +89 -0
- package/src/types.ts +95 -6
- package/tests/agent.test.ts +123 -70
- package/tests/compaction.test.ts +310 -0
- package/tests/context-manager.test.ts +73 -47
- package/tests/conversation-memory.test.ts +128 -0
- package/tests/output.test.ts +128 -0
- package/tests/providers.test.ts +574 -0
- package/tests/rate-limiter.test.ts +108 -0
- package/tests/session.test.ts +139 -48
- package/tests/setup.ts +82 -240
- package/tests/storage.test.ts +224 -0
- package/tests/tone-detector.test.ts +80 -0
- package/tsconfig.json +4 -5
- package/vitest.setup.ts +1 -0
- package/README.md +0 -564
- package/TOOLS.md +0 -294
- package/lib/tools.d.ts +0 -45
- package/lib/tools.d.ts.map +0 -1
- package/lib/tools.js +0 -194
- package/lib/tools.js.map +0 -1
- package/src/tools.ts +0 -205
- package/tests/ai-trigger.test.ts +0 -369
- package/tests/integration.test.ts +0 -596
- package/tests/providers.integration.test.ts +0 -227
- package/tests/tool.test.ts +0 -800
- package/tests/tools-builtin.test.ts +0 -346
package/src/storage.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StorageBackend — Unified storage abstraction layer
|
|
3
|
+
*
|
|
4
|
+
* Provides a common interface for AI sub-systems (session, context, memory,
|
|
5
|
+
* user profile, follow-up) to switch between in-memory and database backends
|
|
6
|
+
* without changing business logic.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const backend = new MemoryStorageBackend<MyRecord>();
|
|
10
|
+
* // ... later, when DB is ready:
|
|
11
|
+
* const dbBackend = new DatabaseStorageBackend<MyRecord>(model, { keyField: 'session_id' });
|
|
12
|
+
* service.upgradeBackend(dbBackend);
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface StorageBackend<T extends Record<string, any>> {
|
|
16
|
+
get(key: string): Promise<T | null>;
|
|
17
|
+
set(key: string, value: T): Promise<void>;
|
|
18
|
+
delete(key: string): Promise<boolean>;
|
|
19
|
+
list(filter?: Partial<T>): Promise<T[]>;
|
|
20
|
+
clear(): Promise<void>;
|
|
21
|
+
readonly type: 'memory' | 'database';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* In-memory storage backend.
|
|
26
|
+
*/
|
|
27
|
+
export class MemoryStorageBackend<T extends Record<string, any>> implements StorageBackend<T> {
|
|
28
|
+
readonly type = 'memory' as const;
|
|
29
|
+
private data = new Map<string, T>();
|
|
30
|
+
|
|
31
|
+
async get(key: string): Promise<T | null> {
|
|
32
|
+
return this.data.get(key) ?? null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async set(key: string, value: T): Promise<void> {
|
|
36
|
+
this.data.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async delete(key: string): Promise<boolean> {
|
|
40
|
+
return this.data.delete(key);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async list(filter?: Partial<T>): Promise<T[]> {
|
|
44
|
+
const all = Array.from(this.data.values());
|
|
45
|
+
if (!filter) return all;
|
|
46
|
+
return all.filter(item => {
|
|
47
|
+
for (const [k, v] of Object.entries(filter)) {
|
|
48
|
+
if (item[k] !== v) return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async clear(): Promise<void> {
|
|
55
|
+
this.data.clear();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Database model interface aligned with @zhin.js/database's RelatedModel API.
|
|
61
|
+
*/
|
|
62
|
+
export interface DbModel {
|
|
63
|
+
select(...fields: string[]): any;
|
|
64
|
+
create(data: Record<string, any>): Promise<any>;
|
|
65
|
+
update(data: Partial<any>): any;
|
|
66
|
+
delete(condition: Record<string, any>): any;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Database-backed storage backend.
|
|
71
|
+
*/
|
|
72
|
+
export class DatabaseStorageBackend<T extends Record<string, any>> implements StorageBackend<T> {
|
|
73
|
+
readonly type = 'database' as const;
|
|
74
|
+
|
|
75
|
+
constructor(
|
|
76
|
+
private model: DbModel,
|
|
77
|
+
private options: {
|
|
78
|
+
/** The field name used as lookup key (e.g. 'session_id', 'user_id') */
|
|
79
|
+
keyField: string;
|
|
80
|
+
},
|
|
81
|
+
) {}
|
|
82
|
+
|
|
83
|
+
async get(key: string): Promise<T | null> {
|
|
84
|
+
const rows: T[] = await this.model
|
|
85
|
+
.select()
|
|
86
|
+
.where({ [this.options.keyField]: key });
|
|
87
|
+
return rows[0] ?? null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async set(key: string, value: T): Promise<void> {
|
|
91
|
+
const existing = await this.get(key);
|
|
92
|
+
if (existing) {
|
|
93
|
+
await this.model
|
|
94
|
+
.update(value)
|
|
95
|
+
.where({ [this.options.keyField]: key });
|
|
96
|
+
} else {
|
|
97
|
+
await this.model.create({ ...value, [this.options.keyField]: key });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async delete(key: string): Promise<boolean> {
|
|
102
|
+
try {
|
|
103
|
+
await this.model.delete({ [this.options.keyField]: key });
|
|
104
|
+
return true;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async list(filter?: Partial<T>): Promise<T[]> {
|
|
111
|
+
if (filter) {
|
|
112
|
+
return this.model.select().where(filter);
|
|
113
|
+
}
|
|
114
|
+
return this.model.select();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async clear(): Promise<void> {
|
|
118
|
+
const all = await this.list();
|
|
119
|
+
for (const item of all) {
|
|
120
|
+
const key = (item as Record<string, unknown>)[this.options.keyField] as string | undefined;
|
|
121
|
+
if (key) await this.delete(key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Helper to create a swappable backend ref.
|
|
128
|
+
* Call `swap(newBackend)` to atomically upgrade from memory to database.
|
|
129
|
+
*/
|
|
130
|
+
export function createSwappableBackend<T extends Record<string, any>>(
|
|
131
|
+
initial: StorageBackend<T>,
|
|
132
|
+
): { backend: StorageBackend<T>; swap: (next: StorageBackend<T>) => void } {
|
|
133
|
+
const ref = { backend: initial, swap: (next: StorageBackend<T>) => { ref.backend = next; } };
|
|
134
|
+
return ref;
|
|
135
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToneDetector — 轻量级情绪/语气检测
|
|
3
|
+
*
|
|
4
|
+
* 通过标点符号、emoji 密度、关键词分析用户语气,
|
|
5
|
+
* 生成一条 hint 注入 system prompt,让 AI 的回复匹配用户情绪。
|
|
6
|
+
*
|
|
7
|
+
* 零 LLM 开销,纯正则/统计分析。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type Tone = 'neutral' | 'frustrated' | 'excited' | 'questioning' | 'sad' | 'urgent';
|
|
11
|
+
|
|
12
|
+
interface ToneResult {
|
|
13
|
+
tone: Tone;
|
|
14
|
+
hint: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 常见负面情绪词
|
|
18
|
+
const FRUSTRATED_WORDS = /不行|不对|又错|还是不|怎么回事|搞不定|烦死|崩溃|无语|什么鬼|bug|报错|失败|出问题/;
|
|
19
|
+
const SAD_WORDS = /难过|伤心|失落|遗憾|可惜|唉|哎|不开心|郁闷|心累/;
|
|
20
|
+
const URGENT_WORDS = /急|赶紧|马上|立刻|紧急|尽快|快点|asap|hurry/i;
|
|
21
|
+
const EXCITED_WORDS = /太好了|太棒了|厉害|牛|可以|成功|搞定|完美|赞|nice|amazing|awesome|cool/i;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 检测用户消息的情绪语气
|
|
25
|
+
*/
|
|
26
|
+
export function detectTone(message: string): ToneResult {
|
|
27
|
+
const len = message.length;
|
|
28
|
+
if (len === 0) return { tone: 'neutral', hint: '' };
|
|
29
|
+
|
|
30
|
+
// 统计特征
|
|
31
|
+
const exclamations = (message.match(/!/g) || []).length + (message.match(/!/g) || []).length;
|
|
32
|
+
const questions = (message.match(/\?/g) || []).length + (message.match(/?/g) || []).length;
|
|
33
|
+
const ellipsis = (message.match(/\.\.\./g) || []).length + (message.match(/…/g) || []).length;
|
|
34
|
+
const capsRatio = len > 5 ? (message.match(/[A-Z]/g) || []).length / len : 0;
|
|
35
|
+
|
|
36
|
+
// emoji 检测(常见 Unicode 范围)
|
|
37
|
+
const emojiCount = (message.match(/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu) || []).length;
|
|
38
|
+
|
|
39
|
+
// 关键词检测
|
|
40
|
+
const isFrustrated = FRUSTRATED_WORDS.test(message);
|
|
41
|
+
const isSad = SAD_WORDS.test(message);
|
|
42
|
+
const isUrgent = URGENT_WORDS.test(message);
|
|
43
|
+
const isExcited = EXCITED_WORDS.test(message);
|
|
44
|
+
|
|
45
|
+
// 判定优先级: frustrated > urgent > sad > excited > questioning > neutral
|
|
46
|
+
if (isFrustrated || (exclamations >= 3 && !isExcited)) {
|
|
47
|
+
return {
|
|
48
|
+
tone: 'frustrated',
|
|
49
|
+
hint: '用户似乎有些沮丧或受挫,请用耐心、理解的语气回复,先表示共情再提供帮助。',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (isUrgent) {
|
|
54
|
+
return {
|
|
55
|
+
tone: 'urgent',
|
|
56
|
+
hint: '用户似乎很着急,请直接给出解决方案,减少寒暄,优先效率。',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isSad || ellipsis >= 2) {
|
|
61
|
+
return {
|
|
62
|
+
tone: 'sad',
|
|
63
|
+
hint: '用户的语气似乎有些低落,请用温暖、关心的语气回复。',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (isExcited || (emojiCount >= 2 && exclamations >= 1)) {
|
|
68
|
+
return {
|
|
69
|
+
tone: 'excited',
|
|
70
|
+
hint: '用户的心情不错,可以用更活泼、热情的语气回复。',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (questions >= 2 || (questions >= 1 && len < 20)) {
|
|
75
|
+
return {
|
|
76
|
+
tone: 'questioning',
|
|
77
|
+
hint: '', // 提问是正常的,不需要特殊 hint
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (capsRatio > 0.5 && len > 10) {
|
|
82
|
+
return {
|
|
83
|
+
tone: 'frustrated',
|
|
84
|
+
hint: '用户使用了大量大写字母,可能在表达强烈情绪,请注意语气。',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { tone: 'neutral', hint: '' };
|
|
89
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// ============================================================================
|
|
9
9
|
|
|
10
10
|
/** 消息角色 */
|
|
11
|
-
export type MessageRole = 'system' | 'user' | 'assistant' | 'tool';
|
|
11
|
+
export type MessageRole = 'system' | 'user' | 'assistant' | 'tool' | 'tool_call' | 'tool_result';
|
|
12
12
|
|
|
13
13
|
/** 聊天消息 */
|
|
14
14
|
export interface ChatMessage {
|
|
@@ -51,10 +51,11 @@ export interface JsonSchema {
|
|
|
51
51
|
properties?: Record<string, JsonSchema>;
|
|
52
52
|
required?: string[];
|
|
53
53
|
items?: JsonSchema;
|
|
54
|
-
enum?:
|
|
54
|
+
enum?: unknown[];
|
|
55
55
|
description?: string;
|
|
56
|
-
default?:
|
|
57
|
-
|
|
56
|
+
default?: unknown;
|
|
57
|
+
/** JSON Schema allows arbitrary extension properties */
|
|
58
|
+
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any -- JSON Schema spec requires open index
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
// ============================================================================
|
|
@@ -75,6 +76,8 @@ export interface ChatCompletionRequest {
|
|
|
75
76
|
presence_penalty?: number;
|
|
76
77
|
frequency_penalty?: number;
|
|
77
78
|
user?: string;
|
|
79
|
+
/** 是否启用模型思考(如 qwen3 的 <think> 模式)。设为 false 可跳过思考加速响应。 */
|
|
80
|
+
think?: boolean;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
/** 聊天补全响应 */
|
|
@@ -126,14 +129,27 @@ export interface ChatCompletionChunkChoice {
|
|
|
126
129
|
// Provider 类型
|
|
127
130
|
// ============================================================================
|
|
128
131
|
|
|
132
|
+
/** Provider 能力声明 */
|
|
133
|
+
export interface ProviderCapabilities {
|
|
134
|
+
vision?: boolean;
|
|
135
|
+
streaming?: boolean;
|
|
136
|
+
toolCalling?: boolean;
|
|
137
|
+
thinking?: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
129
140
|
/** Provider 配置 */
|
|
130
141
|
export interface ProviderConfig {
|
|
131
142
|
apiKey?: string;
|
|
132
143
|
baseUrl?: string;
|
|
133
144
|
defaultModel?: string;
|
|
145
|
+
models?: string[];
|
|
134
146
|
timeout?: number;
|
|
135
147
|
maxRetries?: number;
|
|
136
148
|
headers?: Record<string, string>;
|
|
149
|
+
/** 上下文窗口大小(token 数),各 Provider 映射到自身参数 */
|
|
150
|
+
contextWindow?: number;
|
|
151
|
+
/** Provider 能力声明 */
|
|
152
|
+
capabilities?: ProviderCapabilities;
|
|
137
153
|
}
|
|
138
154
|
|
|
139
155
|
/** Provider 接口 */
|
|
@@ -152,6 +168,33 @@ export interface AIProvider {
|
|
|
152
168
|
|
|
153
169
|
/** 检查连接 */
|
|
154
170
|
healthCheck?(): Promise<boolean>;
|
|
171
|
+
|
|
172
|
+
/** 上下文窗口大小(token 数),由 Provider 实现暴露 */
|
|
173
|
+
contextWindow?: number;
|
|
174
|
+
|
|
175
|
+
/** Provider 能力声明 */
|
|
176
|
+
capabilities?: ProviderCapabilities;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// Tool Context (generic, IM-free)
|
|
181
|
+
// ============================================================================
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 通用工具执行上下文。
|
|
185
|
+
*
|
|
186
|
+
* 定义了平台无关的字段,IM 应用通过 core 的 IMToolContext 扩展。
|
|
187
|
+
* 非 IM 应用可直接使用此接口。
|
|
188
|
+
*/
|
|
189
|
+
export interface ToolContext {
|
|
190
|
+
/** 来源平台标识 */
|
|
191
|
+
platform?: string;
|
|
192
|
+
/** 发送者 ID */
|
|
193
|
+
senderId?: string;
|
|
194
|
+
/** 场景 / 频道 ID */
|
|
195
|
+
sceneId?: string;
|
|
196
|
+
/** 额外数据(应用自行扩展) */
|
|
197
|
+
[key: string]: unknown;
|
|
155
198
|
}
|
|
156
199
|
|
|
157
200
|
// ============================================================================
|
|
@@ -163,7 +206,31 @@ export interface AgentTool {
|
|
|
163
206
|
name: string;
|
|
164
207
|
description: string;
|
|
165
208
|
parameters: JsonSchema;
|
|
166
|
-
execute: (args: Record<string, any>) => Promise<
|
|
209
|
+
execute: (args: Record<string, any>) => Promise<unknown>;
|
|
210
|
+
/** 工具标签,用于分类和快速匹配 */
|
|
211
|
+
tags?: string[];
|
|
212
|
+
/** 触发关键词,用户消息包含这些词时优先选择此工具 */
|
|
213
|
+
keywords?: string[];
|
|
214
|
+
/** 所需权限级别 (0=所有人, 1=群管理, 2=群主, 3=Bot管理员, 4=拥有者) */
|
|
215
|
+
permissionLevel?: number;
|
|
216
|
+
/** 是否允许预执行(opt-in),默认 false */
|
|
217
|
+
preExecutable?: boolean;
|
|
218
|
+
/** 工具分类(如 file / shell / web),用于 formatToolTitle 等展示 */
|
|
219
|
+
kind?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 工具过滤选项
|
|
224
|
+
* 在 Agent.run() / runStream() 中启用程序化工具预过滤,
|
|
225
|
+
* 省去额外的 AI 意图分析往返
|
|
226
|
+
*/
|
|
227
|
+
export interface ToolFilterOptions {
|
|
228
|
+
/** 调用者权限级别 (0-4),高于工具要求才能使用 */
|
|
229
|
+
callerPermissionLevel?: number;
|
|
230
|
+
/** 最大返回工具数量 (默认 10) */
|
|
231
|
+
maxTools?: number;
|
|
232
|
+
/** 最低相关性得分阈值,低于此分数的工具被过滤掉 (默认 0.1) */
|
|
233
|
+
minScore?: number;
|
|
167
234
|
}
|
|
168
235
|
|
|
169
236
|
/** Agent 配置 */
|
|
@@ -215,6 +282,13 @@ export interface Session {
|
|
|
215
282
|
// AI Service 配置
|
|
216
283
|
// ============================================================================
|
|
217
284
|
|
|
285
|
+
/** Ollama-specific fields for AIConfig typing (mirrors OllamaConfig) */
|
|
286
|
+
export interface OllamaProviderConfig extends ProviderConfig {
|
|
287
|
+
host?: string;
|
|
288
|
+
models?: string[];
|
|
289
|
+
num_ctx?: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
218
292
|
/** AI 服务配置 */
|
|
219
293
|
export interface AIConfig {
|
|
220
294
|
enabled?: boolean;
|
|
@@ -225,7 +299,7 @@ export interface AIConfig {
|
|
|
225
299
|
deepseek?: ProviderConfig;
|
|
226
300
|
moonshot?: ProviderConfig;
|
|
227
301
|
zhipu?: ProviderConfig;
|
|
228
|
-
ollama?:
|
|
302
|
+
ollama?: OllamaProviderConfig;
|
|
229
303
|
custom?: ProviderConfig[];
|
|
230
304
|
};
|
|
231
305
|
sessions?: {
|
|
@@ -250,6 +324,21 @@ export interface AIConfig {
|
|
|
250
324
|
/** 自定义总结提示词 */
|
|
251
325
|
summaryPrompt?: string;
|
|
252
326
|
};
|
|
327
|
+
/** Agent 工具开关与执行安全 */
|
|
328
|
+
agent?: {
|
|
329
|
+
/** 禁用的工具名列表,这些工具不会下发给 AI */
|
|
330
|
+
disabledTools?: string[];
|
|
331
|
+
/** 仅允许的工具名列表;若设置则只下发列表中的工具(与 disabledTools 二选一,allowedTools 优先) */
|
|
332
|
+
allowedTools?: string[];
|
|
333
|
+
/** bash 执行策略:deny=禁止执行,allowlist=仅允许列表内命令,full=不限制 */
|
|
334
|
+
execSecurity?: 'deny' | 'allowlist' | 'full';
|
|
335
|
+
/** 预设命令白名单模式:readonly / network / development / custom(默认 custom,使用自定义 execAllowlist) */
|
|
336
|
+
execPreset?: 'readonly' | 'network' | 'development' | 'custom';
|
|
337
|
+
/** allowlist 模式下允许的命令(支持正则字符串,如 "^ls "、"^cat "),与 preset 合并 */
|
|
338
|
+
execAllowlist?: string[];
|
|
339
|
+
/** allowlist 未命中时:true=需审批(当前实现为拒绝并提示),false=直接拒绝 */
|
|
340
|
+
execAsk?: boolean;
|
|
341
|
+
};
|
|
253
342
|
/** AI 触发配置 */
|
|
254
343
|
trigger?: {
|
|
255
344
|
/** 是否启用(默认 true) */
|