@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.00cf97b.20260317

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.mjs CHANGED
@@ -54,7 +54,6 @@ const ToolInputSchemas = {
54
54
  queryString: z.string().describe("用户的实际问题或搜索查询"),
55
55
  knowledgeBaseNames: z.string().describe("知识库名称,多个用逗号分隔")
56
56
  }),
57
- read_rules: z.object({ ruleNames: z.string().describe("要读取的规则关键词,用逗号分隔,格式:{ruleName}_{ruleId}") }),
58
57
  mcp_get_tool_description: z.object({ toolRequests: z.string().describe("JSON 字符串,二维数组格式:[[\"server1\", \"tool1\"], [\"server2\", \"tool2\"]]") }),
59
58
  mcp_call_tool: z.object({
60
59
  serverName: z.string().describe("MCP 服务器名称"),
@@ -68,13 +67,6 @@ const ToolInputSchemas = {
68
67
  arguments: z.record(z.unknown()).optional().describe("资源模板的参数"),
69
68
  downloadPath: z.string().optional().describe("可选的绝对路径,用于保存资源到磁盘")
70
69
  }),
71
- create_rule: z.object({
72
- ruleScope: z.string().describe("规则范围,project rule 或 user rule"),
73
- ruleName: z.string().describe("规则文件名,不带扩展名"),
74
- ruleType: z.string().describe("规则类型,always、manual 或 requested"),
75
- ruleContent: z.string().describe("规则内容,使用 Markdown 格式"),
76
- ruleDescription: z.string().optional().describe("规则描述,使用 Markdown 格式")
77
- }),
78
70
  update_memory: z.object({
79
71
  action: z.enum([
80
72
  "create",
@@ -133,7 +125,6 @@ const ToolInputSchemas = {
133
125
  cloud_studio_fetch_log: z.object({}).passthrough(),
134
126
  cloud_studio_execute_command: z.object({}).passthrough(),
135
127
  cloud_studio_deploy_sandbox: z.object({}).passthrough(),
136
- component_get_prompt: z.object({}).passthrough(),
137
128
  web_fetch: z.object({
138
129
  url: z.string().describe("要获取内容的 URL"),
139
130
  fetchInfo: z.string().describe("用户想要获取的信息描述")
@@ -141,7 +132,9 @@ const ToolInputSchemas = {
141
132
  use_skill: z.object({ command: z.string().describe("技能名称(不含参数),如 \"pdf\" 或 \"xlsx\"") }),
142
133
  web_search: z.object({
143
134
  explanation: z.string().describe("为什么使用此工具的一句话解释"),
144
- searchTerm: z.string().describe("要在网络上搜索的搜索词")
135
+ query: z.string().describe("搜索关键词"),
136
+ max_results: z.number().optional().describe("最大返回数量"),
137
+ language: z.string().optional().describe("语言代码,例如 zh-CN")
145
138
  }),
146
139
  task: z.object({
147
140
  subagent_name: z.string().describe("要调用的子代理名称"),
@@ -219,11 +212,6 @@ const ToolOutputSchemas = {
219
212
  selectedKnowledgeBases: z.string(),
220
213
  queryInput: z.string()
221
214
  }),
222
- read_rules: z.object({
223
- type: z.literal("rule_match_result"),
224
- ruleDescription: z.string(),
225
- filePaths: z.array(z.string())
226
- }),
227
215
  mcp_get_tool_description: z.object({}).passthrough(),
228
216
  mcp_call_tool: z.object({
229
217
  type: z.literal("mcp_call_tool_result"),
@@ -260,17 +248,6 @@ const ToolOutputSchemas = {
260
248
  content: z.string(),
261
249
  downloadPath: z.string().optional()
262
250
  }),
263
- create_rule: z.object({
264
- type: z.literal("rule_create_result"),
265
- ruleName: z.string(),
266
- createState: z.enum([
267
- "success",
268
- "invoke",
269
- "cancelled"
270
- ]),
271
- hint: z.string().optional(),
272
- filePath: z.string().optional()
273
- }),
274
251
  update_memory: z.object({
275
252
  type: z.literal("update_memory_result"),
276
253
  success: z.boolean(),
@@ -284,9 +261,9 @@ const ToolOutputSchemas = {
284
261
  }),
285
262
  search_content: z.object({
286
263
  type: z.literal("search_content_result"),
287
- directory: z.string(),
264
+ path: z.string(),
288
265
  pattern: z.string(),
289
- fileTypes: z.string(),
266
+ glob: z.string(),
290
267
  matches: z.array(z.object({
291
268
  filePath: z.string(),
292
269
  content: z.string(),
@@ -298,7 +275,7 @@ const ToolOutputSchemas = {
298
275
  totalCount: z.number(),
299
276
  hasMore: z.boolean(),
300
277
  offset: z.number(),
301
- limit: z.number(),
278
+ headLimit: z.number(),
302
279
  contextBefore: z.number(),
303
280
  contextAfter: z.number(),
304
281
  contextAround: z.number().optional(),
@@ -484,15 +461,6 @@ const ToolOutputSchemas = {
484
461
  }).optional()
485
462
  }))
486
463
  }),
487
- component_get_prompt: z.object({
488
- type: z.literal("component_get_prompt_result"),
489
- componentType: z.string(),
490
- webFramework: z.string(),
491
- data: z.object({
492
- type: z.literal("text"),
493
- text: z.string()
494
- })
495
- }),
496
464
  web_fetch: z.object({
497
465
  type: z.literal("web_fetch_tool_result"),
498
466
  message: z.string(),
@@ -508,14 +476,29 @@ const ToolOutputSchemas = {
508
476
  }),
509
477
  web_search: z.object({
510
478
  type: z.literal("web_search_tool_result"),
511
- data: z.array(z.object({
512
- passage: z.string(),
513
- uri: z.string(),
514
- site: z.string(),
479
+ query: z.string().optional(),
480
+ searchType: z.literal("text2text").optional(),
481
+ provider: z.string().optional(),
482
+ results: z.array(z.object({
515
483
  title: z.string(),
516
- snippets: z.array(z.string()).optional(),
517
- content: z.string().optional()
484
+ url: z.string(),
485
+ snippet: z.string().optional(),
486
+ site: z.string().optional(),
487
+ highlights: z.array(z.string()).optional(),
488
+ content: z.string().optional(),
489
+ favicon: z.string().optional()
518
490
  })),
491
+ images: z.array(z.object({
492
+ url: z.string(),
493
+ description: z.string().optional(),
494
+ sourceUrl: z.string().optional(),
495
+ width: z.number().optional(),
496
+ height: z.number().optional(),
497
+ title: z.string().optional(),
498
+ siteName: z.string().optional()
499
+ })),
500
+ totalResults: z.number().optional(),
501
+ responseTimeMs: z.number().optional(),
519
502
  searchInput: z.string().optional()
520
503
  }),
521
504
  task: z.object({
@@ -661,7 +644,11 @@ const ExtensionMethod = {
661
644
  CHECKPOINT: "_codebuddy.ai/checkpoint",
662
645
  USAGE: "_codebuddy.ai/usage",
663
646
  COMMAND: "_codebuddy.ai/command",
664
- AUTH_URL: "_codebuddy.ai/authUrl"
647
+ AUTH_URL: "_codebuddy.ai/authUrl",
648
+ FILE_HISTORY_SNAPSHOT: "_codebuddy.ai/file_history_snapshot",
649
+ DELEGATE_TOOL: "_codebuddy.ai/delegateTool",
650
+ DELEGATE_TOOLS_CHANGED: "_codebuddy.ai/delegateToolsChanged",
651
+ UI_CONTROL: "_codebuddy.ai/uiControl"
665
652
  };
666
653
  /**
667
654
  * All known extension methods
@@ -672,7 +659,10 @@ const KNOWN_EXTENSIONS = [
672
659
  ExtensionMethod.CHECKPOINT,
673
660
  ExtensionMethod.USAGE,
674
661
  ExtensionMethod.COMMAND,
675
- ExtensionMethod.AUTH_URL
662
+ ExtensionMethod.AUTH_URL,
663
+ ExtensionMethod.FILE_HISTORY_SNAPSHOT,
664
+ ExtensionMethod.DELEGATE_TOOL,
665
+ ExtensionMethod.DELEGATE_TOOLS_CHANGED
676
666
  ];
677
667
 
678
668
  //#endregion
@@ -723,7 +713,7 @@ function parseSSELine(line, currentEvent) {
723
713
  };
724
714
  }
725
715
  function streamableHttp(options) {
726
- const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, postTimeout = 3e4, backpressure = {} } = options;
716
+ const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, connectionTimeout = 3e4, postTimeout = 3e4, backpressure = {} } = options;
727
717
  const { enabled: reconnectEnabled = true, initialDelay = 1e3, maxDelay = 3e4, maxRetries = Infinity, jitter: jitterEnabled = true } = reconnect;
728
718
  const { highWaterMark = 100, lowWaterMark = 50, pauseTimeout = 5e3 } = backpressure;
729
719
  let connectionId;
@@ -934,11 +924,21 @@ function streamableHttp(options) {
934
924
  const headers = buildHeaders();
935
925
  headers["Accept"] = "text/event-stream";
936
926
  if (lastEventId) headers["Last-Event-ID"] = lastEventId;
937
- const response = await customFetch(endpoint, {
938
- method: "GET",
939
- headers,
940
- signal: combinedSignal
941
- });
927
+ const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
928
+ const connectController = new AbortController();
929
+ const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
930
+ if (externalSignal) externalSignal.addEventListener("abort", () => connectController.abort(), { once: true });
931
+ abortController.signal.addEventListener("abort", () => connectController.abort(), { once: true });
932
+ let response;
933
+ try {
934
+ response = await customFetch(endpoint, {
935
+ method: "GET",
936
+ headers,
937
+ signal: connectController.signal
938
+ });
939
+ } finally {
940
+ clearTimeout(connectTimer);
941
+ }
942
942
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
943
943
  const newConnectionId = response.headers.get("Acp-Connection-Id");
944
944
  if (!newConnectionId) throw new Error("Server did not return Acp-Connection-Id header");
@@ -1874,6 +1874,9 @@ var StreamableHttpClient = class {
1874
1874
  headers: this.options.headers,
1875
1875
  reconnect: this.options.reconnect,
1876
1876
  fetch: this.options.fetch,
1877
+ heartbeatTimeout: this.options.heartbeatTimeout,
1878
+ postTimeout: this.options.postTimeout,
1879
+ connectionTimeout: this.options.connectionTimeout,
1877
1880
  onConnect: (connectionId) => {
1878
1881
  this.options.logger?.debug(`Transport connected: ${connectionId}`);
1879
1882
  },
@@ -1948,10 +1951,6 @@ var StreamableHttpClient = class {
1948
1951
  },
1949
1952
  requestPermission: async (params) => this.handleRequestPermission(params),
1950
1953
  extNotification: async (method, params) => {
1951
- console.log("[ACP-Client] extNotification callback invoked:", {
1952
- method,
1953
- paramsKeys: Object.keys(params)
1954
- });
1955
1954
  await this.handleExtNotification(method, params);
1956
1955
  },
1957
1956
  extMethod: async (method, params) => this.handleExtMethod(method, params)
@@ -1959,10 +1958,15 @@ var StreamableHttpClient = class {
1959
1958
  }
1960
1959
  /**
1961
1960
  * Create a new session
1961
+ *
1962
+ * Retries on transient network errors (e.g., proxy connection reset)
1963
+ * since session/new is idempotent and safe to retry.
1962
1964
  */
1963
1965
  async createSession(cwd) {
1964
1966
  this.ensureInitialized("createSession");
1965
- try {
1967
+ const maxRetries = 2;
1968
+ let lastError;
1969
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
1966
1970
  const response = await this.connection.newSession({
1967
1971
  cwd,
1968
1972
  mcpServers: []
@@ -1970,8 +1974,16 @@ var StreamableHttpClient = class {
1970
1974
  this.options.logger?.info(`Session created: ${response.sessionId}`);
1971
1975
  return response;
1972
1976
  } catch (err) {
1973
- throw new SessionError(`Failed to create session: ${err instanceof Error ? err.message : String(err)}`, void 0, err instanceof Error ? err : void 0);
1977
+ lastError = err instanceof Error ? err : new Error(String(err));
1978
+ if (attempt < maxRetries && isRetryableNetworkError(err)) {
1979
+ const delay = 500 * Math.pow(2, attempt);
1980
+ this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
1981
+ await new Promise((resolve) => setTimeout(resolve, delay));
1982
+ continue;
1983
+ }
1984
+ throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
1974
1985
  }
1986
+ throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
1975
1987
  }
1976
1988
  /**
1977
1989
  * Load an existing session
@@ -2195,6 +2207,18 @@ var StreamableHttpClient = class {
2195
2207
  if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
2196
2208
  }
2197
2209
  };
2210
+ /**
2211
+ * Check if an error is a retryable network-level error.
2212
+ * Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
2213
+ */
2214
+ function isRetryableNetworkError(error) {
2215
+ if (error instanceof TypeError) return true;
2216
+ if (error instanceof Error) {
2217
+ const msg = error.message.toLowerCase();
2218
+ return msg.includes("failed to fetch") || msg.includes("fetch failed") || msg.includes("network request failed") || msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("socket hang up");
2219
+ }
2220
+ return false;
2221
+ }
2198
2222
 
2199
2223
  //#endregion
2200
2224
  //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
@@ -2230,7 +2254,7 @@ var CloudAgentConnection = class {
2230
2254
  fetch: config.fetch,
2231
2255
  clientCapabilities: config.clientCapabilities,
2232
2256
  onSessionUpdate: (update) => {
2233
- if (!this._isStreaming) this.emit("sessionUpdate", update);
2257
+ if (!this._isStreaming && this.isOwnSessionNotification(update)) this.emit("sessionUpdate", update);
2234
2258
  },
2235
2259
  onArtifact: (artifact, event) => {
2236
2260
  console.log("[CloudConnection] onArtifact callback:", {
@@ -2244,10 +2268,41 @@ var CloudAgentConnection = class {
2244
2268
  },
2245
2269
  onUsageUpdate: (usage) => {
2246
2270
  this.emit("usageUpdate", usage);
2271
+ },
2272
+ onExtNotification: (method, params) => {
2273
+ console.log("[CloudConnection] Received extNotification:", {
2274
+ method,
2275
+ paramsKeys: Object.keys(params)
2276
+ });
2277
+ if (method === ExtensionMethod.COMMAND) {
2278
+ const action = params.action;
2279
+ const commandParams = params.params;
2280
+ console.log("[CloudConnection] Emitting command event:", {
2281
+ action,
2282
+ paramsKeys: commandParams ? Object.keys(commandParams) : []
2283
+ });
2284
+ this.emit("command", {
2285
+ action,
2286
+ params: commandParams
2287
+ });
2288
+ }
2247
2289
  }
2248
2290
  });
2249
2291
  this.setupEventForwarding();
2250
2292
  }
2293
+ /**
2294
+ * Check whether a notification belongs to this connection's own session.
2295
+ *
2296
+ * CloudConnection.createSession() overrides sessionId to agentId, so the
2297
+ * rest of the client stack uses agentId as the canonical session
2298
+ * identifier. Notifications whose sessionId differs from agentId
2299
+ * originate from sub-agent sessions running inside the same sandbox and
2300
+ * should be silently ignored at this layer — the adapter layer handles
2301
+ * sub-agent messages independently via parentToolUseId in _meta.
2302
+ */
2303
+ isOwnSessionNotification(notification) {
2304
+ return notification.sessionId === this.agentId;
2305
+ }
2251
2306
  setupEventForwarding() {
2252
2307
  this.client.on("connecting", () => {
2253
2308
  this.emit("connecting", void 0);
@@ -2372,7 +2427,7 @@ var CloudAgentConnection = class {
2372
2427
  }
2373
2428
  async createSession(params) {
2374
2429
  return {
2375
- ...await this.client.loadSession(this.agentId, this.cwd),
2430
+ ...await this.client.createSession(this.cwd),
2376
2431
  sessionId: this.agentId
2377
2432
  };
2378
2433
  }
@@ -2408,6 +2463,7 @@ var CloudAgentConnection = class {
2408
2463
  let resolveUpdate = null;
2409
2464
  let done = false;
2410
2465
  const listener = (update) => {
2466
+ if (!this.isOwnSessionNotification(update)) return;
2411
2467
  if (resolveUpdate) {
2412
2468
  resolveUpdate(update);
2413
2469
  resolveUpdate = null;
@@ -2490,6 +2546,16 @@ var CloudAgentConnection = class {
2490
2546
  get sessionConnectionInfo() {
2491
2547
  return this._sessionConnectionInfo;
2492
2548
  }
2549
+ async reportTelemetry(eventName, payload) {
2550
+ try {
2551
+ await this.client.extMethod("reportTelemetry", {
2552
+ eventName,
2553
+ payload
2554
+ });
2555
+ } catch (error) {
2556
+ console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
2557
+ }
2558
+ }
2493
2559
  async extMethod(method, params) {
2494
2560
  return this.client.extMethod(method, params);
2495
2561
  }
@@ -3092,7 +3158,7 @@ var utils_default = {
3092
3158
  *
3093
3159
  * @returns {Error} The created error.
3094
3160
  */
3095
- function AxiosError(message, code, config, request, response) {
3161
+ function AxiosError$1(message, code, config, request, response) {
3096
3162
  Error.call(this);
3097
3163
  if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
3098
3164
  else this.stack = (/* @__PURE__ */ new Error()).stack;
@@ -3106,7 +3172,7 @@ function AxiosError(message, code, config, request, response) {
3106
3172
  this.status = response.status ? response.status : null;
3107
3173
  }
3108
3174
  }
3109
- utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
3175
+ utils_default.inherits(AxiosError$1, Error, { toJSON: function toJSON() {
3110
3176
  return {
3111
3177
  message: this.message,
3112
3178
  name: this.name,
@@ -3121,7 +3187,7 @@ utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
3121
3187
  status: this.status
3122
3188
  };
3123
3189
  } });
3124
- const prototype$1 = AxiosError.prototype;
3190
+ const prototype$1 = AxiosError$1.prototype;
3125
3191
  const descriptors = {};
3126
3192
  [
3127
3193
  "ERR_BAD_OPTION_VALUE",
@@ -3139,22 +3205,22 @@ const descriptors = {};
3139
3205
  ].forEach((code) => {
3140
3206
  descriptors[code] = { value: code };
3141
3207
  });
3142
- Object.defineProperties(AxiosError, descriptors);
3208
+ Object.defineProperties(AxiosError$1, descriptors);
3143
3209
  Object.defineProperty(prototype$1, "isAxiosError", { value: true });
3144
- AxiosError.from = (error, code, config, request, response, customProps) => {
3210
+ AxiosError$1.from = (error, code, config, request, response, customProps) => {
3145
3211
  const axiosError = Object.create(prototype$1);
3146
3212
  utils_default.toFlatObject(error, axiosError, function filter(obj) {
3147
3213
  return obj !== Error.prototype;
3148
3214
  }, (prop) => {
3149
3215
  return prop !== "isAxiosError";
3150
3216
  });
3151
- AxiosError.call(axiosError, error.message, code, config, request, response);
3217
+ AxiosError$1.call(axiosError, error.message, code, config, request, response);
3152
3218
  axiosError.cause = error;
3153
3219
  axiosError.name = error.name;
3154
3220
  customProps && Object.assign(axiosError, customProps);
3155
3221
  return axiosError;
3156
3222
  };
3157
- var AxiosError_default = AxiosError;
3223
+ var AxiosError_default = AxiosError$1;
3158
3224
 
3159
3225
  //#endregion
3160
3226
  //#region ../agent-provider/node_modules/axios/lib/helpers/null.js
@@ -3233,7 +3299,7 @@ const predicates = utils_default.toFlatObject(utils_default, {}, null, function
3233
3299
  *
3234
3300
  * @returns
3235
3301
  */
3236
- function toFormData(obj, formData, options) {
3302
+ function toFormData$1(obj, formData, options) {
3237
3303
  if (!utils_default.isObject(obj)) throw new TypeError("target must be an object");
3238
3304
  formData = formData || new (null_default || FormData)();
3239
3305
  options = utils_default.toFlatObject(options, {
@@ -3304,7 +3370,7 @@ function toFormData(obj, formData, options) {
3304
3370
  build(obj);
3305
3371
  return formData;
3306
3372
  }
3307
- var toFormData_default = toFormData;
3373
+ var toFormData_default = toFormData$1;
3308
3374
 
3309
3375
  //#endregion
3310
3376
  //#region ../agent-provider/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
@@ -3821,7 +3887,7 @@ function buildAccessors(obj, header) {
3821
3887
  });
3822
3888
  });
3823
3889
  }
3824
- var AxiosHeaders = class {
3890
+ var AxiosHeaders$1 = class {
3825
3891
  constructor(headers) {
3826
3892
  headers && this.set(headers);
3827
3893
  }
@@ -3959,7 +4025,7 @@ var AxiosHeaders = class {
3959
4025
  return this;
3960
4026
  }
3961
4027
  };
3962
- AxiosHeaders.accessor([
4028
+ AxiosHeaders$1.accessor([
3963
4029
  "Content-Type",
3964
4030
  "Content-Length",
3965
4031
  "Accept",
@@ -3967,7 +4033,7 @@ AxiosHeaders.accessor([
3967
4033
  "User-Agent",
3968
4034
  "Authorization"
3969
4035
  ]);
3970
- utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
4036
+ utils_default.reduceDescriptors(AxiosHeaders$1.prototype, ({ value }, key) => {
3971
4037
  let mapped = key[0].toUpperCase() + key.slice(1);
3972
4038
  return {
3973
4039
  get: () => value,
@@ -3976,8 +4042,8 @@ utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
3976
4042
  }
3977
4043
  };
3978
4044
  });
3979
- utils_default.freezeMethods(AxiosHeaders);
3980
- var AxiosHeaders_default = AxiosHeaders;
4045
+ utils_default.freezeMethods(AxiosHeaders$1);
4046
+ var AxiosHeaders_default = AxiosHeaders$1;
3981
4047
 
3982
4048
  //#endregion
3983
4049
  //#region ../agent-provider/node_modules/axios/lib/core/transformData.js
@@ -4003,7 +4069,7 @@ function transformData(fns, response) {
4003
4069
 
4004
4070
  //#endregion
4005
4071
  //#region ../agent-provider/node_modules/axios/lib/cancel/isCancel.js
4006
- function isCancel(value) {
4072
+ function isCancel$1(value) {
4007
4073
  return !!(value && value.__CANCEL__);
4008
4074
  }
4009
4075
 
@@ -4018,12 +4084,12 @@ function isCancel(value) {
4018
4084
  *
4019
4085
  * @returns {CanceledError} The created error.
4020
4086
  */
4021
- function CanceledError(message, config, request) {
4087
+ function CanceledError$1(message, config, request) {
4022
4088
  AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
4023
4089
  this.name = "CanceledError";
4024
4090
  }
4025
- utils_default.inherits(CanceledError, AxiosError_default, { __CANCEL__: true });
4026
- var CanceledError_default = CanceledError;
4091
+ utils_default.inherits(CanceledError$1, AxiosError_default, { __CANCEL__: true });
4092
+ var CanceledError_default = CanceledError$1;
4027
4093
 
4028
4094
  //#endregion
4029
4095
  //#region ../agent-provider/node_modules/axios/lib/core/settle.js
@@ -4250,7 +4316,7 @@ const headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...
4250
4316
  *
4251
4317
  * @returns {Object} New object resulting from merging config2 to config1
4252
4318
  */
4253
- function mergeConfig(config1, config2) {
4319
+ function mergeConfig$1(config1, config2) {
4254
4320
  config2 = config2 || {};
4255
4321
  const config = {};
4256
4322
  function getMergedValue(target, source, prop, caseless) {
@@ -4316,7 +4382,7 @@ function mergeConfig(config1, config2) {
4316
4382
  //#endregion
4317
4383
  //#region ../agent-provider/node_modules/axios/lib/helpers/resolveConfig.js
4318
4384
  var resolveConfig_default = (config) => {
4319
- const newConfig = mergeConfig({}, config);
4385
+ const newConfig = mergeConfig$1({}, config);
4320
4386
  let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
4321
4387
  newConfig.headers = headers = AxiosHeaders_default.from(headers);
4322
4388
  newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
@@ -4747,7 +4813,7 @@ function dispatchRequest(config) {
4747
4813
  response.headers = AxiosHeaders_default.from(response.headers);
4748
4814
  return response;
4749
4815
  }, function onAdapterRejection(reason) {
4750
- if (!isCancel(reason)) {
4816
+ if (!isCancel$1(reason)) {
4751
4817
  throwIfCancellationRequested(config);
4752
4818
  if (reason && reason.response) {
4753
4819
  reason.response.data = transformData.call(config, config.transformResponse, reason.response);
@@ -4760,7 +4826,7 @@ function dispatchRequest(config) {
4760
4826
 
4761
4827
  //#endregion
4762
4828
  //#region ../agent-provider/node_modules/axios/lib/env/data.js
4763
- const VERSION = "1.10.0";
4829
+ const VERSION$1 = "1.10.0";
4764
4830
 
4765
4831
  //#endregion
4766
4832
  //#region ../agent-provider/node_modules/axios/lib/helpers/validator.js
@@ -4789,7 +4855,7 @@ const deprecatedWarnings = {};
4789
4855
  */
4790
4856
  validators$1.transitional = function transitional(validator, version, message) {
4791
4857
  function formatMessage(opt, desc) {
4792
- return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
4858
+ return "[Axios v" + VERSION$1 + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
4793
4859
  }
4794
4860
  return (value, opt, opts) => {
4795
4861
  if (validator === false) throw new AxiosError_default(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError_default.ERR_DEPRECATED);
@@ -4846,7 +4912,7 @@ const validators = validator_default.validators;
4846
4912
  *
4847
4913
  * @return {Axios} A new instance of Axios
4848
4914
  */
4849
- var Axios = class {
4915
+ var Axios$1 = class {
4850
4916
  constructor(instanceConfig) {
4851
4917
  this.defaults = instanceConfig || {};
4852
4918
  this.interceptors = {
@@ -4883,7 +4949,7 @@ var Axios = class {
4883
4949
  config = config || {};
4884
4950
  config.url = configOrUrl;
4885
4951
  } else config = configOrUrl || {};
4886
- config = mergeConfig(this.defaults, config);
4952
+ config = mergeConfig$1(this.defaults, config);
4887
4953
  const { transitional, paramsSerializer, headers } = config;
4888
4954
  if (transitional !== void 0) validator_default.assertOptions(transitional, {
4889
4955
  silentJSONParsing: validators.transitional(validators.boolean),
@@ -4962,7 +5028,7 @@ var Axios = class {
4962
5028
  return promise;
4963
5029
  }
4964
5030
  getUri(config) {
4965
- config = mergeConfig(this.defaults, config);
5031
+ config = mergeConfig$1(this.defaults, config);
4966
5032
  return buildURL(buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls), config.params, config.paramsSerializer);
4967
5033
  }
4968
5034
  };
@@ -4972,8 +5038,8 @@ utils_default.forEach([
4972
5038
  "head",
4973
5039
  "options"
4974
5040
  ], function forEachMethodNoData(method) {
4975
- Axios.prototype[method] = function(url, config) {
4976
- return this.request(mergeConfig(config || {}, {
5041
+ Axios$1.prototype[method] = function(url, config) {
5042
+ return this.request(mergeConfig$1(config || {}, {
4977
5043
  method,
4978
5044
  url,
4979
5045
  data: (config || {}).data
@@ -4987,7 +5053,7 @@ utils_default.forEach([
4987
5053
  ], function forEachMethodWithData(method) {
4988
5054
  function generateHTTPMethod(isForm) {
4989
5055
  return function httpMethod(url, data, config) {
4990
- return this.request(mergeConfig(config || {}, {
5056
+ return this.request(mergeConfig$1(config || {}, {
4991
5057
  method,
4992
5058
  headers: isForm ? { "Content-Type": "multipart/form-data" } : {},
4993
5059
  url,
@@ -4995,10 +5061,10 @@ utils_default.forEach([
4995
5061
  }));
4996
5062
  };
4997
5063
  }
4998
- Axios.prototype[method] = generateHTTPMethod();
4999
- Axios.prototype[method + "Form"] = generateHTTPMethod(true);
5064
+ Axios$1.prototype[method] = generateHTTPMethod();
5065
+ Axios$1.prototype[method + "Form"] = generateHTTPMethod(true);
5000
5066
  });
5001
- var Axios_default = Axios;
5067
+ var Axios_default = Axios$1;
5002
5068
 
5003
5069
  //#endregion
5004
5070
  //#region ../agent-provider/node_modules/axios/lib/cancel/CancelToken.js
@@ -5009,7 +5075,7 @@ var Axios_default = Axios;
5009
5075
  *
5010
5076
  * @returns {CancelToken}
5011
5077
  */
5012
- var CancelToken = class CancelToken {
5078
+ var CancelToken$1 = class CancelToken$1 {
5013
5079
  constructor(executor) {
5014
5080
  if (typeof executor !== "function") throw new TypeError("executor must be a function.");
5015
5081
  let resolvePromise;
@@ -5081,14 +5147,14 @@ var CancelToken = class CancelToken {
5081
5147
  static source() {
5082
5148
  let cancel;
5083
5149
  return {
5084
- token: new CancelToken(function executor(c) {
5150
+ token: new CancelToken$1(function executor(c) {
5085
5151
  cancel = c;
5086
5152
  }),
5087
5153
  cancel
5088
5154
  };
5089
5155
  }
5090
5156
  };
5091
- var CancelToken_default = CancelToken;
5157
+ var CancelToken_default = CancelToken$1;
5092
5158
 
5093
5159
  //#endregion
5094
5160
  //#region ../agent-provider/node_modules/axios/lib/helpers/spread.js
@@ -5113,7 +5179,7 @@ var CancelToken_default = CancelToken;
5113
5179
  *
5114
5180
  * @returns {Function}
5115
5181
  */
5116
- function spread(callback) {
5182
+ function spread$1(callback) {
5117
5183
  return function wrap(arr) {
5118
5184
  return callback.apply(null, arr);
5119
5185
  };
@@ -5128,13 +5194,13 @@ function spread(callback) {
5128
5194
  *
5129
5195
  * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
5130
5196
  */
5131
- function isAxiosError(payload) {
5197
+ function isAxiosError$1(payload) {
5132
5198
  return utils_default.isObject(payload) && payload.isAxiosError === true;
5133
5199
  }
5134
5200
 
5135
5201
  //#endregion
5136
5202
  //#region ../agent-provider/node_modules/axios/lib/helpers/HttpStatusCode.js
5137
- const HttpStatusCode = {
5203
+ const HttpStatusCode$1 = {
5138
5204
  Continue: 100,
5139
5205
  SwitchingProtocols: 101,
5140
5206
  Processing: 102,
@@ -5199,10 +5265,10 @@ const HttpStatusCode = {
5199
5265
  NotExtended: 510,
5200
5266
  NetworkAuthenticationRequired: 511
5201
5267
  };
5202
- Object.entries(HttpStatusCode).forEach(([key, value]) => {
5203
- HttpStatusCode[value] = key;
5268
+ Object.entries(HttpStatusCode$1).forEach(([key, value]) => {
5269
+ HttpStatusCode$1[value] = key;
5204
5270
  });
5205
- var HttpStatusCode_default = HttpStatusCode;
5271
+ var HttpStatusCode_default = HttpStatusCode$1;
5206
5272
 
5207
5273
  //#endregion
5208
5274
  //#region ../agent-provider/node_modules/axios/lib/axios.js
@@ -5219,7 +5285,7 @@ function createInstance(defaultConfig) {
5219
5285
  utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
5220
5286
  utils_default.extend(instance, context, null, { allOwnKeys: true });
5221
5287
  instance.create = function create(instanceConfig) {
5222
- return createInstance(mergeConfig(defaultConfig, instanceConfig));
5288
+ return createInstance(mergeConfig$1(defaultConfig, instanceConfig));
5223
5289
  };
5224
5290
  return instance;
5225
5291
  }
@@ -5227,17 +5293,17 @@ const axios = createInstance(defaults_default);
5227
5293
  axios.Axios = Axios_default;
5228
5294
  axios.CanceledError = CanceledError_default;
5229
5295
  axios.CancelToken = CancelToken_default;
5230
- axios.isCancel = isCancel;
5231
- axios.VERSION = VERSION;
5296
+ axios.isCancel = isCancel$1;
5297
+ axios.VERSION = VERSION$1;
5232
5298
  axios.toFormData = toFormData_default;
5233
5299
  axios.AxiosError = AxiosError_default;
5234
5300
  axios.Cancel = axios.CanceledError;
5235
5301
  axios.all = function all(promises) {
5236
5302
  return Promise.all(promises);
5237
5303
  };
5238
- axios.spread = spread;
5239
- axios.isAxiosError = isAxiosError;
5240
- axios.mergeConfig = mergeConfig;
5304
+ axios.spread = spread$1;
5305
+ axios.isAxiosError = isAxiosError$1;
5306
+ axios.mergeConfig = mergeConfig$1;
5241
5307
  axios.AxiosHeaders = AxiosHeaders_default;
5242
5308
  axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
5243
5309
  axios.getAdapter = adapters_default.getAdapter;
@@ -5245,6 +5311,10 @@ axios.HttpStatusCode = HttpStatusCode_default;
5245
5311
  axios.default = axios;
5246
5312
  var axios_default = axios;
5247
5313
 
5314
+ //#endregion
5315
+ //#region ../agent-provider/node_modules/axios/index.js
5316
+ const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig } = axios_default;
5317
+
5248
5318
  //#endregion
5249
5319
  //#region ../agent-provider/src/http/http-service.ts
5250
5320
  /**
@@ -5253,7 +5323,7 @@ var axios_default = axios;
5253
5323
  * 特性:
5254
5324
  * - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
5255
5325
  * - 支持拦截器注册(其他模块可注入 header)
5256
- * - 统一 401/403 处理
5326
+ * - 统一 401 处理(支持自动刷新 token 并重试)
5257
5327
  * - 自动携带凭证(withCredentials)
5258
5328
  * - 类型安全
5259
5329
  *
@@ -5293,6 +5363,8 @@ var HttpService = class HttpService {
5293
5363
  */
5294
5364
  constructor(config = {}) {
5295
5365
  this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
5366
+ this.isRefreshing = false;
5367
+ this.refreshSubscribers = [];
5296
5368
  this.config = config;
5297
5369
  this.axiosInstance = axios_default.create({
5298
5370
  baseURL: config.baseURL?.replace(/\/$/, "") || "",
@@ -5333,18 +5405,54 @@ var HttpService = class HttpService {
5333
5405
  }, (error) => Promise.reject(error));
5334
5406
  }
5335
5407
  /**
5336
- * 注册默认响应拦截器(处理 401/403)
5408
+ * 注册默认响应拦截器(处理 401,支持自动重试)
5337
5409
  */
5338
5410
  registerDefaultResponseInterceptor() {
5339
- this.axiosInstance.interceptors.response.use((response) => response, (error) => {
5340
- if (error.response?.status === 401 || error.response?.status === 403) {
5341
- console.warn("[HttpService] Unauthorized (401/403), triggering callbacks");
5342
- this.triggerUnauthorizedCallbacks();
5411
+ this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
5412
+ const originalRequest = error.config;
5413
+ if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
5414
+ if (originalRequest.url?.includes("/console/accounts")) {
5415
+ console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
5416
+ return Promise.reject(error);
5417
+ }
5418
+ console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
5419
+ originalRequest._retry = true;
5420
+ if (this.isRefreshing) return new Promise((resolve, reject) => {
5421
+ this.refreshSubscribers.push((success) => {
5422
+ if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
5423
+ else reject(error);
5424
+ });
5425
+ });
5426
+ this.isRefreshing = true;
5427
+ try {
5428
+ await this.triggerUnauthorizedCallbacks();
5429
+ this.onRefreshSuccess();
5430
+ return this.axiosInstance.request(originalRequest);
5431
+ } catch (refreshError) {
5432
+ this.onRefreshFailure();
5433
+ return Promise.reject(error);
5434
+ } finally {
5435
+ this.isRefreshing = false;
5436
+ }
5343
5437
  }
5344
5438
  return Promise.reject(error);
5345
5439
  });
5346
5440
  }
5347
5441
  /**
5442
+ * token 刷新成功,通知所有等待的请求
5443
+ */
5444
+ onRefreshSuccess() {
5445
+ this.refreshSubscribers.forEach((callback) => callback(true));
5446
+ this.refreshSubscribers = [];
5447
+ }
5448
+ /**
5449
+ * token 刷新失败,通知所有等待的请求
5450
+ */
5451
+ onRefreshFailure() {
5452
+ this.refreshSubscribers.forEach((callback) => callback(false));
5453
+ this.refreshSubscribers = [];
5454
+ }
5455
+ /**
5348
5456
  * 注册请求拦截器
5349
5457
  * @param onFulfilled 请求成功拦截器
5350
5458
  * @param onRejected 请求失败拦截器
@@ -5421,16 +5529,18 @@ var HttpService = class HttpService {
5421
5529
  this.unauthorizedCallbacks.delete(callback);
5422
5530
  }
5423
5531
  /**
5424
- * 触发所有 401 回调
5532
+ * 触发所有 401 回调并等待完成
5533
+ * @returns Promise,等待所有回调完成
5534
+ * @throws 如果任何回调失败,则抛出错误
5425
5535
  */
5426
- triggerUnauthorizedCallbacks() {
5427
- this.unauthorizedCallbacks.forEach((callback) => {
5428
- try {
5429
- callback();
5430
- } catch (error) {
5431
- console.error("[HttpService] Error in unauthorized callback:", error);
5432
- }
5433
- });
5536
+ async triggerUnauthorizedCallbacks() {
5537
+ const callbacks = Array.from(this.unauthorizedCallbacks);
5538
+ const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
5539
+ if (failedResults.length > 0) {
5540
+ const errors = failedResults.map((r) => r.reason);
5541
+ console.error("[HttpService] Some unauthorized callbacks failed:", errors);
5542
+ throw errors[0];
5543
+ }
5434
5544
  }
5435
5545
  /**
5436
5546
  * 更新 authToken
@@ -5542,6 +5652,7 @@ var AccountService = class {
5542
5652
  this.initPromise = null;
5543
5653
  this.initResolve = null;
5544
5654
  this.requestInterceptorId = null;
5655
+ this.crossTabBroadcaster = null;
5545
5656
  this.initPromise = new Promise((resolve) => {
5546
5657
  this.initResolve = resolve;
5547
5658
  });
@@ -5590,15 +5701,34 @@ var AccountService = class {
5590
5701
  this.initialized = true;
5591
5702
  this.initResolve?.(account);
5592
5703
  }
5593
- if (!wasInitialized || prev?.uid !== account?.uid) this.notifyListeners();
5704
+ if (!wasInitialized || prev?.uid !== account?.uid) {
5705
+ this.notifyListeners();
5706
+ if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
5707
+ }
5594
5708
  }
5595
5709
  /**
5596
5710
  * 清除账号(登出)
5711
+ * 先广播登出消息,再清除本地账号
5597
5712
  */
5598
5713
  clearAccount() {
5714
+ if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
5599
5715
  this.setAccount(null);
5600
5716
  }
5601
5717
  /**
5718
+ * 静默清除账号(不广播)
5719
+ * 用于收到其他标签页 logout 消息时,避免循环广播
5720
+ */
5721
+ clearAccountSilently() {
5722
+ this.setAccount(null);
5723
+ }
5724
+ /**
5725
+ * 设置跨标签页认证同步广播器
5726
+ * 应在应用初始化时由上层(如 agent-ui)调用
5727
+ */
5728
+ setCrossTabBroadcaster(broadcaster) {
5729
+ this.crossTabBroadcaster = broadcaster;
5730
+ }
5731
+ /**
5602
5732
  * 订阅账号变化
5603
5733
  * @param callback 变化时的回调函数
5604
5734
  * @returns 取消订阅函数
@@ -5663,6 +5793,114 @@ var AccountService = class {
5663
5793
  * 导出单例实例
5664
5794
  */
5665
5795
  const accountService = new AccountService();
5796
+ /**
5797
+ * 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
5798
+ * 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
5799
+ */
5800
+ if (typeof window !== "undefined") window.__genieAccountService = accountService;
5801
+
5802
+ //#endregion
5803
+ //#region ../agent-provider/src/common/utils/lru-cache.ts
5804
+ /**
5805
+ * LRU (Least Recently Used) Cache
5806
+ * 当缓存达到容量上限时,自动淘汰最久未使用的数据
5807
+ *
5808
+ * @template K - Key 类型
5809
+ * @template V - Value 类型
5810
+ */
5811
+ var LRUCache = class {
5812
+ /**
5813
+ * 创建 LRU 缓存实例
5814
+ * @param capacity - 缓存容量上限
5815
+ */
5816
+ constructor(capacity) {
5817
+ if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
5818
+ this.capacity = capacity;
5819
+ this.cache = /* @__PURE__ */ new Map();
5820
+ }
5821
+ /**
5822
+ * 获取缓存值
5823
+ * @param key - 键
5824
+ * @returns 值,如果不存在返回 undefined
5825
+ */
5826
+ get(key) {
5827
+ if (!this.cache.has(key)) return;
5828
+ const value = this.cache.get(key);
5829
+ this.cache.delete(key);
5830
+ this.cache.set(key, value);
5831
+ return value;
5832
+ }
5833
+ /**
5834
+ * 设置缓存值
5835
+ * @param key - 键
5836
+ * @param value - 值
5837
+ */
5838
+ set(key, value) {
5839
+ if (this.cache.has(key)) this.cache.delete(key);
5840
+ else if (this.cache.size >= this.capacity) {
5841
+ const firstKey = this.cache.keys().next().value;
5842
+ this.cache.delete(firstKey);
5843
+ }
5844
+ this.cache.set(key, value);
5845
+ }
5846
+ /**
5847
+ * 检查 key 是否存在
5848
+ * @param key - 键
5849
+ * @returns 是否存在
5850
+ */
5851
+ has(key) {
5852
+ return this.cache.has(key);
5853
+ }
5854
+ /**
5855
+ * 删除指定 key
5856
+ * @param key - 键
5857
+ * @returns 是否删除成功
5858
+ */
5859
+ delete(key) {
5860
+ return this.cache.delete(key);
5861
+ }
5862
+ /**
5863
+ * 清空缓存
5864
+ */
5865
+ clear() {
5866
+ this.cache.clear();
5867
+ }
5868
+ /**
5869
+ * 获取当前缓存大小
5870
+ * @returns 当前缓存的元素数量
5871
+ */
5872
+ get size() {
5873
+ return this.cache.size;
5874
+ }
5875
+ /**
5876
+ * 获取缓存容量
5877
+ * @returns 缓存容量上限
5878
+ */
5879
+ get maxSize() {
5880
+ return this.capacity;
5881
+ }
5882
+ /**
5883
+ * 获取所有 key
5884
+ * @returns key 数组
5885
+ */
5886
+ keys() {
5887
+ return Array.from(this.cache.keys());
5888
+ }
5889
+ /**
5890
+ * 获取所有 value
5891
+ * @returns value 数组
5892
+ */
5893
+ values() {
5894
+ return Array.from(this.cache.values());
5895
+ }
5896
+ /**
5897
+ * 遍历缓存
5898
+ * @param callback - 回调函数
5899
+ */
5900
+ forEach(callback) {
5901
+ this.cache.forEach((value, key) => callback(value, key));
5902
+ }
5903
+ };
5666
5904
 
5667
5905
  //#endregion
5668
5906
  //#region ../agent-provider/src/common/utils/concurrency.ts
@@ -5765,20 +6003,32 @@ var CosUploadService = class {
5765
6003
  * 上传单个文件到 COS
5766
6004
  *
5767
6005
  * @param file - 要上传的文件
6006
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
5768
6007
  * @returns 上传结果,包含访问 URL 或错误信息
5769
6008
  */
5770
- async uploadFile(file) {
6009
+ async uploadFile(file, abortSignal) {
5771
6010
  const filename = file.name;
5772
6011
  this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
5773
6012
  try {
6013
+ if (abortSignal?.aborted) return {
6014
+ success: false,
6015
+ error: "Upload cancelled",
6016
+ aborted: true
6017
+ };
5774
6018
  const objectKey = this.generateObjectKey(filename);
5775
6019
  this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
5776
6020
  const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
5777
6021
  if (!presignedItem) throw new Error("No presigned URL item returned");
6022
+ if (abortSignal?.aborted) return {
6023
+ success: false,
6024
+ error: "Upload cancelled",
6025
+ aborted: true
6026
+ };
5778
6027
  const uploadResponse = await fetch(presignedItem.upload_url, {
5779
6028
  method: "PUT",
5780
6029
  body: file,
5781
- headers: { "Content-Type": file.type || "application/octet-stream" }
6030
+ headers: { "Content-Type": file.type || "application/octet-stream" },
6031
+ signal: abortSignal
5782
6032
  });
5783
6033
  if (!uploadResponse.ok) {
5784
6034
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -5792,6 +6042,11 @@ var CosUploadService = class {
5792
6042
  objectKey
5793
6043
  };
5794
6044
  } catch (error) {
6045
+ if (error instanceof Error && error.name === "AbortError") return {
6046
+ success: false,
6047
+ error: "Upload cancelled",
6048
+ aborted: true
6049
+ };
5795
6050
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
5796
6051
  this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
5797
6052
  return {
@@ -5806,14 +6061,25 @@ var CosUploadService = class {
5806
6061
  * 使用并发控制,限制同时上传的文件数量
5807
6062
  *
5808
6063
  * @param files - 要上传的文件数组
6064
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
5809
6065
  * @returns 所有文件的上传结果
5810
6066
  */
5811
- async uploadFiles(files) {
6067
+ async uploadFiles(files, abortSignal) {
5812
6068
  if (files.length === 0) return {
5813
6069
  success: true,
5814
6070
  urls: [],
5815
6071
  results: []
5816
6072
  };
6073
+ if (abortSignal?.aborted) return {
6074
+ success: false,
6075
+ error: "Upload cancelled",
6076
+ aborted: true,
6077
+ results: files.map(() => ({
6078
+ success: false,
6079
+ error: "Upload cancelled",
6080
+ aborted: true
6081
+ }))
6082
+ };
5817
6083
  this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
5818
6084
  try {
5819
6085
  const fileInfos = files.map((file) => ({
@@ -5825,6 +6091,12 @@ var CosUploadService = class {
5825
6091
  this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
5826
6092
  if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
5827
6093
  const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
6094
+ if (abortSignal?.aborted) return {
6095
+ success: false,
6096
+ error: "Upload cancelled",
6097
+ aborted: true,
6098
+ objectKey: fileInfos[index].objectKey
6099
+ };
5828
6100
  const presignedItem = presignedResponse.items[index];
5829
6101
  if (!presignedItem) return {
5830
6102
  success: false,
@@ -5835,7 +6107,8 @@ var CosUploadService = class {
5835
6107
  const uploadResponse = await fetch(presignedItem.upload_url, {
5836
6108
  method: "PUT",
5837
6109
  body: file,
5838
- headers: { "Content-Type": file.type || "application/octet-stream" }
6110
+ headers: { "Content-Type": file.type || "application/octet-stream" },
6111
+ signal: abortSignal
5839
6112
  });
5840
6113
  if (!uploadResponse.ok) {
5841
6114
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -5852,6 +6125,12 @@ var CosUploadService = class {
5852
6125
  objectKey: presignedItem.object_key
5853
6126
  };
5854
6127
  } catch (error) {
6128
+ if (error instanceof Error && error.name === "AbortError") return {
6129
+ success: false,
6130
+ error: "Upload cancelled",
6131
+ aborted: true,
6132
+ objectKey: presignedItem.object_key
6133
+ };
5855
6134
  return {
5856
6135
  success: false,
5857
6136
  error: error instanceof Error ? error.message : "Unknown error",
@@ -5905,35 +6184,35 @@ var CosUploadService = class {
5905
6184
  * E2B Filesystem Implementation
5906
6185
  *
5907
6186
  * Provides FilesResource implementation using E2B Sandbox SDK.
5908
- * Directly uses e2b SDK types.
6187
+ * Supports optional auto-reconnect on auth failure (401/403).
5909
6188
  *
5910
6189
  * @see https://e2b.dev/docs/filesystem/read-write
5911
6190
  * @see https://e2b.dev/docs/filesystem/watch
5912
6191
  */
5913
6192
  /**
5914
- * E2B Filesystem Implementation
6193
+ * E2B Filesystem
5915
6194
  *
5916
- * Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource interface.
6195
+ * Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource.
6196
+ * When `reconnectFn` is provided, automatically reconnects on auth errors.
5917
6197
  *
5918
6198
  * @example
5919
6199
  * ```typescript
5920
- * const fs = await E2BFilesystem.connect({
5921
- * sandboxId: 'sandbox-123',
5922
- * apiKey: 'e2b_xxx'
5923
- * });
5924
- *
5925
- * // Read/write files
5926
- * await fs.write('/test.txt', 'Hello World');
5927
- * const content = await fs.read('/test.txt');
6200
+ * // Basic usage (no auto-reconnect)
6201
+ * const fs = await E2BFilesystem.connect({ sandboxId: '...' });
5928
6202
  *
5929
- * // Watch for changes
5930
- * const handle = await fs.watchDir('/workspace', (event) => {
5931
- * console.log('File changed:', event);
5932
- * });
6203
+ * // With auto-reconnect on token expiry
6204
+ * const fs = await E2BFilesystem.connect({ sandboxId: '...' });
6205
+ * fs.setReconnectFn(async () => fetchFreshConnectionInfo());
5933
6206
  * ```
5934
6207
  */
5935
6208
  var E2BFilesystem = class E2BFilesystem {
6209
+ static {
6210
+ this.MIN_RECONNECT_INTERVAL_MS = 10 * 1e3;
6211
+ }
5936
6212
  constructor(sandbox) {
6213
+ this.isReconnecting = false;
6214
+ this.reconnectSubscribers = [];
6215
+ this.lastReconnectAt = 0;
5937
6216
  this.sandbox = sandbox;
5938
6217
  }
5939
6218
  /**
@@ -5949,38 +6228,111 @@ var E2BFilesystem = class E2BFilesystem {
5949
6228
  }));
5950
6229
  }
5951
6230
  /**
6231
+ * Set reconnect callback. When set, auth errors trigger automatic reconnect + retry.
6232
+ */
6233
+ setReconnectFn(fn) {
6234
+ this.reconnectFn = fn;
6235
+ }
6236
+ /**
5952
6237
  * Get the underlying E2B Sandbox instance
5953
6238
  */
5954
6239
  getSandbox() {
5955
6240
  return this.sandbox;
5956
6241
  }
6242
+ isAuthError(error) {
6243
+ if (!error || typeof error !== "object") return false;
6244
+ const err = error;
6245
+ if (err.status === 401 || err.status === 403) return true;
6246
+ if (err.statusCode === 401 || err.statusCode === 403) return true;
6247
+ if (err.response && typeof err.response === "object") {
6248
+ const resp = err.response;
6249
+ if (resp.status === 401 || resp.status === 403) return true;
6250
+ }
6251
+ if (typeof err.message === "string") {
6252
+ const msg = err.message.toLowerCase();
6253
+ if (msg.includes("unauthorized") || msg.includes("token expired") || msg.includes("authentication")) return true;
6254
+ }
6255
+ return false;
6256
+ }
6257
+ canAttemptReconnect() {
6258
+ return Date.now() - this.lastReconnectAt >= E2BFilesystem.MIN_RECONNECT_INTERVAL_MS;
6259
+ }
6260
+ /**
6261
+ * Reconnect with fresh credentials.
6262
+ * Only one reconnect in-flight at a time; concurrent callers share the result.
6263
+ */
6264
+ async reconnect() {
6265
+ if (this.isReconnecting) return new Promise((resolve, reject) => {
6266
+ this.reconnectSubscribers.push((success) => {
6267
+ if (success) resolve();
6268
+ else reject(/* @__PURE__ */ new Error("E2B sandbox reconnect failed"));
6269
+ });
6270
+ });
6271
+ this.isReconnecting = true;
6272
+ this.lastReconnectAt = Date.now();
6273
+ try {
6274
+ const info = await this.reconnectFn();
6275
+ this.sandbox = await Sandbox.connect(info.sandboxId, {
6276
+ domain: info.domain,
6277
+ apiUrl: info.apiUrl,
6278
+ requestTimeoutMs: info.requestTimeoutMs,
6279
+ debug: info.debug,
6280
+ headers: info.headers
6281
+ });
6282
+ this.reconnectSubscribers.forEach((cb) => cb(true));
6283
+ this.reconnectSubscribers = [];
6284
+ } catch (error) {
6285
+ this.reconnectSubscribers.forEach((cb) => cb(false));
6286
+ this.reconnectSubscribers = [];
6287
+ throw error;
6288
+ } finally {
6289
+ this.isReconnecting = false;
6290
+ }
6291
+ }
6292
+ /**
6293
+ * Execute an operation. If reconnectFn is set and an auth error occurs,
6294
+ * reconnect and retry once.
6295
+ */
6296
+ async exec(operation) {
6297
+ try {
6298
+ return await operation(this.sandbox.files);
6299
+ } catch (error) {
6300
+ if (this.reconnectFn && this.isAuthError(error) && this.canAttemptReconnect()) {
6301
+ await this.reconnect();
6302
+ return operation(this.sandbox.files);
6303
+ }
6304
+ throw error;
6305
+ }
6306
+ }
5957
6307
  read(path, opts) {
5958
- return this.sandbox.files.read(path, opts);
6308
+ return this.exec((f) => f.read(path, opts));
5959
6309
  }
5960
6310
  write(pathOrFiles, dataOrOpts, opts) {
5961
- if (Array.isArray(pathOrFiles)) return this.sandbox.files.write(pathOrFiles, dataOrOpts);
5962
- return this.sandbox.files.write(pathOrFiles, dataOrOpts, opts);
6311
+ return this.exec((f) => {
6312
+ if (Array.isArray(pathOrFiles)) return f.write(pathOrFiles, dataOrOpts);
6313
+ return f.write(pathOrFiles, dataOrOpts, opts);
6314
+ });
5963
6315
  }
5964
6316
  async list(path, opts) {
5965
- return this.sandbox.files.list(path, opts);
6317
+ return this.exec((f) => f.list(path, opts));
5966
6318
  }
5967
6319
  async exists(path, opts) {
5968
- return this.sandbox.files.exists(path, opts);
6320
+ return this.exec((f) => f.exists(path, opts));
5969
6321
  }
5970
6322
  async makeDir(path, opts) {
5971
- return this.sandbox.files.makeDir(path, opts);
6323
+ return this.exec((f) => f.makeDir(path, opts));
5972
6324
  }
5973
6325
  async remove(path, opts) {
5974
- return this.sandbox.files.remove(path, opts);
6326
+ return this.exec((f) => f.remove(path, opts));
5975
6327
  }
5976
6328
  async rename(oldPath, newPath, opts) {
5977
- return this.sandbox.files.rename(oldPath, newPath, opts);
6329
+ return this.exec((f) => f.rename(oldPath, newPath, opts));
5978
6330
  }
5979
6331
  async getInfo(path, opts) {
5980
- return this.sandbox.files.getInfo(path, opts);
6332
+ return this.exec((f) => f.getInfo(path, opts));
5981
6333
  }
5982
6334
  async watchDir(path, onEvent, opts) {
5983
- return this.sandbox.files.watchDir(path, onEvent, opts);
6335
+ return this.exec((f) => f.watchDir(path, onEvent, opts));
5984
6336
  }
5985
6337
  };
5986
6338
 
@@ -6132,8 +6484,11 @@ var CloudAgentProvider = class CloudAgentProvider {
6132
6484
  this.filesystemCache = /* @__PURE__ */ new Map();
6133
6485
  this.connectionCache = /* @__PURE__ */ new Map();
6134
6486
  this.eventListeners = /* @__PURE__ */ new Map();
6487
+ this.productConfigCache = null;
6135
6488
  this.options = options;
6136
6489
  this.logger = options.logger;
6490
+ this.marketplaceCache = new LRUCache(200);
6491
+ this.pluginCache = new LRUCache(2e3);
6137
6492
  if (options.endpoint) httpService.setBaseURL(options.endpoint);
6138
6493
  if (options.authToken) httpService.setAuthToken(options.authToken);
6139
6494
  if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
@@ -6184,7 +6539,14 @@ var CloudAgentProvider = class CloudAgentProvider {
6184
6539
  const cached = this.filesystemCache.get(agentId);
6185
6540
  if (cached) return cached;
6186
6541
  const info = await this.getSandboxInfo(agentId);
6187
- const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
6542
+ const e2bFilesystem = await E2BFilesystem.connect(info);
6543
+ e2bFilesystem.setReconnectFn(async () => {
6544
+ this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
6545
+ const newInfo = await this.getSandboxInfo(agentId);
6546
+ this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
6547
+ return newInfo;
6548
+ });
6549
+ const filesystem = createAgentFilesystem(e2bFilesystem);
6188
6550
  this.filesystemCache.set(agentId, filesystem);
6189
6551
  this.logger?.debug(`Created filesystem for agent: ${agentId}`);
6190
6552
  return filesystem;
@@ -6252,15 +6614,9 @@ var CloudAgentProvider = class CloudAgentProvider {
6252
6614
  const url = this.buildGetUrl("/console/as/conversations/", params);
6253
6615
  const apiResponse = await httpService.get(url);
6254
6616
  if (!apiResponse.data) throw new Error("No data in API response");
6255
- const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
6256
- const pagination = apiResponse.data.pagination;
6257
- console.log("[CloudAgentProvider] API response:", {
6258
- agentsCount: agents.length,
6259
- pagination
6260
- });
6261
6617
  return {
6262
- agents,
6263
- pagination
6618
+ agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
6619
+ pagination: apiResponse.data.pagination
6264
6620
  };
6265
6621
  } catch (error) {
6266
6622
  this.logger?.error("Failed to list agents:", error);
@@ -6270,13 +6626,21 @@ var CloudAgentProvider = class CloudAgentProvider {
6270
6626
  /**
6271
6627
  * Create a new conversation
6272
6628
  * POST {endpoint}/console/as/conversations
6629
+ * @param params - Session params containing cwd and optional configuration
6273
6630
  */
6274
- async create() {
6631
+ async create(params) {
6275
6632
  try {
6276
- const apiResponse = await httpService.post("/console/as/conversations/", {
6277
- prompt: "",
6278
- model: "deepseek-r1"
6279
- });
6633
+ const { options = {} } = params;
6634
+ const codebuddyMeta = options._meta?.["codebuddy.ai"];
6635
+ const tagsObj = options.tags || codebuddyMeta?.tags;
6636
+ const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
6637
+ const createPayload = {
6638
+ prompt: (options.prompt || "").slice(0, 100),
6639
+ model: options.model || "deepseek-r1",
6640
+ ...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
6641
+ };
6642
+ console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
6643
+ const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
6280
6644
  if (!apiResponse.data) throw new Error("No data in API response");
6281
6645
  this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
6282
6646
  return apiResponse.data.id;
@@ -6326,7 +6690,14 @@ var CloudAgentProvider = class CloudAgentProvider {
6326
6690
  } catch (error) {
6327
6691
  this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
6328
6692
  }
6329
- if (this.connectionCache.get(endpoint)) this.connectionCache.delete(endpoint);
6693
+ const existingConnection = this.connectionCache.get(endpoint);
6694
+ if (existingConnection) {
6695
+ this.connectionCache.delete(endpoint);
6696
+ existingConnection.removeAllListeners();
6697
+ existingConnection.disconnect().catch((err) => {
6698
+ this.logger?.debug("Failed to disconnect old connection:", err);
6699
+ });
6700
+ }
6330
6701
  const clientCapabilities = {
6331
6702
  ...this.options.clientCapabilities,
6332
6703
  _meta: {
@@ -6446,6 +6817,35 @@ var CloudAgentProvider = class CloudAgentProvider {
6446
6817
  }
6447
6818
  }
6448
6819
  /**
6820
+ * Update conversation status by ID
6821
+ * POST {endpoint}/console/as/conversations/{agentId}
6822
+ *
6823
+ * @param agentId - Conversation ID to update
6824
+ * @param status - New status for the conversation
6825
+ * @returns PatchConversationResponse containing the updated conversation ID
6826
+ *
6827
+ * @example
6828
+ * ```typescript
6829
+ * const result = await provider.updateStatus('agent-123', 'completed');
6830
+ * console.log('Updated conversation status:', result.id);
6831
+ * ```
6832
+ */
6833
+ async updateStatus(agentId, status) {
6834
+ try {
6835
+ const body = { status };
6836
+ const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
6837
+ if (!apiResponse.data) {
6838
+ this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
6839
+ return { id: agentId };
6840
+ }
6841
+ this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
6842
+ return apiResponse.data;
6843
+ } catch (error) {
6844
+ this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
6845
+ throw error;
6846
+ }
6847
+ }
6848
+ /**
6449
6849
  * Get available models from product configuration
6450
6850
  *
6451
6851
  * GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
@@ -6476,9 +6876,13 @@ var CloudAgentProvider = class CloudAgentProvider {
6476
6876
  this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
6477
6877
  return [];
6478
6878
  }
6479
- const models = apiResponse.data.models ?? [];
6480
- this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /console/enterprises API`);
6481
- return models.map((model) => ({
6879
+ this.productConfigCache = apiResponse.data;
6880
+ const productConfig = apiResponse.data;
6881
+ const allModels = productConfig.models ?? [];
6882
+ const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
6883
+ const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
6884
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
6885
+ return filteredModels.map((model) => ({
6482
6886
  id: model.id,
6483
6887
  name: model.name ?? model.id,
6484
6888
  description: model.description,
@@ -6500,6 +6904,43 @@ var CloudAgentProvider = class CloudAgentProvider {
6500
6904
  }
6501
6905
  }
6502
6906
  /**
6907
+ * 获取产品部署类型(从缓存)
6908
+ * 需要先调用 getModels() 初始化缓存
6909
+ *
6910
+ * @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
6911
+ */
6912
+ getDeploymentType() {
6913
+ return this.productConfigCache?.deploymentType ?? "SaaS";
6914
+ }
6915
+ /**
6916
+ * 获取 Credit 购买引导配置(从缓存)
6917
+ * 需要先调用 getModels() 初始化缓存
6918
+ *
6919
+ * @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
6920
+ */
6921
+ getCreditPurchaseActions() {
6922
+ return this.productConfigCache?.config?.creditPurchaseActions ?? {
6923
+ "14018": {
6924
+ labelZh: "获取 Credits",
6925
+ labelEn: "Get credits",
6926
+ url: "https://www.codebuddy.cn/profile/plan",
6927
+ showButton: true
6928
+ },
6929
+ "6004": {
6930
+ labelZh: "升级专业版",
6931
+ labelEn: "Upgrade to Pro",
6932
+ url: "https://www.codebuddy.cn/profile/plan",
6933
+ showButton: true
6934
+ },
6935
+ "6005": {
6936
+ labelZh: "升级专业版",
6937
+ labelEn: "Upgrade to Pro",
6938
+ url: "https://www.codebuddy.cn/profile/plan",
6939
+ showButton: true
6940
+ }
6941
+ };
6942
+ }
6943
+ /**
6503
6944
  * Generate a unique request ID
6504
6945
  */
6505
6946
  generateRequestId() {
@@ -6582,7 +7023,7 @@ var CloudAgentProvider = class CloudAgentProvider {
6582
7023
  /**
6583
7024
  * Upload files to cloud storage via COS presigned URL
6584
7025
  *
6585
- * @param params - files array (File objects in browser)
7026
+ * @param params - files array (File objects in browser), optional abortSignal
6586
7027
  * @returns Response with corresponding cloud URLs
6587
7028
  */
6588
7029
  async uploadFile(params) {
@@ -6592,12 +7033,13 @@ var CloudAgentProvider = class CloudAgentProvider {
6592
7033
  success: false,
6593
7034
  error: "No valid File objects provided"
6594
7035
  };
6595
- const result = await this.cosUploadService.uploadFiles(files);
7036
+ const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
6596
7037
  return {
6597
7038
  success: result.success,
6598
7039
  urls: result.urls,
6599
7040
  expireSeconds: result.expireSeconds,
6600
- error: result.error
7041
+ error: result.error,
7042
+ aborted: result.aborted
6601
7043
  };
6602
7044
  }
6603
7045
  /**
@@ -6639,20 +7081,22 @@ var CloudAgentProvider = class CloudAgentProvider {
6639
7081
  }
6640
7082
  /**
6641
7083
  * 获取支持的场景列表
6642
- * API 端点: GET /console/as/support/scenes
7084
+ * API 端点: GET /v2/as/support/scenes (不鉴权)
6643
7085
  * 用于 Welcome 页面的 QuickActions 快捷操作
6644
7086
  *
7087
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
6645
7088
  * @returns Promise<SupportScene[]> 支持的场景列表
6646
7089
  */
6647
- async getSupportScenes() {
7090
+ async getSupportScenes(locale) {
6648
7091
  try {
6649
- const apiResponse = await httpService.get("/console/as/support/scenes");
7092
+ const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
7093
+ const apiResponse = await httpService.get(url);
6650
7094
  if (!apiResponse.data) {
6651
7095
  this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
6652
7096
  return [];
6653
7097
  }
6654
7098
  const scenes = apiResponse.data.scenes || [];
6655
- this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
7099
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
6656
7100
  return scenes;
6657
7101
  } catch (error) {
6658
7102
  this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
@@ -6668,7 +7112,8 @@ var CloudAgentProvider = class CloudAgentProvider {
6668
7112
  type: "cloud",
6669
7113
  status,
6670
7114
  createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
6671
- capabilities: this.options.clientCapabilities
7115
+ capabilities: this.options.clientCapabilities,
7116
+ isUserDefinedTitle: data.isUserDefinedTitle
6672
7117
  };
6673
7118
  }
6674
7119
  /**
@@ -6684,55 +7129,498 @@ var CloudAgentProvider = class CloudAgentProvider {
6684
7129
  const queryString = searchParams.toString();
6685
7130
  return queryString ? `${path}?${queryString}` : path;
6686
7131
  }
6687
- };
6688
-
6689
- //#endregion
6690
- //#region ../agent-provider/src/common/client/session.ts
6691
- /**
6692
- * ActiveSessionImpl - Implements the ActiveSession interface
6693
- *
6694
- * This class wraps an AgentConnection and provides the session-centric API.
6695
- * It is created by SessionManager when creating or loading sessions.
6696
- *
6697
- * @example
6698
- * ```typescript
6699
- * // Created by client.sessions.new() or client.sessions.load()
6700
- * const session = await client.sessions.new({ cwd: '/workspace' });
6701
- *
6702
- * // Access agent state
6703
- * console.log(session.agentState.status);
6704
- *
6705
- * // Send prompt
6706
- * const response = await session.prompts.send({ content: 'Hello!' });
6707
- *
6708
- * // Cleanup
6709
- * session.disconnect();
6710
- * ```
6711
- */
6712
- var ActiveSessionImpl = class {
6713
7132
  /**
6714
- * Create an ActiveSessionImpl instance
6715
- *
6716
- * @param sessionId - Session ID
6717
- * @param agentId - Agent ID
6718
- * @param connection - Already connected AgentConnection
6719
- * @param options - Additional options
7133
+ * 获取已安装插件列表
7134
+ * GET /console/as/user/plugins/installed
6720
7135
  */
6721
- constructor(sessionId, agentId, connection, options = {}) {
6722
- this._availableCommands = [];
6723
- this.listeners = /* @__PURE__ */ new Map();
6724
- this.onceListeners = /* @__PURE__ */ new Map();
6725
- this._id = sessionId;
6726
- this._agentId = agentId;
6727
- this.connection = connection;
6728
- this.logger = options.logger;
6729
- this._getFilesystem = options.getFilesystem;
6730
- this._connectionInfo = options.connectionInfo;
6731
- this.setupConnectionEvents(connection);
6732
- this.agent = this.createAgentOperations();
6733
- this.prompts = this.createPromptsResource();
6734
- this.artifacts = this.createArtifactsResource();
6735
- this.files = this.createFilesResource();
7136
+ async getInstalledPlugins(forceRefresh) {
7137
+ try {
7138
+ const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
7139
+ name: p.plugin_name,
7140
+ marketplaceName: p.marketplace_name,
7141
+ status: p.enabled ? "enabled" : "disabled",
7142
+ description: p.description,
7143
+ version: p.version,
7144
+ installScope: p.scope === "local" ? "project" : p.scope,
7145
+ installedScopes: [p.scope],
7146
+ installId: p.id
7147
+ }));
7148
+ result.forEach((plugin) => {
7149
+ this.pluginCache.set(plugin.name, plugin);
7150
+ });
7151
+ return result;
7152
+ } catch (error) {
7153
+ this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
7154
+ throw error;
7155
+ }
7156
+ }
7157
+ /**
7158
+ * 安装插件
7159
+ * POST /console/as/user/plugins/install
7160
+ *
7161
+ * @param pluginNames - 插件名称数组
7162
+ * @param marketplaceNameOrId - 市场名称或 ID
7163
+ */
7164
+ async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
7165
+ try {
7166
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7167
+ if (!marketplaceId) return {
7168
+ success: false,
7169
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7170
+ };
7171
+ const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
7172
+ plugin_name: pluginName,
7173
+ marketplace_id: marketplaceId,
7174
+ version: "latest"
7175
+ })))).filter((r) => r.status === "rejected");
7176
+ if (failed.length > 0) {
7177
+ const errors = failed.map((r) => r.reason?.message || "Unknown error");
7178
+ return {
7179
+ success: false,
7180
+ error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
7181
+ };
7182
+ }
7183
+ return { success: true };
7184
+ } catch (error) {
7185
+ return {
7186
+ success: false,
7187
+ error: this.extractErrorMessage(error)
7188
+ };
7189
+ }
7190
+ }
7191
+ /**
7192
+ * 卸载插件
7193
+ * POST /console/as/user/plugins/installed/:id/uninstall
7194
+ *
7195
+ * 完整链路:
7196
+ * CloudAgentProvider.uninstallPlugin()
7197
+ * -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
7198
+ * -> agentserver: PluginInstallService.Uninstall()
7199
+ * -> 软删除 DB + 异步同步到活跃沙箱
7200
+ *
7201
+ * @param pluginName - 插件名称
7202
+ * @param marketplaceName - 市场名称(用于标识唯一插件)
7203
+ * @param scope - 卸载范围 ('user' | 'project' | 'project-local')
7204
+ */
7205
+ async uninstallPlugin(pluginName, marketplaceName, scope) {
7206
+ try {
7207
+ const installId = await this.findPluginInstallId(pluginName);
7208
+ if (!installId) return {
7209
+ success: false,
7210
+ error: `Plugin not found or not installed: ${pluginName}`
7211
+ };
7212
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
7213
+ return { success: true };
7214
+ } catch (error) {
7215
+ return {
7216
+ success: false,
7217
+ error: this.extractErrorMessage(error)
7218
+ };
7219
+ }
7220
+ }
7221
+ /**
7222
+ * 获取插件市场列表
7223
+ * GET /console/as/marketplace/sources
7224
+ */
7225
+ async getPluginMarketplaces(forceRefresh) {
7226
+ try {
7227
+ const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
7228
+ id: src.id,
7229
+ name: src.name,
7230
+ type: this.mapSourceType(src.source_type),
7231
+ source: { url: src.url },
7232
+ description: src.name,
7233
+ isBuiltin: src.is_default
7234
+ }));
7235
+ result.forEach((m) => {
7236
+ const marketplaceInfo = {
7237
+ id: m.id,
7238
+ name: m.name
7239
+ };
7240
+ this.marketplaceCache.set(m.name, marketplaceInfo);
7241
+ this.marketplaceCache.set(m.id, marketplaceInfo);
7242
+ });
7243
+ return result;
7244
+ } catch (error) {
7245
+ this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
7246
+ throw error;
7247
+ }
7248
+ }
7249
+ /**
7250
+ * 获取市场下的插件列表
7251
+ * - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
7252
+ * - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
7253
+ *
7254
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
7255
+ * @param forceRefresh - 是否强制刷新
7256
+ * @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
7257
+ */
7258
+ async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
7259
+ try {
7260
+ if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
7261
+ q: searchText,
7262
+ page: 1,
7263
+ page_size: 100
7264
+ } })).data?.plugins || []).map((p) => this.mapPluginData(p));
7265
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
7266
+ if (!sourceId) {
7267
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
7268
+ return [];
7269
+ }
7270
+ const params = {
7271
+ source_id: sourceId,
7272
+ page: 1,
7273
+ page_size: 100
7274
+ };
7275
+ return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
7276
+ } catch (error) {
7277
+ this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
7278
+ throw error;
7279
+ }
7280
+ }
7281
+ /**
7282
+ * 获取插件详情
7283
+ * GET /console/as/marketplace/plugins/:name/detail
7284
+ *
7285
+ * @param pluginName - 插件名称
7286
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
7287
+ */
7288
+ async getPluginDetail(pluginName, marketplaceNameOrId) {
7289
+ try {
7290
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
7291
+ if (!sourceId) {
7292
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
7293
+ return null;
7294
+ }
7295
+ const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
7296
+ if (!p) return null;
7297
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
7298
+ const tags = p.tags ? JSON.parse(p.tags) : [];
7299
+ return {
7300
+ name: p.name,
7301
+ marketplaceName: p.marketplace_name,
7302
+ description: p.description,
7303
+ version: p.version,
7304
+ iconUrl: p.icon_url,
7305
+ tags,
7306
+ installed: p.installed,
7307
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
7308
+ readme: p.readme,
7309
+ author: p.author,
7310
+ homepage: p.homepage,
7311
+ repositoryUrl: p.repository_url,
7312
+ license: p.license,
7313
+ ...capabilities
7314
+ };
7315
+ } catch (error) {
7316
+ this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
7317
+ throw error;
7318
+ }
7319
+ }
7320
+ /**
7321
+ * 添加插件市场
7322
+ * POST /console/as/marketplace/sources
7323
+ */
7324
+ async addPluginMarketplace(sourceUrl, name) {
7325
+ try {
7326
+ const body = {
7327
+ source_type: sourceUrl.startsWith("http") ? "url" : "github",
7328
+ url: sourceUrl,
7329
+ name: name || sourceUrl
7330
+ };
7331
+ const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
7332
+ if (!sourceData) throw new Error("Invalid response from server");
7333
+ const marketplaceInfo = {
7334
+ id: sourceData.id,
7335
+ name: sourceData.name
7336
+ };
7337
+ this.marketplaceCache.set(sourceData.name, marketplaceInfo);
7338
+ this.marketplaceCache.set(sourceData.id, marketplaceInfo);
7339
+ return {
7340
+ success: true,
7341
+ marketplace: {
7342
+ id: sourceData.id,
7343
+ name: sourceData.name,
7344
+ type: this.mapSourceType(sourceData.source_type),
7345
+ source: { url: sourceData.url },
7346
+ isBuiltin: sourceData.is_default
7347
+ }
7348
+ };
7349
+ } catch (error) {
7350
+ return {
7351
+ success: false,
7352
+ error: this.extractErrorMessage(error)
7353
+ };
7354
+ }
7355
+ }
7356
+ /**
7357
+ * 删除插件市场
7358
+ * POST /console/as/marketplace/sources/:id/delete
7359
+ */
7360
+ async removePluginMarketplace(marketplaceNameOrId) {
7361
+ try {
7362
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7363
+ if (!marketplaceId) return {
7364
+ success: false,
7365
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7366
+ };
7367
+ await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
7368
+ return { success: true };
7369
+ } catch (error) {
7370
+ return {
7371
+ success: false,
7372
+ error: this.extractErrorMessage(error)
7373
+ };
7374
+ }
7375
+ }
7376
+ /**
7377
+ * 刷新插件市场
7378
+ * POST /console/as/marketplace/sources/:id/check-updates
7379
+ */
7380
+ async refreshPluginMarketplace(marketplaceNameOrId) {
7381
+ try {
7382
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
7383
+ if (!marketplaceId) return {
7384
+ success: false,
7385
+ error: `Marketplace not found: ${marketplaceNameOrId}`
7386
+ };
7387
+ return {
7388
+ success: true,
7389
+ plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
7390
+ };
7391
+ } catch (error) {
7392
+ return {
7393
+ success: false,
7394
+ error: this.extractErrorMessage(error)
7395
+ };
7396
+ }
7397
+ }
7398
+ /**
7399
+ * 批量切换插件启用/禁用状态
7400
+ * POST /console/as/user/plugins/installed/:id/toggle
7401
+ */
7402
+ async batchTogglePlugins(request) {
7403
+ try {
7404
+ const results = await Promise.allSettled(request.items.map(async (item) => {
7405
+ const installId = await this.findPluginInstallId(item.pluginName);
7406
+ if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
7407
+ const enabled = item.operation === "enable";
7408
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
7409
+ return item;
7410
+ }));
7411
+ const succeededPlugins = [];
7412
+ const failedPlugins = [];
7413
+ results.forEach((r, i) => {
7414
+ const item = request.items[i];
7415
+ if (r.status === "fulfilled") succeededPlugins.push(item);
7416
+ else failedPlugins.push({
7417
+ ...item,
7418
+ error: r.reason?.message || "Unknown error"
7419
+ });
7420
+ });
7421
+ return {
7422
+ success: failedPlugins.length === 0,
7423
+ succeededPlugins,
7424
+ failedPlugins
7425
+ };
7426
+ } catch (error) {
7427
+ return {
7428
+ success: false,
7429
+ succeededPlugins: [],
7430
+ failedPlugins: request.items.map((item) => ({
7431
+ ...item,
7432
+ error: this.extractErrorMessage(error)
7433
+ }))
7434
+ };
7435
+ }
7436
+ }
7437
+ /**
7438
+ * 将后端插件数据映射为前端格式
7439
+ */
7440
+ mapPluginData(p) {
7441
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
7442
+ let tags = [];
7443
+ if (p.tags) {
7444
+ if (Array.isArray(p.tags)) tags = p.tags;
7445
+ else if (typeof p.tags === "string") try {
7446
+ const parsed = JSON.parse(p.tags);
7447
+ tags = Array.isArray(parsed) ? parsed : [parsed];
7448
+ } catch {
7449
+ tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
7450
+ }
7451
+ }
7452
+ return {
7453
+ name: p.name,
7454
+ marketplaceName: p.marketplace_name,
7455
+ description: p.description,
7456
+ version: p.version,
7457
+ iconUrl: p.icon_url,
7458
+ tags,
7459
+ installed: p.installed,
7460
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
7461
+ installedScopes: p.installed ? [p.installed_scope] : [],
7462
+ ...capabilities
7463
+ };
7464
+ }
7465
+ /**
7466
+ * 从缓存中查找插件的 install_id
7467
+ * 如果缓存未命中,则调用 API 获取并缓存
7468
+ *
7469
+ * @param pluginName - 插件名称
7470
+ * @returns install_id 或 null
7471
+ */
7472
+ async findPluginInstallId(pluginName) {
7473
+ const cached = this.pluginCache.get(pluginName);
7474
+ if (cached) return cached.installId;
7475
+ await this.getInstalledPlugins();
7476
+ return this.pluginCache.get(pluginName)?.installId || null;
7477
+ }
7478
+ /**
7479
+ * 从缓存中查找 marketplace ID
7480
+ * 如果缓存未命中,则调用 API 获取并缓存
7481
+ */
7482
+ async findMarketplaceId(nameOrId) {
7483
+ const cached = this.marketplaceCache.get(nameOrId);
7484
+ if (cached) return cached.id;
7485
+ await this.getPluginMarketplaces();
7486
+ return this.marketplaceCache.get(nameOrId)?.id || null;
7487
+ }
7488
+ /**
7489
+ * 提取 API 错误信息
7490
+ * 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
7491
+ */
7492
+ extractErrorMessage(error) {
7493
+ if (error instanceof AxiosError) {
7494
+ const status = error.response?.status;
7495
+ const apiResponse = error.response?.data;
7496
+ const parts = [];
7497
+ if (status) parts.push(`HTTP ${status}`);
7498
+ if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
7499
+ if (apiResponse?.msg) parts.push(apiResponse.msg);
7500
+ else if (error.message) parts.push(error.message);
7501
+ const errorMessage = parts.join(" - ");
7502
+ this.logger?.error("[CloudAgentProvider] API Error:", {
7503
+ status,
7504
+ code: apiResponse?.code,
7505
+ msg: apiResponse?.msg,
7506
+ requestId: apiResponse?.requestId,
7507
+ url: error.config?.url,
7508
+ method: error.config?.method
7509
+ });
7510
+ return errorMessage;
7511
+ }
7512
+ if (error instanceof Error) return error.message;
7513
+ return "Unknown error";
7514
+ }
7515
+ /**
7516
+ * 映射后端 source_type 到前端类型
7517
+ */
7518
+ mapSourceType(sourceType) {
7519
+ switch (sourceType) {
7520
+ case "github": return "github";
7521
+ case "official": return "custom";
7522
+ default: return "custom";
7523
+ }
7524
+ }
7525
+ /**
7526
+ * 解析 capabilities JSON 字符串
7527
+ */
7528
+ parseCapabilities(capabilitiesStr) {
7529
+ try {
7530
+ const cap = JSON.parse(capabilitiesStr);
7531
+ return {
7532
+ commands: cap.commands,
7533
+ skills: cap.skills,
7534
+ mcpServers: cap.mcp,
7535
+ agents: cap.agents,
7536
+ hooks: cap.hooks,
7537
+ rules: cap.rules
7538
+ };
7539
+ } catch {
7540
+ return {};
7541
+ }
7542
+ }
7543
+ /**
7544
+ * 上报 telemetry 事件(Cloud 模式)
7545
+ * 通过 HTTP POST 发送到 /v2/report
7546
+ * 注入用户信息和浏览器环境等公共字段
7547
+ */
7548
+ async reportTelemetry(eventName, payload) {
7549
+ try {
7550
+ const account = accountService.getAccount();
7551
+ const commonFields = {};
7552
+ if (account) {
7553
+ commonFields.userId = account.uid;
7554
+ commonFields.userNickname = account.nickname;
7555
+ if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
7556
+ if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
7557
+ }
7558
+ if (typeof navigator !== "undefined") {
7559
+ commonFields.userAgent = navigator.userAgent;
7560
+ commonFields.os = navigator.platform;
7561
+ }
7562
+ const events = [{
7563
+ eventCode: eventName,
7564
+ timestamp: Date.now(),
7565
+ reportDelay: 0,
7566
+ ...commonFields,
7567
+ ...payload
7568
+ }];
7569
+ await httpService.post("/v2/report", events);
7570
+ } catch (error) {
7571
+ this.logger?.warn("reportTelemetry() failed:", error);
7572
+ }
7573
+ }
7574
+ };
7575
+
7576
+ //#endregion
7577
+ //#region ../agent-provider/src/common/client/session.ts
7578
+ /**
7579
+ * ActiveSessionImpl - Implements the ActiveSession interface
7580
+ *
7581
+ * This class wraps an AgentConnection and provides the session-centric API.
7582
+ * It is created by SessionManager when creating or loading sessions.
7583
+ *
7584
+ * @example
7585
+ * ```typescript
7586
+ * // Created by client.sessions.new() or client.sessions.load()
7587
+ * const session = await client.sessions.new({ cwd: '/workspace' });
7588
+ *
7589
+ * // Access agent state
7590
+ * console.log(session.agentState.status);
7591
+ *
7592
+ * // Send prompt
7593
+ * const response = await session.prompts.send({ content: 'Hello!' });
7594
+ *
7595
+ * // Cleanup
7596
+ * session.disconnect();
7597
+ * ```
7598
+ */
7599
+ var ActiveSessionImpl = class {
7600
+ /**
7601
+ * Create an ActiveSessionImpl instance
7602
+ *
7603
+ * @param sessionId - Session ID
7604
+ * @param agentId - Agent ID
7605
+ * @param connection - Already connected AgentConnection
7606
+ * @param options - Additional options
7607
+ */
7608
+ constructor(sessionId, agentId, connection, options = {}) {
7609
+ this._availableCommands = [];
7610
+ this.listeners = /* @__PURE__ */ new Map();
7611
+ this.onceListeners = /* @__PURE__ */ new Map();
7612
+ this.connectionListeners = [];
7613
+ this._id = sessionId;
7614
+ this._agentId = agentId;
7615
+ this.connection = connection;
7616
+ this.logger = options.logger;
7617
+ this._getFilesystem = options.getFilesystem;
7618
+ this._connectionInfo = options.connectionInfo;
7619
+ this.setupConnectionEvents(connection);
7620
+ this.agent = this.createAgentOperations();
7621
+ this.prompts = this.createPromptsResource();
7622
+ this.artifacts = this.createArtifactsResource();
7623
+ this.files = this.createFilesResource();
6736
7624
  }
6737
7625
  /**
6738
7626
  * Session ID
@@ -6747,6 +7635,18 @@ var ActiveSessionImpl = class {
6747
7635
  return this._agentId;
6748
7636
  }
6749
7637
  /**
7638
+ * Actual workspace path (set from newSession response _meta)
7639
+ */
7640
+ get cwd() {
7641
+ return this._cwd;
7642
+ }
7643
+ /**
7644
+ * Set actual workspace path (called by SessionManager after createSession)
7645
+ */
7646
+ setCwd(cwd) {
7647
+ this._cwd = cwd;
7648
+ }
7649
+ /**
6750
7650
  * Agent state (live connection state)
6751
7651
  * Returns LocalAgentState or CloudAgentState based on transport type
6752
7652
  */
@@ -6963,8 +7863,8 @@ var ActiveSessionImpl = class {
6963
7863
  * await session.setMode('architect');
6964
7864
  * ```
6965
7865
  */
6966
- async setMode(modeId) {
6967
- if (this._availableModes) {
7866
+ async setMode(modeId, skipAvailableChecker) {
7867
+ if (this._availableModes && !skipAvailableChecker) {
6968
7868
  if (!this._availableModes.some((m) => m.id === modeId)) {
6969
7869
  const availableIds = this._availableModes.map((m) => m.id).join(", ");
6970
7870
  throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
@@ -6987,6 +7887,7 @@ var ActiveSessionImpl = class {
6987
7887
  * ```
6988
7888
  */
6989
7889
  async setSessionModel(modelId) {
7890
+ this._currentModelId = modelId;
6990
7891
  await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
6991
7892
  }
6992
7893
  /**
@@ -7064,11 +7965,23 @@ var ActiveSessionImpl = class {
7064
7965
  * Disconnect from the session/agent
7065
7966
  */
7066
7967
  disconnect() {
7968
+ this.removeConnectionListeners();
7067
7969
  this.connection.disconnect();
7068
7970
  this.removeAllListeners();
7069
7971
  this.logger?.info(`Session ${this._id}: Disconnected`);
7070
7972
  }
7071
7973
  /**
7974
+ * Detach the session from connection events without disconnecting the connection.
7975
+ * This should be called when the session is being replaced but the connection is shared.
7976
+ * Unlike disconnect(), this only removes event listeners without closing the connection.
7977
+ */
7978
+ detach() {
7979
+ this.logger?.info(`Session ${this._id}: Detaching from connection events`);
7980
+ this.removeConnectionListeners();
7981
+ this.removeAllListeners();
7982
+ this.logger?.info(`Session ${this._id}: Detached successfully`);
7983
+ }
7984
+ /**
7072
7985
  * Symbol.dispose for 'using' keyword support
7073
7986
  * Automatically disconnects and cleans up when session goes out of scope
7074
7987
  *
@@ -7087,60 +8000,85 @@ var ActiveSessionImpl = class {
7087
8000
  if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
7088
8001
  return this.connection;
7089
8002
  }
8003
+ /**
8004
+ * 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
8005
+ */
8006
+ addConnectionListener(connection, event, listener) {
8007
+ connection.on(event, listener);
8008
+ this.connectionListeners.push({
8009
+ event,
8010
+ listener
8011
+ });
8012
+ }
8013
+ /**
8014
+ * 从 connection 上移除所有本 session 注册的 listener
8015
+ */
8016
+ removeConnectionListeners() {
8017
+ for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
8018
+ this.connectionListeners = [];
8019
+ }
7090
8020
  setupConnectionEvents(connection) {
7091
- connection.on("connected", () => {
8021
+ this.addConnectionListener(connection, "connected", () => {
7092
8022
  this.emit("connected", void 0);
7093
8023
  });
7094
- connection.on("disconnected", () => {
8024
+ this.addConnectionListener(connection, "disconnected", () => {
7095
8025
  this.emit("disconnected", void 0);
7096
8026
  });
7097
- connection.on("error", (error) => {
8027
+ this.addConnectionListener(connection, "error", (error) => {
7098
8028
  this.emit("error", error);
7099
8029
  });
7100
- connection.on("sessionUpdate", (update) => {
8030
+ this.addConnectionListener(connection, "sessionUpdate", (update) => {
8031
+ const notificationSessionId = update?.sessionId;
8032
+ if (notificationSessionId && notificationSessionId !== this._id) {
8033
+ console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
8034
+ return;
8035
+ }
7101
8036
  this.emit("sessionUpdate", update);
7102
8037
  });
7103
- connection.on("artifactCreated", (artifact) => {
7104
- console.log("[Session] Forwarding artifactCreated:", {
7105
- artifactUri: artifact.uri,
7106
- artifactType: artifact.type
7107
- });
8038
+ this.addConnectionListener(connection, "artifactCreated", (artifact) => {
8039
+ if (!this.shouldForwardArtifact(artifact)) return;
7108
8040
  this.emit("artifactCreated", artifact);
7109
8041
  });
7110
- connection.on("artifactUpdated", (artifact) => {
7111
- console.log("[Session] Forwarding artifactUpdated:", {
7112
- artifactUri: artifact.uri,
7113
- artifactType: artifact.type
7114
- });
8042
+ this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
8043
+ if (!this.shouldForwardArtifact(artifact)) return;
7115
8044
  this.emit("artifactUpdated", artifact);
7116
8045
  });
7117
- connection.on("artifactDeleted", (artifact) => {
7118
- console.log("[Session] Forwarding artifactDeleted:", { artifactUri: artifact.uri });
8046
+ this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
8047
+ if (!this.shouldForwardArtifact(artifact)) return;
7119
8048
  this.emit("artifactDeleted", artifact);
7120
8049
  });
7121
- connection.on("permissionRequest", (request) => {
8050
+ this.addConnectionListener(connection, "permissionRequest", (request) => {
7122
8051
  this.emit("permissionRequest", request);
7123
8052
  });
7124
- connection.on("questionRequest", (request) => {
8053
+ this.addConnectionListener(connection, "questionRequest", (request) => {
7125
8054
  this.emit("questionRequest", request);
7126
8055
  });
7127
- connection.on("questionCancelled", () => {
8056
+ this.addConnectionListener(connection, "questionCancelled", () => {
7128
8057
  this.prompts.cancel();
7129
8058
  });
7130
- connection.on("usageUpdate", (usage) => {
8059
+ this.addConnectionListener(connection, "usageUpdate", (usage) => {
7131
8060
  this.emit("usageUpdate", usage);
7132
8061
  });
7133
- connection.on("checkpointCreated", (checkpoint) => {
8062
+ this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
8063
+ const originSessionId = checkpoint.__sessionId;
8064
+ if (originSessionId && originSessionId !== this._id) return;
7134
8065
  this.emit("checkpointCreated", checkpoint);
7135
8066
  });
7136
- connection.on("checkpointUpdated", (checkpoint) => {
8067
+ this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
8068
+ const originSessionId = checkpoint.__sessionId;
8069
+ if (originSessionId && originSessionId !== this._id) return;
7137
8070
  this.emit("checkpointUpdated", checkpoint);
7138
8071
  });
7139
- connection.on("command", (command) => {
7140
- console.log("[Session] Forwarding command:", {
7141
- action: command.action,
7142
- paramsKeys: command.params ? Object.keys(command.params) : []
7143
- });
8072
+ this.addConnectionListener(connection, "command", (command) => {
8073
+ const originSessionId = command.__sessionId;
8074
+ if (originSessionId && originSessionId !== this._id) {
8075
+ console.log("[Session] Command not forwarded:", {
8076
+ command,
8077
+ originSessionId,
8078
+ sessionId: this._id
8079
+ });
8080
+ return;
8081
+ }
7144
8082
  this.emit("command", command);
7145
8083
  });
7146
8084
  }
@@ -7150,19 +8088,20 @@ var ActiveSessionImpl = class {
7150
8088
  _meta: response._meta ?? void 0
7151
8089
  };
7152
8090
  }
8091
+ /**
8092
+ * 判断 artifact 是否应该转发给当前 session
8093
+ * 所有类型的 artifact 都按 __sessionId 严格隔离
8094
+ */
8095
+ shouldForwardArtifact(artifact) {
8096
+ const originSessionId = artifact.__sessionId;
8097
+ if (!originSessionId || originSessionId !== this._id) return false;
8098
+ return true;
8099
+ }
7153
8100
  };
7154
8101
 
7155
8102
  //#endregion
7156
8103
  //#region ../agent-provider/src/common/client/session-manager.ts
7157
8104
  /**
7158
- * SessionManager - Manages session lifecycle and connections
7159
- *
7160
- * Provides the core implementation for session-centric API operations:
7161
- * - list() - Lists sessions (mapped from agents)
7162
- * - createSession() - Creates new session (auto-creates agent)
7163
- * - loadSession() - Loads existing session (finds agent by sessionId)
7164
- */
7165
- /**
7166
8105
  * SessionManager - Session lifecycle management
7167
8106
  *
7168
8107
  * This class manages the relationship between sessions and agents.
@@ -7212,7 +8151,8 @@ var SessionManager = class {
7212
8151
  createdAt: agent.createdAt,
7213
8152
  lastActivityAt: agent.updatedAt,
7214
8153
  cwd: agent.type === "local" ? agent.cwd : void 0,
7215
- isPlayground: agent.isPlayground
8154
+ isPlayground: agent.isPlayground,
8155
+ isUserDefinedTitle: agent.isUserDefinedTitle
7216
8156
  }));
7217
8157
  console.log("[SessionManager] Returning sessions:", {
7218
8158
  count: sessions.length,
@@ -7239,13 +8179,26 @@ var SessionManager = class {
7239
8179
  if (this.provider.create) {
7240
8180
  agentId = await this.provider.create(params);
7241
8181
  this.logger?.debug(`Created new agent: ${agentId}`);
8182
+ if (params.options?.onSessionPrepared) {
8183
+ const initialPrompt = params.options?.prompt;
8184
+ const initialTitle = initialPrompt?.slice(0, 50) || "";
8185
+ params.options.onSessionPrepared({
8186
+ id: agentId,
8187
+ agentId,
8188
+ name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
8189
+ status: "connecting",
8190
+ cwd: params.cwd || "",
8191
+ createdAt: /* @__PURE__ */ new Date()
8192
+ });
8193
+ this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
8194
+ }
7242
8195
  } else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
7243
8196
  const connection = await this.provider.connect(agentId);
7244
8197
  this.logger?.debug(`Connected to agent: ${agentId}`);
7245
8198
  const response = await connection.createSession({
7246
- _meta: params._meta,
8199
+ _meta: params.options?._meta,
7247
8200
  cwd: params.cwd,
7248
- mcpServers: params.mcpServers
8201
+ mcpServers: params.options?.mcpServers
7249
8202
  });
7250
8203
  if (this.provider.registerSession) {
7251
8204
  this.provider.registerSession(response.sessionId, agentId);
@@ -7258,14 +8211,10 @@ var SessionManager = class {
7258
8211
  connectionInfo
7259
8212
  });
7260
8213
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
7261
- if (response.models?.availableModels) {
7262
- const localModels = response.models.availableModels.map((m) => ({
7263
- id: m.modelId,
7264
- name: m.name,
7265
- description: m.description ?? void 0
7266
- }));
7267
- session.setModels(localModels, response.models?.currentModelId);
7268
- }
8214
+ const availableModels = this.extractAvailableModels(response);
8215
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
8216
+ const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
8217
+ if (responseCwd) session.setCwd(responseCwd);
7269
8218
  this.logger?.info(`Session created: ${response.sessionId}`);
7270
8219
  return session;
7271
8220
  }
@@ -7301,17 +8250,31 @@ var SessionManager = class {
7301
8250
  mcpServers: params.mcpServers
7302
8251
  });
7303
8252
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
7304
- if (response.models?.availableModels) {
7305
- const localModels = response.models.availableModels.map((m) => ({
7306
- id: m.modelId,
7307
- name: m.name,
7308
- description: m.description ?? void 0
7309
- }));
7310
- session.setModels(localModels, response.models?.currentModelId);
7311
- }
8253
+ const availableModels = this.extractAvailableModels(response);
8254
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
7312
8255
  this.logger?.info(`Session loaded: ${params.sessionId}`);
7313
8256
  return session;
7314
8257
  }
8258
+ /**
8259
+ * 从 ACP response 中提取可用模型列表
8260
+ *
8261
+ * 优先级:
8262
+ * 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
8263
+ * 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
8264
+ * 3. undefined - 都没有时返回 undefined
8265
+ *
8266
+ * @param response - ACP 响应对象
8267
+ * @returns ModelInfo[] | undefined
8268
+ */
8269
+ extractAvailableModels(response) {
8270
+ const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
8271
+ if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
8272
+ const availableModels = response.models?.availableModels;
8273
+ if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
8274
+ ...model,
8275
+ ...model._meta?.["codebuddy.ai"] || {}
8276
+ }));
8277
+ }
7315
8278
  };
7316
8279
 
7317
8280
  //#endregion
@@ -7418,13 +8381,33 @@ var AgentClient = class {
7418
8381
  throw error;
7419
8382
  }
7420
8383
  },
7421
- move: async (sessionId) => {
7422
- this.logger?.debug("AgentClient.sessions.move called", { sessionId });
8384
+ updateStatus: async (sessionId, status) => {
8385
+ this.logger?.debug("AgentClient.sessions.updateStatus called", {
8386
+ sessionId,
8387
+ status
8388
+ });
7423
8389
  try {
7424
- if (this.provider.move) {
7425
- const result = await this.provider.move(sessionId);
7426
- this.logger?.info("Session moved successfully", { sessionId });
7427
- return result;
8390
+ if (this.provider.updateStatus) {
8391
+ const result = await this.provider.updateStatus(sessionId, status);
8392
+ this.logger?.info("Session status updated successfully", {
8393
+ sessionId,
8394
+ status
8395
+ });
8396
+ return result;
8397
+ }
8398
+ throw new Error("Provider does not support updateStatus method");
8399
+ } catch (error) {
8400
+ this.logger?.error("Failed to update session status", error);
8401
+ throw error;
8402
+ }
8403
+ },
8404
+ move: async (sessionId) => {
8405
+ this.logger?.debug("AgentClient.sessions.move called", { sessionId });
8406
+ try {
8407
+ if (this.provider.move) {
8408
+ const result = await this.provider.move(sessionId);
8409
+ this.logger?.info("Session moved successfully", { sessionId });
8410
+ return result;
7428
8411
  }
7429
8412
  throw new Error("Provider does not support move method");
7430
8413
  } catch (error) {
@@ -7465,6 +8448,100 @@ var AgentClient = class {
7465
8448
  return [];
7466
8449
  }
7467
8450
  },
8451
+ getAutomationSnapshot: async () => {
8452
+ try {
8453
+ if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
8454
+ this.logger?.warn("Provider does not support getAutomationSnapshot");
8455
+ } catch (error) {
8456
+ this.logger?.error("Failed to get automation snapshot", error);
8457
+ }
8458
+ return {
8459
+ automations: [],
8460
+ inbox: [],
8461
+ runtimeState: {},
8462
+ updatedAt: Date.now()
8463
+ };
8464
+ },
8465
+ updateAutomation: async (payload) => {
8466
+ try {
8467
+ if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
8468
+ this.logger?.warn("Provider does not support updateAutomation");
8469
+ } catch (error) {
8470
+ this.logger?.error("Failed to update automation", error);
8471
+ return {
8472
+ success: false,
8473
+ message: error instanceof Error ? error.message : "Unknown error"
8474
+ };
8475
+ }
8476
+ return {
8477
+ success: false,
8478
+ message: "Provider does not support updateAutomation"
8479
+ };
8480
+ },
8481
+ deleteAutomation: async (id) => {
8482
+ try {
8483
+ if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
8484
+ this.logger?.warn("Provider does not support deleteAutomation");
8485
+ } catch (error) {
8486
+ this.logger?.error("Failed to delete automation", error);
8487
+ return {
8488
+ success: false,
8489
+ message: error instanceof Error ? error.message : "Unknown error"
8490
+ };
8491
+ }
8492
+ return {
8493
+ success: false,
8494
+ message: "Provider does not support deleteAutomation"
8495
+ };
8496
+ },
8497
+ archiveAutomationInboxItem: async (itemId) => {
8498
+ try {
8499
+ if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
8500
+ this.logger?.warn("Provider does not support archiveAutomationInboxItem");
8501
+ } catch (error) {
8502
+ this.logger?.error("Failed to archive automation inbox item", error);
8503
+ return {
8504
+ success: false,
8505
+ message: error instanceof Error ? error.message : "Unknown error"
8506
+ };
8507
+ }
8508
+ return {
8509
+ success: false,
8510
+ message: "Provider does not support archiveAutomationInboxItem"
8511
+ };
8512
+ },
8513
+ deleteAutomationInboxItem: async (itemId) => {
8514
+ try {
8515
+ if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
8516
+ this.logger?.warn("Provider does not support deleteAutomationInboxItem");
8517
+ } catch (error) {
8518
+ this.logger?.error("Failed to delete automation inbox item", error);
8519
+ return {
8520
+ success: false,
8521
+ message: error instanceof Error ? error.message : "Unknown error"
8522
+ };
8523
+ }
8524
+ return {
8525
+ success: false,
8526
+ message: "Provider does not support deleteAutomationInboxItem"
8527
+ };
8528
+ },
8529
+ testAutomation: async (id) => {
8530
+ try {
8531
+ if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
8532
+ this.logger?.warn("Provider does not support testAutomation");
8533
+ } catch (error) {
8534
+ this.logger?.error("Failed to test automation", error);
8535
+ return {
8536
+ success: false,
8537
+ message: error instanceof Error ? error.message : "Unknown error"
8538
+ };
8539
+ }
8540
+ return {
8541
+ success: false,
8542
+ message: "Provider does not support testAutomation"
8543
+ };
8544
+ },
7468
8545
  on: (event, handler) => {
7469
8546
  if (this.provider.on) this.provider.on(event, handler);
7470
8547
  else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
@@ -7584,6 +8661,189 @@ var AgentClient = class {
7584
8661
  };
7585
8662
  }
7586
8663
  },
8664
+ getSubagentList: async (params) => {
8665
+ try {
8666
+ if (this.provider && this.provider.getSubagentList) {
8667
+ const result = await this.provider.getSubagentList(params);
8668
+ this.logger?.info("Subagent list retrieved", {
8669
+ resultCount: result.results.length,
8670
+ hasError: !!result.error
8671
+ });
8672
+ return result;
8673
+ }
8674
+ return {
8675
+ results: [],
8676
+ error: "Provider does not support getSubagentList"
8677
+ };
8678
+ } catch (error) {
8679
+ this.logger?.error("Failed to get subagent list", error);
8680
+ return {
8681
+ results: [],
8682
+ error: error instanceof Error ? error.message : "Unknown error"
8683
+ };
8684
+ }
8685
+ },
8686
+ getSkillList: async (params) => {
8687
+ try {
8688
+ if (this.provider && this.provider.getSkillList) {
8689
+ const result = await this.provider.getSkillList(params);
8690
+ this.logger?.info("Skill list retrieved", {
8691
+ resultCount: result.results.length,
8692
+ hasError: !!result.error
8693
+ });
8694
+ return result;
8695
+ }
8696
+ return {
8697
+ results: [],
8698
+ error: "Provider does not support getSkillList"
8699
+ };
8700
+ } catch (error) {
8701
+ this.logger?.error("Failed to get skill list", error);
8702
+ return {
8703
+ results: [],
8704
+ error: error instanceof Error ? error.message : "Unknown error"
8705
+ };
8706
+ }
8707
+ },
8708
+ importSkill: async (params) => {
8709
+ try {
8710
+ if (this.provider && this.provider.importSkill) {
8711
+ const result = await this.provider.importSkill(params);
8712
+ this.logger?.info("Import skill completed", {
8713
+ success: result.success,
8714
+ hasError: !!result.error
8715
+ });
8716
+ return result;
8717
+ }
8718
+ return {
8719
+ success: false,
8720
+ error: "Provider does not support importSkill"
8721
+ };
8722
+ } catch (error) {
8723
+ this.logger?.error("Failed to import skill", error);
8724
+ return {
8725
+ success: false,
8726
+ error: error instanceof Error ? error.message : "Unknown error"
8727
+ };
8728
+ }
8729
+ },
8730
+ toggleSkill: async (params) => {
8731
+ try {
8732
+ if (this.provider && this.provider.toggleSkill) {
8733
+ const result = await this.provider.toggleSkill(params);
8734
+ this.logger?.info("Toggle skill completed", {
8735
+ success: result.success,
8736
+ hasError: !!result.error
8737
+ });
8738
+ return result;
8739
+ }
8740
+ return {
8741
+ success: false,
8742
+ error: "Provider does not support toggleSkill"
8743
+ };
8744
+ } catch (error) {
8745
+ this.logger?.error("Failed to toggle skill", error);
8746
+ return {
8747
+ success: false,
8748
+ error: error instanceof Error ? error.message : "Unknown error"
8749
+ };
8750
+ }
8751
+ },
8752
+ deleteSkill: async (params) => {
8753
+ try {
8754
+ if (this.provider && this.provider.deleteSkill) {
8755
+ const result = await this.provider.deleteSkill(params);
8756
+ this.logger?.info("Delete skill completed", {
8757
+ success: result.success,
8758
+ hasError: !!result.error
8759
+ });
8760
+ return result;
8761
+ }
8762
+ return {
8763
+ success: false,
8764
+ error: "Provider does not support deleteSkill"
8765
+ };
8766
+ } catch (error) {
8767
+ this.logger?.error("Failed to delete skill", error);
8768
+ return {
8769
+ success: false,
8770
+ error: error instanceof Error ? error.message : "Unknown error"
8771
+ };
8772
+ }
8773
+ },
8774
+ getSkillContent: async (params) => {
8775
+ try {
8776
+ if (this.provider && this.provider.getSkillContent) {
8777
+ const result = await this.provider.getSkillContent(params);
8778
+ this.logger?.info("Get skill content completed", { hasError: !!result.error });
8779
+ return result;
8780
+ }
8781
+ return {
8782
+ content: "",
8783
+ error: "Provider does not support getSkillContent"
8784
+ };
8785
+ } catch (error) {
8786
+ this.logger?.error("Failed to get skill content", error);
8787
+ return {
8788
+ content: "",
8789
+ error: error instanceof Error ? error.message : "Unknown error"
8790
+ };
8791
+ }
8792
+ },
8793
+ getMarketplaceSkills: async () => {
8794
+ try {
8795
+ if (this.provider && this.provider.getMarketplaceSkills) {
8796
+ const result = await this.provider.getMarketplaceSkills();
8797
+ this.logger?.info("Marketplace skills retrieved", {
8798
+ resultCount: result.results.length,
8799
+ hasError: !!result.error
8800
+ });
8801
+ return result;
8802
+ }
8803
+ return {
8804
+ results: [],
8805
+ error: "Provider does not support getMarketplaceSkills"
8806
+ };
8807
+ } catch (error) {
8808
+ this.logger?.error("Failed to get marketplace skills", error);
8809
+ return {
8810
+ results: [],
8811
+ error: error instanceof Error ? error.message : "Unknown error"
8812
+ };
8813
+ }
8814
+ },
8815
+ getMarketplaceSkillContent: async (params) => {
8816
+ try {
8817
+ if (this.provider && this.provider.getMarketplaceSkillContent) return await this.provider.getMarketplaceSkillContent(params);
8818
+ return {
8819
+ content: "",
8820
+ error: "Provider does not support getMarketplaceSkillContent"
8821
+ };
8822
+ } catch (error) {
8823
+ this.logger?.error("Failed to get marketplace skill content", error);
8824
+ return {
8825
+ content: "",
8826
+ error: error instanceof Error ? error.message : "Unknown error"
8827
+ };
8828
+ }
8829
+ },
8830
+ installMarketplaceSkill: async (params) => {
8831
+ try {
8832
+ if (this.provider && this.provider.installMarketplaceSkill) return await this.provider.installMarketplaceSkill(params);
8833
+ return {
8834
+ success: false,
8835
+ skillName: params.skillName,
8836
+ errorMessage: "Provider does not support installMarketplaceSkill"
8837
+ };
8838
+ } catch (error) {
8839
+ this.logger?.error("Failed to install marketplace skill", error);
8840
+ return {
8841
+ success: false,
8842
+ skillName: params.skillName,
8843
+ errorMessage: error instanceof Error ? error.message : "Unknown error"
8844
+ };
8845
+ }
8846
+ },
7587
8847
  batchTogglePlugins: async (request) => {
7588
8848
  try {
7589
8849
  if (this.provider && this.provider.batchTogglePlugins) {
@@ -7628,37 +8888,229 @@ var AgentClient = class {
7628
8888
  return [];
7629
8889
  }
7630
8890
  },
7631
- installPlugins: async (pluginNames, marketplaceName, installScope) => {
8891
+ installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
8892
+ try {
8893
+ if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
8894
+ const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
8895
+ this.logger?.info("Install plugins", {
8896
+ pluginNames,
8897
+ marketplaceName,
8898
+ success: result.success
8899
+ });
8900
+ return result;
8901
+ }
8902
+ this.logger?.warn("Provider does not support installPlugins");
8903
+ return {
8904
+ success: false,
8905
+ error: "Provider does not support installPlugins"
8906
+ };
8907
+ } catch (error) {
8908
+ this.logger?.error("Failed to install plugins", error);
8909
+ return {
8910
+ success: false,
8911
+ error: error instanceof Error ? error.message : "Unknown error"
8912
+ };
8913
+ }
8914
+ },
8915
+ uninstallPlugin: async (pluginName, marketplaceName, scope) => {
8916
+ try {
8917
+ if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
8918
+ const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
8919
+ this.logger?.info("Uninstall plugin", {
8920
+ pluginName,
8921
+ marketplaceName,
8922
+ scope,
8923
+ success: result.success
8924
+ });
8925
+ return result;
8926
+ }
8927
+ this.logger?.warn("Provider does not support uninstallPlugin");
8928
+ return {
8929
+ success: false,
8930
+ error: "Provider does not support uninstallPlugin"
8931
+ };
8932
+ } catch (error) {
8933
+ this.logger?.error("Failed to uninstall plugin", error);
8934
+ return {
8935
+ success: false,
8936
+ error: error instanceof Error ? error.message : "Unknown error"
8937
+ };
8938
+ }
8939
+ },
8940
+ updatePlugin: async (pluginName, marketplaceName) => {
8941
+ try {
8942
+ if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
8943
+ const result = await this.provider.updatePlugin(pluginName, marketplaceName);
8944
+ this.logger?.info("Update plugin", {
8945
+ pluginName,
8946
+ marketplaceName,
8947
+ success: result.success
8948
+ });
8949
+ return result;
8950
+ }
8951
+ this.logger?.warn("Provider does not support updatePlugin");
8952
+ return {
8953
+ success: false,
8954
+ error: "Provider does not support updatePlugin"
8955
+ };
8956
+ } catch (error) {
8957
+ this.logger?.error("Failed to update plugin", error);
8958
+ return {
8959
+ success: false,
8960
+ error: error instanceof Error ? error.message : "Unknown error"
8961
+ };
8962
+ }
8963
+ },
8964
+ getPluginMarketplaces: async (forceRefresh) => {
8965
+ try {
8966
+ if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
8967
+ const result = await this.provider.getPluginMarketplaces(forceRefresh);
8968
+ this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
8969
+ return result;
8970
+ }
8971
+ this.logger?.warn("Provider does not support getPluginMarketplaces");
8972
+ return [];
8973
+ } catch (error) {
8974
+ this.logger?.error("Failed to get plugin marketplaces", error);
8975
+ return [];
8976
+ }
8977
+ },
8978
+ getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
8979
+ try {
8980
+ if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
8981
+ const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
8982
+ this.logger?.info("Got marketplace plugins", {
8983
+ marketplaceName,
8984
+ count: result?.length ?? 0
8985
+ });
8986
+ return result;
8987
+ }
8988
+ this.logger?.warn("Provider does not support getMarketplacePlugins");
8989
+ return [];
8990
+ } catch (error) {
8991
+ this.logger?.error("Failed to get marketplace plugins", error);
8992
+ return [];
8993
+ }
8994
+ },
8995
+ getPluginDetail: async (pluginName, marketplaceName) => {
8996
+ try {
8997
+ if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
8998
+ const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
8999
+ this.logger?.info("Got plugin detail", {
9000
+ pluginName,
9001
+ marketplaceName
9002
+ });
9003
+ return result;
9004
+ }
9005
+ this.logger?.warn("Provider does not support getPluginDetail");
9006
+ return null;
9007
+ } catch (error) {
9008
+ this.logger?.error("Failed to get plugin detail", error);
9009
+ return null;
9010
+ }
9011
+ },
9012
+ addPluginMarketplace: async (source, name) => {
9013
+ try {
9014
+ if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
9015
+ const result = await this.provider.addPluginMarketplace(source, name);
9016
+ this.logger?.info("Add plugin marketplace", {
9017
+ source,
9018
+ name,
9019
+ success: result.success
9020
+ });
9021
+ return result;
9022
+ }
9023
+ this.logger?.warn("Provider does not support addPluginMarketplace");
9024
+ return {
9025
+ success: false,
9026
+ error: "Provider does not support addPluginMarketplace"
9027
+ };
9028
+ } catch (error) {
9029
+ this.logger?.error("Failed to add plugin marketplace", error);
9030
+ return {
9031
+ success: false,
9032
+ error: error instanceof Error ? error.message : "Unknown error"
9033
+ };
9034
+ }
9035
+ },
9036
+ removePluginMarketplace: async (marketplaceName) => {
9037
+ try {
9038
+ if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
9039
+ const result = await this.provider.removePluginMarketplace(marketplaceName);
9040
+ this.logger?.info("Remove plugin marketplace", {
9041
+ marketplaceName,
9042
+ success: result.success
9043
+ });
9044
+ return result;
9045
+ }
9046
+ this.logger?.warn("Provider does not support removePluginMarketplace");
9047
+ return {
9048
+ success: false,
9049
+ error: "Provider does not support removePluginMarketplace"
9050
+ };
9051
+ } catch (error) {
9052
+ this.logger?.error("Failed to remove plugin marketplace", error);
9053
+ return {
9054
+ success: false,
9055
+ error: error instanceof Error ? error.message : "Unknown error"
9056
+ };
9057
+ }
9058
+ },
9059
+ refreshPluginMarketplace: async (marketplaceName) => {
7632
9060
  try {
7633
- if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
7634
- const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
7635
- this.logger?.info("Install plugins", {
7636
- pluginNames,
9061
+ if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
9062
+ const result = await this.provider.refreshPluginMarketplace(marketplaceName);
9063
+ this.logger?.info("Refresh plugin marketplace", {
7637
9064
  marketplaceName,
7638
9065
  success: result.success
7639
9066
  });
7640
9067
  return result;
7641
9068
  }
7642
- this.logger?.warn("Provider does not support installPlugins");
9069
+ this.logger?.warn("Provider does not support refreshPluginMarketplace");
7643
9070
  return {
7644
9071
  success: false,
7645
- error: "Provider does not support installPlugins"
9072
+ error: "Provider does not support refreshPluginMarketplace"
7646
9073
  };
7647
9074
  } catch (error) {
7648
- this.logger?.error("Failed to install plugins", error);
9075
+ this.logger?.error("Failed to refresh plugin marketplace", error);
7649
9076
  return {
7650
9077
  success: false,
7651
9078
  error: error instanceof Error ? error.message : "Unknown error"
7652
9079
  };
7653
9080
  }
7654
9081
  },
7655
- getSupportScenes: async () => {
9082
+ openFolderInNewWindow: async (folderPath) => {
9083
+ try {
9084
+ if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
9085
+ await this.provider.openFolderInNewWindow(folderPath);
9086
+ this.logger?.info("Opened folder in new window", { folderPath });
9087
+ } else {
9088
+ this.logger?.warn("Provider does not support openFolderInNewWindow");
9089
+ throw new Error("Provider does not support openFolderInNewWindow");
9090
+ }
9091
+ } catch (error) {
9092
+ this.logger?.error("Failed to open folder in new window", error);
9093
+ throw error;
9094
+ }
9095
+ },
9096
+ openFolder: async (folderPath) => {
7656
9097
  try {
7657
- if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") {
7658
- const result = await this.provider.getSupportScenes();
7659
- this.logger?.info("Got support scenes", { count: result?.length ?? 0 });
9098
+ if (this.provider && "openFolder" in this.provider && typeof this.provider.openFolder === "function") {
9099
+ const result = await this.provider.openFolder(folderPath);
9100
+ this.logger?.info("Opened folder in system file manager", { folderPath });
7660
9101
  return result;
9102
+ } else {
9103
+ this.logger?.warn("Provider does not support openFolder");
9104
+ throw new Error("Provider does not support openFolder");
7661
9105
  }
9106
+ } catch (error) {
9107
+ this.logger?.error("Failed to open folder", error);
9108
+ throw error;
9109
+ }
9110
+ },
9111
+ getSupportScenes: async (locale) => {
9112
+ try {
9113
+ if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
7662
9114
  this.logger?.warn("Provider does not support getSupportScenes");
7663
9115
  return [];
7664
9116
  } catch (error) {
@@ -7666,6 +9118,193 @@ var AgentClient = class {
7666
9118
  return [];
7667
9119
  }
7668
9120
  },
9121
+ getProductScenes: async (locale) => {
9122
+ try {
9123
+ if (this.provider?.getProductScenes) {
9124
+ const result = await this.provider.getProductScenes(locale);
9125
+ this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
9126
+ return result;
9127
+ }
9128
+ this.logger?.warn("Provider does not support getProductScenes");
9129
+ return [];
9130
+ } catch (error) {
9131
+ this.logger?.error("Failed to get product scenes", error);
9132
+ return [];
9133
+ }
9134
+ },
9135
+ getAvailableCommands: async (params) => {
9136
+ try {
9137
+ if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
9138
+ const result = await this.provider.getAvailableCommands(params);
9139
+ this.logger?.info("Got available commands from provider", {
9140
+ sessionId: params?.sessionId ?? "(default)",
9141
+ count: result?.length ?? 0
9142
+ });
9143
+ return result;
9144
+ }
9145
+ this.logger?.warn("Provider does not support getAvailableCommands", { params });
9146
+ return [];
9147
+ } catch (error) {
9148
+ this.logger?.error("Failed to get available commands", error);
9149
+ return [];
9150
+ }
9151
+ },
9152
+ reportTelemetry: async (eventName, payload) => {
9153
+ try {
9154
+ if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
9155
+ else this.logger?.warn("Provider does not support reportTelemetry");
9156
+ } catch (error) {
9157
+ this.logger?.error("Failed to report telemetry", error);
9158
+ }
9159
+ },
9160
+ getProductConfiguration: async () => {
9161
+ try {
9162
+ if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
9163
+ this.logger?.warn("Provider does not support getProductConfiguration");
9164
+ return {};
9165
+ } catch (error) {
9166
+ this.logger?.error("Failed to get product configuration", error);
9167
+ return {};
9168
+ }
9169
+ },
9170
+ getUserInfo: async () => {
9171
+ this.logger?.info("[AgentClient.sessions] getUserInfo() called");
9172
+ try {
9173
+ if (this.provider?.getUserInfo) {
9174
+ const result = await this.provider.getUserInfo();
9175
+ this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
9176
+ return result;
9177
+ }
9178
+ this.logger?.warn("Provider does not support getUserInfo");
9179
+ return {};
9180
+ } catch (error) {
9181
+ this.logger?.error("Failed to get user info", error);
9182
+ return {};
9183
+ }
9184
+ },
9185
+ respondToSampling: async (sessionId, response) => {
9186
+ try {
9187
+ if (this.provider?.respondToSampling) {
9188
+ await this.provider.respondToSampling(sessionId, response);
9189
+ this.logger?.info("Responded to sampling request", {
9190
+ sessionId,
9191
+ requestId: response.id,
9192
+ approved: response.approved
9193
+ });
9194
+ } else this.logger?.warn("Provider does not support respondToSampling");
9195
+ } catch (error) {
9196
+ this.logger?.error("Failed to respond to sampling request", error);
9197
+ throw error;
9198
+ }
9199
+ },
9200
+ respondToRoots: async (sessionId, response) => {
9201
+ try {
9202
+ if (this.provider?.respondToRoots) {
9203
+ await this.provider.respondToRoots(sessionId, response);
9204
+ this.logger?.info("Responded to roots request", {
9205
+ sessionId,
9206
+ requestId: response.id,
9207
+ approved: response.approved
9208
+ });
9209
+ } else this.logger?.warn("Provider does not support respondToRoots");
9210
+ } catch (error) {
9211
+ this.logger?.error("Failed to respond to roots request", error);
9212
+ throw error;
9213
+ }
9214
+ },
9215
+ subscribeSamplingRequests: (serverName, callback) => {
9216
+ if (this.provider?.subscribeSamplingRequests) {
9217
+ this.logger?.info("Subscribing to sampling requests", { serverName });
9218
+ return this.provider.subscribeSamplingRequests(serverName, callback);
9219
+ }
9220
+ this.logger?.warn("Provider does not support subscribeSamplingRequests");
9221
+ return () => {};
9222
+ },
9223
+ subscribeRootsRequests: (serverName, callback) => {
9224
+ if (this.provider?.subscribeRootsRequests) {
9225
+ this.logger?.info("Subscribing to roots requests", { serverName });
9226
+ return this.provider.subscribeRootsRequests(serverName, callback);
9227
+ }
9228
+ this.logger?.warn("Provider does not support subscribeRootsRequests");
9229
+ return () => {};
9230
+ },
9231
+ getMcpServers: async () => {
9232
+ if (this.provider?.getMcpServers) {
9233
+ this.logger?.info("Getting MCP servers list");
9234
+ return this.provider.getMcpServers();
9235
+ }
9236
+ this.logger?.warn("Provider does not support getMcpServers");
9237
+ return [];
9238
+ },
9239
+ toggleMcpServer: async (serverName, enabled) => {
9240
+ if (this.provider?.toggleMcpServer) {
9241
+ this.logger?.info("Toggling MCP server", {
9242
+ serverName,
9243
+ enabled
9244
+ });
9245
+ await this.provider.toggleMcpServer(serverName, enabled);
9246
+ } else {
9247
+ this.logger?.warn("Provider does not support toggleMcpServer");
9248
+ throw new Error("toggleMcpServer not supported by provider");
9249
+ }
9250
+ },
9251
+ reconnectMcpServer: async (serverName, forceHttpCallback) => {
9252
+ if (this.provider?.reconnectMcpServer) {
9253
+ this.logger?.info("Reconnecting MCP server", {
9254
+ serverName,
9255
+ forceHttpCallback
9256
+ });
9257
+ await this.provider.reconnectMcpServer(serverName, forceHttpCallback);
9258
+ } else {
9259
+ this.logger?.warn("Provider does not support reconnectMcpServer");
9260
+ throw new Error("reconnectMcpServer not supported by provider");
9261
+ }
9262
+ },
9263
+ deleteMcpServer: async (serverName) => {
9264
+ if (this.provider?.deleteMcpServer) {
9265
+ this.logger?.info("Deleting MCP server", { serverName });
9266
+ await this.provider.deleteMcpServer(serverName);
9267
+ } else {
9268
+ this.logger?.warn("Provider does not support deleteMcpServer");
9269
+ throw new Error("deleteMcpServer not supported by provider");
9270
+ }
9271
+ },
9272
+ openMcpConfig: async () => {
9273
+ if (this.provider?.openMcpConfig) {
9274
+ this.logger?.info("Opening MCP config");
9275
+ await this.provider.openMcpConfig();
9276
+ } else {
9277
+ this.logger?.warn("Provider does not support openMcpConfig");
9278
+ throw new Error("openMcpConfig not supported by provider");
9279
+ }
9280
+ },
9281
+ getMcpConfigContent: async () => {
9282
+ if (this.provider?.getMcpConfigContent) {
9283
+ this.logger?.info("Getting MCP config content");
9284
+ return await this.provider.getMcpConfigContent();
9285
+ } else {
9286
+ this.logger?.warn("Provider does not support getMcpConfigContent");
9287
+ throw new Error("getMcpConfigContent not supported by provider");
9288
+ }
9289
+ },
9290
+ saveMcpConfigContent: async (content) => {
9291
+ if (this.provider?.saveMcpConfigContent) {
9292
+ this.logger?.info("Saving MCP config content");
9293
+ await this.provider.saveMcpConfigContent(content);
9294
+ } else {
9295
+ this.logger?.warn("Provider does not support saveMcpConfigContent");
9296
+ throw new Error("saveMcpConfigContent not supported by provider");
9297
+ }
9298
+ },
9299
+ clipboardReadText: async () => {
9300
+ if (this.provider?.clipboardReadText) {
9301
+ this.logger?.info("Reading clipboard text");
9302
+ return await this.provider.clipboardReadText();
9303
+ } else {
9304
+ this.logger?.warn("Provider does not support clipboardReadText");
9305
+ throw new Error("clipboardReadText not supported by provider");
9306
+ }
9307
+ },
7669
9308
  models: this.createModelsResource()
7670
9309
  };
7671
9310
  }
@@ -7732,6 +9371,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
7732
9371
  return AccountStatus;
7733
9372
  }({});
7734
9373
 
9374
+ //#endregion
9375
+ //#region ../agent-provider/src/backend/service/oauth-repository-service.ts
9376
+ /**
9377
+ * OAuth Repository Service
9378
+ *
9379
+ * 封装 OAuth 连接器相关的仓库和分支操作
9380
+ */
9381
+ /**
9382
+ * OAuth Repository Service
9383
+ *
9384
+ * 提供仓库和分支的查询操作
9385
+ */
9386
+ var OAuthRepositoryService = class {
9387
+ /**
9388
+ * 获取仓库分支列表
9389
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
9390
+ *
9391
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
9392
+ * @param params 平台特定的查询参数
9393
+ * @param page 页码,从1开始,0表示不分页获取全部
9394
+ * @param perPage 每页数量,最大100
9395
+ * @returns Promise<OauthBranch[]> 分支列表
9396
+ *
9397
+ * @example
9398
+ * ```typescript
9399
+ * // GitHub
9400
+ * const branches = await service.getBranches('github', {
9401
+ * owner: 'CodeBuddy-Official-Account',
9402
+ * repo: 'CodeBuddyIDE'
9403
+ * });
9404
+ *
9405
+ * // Gongfeng
9406
+ * const branches = await service.getBranches('gongfeng', {
9407
+ * project_id: '1611499'
9408
+ * });
9409
+ *
9410
+ * // CNB
9411
+ * const branches = await service.getBranches('cnb', {
9412
+ * repo: 'genie/genie-ide'
9413
+ * });
9414
+ * ```
9415
+ */
9416
+ async getBranches(connector, params, page = 0, perPage = 100) {
9417
+ try {
9418
+ const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
9419
+ console.log(`[OAuthRepositoryService] GET ${url}`);
9420
+ const apiResponse = await httpService.get(url);
9421
+ if (!apiResponse.data) {
9422
+ console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
9423
+ return [];
9424
+ }
9425
+ const branches = apiResponse.data.branches || [];
9426
+ console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
9427
+ return branches;
9428
+ } catch (error) {
9429
+ console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
9430
+ throw error;
9431
+ }
9432
+ }
9433
+ /**
9434
+ * 获取仓库列表
9435
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
9436
+ *
9437
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
9438
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
9439
+ *
9440
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
9441
+ * @param page 页码,从1开始,0表示不分页获取全部
9442
+ * - GitHub 只支持全量数据,必须传 0
9443
+ * - 工蜂和 CNB 依据前端逻辑而定
9444
+ * @param perPage 每页数量,最大100
9445
+ * @returns Promise<ListReposResponse> 仓库列表响应
9446
+ *
9447
+ * @example
9448
+ * ```typescript
9449
+ * // GitHub - 必须传 page=0 获取全量数据
9450
+ * const response = await service.getRepositories('github', 0, 100);
9451
+ * // response.github_repos 是 map: installation_id => repo[]
9452
+ *
9453
+ * // Gongfeng
9454
+ * const response = await service.getRepositories('gongfeng', 0, 100);
9455
+ * // response.gongfeng_repos 是数组
9456
+ *
9457
+ * // CNB
9458
+ * const response = await service.getRepositories('cnb', 0, 100);
9459
+ * // response.cnb_repos 是数组
9460
+ * ```
9461
+ */
9462
+ async getRepositories(connector, page = 0, perPage = 100) {
9463
+ try {
9464
+ const queryParams = new URLSearchParams();
9465
+ queryParams.append("page", String(page));
9466
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
9467
+ const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
9468
+ console.log(`[OAuthRepositoryService] GET ${url}`);
9469
+ const apiResponse = await httpService.get(url);
9470
+ if (!apiResponse.data) {
9471
+ console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
9472
+ return {};
9473
+ }
9474
+ const response = apiResponse.data;
9475
+ this.logRepositoryCounts(response);
9476
+ return response;
9477
+ } catch (error) {
9478
+ console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
9479
+ throw error;
9480
+ }
9481
+ }
9482
+ /**
9483
+ * 构建分支查询参数
9484
+ */
9485
+ buildBranchQueryParams(connector, params, page, perPage) {
9486
+ const queryParams = new URLSearchParams();
9487
+ queryParams.append("page", String(page));
9488
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
9489
+ if (connector === "github") {
9490
+ const githubParams = params;
9491
+ if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
9492
+ queryParams.append("owner", githubParams.owner);
9493
+ queryParams.append("repo", githubParams.repo);
9494
+ } else if (connector === "gongfeng") {
9495
+ const gongfengParams = params;
9496
+ if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
9497
+ queryParams.append("project_id", gongfengParams.project_id);
9498
+ } else if (connector === "cnb") {
9499
+ const cnbParams = params;
9500
+ if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
9501
+ queryParams.append("repo", cnbParams.repo);
9502
+ } else throw new Error(`Unknown connector: ${connector}`);
9503
+ return queryParams;
9504
+ }
9505
+ /**
9506
+ * 记录仓库数量日志
9507
+ */
9508
+ logRepositoryCounts(response) {
9509
+ if (response.github_repos) {
9510
+ const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
9511
+ console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
9512
+ }
9513
+ if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
9514
+ if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
9515
+ }
9516
+ };
9517
+ /**
9518
+ * OAuth Repository Service 单例实例
9519
+ */
9520
+ const oauthRepositoryService = new OAuthRepositoryService();
9521
+
7735
9522
  //#endregion
7736
9523
  //#region ../agent-provider/src/backend/backend-provider.ts
7737
9524
  /**
@@ -7740,28 +9527,17 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
7740
9527
  * 封装与后端 API 的 HTTP 通信
7741
9528
  */
7742
9529
  /**
7743
- * 判断当前是否在 SSO 域名下
7744
- * SSO 域名格式: xxx.sso.copilot.tencent.com xxx.sso.codebuddy.cn
9530
+ * 判断当前账号是否是 SSO 账号
9531
+ * 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
7745
9532
  */
7746
- const isSSODomain = () => {
7747
- const { hostname } = window.location;
7748
- return hostname.includes(".sso.copilot") || hostname.includes("sso.codebuddy.cn") || hostname.includes(".sso.copilot-staging") || hostname.includes(".staging-sso.codebuddy.cn");
7749
- };
7750
9533
  /**
7751
- * 获取登录页面 URL
7752
- * - SSO 域名下需要跳转到对应的预发/生产域名
7753
- * - 非 SSO 域名直接使用当前域名
9534
+ * 根据路径获取完整 URL
9535
+ * - SSO 账号需要跳转到对应的预发/生产域名
9536
+ * - 非 SSO 账号直接使用当前域名
9537
+ * @param path 路径,如 '/login'、'/logout'、'/home' 等
9538
+ * @returns 完整的 URL 地址
7754
9539
  */
7755
- const getLoginUrl = () => {
7756
- const { hostname, protocol } = window.location;
7757
- if (isSSODomain()) {
7758
- const isCodebuddy = hostname.includes("codebuddy.cn");
7759
- const isStaging = hostname.includes("staging");
7760
- if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
7761
- else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
7762
- }
7763
- return `${window.location.origin}/login`;
7764
- };
9540
+ const getFullUrl = (path) => `${window.location.origin}${path}`;
7765
9541
  /** 获取当前域名的账号选择页面 URL */
7766
9542
  const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
7767
9543
  /** localStorage 中存储选中账号 ID 的 key */
@@ -7798,12 +9574,30 @@ var BackendProvider = class {
7798
9574
  constructor(config) {
7799
9575
  httpService.setBaseURL(config.baseUrl);
7800
9576
  if (config.authToken) httpService.setAuthToken(config.authToken);
7801
- httpService.onUnauthorized(() => {
7802
- console.log("[BackendProvider] User unauthorized (401/403), triggering logout");
7803
- this.logout().catch((error) => {
7804
- console.error("[BackendProvider] Logout failed in 401 handler:", error);
9577
+ httpService.onUnauthorized(() => this.handleUnauthorized());
9578
+ }
9579
+ /**
9580
+ * 处理 401 未授权错误
9581
+ * 先尝试刷新 token,失败后再执行登出流程
9582
+ *
9583
+ * @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
9584
+ */
9585
+ async handleUnauthorized() {
9586
+ console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
9587
+ try {
9588
+ if (await this.refreshToken()) {
9589
+ console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
9590
+ return;
9591
+ }
9592
+ throw new Error("Token refresh returned null");
9593
+ } catch (error) {
9594
+ console.error("[BackendProvider] Token refresh failed after 401:", error);
9595
+ console.log("[BackendProvider] Token refresh failed, triggering logout");
9596
+ this.logout().catch((logoutError) => {
9597
+ console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
7805
9598
  });
7806
- });
9599
+ throw error;
9600
+ }
7807
9601
  }
7808
9602
  /**
7809
9603
  * 获取当前账号信息
@@ -7848,7 +9642,7 @@ var BackendProvider = class {
7848
9642
  return account;
7849
9643
  }
7850
9644
  const redirectUrl = encodeURIComponent(window.location.href);
7851
- window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
9645
+ window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
7852
9646
  accountService.setAccount(null);
7853
9647
  return null;
7854
9648
  } catch (error) {
@@ -7871,7 +9665,8 @@ var BackendProvider = class {
7871
9665
  activeStatus: connector.active_status,
7872
9666
  displayName: connector.display_name,
7873
9667
  oauthClientId: connector.oauth_client_id,
7874
- oauthRedirectUrl: connector.oauth_redirect_url
9668
+ oauthRedirectUrl: connector.oauth_redirect_url,
9669
+ oauthAppName: connector.oauth_app_name
7875
9670
  })) };
7876
9671
  }
7877
9672
  throw result;
@@ -7953,7 +9748,8 @@ var BackendProvider = class {
7953
9748
  connectStatus: connector.connect_status,
7954
9749
  displayName: connector.display_name,
7955
9750
  oauthClientId: connector.oauth_client_id,
7956
- oauthRedirectUrl: connector.oauth_redirect_url
9751
+ oauthRedirectUrl: connector.oauth_redirect_url,
9752
+ oauthAppName: connector.oauth_app_name
7957
9753
  })) };
7958
9754
  }
7959
9755
  throw result;
@@ -8065,10 +9861,11 @@ var BackendProvider = class {
8065
9861
  if (!time) return 0;
8066
9862
  return new Date(time).getTime();
8067
9863
  };
8068
- const dailyCredits = [CommodityCode.free, CommodityCode.freeMon];
9864
+ const dailyCredits = [CommodityCode.free];
8069
9865
  const planResources = resources.map((r) => {
8070
9866
  const isDaily = dailyCredits.includes(r.PackageCode);
8071
9867
  const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
9868
+ const refreshAt = parseTime(r.CycleEndTime) + 1e3;
8072
9869
  return {
8073
9870
  id: r.ResourceId,
8074
9871
  name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
@@ -8078,7 +9875,7 @@ var BackendProvider = class {
8078
9875
  used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
8079
9876
  left: Number(r.CycleCapacityRemainPrecise) || 0,
8080
9877
  expireAt: parseTime(endTime),
8081
- refreshAt: isDaily ? void 0 : parseTime(r.CycleEndTime)
9878
+ refreshAt: isDaily ? void 0 : refreshAt
8082
9879
  };
8083
9880
  }).sort((a, b) => {
8084
9881
  const getPriority = (code) => {
@@ -8086,10 +9883,11 @@ var BackendProvider = class {
8086
9883
  CommodityCode.proMon,
8087
9884
  CommodityCode.proMonPlus,
8088
9885
  CommodityCode.proYear,
9886
+ CommodityCode.freeMon,
8089
9887
  CommodityCode.extra
8090
9888
  ].includes(code)) return 1;
8091
9889
  if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
8092
- if ([CommodityCode.free, CommodityCode.freeMon].includes(code)) return 3;
9890
+ if ([CommodityCode.free].includes(code)) return 3;
8093
9891
  return 4;
8094
9892
  };
8095
9893
  return getPriority(a.packageCode) - getPriority(b.packageCode);
@@ -8210,38 +10008,69 @@ var BackendProvider = class {
8210
10008
  */
8211
10009
  async login() {
8212
10010
  const redirectUrl = encodeURIComponent(window.location.href);
8213
- window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
10011
+ window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
8214
10012
  }
8215
10013
  /**
8216
10014
  * 登出账号
8217
- * Web 环境: 通过 iframe 访问登出 URL 清除 cookie
10015
+ *
10016
+ * 策略:
10017
+ * - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
10018
+ * - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
8218
10019
  */
8219
10020
  async logout() {
8220
- const url = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
10021
+ const account = accountService.getAccount();
10022
+ if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
10023
+ else await this.logoutViaHttp();
10024
+ localStorage.removeItem(SELECTED_ACCOUNT_KEY);
10025
+ accountService.clearAccount();
10026
+ }
10027
+ /**
10028
+ * IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
10029
+ * 轮询 iframe URL 变化检测完成,兜底超时 5 秒
10030
+ */
10031
+ async logoutViaIframe() {
10032
+ const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
8221
10033
  try {
8222
10034
  await new Promise((resolve) => {
8223
10035
  const iframe = document.createElement("iframe");
8224
10036
  iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
8225
- iframe.src = url;
8226
- const timeout = setTimeout(() => {
8227
- cleanup();
8228
- resolve();
8229
- }, 5e3);
8230
- const cleanup = () => {
10037
+ iframe.src = logoutUrl;
10038
+ let pollTimer;
10039
+ let settled = false;
10040
+ const done = () => {
10041
+ if (settled) return;
10042
+ settled = true;
10043
+ clearInterval(pollTimer);
8231
10044
  clearTimeout(timeout);
8232
10045
  if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
8233
- };
8234
- iframe.onerror = () => {
8235
- cleanup();
8236
10046
  resolve();
8237
10047
  };
10048
+ let wasRedirecting = false;
10049
+ pollTimer = setInterval(() => {
10050
+ try {
10051
+ const href = iframe.contentWindow?.location?.href;
10052
+ if (wasRedirecting && href) done();
10053
+ } catch {
10054
+ wasRedirecting = true;
10055
+ }
10056
+ }, 100);
10057
+ const timeout = setTimeout(done, 5e3);
10058
+ iframe.onerror = done;
8238
10059
  document.body.appendChild(iframe);
8239
10060
  });
8240
10061
  } catch (error) {
8241
- console.error("[BackendProvider] logout failed:", error);
10062
+ console.error("[BackendProvider] logout via iframe failed:", error);
10063
+ }
10064
+ }
10065
+ /**
10066
+ * 非 IOA 企业登出:直接 HTTP 请求 /console/logout
10067
+ */
10068
+ async logoutViaHttp() {
10069
+ try {
10070
+ await httpService.get("/console/logout");
10071
+ } catch (error) {
10072
+ console.error("[BackendProvider] logout via http failed:", error);
8242
10073
  }
8243
- localStorage.removeItem(SELECTED_ACCOUNT_KEY);
8244
- accountService.clearAccount();
8245
10074
  }
8246
10075
  /**
8247
10076
  * 批量切换插件状态
@@ -8273,6 +10102,101 @@ var BackendProvider = class {
8273
10102
  return null;
8274
10103
  }
8275
10104
  }
10105
+ /**
10106
+ * 刷新 Token
10107
+ * 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
10108
+ * @returns Promise<Account | null> 刷新后的账号信息
10109
+ */
10110
+ async refreshToken() {
10111
+ console.log("[BackendProvider] Refreshing token...");
10112
+ try {
10113
+ const account = await this.getAccount();
10114
+ console.log("[BackendProvider] Token refreshed, account:", account?.uid);
10115
+ return account;
10116
+ } catch (error) {
10117
+ console.error("[BackendProvider] refreshToken failed:", error);
10118
+ return null;
10119
+ }
10120
+ }
10121
+ /**
10122
+ * 获取仓库分支列表
10123
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
10124
+ *
10125
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
10126
+ * @param params 平台特定的查询参数
10127
+ * @param page 页码,从1开始,0表示不分页获取全部
10128
+ * @param perPage 每页数量,最大100
10129
+ * @returns Promise<OauthBranch[]> 分支列表
10130
+ */
10131
+ async getBranches(connector, params, page = 0, perPage = 100) {
10132
+ return oauthRepositoryService.getBranches(connector, params, page, perPage);
10133
+ }
10134
+ /**
10135
+ * 获取仓库列表
10136
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
10137
+ *
10138
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
10139
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
10140
+ *
10141
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
10142
+ * @param page 页码,从1开始,0表示不分页获取全部
10143
+ * - GitHub 只支持全量数据,必须传 0
10144
+ * - 工蜂和 CNB 依据前端逻辑而定
10145
+ * @param perPage 每页数量,最大100
10146
+ * @returns Promise<ListReposResponse> 仓库列表响应
10147
+ */
10148
+ async getRepositories(connector, page = 0, perPage = 100) {
10149
+ return oauthRepositoryService.getRepositories(connector, page, perPage);
10150
+ }
10151
+ /**
10152
+ * 保存待发送的输入内容到后端
10153
+ * API 端点: POST /api/v1/code-id
10154
+ */
10155
+ async savePendingInput(code) {
10156
+ try {
10157
+ const result = await httpService.post("/api/v1/code-id", { code });
10158
+ return result?.codeId || result?.data?.codeId || null;
10159
+ } catch (e) {
10160
+ console.warn("[BackendProvider] savePendingInput failed:", e);
10161
+ return null;
10162
+ }
10163
+ }
10164
+ /**
10165
+ * 从后端加载待发送的输入内容
10166
+ * API 端点: GET /api/v1/code?id=xxx
10167
+ */
10168
+ async loadPendingInput(codeId) {
10169
+ try {
10170
+ const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
10171
+ return result?.code || result?.data?.code || null;
10172
+ } catch (e) {
10173
+ console.warn("[BackendProvider] loadPendingInput failed:", e);
10174
+ return null;
10175
+ }
10176
+ }
10177
+ /**
10178
+ * 获取每日签到状态
10179
+ * API 端点: POST /billing/meter/checkin-status
10180
+ */
10181
+ async getCheckinStatus() {
10182
+ try {
10183
+ const result = await httpService.post("/billing/meter/checkin-status", {});
10184
+ if (result?.code === 0 && result?.data) return result.data;
10185
+ return null;
10186
+ } catch (error) {
10187
+ console.error("[BackendProvider] getCheckinStatus failed:", error);
10188
+ return null;
10189
+ }
10190
+ }
10191
+ /**
10192
+ * 执行每日签到
10193
+ * API 端点: POST /billing/meter/daily-checkin
10194
+ */
10195
+ async claimDailyCheckin() {
10196
+ const result = await httpService.post("/billing/meter/daily-checkin", {});
10197
+ if (result?.code === 0 && result?.data) return result.data;
10198
+ throw new Error(result?.msg || "Checkin failed");
10199
+ }
8276
10200
  };
8277
10201
  /**
8278
10202
  * 创建 BackendProvider 实例
@@ -8312,8 +10236,12 @@ const BACKEND_REQUEST_TYPES = {
8312
10236
  GET_FILE: "backend:get-file",
8313
10237
  RELOAD_WINDOW: "backend:reload-window",
8314
10238
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
10239
+ OPEN_EXTERNAL: "backend:open-external",
8315
10240
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
8316
- GET_SUPPORT_SCENES: "backend:get-support-scenes"
10241
+ GET_SUPPORT_SCENES: "backend:get-support-scenes",
10242
+ GET_ACCOUNT_USAGE: "backend:get-account-usage",
10243
+ GET_CHECKIN_STATUS: "backend:get-checkin-status",
10244
+ CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin"
8317
10245
  };
8318
10246
  /**
8319
10247
  * 生成唯一请求 ID
@@ -8360,12 +10288,14 @@ var IPCBackendProvider = class {
8360
10288
  */
8361
10289
  async getAccount() {
8362
10290
  this.log("Getting account via IPC");
10291
+ const startTime = performance.now();
8363
10292
  try {
8364
10293
  const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
10294
+ this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
8365
10295
  accountService.setAccount(account);
8366
10296
  return account;
8367
10297
  } catch (error) {
8368
- this.log("Get account failed:", error);
10298
+ this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
8369
10299
  accountService.setAccount(null);
8370
10300
  return null;
8371
10301
  }
@@ -8607,6 +10537,20 @@ var IPCBackendProvider = class {
8607
10537
  }
8608
10538
  }
8609
10539
  /**
10540
+ * 在外部浏览器中打开链接
10541
+ * IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
10542
+ * @param url 要打开的 URL
10543
+ */
10544
+ async openExternal(url) {
10545
+ this.log("Opening external URL via IPC:", url);
10546
+ try {
10547
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
10548
+ } catch (error) {
10549
+ this.log("Open external request failed:", error);
10550
+ throw error;
10551
+ }
10552
+ }
10553
+ /**
8610
10554
  * 批量切换插件状态
8611
10555
  * IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
8612
10556
  */
@@ -8640,6 +10584,51 @@ var IPCBackendProvider = class {
8640
10584
  }
8641
10585
  }
8642
10586
  /**
10587
+ * 获取账号用量信息(积分/Credits)
10588
+ * IDE 环境: 通过 IPC 实时获取用量信息,每次打开菜单时调用
10589
+ *
10590
+ * 调用链:
10591
+ * 1. agent-ui: IPCBackendProvider.getAccountUsage()
10592
+ * 2. Agent Manager renderer: BackendService.getAccountUsage()
10593
+ * 3. Main Process: codebuddy:getAccountUsage IPC handler
10594
+ * 4. 返回 { usageLeft, usageTotal, editionType, refreshAt } 等字段
10595
+ */
10596
+ async getAccountUsage() {
10597
+ this.log("Getting account usage via IPC");
10598
+ try {
10599
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT_USAGE);
10600
+ } catch (error) {
10601
+ this.log("Get account usage failed:", error);
10602
+ return null;
10603
+ }
10604
+ }
10605
+ /**
10606
+ * 获取每日签到状态
10607
+ * IDE 环境: 通过 IPC 获取签到状态
10608
+ */
10609
+ async getCheckinStatus() {
10610
+ this.log("Getting checkin status via IPC");
10611
+ try {
10612
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_CHECKIN_STATUS);
10613
+ } catch (error) {
10614
+ this.log("Get checkin status failed:", error);
10615
+ return null;
10616
+ }
10617
+ }
10618
+ /**
10619
+ * 执行每日签到
10620
+ * IDE 环境: 通过 IPC 执行签到
10621
+ */
10622
+ async claimDailyCheckin() {
10623
+ this.log("Claiming daily checkin via IPC");
10624
+ try {
10625
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.CLAIM_DAILY_CHECKIN);
10626
+ } catch (error) {
10627
+ this.log("Claim daily checkin failed:", error);
10628
+ throw error;
10629
+ }
10630
+ }
10631
+ /**
8643
10632
  * 调试日志
8644
10633
  */
8645
10634
  log(...args) {