@tencent-ai/cloud-agent-sdk 0.2.14-next.e4d2ba9.20260321 → 0.2.15-next.2d2b062.20260324

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/dist/index.cjs CHANGED
@@ -963,6 +963,7 @@ function streamableHttp(options) {
963
963
  const headers = buildHeaders();
964
964
  headers["Accept"] = "text/event-stream";
965
965
  if (lastEventId) headers["Last-Event-ID"] = lastEventId;
966
+ if (connectionId) headers["Acp-Connection-Id"] = connectionId;
966
967
  const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
967
968
  const connectController = new AbortController();
968
969
  const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
@@ -1182,6 +1183,10 @@ var ArtifactManager = class {
1182
1183
  */
1183
1184
  const DEFAULT_INITIALIZE_TIMEOUT = 3e4;
1184
1185
  /**
1186
+ * Default timeout for session/new operation (waiting for sessionId via SSE)
1187
+ */
1188
+ const DEFAULT_SESSION_TIMEOUT = 3e4;
1189
+ /**
1185
1190
  * Default timeout for permission requests
1186
1191
  */
1187
1192
  const DEFAULT_PERMISSION_TIMEOUT = 3e5;
@@ -1237,8 +1242,14 @@ var InitializationError = class extends ACPClientError {
1237
1242
  * Error thrown for session-related failures
1238
1243
  */
1239
1244
  var SessionError = class extends ACPClientError {
1240
- constructor(message, sessionId, cause) {
1241
- super(message, "SESSION_ERROR", cause);
1245
+ constructor(message, sessionId, agentIdOrCause, cause) {
1246
+ if (agentIdOrCause instanceof Error) {
1247
+ super(message, "SESSION_ERROR", agentIdOrCause);
1248
+ this.agentId = void 0;
1249
+ } else {
1250
+ super(message, "SESSION_ERROR", cause);
1251
+ this.agentId = agentIdOrCause;
1252
+ }
1242
1253
  this.name = "SessionError";
1243
1254
  this.sessionId = sessionId;
1244
1255
  }
@@ -2000,16 +2011,31 @@ var StreamableHttpClient = class {
2000
2011
  *
2001
2012
  * Retries on transient network errors (e.g., proxy connection reset)
2002
2013
  * since session/new is idempotent and safe to retry.
2014
+ *
2015
+ * A timeout (`options.sessionTimeout`, default 30 s) guards against the
2016
+ * SSE response never arriving — the POST returns 202 immediately and the
2017
+ * sessionId comes back via GET SSE, which can hang indefinitely.
2018
+ * On timeout a `SessionError` is thrown.
2003
2019
  */
2004
2020
  async createSession(cwd) {
2005
2021
  this.ensureInitialized("createSession");
2006
2022
  const maxRetries = 2;
2023
+ const timeout = this.options.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;
2007
2024
  let lastError;
2008
2025
  for (let attempt = 0; attempt <= maxRetries; attempt++) try {
2009
- const response = await this.connection.newSession({
2026
+ const sessionPromise = this.connection.newSession({
2010
2027
  cwd,
2011
2028
  mcpServers: []
2012
2029
  });
2030
+ let response;
2031
+ if (timeout > 0) {
2032
+ const timeoutPromise = new Promise((_, reject) => {
2033
+ setTimeout(() => {
2034
+ reject(new SessionError(`session/new timed out after ${timeout}ms`));
2035
+ }, timeout);
2036
+ });
2037
+ response = await Promise.race([sessionPromise, timeoutPromise]);
2038
+ } else response = await sessionPromise;
2013
2039
  this.options.logger?.info(`Session created: ${response.sessionId}`);
2014
2040
  return response;
2015
2041
  } catch (err) {
@@ -18100,226 +18126,3654 @@ var CloudAgentProvider = class CloudAgentProvider {
18100
18126
  };
18101
18127
 
18102
18128
  //#endregion
18103
- //#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
18129
+ //#region ../agent-provider/src/common/providers/local-agent-provider/acp/json-rpc.ts
18104
18130
  /**
18105
- * Local Agent Connection
18106
- * Wraps AcpJsonRpcClient to implement AgentConnection interface
18131
+ * JSON-RPC 编解码器
18107
18132
  *
18108
- * Uses IWidgetChannel for IPC communication with ExtensionHost
18109
- * Migrated from ipc-agent-provider for unified local agent access
18133
+ * 提供 JSON-RPC 2.0 消息的编码和解码功能
18110
18134
  */
18111
-
18112
- //#endregion
18113
- //#region ../agent-provider/src/common/client/session.ts
18135
+ var JsonRpcEncoder = class {
18136
+ /**
18137
+ * 编码请求消息
18138
+ * @param id 请求 ID
18139
+ * @param method 方法名
18140
+ * @param params 参数 (可选)
18141
+ * @returns AcpRpcEnvelope 包装的 JSON-RPC 请求
18142
+ */
18143
+ static encodeRequest(id, method, params) {
18144
+ return {
18145
+ type: "acp-rpc",
18146
+ payload: {
18147
+ jsonrpc: "2.0",
18148
+ id,
18149
+ method,
18150
+ params
18151
+ }
18152
+ };
18153
+ }
18154
+ /**
18155
+ * 编码通知消息 (无响应)
18156
+ * @param method 方法名
18157
+ * @param params 参数 (可选)
18158
+ * @returns AcpRpcEnvelope 包装的 JSON-RPC 通知
18159
+ */
18160
+ static encodeNotification(method, params) {
18161
+ return {
18162
+ type: "acp-rpc",
18163
+ payload: {
18164
+ jsonrpc: "2.0",
18165
+ method,
18166
+ params
18167
+ }
18168
+ };
18169
+ }
18170
+ /**
18171
+ * 编码成功响应
18172
+ * @param id 对应请求的 ID
18173
+ * @param result 结果数据
18174
+ * @returns AcpRpcEnvelope 包装的 JSON-RPC 响应
18175
+ */
18176
+ static encodeSuccessResponse(id, result) {
18177
+ return {
18178
+ type: "acp-rpc",
18179
+ payload: {
18180
+ jsonrpc: "2.0",
18181
+ id,
18182
+ result
18183
+ }
18184
+ };
18185
+ }
18186
+ /**
18187
+ * 编码错误响应
18188
+ * @param id 对应请求的 ID
18189
+ * @param error 错误信息
18190
+ * @returns AcpRpcEnvelope 包装的 JSON-RPC 错误响应
18191
+ */
18192
+ static encodeErrorResponse(id, error) {
18193
+ return {
18194
+ type: "acp-rpc",
18195
+ payload: {
18196
+ jsonrpc: "2.0",
18197
+ id,
18198
+ error
18199
+ }
18200
+ };
18201
+ }
18202
+ /**
18203
+ * 解码 ACP RPC 消息
18204
+ * @param envelope AcpRpcEnvelope
18205
+ * @returns 解码后的 JSON-RPC 消息
18206
+ * @throws 如果消息格式无效
18207
+ */
18208
+ static decode(envelope) {
18209
+ if (!this.isAcpRpcMessage(envelope)) throw new Error("Invalid ACP RPC message format");
18210
+ const payload = envelope.payload;
18211
+ if (typeof payload !== "object" || payload === null) throw new Error("Invalid JSON-RPC payload");
18212
+ if (payload.jsonrpc !== "2.0") throw new Error("Invalid JSON-RPC version (must be \"2.0\")");
18213
+ const hasId = "id" in payload;
18214
+ const hasMethod = typeof payload.method === "string";
18215
+ if (!hasId && !hasMethod) throw new Error("Invalid JSON-RPC message (must have id or method)");
18216
+ return payload;
18217
+ }
18218
+ /**
18219
+ * 检查是否为 ACP RPC 消息
18220
+ * @param message 待检查的消息
18221
+ * @returns 是否为有效的 AcpRpcEnvelope
18222
+ */
18223
+ static isAcpRpcMessage(message) {
18224
+ return typeof message === "object" && message !== null && "type" in message && message.type === "acp-rpc" && "payload" in message;
18225
+ }
18226
+ /**
18227
+ * 检查是否为 JSON-RPC 请求 (有 id 字段)
18228
+ * @param message JSON-RPC 消息
18229
+ * @returns 是否为请求消息
18230
+ */
18231
+ static isRequest(message) {
18232
+ return "id" in message && "method" in message;
18233
+ }
18234
+ /**
18235
+ * 检查是否为 JSON-RPC 响应 (有 id 字段,有 result 或 error)
18236
+ * @param message JSON-RPC 消息
18237
+ * @returns 是否为响应消息
18238
+ */
18239
+ static isResponse(message) {
18240
+ return "id" in message && ("result" in message || "error" in message);
18241
+ }
18242
+ /**
18243
+ * 检查是否为 JSON-RPC 通知 (无 id 字段)
18244
+ * @param message JSON-RPC 消息
18245
+ * @returns 是否为通知消息
18246
+ */
18247
+ static isNotification(message) {
18248
+ return !("id" in message) && "method" in message;
18249
+ }
18250
+ };
18114
18251
  /**
18115
- * ActiveSessionImpl - Implements the ActiveSession interface
18116
- *
18117
- * This class wraps an AgentConnection and provides the session-centric API.
18118
- * It is created by SessionManager when creating or loading sessions.
18119
- *
18120
- * @example
18121
- * ```typescript
18122
- * // Created by client.sessions.new() or client.sessions.load()
18123
- * const session = await client.sessions.new({ cwd: '/workspace' });
18124
- *
18125
- * // Access agent state
18126
- * console.log(session.agentState.status);
18127
- *
18128
- * // Send prompt
18129
- * const response = await session.prompts.send({ content: 'Hello!' });
18252
+ * 请求 ID 生成器
18130
18253
  *
18131
- * // Cleanup
18132
- * session.disconnect();
18133
- * ```
18254
+ * 格式: acp_{序号}_{时间戳}
18255
+ * 示例: acp_1_1704067200000
18134
18256
  */
18135
- var ActiveSessionImpl = class {
18257
+ var RequestIdGenerator = class {
18258
+ constructor() {
18259
+ this.counter = 0;
18260
+ }
18136
18261
  /**
18137
- * Create an ActiveSessionImpl instance
18138
- *
18139
- * @param sessionId - Session ID
18140
- * @param agentId - Agent ID
18141
- * @param connection - Already connected AgentConnection
18142
- * @param options - Additional options
18262
+ * 生成下一个请求 ID
18263
+ * @returns 请求 ID 字符串
18143
18264
  */
18144
- constructor(sessionId, agentId, connection, options = {}) {
18145
- this._availableCommands = [];
18146
- this.listeners = /* @__PURE__ */ new Map();
18147
- this.onceListeners = /* @__PURE__ */ new Map();
18148
- this.connectionListeners = [];
18149
- this._id = sessionId;
18150
- this._agentId = agentId;
18151
- this.connection = connection;
18152
- this.logger = options.logger;
18153
- this._getFilesystem = options.getFilesystem;
18154
- this._connectionInfo = options.connectionInfo;
18155
- this.setupConnectionEvents(connection);
18156
- this.agent = this.createAgentOperations();
18157
- this.prompts = this.createPromptsResource();
18158
- this.artifacts = this.createArtifactsResource();
18159
- this.files = this.createFilesResource();
18265
+ generate() {
18266
+ return `acp_${++this.counter}`;
18160
18267
  }
18161
18268
  /**
18162
- * Session ID
18269
+ * 重置计数器
18163
18270
  */
18164
- get id() {
18165
- return this._id;
18271
+ reset() {
18272
+ this.counter = 0;
18273
+ }
18274
+ };
18275
+
18276
+ //#endregion
18277
+ //#region ../agent-provider/src/common/providers/local-agent-provider/acp/types.ts
18278
+ /**
18279
+ * 特殊 sessionId 常量
18280
+ * 这些 sessionId 不需要路由到特定窗口
18281
+ */
18282
+ const SPECIAL_SESSION_IDS = {
18283
+ GLOBAL: "__global__",
18284
+ NEW: "__new__",
18285
+ WORKSPACE: "__workspace__",
18286
+ BROADCAST: "__broadcast__"
18287
+ };
18288
+ /**
18289
+ * 检查是否为保留 sessionId
18290
+ * @param sessionId 会话 ID
18291
+ * @returns 是否为保留 sessionId
18292
+ */
18293
+ function isReservedSessionId(sessionId) {
18294
+ return Object.values(SPECIAL_SESSION_IDS).includes(sessionId) && sessionId !== SPECIAL_SESSION_IDS.BROADCAST;
18295
+ }
18296
+
18297
+ //#endregion
18298
+ //#region ../agent-provider/src/common/providers/local-agent-provider/acp/acp-json-rpc-client.ts
18299
+ /**
18300
+ * ACP JSON-RPC Client 实现
18301
+ *
18302
+ * 实现 IAcpJsonRpcClient 接口
18303
+ */
18304
+ var AcpJsonRpcClient = class {
18305
+ constructor(channel, config = {}) {
18306
+ this.requestIdGenerator = new RequestIdGenerator();
18307
+ this.pendingRequests = /* @__PURE__ */ new Map();
18308
+ this.sessionUpdateCallbacks = /* @__PURE__ */ new Set();
18309
+ this.permissionRequestResolvers = /* @__PURE__ */ new Map();
18310
+ this.extNotificationCallbacks = /* @__PURE__ */ new Set();
18311
+ this.eventHandlers = [];
18312
+ this.channel = channel;
18313
+ this.debug = true;
18314
+ this.setupMessageListener();
18315
+ this.log("AcpJsonRpcClient initialized");
18316
+ }
18317
+ async initialize(request) {
18318
+ this.log("initialize called");
18319
+ return await this.sendRequest(SPECIAL_SESSION_IDS.GLOBAL, "initialize", request);
18320
+ }
18321
+ async authenticate(request) {
18322
+ this.log("authenticate called");
18323
+ await this.sendRequest(SPECIAL_SESSION_IDS.GLOBAL, "authenticate", request);
18324
+ }
18325
+ async newSession(request) {
18326
+ this.log("newSession called");
18327
+ return await this.sendRequest(SPECIAL_SESSION_IDS.NEW, "newSession", request);
18328
+ }
18329
+ async loadSession(request) {
18330
+ this.log(`[AcpJsonRpcClient] loadSession called:, ${request.sessionId}`);
18331
+ const response = await this.sendRequest(request.sessionId, "loadSession", request);
18332
+ this.log(`[AcpJsonRpcClient] loadSession response:, ${response}`);
18333
+ return response;
18334
+ }
18335
+ async prompt(request) {
18336
+ this.log("prompt called for session:", request.sessionId);
18337
+ return await this.sendRequest(request.sessionId, "prompt", request);
18338
+ }
18339
+ async cancel(params) {
18340
+ this.log("cancel called for session:", params.sessionId);
18341
+ const envelope = JsonRpcEncoder.encodeNotification("cancel", params);
18342
+ this.send(params.sessionId, envelope);
18343
+ }
18344
+ async setSessionModel(params) {
18345
+ this.log("setSessionModel called for session:", params.sessionId);
18346
+ return await this.sendRequest(params.sessionId, "setSessionModel", params);
18347
+ }
18348
+ async setSessionMode(params) {
18349
+ this.log("setSessionMode called for session:", params.sessionId);
18350
+ return await this.sendRequest(params.sessionId, "setSessionMode", params);
18351
+ }
18352
+ async toolCallback(request) {
18353
+ this.log("toolCallback called for session:", request.sessionId, "action:", request.action);
18354
+ return await this.sendRequest(request.sessionId, "toolCallback", request);
18166
18355
  }
18167
18356
  /**
18168
- * Agent ID
18357
+ * 打开工作区窗口
18358
+ * 使用 __workspace__ session ID,由 Main Process 直接处理
18359
+ * @param request 打开工作区请求
18360
+ * @returns 打开工作区响应
18169
18361
  */
18170
- get agentId() {
18171
- return this._agentId;
18362
+ async openWorkspace(request) {
18363
+ this.log("openWorkspace called", request);
18364
+ return await this.sendRequest(SPECIAL_SESSION_IDS.WORKSPACE, "openWorkspace", request);
18172
18365
  }
18173
18366
  /**
18174
- * Actual workspace path (set from newSession response _meta)
18367
+ * 发送全局请求
18368
+ * 使用 __global__ session ID,会根据 params.cwd 路由到对应窗口
18369
+ * cwd 为空时会路由到 Default Window
18370
+ * @param method 方法名
18371
+ * @param params 请求参数
18372
+ * @returns 响应结果
18175
18373
  */
18176
- get cwd() {
18177
- return this._cwd;
18374
+ async sendBroadcastRequest(method, params) {
18375
+ this.log(`sendGlobalRequest called: ${method}`, params);
18376
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, method, params);
18178
18377
  }
18179
18378
  /**
18180
- * Set actual workspace path (called by SessionManager after createSession)
18379
+ * 获取当前工作区列表
18380
+ * 使用 __workspace__ session ID,由 Main Process 直接处理
18381
+ * @param request 获取工作区列表请求
18382
+ * @returns 获取工作区列表响应
18181
18383
  */
18182
- setCwd(cwd) {
18183
- this._cwd = cwd;
18384
+ async getCurrentWorkspaces(request) {
18385
+ this.log("getCurrentWorkspaces called", request);
18386
+ return await this.sendRequest(SPECIAL_SESSION_IDS.WORKSPACE, "getCurrentWorkspaces", request ?? {});
18184
18387
  }
18185
18388
  /**
18186
- * Agent state (live connection state)
18187
- * Returns LocalAgentState or CloudAgentState based on transport type
18389
+ * 获取已安装插件列表
18390
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18391
+ * @param request 获取插件请求
18392
+ * @returns 已安装插件列表响应
18188
18393
  */
18189
- get agentState() {
18190
- return {
18191
- id: this._agentId,
18192
- status: this.connection.state,
18193
- capabilities: this.connection.capabilities,
18194
- type: this.connection.transport,
18195
- cwd: this.connection.cwd || ""
18196
- };
18394
+ async getInstalledPlugins(request) {
18395
+ this.log("getInstalledPlugins called", request);
18396
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getInstalledPlugins", request ?? {});
18197
18397
  }
18198
18398
  /**
18199
- * Get agent capabilities (available after connection)
18399
+ * 安装插件
18400
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18401
+ * @param request 安装插件请求
18402
+ * @returns 安装结果
18200
18403
  */
18201
- get capabilities() {
18202
- return this.connection.capabilities;
18404
+ async installPlugins(request) {
18405
+ this.log("installPlugins called", request);
18406
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "installPlugins", request);
18203
18407
  }
18204
18408
  /**
18205
- * Available session modes
18409
+ * 卸载插件
18410
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18411
+ * @param request 卸载插件请求
18412
+ * @returns 卸载结果
18206
18413
  */
18207
- get availableModes() {
18208
- return this._availableModes;
18414
+ async uninstallPlugin(request) {
18415
+ this.log("uninstallPlugin called", request);
18416
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "uninstallPlugin", request);
18209
18417
  }
18210
18418
  /**
18211
- * Current session mode
18419
+ * 更新插件到最新版本
18420
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18421
+ * @param request 更新插件请求
18422
+ * @returns 更新结果
18212
18423
  */
18213
- get currentMode() {
18214
- return this._currentMode;
18424
+ async updatePlugin(request) {
18425
+ this.log("updatePlugin called", request);
18426
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "updatePlugin", request);
18215
18427
  }
18216
18428
  /**
18217
- * Available models for this session
18429
+ * 批量切换插件状态
18430
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18431
+ * @param request 批量切换插件请求
18432
+ * @returns 批量操作结果
18218
18433
  */
18219
- get availableModels() {
18220
- return this._availableModels;
18434
+ async batchTogglePlugins(request) {
18435
+ this.log("batchTogglePlugins called", request);
18436
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "batchTogglePlugins", request);
18221
18437
  }
18222
18438
  /**
18223
- * Current model ID
18439
+ * 获取插件市场列表
18440
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18441
+ * @param request 获取市场列表请求
18442
+ * @returns 插件市场列表响应
18224
18443
  */
18225
- get currentModelId() {
18226
- return this._currentModelId;
18444
+ async getPluginMarketplaces(request) {
18445
+ this.log("getPluginMarketplaces called", request);
18446
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getPluginMarketplaces", request ?? {});
18227
18447
  }
18228
18448
  /**
18229
- * Available slash commands
18230
- *
18231
- * When Agent sends available_commands_update, this list is automatically updated.
18232
- * Commands can be accessed directly without waiting for events.
18449
+ * 获取市场下的插件列表
18450
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18451
+ * @param request 获取市场插件请求
18452
+ * @returns 市场插件列表响应
18233
18453
  */
18234
- get availableCommands() {
18235
- return this._availableCommands;
18454
+ async getMarketplacePlugins(request) {
18455
+ this.log("getMarketplacePlugins called", request);
18456
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getMarketplacePlugins", request);
18236
18457
  }
18237
18458
  /**
18238
- * Set available commands (called when available_commands_update is received)
18459
+ * 获取插件详情
18460
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18461
+ * @param request 获取插件详情请求
18462
+ * @returns 插件详情响应
18239
18463
  */
18240
- setAvailableCommands(commands) {
18241
- this._availableCommands = commands;
18242
- this.logger?.info(`Session ${this._id}: Available commands updated, count: ${commands.length}`);
18464
+ async getPluginDetail(request) {
18465
+ this.log("getPluginDetail called", request);
18466
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getPluginDetail", request);
18243
18467
  }
18244
18468
  /**
18245
- * Check if the session is active
18469
+ * 添加插件市场
18470
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18471
+ * @param request 添加市场请求
18472
+ * @returns 添加结果
18246
18473
  */
18247
- get isActive() {
18248
- return this.connection.isInitialized;
18474
+ async addPluginMarketplace(request) {
18475
+ this.log("addPluginMarketplace called", request);
18476
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "addPluginMarketplace", request);
18249
18477
  }
18250
18478
  /**
18251
- * Session connection information (only available for cloud sessions)
18252
- * 会话连接信息,包括sandboxId、link、token等
18479
+ * 删除插件市场
18480
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18481
+ * @param request 删除市场请求
18482
+ * @returns 删除结果
18253
18483
  */
18254
- get connectionInfo() {
18255
- return this._connectionInfo;
18484
+ async removePluginMarketplace(request) {
18485
+ this.log("removePluginMarketplace called", request);
18486
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "removePluginMarketplace", request);
18256
18487
  }
18257
18488
  /**
18258
- * Set session modes (called after create/load)
18489
+ * 刷新插件市场
18490
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18491
+ * @param request 刷新市场请求
18492
+ * @returns 刷新结果
18259
18493
  */
18260
- setModes(availableModes, currentMode) {
18261
- this._availableModes = availableModes;
18262
- this._currentMode = currentMode;
18494
+ async refreshPluginMarketplace(request) {
18495
+ this.log("refreshPluginMarketplace called", request);
18496
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "refreshPluginMarketplace", request);
18263
18497
  }
18264
18498
  /**
18265
- * Set available models (called after create/load)
18499
+ * 用新窗口打开文件夹
18500
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18501
+ *
18502
+ * @param request 打开文件夹请求
18266
18503
  */
18267
- setModels(availableModels, currentModelId) {
18268
- this._availableModels = availableModels;
18269
- this._currentModelId = currentModelId;
18504
+ async openFolderInNewWindow(request) {
18505
+ this.log("openFolderInNewWindow called", request);
18506
+ await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "openFolderInNewWindow", request);
18270
18507
  }
18271
- createAgentOperations() {
18272
- const self = this;
18273
- return {
18274
- get id() {
18275
- return self._agentId;
18276
- },
18277
- get state() {
18278
- return self.agentState;
18279
- },
18280
- get isConnected() {
18281
- return self.connection.isInitialized;
18282
- },
18283
- get capabilities() {
18284
- return self.connection.capabilities;
18285
- }
18508
+ /**
18509
+ * 获取支持的场景列表(从后端 API)
18510
+ * 使用 __broadcast__ session ID,由 Extension Host 处理
18511
+ * 用于 Welcome 页面的 QuickActions 快捷操作
18512
+ *
18513
+ * 调用链:
18514
+ * 1. AgentNewAdapter.getTemplates() 调用此方法
18515
+ * 2. 通过 ACP 协议发送请求到 Extension Host
18516
+ * 3. Extension Host 调用 RestOperations -> GET /v2/as/support/scenes
18517
+ * 4. 返回 SupportSceneInfo[] 数据
18518
+ *
18519
+ * @param request 获取支持场景请求
18520
+ * @returns 支持的场景列表响应
18521
+ */
18522
+ async getSupportScenes(request) {
18523
+ this.log("getSupportScenes called", request);
18524
+ return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getSupportScenes", request ?? {});
18525
+ }
18526
+ getAutomationBridge() {
18527
+ return globalThis.__codebuddyAutomationBridge;
18528
+ }
18529
+ requireAutomationBridge() {
18530
+ const bridge = this.getAutomationBridge();
18531
+ if (!bridge) throw new Error("Automation bridge is unavailable; automation now only supports the Main/Renderer orchestration path");
18532
+ return bridge;
18533
+ }
18534
+ async getAutomationSnapshot() {
18535
+ this.log("getAutomationSnapshot called");
18536
+ const bridge = this.requireAutomationBridge();
18537
+ if (!bridge.getAutomationSnapshot) throw new Error("Automation bridge does not implement getAutomationSnapshot");
18538
+ return await bridge.getAutomationSnapshot();
18539
+ }
18540
+ async updateAutomation(request) {
18541
+ this.log("updateAutomation called", request);
18542
+ const bridge = this.requireAutomationBridge();
18543
+ if (!bridge.updateAutomation) throw new Error("Automation bridge does not implement updateAutomation");
18544
+ return await bridge.updateAutomation(request);
18545
+ }
18546
+ async deleteAutomation(request) {
18547
+ this.log("deleteAutomation called", request);
18548
+ const bridge = this.requireAutomationBridge();
18549
+ if (!bridge.deleteAutomation) throw new Error("Automation bridge does not implement deleteAutomation");
18550
+ return await bridge.deleteAutomation(request.id);
18551
+ }
18552
+ async archiveAutomationInboxItem(request) {
18553
+ this.log("archiveAutomationInboxItem called", request);
18554
+ const bridge = this.requireAutomationBridge();
18555
+ if (!bridge.archiveAutomationInboxItem) throw new Error("Automation bridge does not implement archiveAutomationInboxItem");
18556
+ return await bridge.archiveAutomationInboxItem(request.itemId);
18557
+ }
18558
+ async deleteAutomationInboxItem(request) {
18559
+ this.log("deleteAutomationInboxItem called", request);
18560
+ const bridge = this.requireAutomationBridge();
18561
+ if (!bridge.deleteAutomationInboxItem) throw new Error("Automation bridge does not implement deleteAutomationInboxItem");
18562
+ return await bridge.deleteAutomationInboxItem(request.itemId);
18563
+ }
18564
+ async testAutomation(request) {
18565
+ this.log("testAutomation called", request);
18566
+ const bridge = this.requireAutomationBridge();
18567
+ if (!bridge.testAutomation) throw new Error("Automation bridge does not implement testAutomation");
18568
+ return await bridge.testAutomation(request.id);
18569
+ }
18570
+ onSessionUpdate(callback) {
18571
+ this.sessionUpdateCallbacks.add(callback);
18572
+ this.log("onSessionUpdate: callback registered");
18573
+ return () => {
18574
+ this.sessionUpdateCallbacks.delete(callback);
18575
+ this.log("onSessionUpdate: callback unregistered");
18286
18576
  };
18287
18577
  }
18288
- createPromptsResource() {
18289
- return {
18290
- send: async (params) => {
18291
- const response = await this.getConnectionOrThrow().prompt(this._id, params);
18292
- return this.mapPromptResponse(response);
18293
- },
18294
- stream: (params) => {
18295
- return this.getConnectionOrThrow().promptStream(this._id, params);
18296
- },
18297
- cancel: async () => {
18298
- await this.getConnectionOrThrow().cancel(this._id);
18299
- }
18578
+ onRequestPermission(sessionId, callback) {
18579
+ this.permissionRequestResolvers.set(sessionId, callback);
18580
+ this.log("onRequestPermission: callback registered");
18581
+ return () => {
18582
+ this.permissionRequestResolvers.delete(sessionId);
18583
+ this.log("onRequestPermission: callback unregistered");
18300
18584
  };
18301
18585
  }
18302
- createArtifactsResource() {
18303
- const notSupported = () => {
18304
- throw new Error("Artifact management is no longer supported through this API");
18305
- };
18306
- return {
18307
- list: async (_params) => {
18308
- notSupported();
18309
- return [];
18310
- },
18311
- retrieve: async (_artifactId) => {
18312
- notSupported();
18313
- },
18314
- content: async (_artifactId) => {
18315
- notSupported();
18316
- return "";
18317
- }
18586
+ /**
18587
+ * 监听 extNotification 推送
18588
+ * 用于接收 artifact 等扩展通知
18589
+ */
18590
+ onExtNotification(callback) {
18591
+ this.extNotificationCallbacks.add(callback);
18592
+ this.log("onExtNotification: callback registered");
18593
+ return () => {
18594
+ this.extNotificationCallbacks.delete(callback);
18595
+ this.log("onExtNotification: callback unregistered");
18318
18596
  };
18319
18597
  }
18320
- /**
18321
- * Create files resource with lazy-loaded filesystem
18322
- *
18598
+ destroy() {
18599
+ this.log("Destroying AcpJsonRpcClient");
18600
+ this.eventHandlers.forEach(({ event, handler }) => {
18601
+ this.channel.off(event, handler);
18602
+ });
18603
+ this.eventHandlers.length = 0;
18604
+ this.pendingRequests.forEach(({ reject }) => {
18605
+ reject(/* @__PURE__ */ new Error("Client destroyed"));
18606
+ });
18607
+ this.pendingRequests.clear();
18608
+ this.sessionUpdateCallbacks.clear();
18609
+ this.permissionRequestResolvers.clear();
18610
+ }
18611
+ /**
18612
+ * 发送 JSON-RPC 请求并等待响应
18613
+ * @param sessionId 会话 ID
18614
+ * @param method 方法名
18615
+ * @param params 参数
18616
+ * @returns Promise<T> 响应结果
18617
+ */
18618
+ async sendRequest(sessionId, method, params) {
18619
+ const id = this.requestIdGenerator.generate();
18620
+ const envelope = JsonRpcEncoder.encodeRequest(id, method, {
18621
+ sessionId: isReservedSessionId(sessionId) ? void 0 : sessionId,
18622
+ ...params ?? {}
18623
+ });
18624
+ return new Promise(async (resolve, reject) => {
18625
+ this.pendingRequests.set(id, {
18626
+ resolve,
18627
+ reject,
18628
+ method,
18629
+ sessionId
18630
+ });
18631
+ if (!await this.send(sessionId, envelope)) {
18632
+ this.pendingRequests.delete(id);
18633
+ reject(/* @__PURE__ */ new Error(`Failed to send ${method} request for session: ${sessionId}`));
18634
+ }
18635
+ this.log(`Request sent: ${method} (id: ${id}, session: ${sessionId})`);
18636
+ });
18637
+ }
18638
+ /**
18639
+ * 发送消息到指定 session
18640
+ * @param sessionId 会话 ID
18641
+ * @param envelope ACP RPC 消息包装
18642
+ * @returns 是否发送成功
18643
+ */
18644
+ send(sessionId, envelope) {
18645
+ return this.channel.sendNotification(sessionId, envelope);
18646
+ }
18647
+ /**
18648
+ * 设置消息监听器
18649
+ * 处理来自 ExtensionHost 的 JSON-RPC 响应和推送
18650
+ */
18651
+ setupMessageListener() {
18652
+ const responseHandler = (data) => {
18653
+ if (!JsonRpcEncoder.isAcpRpcMessage(data)) return;
18654
+ try {
18655
+ const message = JsonRpcEncoder.decode(data);
18656
+ if (JsonRpcEncoder.isResponse(message)) this.handleResponse(message);
18657
+ else if (JsonRpcEncoder.isNotification(message)) this.handleNotification(message);
18658
+ else if (JsonRpcEncoder.isRequest(message)) this.handleNotification(message);
18659
+ } catch (error) {
18660
+ this.log("Error handling message:", error);
18661
+ }
18662
+ };
18663
+ this.channel.on("acp-rpc-response", responseHandler);
18664
+ this.eventHandlers.push({
18665
+ event: "acp-rpc-response",
18666
+ handler: responseHandler
18667
+ });
18668
+ this.channel.on("message", responseHandler);
18669
+ this.eventHandlers.push({
18670
+ event: "message",
18671
+ handler: responseHandler
18672
+ });
18673
+ this.log("Message listener setup complete");
18674
+ }
18675
+ /**
18676
+ * 处理 JSON-RPC 响应
18677
+ */
18678
+ handleResponse(response) {
18679
+ const pending = this.pendingRequests.get(response.id);
18680
+ if (!pending) {
18681
+ this.log(`No pending request found for response id: ${response.id}`);
18682
+ return;
18683
+ }
18684
+ this.pendingRequests.delete(response.id);
18685
+ if (response.error) {
18686
+ const error = new Error(response.error.message);
18687
+ error.code = response.error.code;
18688
+ pending.reject(error);
18689
+ } else pending.resolve(response.result);
18690
+ this.log(`Response received for request: ${pending.method} (id: ${response.id})`);
18691
+ }
18692
+ /**
18693
+ * 处理 JSON-RPC 推送消息
18694
+ */
18695
+ handleNotification(notification) {
18696
+ const method = notification.method;
18697
+ let params = notification.params;
18698
+ if (notification.id) params = {
18699
+ ...params,
18700
+ requestId: notification.id
18701
+ };
18702
+ this.log(`Notification received: ${method}`);
18703
+ switch (method) {
18704
+ case "session/update":
18705
+ case "sessionUpdate":
18706
+ this.handleSessionUpdate(params);
18707
+ break;
18708
+ case "request/permission":
18709
+ case "requestPermission":
18710
+ this.handlePermissionRequest(params);
18711
+ break;
18712
+ case "extNotification":
18713
+ this.handleExtNotification(params);
18714
+ break;
18715
+ default: this.log(`Unknown notification method: ${method}`);
18716
+ }
18717
+ }
18718
+ /**
18719
+ * 处理 extNotification 推送
18720
+ */
18721
+ handleExtNotification(params) {
18722
+ if (!params) {
18723
+ this.log("extNotification missing params");
18724
+ return;
18725
+ }
18726
+ const method = params.method;
18727
+ const extParams = params.params || {};
18728
+ this.log("handleExtNotification:", {
18729
+ method,
18730
+ extParams
18731
+ });
18732
+ this.extNotificationCallbacks.forEach((callback) => {
18733
+ try {
18734
+ callback(method, extParams);
18735
+ } catch (error) {
18736
+ this.log("extNotification callback error:", error);
18737
+ }
18738
+ });
18739
+ }
18740
+ /**
18741
+ * 处理会话更新推送
18742
+ *
18743
+ * 支持 ACP 协议定义的多种 session update 类型:
18744
+ * - available_commands_update: 可用命令列表更新
18745
+ * - 其他类型: 原样转发给调用方
18746
+ */
18747
+ handleSessionUpdate(params) {
18748
+ if (!params) {
18749
+ this.log("Session update notification missing params");
18750
+ return;
18751
+ }
18752
+ const sessionId = params.sessionId;
18753
+ const messageId = params.messageId;
18754
+ if (!sessionId) {
18755
+ this.log("Invalid session update params:", params);
18756
+ return;
18757
+ }
18758
+ const update = params.update;
18759
+ if (update?.sessionUpdate === "available_commands_update") {
18760
+ this.log(`Available commands update received for session: ${sessionId}`);
18761
+ const rawCommands = update?.availableCommands;
18762
+ if (Array.isArray(rawCommands)) {
18763
+ const commands = rawCommands.filter((cmd) => cmd !== null && typeof cmd === "object" && typeof cmd.name === "string").map((cmd) => ({
18764
+ name: String(cmd.name),
18765
+ description: typeof cmd.description === "string" ? cmd.description : "",
18766
+ input: cmd.input && typeof cmd.input === "object" ? { hint: typeof cmd.input.hint === "string" ? cmd.input.hint : void 0 } : void 0
18767
+ }));
18768
+ this.log(`Parsed ${commands.length} available commands`);
18769
+ } else this.log("Invalid availableCommands format in available_commands_update");
18770
+ }
18771
+ const sessionUpdateParams = {
18772
+ notification: params,
18773
+ ...messageId && { messageId }
18774
+ };
18775
+ if (this.sessionUpdateCallbacks.size === 0) this.log(`handleSessionUpdate: no callbacks registered (expected for sharedAcpClient), type=${update?.sessionUpdate}, sessionId=${sessionId?.substring(0, 8)}`);
18776
+ this.sessionUpdateCallbacks.forEach((callback) => {
18777
+ try {
18778
+ callback(sessionUpdateParams);
18779
+ } catch (error) {
18780
+ this.log("Session update callback error:", error);
18781
+ }
18782
+ });
18783
+ }
18784
+ /**
18785
+ * 处理权限请求推送
18786
+ */
18787
+ async handlePermissionRequest(params) {
18788
+ if (!params) {
18789
+ this.log("Permission request notification missing params");
18790
+ return;
18791
+ }
18792
+ const sessionId = params.sessionId;
18793
+ const requestId = params.requestId;
18794
+ const toolCall = params.toolCall;
18795
+ const options = params.options;
18796
+ if (!sessionId || !requestId || !toolCall) {
18797
+ this.log("Invalid permission request params:", params);
18798
+ return;
18799
+ }
18800
+ const permissionParams = {
18801
+ sessionId,
18802
+ requestId,
18803
+ toolCall,
18804
+ options
18805
+ };
18806
+ const resolver = this.permissionRequestResolvers.get(sessionId);
18807
+ if (!resolver) {
18808
+ this.log("No permission resolver registered, cancelling...");
18809
+ return;
18810
+ }
18811
+ try {
18812
+ const outcome = await resolver(permissionParams);
18813
+ this.sendPermissionResponse(sessionId, requestId, outcome);
18814
+ } catch (error) {
18815
+ this.log("Permission resolver error:", error);
18816
+ this.sendPermissionResponse(sessionId, requestId, { outcome: "cancelled" });
18817
+ }
18818
+ }
18819
+ /**
18820
+ * 发送权限响应
18821
+ */
18822
+ sendPermissionResponse(sessionId, requestId, outcome) {
18823
+ const envelope = JsonRpcEncoder.encodeSuccessResponse(requestId, outcome);
18824
+ this.send(sessionId, envelope);
18825
+ this.log("Permission response sent for request:", requestId);
18826
+ }
18827
+ /**
18828
+ * 调试日志
18829
+ */
18830
+ log(...args) {
18831
+ if (this.debug) console.log("[AcpJsonRpcClient]", ...args);
18832
+ }
18833
+ };
18834
+
18835
+ //#endregion
18836
+ //#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
18837
+ /**
18838
+ * Local Agent Connection
18839
+ * Wraps AcpJsonRpcClient to implement AgentConnection interface
18840
+ *
18841
+ * Uses IWidgetChannel for IPC communication with ExtensionHost
18842
+ * Migrated from ipc-agent-provider for unified local agent access
18843
+ */
18844
+ /**
18845
+ * Local Agent Connection implementation
18846
+ * Uses AcpJsonRpcClient to communicate with ExtensionHost via IWidgetChannel
18847
+ *
18848
+ * Phase 1 Implementation:
18849
+ * - connect/disconnect: ✅ via initialize
18850
+ * - createSession/loadSession: ✅ via AcpJsonRpcClient
18851
+ * - prompt/cancel: ✅ via AcpJsonRpcClient
18852
+ * - Event forwarding: ✅ sessionUpdate, permissionRequest
18853
+ * - resolvePermission/rejectPermission: ✅ via callback
18854
+ *
18855
+ * Phase 2 (Not Implemented):
18856
+ * - promptStream: throws 'Not implemented'
18857
+ * - Artifact methods: return empty
18858
+ * - Question methods: return empty
18859
+ * - Extension methods: throw 'Not implemented'
18860
+ * - Filesystem methods: throw 'Not implemented'
18861
+ */
18862
+ var LocalAgentConnection = class {
18863
+ constructor(agentId, config) {
18864
+ this.listeners = /* @__PURE__ */ new Map();
18865
+ this.onceListeners = /* @__PURE__ */ new Map();
18866
+ this._state = "disconnected";
18867
+ this._isInitialized = false;
18868
+ this.pendingPermissions = /* @__PURE__ */ new Map();
18869
+ this.artifactCache = /* @__PURE__ */ new Map();
18870
+ this.transport = "local";
18871
+ this.onRequest = void 0;
18872
+ this.agentId = agentId;
18873
+ this.cwd = agentId;
18874
+ this.debug = config.debug ?? false;
18875
+ this.permissionTimeout = config.permissionTimeout ?? 3e4;
18876
+ this.permissionAutoRejectOnTimeout = config.permissionAutoRejectOnTimeout ?? false;
18877
+ this.acpClient = new AcpJsonRpcClient(config.channel, {
18878
+ timeoutMs: config.acpConfig?.timeoutMs ?? 3e4,
18879
+ debug: this.debug
18880
+ });
18881
+ this.setupEventForwarding();
18882
+ this.log("LocalAgentConnection initialized");
18883
+ }
18884
+ setupEventForwarding() {
18885
+ this.acpClient.onSessionUpdate((params) => {
18886
+ this.emit("sessionUpdate", params.notification);
18887
+ });
18888
+ this.acpClient.onExtNotification((method, params) => {
18889
+ console.log("[LocalConnection] Received extNotification:", {
18890
+ method,
18891
+ paramsKeys: Object.keys(params)
18892
+ });
18893
+ if (method === "_codebuddy.ai/artifact") {
18894
+ const event = params.event;
18895
+ const artifact = params.artifact;
18896
+ const notificationSessionId = params.sessionId;
18897
+ if (artifact?.uri) {
18898
+ if (event === "created" || event === "updated") this.artifactCache.set(artifact.uri, artifact);
18899
+ else if (event === "deleted") this.artifactCache.delete(artifact.uri);
18900
+ }
18901
+ if (artifact && notificationSessionId) artifact.__sessionId = notificationSessionId;
18902
+ if (event === "created") this.emit("artifactCreated", artifact);
18903
+ else if (event === "updated") this.emit("artifactUpdated", artifact);
18904
+ else if (event === "deleted") this.emit("artifactDeleted", artifact);
18905
+ }
18906
+ if (method === "_codebuddy.ai/checkpoint") {
18907
+ const event = params.event;
18908
+ const checkpoint = params.checkpoint;
18909
+ const checkpointSessionId = params.sessionId;
18910
+ console.log("[LocalConnection] Emitting checkpoint event:", {
18911
+ event,
18912
+ checkpointId: checkpoint?.id,
18913
+ checkpointSessionId,
18914
+ filesCount: checkpoint?.fileChanges?.files?.length ?? 0
18915
+ });
18916
+ if (checkpoint && checkpointSessionId) checkpoint.__sessionId = checkpointSessionId;
18917
+ if (event === "created") this.emit("checkpointCreated", checkpoint);
18918
+ else if (event === "updated") this.emit("checkpointUpdated", checkpoint);
18919
+ }
18920
+ if (method === ExtensionMethod.COMMAND) {
18921
+ const action = params.action;
18922
+ const commandParams = params.params;
18923
+ const commandSessionId = params.sessionId;
18924
+ console.log("[LocalConnection] Emitting command event:", {
18925
+ action,
18926
+ commandSessionId,
18927
+ paramsKeys: commandParams ? Object.keys(commandParams) : []
18928
+ });
18929
+ const commandData = {
18930
+ action,
18931
+ params: commandParams
18932
+ };
18933
+ if (commandSessionId) commandData.__sessionId = commandSessionId;
18934
+ this.emit("command", commandData);
18935
+ }
18936
+ });
18937
+ this.log("Event forwarding setup complete");
18938
+ }
18939
+ on(event, listener) {
18940
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
18941
+ this.listeners.get(event).add(listener);
18942
+ return this;
18943
+ }
18944
+ off(event, listener) {
18945
+ const eventListeners = this.listeners.get(event);
18946
+ if (eventListeners) eventListeners.delete(listener);
18947
+ const onceEventListeners = this.onceListeners.get(event);
18948
+ if (onceEventListeners) onceEventListeners.delete(listener);
18949
+ return this;
18950
+ }
18951
+ once(event, listener) {
18952
+ if (!this.onceListeners.has(event)) this.onceListeners.set(event, /* @__PURE__ */ new Set());
18953
+ this.onceListeners.get(event).add(listener);
18954
+ return this;
18955
+ }
18956
+ emit(event, data) {
18957
+ const regularListeners = this.listeners.get(event);
18958
+ const onceEventListeners = this.onceListeners.get(event);
18959
+ if (event === "sessionUpdate") {
18960
+ const updateType = data?.update?.sessionUpdate;
18961
+ const notifSessionId = data?.sessionId;
18962
+ if ((regularListeners?.size ?? 0) + (onceEventListeners?.size ?? 0) === 0) console.warn(`[RT-DEBUG][LocalConn] emit sessionUpdate NO LISTENERS: type=${updateType}, sessionId=${notifSessionId?.substring(0, 8)}, cwd=${this.cwd?.substring(this.cwd.length - 20)}`);
18963
+ }
18964
+ let hasListeners = false;
18965
+ if (regularListeners && regularListeners.size > 0) {
18966
+ hasListeners = true;
18967
+ for (const listener of regularListeners) try {
18968
+ const result = listener(data);
18969
+ if (result instanceof Promise) result.catch((err) => {
18970
+ console.error(`Error in async event listener for '${String(event)}':`, err);
18971
+ });
18972
+ } catch (err) {
18973
+ console.error(`Error in event listener for '${String(event)}':`, err);
18974
+ }
18975
+ }
18976
+ if (onceEventListeners && onceEventListeners.size > 0) {
18977
+ hasListeners = true;
18978
+ const listenersToCall = Array.from(onceEventListeners);
18979
+ this.onceListeners.delete(event);
18980
+ for (const listener of listenersToCall) try {
18981
+ const result = listener(data);
18982
+ if (result instanceof Promise) result.catch((err) => {
18983
+ console.error(`Error in async once event listener for '${String(event)}':`, err);
18984
+ });
18985
+ } catch (err) {
18986
+ console.error(`Error in once event listener for '${String(event)}':`, err);
18987
+ }
18988
+ }
18989
+ return hasListeners;
18990
+ }
18991
+ removeAllListeners(event) {
18992
+ if (event !== void 0) {
18993
+ this.listeners.delete(event);
18994
+ this.onceListeners.delete(event);
18995
+ } else {
18996
+ this.listeners.clear();
18997
+ this.onceListeners.clear();
18998
+ }
18999
+ return this;
19000
+ }
19001
+ get state() {
19002
+ return this._state;
19003
+ }
19004
+ get isInitialized() {
19005
+ return this._isInitialized;
19006
+ }
19007
+ get capabilities() {
19008
+ return this._capabilities;
19009
+ }
19010
+ get initializeResult() {
19011
+ return this._initializeResult;
19012
+ }
19013
+ async connect(_clientCapabilities) {
19014
+ this.log("Connecting...");
19015
+ this._state = "connecting";
19016
+ this.emit("connecting", void 0);
19017
+ this._isInitialized = true;
19018
+ this._initializeResult = { protocolVersion: _agentclientprotocol_sdk.PROTOCOL_VERSION };
19019
+ this.emit("connected", void 0);
19020
+ this._state = "initialized";
19021
+ return this._initializeResult;
19022
+ }
19023
+ disconnect() {
19024
+ this.log("Disconnecting...");
19025
+ this.cleanupPendingPermissionsOnDisconnect();
19026
+ this._state = "disconnected";
19027
+ this._isInitialized = false;
19028
+ this.acpClient.destroy();
19029
+ this.emit("disconnected", void 0);
19030
+ this.log("Disconnected");
19031
+ }
19032
+ /**
19033
+ * 清理断开连接时所有待处理的权限请求
19034
+ * 在连接断开时主动清理所有 pending 权限请求并返回 cancelled
19035
+ */
19036
+ cleanupPendingPermissionsOnDisconnect() {
19037
+ const count = this.pendingPermissions.size;
19038
+ if (count === 0) return;
19039
+ for (const [requestId] of this.pendingPermissions) {
19040
+ const resolver = this[`_permissionResolver_${requestId}`];
19041
+ if (resolver) {
19042
+ resolver({ outcome: "cancelled" });
19043
+ delete this[`_permissionResolver_${requestId}`];
19044
+ }
19045
+ }
19046
+ this.pendingPermissions.clear();
19047
+ this.log(`Cleaned up ${count} pending permission request(s) on disconnect`);
19048
+ }
19049
+ async createSession(params) {
19050
+ this.log("Creating session with cwd:", params.cwd, params._meta);
19051
+ const response = await this.acpClient.newSession({
19052
+ _meta: params._meta,
19053
+ cwd: params.cwd,
19054
+ mcpServers: params.mcpServers ?? []
19055
+ });
19056
+ this.bindPermissionRequest(response.sessionId);
19057
+ return response;
19058
+ }
19059
+ async loadSession(params) {
19060
+ if (!params.sessionId) throw new Error("sessionId is required for loadSession");
19061
+ this.log(`[LocalAgentConnection] loadSession: ${params.sessionId}`);
19062
+ const response = await this.acpClient.loadSession({
19063
+ sessionId: params.sessionId,
19064
+ cwd: params.cwd,
19065
+ mcpServers: params.mcpServers ?? []
19066
+ });
19067
+ this.log(`[LocalAgentConnection] loadSession response:${response}`);
19068
+ this.bindPermissionRequest(params.sessionId);
19069
+ return response;
19070
+ }
19071
+ async bindPermissionRequest(sessionId) {
19072
+ if (this.onRequest) this.onRequest();
19073
+ this.onRequest = this.acpClient.onRequestPermission(sessionId, async (params) => {
19074
+ this.pendingPermissions.set(params.requestId, {
19075
+ params,
19076
+ createdAt: Date.now()
19077
+ });
19078
+ const permissionRequest = {
19079
+ sessionId: params.sessionId,
19080
+ toolCall: params.toolCall,
19081
+ options: params.options
19082
+ };
19083
+ this.emit("permissionRequest", {
19084
+ requestId: params.requestId,
19085
+ params: permissionRequest
19086
+ });
19087
+ return new Promise((resolve) => {
19088
+ const checkResolution = () => {
19089
+ if (!this.pendingPermissions.has(params.requestId)) return;
19090
+ const pending = this.pendingPermissions.get(params.requestId);
19091
+ if (pending && Date.now() - pending.createdAt > this.permissionTimeout) {
19092
+ this.pendingPermissions.delete(params.requestId);
19093
+ this.emit("permissionTimeout", { requestId: params.requestId });
19094
+ if (this.permissionAutoRejectOnTimeout) resolve({ outcome: "cancelled" });
19095
+ }
19096
+ };
19097
+ this[`_permissionResolver_${params.requestId}`] = resolve;
19098
+ setTimeout(checkResolution, this.permissionTimeout);
19099
+ });
19100
+ });
19101
+ }
19102
+ async setSessionMode(sessionId, modeId) {
19103
+ this.log("Setting session mode:", sessionId, "to", modeId);
19104
+ return await this.acpClient.setSessionMode({
19105
+ sessionId,
19106
+ modeId
19107
+ });
19108
+ }
19109
+ async setSessionModel(sessionId, modelId) {
19110
+ this.log("Setting session model:", sessionId, "to", modelId);
19111
+ return await this.acpClient.setSessionModel({
19112
+ sessionId,
19113
+ modelId
19114
+ });
19115
+ }
19116
+ /**
19117
+ * 打开工作区窗口
19118
+ * 使用 __workspace__ session ID,由 Main Process 直接处理,不转发到 ExtensionHost
19119
+ * @param params 打开工作区请求参数
19120
+ * @returns 打开工作区响应
19121
+ */
19122
+ async openWorkspace(params) {
19123
+ this.log("Opening workspace:", params.cwd);
19124
+ return await this.acpClient.openWorkspace(params);
19125
+ }
19126
+ async prompt(sessionId, params) {
19127
+ const prompt = typeof params.content === "string" ? [{
19128
+ type: "text",
19129
+ text: params.content
19130
+ }] : params.content;
19131
+ this.log("Sending prompt to session:", sessionId);
19132
+ return await this.acpClient.prompt({
19133
+ sessionId,
19134
+ prompt,
19135
+ _meta: {
19136
+ ...params._meta,
19137
+ planMode: params.planMode
19138
+ }
19139
+ });
19140
+ }
19141
+ async *promptStream(_sessionId, _params) {
19142
+ throw new Error("promptStream not implemented for Local connection");
19143
+ }
19144
+ async cancel(sessionId) {
19145
+ this.log("Cancelling session:", sessionId);
19146
+ await this.acpClient.cancel({ sessionId });
19147
+ }
19148
+ getArtifacts() {
19149
+ return new Map(this.artifactCache);
19150
+ }
19151
+ getArtifact(uri) {
19152
+ return this.artifactCache.get(uri);
19153
+ }
19154
+ getArtifactsByType(type) {
19155
+ return Array.from(this.artifactCache.values()).filter((artifact) => artifact.type === type);
19156
+ }
19157
+ async fetchArtifactContent(_artifact) {
19158
+ throw new Error("fetchArtifactContent not implemented for Local connection");
19159
+ }
19160
+ async fetchArtifactContentById(_id) {
19161
+ throw new Error("fetchArtifactContentById not implemented for Local connection");
19162
+ }
19163
+ resolvePermission(requestId, optionId) {
19164
+ const resolver = this[`_permissionResolver_${requestId}`];
19165
+ if (resolver) {
19166
+ this.pendingPermissions.delete(requestId);
19167
+ delete this[`_permissionResolver_${requestId}`];
19168
+ resolver({
19169
+ outcome: "selected",
19170
+ optionId
19171
+ });
19172
+ this.emit("permissionResolved", {
19173
+ requestId,
19174
+ optionId
19175
+ });
19176
+ return true;
19177
+ }
19178
+ return false;
19179
+ }
19180
+ rejectPermission(requestId, reason) {
19181
+ const resolver = this[`_permissionResolver_${requestId}`];
19182
+ if (resolver) {
19183
+ this.pendingPermissions.delete(requestId);
19184
+ delete this[`_permissionResolver_${requestId}`];
19185
+ resolver({ outcome: "cancelled" });
19186
+ this.emit("permissionRejected", {
19187
+ requestId,
19188
+ reason
19189
+ });
19190
+ return true;
19191
+ }
19192
+ return false;
19193
+ }
19194
+ getPendingPermissions() {
19195
+ const result = /* @__PURE__ */ new Map();
19196
+ for (const [requestId, pending] of this.pendingPermissions) result.set(requestId, {
19197
+ params: {
19198
+ sessionId: pending.params.sessionId,
19199
+ toolCall: pending.params.toolCall,
19200
+ options: pending.params.options
19201
+ },
19202
+ createdAt: pending.createdAt
19203
+ });
19204
+ return result;
19205
+ }
19206
+ hasPendingPermissions() {
19207
+ return this.pendingPermissions.size > 0;
19208
+ }
19209
+ answerQuestion(_toolCallId, _answers) {
19210
+ return false;
19211
+ }
19212
+ /**
19213
+ * 工具回调操作
19214
+ * 用于对正在执行的工具进行 approve / skip / cancel 操作
19215
+ * @param sessionId 会话 ID
19216
+ * @param toolCallId 工具调用 ID
19217
+ * @param toolName 工具名称
19218
+ * @param action 操作类型 ('approve' | 'skip' | 'cancel')
19219
+ * @returns 工具回调响应
19220
+ */
19221
+ async toolCallback(sessionId, toolCallId, toolName, action) {
19222
+ this.log("toolCallback called for session:", sessionId, "action:", action);
19223
+ const request = {
19224
+ sessionId,
19225
+ toolCallId,
19226
+ toolName,
19227
+ action
19228
+ };
19229
+ return await this.acpClient.toolCallback(request);
19230
+ }
19231
+ cancelQuestion(_toolCallId, _reason) {
19232
+ return false;
19233
+ }
19234
+ getPendingQuestions() {
19235
+ return /* @__PURE__ */ new Map();
19236
+ }
19237
+ hasPendingQuestions() {
19238
+ return false;
19239
+ }
19240
+ async reportTelemetry(eventName, payload) {
19241
+ try {
19242
+ await this.acpClient.sendRequest(this.agentId, "reportTelemetry", {
19243
+ eventName,
19244
+ payload
19245
+ });
19246
+ } catch (error) {
19247
+ console.warn("[LocalAgentConnection] reportTelemetry failed:", error);
19248
+ }
19249
+ }
19250
+ async extMethod(_method, _params) {
19251
+ throw new Error("extMethod not implemented for Local connection");
19252
+ }
19253
+ async extNotification(_method, _params) {
19254
+ throw new Error("extNotification not implemented for Local connection");
19255
+ }
19256
+ async readFile(_path) {
19257
+ throw new Error("readFile not implemented for Local connection");
19258
+ }
19259
+ async listDir(_path) {
19260
+ throw new Error("listDir not implemented for Local connection");
19261
+ }
19262
+ async fileExists(_path) {
19263
+ throw new Error("fileExists not implemented for Local connection");
19264
+ }
19265
+ async fileStat(_path) {
19266
+ throw new Error("fileStat not implemented for Local connection");
19267
+ }
19268
+ log(...args) {
19269
+ console.log("[LocalAgentConnection]", ...args);
19270
+ }
19271
+ };
19272
+
19273
+ //#endregion
19274
+ //#region ../agent-provider/src/common/providers/local-agent-provider/local-filesystem.ts
19275
+ /**
19276
+ * Filesystem JSON-RPC 方法名称
19277
+ */
19278
+ const FILESYSTEM_METHODS = {
19279
+ READ: "fs/read",
19280
+ WRITE: "fs/write",
19281
+ LIST: "fs/list",
19282
+ EXISTS: "fs/exists",
19283
+ MAKE_DIR: "fs/makeDir",
19284
+ REMOVE: "fs/remove",
19285
+ RENAME: "fs/rename",
19286
+ GET_INFO: "fs/getInfo",
19287
+ WATCH_DIR: "fs/watchDir",
19288
+ UNWATCH: "fs/unwatch"
19289
+ };
19290
+ /**
19291
+ * Watch 句柄 ID 生成计数器
19292
+ */
19293
+ let watchIdCounter = 0;
19294
+ /**
19295
+ * LocalFilesystem
19296
+ *
19297
+ * 实现 FilesResource 接口,将 filesystem 操作通过 JSON-RPC 发送到 ExtensionHost
19298
+ */
19299
+ var LocalFilesystem = class {
19300
+ constructor(client, sessionId, options) {
19301
+ this.watchCallbacks = /* @__PURE__ */ new Map();
19302
+ this.client = client;
19303
+ this.sessionId = sessionId;
19304
+ this.debug = options?.debug ?? false;
19305
+ this.setupEventListener();
19306
+ }
19307
+ /**
19308
+ * 设置 filesystem 事件监听(用于 watchDir)
19309
+ */
19310
+ setupEventListener() {
19311
+ const dispose = this.client.onExtNotification((method, params) => {
19312
+ if (method !== "fs-event") return;
19313
+ const data = params;
19314
+ if (data?.watchId) {
19315
+ const callback = this.watchCallbacks.get(data.watchId);
19316
+ if (callback) callback({
19317
+ type: data.eventType,
19318
+ name: data.name,
19319
+ path: data.path
19320
+ });
19321
+ }
19322
+ });
19323
+ this.eventCleanup = () => dispose?.();
19324
+ }
19325
+ /**
19326
+ * 发送 filesystem 请求到 ExtensionHost
19327
+ */
19328
+ async sendRequest(method, params) {
19329
+ console.log(`[LocalFilesystem] sendRequest: ${method}`, params);
19330
+ try {
19331
+ const result = await this.client.sendRequest(this.sessionId, method, params);
19332
+ if (result && typeof result === "object") {
19333
+ if ("error" in result && result.error) throw new Error(result.error);
19334
+ if ("data" in result) return result.data;
19335
+ }
19336
+ return result;
19337
+ } catch (error) {
19338
+ console.error("[LocalFilesystem] error:", error);
19339
+ throw error;
19340
+ }
19341
+ }
19342
+ async read(path, opts) {
19343
+ const format = opts?.format ?? "text";
19344
+ if (format === "text") return this.sendRequest(FILESYSTEM_METHODS.READ, {
19345
+ path,
19346
+ opts,
19347
+ format: "text"
19348
+ });
19349
+ const base64Data = await this.sendRequest(FILESYSTEM_METHODS.READ, {
19350
+ path,
19351
+ opts,
19352
+ format: "bytes"
19353
+ });
19354
+ const binaryString = atob(base64Data);
19355
+ const bytes = new Uint8Array(binaryString.length);
19356
+ for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
19357
+ if (format === "bytes") return bytes;
19358
+ else if (format === "blob") return new Blob([bytes]);
19359
+ else return new ReadableStream({ start(controller) {
19360
+ controller.enqueue(bytes);
19361
+ controller.close();
19362
+ } });
19363
+ }
19364
+ async write(pathOrFiles, dataOrOpts, opts) {
19365
+ console.log("[LocalFilesystem] write() called:", {
19366
+ sessionId: this.sessionId,
19367
+ isArray: Array.isArray(pathOrFiles),
19368
+ pathOrFiles: Array.isArray(pathOrFiles) ? `${pathOrFiles.length} files` : pathOrFiles
19369
+ });
19370
+ if (Array.isArray(pathOrFiles)) {
19371
+ const files = await Promise.all(pathOrFiles.map(async (entry) => ({
19372
+ path: entry.path,
19373
+ data: await this.convertDataToString(entry.data)
19374
+ })));
19375
+ return this.sendRequest(FILESYSTEM_METHODS.WRITE, {
19376
+ path: "",
19377
+ files,
19378
+ opts: dataOrOpts
19379
+ });
19380
+ } else {
19381
+ const dataStr = await this.convertDataToString(dataOrOpts);
19382
+ return this.sendRequest(FILESYSTEM_METHODS.WRITE, {
19383
+ path: pathOrFiles,
19384
+ data: dataStr,
19385
+ opts
19386
+ });
19387
+ }
19388
+ }
19389
+ /**
19390
+ * 将数据转换为字符串(用于传输)
19391
+ */
19392
+ async convertDataToString(data) {
19393
+ if (typeof data === "string") return data;
19394
+ else if (data instanceof ArrayBuffer) return this.arrayBufferToBase64(data);
19395
+ else if (data instanceof Blob) return this.blobToBase64(data);
19396
+ else if (data && typeof data.getReader === "function") {
19397
+ const reader = data.getReader();
19398
+ const chunks = [];
19399
+ let done = false;
19400
+ while (!done) {
19401
+ const result = await reader.read();
19402
+ done = result.done;
19403
+ if (result.value) chunks.push(result.value);
19404
+ }
19405
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
19406
+ const combined = new Uint8Array(totalLength);
19407
+ let offset = 0;
19408
+ for (const chunk of chunks) {
19409
+ combined.set(chunk, offset);
19410
+ offset += chunk.length;
19411
+ }
19412
+ return this.arrayBufferToBase64(combined.buffer);
19413
+ }
19414
+ return String(data);
19415
+ }
19416
+ /**
19417
+ * 列出目录内容
19418
+ */
19419
+ async list(path, opts) {
19420
+ return this.sendRequest(FILESYSTEM_METHODS.LIST, {
19421
+ path,
19422
+ opts,
19423
+ depth: opts?.depth
19424
+ });
19425
+ }
19426
+ /**
19427
+ * 检查路径是否存在
19428
+ */
19429
+ async exists(path, opts) {
19430
+ return this.sendRequest(FILESYSTEM_METHODS.EXISTS, {
19431
+ path,
19432
+ opts
19433
+ });
19434
+ }
19435
+ /**
19436
+ * 创建目录
19437
+ */
19438
+ async makeDir(path, opts) {
19439
+ return this.sendRequest(FILESYSTEM_METHODS.MAKE_DIR, {
19440
+ path,
19441
+ opts
19442
+ });
19443
+ }
19444
+ /**
19445
+ * 删除文件或目录
19446
+ */
19447
+ async remove(path, opts) {
19448
+ await this.sendRequest(FILESYSTEM_METHODS.REMOVE, {
19449
+ path,
19450
+ opts
19451
+ });
19452
+ }
19453
+ /**
19454
+ * 重命名/移动文件或目录
19455
+ */
19456
+ async rename(oldPath, newPath, opts) {
19457
+ return this.sendRequest(FILESYSTEM_METHODS.RENAME, {
19458
+ path: oldPath,
19459
+ newPath,
19460
+ opts
19461
+ });
19462
+ }
19463
+ /**
19464
+ * 获取文件或目录信息
19465
+ */
19466
+ async getInfo(path, opts) {
19467
+ return this.sendRequest(FILESYSTEM_METHODS.GET_INFO, {
19468
+ path,
19469
+ opts
19470
+ });
19471
+ }
19472
+ /**
19473
+ * 监听目录变化
19474
+ */
19475
+ async watchDir(path, onEvent, opts) {
19476
+ const watchId = `watch-${++watchIdCounter}-${Date.now()}`;
19477
+ this.watchCallbacks.set(watchId, onEvent);
19478
+ await this.sendRequest(FILESYSTEM_METHODS.WATCH_DIR, {
19479
+ path,
19480
+ recursive: opts?.recursive,
19481
+ opts: {
19482
+ ...opts,
19483
+ user: watchId
19484
+ }
19485
+ });
19486
+ return { stop: async () => {
19487
+ this.watchCallbacks.delete(watchId);
19488
+ try {
19489
+ await this.sendRequest(FILESYSTEM_METHODS.UNWATCH, {
19490
+ path,
19491
+ opts: { user: watchId }
19492
+ });
19493
+ } catch (error) {
19494
+ this.log(`Failed to unwatch: ${path}`, error);
19495
+ }
19496
+ if (opts?.onExit) opts.onExit();
19497
+ } };
19498
+ }
19499
+ /**
19500
+ * ArrayBuffer 转 Base64
19501
+ */
19502
+ arrayBufferToBase64(buffer) {
19503
+ const bytes = new Uint8Array(buffer);
19504
+ let binary = "";
19505
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
19506
+ return btoa(binary);
19507
+ }
19508
+ /**
19509
+ * Blob 转 Base64
19510
+ */
19511
+ async blobToBase64(blob) {
19512
+ return new Promise((resolve, reject) => {
19513
+ const reader = new FileReader();
19514
+ reader.onloadend = () => {
19515
+ const result = reader.result;
19516
+ resolve(result.split(",")[1] || result);
19517
+ };
19518
+ reader.onerror = reject;
19519
+ reader.readAsDataURL(blob);
19520
+ });
19521
+ }
19522
+ /**
19523
+ * 日志输出
19524
+ */
19525
+ log(...args) {
19526
+ if (this.debug) console.log("[LocalFilesystem]", ...args);
19527
+ }
19528
+ /**
19529
+ * 销毁实例,清理资源
19530
+ */
19531
+ destroy() {
19532
+ this.watchCallbacks.clear();
19533
+ if (this.eventCleanup) {
19534
+ this.eventCleanup();
19535
+ this.eventCleanup = void 0;
19536
+ }
19537
+ }
19538
+ };
19539
+
19540
+ //#endregion
19541
+ //#region ../agent-provider/src/common/providers/local-agent-provider/local-provider.ts
19542
+ /**
19543
+ * LocalAgentProvider
19544
+ *
19545
+ * 实现统一两层模型的 Local 端适配:
19546
+ * - 第一层 (Window/Workspace): 由 IDE/用户管理,Provider 只负责调用 openWorkspace
19547
+ * - 第二层 (Session): 由 Provider 管理
19548
+ *
19549
+ * 核心方法语义:
19550
+ * - create(): 返回 cwd 作为 "agentId"(需先调用 prepareCreate 预设)
19551
+ * - get(id): 支持 cwd 和 sessionId 两种输入,返回 Session 状态
19552
+ * - list(): 返回所有 Sessions
19553
+ * - connect(id): 智能判断 id 类型,建立连接
19554
+ * - delete(id): 删除 Session
19555
+ *
19556
+ * 同时实现 FilesystemProvider 接口,提供文件系统访问能力
19557
+ */
19558
+ var LocalAgentProvider = class LocalAgentProvider {
19559
+ static {
19560
+ this.ARCHIVED_SESSIONS_KEY = "genie-archived-session-ids";
19561
+ }
19562
+ /**
19563
+ * 从 localStorage 读取归档 session ID 集合
19564
+ */
19565
+ getArchivedSessionIds() {
19566
+ try {
19567
+ const raw = localStorage.getItem(LocalAgentProvider.ARCHIVED_SESSIONS_KEY);
19568
+ return raw ? new Set(JSON.parse(raw)) : /* @__PURE__ */ new Set();
19569
+ } catch {
19570
+ return /* @__PURE__ */ new Set();
19571
+ }
19572
+ }
19573
+ /**
19574
+ * 将归档 session ID 集合保存到 localStorage
19575
+ */
19576
+ saveArchivedSessionIds(ids) {
19577
+ try {
19578
+ localStorage.setItem(LocalAgentProvider.ARCHIVED_SESSIONS_KEY, JSON.stringify([...ids]));
19579
+ } catch {}
19580
+ }
19581
+ constructor(options) {
19582
+ this.connections = /* @__PURE__ */ new Map();
19583
+ this.sessionCwdMap = /* @__PURE__ */ new Map();
19584
+ this.filesystemCache = /* @__PURE__ */ new Map();
19585
+ this.samplingRequestCallbacks = /* @__PURE__ */ new Map();
19586
+ this.rootsRequestCallbacks = /* @__PURE__ */ new Map();
19587
+ this.mcpEventListenersSetup = false;
19588
+ if (!options.channel) throw new Error("Channel is required for LocalAgentProvider");
19589
+ this.options = options;
19590
+ this.channel = options.channel;
19591
+ this.sharedAcpClient = new AcpJsonRpcClient(this.channel, {
19592
+ timeoutMs: options.acpConfig?.timeoutMs ?? 3e4,
19593
+ debug: options.debug ?? false
19594
+ });
19595
+ this.sharedAcpClient.onExtNotification((method, params) => {
19596
+ if (method === "_codebuddy.ai/automation_snapshot") {
19597
+ const snapshot = params.snapshot;
19598
+ if (snapshot) this.channel.emit("automationSnapshotUpdate", snapshot);
19599
+ }
19600
+ if (method === "_codebuddy.ai/plugins_changed") this.channel.emit("pluginsChanged", params);
19601
+ if (method === "_codebuddy.ai/models_changed") this.channel.emit("modelsChanged", params);
19602
+ if (method === "_codebuddy.ai/product_config_changed") this.channel.emit("productConfigChanged", params);
19603
+ if (method === "_codebuddy.ai/identity_changed") {
19604
+ console.log("[LocalProvider] Received identity_changed extNotification, emitting identityChanged event:", JSON.stringify(params));
19605
+ this.channel.emit("identityChanged", params);
19606
+ }
19607
+ if (method === "_codebuddy.ai/mcp_servers_changed") this.channel.emit("mcpServersChanged", params);
19608
+ });
19609
+ }
19610
+ /**
19611
+ * 实现 AgentProvider.filesystem 属性
19612
+ * 返回 this,因为 LocalAgentProvider 本身实现了 FilesystemProvider 接口
19613
+ */
19614
+ get filesystem() {
19615
+ return this;
19616
+ }
19617
+ /**
19618
+ * 创建 "Agent"
19619
+ *
19620
+ * 对于 Local,返回 cwd 作为 agentId
19621
+ * 后续 connect(cwd) 和 connection.createSession() 会使用它
19622
+ *
19623
+ * @param params - 会话创建参数,包含 cwd
19624
+ * @returns cwd 作为 "agentId"
19625
+ */
19626
+ async create(params) {
19627
+ const cwd = params?.cwd;
19628
+ if (cwd === void 0) throw new Error("cwd is required for LocalAgentProvider.create()");
19629
+ this.log(`create() returning cwd as agentId: ${cwd}`);
19630
+ return cwd;
19631
+ }
19632
+ /**
19633
+ * 获取 Session 状态
19634
+ *
19635
+ * SessionManager.loadSession() 会用 sessionId 调用此方法
19636
+ * 需要返回包含 cwd 的状态,以便后续 connect()
19637
+ *
19638
+ * @param id - 可能是 sessionId 或 cwd
19639
+ * @returns Session 状态
19640
+ */
19641
+ async get(id) {
19642
+ this.log(`get() called with: ${id}`);
19643
+ if (id.startsWith("/")) return {
19644
+ id,
19645
+ type: "local",
19646
+ status: "disconnected",
19647
+ cwd: id
19648
+ };
19649
+ const cachedCwd = this.sessionCwdMap.get(id);
19650
+ if (cachedCwd) return {
19651
+ id,
19652
+ type: "local",
19653
+ status: "connected",
19654
+ cwd: cachedCwd
19655
+ };
19656
+ try {
19657
+ const session = (await this.channel.callMethod("__backend__", {
19658
+ type: "backend",
19659
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19660
+ params: {
19661
+ type: "backend:get-session-request",
19662
+ params: { sessionId: id }
19663
+ }
19664
+ }, 5e3)).data?.session;
19665
+ if (!session) {
19666
+ this.log(`Session not found: ${id}`);
19667
+ return;
19668
+ }
19669
+ if (session.cwd) this.sessionCwdMap.set(id, session.cwd);
19670
+ return {
19671
+ id: session.sessionId,
19672
+ type: "local",
19673
+ status: session.status === "active" ? "connected" : "disconnected",
19674
+ cwd: session.cwd,
19675
+ name: session.name,
19676
+ createdAt: session.createdAt ? new Date(session.createdAt) : void 0
19677
+ };
19678
+ } catch (error) {
19679
+ this.log(`Failed to get session ${id}:`, error);
19680
+ return;
19681
+ }
19682
+ }
19683
+ /**
19684
+ * 列出所有 Sessions
19685
+ *
19686
+ * @param options - 可选的查询参数(过滤、排序、userId)
19687
+ * @returns Session 状态列表和分页信息
19688
+ */
19689
+ async list(options) {
19690
+ this.log("list() called with options:", options);
19691
+ if (!options?.userId) {
19692
+ this.log("No userId provided, returning empty list");
19693
+ return {
19694
+ agents: [],
19695
+ pagination: {
19696
+ page: 1,
19697
+ size: 0,
19698
+ total: 0,
19699
+ totalPages: 0,
19700
+ hasNext: false,
19701
+ hasPrev: false
19702
+ }
19703
+ };
19704
+ }
19705
+ try {
19706
+ const sessions = (await this.channel.callMethod("__backend__", {
19707
+ type: "backend",
19708
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19709
+ params: {
19710
+ type: "backend:list-sessions-request",
19711
+ params: { userId: options.userId }
19712
+ }
19713
+ }, 5e3)).data?.sessions ?? [];
19714
+ this.log(`Found ${sessions.length} sessions for userId: ${options.userId}`);
19715
+ for (const session of sessions) if (session.sessionId && session.cwd) this.sessionCwdMap.set(session.sessionId, session.cwd);
19716
+ let result = sessions.map((session) => ({
19717
+ id: session.sessionId,
19718
+ type: "local",
19719
+ status: session.status,
19720
+ cwd: session.cwd,
19721
+ name: session.name,
19722
+ createdAt: session.createdAt ? new Date(session.createdAt) : void 0,
19723
+ isPlayground: session.isPlayground,
19724
+ isUserDefinedTitle: session.isUserDefinedTitle
19725
+ }));
19726
+ const archivedIds = this.getArchivedSessionIds();
19727
+ const isQueryingArchived = options?.filters?.some((f) => f.field === "status" && f.value.split(",").map((v) => v.trim()).includes("archived"));
19728
+ if (isQueryingArchived) result = result.filter((s) => archivedIds.has(s.id)).map((s) => ({
19729
+ ...s,
19730
+ status: "archived"
19731
+ }));
19732
+ else result = result.filter((s) => !archivedIds.has(s.id));
19733
+ if (options) result = this.applyFilters(result, options, isQueryingArchived);
19734
+ return {
19735
+ agents: result,
19736
+ pagination: {
19737
+ page: 1,
19738
+ size: result.length,
19739
+ total: result.length,
19740
+ totalPages: 1,
19741
+ hasNext: false,
19742
+ hasPrev: false
19743
+ }
19744
+ };
19745
+ } catch (error) {
19746
+ this.log("Failed to list sessions:", error);
19747
+ return {
19748
+ agents: [],
19749
+ pagination: {
19750
+ page: 1,
19751
+ size: 0,
19752
+ total: 0,
19753
+ totalPages: 0,
19754
+ hasNext: false,
19755
+ hasPrev: false
19756
+ }
19757
+ };
19758
+ }
19759
+ }
19760
+ /**
19761
+ * 应用本地过滤和排序
19762
+ */
19763
+ applyFilters(sessions, options, skipStatusArchived = false) {
19764
+ let filtered = [...sessions];
19765
+ if (options.title) {
19766
+ const keyword = options.title.toLowerCase();
19767
+ filtered = filtered.filter((s) => s.name?.toLowerCase().includes(keyword));
19768
+ }
19769
+ if (options.filters?.length) for (const filter of options.filters) {
19770
+ if (skipStatusArchived && filter.field === "status" && filter.value.includes("archived")) continue;
19771
+ const values = filter.value.split(",").map((v) => v.trim());
19772
+ filtered = filtered.filter((s) => {
19773
+ const fieldValue = s[filter.field];
19774
+ if (filter.field === "sessionStatus") return values.includes(s.status.toLowerCase()) || values.includes(String(fieldValue));
19775
+ return values.includes(String(fieldValue));
19776
+ });
19777
+ }
19778
+ if (options.dayRange !== void 0) {
19779
+ const cutoff = /* @__PURE__ */ new Date();
19780
+ cutoff.setDate(cutoff.getDate() - (options.dayRange - 1));
19781
+ cutoff.setHours(0, 0, 0, 0);
19782
+ filtered = filtered.filter((s) => s.createdAt && s.createdAt >= cutoff);
19783
+ }
19784
+ if (options.sort) {
19785
+ const { orderBy, order = "asc" } = options.sort;
19786
+ filtered.sort((a, b) => {
19787
+ const aVal = a[orderBy];
19788
+ const bVal = b[orderBy];
19789
+ if (aVal === void 0) return 1;
19790
+ if (bVal === void 0) return -1;
19791
+ let cmp = 0;
19792
+ if (aVal instanceof Date && bVal instanceof Date) cmp = aVal.getTime() - bVal.getTime();
19793
+ else if (typeof aVal === "string" && typeof bVal === "string") cmp = aVal.localeCompare(bVal);
19794
+ else cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
19795
+ return order === "desc" ? -cmp : cmp;
19796
+ });
19797
+ }
19798
+ return filtered;
19799
+ }
19800
+ /**
19801
+ * 连接到工作区
19802
+ *
19803
+ * @param id - 可能是 cwd(createSession 流程)或 sessionId(loadSession 流程)
19804
+ * @returns LocalAgentConnection 实例
19805
+ */
19806
+ async connect(id) {
19807
+ this.log(`connect() called with: ${id}`);
19808
+ let cwd;
19809
+ if (id.includes("/")) cwd = id;
19810
+ else {
19811
+ cwd = this.sessionCwdMap.get(id) || "";
19812
+ if (cwd === void 0) cwd = (await this.get(id))?.cwd || "";
19813
+ if (cwd === void 0) throw new Error(`Cannot find cwd for session: ${id}`);
19814
+ }
19815
+ this.log(`Resolved cwd: ${cwd}`);
19816
+ const existing = this.connections.get(cwd);
19817
+ if (existing?.isInitialized) {
19818
+ this.log(`Reusing existing connection for: ${cwd}`);
19819
+ return existing;
19820
+ }
19821
+ this.log(`Opening workspace: ${cwd}`);
19822
+ const clientCapabilities = {
19823
+ ...this.options.clientCapabilities,
19824
+ _meta: {
19825
+ ...this.options.clientCapabilities?._meta,
19826
+ "codebuddy.ai": {
19827
+ ...this.options.clientCapabilities?._meta?.["codebuddy.ai"],
19828
+ cwd
19829
+ }
19830
+ }
19831
+ };
19832
+ const config = {
19833
+ channel: this.channel,
19834
+ debug: this.options.debug,
19835
+ acpConfig: this.options.acpConfig,
19836
+ permissionTimeout: this.options.permissionTimeout,
19837
+ permissionAutoRejectOnTimeout: this.options.permissionAutoRejectOnTimeout,
19838
+ clientCapabilities
19839
+ };
19840
+ const connection = new LocalAgentConnection(cwd, config);
19841
+ await connection.connect(clientCapabilities);
19842
+ this.connections.set(cwd, connection);
19843
+ connection.once("disconnected", () => {
19844
+ this.connections.delete(cwd);
19845
+ this.log(`Connection removed from cache: ${cwd}`);
19846
+ });
19847
+ this.log(`Connected to workspace: ${cwd}`);
19848
+ return connection;
19849
+ }
19850
+ /**
19851
+ * 删除 Session
19852
+ *
19853
+ * @param sessionId - 会话 ID
19854
+ * @returns 是否成功删除
19855
+ */
19856
+ async delete(sessionId) {
19857
+ this.log(`delete() called with: ${sessionId}`);
19858
+ this.sessionCwdMap.delete(sessionId);
19859
+ try {
19860
+ return (await this.channel.callMethod("__backend__", {
19861
+ type: "backend",
19862
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19863
+ params: {
19864
+ type: "backend:delete-session-request",
19865
+ params: { sessionId }
19866
+ }
19867
+ }, 5e3)).data?.success ?? false;
19868
+ } catch (error) {
19869
+ this.log(`Failed to delete session ${sessionId}:`, error);
19870
+ return false;
19871
+ }
19872
+ }
19873
+ /**
19874
+ * 归档 Session
19875
+ *
19876
+ * 纯前端实现:将 sessionId 加入 localStorage 的归档集合,
19877
+ * 不删除后端数据,以便后续可恢复。
19878
+ *
19879
+ * @param sessionId - 会话 ID
19880
+ * @returns 包含归档的 session ID 的对象
19881
+ */
19882
+ async archive(sessionId) {
19883
+ this.log(`archive() called with: ${sessionId}`);
19884
+ const archivedIds = this.getArchivedSessionIds();
19885
+ archivedIds.add(sessionId);
19886
+ this.saveArchivedSessionIds(archivedIds);
19887
+ return { id: sessionId };
19888
+ }
19889
+ /**
19890
+ * 更新 Session 状态
19891
+ *
19892
+ * 纯前端实现:当 status 不为 'archived' 时,将 sessionId 从归档集合中移除(恢复归档)。
19893
+ *
19894
+ * @param sessionId - 会话 ID
19895
+ * @param status - 新状态
19896
+ * @returns 包含 session ID 的对象
19897
+ */
19898
+ async updateStatus(sessionId, status) {
19899
+ this.log(`updateStatus() called with: ${sessionId}, status: ${status}`);
19900
+ if (status !== "archived") {
19901
+ const archivedIds = this.getArchivedSessionIds();
19902
+ archivedIds.delete(sessionId);
19903
+ this.saveArchivedSessionIds(archivedIds);
19904
+ } else {
19905
+ const archivedIds = this.getArchivedSessionIds();
19906
+ archivedIds.add(sessionId);
19907
+ this.saveArchivedSessionIds(archivedIds);
19908
+ }
19909
+ return { id: sessionId };
19910
+ }
19911
+ /**
19912
+ * 重命名 Session
19913
+ *
19914
+ * @param sessionId - 会话 ID
19915
+ * @param title - 新标题
19916
+ * @returns 包含重命名的 session ID 的对象
19917
+ */
19918
+ async rename(sessionId, title) {
19919
+ this.log(`rename() called with: ${sessionId}, title: ${title}`);
19920
+ try {
19921
+ const response = await this.channel.callMethod("__backend__", {
19922
+ type: "backend",
19923
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19924
+ params: {
19925
+ type: "backend:rename-session-request",
19926
+ params: {
19927
+ sessionId,
19928
+ title
19929
+ }
19930
+ }
19931
+ }, 5e3);
19932
+ if (!response.data?.success) throw new Error(response.data?.error || "Failed to rename session");
19933
+ return { id: sessionId };
19934
+ } catch (error) {
19935
+ this.log(`Failed to rename session ${sessionId}:`, error);
19936
+ throw error;
19937
+ }
19938
+ }
19939
+ /**
19940
+ * 移动 Session
19941
+ * 将 Playground 会话转换为普通会话(从 Playground 移动到 Workspace)
19942
+ *
19943
+ * @param sessionId - 会话 ID
19944
+ * @returns 包含移动的 session ID 的对象
19945
+ */
19946
+ async move(sessionId) {
19947
+ this.log(`move() called with: ${sessionId}`);
19948
+ try {
19949
+ const response = await this.channel.callMethod("__backend__", {
19950
+ type: "backend",
19951
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19952
+ params: {
19953
+ type: "backend:move-session-request",
19954
+ params: { sessionId }
19955
+ }
19956
+ }, 5e3);
19957
+ if (!response.data?.success) throw new Error(response.data?.error || "Failed to move session");
19958
+ this.log(`move() succeeded for: ${sessionId}`);
19959
+ return { id: sessionId };
19960
+ } catch (error) {
19961
+ this.log(`Failed to move session ${sessionId}:`, error);
19962
+ throw error;
19963
+ }
19964
+ }
19965
+ /**
19966
+ * 注册 sessionId → cwd 映射
19967
+ *
19968
+ * 在 connection.createSession() 成功后调用,记录新创建的 session
19969
+ *
19970
+ * @param sessionId - 会话 ID
19971
+ * @param cwd - 工作区路径
19972
+ */
19973
+ registerSession(sessionId, cwd) {
19974
+ this.sessionCwdMap.set(sessionId, cwd);
19975
+ this.log(`Registered session: ${sessionId} → ${cwd}`);
19976
+ }
19977
+ /**
19978
+ * 获取可用模型列表
19979
+ *
19980
+ * 通过 ACP JSON-RPC 调用 getModels 方法
19981
+ * cwd 为空时会路由到 Default Window
19982
+ *
19983
+ * @param cwd - 工作目录,可选。为空时使用 Default Window
19984
+ * @param mode - 模式过滤,可选
19985
+ * @returns 模型列表
19986
+ */
19987
+ async getModels(cwd, mode) {
19988
+ this.log(`getModels() called with cwd: ${cwd || "(empty)"}, mode: ${mode || "(default)"}`);
19989
+ try {
19990
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getModels", {
19991
+ cwd: cwd || "",
19992
+ mode
19993
+ });
19994
+ if (response?.models) {
19995
+ this.log(`getModels() returned ${response.models.length} models`);
19996
+ return response.models.map((model) => ({
19997
+ ...model,
19998
+ ...model._meta?.["codebuddy.ai"] || {}
19999
+ }));
20000
+ }
20001
+ this.log("getModels() - no models in response");
20002
+ return [];
20003
+ } catch (error) {
20004
+ this.log("getModels() failed:", error);
20005
+ return [];
20006
+ }
20007
+ }
20008
+ /**
20009
+ * 获取可用模式列表
20010
+ *
20011
+ * 通过 ACP JSON-RPC 调用 getModes 方法
20012
+ * cwd 为空时会路由到 Default Window
20013
+ *
20014
+ * @param cwd - 工作目录,可选。为空时使用 Default Window
20015
+ * @returns 模式列表
20016
+ */
20017
+ async getModes(cwd) {
20018
+ this.log(`getModes() called with cwd: ${cwd || "(empty)"}`);
20019
+ try {
20020
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getModes", { cwd: cwd || "" });
20021
+ if (response?.modes) {
20022
+ this.log(`getModes() returned ${response.modes.length} modes`);
20023
+ return response.modes;
20024
+ }
20025
+ this.log("getModes() - no modes in response");
20026
+ return [];
20027
+ } catch (error) {
20028
+ this.log("getModes() failed:", error);
20029
+ return [];
20030
+ }
20031
+ }
20032
+ /**
20033
+ * 获取产品配置子集
20034
+ *
20035
+ * 通过 ACP JSON-RPC 调用 getProductConfiguration 方法
20036
+ * 返回 deploymentType、creditPurchaseActions、links 等配置
20037
+ *
20038
+ * @returns 产品配置子集
20039
+ */
20040
+ async getProductConfiguration() {
20041
+ this.log("getProductConfiguration() called");
20042
+ try {
20043
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getProductConfiguration", {});
20044
+ this.log("getProductConfiguration() returned:", response);
20045
+ return response ?? {};
20046
+ } catch (error) {
20047
+ this.log("getProductConfiguration() failed:", error);
20048
+ return {};
20049
+ }
20050
+ }
20051
+ /**
20052
+ * 获取 Identity 名称
20053
+ * 读取 ~/.workbuddy/IDENTITY.md 中的 Name 字段,用于兼容 localStorage 尚未缓存的历史用户
20054
+ */
20055
+ async getIdentityName() {
20056
+ try {
20057
+ return await this.sharedAcpClient.sendBroadcastRequest("getIdentityName", {}) ?? {};
20058
+ } catch (error) {
20059
+ this.log("getIdentityName() failed:", error);
20060
+ return {};
20061
+ }
20062
+ }
20063
+ /**
20064
+ * 获取用户信息
20065
+ *
20066
+ * 通过 ACP JSON-RPC 调用 getUserInfo 方法
20067
+ * 返回当前登录用户的 enterpriseId 等信息
20068
+ *
20069
+ * @returns 用户信息
20070
+ */
20071
+ async getUserInfo() {
20072
+ this.log("getUserInfo() called");
20073
+ try {
20074
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getUserInfo", {});
20075
+ this.log("getUserInfo() returned:", response);
20076
+ return response ?? {};
20077
+ } catch (error) {
20078
+ this.log("getUserInfo() failed:", error);
20079
+ return {};
20080
+ }
20081
+ }
20082
+ /**
20083
+ * 打开工作区窗口
20084
+ * 这是 LocalAgentProvider 特有的能力,通过 shared ACP client 发送请求,
20085
+ * 使用 __workspace__ session ID,由 Main Process 直接处理,不转发到 ExtensionHost
20086
+ *
20087
+ * @param params 打开工作区请求参数
20088
+ * @returns 打开工作区响应
20089
+ */
20090
+ async openWorkspace(params) {
20091
+ this.log("Opening workspace:", params.cwd);
20092
+ return await this.sharedAcpClient.openWorkspace(params);
20093
+ }
20094
+ /**
20095
+ * 选择文件
20096
+ * 通过 BackendService 的 __backend__ session 处理
20097
+ *
20098
+ * @param params 文件选择参数
20099
+ * @returns 选择结果
20100
+ */
20101
+ async pickFile(params) {
20102
+ this.log("Picking file:", params);
20103
+ try {
20104
+ const response = await this.channel.callMethod("__backend__", {
20105
+ type: "backend",
20106
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
20107
+ params: {
20108
+ type: "backend:pick-file",
20109
+ params
20110
+ }
20111
+ }, 3e4);
20112
+ this.log("pickFile() response:", response.data);
20113
+ const data = response.data;
20114
+ return {
20115
+ files: data.filePaths || [],
20116
+ canceled: data.canceled ?? false,
20117
+ error: data.error
20118
+ };
20119
+ } catch (error) {
20120
+ this.log("pickFile() failed:", error);
20121
+ return {
20122
+ files: [],
20123
+ canceled: true,
20124
+ error: error instanceof Error ? error.message : "Unknown error"
20125
+ };
20126
+ }
20127
+ }
20128
+ /**
20129
+ * 选择文件夹
20130
+ * 通过 BackendService 的 __backend__ session 处理
20131
+ *
20132
+ * @param params 文件夹选择参数
20133
+ * @returns 选择结果
20134
+ */
20135
+ async pickFolder(params) {
20136
+ this.log("Picking folder:", params);
20137
+ try {
20138
+ const response = await this.channel.callMethod("__backend__", {
20139
+ type: "backend",
20140
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
20141
+ params: {
20142
+ type: "backend:pick-folder",
20143
+ params
20144
+ }
20145
+ }, 3e4);
20146
+ this.log("pickFolder() response:", response.data);
20147
+ const data = response.data;
20148
+ return {
20149
+ folderPaths: data?.paths || [],
20150
+ canceled: data?.cancelled ?? true,
20151
+ error: data?.error
20152
+ };
20153
+ } catch (error) {
20154
+ this.log("pickFolder() failed:", error);
20155
+ return {
20156
+ folderPaths: [],
20157
+ canceled: true,
20158
+ error: error instanceof Error ? error.message : "Unknown error"
20159
+ };
20160
+ }
20161
+ }
20162
+ /**
20163
+ * 搜索文件
20164
+ * 通过 shared ACP client 发送广播请求到 Extension Host
20165
+ * Extension Host 中的 FileSearchService 会处理这个请求
20166
+ *
20167
+ * @param params 搜索参数
20168
+ * @returns 搜索结果
20169
+ */
20170
+ async searchFile(params) {
20171
+ this.log("Searching files:", params.options);
20172
+ try {
20173
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getWorkspaceFiles", {
20174
+ cwd: params.cwd,
20175
+ search: params.options.search,
20176
+ resultNum: params.options.resultNum
20177
+ });
20178
+ this.log("searchFile() response:", response);
20179
+ return {
20180
+ results: response.results.map((r) => ({
20181
+ ...r,
20182
+ type: r.type
20183
+ })),
20184
+ error: void 0
20185
+ };
20186
+ } catch (error) {
20187
+ this.log("searchFile() failed:", error);
20188
+ return {
20189
+ results: [],
20190
+ error: error instanceof Error ? error.message : "Unknown error"
20191
+ };
20192
+ }
20193
+ }
20194
+ /**
20195
+ * 获取 Subagent 列表
20196
+ * 通过 shared ACP client 发送广播请求到 Extension Host
20197
+ * Extension Host 中的 SubagentsService 会处理这个请求
20198
+ *
20199
+ * @param params 查询参数
20200
+ * @returns Subagent 列表
20201
+ */
20202
+ async getSubagentList(params) {
20203
+ this.log("Getting subagent list:", params.options);
20204
+ try {
20205
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getSubagentList", {
20206
+ cwd: params.cwd,
20207
+ search: params.options.search,
20208
+ resultNum: params.options.resultNum,
20209
+ agentMode: params.options.agentMode || "all",
20210
+ onlyEnabled: params.options.onlyEnabled
20211
+ });
20212
+ this.log("getSubagentList() response:", response);
20213
+ return {
20214
+ results: response.subagents,
20215
+ error: void 0
20216
+ };
20217
+ } catch (error) {
20218
+ this.log("getSubagentList() failed:", error);
20219
+ return {
20220
+ results: [],
20221
+ error: error instanceof Error ? error.message : "Unknown error"
20222
+ };
20223
+ }
20224
+ }
20225
+ /**
20226
+ * 获取 Skill 列表
20227
+ *
20228
+ * @param params 查询参数
20229
+ * @returns Skill 列表
20230
+ */
20231
+ async getSkillList(params) {
20232
+ this.log("Getting skill list:", params);
20233
+ const useGlobal = params?.global ?? true;
20234
+ try {
20235
+ if (!useGlobal || params?.cwd) {
20236
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getSkillList", {
20237
+ cwd: params?.cwd ?? "",
20238
+ excludePluginSkills: params?.excludePluginSkills ?? false
20239
+ });
20240
+ this.log("getSkillList() broadcast response:", response);
20241
+ return {
20242
+ results: response.skills,
20243
+ error: void 0
20244
+ };
20245
+ }
20246
+ const backendResponse = await this.channel.callMethod("__backend__", {
20247
+ type: "backend",
20248
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
20249
+ params: {
20250
+ type: "backend:get-skill-list",
20251
+ params: { skillScanDirs: params?.skillScanDirs }
20252
+ }
20253
+ }, 3e4);
20254
+ this.log("getSkillList() backend response:", backendResponse.data);
20255
+ const data = backendResponse.data;
20256
+ return {
20257
+ results: data?.skills ?? [],
20258
+ error: data?.error
20259
+ };
20260
+ } catch (error) {
20261
+ this.log("getSkillList() failed:", error);
20262
+ return {
20263
+ results: [],
20264
+ error: error instanceof Error ? error.message : "Unknown error"
20265
+ };
20266
+ }
20267
+ }
20268
+ /**
20269
+ * 导入 Skill 文件夹
20270
+ *
20271
+ * @param params 导入参数
20272
+ * @returns 导入结果
20273
+ */
20274
+ async importSkill(params) {
20275
+ this.log("Importing skill folder:", params);
20276
+ try {
20277
+ const backendResponse = await this.channel.callMethod("__backend__", {
20278
+ type: "backend",
20279
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
20280
+ params: {
20281
+ type: "backend:import-skill",
20282
+ params: {
20283
+ cwd: params.cwd,
20284
+ source: params.source,
20285
+ folderPath: params.folderPath
20286
+ }
20287
+ }
20288
+ }, 3e4);
20289
+ this.log("importSkill() backend response:", backendResponse.data);
20290
+ const data = backendResponse.data;
20291
+ return {
20292
+ success: data?.success ?? false,
20293
+ error: data?.error,
20294
+ skillName: data?.skillName
20295
+ };
20296
+ } catch (error) {
20297
+ this.log("importSkill() failed:", error);
20298
+ return {
20299
+ success: false,
20300
+ error: error instanceof Error ? error.message : "Unknown error"
20301
+ };
20302
+ }
20303
+ }
20304
+ /**
20305
+ * 切换 Skill 启用/禁用状态
20306
+ *
20307
+ * @param params 切换参数
20308
+ * @returns 切换结果
20309
+ */
20310
+ async toggleSkill(params) {
20311
+ this.log("Toggling skill:", params.filePath, "disable:", params.disable);
20312
+ try {
20313
+ const response = await this.sharedAcpClient.sendBroadcastRequest("toggleSkill", {
20314
+ filePath: params.filePath,
20315
+ disable: params.disable
20316
+ });
20317
+ this.log("toggleSkill() response:", response);
20318
+ return {
20319
+ success: response?.success ?? false,
20320
+ error: response?.error
20321
+ };
20322
+ } catch (error) {
20323
+ this.log("toggleSkill() failed:", error);
20324
+ return {
20325
+ success: false,
20326
+ error: error instanceof Error ? error.message : "Unknown error"
20327
+ };
20328
+ }
20329
+ }
20330
+ /**
20331
+ * 删除 Skill
20332
+ *
20333
+ * @param params 删除参数
20334
+ * @returns 删除结果
20335
+ */
20336
+ async deleteSkill(params) {
20337
+ this.log("Deleting skill:", params.name, "at", params.filePath);
20338
+ try {
20339
+ const response = await this.sharedAcpClient.sendBroadcastRequest("deleteSkill", {
20340
+ filePath: params.filePath,
20341
+ name: params.name
20342
+ });
20343
+ this.log("deleteSkill() response:", response);
20344
+ return {
20345
+ success: response?.success ?? false,
20346
+ error: response?.error
20347
+ };
20348
+ } catch (error) {
20349
+ this.log("deleteSkill() failed:", error);
20350
+ return {
20351
+ success: false,
20352
+ error: error instanceof Error ? error.message : "Unknown error"
20353
+ };
20354
+ }
20355
+ }
20356
+ /**
20357
+ * 获取 Skill 内容
20358
+ * 通过 shared ACP client 发送广播请求到 Extension Host
20359
+ *
20360
+ * @param params 包含 filePath 的参数
20361
+ * @returns Skill 文件内容
20362
+ */
20363
+ async getSkillContent(params) {
20364
+ this.log("Getting skill content:", params.filePath);
20365
+ try {
20366
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getSkillContent", { filePath: params.filePath });
20367
+ this.log("getSkillContent() response:", response);
20368
+ return {
20369
+ content: response?.content ?? "",
20370
+ error: response?.error
20371
+ };
20372
+ } catch (error) {
20373
+ this.log("getSkillContent() failed:", error);
20374
+ return {
20375
+ content: "",
20376
+ error: error instanceof Error ? error.message : "Unknown error"
20377
+ };
20378
+ }
20379
+ }
20380
+ /**
20381
+ * 获取推荐 Skill 列表(从市场)
20382
+ * 通过 shared ACP client 发送广播请求到 Extension Host
20383
+ *
20384
+ * @returns 推荐 Skill 列表
20385
+ */
20386
+ async getMarketplaceSkills() {
20387
+ this.log("Getting marketplace skills");
20388
+ try {
20389
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getMarketplaceSkills", {});
20390
+ this.log("getMarketplaceSkills() response:", response);
20391
+ return { results: response?.skills ?? [] };
20392
+ } catch (error) {
20393
+ this.log("getMarketplaceSkills() failed:", error);
20394
+ return {
20395
+ results: [],
20396
+ error: error instanceof Error ? error.message : "Unknown error"
20397
+ };
20398
+ }
20399
+ }
20400
+ /**
20401
+ * 获取市场 Skill 内容
20402
+ * 通过 shared ACP client 发送广播请求到 Extension Host
20403
+ *
20404
+ * @param params 包含 skillName 的参数
20405
+ * @returns Skill 文件内容
20406
+ */
20407
+ async getMarketplaceSkillContent(params) {
20408
+ this.log("Getting marketplace skill content:", params.skillName);
20409
+ try {
20410
+ const response = await this.sharedAcpClient.sendBroadcastRequest("getMarketplaceSkillContent", { skillName: params.skillName });
20411
+ this.log("getMarketplaceSkillContent() response:", response);
20412
+ return {
20413
+ content: response?.content ?? "",
20414
+ error: response?.error
20415
+ };
20416
+ } catch (error) {
20417
+ this.log("getMarketplaceSkillContent() failed:", error);
20418
+ return {
20419
+ content: "",
20420
+ error: error instanceof Error ? error.message : "Unknown error"
20421
+ };
20422
+ }
20423
+ }
20424
+ /**
20425
+ * 安装市场 Skill 到用户目录
20426
+ * 通过 shared ACP client 发送广播请求到 Extension Host
20427
+ *
20428
+ * @param params 包含 skillName 的参数
20429
+ * @returns 安装结果
20430
+ */
20431
+ async installMarketplaceSkill(params) {
20432
+ this.log("Installing marketplace skill:", params.skillName);
20433
+ try {
20434
+ const response = await this.sharedAcpClient.sendBroadcastRequest("installMarketplaceSkill", { skillName: params.skillName });
20435
+ this.log("installMarketplaceSkill() response:", response);
20436
+ return {
20437
+ success: response?.success ?? false,
20438
+ skillName: response?.skillName ?? params.skillName,
20439
+ errorMessage: response?.errorMessage
20440
+ };
20441
+ } catch (error) {
20442
+ this.log("installMarketplaceSkill() failed:", error);
20443
+ return {
20444
+ success: false,
20445
+ skillName: params.skillName,
20446
+ errorMessage: error instanceof Error ? error.message : "Unknown error"
20447
+ };
20448
+ }
20449
+ }
20450
+ /**
20451
+ * 批量切换插件状态
20452
+ * 通过 ACP 协议调用 Extension Host 的 PluginService
20453
+ *
20454
+ * @param request 批量插件操作请求
20455
+ * @returns 批量操作结果
20456
+ */
20457
+ async batchTogglePlugins(request) {
20458
+ this.log("Batch toggling plugins:", request);
20459
+ try {
20460
+ const result = await this.sharedAcpClient.batchTogglePlugins(request);
20461
+ this.log("batchTogglePlugins() response:", result);
20462
+ return {
20463
+ success: result.success,
20464
+ succeededPlugins: result.succeededPlugins,
20465
+ failedPlugins: result.failedPlugins.map((item) => ({
20466
+ pluginName: item.pluginName,
20467
+ marketplaceName: item.marketplaceName,
20468
+ scope: item.scope,
20469
+ operation: item.operation,
20470
+ error: item.error ?? "Unknown error"
20471
+ }))
20472
+ };
20473
+ } catch (error) {
20474
+ this.log("batchTogglePlugins() failed:", error);
20475
+ return {
20476
+ success: false,
20477
+ succeededPlugins: [],
20478
+ failedPlugins: request.items.map((item) => ({
20479
+ ...item,
20480
+ error: error instanceof Error ? error.message : "Unknown error"
20481
+ }))
20482
+ };
20483
+ }
20484
+ }
20485
+ /**
20486
+ * 获取当前工作区列表
20487
+ * 这是 LocalAgentProvider 特有的能力,通过 shared ACP client 发送请求,
20488
+ * 使用 __workspace__ session ID,由 Main Process 直接处理
20489
+ *
20490
+ * @param filter 可选的过滤参数
20491
+ * @returns 工作区列表
20492
+ */
20493
+ async getCurrentWorkspaces(filter) {
20494
+ this.log("Getting current workspaces:", filter);
20495
+ try {
20496
+ return (await this.sharedAcpClient.getCurrentWorkspaces({ filter })).workspaces ?? [];
20497
+ } catch (error) {
20498
+ this.log("getCurrentWorkspaces() failed:", error);
20499
+ return [];
20500
+ }
20501
+ }
20502
+ async getAutomationSnapshot() {
20503
+ this.log("Getting automation snapshot");
20504
+ return await this.sharedAcpClient.getAutomationSnapshot();
20505
+ }
20506
+ async updateAutomation(payload) {
20507
+ this.log("Updating automation:", payload?.id || payload?.name || "(new)");
20508
+ return await this.sharedAcpClient.updateAutomation(payload);
20509
+ }
20510
+ async deleteAutomation(id) {
20511
+ this.log("Deleting automation:", id);
20512
+ return await this.sharedAcpClient.deleteAutomation({ id });
20513
+ }
20514
+ async archiveAutomationInboxItem(itemId) {
20515
+ this.log("Archiving automation inbox item:", itemId);
20516
+ return await this.sharedAcpClient.archiveAutomationInboxItem({ itemId });
20517
+ }
20518
+ async deleteAutomationInboxItem(itemId) {
20519
+ this.log("Deleting automation inbox item:", itemId);
20520
+ return await this.sharedAcpClient.deleteAutomationInboxItem({ itemId });
20521
+ }
20522
+ async testAutomation(id) {
20523
+ this.log("Testing automation:", id);
20524
+ return await this.sharedAcpClient.testAutomation({ id });
20525
+ }
20526
+ /**
20527
+ * 获取已安装插件列表
20528
+ * 通过 shared ACP client 发送请求
20529
+ *
20530
+ * @param forceRefresh 是否强制刷新缓存
20531
+ * @returns 已安装插件列表
20532
+ */
20533
+ async getInstalledPlugins(forceRefresh) {
20534
+ this.log("Getting installed plugins, forceRefresh:", forceRefresh);
20535
+ try {
20536
+ const result = await this.sharedAcpClient.getInstalledPlugins({ forceRefresh });
20537
+ this.log(`Got ${result.plugins?.length ?? 0} installed plugins`);
20538
+ return result.plugins ?? [];
20539
+ } catch (error) {
20540
+ this.log("getInstalledPlugins() failed:", error);
20541
+ return [];
20542
+ }
20543
+ }
20544
+ /**
20545
+ * 安装插件
20546
+ * 通过 shared ACP client 发送请求
20547
+ *
20548
+ * @param pluginNames 插件名称数组
20549
+ * @param marketplaceName 插件市场名称
20550
+ * @param installScope 安装范围
20551
+ * @param marketplaceSource 市场源地址(当市场不存在时用于自动添加市场)
20552
+ * @returns 安装结果
20553
+ */
20554
+ async installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) {
20555
+ this.log("Installing plugins:", pluginNames, "from", marketplaceName, marketplaceSource ? `(source: ${marketplaceSource})` : "", workspacePath ? `(workspacePath: ${workspacePath})` : "");
20556
+ try {
20557
+ return await this.sharedAcpClient.installPlugins({
20558
+ pluginNames,
20559
+ marketplaceName,
20560
+ installScope,
20561
+ marketplaceSource,
20562
+ workspacePath
20563
+ });
20564
+ } catch (error) {
20565
+ this.log("installPlugins() failed:", error);
20566
+ return {
20567
+ success: false,
20568
+ error: error instanceof Error ? error.message : "Unknown error"
20569
+ };
20570
+ }
20571
+ }
20572
+ /**
20573
+ * 卸载插件
20574
+ * 通过 shared ACP client 发送请求
20575
+ *
20576
+ * @param pluginName 插件名称
20577
+ * @param marketplaceName 插件市场名称
20578
+ * @param scope 卸载范围
20579
+ * @returns 卸载结果
20580
+ */
20581
+ async uninstallPlugin(pluginName, marketplaceName, scope) {
20582
+ this.log("Uninstalling plugin:", pluginName, "from", marketplaceName, "scope:", scope);
20583
+ try {
20584
+ return await this.sharedAcpClient.uninstallPlugin({
20585
+ pluginName,
20586
+ marketplaceName,
20587
+ scope
20588
+ });
20589
+ } catch (error) {
20590
+ this.log("uninstallPlugin() failed:", error);
20591
+ return {
20592
+ success: false,
20593
+ error: error instanceof Error ? error.message : "Unknown error"
20594
+ };
20595
+ }
20596
+ }
20597
+ /**
20598
+ * 更新插件到最新版本
20599
+ * 通过 shared ACP client 发送请求
20600
+ *
20601
+ * @param pluginName 插件名称
20602
+ * @param marketplaceName 插件市场名称
20603
+ * @returns 更新结果
20604
+ */
20605
+ async updatePlugin(pluginName, marketplaceName) {
20606
+ this.log("Updating plugin:", pluginName, "from", marketplaceName);
20607
+ try {
20608
+ return await this.sharedAcpClient.updatePlugin({
20609
+ pluginName,
20610
+ marketplaceName
20611
+ });
20612
+ } catch (error) {
20613
+ this.log("updatePlugin() failed:", error);
20614
+ return {
20615
+ success: false,
20616
+ error: error instanceof Error ? error.message : "Unknown error"
20617
+ };
20618
+ }
20619
+ }
20620
+ /**
20621
+ * 获取插件市场列表
20622
+ * 通过 shared ACP client 发送请求
20623
+ *
20624
+ * @param forceRefresh 是否强制刷新缓存
20625
+ * @returns 插件市场列表
20626
+ */
20627
+ async getPluginMarketplaces(forceRefresh) {
20628
+ this.log("Getting plugin marketplaces, forceRefresh:", forceRefresh);
20629
+ try {
20630
+ const result = await this.sharedAcpClient.getPluginMarketplaces({ forceRefresh });
20631
+ this.log(`Got ${result.marketplaces?.length ?? 0} marketplaces`);
20632
+ return result.marketplaces ?? [];
20633
+ } catch (error) {
20634
+ this.log("getPluginMarketplaces() failed:", error);
20635
+ return [];
20636
+ }
20637
+ }
20638
+ /**
20639
+ * 获取市场下的插件列表
20640
+ * 通过 shared ACP client 发送请求
20641
+ *
20642
+ * @param marketplaceName 市场名称
20643
+ * @param forceRefresh 是否强制刷新缓存
20644
+ * @param searchText 搜索关键词
20645
+ * @returns 插件列表
20646
+ */
20647
+ async getMarketplacePlugins(marketplaceName, forceRefresh, searchText) {
20648
+ this.log("Getting marketplace plugins:", marketplaceName, "forceRefresh:", forceRefresh, "searchText:", searchText);
20649
+ try {
20650
+ const result = await this.sharedAcpClient.getMarketplacePlugins({
20651
+ marketplaceName,
20652
+ forceRefresh,
20653
+ searchText
20654
+ });
20655
+ this.log(`Got ${result.plugins?.length ?? 0} plugins from marketplace ${marketplaceName}`);
20656
+ return result.plugins ?? [];
20657
+ } catch (error) {
20658
+ this.log("getMarketplacePlugins() failed:", error);
20659
+ return [];
20660
+ }
20661
+ }
20662
+ /**
20663
+ * 获取插件详情
20664
+ * 通过 shared ACP client 发送请求
20665
+ *
20666
+ * @param pluginName 插件名称
20667
+ * @param marketplaceName 市场名称
20668
+ * @returns 插件详情
20669
+ */
20670
+ async getPluginDetail(pluginName, marketplaceName) {
20671
+ this.log("Getting plugin detail:", pluginName, "from", marketplaceName);
20672
+ try {
20673
+ return (await this.sharedAcpClient.getPluginDetail({
20674
+ pluginName,
20675
+ marketplaceName
20676
+ })).plugin ?? null;
20677
+ } catch (error) {
20678
+ this.log("getPluginDetail() failed:", error);
20679
+ return null;
20680
+ }
20681
+ }
20682
+ /**
20683
+ * 添加插件市场
20684
+ * 通过 shared ACP client 发送请求
20685
+ *
20686
+ * @param source 市场源 URL 或 GitHub repo
20687
+ * @param name 市场名称(可选)
20688
+ * @returns 添加结果
20689
+ */
20690
+ async addPluginMarketplace(source, name) {
20691
+ this.log("Adding plugin marketplace:", source, name ? `as ${name}` : "");
20692
+ try {
20693
+ return await this.sharedAcpClient.addPluginMarketplace({
20694
+ source,
20695
+ name
20696
+ });
20697
+ } catch (error) {
20698
+ this.log("addPluginMarketplace() failed:", error);
20699
+ return {
20700
+ success: false,
20701
+ error: error instanceof Error ? error.message : "Unknown error"
20702
+ };
20703
+ }
20704
+ }
20705
+ /**
20706
+ * 删除插件市场
20707
+ * 通过 shared ACP client 发送请求
20708
+ *
20709
+ * @param marketplaceName 市场名称
20710
+ * @returns 删除结果
20711
+ */
20712
+ async removePluginMarketplace(marketplaceName) {
20713
+ this.log("Removing plugin marketplace:", marketplaceName);
20714
+ try {
20715
+ return await this.sharedAcpClient.removePluginMarketplace({ marketplaceName });
20716
+ } catch (error) {
20717
+ this.log("removePluginMarketplace() failed:", error);
20718
+ return {
20719
+ success: false,
20720
+ error: error instanceof Error ? error.message : "Unknown error"
20721
+ };
20722
+ }
20723
+ }
20724
+ /**
20725
+ * 刷新插件市场
20726
+ * 通过 shared ACP client 发送请求
20727
+ *
20728
+ * @param marketplaceName 市场名称
20729
+ * @returns 刷新结果
20730
+ */
20731
+ async refreshPluginMarketplace(marketplaceName) {
20732
+ this.log("Refreshing plugin marketplace:", marketplaceName);
20733
+ try {
20734
+ return await this.sharedAcpClient.refreshPluginMarketplace({ marketplaceName });
20735
+ } catch (error) {
20736
+ this.log("refreshPluginMarketplace() failed:", error);
20737
+ return {
20738
+ success: false,
20739
+ error: error instanceof Error ? error.message : "Unknown error"
20740
+ };
20741
+ }
20742
+ }
20743
+ /**
20744
+ * 用新窗口打开文件夹
20745
+ * 通过 shared ACP client 发送请求到 Extension Host
20746
+ *
20747
+ * 调用链:
20748
+ * 1. client.sessions.openFolderInNewWindow() 调用此方法
20749
+ * 2. LocalAgentProvider -> AcpJsonRpcClient.openFolderInNewWindow()
20750
+ * 3. Extension Host -> PluginService.openFolderInNewWindow()
20751
+ * 4. VS Code 执行 vscode.openFolder 命令
20752
+ *
20753
+ * @param folderPath 文件夹路径
20754
+ */
20755
+ async openFolderInNewWindow(folderPath) {
20756
+ this.log("Opening folder in new window:", folderPath);
20757
+ try {
20758
+ await this.sharedAcpClient.openFolderInNewWindow({ folderPath });
20759
+ } catch (error) {
20760
+ this.log("openFolderInNewWindow() failed:", error);
20761
+ throw error;
20762
+ }
20763
+ }
20764
+ /**
20765
+ * 在系统文件管理器中打开目录
20766
+ * 通过 __backend__ 路由到 BackendService → Main Process → shell.openPath
20767
+ *
20768
+ * @param folderPath 文件夹路径
20769
+ */
20770
+ async openFolder(folderPath) {
20771
+ this.log("Opening folder in system file manager:", folderPath);
20772
+ try {
20773
+ const response = await this.channel.callMethod("__backend__", {
20774
+ type: "backend",
20775
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
20776
+ params: {
20777
+ type: "backend:open-folder",
20778
+ params: { folderPath }
20779
+ }
20780
+ }, 1e4);
20781
+ this.log("openFolder() response:", response.data);
20782
+ return response.data;
20783
+ } catch (error) {
20784
+ this.log("openFolder() failed:", error);
20785
+ throw error;
20786
+ }
20787
+ }
20788
+ /**
20789
+ * 获取支持的场景列表(从后端 API)
20790
+ * 通过 shared ACP client 发送请求到 Extension Host
20791
+ * 用于 Welcome 页面的 QuickActions 快捷操作
20792
+ *
20793
+ * 调用链:
20794
+ * 1. client.sessions.getSupportScenes() 调用此方法
20795
+ * 2. LocalAgentProvider -> AcpJsonRpcClient.getSupportScenes()
20796
+ * 3. Extension Host 调用 RestOperations -> GET /v2/as/support/scenes
20797
+ * 4. 返回 SupportSceneInfo[] 数据
20798
+ *
20799
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
20800
+ * @returns 支持的场景列表
20801
+ */
20802
+ async getSupportScenes(locale) {
20803
+ this.log("Getting support scenes", locale ? `with locale: ${locale}` : "");
20804
+ try {
20805
+ const result = await this.sharedAcpClient.getSupportScenes({ locale });
20806
+ this.log(`Got ${result.scenes?.length ?? 0} support scenes`);
20807
+ return result.scenes ?? [];
20808
+ } catch (error) {
20809
+ this.log("getSupportScenes() failed:", error);
20810
+ return [];
20811
+ }
20812
+ }
20813
+ /**
20814
+ * 获取产品配置中的场景列表
20815
+ * 通过 BackendService 的 __backend__ session 路由到 Main Process 直接读取本地 scenes.json 文件
20816
+ * 无需等待 Extension Host 初始化,页面打开时即可快速加载
20817
+ *
20818
+ * 调用链:
20819
+ * 1. LocalAgentProvider.getProductScenes()
20820
+ * 2. channel.callMethod('__backend__', ...) → BackendService.getProductScenes()
20821
+ * 3. ipcRenderer.invoke('codebuddy:getProductScenes') → Main Process
20822
+ * 4. fs.readFile(~/.codebuddy/plugins/marketplaces/cb_teams_marketplace/scenes.json)
20823
+ * 5. 返回 SupportSceneInfo[] 数据
20824
+ *
20825
+ * @param locale - 可选,语言环境
20826
+ * @returns 产品配置中的场景列表
20827
+ */
20828
+ async getProductScenes(locale) {
20829
+ this.log("Getting product scenes", locale ? `with locale: ${locale}` : "");
20830
+ try {
20831
+ const scenes = (await this.channel.callMethod("__backend__", {
20832
+ type: "backend",
20833
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
20834
+ params: {
20835
+ type: "backend:get-product-scenes",
20836
+ params: { locale }
20837
+ }
20838
+ }, 1e4)).data?.scenes ?? [];
20839
+ this.log(`Got ${scenes.length} product scenes`);
20840
+ return scenes;
20841
+ } catch (error) {
20842
+ this.log("getProductScenes() failed:", error);
20843
+ return [];
20844
+ }
20845
+ }
20846
+ /**
20847
+ * 获取会话的可用命令列表
20848
+ * 通过 shared ACP client 发送请求到 Extension Host
20849
+ * 如果 session.availableCommands 有值则返回缓存值,否则从后端获取
20850
+ *
20851
+ * 调用链:
20852
+ * 1. client.sessions.getAvailableCommands(params) 调用此方法
20853
+ * 2. LocalAgentProvider -> AcpJsonRpcClient.sendRequest()
20854
+ * 3. Extension Host 返回命令列表
20855
+ *
20856
+ * @param params - 命令获取参数
20857
+ * @returns 可用命令列表
20858
+ */
20859
+ async getAvailableCommands(params) {
20860
+ this.log("Getting available commands for session:", params?.sessionId ?? "(default)");
20861
+ try {
20862
+ let result;
20863
+ if (params?.sessionId) result = await this.sharedAcpClient.sendRequest(params.sessionId, "getAvailableCommands", params);
20864
+ else result = await this.sharedAcpClient.sendBroadcastRequest("getAvailableCommands", params || {});
20865
+ this.log(`Got ${result.availableCommands?.length ?? 0} available commands`);
20866
+ return result.availableCommands ?? [];
20867
+ } catch (error) {
20868
+ this.log("getAvailableCommands() failed:", error);
20869
+ return [];
20870
+ }
20871
+ }
20872
+ async requestYieldAfterCurrentStep(sessionId) {
20873
+ this.log(`requestYieldAfterCurrentStep() called: sessionId=${sessionId}`);
20874
+ try {
20875
+ const accepted = !!(await this.sharedAcpClient.sendRequest(sessionId, "requestYieldAfterCurrentStep", { sessionId }))?.accepted;
20876
+ this.log(`requestYieldAfterCurrentStep() completed: sessionId=${sessionId}, accepted=${accepted}`);
20877
+ return accepted;
20878
+ } catch (error) {
20879
+ this.log("requestYieldAfterCurrentStep() failed:", error);
20880
+ return false;
20881
+ }
20882
+ }
20883
+ /**
20884
+ * 上报 telemetry 事件
20885
+ * 通过 ACP 链路发送到 IDE 端,由 AcpAgentImpl → EventService 完成上报
20886
+ */
20887
+ async reportTelemetry(eventName, payload) {
20888
+ try {
20889
+ await this.sharedAcpClient.sendBroadcastRequest("reportTelemetry", {
20890
+ eventName,
20891
+ payload
20892
+ });
20893
+ } catch (error) {
20894
+ this.log("reportTelemetry() failed:", error);
20895
+ }
20896
+ }
20897
+ /**
20898
+ * 设置 MCP 事件监听器
20899
+ * 只需要设置一次,用于接收后端推送的确认请求
20900
+ */
20901
+ setupMcpEventListeners() {
20902
+ if (this.mcpEventListenersSetup) return;
20903
+ this.mcpEventListenersSetup = true;
20904
+ this.channel.on("mcp-sampling-confirm-request", (data) => {
20905
+ this.log("[MCP] Received sampling confirm request:", data);
20906
+ const serverName = data?.serverName;
20907
+ if (serverName) this.samplingRequestCallbacks.get(serverName)?.forEach((callback) => {
20908
+ try {
20909
+ callback(data);
20910
+ } catch (error) {
20911
+ this.log("[MCP] Sampling callback error:", error);
20912
+ }
20913
+ });
20914
+ });
20915
+ this.channel.on("mcp-roots-confirm-request", (data) => {
20916
+ this.log("[MCP] Received roots confirm request:", data);
20917
+ const serverName = data?.serverName;
20918
+ if (serverName) this.rootsRequestCallbacks.get(serverName)?.forEach((callback) => {
20919
+ try {
20920
+ callback(data);
20921
+ } catch (error) {
20922
+ this.log("[MCP] Roots callback error:", error);
20923
+ }
20924
+ });
20925
+ });
20926
+ this.log("[MCP] Event listeners setup completed");
20927
+ }
20928
+ /**
20929
+ * 响应 MCP Sampling 确认请求
20930
+ * 将用户的决策发送到后端
20931
+ *
20932
+ * @param sessionId 会话 ID
20933
+ * @param response Sampling 确认响应
20934
+ */
20935
+ async respondToSampling(sessionId, response) {
20936
+ this.log(`respondToSampling() called: sessionId=${sessionId}, requestId=${response.id}, approved=${response.approved}`);
20937
+ try {
20938
+ await this.sharedAcpClient.sendRequest(sessionId, "respondToSampling", {
20939
+ sessionId,
20940
+ ...response
20941
+ });
20942
+ this.log(`respondToSampling() completed for request: ${response.id}`);
20943
+ } catch (error) {
20944
+ this.log("respondToSampling() failed:", error);
20945
+ throw error;
20946
+ }
20947
+ }
20948
+ /**
20949
+ * 响应 MCP Roots 确认请求
20950
+ * 将用户的决策发送到后端
20951
+ *
20952
+ * @param sessionId 会话 ID
20953
+ * @param response Roots 确认响应
20954
+ */
20955
+ async respondToRoots(sessionId, response) {
20956
+ this.log(`respondToRoots() called: sessionId=${sessionId}, requestId=${response.id}, approved=${response.approved}`);
20957
+ try {
20958
+ await this.sharedAcpClient.sendRequest(sessionId, "respondToRoots", {
20959
+ sessionId,
20960
+ ...response
20961
+ });
20962
+ this.log(`respondToRoots() completed for request: ${response.id}`);
20963
+ } catch (error) {
20964
+ this.log("respondToRoots() failed:", error);
20965
+ throw error;
20966
+ }
20967
+ }
20968
+ /**
20969
+ * 订阅 MCP Sampling 确认请求
20970
+ * 当 MCP 服务器发起 Sampling 请求时触发回调
20971
+ *
20972
+ * @param serverName MCP 服务器名称
20973
+ * @param callback 请求回调
20974
+ * @returns 取消订阅函数
20975
+ */
20976
+ subscribeSamplingRequests(serverName, callback) {
20977
+ this.log(`subscribeSamplingRequests() called for server: ${serverName}`);
20978
+ this.setupMcpEventListeners();
20979
+ let callbacks = this.samplingRequestCallbacks.get(serverName);
20980
+ if (!callbacks) {
20981
+ callbacks = /* @__PURE__ */ new Set();
20982
+ this.samplingRequestCallbacks.set(serverName, callbacks);
20983
+ }
20984
+ callbacks.add(callback);
20985
+ return () => {
20986
+ this.log(`Unsubscribing sampling requests for server: ${serverName}`);
20987
+ const cbs = this.samplingRequestCallbacks.get(serverName);
20988
+ cbs?.delete(callback);
20989
+ if (cbs?.size === 0) this.samplingRequestCallbacks.delete(serverName);
20990
+ };
20991
+ }
20992
+ /**
20993
+ * 订阅 MCP Roots 确认请求
20994
+ * 当 MCP 服务器发起 Roots 请求时触发回调
20995
+ *
20996
+ * @param serverName MCP 服务器名称
20997
+ * @param callback 请求回调
20998
+ * @returns 取消订阅函数
20999
+ */
21000
+ subscribeRootsRequests(serverName, callback) {
21001
+ this.log(`subscribeRootsRequests() called for server: ${serverName}`);
21002
+ this.setupMcpEventListeners();
21003
+ let callbacks = this.rootsRequestCallbacks.get(serverName);
21004
+ if (!callbacks) {
21005
+ callbacks = /* @__PURE__ */ new Set();
21006
+ this.rootsRequestCallbacks.set(serverName, callbacks);
21007
+ }
21008
+ callbacks.add(callback);
21009
+ return () => {
21010
+ this.log(`Unsubscribing roots requests for server: ${serverName}`);
21011
+ const cbs = this.rootsRequestCallbacks.get(serverName);
21012
+ cbs?.delete(callback);
21013
+ if (cbs?.size === 0) this.rootsRequestCallbacks.delete(serverName);
21014
+ };
21015
+ }
21016
+ /**
21017
+ * 获取灵感卡片列表
21018
+ *
21019
+ * @param query - 查询参数(分页、分类、日期筛选等)
21020
+ * @returns 灵感卡片列表结果
21021
+ */
21022
+ async getInspirations(query) {
21023
+ this.log("getInspirations() called:", query);
21024
+ try {
21025
+ const response = await this.channel.callMethod("__backend__", {
21026
+ type: "backend",
21027
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21028
+ params: {
21029
+ type: "backend:inspiration-list",
21030
+ params: query
21031
+ }
21032
+ }, 1e4);
21033
+ this.log("getInspirations() response:", response.data);
21034
+ return response.data;
21035
+ } catch (error) {
21036
+ this.log("getInspirations() failed:", error);
21037
+ return {
21038
+ cards: [],
21039
+ total: 0
21040
+ };
21041
+ }
21042
+ }
21043
+ /**
21044
+ * 获取灵感卡片详情
21045
+ *
21046
+ * @param params - 包含 cardId 的参数
21047
+ * @returns 灵感卡片详情
21048
+ */
21049
+ async getInspirationDetail(params) {
21050
+ this.log("getInspirationDetail() called:", params);
21051
+ try {
21052
+ const response = await this.channel.callMethod("__backend__", {
21053
+ type: "backend",
21054
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21055
+ params: {
21056
+ type: "backend:inspiration-detail",
21057
+ params
21058
+ }
21059
+ }, 5e3);
21060
+ this.log("getInspirationDetail() response:", response.data);
21061
+ return response.data;
21062
+ } catch (error) {
21063
+ this.log("getInspirationDetail() failed:", error);
21064
+ return;
21065
+ }
21066
+ }
21067
+ /**
21068
+ * 标记灵感卡片为已读
21069
+ *
21070
+ * @param params - 包含 cardId 的参数
21071
+ * @returns 操作结果
21072
+ */
21073
+ async markInspirationRead(params) {
21074
+ this.log("markInspirationRead() called:", params);
21075
+ try {
21076
+ return (await this.channel.callMethod("__backend__", {
21077
+ type: "backend",
21078
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21079
+ params: {
21080
+ type: "backend:inspiration-mark-read",
21081
+ params
21082
+ }
21083
+ }, 5e3)).data;
21084
+ } catch (error) {
21085
+ this.log("markInspirationRead() failed:", error);
21086
+ return { success: false };
21087
+ }
21088
+ }
21089
+ /**
21090
+ * 对灵感卡片提交反馈
21091
+ *
21092
+ * @param feedback - 反馈数据(cardId、feedbackType 等)
21093
+ * @returns 操作结果
21094
+ */
21095
+ async addInspirationFeedback(feedback) {
21096
+ this.log("addInspirationFeedback() called:", feedback);
21097
+ try {
21098
+ return (await this.channel.callMethod("__backend__", {
21099
+ type: "backend",
21100
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21101
+ params: {
21102
+ type: "backend:inspiration-feedback",
21103
+ params: feedback
21104
+ }
21105
+ }, 5e3)).data;
21106
+ } catch (error) {
21107
+ this.log("addInspirationFeedback() failed:", error);
21108
+ return { success: false };
21109
+ }
21110
+ }
21111
+ /**
21112
+ * 收藏/取消收藏灵感卡片
21113
+ *
21114
+ * @param params - 包含 cardId 的参数
21115
+ * @returns 操作结果
21116
+ */
21117
+ async saveInspiration(params) {
21118
+ this.log("saveInspiration() called:", params);
21119
+ try {
21120
+ return (await this.channel.callMethod("__backend__", {
21121
+ type: "backend",
21122
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21123
+ params: {
21124
+ type: "backend:inspiration-save",
21125
+ params
21126
+ }
21127
+ }, 5e3)).data;
21128
+ } catch (error) {
21129
+ this.log("saveInspiration() failed:", error);
21130
+ return { success: false };
21131
+ }
21132
+ }
21133
+ /**
21134
+ * 获取灵感功能用户配置
21135
+ *
21136
+ * @returns 用户配置
21137
+ */
21138
+ async getInspirationSettings() {
21139
+ this.log("getInspirationSettings() called");
21140
+ try {
21141
+ const response = await this.channel.callMethod("__backend__", {
21142
+ type: "backend",
21143
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21144
+ params: {
21145
+ type: "backend:inspiration-settings-get",
21146
+ params: {}
21147
+ }
21148
+ }, 5e3);
21149
+ this.log("getInspirationSettings() response:", response.data);
21150
+ return response.data;
21151
+ } catch (error) {
21152
+ this.log("getInspirationSettings() failed:", error);
21153
+ return {};
21154
+ }
21155
+ }
21156
+ /**
21157
+ * 保存灵感功能用户配置
21158
+ *
21159
+ * @param config - 用户配置数据
21160
+ * @returns 操作结果
21161
+ */
21162
+ async saveInspirationSettings(config) {
21163
+ this.log("saveInspirationSettings() called:", config);
21164
+ try {
21165
+ return (await this.channel.callMethod("__backend__", {
21166
+ type: "backend",
21167
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21168
+ params: {
21169
+ type: "backend:inspiration-settings-save",
21170
+ params: config
21171
+ }
21172
+ }, 5e3)).data;
21173
+ } catch (error) {
21174
+ this.log("saveInspirationSettings() failed:", error);
21175
+ return { success: false };
21176
+ }
21177
+ }
21178
+ /**
21179
+ * 检查灵感引导是否完成
21180
+ *
21181
+ * @returns 引导完成状态
21182
+ */
21183
+ async checkInspirationOnboarding() {
21184
+ this.log("checkInspirationOnboarding() called");
21185
+ try {
21186
+ return (await this.channel.callMethod("__backend__", {
21187
+ type: "backend",
21188
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21189
+ params: {
21190
+ type: "backend:inspiration-onboarding-check",
21191
+ params: {}
21192
+ }
21193
+ }, 5e3)).data;
21194
+ } catch (error) {
21195
+ this.log("checkInspirationOnboarding() failed:", error);
21196
+ return { completed: false };
21197
+ }
21198
+ }
21199
+ /**
21200
+ * 完成灵感引导
21201
+ *
21202
+ * @returns 操作结果
21203
+ */
21204
+ async completeInspirationOnboarding() {
21205
+ this.log("completeInspirationOnboarding() called");
21206
+ try {
21207
+ return (await this.channel.callMethod("__backend__", {
21208
+ type: "backend",
21209
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21210
+ params: {
21211
+ type: "backend:inspiration-onboarding-complete",
21212
+ params: {}
21213
+ }
21214
+ }, 5e3)).data;
21215
+ } catch (error) {
21216
+ this.log("completeInspirationOnboarding() failed:", error);
21217
+ return { success: false };
21218
+ }
21219
+ }
21220
+ /**
21221
+ * 注入 demo 灵感卡片(首次引导完成后调用)
21222
+ *
21223
+ * @returns 操作结果
21224
+ */
21225
+ async injectDemoInspirationCards() {
21226
+ this.log("injectDemoInspirationCards() called");
21227
+ try {
21228
+ return (await this.channel.callMethod("__backend__", {
21229
+ type: "backend",
21230
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21231
+ params: {
21232
+ type: "backend:inspiration-inject-demo",
21233
+ params: {}
21234
+ }
21235
+ }, 1e4)).data;
21236
+ } catch (error) {
21237
+ this.log("injectDemoInspirationCards() failed:", error);
21238
+ return { success: false };
21239
+ }
21240
+ }
21241
+ /**
21242
+ * 添加灵感策展指令
21243
+ *
21244
+ * @param params - 策展指令数据
21245
+ * @returns 操作结果
21246
+ */
21247
+ async addInspirationCuration(params) {
21248
+ this.log("addInspirationCuration() called:", params);
21249
+ try {
21250
+ return (await this.channel.callMethod("__backend__", {
21251
+ type: "backend",
21252
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21253
+ params: {
21254
+ type: "backend:inspiration-curation-add",
21255
+ params
21256
+ }
21257
+ }, 5e3)).data;
21258
+ } catch (error) {
21259
+ this.log("addInspirationCuration() failed:", error);
21260
+ return { success: false };
21261
+ }
21262
+ }
21263
+ /**
21264
+ * 获取灵感策展指令列表
21265
+ *
21266
+ * @returns 策展指令列表
21267
+ */
21268
+ async listInspirationCurations() {
21269
+ this.log("listInspirationCurations() called");
21270
+ try {
21271
+ const response = await this.channel.callMethod("__backend__", {
21272
+ type: "backend",
21273
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21274
+ params: {
21275
+ type: "backend:inspiration-curation-list",
21276
+ params: {}
21277
+ }
21278
+ }, 5e3);
21279
+ this.log("listInspirationCurations() response:", response.data);
21280
+ return response.data;
21281
+ } catch (error) {
21282
+ this.log("listInspirationCurations() failed:", error);
21283
+ return { directives: [] };
21284
+ }
21285
+ }
21286
+ /**
21287
+ * 更新灵感策展指令
21288
+ *
21289
+ * @param params - 包含 directiveId 和更新字段的参数
21290
+ * @returns 操作结果
21291
+ */
21292
+ async updateInspirationCuration(params) {
21293
+ this.log("updateInspirationCuration() called:", params);
21294
+ try {
21295
+ return (await this.channel.callMethod("__backend__", {
21296
+ type: "backend",
21297
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21298
+ params: {
21299
+ type: "backend:inspiration-curation-update",
21300
+ params
21301
+ }
21302
+ }, 5e3)).data;
21303
+ } catch (error) {
21304
+ this.log("updateInspirationCuration() failed:", error);
21305
+ return { success: false };
21306
+ }
21307
+ }
21308
+ /**
21309
+ * 删除灵感策展指令
21310
+ *
21311
+ * @param params - 包含 directiveId 的参数
21312
+ * @returns 操作结果
21313
+ */
21314
+ async deleteInspirationCuration(params) {
21315
+ this.log("deleteInspirationCuration() called:", params);
21316
+ try {
21317
+ return (await this.channel.callMethod("__backend__", {
21318
+ type: "backend",
21319
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21320
+ params: {
21321
+ type: "backend:inspiration-curation-delete",
21322
+ params
21323
+ }
21324
+ }, 5e3)).data;
21325
+ } catch (error) {
21326
+ this.log("deleteInspirationCuration() failed:", error);
21327
+ return { success: false };
21328
+ }
21329
+ }
21330
+ /**
21331
+ * 生成灵感测试数据
21332
+ *
21333
+ * @returns 生成结果
21334
+ */
21335
+ async generateInspirationTestData() {
21336
+ this.log("generateInspirationTestData() called");
21337
+ try {
21338
+ const response = await this.channel.callMethod("__backend__", {
21339
+ type: "backend",
21340
+ requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21341
+ params: {
21342
+ type: "backend:inspiration-generate-test-data",
21343
+ params: {}
21344
+ }
21345
+ }, 1e4);
21346
+ this.log("generateInspirationTestData() response:", response.data);
21347
+ return response.data;
21348
+ } catch (error) {
21349
+ this.log("generateInspirationTestData() failed:", error);
21350
+ return { success: false };
21351
+ }
21352
+ }
21353
+ /**
21354
+ * 获取已有连接
21355
+ *
21356
+ * @param cwd - 工作区路径
21357
+ * @returns LocalAgentConnection 实例或 undefined
21358
+ */
21359
+ getConnection(cwd) {
21360
+ return this.connections.get(cwd);
21361
+ }
21362
+ /**
21363
+ * 检查是否存在连接
21364
+ *
21365
+ * @param cwd - 工作区路径
21366
+ * @returns 是否存在连接
21367
+ */
21368
+ hasConnection(cwd) {
21369
+ return this.connections.has(cwd);
21370
+ }
21371
+ /**
21372
+ * 断开所有连接
21373
+ */
21374
+ disconnectAll() {
21375
+ for (const connection of this.connections.values()) connection.disconnect();
21376
+ this.connections.clear();
21377
+ this.sessionCwdMap.clear();
21378
+ for (const fs of this.filesystemCache.values()) fs.destroy();
21379
+ this.filesystemCache.clear();
21380
+ this.samplingRequestCallbacks.clear();
21381
+ this.rootsRequestCallbacks.clear();
21382
+ }
21383
+ /**
21384
+ * 获取 Filesystem 实例
21385
+ *
21386
+ * 对于 Local 模式,agentId 实际上是 sessionId
21387
+ * 通过已有的 MessagePort 通道将 filesystem 请求路由到 ExtensionHost
21388
+ *
21389
+ * @param agentId - Session ID(对于 Local 模式)
21390
+ * @returns FilesResource 实例
21391
+ */
21392
+ async getFilesystem(agentId) {
21393
+ this.log(`getFilesystem() called with: ${agentId}`);
21394
+ const cached = this.filesystemCache.get(agentId);
21395
+ if (cached) {
21396
+ this.log(`Returning cached filesystem for: ${agentId}`);
21397
+ return cached;
21398
+ }
21399
+ const filesystem = new LocalFilesystem(this.sharedAcpClient, agentId, { debug: this.options.debug });
21400
+ this.filesystemCache.set(agentId, filesystem);
21401
+ this.log(`Created new filesystem for: ${agentId}`);
21402
+ return filesystem;
21403
+ }
21404
+ /**
21405
+ * Register an event listener
21406
+ * Forwards to the underlying channel's event system
21407
+ *
21408
+ * @param event - Event name
21409
+ * @param handler - Event handler function
21410
+ */
21411
+ on(event, handler) {
21412
+ this.channel.on(event, handler);
21413
+ this.log(`Registered event listener: ${event}`);
21414
+ }
21415
+ /**
21416
+ * Unregister an event listener
21417
+ * Forwards to the underlying channel's event system
21418
+ *
21419
+ * @param event - Event name
21420
+ * @param handler - Event handler function to remove
21421
+ */
21422
+ off(event, handler) {
21423
+ this.channel.off(event, handler);
21424
+ this.log(`Unregistered event listener: ${event}`);
21425
+ }
21426
+ /**
21427
+ * 获取 MCP 服务器列表
21428
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21429
+ *
21430
+ * @returns MCP 服务器列表
21431
+ */
21432
+ async getMcpServers() {
21433
+ this.log("Getting MCP servers");
21434
+ try {
21435
+ const result = await this.sharedAcpClient.sendBroadcastRequest("getMcpServers", {});
21436
+ this.log(`Got ${result.servers?.length ?? 0} MCP servers`);
21437
+ return result.servers ?? [];
21438
+ } catch (error) {
21439
+ this.log("getMcpServers() failed:", error);
21440
+ return [];
21441
+ }
21442
+ }
21443
+ /**
21444
+ * 切换 MCP 服务器启用/禁用状态
21445
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21446
+ *
21447
+ * @param serverName 服务器名称
21448
+ * @param enabled 是否启用
21449
+ */
21450
+ async toggleMcpServer(serverName, enabled) {
21451
+ this.log(`Toggling MCP server: ${serverName}, enabled: ${enabled}`);
21452
+ try {
21453
+ await this.sharedAcpClient.sendBroadcastRequest("toggleMcpServer", {
21454
+ serverName,
21455
+ enabled
21456
+ });
21457
+ this.log(`toggleMcpServer() completed for: ${serverName}`);
21458
+ } catch (error) {
21459
+ this.log("toggleMcpServer() failed:", error);
21460
+ throw error;
21461
+ }
21462
+ }
21463
+ /**
21464
+ * 重新连接 MCP 服务器
21465
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21466
+ *
21467
+ * @param serverName 服务器名称
21468
+ * @param forceHttpCallback 是否强制使用 HTTP 回调(忽略 mcpSchemaUrl 配置)
21469
+ */
21470
+ async reconnectMcpServer(serverName, forceHttpCallback) {
21471
+ this.log(`Reconnecting MCP server: ${serverName}, forceHttpCallback: ${forceHttpCallback}`);
21472
+ try {
21473
+ await this.sharedAcpClient.sendBroadcastRequest("reconnectMcpServer", {
21474
+ serverName,
21475
+ forceHttpCallback
21476
+ });
21477
+ this.log(`reconnectMcpServer() completed for: ${serverName}`);
21478
+ } catch (error) {
21479
+ this.log("reconnectMcpServer() failed:", error);
21480
+ throw error;
21481
+ }
21482
+ }
21483
+ /**
21484
+ * 删除 MCP 服务器配置
21485
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21486
+ *
21487
+ * @param serverName 服务器名称
21488
+ */
21489
+ async deleteMcpServer(serverName) {
21490
+ this.log(`Deleting MCP server: ${serverName}`);
21491
+ try {
21492
+ await this.sharedAcpClient.sendBroadcastRequest("deleteMcpServer", { serverName });
21493
+ this.log(`deleteMcpServer() completed for: ${serverName}`);
21494
+ } catch (error) {
21495
+ this.log("deleteMcpServer() failed:", error);
21496
+ throw error;
21497
+ }
21498
+ }
21499
+ /**
21500
+ * 打开 MCP 配置文件
21501
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21502
+ * Extension Host 调用 VS Code 命令打开配置文件
21503
+ */
21504
+ async openMcpConfig() {
21505
+ this.log("Opening MCP config");
21506
+ try {
21507
+ await this.sharedAcpClient.sendBroadcastRequest("openMcpConfig", {});
21508
+ this.log("openMcpConfig() completed");
21509
+ } catch (error) {
21510
+ this.log("openMcpConfig() failed:", error);
21511
+ throw error;
21512
+ }
21513
+ }
21514
+ /**
21515
+ * 获取 MCP 配置文件内容
21516
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21517
+ * @returns 配置文件路径和内容
21518
+ */
21519
+ async getMcpConfigContent() {
21520
+ this.log("Getting MCP config content");
21521
+ try {
21522
+ const result = await this.sharedAcpClient.sendBroadcastRequest("getMcpConfigContent", {});
21523
+ this.log("getMcpConfigContent() completed:", result.filePath);
21524
+ return result;
21525
+ } catch (error) {
21526
+ this.log("getMcpConfigContent() failed:", error);
21527
+ throw error;
21528
+ }
21529
+ }
21530
+ /**
21531
+ * 保存 MCP 配置文件内容
21532
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21533
+ * @param content 新的配置内容
21534
+ */
21535
+ async saveMcpConfigContent(content) {
21536
+ this.log("Saving MCP config content");
21537
+ try {
21538
+ await this.sharedAcpClient.sendBroadcastRequest("saveMcpConfigContent", { content });
21539
+ this.log("saveMcpConfigContent() completed");
21540
+ } catch (error) {
21541
+ this.log("saveMcpConfigContent() failed:", error);
21542
+ throw error;
21543
+ }
21544
+ }
21545
+ /**
21546
+ * 读取系统剪贴板文本
21547
+ * 通过 shared ACP client 发送广播请求到 Extension Host
21548
+ * @returns 剪贴板文本内容
21549
+ */
21550
+ async clipboardReadText() {
21551
+ this.log("Reading clipboard text");
21552
+ try {
21553
+ const result = await this.sharedAcpClient.sendBroadcastRequest("clipboardReadText", {});
21554
+ this.log("clipboardReadText() completed");
21555
+ return result?.text ?? "";
21556
+ } catch (error) {
21557
+ this.log("clipboardReadText() failed:", error);
21558
+ throw error;
21559
+ }
21560
+ }
21561
+ log(...args) {
21562
+ if (this.options.debug) console.log("[LocalAgentProvider]", ...args);
21563
+ }
21564
+ };
21565
+
21566
+ //#endregion
21567
+ //#region ../agent-provider/src/common/client/session.ts
21568
+ /**
21569
+ * ActiveSessionImpl - Implements the ActiveSession interface
21570
+ *
21571
+ * This class wraps an AgentConnection and provides the session-centric API.
21572
+ * It is created by SessionManager when creating or loading sessions.
21573
+ *
21574
+ * @example
21575
+ * ```typescript
21576
+ * // Created by client.sessions.new() or client.sessions.load()
21577
+ * const session = await client.sessions.new({ cwd: '/workspace' });
21578
+ *
21579
+ * // Access agent state
21580
+ * console.log(session.agentState.status);
21581
+ *
21582
+ * // Send prompt
21583
+ * const response = await session.prompts.send({ content: 'Hello!' });
21584
+ *
21585
+ * // Cleanup
21586
+ * session.disconnect();
21587
+ * ```
21588
+ */
21589
+ var ActiveSessionImpl = class {
21590
+ /**
21591
+ * Create an ActiveSessionImpl instance
21592
+ *
21593
+ * @param sessionId - Session ID
21594
+ * @param agentId - Agent ID
21595
+ * @param connection - Already connected AgentConnection
21596
+ * @param options - Additional options
21597
+ */
21598
+ constructor(sessionId, agentId, connection, options = {}) {
21599
+ this._availableCommands = [];
21600
+ this.listeners = /* @__PURE__ */ new Map();
21601
+ this.onceListeners = /* @__PURE__ */ new Map();
21602
+ this.connectionListeners = [];
21603
+ this._id = sessionId;
21604
+ this._agentId = agentId;
21605
+ this.connection = connection;
21606
+ this.logger = options.logger;
21607
+ this._getFilesystem = options.getFilesystem;
21608
+ this._connectionInfo = options.connectionInfo;
21609
+ this.setupConnectionEvents(connection);
21610
+ this.agent = this.createAgentOperations();
21611
+ this.prompts = this.createPromptsResource();
21612
+ this.artifacts = this.createArtifactsResource();
21613
+ this.files = this.createFilesResource();
21614
+ }
21615
+ /**
21616
+ * Session ID
21617
+ */
21618
+ get id() {
21619
+ return this._id;
21620
+ }
21621
+ /**
21622
+ * Agent ID
21623
+ */
21624
+ get agentId() {
21625
+ return this._agentId;
21626
+ }
21627
+ /**
21628
+ * Actual workspace path (set from newSession response _meta)
21629
+ */
21630
+ get cwd() {
21631
+ return this._cwd;
21632
+ }
21633
+ /**
21634
+ * Set actual workspace path (called by SessionManager after createSession)
21635
+ */
21636
+ setCwd(cwd) {
21637
+ this._cwd = cwd;
21638
+ }
21639
+ /**
21640
+ * Agent state (live connection state)
21641
+ * Returns LocalAgentState or CloudAgentState based on transport type
21642
+ */
21643
+ get agentState() {
21644
+ return {
21645
+ id: this._agentId,
21646
+ status: this.connection.state,
21647
+ capabilities: this.connection.capabilities,
21648
+ type: this.connection.transport,
21649
+ cwd: this.connection.cwd || ""
21650
+ };
21651
+ }
21652
+ /**
21653
+ * Get agent capabilities (available after connection)
21654
+ */
21655
+ get capabilities() {
21656
+ return this.connection.capabilities;
21657
+ }
21658
+ /**
21659
+ * Available session modes
21660
+ */
21661
+ get availableModes() {
21662
+ return this._availableModes;
21663
+ }
21664
+ /**
21665
+ * Current session mode
21666
+ */
21667
+ get currentMode() {
21668
+ return this._currentMode;
21669
+ }
21670
+ /**
21671
+ * Available models for this session
21672
+ */
21673
+ get availableModels() {
21674
+ return this._availableModels;
21675
+ }
21676
+ /**
21677
+ * Current model ID
21678
+ */
21679
+ get currentModelId() {
21680
+ return this._currentModelId;
21681
+ }
21682
+ /**
21683
+ * Available slash commands
21684
+ *
21685
+ * When Agent sends available_commands_update, this list is automatically updated.
21686
+ * Commands can be accessed directly without waiting for events.
21687
+ */
21688
+ get availableCommands() {
21689
+ return this._availableCommands;
21690
+ }
21691
+ /**
21692
+ * Set available commands (called when available_commands_update is received)
21693
+ */
21694
+ setAvailableCommands(commands) {
21695
+ this._availableCommands = commands;
21696
+ this.logger?.info(`Session ${this._id}: Available commands updated, count: ${commands.length}`);
21697
+ }
21698
+ /**
21699
+ * Check if the session is active
21700
+ */
21701
+ get isActive() {
21702
+ return this.connection.isInitialized;
21703
+ }
21704
+ /**
21705
+ * Session connection information (only available for cloud sessions)
21706
+ * 会话连接信息,包括sandboxId、link、token等
21707
+ */
21708
+ get connectionInfo() {
21709
+ return this._connectionInfo;
21710
+ }
21711
+ /**
21712
+ * Set session modes (called after create/load)
21713
+ */
21714
+ setModes(availableModes, currentMode) {
21715
+ this._availableModes = availableModes;
21716
+ this._currentMode = currentMode;
21717
+ }
21718
+ /**
21719
+ * Set available models (called after create/load)
21720
+ */
21721
+ setModels(availableModels, currentModelId) {
21722
+ this._availableModels = availableModels;
21723
+ this._currentModelId = currentModelId;
21724
+ }
21725
+ createAgentOperations() {
21726
+ const self = this;
21727
+ return {
21728
+ get id() {
21729
+ return self._agentId;
21730
+ },
21731
+ get state() {
21732
+ return self.agentState;
21733
+ },
21734
+ get isConnected() {
21735
+ return self.connection.isInitialized;
21736
+ },
21737
+ get capabilities() {
21738
+ return self.connection.capabilities;
21739
+ }
21740
+ };
21741
+ }
21742
+ createPromptsResource() {
21743
+ return {
21744
+ send: async (params) => {
21745
+ const response = await this.getConnectionOrThrow().prompt(this._id, params);
21746
+ return this.mapPromptResponse(response);
21747
+ },
21748
+ stream: (params) => {
21749
+ return this.getConnectionOrThrow().promptStream(this._id, params);
21750
+ },
21751
+ cancel: async () => {
21752
+ await this.getConnectionOrThrow().cancel(this._id);
21753
+ }
21754
+ };
21755
+ }
21756
+ createArtifactsResource() {
21757
+ const notSupported = () => {
21758
+ throw new Error("Artifact management is no longer supported through this API");
21759
+ };
21760
+ return {
21761
+ list: async (_params) => {
21762
+ notSupported();
21763
+ return [];
21764
+ },
21765
+ retrieve: async (_artifactId) => {
21766
+ notSupported();
21767
+ },
21768
+ content: async (_artifactId) => {
21769
+ notSupported();
21770
+ return "";
21771
+ }
21772
+ };
21773
+ }
21774
+ /**
21775
+ * Create files resource with lazy-loaded filesystem
21776
+ *
18323
21777
  * The filesystem is lazily loaded on first use to avoid unnecessary
18324
21778
  * connections to the sandbox. The actual filesystem instance is obtained
18325
21779
  * via the getter function provided by SessionManager.
@@ -18665,6 +22119,7 @@ var ActiveSessionImpl = class {
18665
22119
  */
18666
22120
  var SessionManager = class {
18667
22121
  constructor(options) {
22122
+ this.pendingConnections = /* @__PURE__ */ new Map();
18668
22123
  this.provider = options.provider;
18669
22124
  this.logger = options.logger;
18670
22125
  }
@@ -18732,11 +22187,76 @@ var SessionManager = class {
18732
22187
  } else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
18733
22188
  const connection = await this.provider.connect(agentId);
18734
22189
  this.logger?.debug(`Connected to agent: ${agentId}`);
22190
+ let response;
22191
+ try {
22192
+ response = await connection.createSession({
22193
+ _meta: params.options?._meta,
22194
+ cwd: params.cwd,
22195
+ mcpServers: params.options?.mcpServers
22196
+ });
22197
+ } catch (err) {
22198
+ this.pendingConnections.set(agentId, connection);
22199
+ this.logger?.debug(`Cached pending connection for agent: ${agentId}`);
22200
+ if (err instanceof SessionError) throw new SessionError(err.message, err.sessionId, agentId, err.cause instanceof Error ? err.cause : void 0);
22201
+ const cause = err instanceof Error ? err : new Error(String(err));
22202
+ throw new SessionError(`Failed to create session: ${cause.message}`, void 0, agentId, cause);
22203
+ }
22204
+ return this.buildActiveSession(response, agentId, connection, params);
22205
+ }
22206
+ /**
22207
+ * Retry creating a session after initial session/new request failed.
22208
+ *
22209
+ * Use this when `sessions.create()` fails at the `session/new` step
22210
+ * (after agent creation and connection establishment succeeded).
22211
+ * The caller retrieves `agentId` from the thrown `SessionError.agentId`,
22212
+ * then calls this method to re-attempt only the `session/new` request
22213
+ * while reusing the already-initialized connection (no re-connect).
22214
+ *
22215
+ * @experimental This API is subject to change
22216
+ *
22217
+ * @param agentId - Agent ID from the failed SessionError
22218
+ * @param params - Original create session params (cwd, mcpServers, etc.)
22219
+ * @returns ActiveSession on success
22220
+ * @throws SessionError if session/new fails again
22221
+ *
22222
+ * @example
22223
+ * ```typescript
22224
+ * try {
22225
+ * const session = await client.sessions.create({ cwd: '/workspace' });
22226
+ * } catch (err) {
22227
+ * if (err instanceof SessionError && err.agentId) {
22228
+ * // Retry with custom logic
22229
+ * for (let i = 0; i < 3; i++) {
22230
+ * try {
22231
+ * const session = await client.sessions.retryNewSession(err.agentId, { cwd: '/workspace' });
22232
+ * break; // success
22233
+ * } catch (retryErr) {
22234
+ * await new Promise(r => setTimeout(r, 1000 * (i + 1)));
22235
+ * }
22236
+ * }
22237
+ * }
22238
+ * }
22239
+ * ```
22240
+ */
22241
+ async retryNewSession(agentId, params) {
22242
+ this.logger?.info(`Retrying session/new for agent: ${agentId}`);
22243
+ const connection = this.pendingConnections.get(agentId);
22244
+ if (!connection) throw new SessionError(`No pending connection found for agent: ${agentId}. retryNewSession() can only be called after a failed sessions.create().`, void 0, agentId);
22245
+ this.logger?.debug(`Reusing cached connection for agent: ${agentId}`);
18735
22246
  const response = await connection.createSession({
18736
22247
  _meta: params.options?._meta,
18737
22248
  cwd: params.cwd,
18738
22249
  mcpServers: params.options?.mcpServers
18739
22250
  });
22251
+ this.pendingConnections.delete(agentId);
22252
+ this.logger?.debug(`Cleared pending connection for agent: ${agentId}`);
22253
+ return this.buildActiveSession(response, agentId, connection, params);
22254
+ }
22255
+ /**
22256
+ * Build an ActiveSession from a NewSessionResponse.
22257
+ * Shared by createSession() and retryNewSession().
22258
+ */
22259
+ buildActiveSession(response, agentId, connection, params) {
18740
22260
  if (this.provider.registerSession) {
18741
22261
  this.provider.registerSession(response.sessionId, agentId);
18742
22262
  this.logger?.debug(`Registered session mapping: ${response.sessionId} → ${agentId}`);
@@ -18880,6 +22400,7 @@ var AgentClient = class {
18880
22400
  return {
18881
22401
  list: async (options) => this.sessionManager.listSessions(options),
18882
22402
  create: async (params) => this.sessionManager.createSession(params),
22403
+ retryNewSession: async (agentId, params) => this.sessionManager.retryNewSession(agentId, params),
18883
22404
  load: async (params) => {
18884
22405
  console.log("[AgentClient] sessions.load called:", params.sessionId);
18885
22406
  return this.sessionManager.loadSession(params);
@@ -20744,6 +24265,28 @@ var BackendProvider = class BackendProvider {
20744
24265
  if (result?.code === 0 && result?.data) return result.data;
20745
24266
  throw new Error(result?.msg || "Checkin failed");
20746
24267
  }
24268
+ /**
24269
+ * 获取专家排行榜数据
24270
+ * API 端点: GET /console/expert/ranking
24271
+ * 完全对齐 getCheckinStatus 的调用方式
24272
+ */
24273
+ async getExpertRanking() {
24274
+ try {
24275
+ const result = await httpService.get("/console/expert/ranking");
24276
+ if (result?.code === 0 && result?.data) {
24277
+ const normalizedItems = Array.isArray(result.data.items) ? result.data.items : Array.isArray(result.data.experts) ? result.data.experts : [];
24278
+ return {
24279
+ items: normalizedItems,
24280
+ total: result.data.total ?? normalizedItems.length,
24281
+ updateTime: result.data.updateTime ?? Date.now()
24282
+ };
24283
+ }
24284
+ return null;
24285
+ } catch (error) {
24286
+ console.error("[BackendProvider] getExpertRanking failed:", error);
24287
+ return null;
24288
+ }
24289
+ }
20747
24290
  static {
20748
24291
  this.SKILLHUB_BASE_URL = "https://lightmake.site";
20749
24292
  }
@@ -20860,13 +24403,20 @@ const BACKEND_REQUEST_TYPES = {
20860
24403
  GET_FILE: "backend:get-file",
20861
24404
  RELOAD_WINDOW: "backend:reload-window",
20862
24405
  SAVE_LOCALE: "backend:save-locale",
24406
+ SAVE_POWER_BLOCKER: "backend:save-power-blocker",
20863
24407
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
20864
24408
  OPEN_EXTERNAL: "backend:open-external",
24409
+ OPEN_LOCAL_FILE: "backend:open-local-file",
24410
+ GET_LOCAL_CUSTOM_MODELS: "backend:get-local-custom-models",
24411
+ SAVE_LOCAL_CUSTOM_MODEL: "backend:save-local-custom-model",
24412
+ DELETE_LOCAL_CUSTOM_MODEL: "backend:delete-local-custom-model",
20865
24413
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
20866
24414
  GET_SUPPORT_SCENES: "backend:get-support-scenes",
20867
24415
  GET_ACCOUNT_USAGE: "backend:get-account-usage",
20868
24416
  GET_CHECKIN_STATUS: "backend:get-checkin-status",
20869
24417
  CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin",
24418
+ GET_ACTIVITY_BANNER: "backend:get-activity-banner",
24419
+ GET_EXPERT_RANKING: "backend:get-expert-ranking",
20870
24420
  SKILLHUB_LIST: "backend:skillhub-list",
20871
24421
  SKILLHUB_CATEGORIES: "backend:skillhub-categories",
20872
24422
  SKILLHUB_SEARCH: "backend:skillhub-search",
@@ -20892,6 +24442,25 @@ var IPCBackendProvider = class {
20892
24442
  this.debug = config.debug ?? false;
20893
24443
  this.timeoutMs = config.timeoutMs ?? 3e4;
20894
24444
  this.log("Initialized with IWidgetChannel");
24445
+ this.setupSessionChangeListener();
24446
+ }
24447
+ /**
24448
+ * 设置会话变化监听器
24449
+ * 监听来自 Extension Host (BackendBridgeService) 推送的 auth:session-changed 事件
24450
+ * 并更新本地 accountService
24451
+ */
24452
+ setupSessionChangeListener() {
24453
+ this.log("Setting up session change listener");
24454
+ this.channel.on("auth:session-changed", (data) => {
24455
+ this.log("Received auth:session-changed event from Extension Host:", data);
24456
+ const account = data?.account || null;
24457
+ accountService.setAccount(account);
24458
+ this.log("Updated accountService with new session", {
24459
+ hasAccount: !!account,
24460
+ accountNickname: account?.nickname
24461
+ });
24462
+ });
24463
+ this.log("Session change listener setup complete");
20895
24464
  }
20896
24465
  /**
20897
24466
  * 发送统一格式的后端请求
@@ -21170,6 +24739,19 @@ var IPCBackendProvider = class {
21170
24739
  }
21171
24740
  }
21172
24741
  /**
24742
+ * 设置防休眠状态
24743
+ * IDE 环境: 通过 IPC 通知主进程启用/禁用 powerSaveBlocker
24744
+ */
24745
+ async savePowerBlocker(params) {
24746
+ this.log("Saving power blocker setting via IPC", params);
24747
+ try {
24748
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_POWER_BLOCKER, params);
24749
+ } catch (error) {
24750
+ this.log("Save power blocker setting failed:", error);
24751
+ throw error;
24752
+ }
24753
+ }
24754
+ /**
21173
24755
  * 关闭 Agent Manager 面板
21174
24756
  * IDE 环境: 通过 IPC 通知 IDE 关闭 Agent Manager(用于返回 IDE)
21175
24757
  */
@@ -21197,6 +24779,41 @@ var IPCBackendProvider = class {
21197
24779
  }
21198
24780
  }
21199
24781
  /**
24782
+ * 打开本地文件
24783
+ * IDE 环境: 通过 IPC 通知 IDE 打开本地配置文件
24784
+ * @param filePath 要打开的文件路径
24785
+ */
24786
+ async openLocalFile(filePath) {
24787
+ this.log("Opening local file via IPC:", filePath);
24788
+ try {
24789
+ if (!await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_LOCAL_FILE, { filePath })) throw new Error(`Failed to open local file: ${filePath}`);
24790
+ } catch (error) {
24791
+ this.log("Open local file request failed:", error);
24792
+ throw error;
24793
+ }
24794
+ }
24795
+ /**
24796
+ * 获取用户级本地自定义模型
24797
+ */
24798
+ async getLocalCustomModels() {
24799
+ this.log("Getting local custom models via IPC");
24800
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_LOCAL_CUSTOM_MODELS);
24801
+ }
24802
+ /**
24803
+ * 保存用户级本地自定义模型
24804
+ */
24805
+ async saveLocalCustomModel(request) {
24806
+ this.log("Saving local custom model via IPC:", request);
24807
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_LOCAL_CUSTOM_MODEL, request);
24808
+ }
24809
+ /**
24810
+ * 删除用户级本地自定义模型
24811
+ */
24812
+ async deleteLocalCustomModel(id) {
24813
+ this.log("Deleting local custom model via IPC:", id);
24814
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.DELETE_LOCAL_CUSTOM_MODEL, { id });
24815
+ }
24816
+ /**
21200
24817
  * 批量切换插件状态
21201
24818
  * IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
21202
24819
  */
@@ -21275,6 +24892,32 @@ var IPCBackendProvider = class {
21275
24892
  }
21276
24893
  }
21277
24894
  /**
24895
+ * 获取活动 Banner
24896
+ * IDE 环境: 通过 IPC 获取活动 Banner
24897
+ */
24898
+ async getActivityBanner() {
24899
+ this.log("Getting activity banner via IPC");
24900
+ try {
24901
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACTIVITY_BANNER);
24902
+ } catch (error) {
24903
+ this.log("Get activity banner failed:", error);
24904
+ return null;
24905
+ }
24906
+ }
24907
+ /**
24908
+ * 获取专家排行榜数据
24909
+ * IDE 环境: 通过 IPC 获取排行榜数据
24910
+ */
24911
+ async getExpertRanking() {
24912
+ this.log("Getting expert ranking via IPC");
24913
+ try {
24914
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_EXPERT_RANKING);
24915
+ } catch (error) {
24916
+ this.log("Get expert ranking failed:", error);
24917
+ return null;
24918
+ }
24919
+ }
24920
+ /**
21278
24921
  * 获取 SkillHub 技能列表
21279
24922
  * IDE 环境: 通过 IPC 获取技能列表
21280
24923
  */
@@ -21420,6 +25063,7 @@ exports.CloudAgentProvider = CloudAgentProvider;
21420
25063
  exports.E2BFilesystem = require_e2b_filesystem.E2BFilesystem;
21421
25064
  exports.HttpService = HttpService;
21422
25065
  exports.IPCBackendProvider = IPCBackendProvider;
25066
+ exports.SessionError = SessionError;
21423
25067
  exports.SessionManager = SessionManager;
21424
25068
  exports.__toESM = __toESM;
21425
25069
  exports.createBackendProvider = createBackendProvider;