-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathBladeAgent.ts
More file actions
223 lines (199 loc) · 6.14 KB
/
BladeAgent.ts
File metadata and controls
223 lines (199 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/**
* Blade ACP Agent 实现
*
* 实现 ACP 协议的 Agent 接口,使 Blade 可以被 Zed、JetBrains 等编辑器调用。
*
*/
import type * as acp from '@agentclientprotocol/sdk';
import {
type Agent as AcpAgentInterface,
type AgentSideConnection,
PROTOCOL_VERSION,
} from '@agentclientprotocol/sdk';
import { nanoid } from 'nanoid';
import { createLogger, LogCategory } from '../logging/Logger.js';
import { McpRegistry } from '../mcp/McpRegistry.js';
import { getConfig } from '../store/vanilla.js';
import { AcpSession } from './Session.js';
const logger = createLogger(LogCategory.AGENT);
/**
* Blade ACP Agent
*
* 实现 ACP 协议的 Agent 接口,处理来自 IDE 的请求。
*/
export class BladeAgent implements AcpAgentInterface {
private sessions: Map<string, AcpSession> = new Map();
private clientCapabilities: acp.ClientCapabilities | undefined;
constructor(private connection: AgentSideConnection) {}
/**
* 初始化连接,协商协议版本和能力
*/
async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResponse> {
logger.info('[BladeAgent] Initializing ACP connection');
logger.debug(
`[BladeAgent] Client capabilities: ${JSON.stringify(params.clientCapabilities)}`
);
// 保存客户端能力,用于后续判断是否使用 IDE 的文件系统
this.clientCapabilities = params.clientCapabilities;
return {
protocolVersion: PROTOCOL_VERSION,
agentCapabilities: {
// 暂不支持加载历史会话(后续可以实现)
loadSession: false,
// 支持的提示能力
promptCapabilities: {
image: true, // 支持图片
audio: false, // 暂不支持音频
embeddedContext: true, // 支持嵌入上下文
},
// MCP 能力(Blade 已有 MCP 支持)
mcpCapabilities: {
http: true,
sse: true,
},
},
};
}
/**
* 认证(Blade 目前不需要认证)
*/
async authenticate(
_params: acp.AuthenticateRequest
): Promise<acp.AuthenticateResponse | void> {
// Blade 使用环境变量中的 API Key,不需要额外认证
return;
}
/**
* 创建新会话
*/
async newSession(params: acp.NewSessionRequest): Promise<acp.NewSessionResponse> {
const sessionId = nanoid();
logger.info(`[BladeAgent] Creating new session: ${sessionId}`);
logger.debug(`[BladeAgent] Session cwd: ${params.cwd || process.cwd()}`);
// 创建会话实例
const session = new AcpSession(
sessionId,
params.cwd || process.cwd(),
this.connection,
this.clientCapabilities
);
// 初始化会话(创建 Agent 等)
await session.initialize();
this.sessions.set(sessionId, session);
logger.info(
`[BladeAgent] Session ${sessionId} created, scheduling available commands update`
);
// 延迟发送 available_commands_update,确保在响应后
session.sendAvailableCommandsDelayed();
// 获取配置中的模型列表
const config = getConfig();
const models = config?.models || [];
const currentModelId = config?.currentModelId || models[0]?.id;
// 构建可用模型列表(不稳定 API)
const availableModels: acp.ModelInfo[] = models.map((m) => ({
modelId: m.id,
name: m.name || m.id,
description: m.provider ? `Provider: ${m.provider}` : undefined,
}));
// 构建可用模式列表(权限模式)
const availableModes: acp.SessionMode[] = [
{
id: 'default',
name: 'Default',
description: 'Ask for confirmation before all file edits and commands',
},
{
id: 'auto-edit',
name: 'Auto Edit',
description: 'Auto-approve file edits, ask for shell commands',
},
{
id: 'yolo',
name: 'Full Auto',
description: 'Auto-approve everything without confirmation',
},
{
id: 'plan',
name: 'Plan Only',
description: 'Read-only mode, no file changes or commands',
},
];
return {
sessionId,
// 返回可用模式(权限控制)
modes: {
availableModes,
currentModeId: 'default',
},
// 返回可用模型(不稳定 API)
models:
availableModels.length > 0
? {
availableModels,
currentModelId,
}
: undefined,
};
}
/**
* 处理提示请求
*/
async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
const session = this.sessions.get(params.sessionId);
if (!session) {
throw new Error(`Session not found: ${params.sessionId}`);
}
return session.prompt(params);
}
/**
* 取消当前操作
*/
async cancel(params: acp.CancelNotification): Promise<void> {
logger.info(
`[BladeAgent] Cancel notification received for session: ${params.sessionId}`
);
const session = this.sessions.get(params.sessionId);
if (session) {
logger.info(`[BladeAgent] Found session, calling session.cancel()`);
session.cancel();
} else {
logger.warn(`[BladeAgent] Session not found for cancel: ${params.sessionId}`);
}
}
/**
* 设置会话模式(权限模式)
*/
async setSessionMode(
params: acp.SetSessionModeRequest
): Promise<acp.SetSessionModeResponse> {
logger.info(`[BladeAgent] Setting session mode: ${params.modeId}`);
const session = this.sessions.get(params.sessionId);
if (session) {
await session.setMode(params.modeId);
}
return {};
}
/**
* 设置会话模型(不稳定 API)
*/
async unstable_setSessionModel?(
params: acp.SetSessionModelRequest
): Promise<acp.SetSessionModelResponse> {
logger.info(`[BladeAgent] Setting session model: ${params.modelId}`);
const session = this.sessions.get(params.sessionId);
if (session) {
await session.setModel(params.modelId);
}
return {};
}
/**
* 清理资源
*/
async destroy(): Promise<void> {
for (const session of this.sessions.values()) {
await session.destroy();
}
this.sessions.clear();
await McpRegistry.getInstance().disconnectAll();
}
}